Skip to main content

Tu Empresa Digital OS — Implementation Plan

historical · tech-lead · updated 2026-06-26 · source

HISTORICAL (2026-06-26). Initial desktop/Tauri + DigitalOcean execution snapshot — superseded. Current direction: comprender-mvp-e1.md, plan-engine.md, the ADRs (decisions/adrs/README.md). Kept for context only.

Owner: José (solo dev). Backend: DigitalOcean. Companion docs: tedos-product-decisions.md, tedos-devops-plan.md, tedos-design-system.md. This doc is the sequenced execution plan Tauri Eng, AI Eng, Security, DevOps, and QA work from.

Agent Roster

Role labelAgent fileScope
Designer.claude/agents/designer/AGENT.mdDesign system, tokens, screens, motion
DevOps.claude/agents/devops/AGENT.mdDO infra, CI/CD, Tauri distribution, migrations
Backend.claude/agents/backend/AGENT.mdFastify API, Drizzle, Postgres, pg-boss, auth, integrations
PM.claude/agents/pm/AGENT.mdBoard state, issue drafting, plan hygiene
Tech Lead.claude/agents/tech-lead/AGENT.mdMonorepo, TypeScript, ADRs, build system
Frontend.claude/agents/frontend/AGENT.mdTauri shell, React UI, Claude streaming, shell-bridge, GSAP
QA.claude/agents/qa/AGENT.mdPlaywright tests, acceptance criteria, MVP gate
Security(no agent yet)OAuth, JWT, stronghold, scrubbing
QA(no agent yet)Acceptance testing, regression, milestone gate

