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 label | Agent file | Scope |
|---|---|---|
| Designer | .claude/agents/designer/AGENT.md | Design system, tokens, screens, motion |
| DevOps | .claude/agents/devops/AGENT.md | DO infra, CI/CD, Tauri distribution, migrations |
| Backend | .claude/agents/backend/AGENT.md | Fastify API, Drizzle, Postgres, pg-boss, auth, integrations |
| PM | .claude/agents/pm/AGENT.md | Board state, issue drafting, plan hygiene |
| Tech Lead | .claude/agents/tech-lead/AGENT.md | Monorepo, TypeScript, ADRs, build system |
| Frontend | .claude/agents/frontend/AGENT.md | Tauri shell, React UI, Claude streaming, shell-bridge, GSAP |
| QA | .claude/agents/qa/AGENT.md | Playwright 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/*.mdcontext injection (thecontexts/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.
| Responsibility | DevOps proposed | PM decision | Why |
|---|---|---|---|
| Auth — JWT issue/verify | Custom Fastify + jose RS256, JWKS endpoint, 2-key rotation | REPLICATE (lean) — RS256 jose, single signing key shipped in shell, no JWKS endpoint yet | Key rotation matters at >100 clients; one key + manual rotate is fine for MVP. |
| User/license DB | Managed Postgres 1GB | REPLICATE | Non-negotiable. |
| Refresh tokens table + rotation | Hashed, with replaced_by reuse-detection | REPLICATE (lean) — hashed + rotation, drop reuse-detection chain (post-MVP) | Rotation alone covers 95% of risk; chain-revoke is a nice-to-have. |
| API — general endpoints | Fastify on App Platform | REPLICATE | Required. |
| Edge Functions — manifest publish | Separate publish endpoint w/ GH OIDC | INLINE into the main API as /v1/internal/manifest/publish behind shared secret | One service, less surface. OIDC adds days; shared secret is fine for solo CI. |
| Edge Functions — Stripe webhook / suspension cron | Nightly cron updating licenses.status | DEFER (post-MVP) — manual UPDATE licenses via psql for first 5 clients | No billing in MVP. Manual is faster than building it. |
| Edge Functions — Claude proxy | Not explicit in DevOps doc; required by product-decisions §5 | REPLICATE + INLINE into main API (/v1/claude/complete + /v1/claude/stream) | Cost-control choke point. Must exist from day one. |
| Edge Functions — telemetry ingest | POST /v1/telemetry | DEFER (M5+) | No client volume yet. Use API logs. |
| Object storage — Spaces | Bucket w/ shell + apps + manifests prefixes | REPLICATE (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. |
| CDN | Spaces CDN + cdn.tedos.tuempresa.digital | REPLICATE (lean) — only for shell binaries; no manifests/apps/* yet | Auto-updater for the shell itself is non-negotiable. |
| Realtime | Not used | SKIP | Polling is fine, plan agrees. |
| Secrets | App Platform encrypted env vars | REPLICATE | Built in, free. |
| Email (password reset) | Resend | DEFER (M2.5) — MVP login has magic-link disabled, only email/password; password reset done by José manually via psql for first 5 clients | Resend setup = DNS + DKIM + DMARC = half-day José doesn't have. |
| Bundle signing (Ed25519) | Per product-decisions §2 | DEFER (M5 stretch, or post-MVP) — micro apps in MVP are first-party only, shipped inside the shell binary. No remote bundles, no signing | Removes the entire signing/key-custody/CI plumbing for v1. |
| Admin dashboard (Next.js) | Per product-decisions §3 | DEFER (post-MVP) | José is the only operator. SQL + a few npm run scripts cover MVP. |
| GlitchTip / PostHog | Per product-decisions §7 | DEFER (post-MVP) | Sentry SaaS free tier (5k events/mo) for crash reporting only in MVP. PostHog skipped entirely until 10+ clients. |
| Stripe billing | Per product-decisions §4 | DEFER (post-MVP) | First clients are hand-billed (invoice or transfer). Stripe = 1+ week of work. |
| Backups | Daily Postgres + weekly pg_dump to Spaces | REPLICATE (lean) — daily auto-backups only, no extra cron | DO daily backups are enough until 10+ clients. |
| Better Stack logs + uptime | Free tier | REPLICATE | Free, takes 30 minutes. |
| Terraform IaC | Full Terraform state in Spaces | DEFER (M2 end) — provision M1/M2 infra manually via DO UI first to move fast; Terraform-ize once stable | IaC 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)
| Field | Value |
|---|---|
| Goal | Tauri shell boots, renders sidebar + topbar + command palette, loads one hardcoded micro app from the AppRegistry. |
| User-visible outcome | Double-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 work | None. |
| Frontend work | Tauri 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 / contracts | Designer → 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. |
| Duration | 7–10 days |
| Owners | Tauri Eng (primary). Designer → .claude/agents/designer/ (tokens.css, primitives spec). |
M2 — Auth + DO API (online, single client)
| Field | Value |
|---|---|
| Goal | Shell authenticates against api.tedos.tuempresa.digital, stores tokens in OS keychain, polls license status, hard-locks on suspended. |
| User-visible outcome | Cold 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 work | Fastify 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 work | LoginScreen (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 / contracts | DevOps → 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. |
| Duration | 10–14 days |
| Owners | Tauri 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)
| Field | Value |
|---|---|
| Goal | User connects Claude (via shared José key, transparent) and Google Sheets (OAuth). Connection states surface in TopBar and ConnectionCard. |
| User-visible outcome | Connections 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 work | Connections 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 / contracts | Security → 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. |
| Duration | 12–14 days |
| Owners | Backend → .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)
| Field | Value |
|---|---|
| Goal | Working Claude chat with streaming, markdown render, and knowledge/*.md toggle injection. |
| User-visible outcome | Assistant 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 work | None 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 work | Assistant — 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 / contracts | None 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. |
| Duration | 8–11 days |
| Owners | Tauri 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)
| Field | Value |
|---|---|
| Goal | Two 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 outcome | Apps 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 work | None new. (CDN bundle endpoint, signing key, bundle download verification all DEFERRED.) |
| Frontend work | Apps 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 / contracts | PM → 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). |
| Duration | 14–18 days |
| Owners | Tauri 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
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Google OAuth loopback flow fails on Windows | M | H | Spike 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+ | M | H | Have tauri-plugin-keyring as backup; test both in M1 spike. |
| Claude streaming via Fastify proxy adds latency / breaks SSE | M | M | Benchmark 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 path | H | H | Cut 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 build | L | M | Document explicitly; acceptable for <50 clients. Add JWKS endpoint when M6 ships. |
| Manual ops (psql for suspension, hand-billing) doesn't scale past 5 clients | H | L | Acceptable — by client #6, M7 admin + M8 Stripe ship. |
| First-party apps inside shell binary = binary bloat and update size | M | L | Code-split per-app at bundle time, lazy-load on launch. Re-evaluate at 5+ apps. |
| Spec drift between Designer and Tauri Eng | M | M | Designer-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 cost | L | H | Set 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 viatailwind.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 viadrizzle-kit push:pgfrom 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 lastactivewas <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
Authorizationheader, allclaude.completeprompt bodies, allfs.*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.
- 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.
- 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.
- Domain confirmed as
tedos.tuempresa.digital? Affects DNS + cert + email DKIM setup in M2. If it'stuempresa.digitalsubdomain or something else, decide before M2 day 1.