TL;DR

  • MVP scope: (1) Tauri shell with login, sidebar nav, command palette, and one micro app loaded from a static registry; (2) DO API with auth + license/entitlement endpoints + Claude proxy; (3) Assistant chat with Claude streaming and knowledge/*.md context injection (the contexts/ folder was removed — knowledge docs are the injection source). No remote bundle delivery, no vibe coding, no admin dashboard for MVP.
  • 5 milestones, ~8–11 weeks total for one dev: M1 (1.5w foundation) → M2 (1.5w auth+API) → M3 (2w connections) → M4 (1.5w assistant) → M5 (2.5w micro app platform). Buffer: 1w.
  • Biggest cuts vs original plan: dropped admin dashboard, remote bundle CDN, Ed25519 bundle signing, Stripe billing, GlitchTip, PostHog, vibe-coding apply, light mode, BYO Claude key, Google Drive/Calendar/Gmail (Sheets only), HA Postgres, Resend email flow, refresh-token reuse-detection, and JWKS endpoint — all DEFERRED to post-MVP.

Supabase Responsibility Audit

DevOps proposed a near-1:1 Supabase→DO mapping. Applying José's "be selective" rule. Decisions are MVP-only; deferred items have a target milestone in parens.

ResponsibilityDevOps proposedPM decisionWhy
Auth — JWT issue/verifyCustom Fastify + jose RS256, JWKS endpoint, 2-key rotationREPLICATE (lean) — RS256 jose, single signing key shipped in shell, no JWKS endpoint yetKey rotation matters at >100 clients; one key + manual rotate is fine for MVP.
User/license DBManaged Postgres 1GBREPLICATENon-negotiable.
Refresh tokens table + rotationHashed, with replaced_by reuse-detectionREPLICATE (lean) — hashed + rotation, drop reuse-detection chain (post-MVP)Rotation alone covers 95% of risk; chain-revoke is a nice-to-have.
API — general endpointsFastify on App PlatformREPLICATERequired.
Edge Functions — manifest publishSeparate publish endpoint w/ GH OIDCINLINE into the main API as /v1/internal/manifest/publish behind shared secretOne service, less surface. OIDC adds days; shared secret is fine for solo CI.
Edge Functions — Stripe webhook / suspension cronNightly cron updating licenses.statusDEFER (post-MVP) — manual UPDATE licenses via psql for first 5 clientsNo billing in MVP. Manual is faster than building it.
Edge Functions — Claude proxyNot explicit in DevOps doc; required by product-decisions §5REPLICATE + INLINE into main API (/v1/claude/complete + /v1/claude/stream)Cost-control choke point. Must exist from day one.
Edge Functions — telemetry ingestPOST /v1/telemetryDEFER (M5+)No client volume yet. Use API logs.
Object storage — SpacesBucket w/ shell + apps + manifests prefixesREPLICATE (lean) — bucket exists, only shell/{version}/* populated for auto-updater. Apps prefix unused in MVP (apps ship inside the shell binary)Saves the entire bundle delivery pipeline.
CDNSpaces CDN + cdn.tedos.tuempresa.digitalREPLICATE (lean) — only for shell binaries; no manifests/apps/* yetAuto-updater for the shell itself is non-negotiable.
RealtimeNot usedSKIPPolling is fine, plan agrees.
SecretsApp Platform encrypted env varsREPLICATEBuilt in, free.
Email (password reset)ResendDEFER (M2.5) — MVP login has magic-link disabled, only email/password; password reset done by José manually via psql for first 5 clientsResend setup = DNS + DKIM + DMARC = half-day José doesn't have.
Bundle signing (Ed25519)Per product-decisions §2DEFER (M5 stretch, or post-MVP) — micro apps in MVP are first-party only, shipped inside the shell binary. No remote bundles, no signingRemoves the entire signing/key-custody/CI plumbing for v1.
Admin dashboard (Next.js)Per product-decisions §3DEFER (post-MVP)José is the only operator. SQL + a few npm run scripts cover MVP.
GlitchTip / PostHogPer product-decisions §7DEFER (post-MVP)Sentry SaaS free tier (5k events/mo) for crash reporting only in MVP. PostHog skipped entirely until 10+ clients.
Stripe billingPer product-decisions §4DEFER (post-MVP)First clients are hand-billed (invoice or transfer). Stripe = 1+ week of work.
BackupsDaily Postgres + weekly pg_dump to SpacesREPLICATE (lean) — daily auto-backups only, no extra cronDO daily backups are enough until 10+ clients.
Better Stack logs + uptimeFree tierREPLICATEFree, takes 30 minutes.
Terraform IaCFull Terraform state in SpacesDEFER (M2 end) — provision M1/M2 infra manually via DO UI first to move fast; Terraform-ize once stableIaC is correct long-term but slows the first 2 weeks.

Net result: ~6 components built in MVP (API, Postgres, Spaces+CDN for shell only, Claude proxy inside API, Sentry SaaS, manual ops scripts). vs ~12 in the DevOps doc. Roughly 40% less infra surface for v1.


Build Sequence

M1 — Foundation (offline shell, no backend)

FieldValue
GoalTauri shell boots, renders sidebar + topbar + command palette, loads one hardcoded micro app from the AppRegistry.
User-visible outcomeDouble-click app icon → shell window opens → see "Home" + one demo app in sidebar → ⌘K opens palette → arrow-key nav works → quit. No login yet (mock user).
Backend workNone.
Frontend workTauri v2 scaffold (macOS first); ShellContext, AppRegistry, Sidebar, TopBar, CommandPalette, all primitives from §4 inventory; demo "Hello" micro app; tokens.css from Designer; ErrorBoundary; bilingual (es-MX/en-US) framework.
Integration / contractsDesigner → Tauri Eng handoff (see below). No external contracts.
Exit criteria(1) npm run tauri dev boots in <3s. (2) ⌘K opens palette, typing filters, Enter navigates. (3) Sidebar renders from AppRegistry.list(), no hardcoded view-switch in App.tsx. (4) Demo micro app declares requiredConnections: [] and renders in center-scroll. (5) Zero raw <button> / <input> / inline hex in feature code (lint rule). (6) Light/dark — dark only, locked.
Duration7–10 days
OwnersTauri Eng (primary). Designer → .claude/agents/designer/ (tokens.css, primitives spec).

M2 — Auth + DO API (online, single client)

FieldValue
GoalShell authenticates against api.tedos.tuempresa.digital, stores tokens in OS keychain, polls license status, hard-locks on suspended.
User-visible outcomeCold launch → Login screen → email+password → shell appears → kill API → grace-banner appears after 60s → re-flip status in DB to suspended → lock screen appears within 6h poll (or 10s if testing).
Backend workFastify on DO App Platform; Managed Postgres 1GB; users, clients, licenses, refresh_tokens tables (Drizzle); endpoints: POST /v1/auth/login, POST /v1/auth/refresh, GET /v1/license/status, GET /healthz. RS256 JWT, single key, 1h access / 30d refresh. Rate-limit on /auth/*.
Frontend workLoginScreen (full-window), Suspended/LockScreen, grace-period TopBar banner; useAuth(); OS-keychain refresh-token storage via tauri-plugin-stronghold; license poll every 60s while running (6h spec is for between-launches; tighter for MVP); auto-refresh of access token.
Integration / contractsDevOps → AI Eng: API base URL, JWT format, endpoint shapes (DevOps doc has them). Security → Tauri Eng: stronghold key naming (com.tedos.refresh_token).
Exit criteria(1) Login + refresh + status poll all work end-to-end. (2) Toggle a row in licenses.status and the shell hard-locks within one poll. (3) Stronghold-stored refresh survives quit/relaunch; access token never written to disk. (4) Invalid creds → typed error; rate-limited → friendly message. (5) Network down for 60s → grace banner; restored → banner clears.
Duration10–14 days
OwnersTauri Eng (shell/UI). Backend → .claude/agents/backend/ (Fastify, Postgres, JWT, endpoints). Security (stronghold + JWT review). DevOps → .claude/agents/devops/ (DO infra provisioning).

M3 — Connections (Claude + Google Sheets only)

FieldValue
GoalUser connects Claude (via shared José key, transparent) and Google Sheets (OAuth). Connection states surface in TopBar and ConnectionCard.
User-visible outcomeConnections screen → Claude shows "Connected (shared plan)" by default → click "Connect Google" → browser OAuth flow → return to shell → Sheets shows connected. TopBar pills reflect both.
Backend work/v1/claude/complete and /v1/claude/stream proxy: adds José's Anthropic key server-side, logs token counts to usage table, rejects on monthly cap with structured 429. Google OAuth: API holds client_id/client_secret; flow uses loopback redirect to a Tauri-registered URL; tokens stored encrypted in stronghold, refresh logic client-side. Cap = 500K/100K tokens (Starter tier — single tier in MVP).
Frontend workConnections screen + ConnectionCard states (disconnected / connecting / connected / refresh-failed); Claude detail (model select, monthly usage StatCard from /v1/usage/me); Google Sheets detail (scope checkboxes — read-only by default); useConnections() hook; ConnectionGate primitive. Drop Drive/Gmail/Calendar from MVP.
Integration / contractsSecurity → all: OAuth redirect URI (http://127.0.0.1:<port>/oauth/google/callback); stronghold key naming (com.tedos.connection.google); cap-exceeded UI copy (es/en).
Exit criteria(1) Claude proxy works end-to-end, usage logged in DB, cap-exceeded returns 429 with retry_after_iso. (2) Google OAuth completes, refresh token works after 1h expiry. (3) ConnectionGate shows when a micro app declares requiredConnections: ["google.sheets"] and user hasn't connected. (4) Disconnect button revokes token in DB and stronghold.
Duration12–14 days
OwnersBackend → .claude/agents/backend/ (Claude proxy, usage accounting, Google OAuth tokens). Tauri Eng (OAuth UX, stronghold). Security (OAuth flow review, cap math, key handling).

M4 — Assistant (chat + context injection)

FieldValue
GoalWorking Claude chat with streaming, markdown render, and knowledge/*.md toggle injection.
User-visible outcomeAssistant view → message list empty state → user types → streamed response renders incrementally → toggle me.md ON → ask "who am I" → answer uses the file. Token usage updates in real time.
Backend workNone new — uses /v1/claude/stream from M3. Add server-sent event support if not already in proxy. Optional: a /v1/claude/models listing endpoint (or hardcode in shell).
Frontend workAssistant — Chat (full-height-split): MessageList, MessageBubble, MessageInput, MarkdownRenderer, model selector, Spinner, token-limit error state. Assistant — Context (FileTree + per-file checkbox, preview pane). System prompt builder that concatenates toggled files. Persistence: local conversations.json via tauri-plugin-store (no cloud sync in MVP).
Integration / contractsNone new.
Exit criteria(1) Streaming works smoothly at 60fps, no jank on long responses. (2) Toggling a context file changes the next response observably. (3) Token-cap error renders the friendly state (no raw 429). (4) Conversation survives quit/relaunch. (5) <MarkdownRenderer> handles code blocks with syntax-highlight.
Duration8–11 days
OwnersTauri Eng (UI). Backend → .claude/agents/backend/ (SSE, system-prompt assembly, streaming reliability). QA (token-cap edge cases).

M5 — Micro App Platform (first-party, in-binary)

FieldValue
GoalTwo reference micro apps (Home dashboard + one productivity app, e.g. "Sheets Inspector") registered via AppRegistry. Bundles ship inside the shell binary — no CDN delivery, no signing.
User-visible outcomeApps gallery shows 2 apps + the demo. Click one → renders, declares requiredConnections, shows ConnectionGate if needed. Both apps use ShellContext (toast, navigate) and the typed shell-bridge (mocked — direct function calls in-process for MVP).
Backend workNone new. (CDN bundle endpoint, signing key, bundle download verification all DEFERRED.)
Frontend workApps gallery screen with AppCard grid, search, empty state. Refactor M1's demo app into the new bundle-shape contract (manifest + entrypoint, but resolved at compile time). Two first-party apps: (a) Home dashboard with StatCards (license, usage, connections), (b) Sheets Inspector — list sheets + open one in a DataTable.
Integration / contractsPM → QA: full acceptance matrix per app (states, connection requirements).
Exit criteria(1) Adding a new micro app = single new directory under src/features/, single registry.register(config) call. Zero changes to App.tsx or Sidebar.tsx. (2) Both apps pass the four-states audit (loading / error / empty / success). (3) Sheets Inspector handles token-refresh-failed gracefully. (4) Lint enforces architecture rules (no raw button, no hex).
Duration14–18 days
OwnersTauri Eng (registry + apps). Backend → .claude/agents/backend/ (shell-bridge typed surface, @tedos/shared contracts). QA (full acceptance pass = MVP gate).

Post-MVP (rough order, not committed)

  • M6: Remote micro app bundles + Ed25519 signing + CDN delivery (unlocks third-party catalog growth).
  • M7: Admin dashboard (Next.js) — at this point José can't manage clients by hand.
  • M8: Stripe billing + automated suspension cron.
  • M9: Vibe coding (diff modal, git checkpoint, path jail, tier-gated apply).
  • M10: Windows build + Azure Trusted Signing.
  • M11: Telemetry (GlitchTip + PostHog), in-shell usage dashboard, multi-tier pricing UI.

Critical Path

Designer:tokens+primitives ──► M1 Foundation ──┐

DevOps:Postgres+App Platform ──► M2 API+Auth ──┴──► M3 Connections ──► M4 Assistant ──► M5 Platform ──► [MVP GATE]

Security:OAuth review ────────────────────────────────────────────────────────┘

Longest path = M1→M2→M3→M4→M5 ≈ 51–67 days serial.

Parallelizable:

  • Designer Phase 2 (tokens.css + primitive specs) can land in week 1 alongside M1 scaffolding — must be done by M1 day 3 or M1 stalls.
  • DevOps provisioning (Postgres, Spaces, App Platform, DNS) runs in parallel with M1. Must be ready by M2 day 1.
  • Security review of OAuth flow can begin during M3 day 1 (spec exists), not at end — gate is "before M3 close."
  • AI Eng can build the Claude proxy server-side during late M2 while Tauri Eng finishes auth UI.

Single biggest schedule risk: M3 Google OAuth in a Tauri loopback flow. Known to be fiddly. Build a spike day-1 of M3.


Risks & Mitigations

RiskLikelihoodImpactMitigation
Google OAuth loopback flow fails on WindowsMHSpike day-1 of M3, fall back to system browser + custom URL scheme (tedos://oauth/callback) registered via Tauri.
Tauri stronghold plugin instability on macOS Sonoma+MHHave tauri-plugin-keyring as backup; test both in M1 spike.
Claude streaming via Fastify proxy adds latency / breaks SSEMMBenchmark in M2 spike. If >500ms TTFT delta, switch to node:stream raw response, or move proxy to a separate App Platform service.
Solo dev burnout from 8–11 week serial pathHHCut M5 second app if needed (MVP can ship with just Home + Sheets); cut M4 conversation persistence; cut bilingual to one language in MVP. None of these block the demo.
RS256 single-key + no JWKS means key rotation requires shipping a new shell buildLMDocument explicitly; acceptable for <50 clients. Add JWKS endpoint when M6 ships.
Manual ops (psql for suspension, hand-billing) doesn't scale past 5 clientsHLAcceptable — by client #6, M7 admin + M8 Stripe ship.
First-party apps inside shell binary = binary bloat and update sizeMLCode-split per-app at bundle time, lazy-load on launch. Re-evaluate at 5+ apps.
Spec drift between Designer and Tauri EngMMDesigner-Eng sync once per week, screens.md is the source of truth, no UI in code that's not in screens.md.
Claude cap math wrong → José eats costLHSet hard daily kill-switch at 2x monthly cap / 30 days. Alert to email at 80% monthly.

Hand-off Contracts

Designer → Tauri Eng

  • tokens.css — all colors, spacing, type scale, radii as CSS custom properties. Dark-only. Mapped to Tailwind via tailwind.config.ts.
  • primitives.md — for each of the 13 primitives in §4: API (props, variants), states, a11y notes, examples.
  • screens.md — for each screen in design-brief inventory: layout mode (center-scroll vs full-height-split), required components, all states (loading/error/empty/success), copy in en + es.
  • No Figma → code automation in MVP. Eng builds from tokens.css + screens.md. Figma is reference.
  • Update cadence: Designer ships Phase 2 once. Bug fixes and additions go in via PR comments on the markdown files.

DevOps → AI Eng

  • Base URL: https://api.tedos.tuempresa.digital (prod), https://api-staging.tedos.tuempresa.digital (staging).
  • Endpoint contracts: as enumerated in DevOps doc §Auth Implementation. AI Eng will add /v1/claude/{complete,stream} and /v1/usage/me — DevOps reviews shape before merge.
  • Env vars: DATABASE_URL, JWT_PRIVATE_KEY, ANTHROPIC_API_KEY, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, STRONGHOLD_SHARED_KEY (for shell), SENTRY_DSN.
  • Deploy: push to main → App Platform auto-deploy. Migrations via drizzle-kit push:pg from CI.
  • Rollback: App Platform UI "Roll back to previous deployment." Document this on day 1.

Security → all

  • OAuth flow: loopback (http://127.0.0.1:<random>) preferred, custom-scheme (tedos://) fallback. State+PKCE mandatory. Tokens never touch logs.
  • Stronghold key naming convention: com.tedos.<purpose>.<id>. Examples: com.tedos.refresh_token, com.tedos.connection.google, com.tedos.connection.claude_byo (post-MVP).
  • Suspension polling cadence: every 60s while shell is in foreground, every 6h while backgrounded. On suspended → hard-lock immediately, even if last active was <60s ago.
  • Claude proxy: rate-limit 30 req/min per user, 100K tokens/day kill-switch per user, 95th-percentile alert at >80% of monthly cap.
  • Sentry scrubbing: strip Authorization header, all claude.complete prompt bodies, all fs.* paths from breadcrumbs.

PM → QA

  • Per-milestone acceptance criteria above are the test plan. QA owns regression run before each milestone close.
  • MVP gate (end of M5): all M1–M5 exit criteria pass on a clean macOS install, with a brand-new client account seeded via SQL. Bilingual sweep (es-MX + en-US) on Login, Home, Apps, Assistant, Connections, Suspended.
  • Bug bar for MVP ship: zero P0 (crash, data loss, security), ≤3 P1 (broken feature with no workaround), ≤10 P2.

Out of Scope for MVP

Explicit list. Bring back in post-MVP per the M6–M11 sketch.

  • Admin dashboard (Next.js app)
  • Remote micro app bundles + signing + CDN delivery
  • Stripe billing + automated suspension cron
  • Vibe coding (any apply flow)
  • Windows build + Azure Trusted Signing
  • Light mode
  • Per-client branding (logo, accent color)
  • Multi-tier pricing (MVP is single-tier, Starter quota)
  • BYO Claude API key
  • Google Drive, Gmail, Calendar connections
  • GlitchTip / PostHog / dedicated telemetry pipeline
  • HA Postgres / standby replica
  • JWKS endpoint + multi-key rotation window
  • Refresh-token reuse-detection chain
  • Token-usage dashboard inside the shell (shows in M3 as a StatCard only, no history graph)
  • Conversation cloud sync (local-only)
  • Resend email + password reset self-service
  • Terraform IaC (provisioned via DO UI for M1/M2, Terraform-ized at end of M2)
  • Free trial logic
  • Update prompt UI (Tauri default updater modal is fine for MVP)
  • Notifications inbox (OS-level toasts only)

Decisions Still Needed from José

Filtered to blocking only. Everything else has a recommendation in product-decisions or this doc — I'm not asking unless a wrong default genuinely costs us time.

  1. macOS-only for MVP, Windows post-MVP? I've assumed yes. Confirms we drop the entire WIN code-signing + Trusted Signing setup from the critical path. If wrong, add ~1 week to M5 for Windows parity.
  2. Single-tier MVP (Starter quota only) for the first 5 clients, with tier UI deferred? I've assumed yes. If you want Pro/Enterprise selectable in the UI from day one, add ~3 days to M2 for plan-aware claims + UI.
  3. Domain confirmed as tedos.tuempresa.digital? Affects DNS + cert + email DKIM setup in M2. If it's tuempresa.digital subdomain or something else, decide before M2 day 1.