evOo-local/docs/PRODUCTION-READINESS.md
Cursor Agent 0c9e6d7c58
fix(security): GitHub broker, drop sudo, DE awareness i18n, CI image tools
Route git push and gh pr through the sandbox supervisor broker so project
computers never receive GITHUB_TOKEN/GH_TOKEN. Remove NOPASSWD sudo from the
computer image. Localize awareness strip copy via Lingui and wire
computer-image-tools.test.sh into the validate-computer-image job.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-07 21:41:56 +00:00

197 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# evOo-local production readiness
Audit of user journeys, automated coverage, known defects, and backlog. Scope: web app (`apps/web`), API, shared packages; mobile (`apps/mobile`) noted where CI does not cover it.
## Product surfaces
| Surface | Entry | Locales in CI |
|--------|--------|----------------|
| Desktop web | ≥768px viewport | EN + DE in targeted specs |
| Mobile web | 390×844, touch | EN + DE in targeted specs |
| API / Postgres journeys | `packages/testkit` | EN |
| Mobile native | Expo app | Maestro smoke only (not full matrix) |
## User journey inventory
Legend: **E2E** = Playwright under `apps/web/e2e/`; **Unit** = vitest in app/packages; **PG** = postgres journey in testkit; **Gap** = missing or partial automation.
### First run & onboarding
| Journey | Routes / UI | Desktop E2E | Mobile E2E | Unit/PG | Gaps |
|--------|-------------|-------------|------------|---------|------|
| Sign up | `/sign-up` | `golden`, `first-run-onboarding-ux` | same | auth PG | — |
| Look category & swipe | `/onboarding` emoji step | `onboarding-oo-swipe`, `onboarding-emoji-first` | swipe spec (desktop viewport) | — | Mobile-specific swipe gestures |
| Name Oo | name step | `first-run-onboarding-ux` | partial | — | — |
| Language | language step | `first-run-onboarding-ux`, `ui-locale` | DE skip label | — | — |
| Voice | voice step | `first-run-onboarding-ux`, `voice` | DE voices | — | Offline voice errors (no vendor names in UI) |
| Integrations / scan | optional steps | `onboarding-discovery`, `onboarding-complete` | — | onboarding PG | — |
| Reload mid-onboarding | `/onboarding` | `onboarding-reload-persistence` | same | `onboarding-draft.test` | — |
| Intake cards (focus, goal, rhythm, quiet hours) | `/app/g/…` | `onboarding-intake` | **`onboarding-intake-de`** | onboarding-intake PG | Full DE rhythm/quiet copy in cards (UI localized; server stores locale-aware focus facts) |
| Deep link `/onboarding` while signed in | redirect rules | **`route-deep-link-matrix`** | same | — | — |
### Session & auth
| Journey | Routes | E2E | Gaps |
|--------|--------|-----|------|
| Sign in / out | `/sign-in`, `/` | `auth-lifecycle`, `browser-auth` | Tailscale / cookie edge cases |
| Password reset | `/forgot-password`, `/reset-password` | `auth-lifecycle` | — |
| Session unavailable / retry | `/app` loading | **`session-unavailable`**, `module-recovery` | — |
| Welcome (signed out) | `/` | `smoke`, **`route-deep-link-matrix`** | — |
### Heute (daily brief)
| Journey | Route | E2E | Gaps |
|--------|-------|-----|------|
| Open Heute | `/app/heute` | `heute-brief`, **`mobile-layout-audit`** | — |
| Coach / goals list | Heute UI | `heute-brief` | — |
| Deep link reload | `/app/heute` | **`route-deep-link-matrix`**, **`journey-console-errors`** | — |
### 1:1 & group chat
| Journey | Route | E2E | Gaps |
|--------|-------|-----|------|
| Main conversation | `/app/g/:groupId` | `main-conversation`, `first-conversation` | — |
| Bot thread | `/app/:botId` | many specs | — |
| Send message | composer | `message-send` | — |
| Streaming / progress | transcript | `chat-progress-messages`, `chat-queued-progress` | Real LLM latency (mocked) |
| Queued / slow model UI | composer + transcript | `chat-queued-progress` | Multi-run queue depth |
| Errors & retry | composer banner | `run-failure`, `computer-screen-error` | — |
| Stop run | composer | `golden` | — |
| Peer / group | `/app/g/…` | `group-chats`, `peer-messages` | — |
| Console cleanliness | main paths | **`journey-console-errors`** (full route + settings matrix) | — |
### Oos (bots)
| Journey | E2E | Gaps |
|--------|-----|------|
| Create / name / archive / delete | `bot-crud`, `new-bot-ux` | — |
| Switch bot / sidebar | `sidebar-nav`, `unterhaltungen-sidebar` | — |
| Avatar / look | `oo-avatar`, `look-suggestion` | — |
| Capabilities | `bot-capabilities` | — |
| Team vs dedicated computer | `team-computer` | Real docker teardown in PG |
### Projekte
| Journey | E2E | Gaps |
|--------|-----|------|
| Create project space | `spaces`, `adaptive-workspace` | — |
| Bind folder + git | `project-main-agent` | — |
| Timeline / goals | `project-main-agent` | Voice-first goal card |
| Empty project | **`empty-project-state`** | — |
### Settings
| Journey | E2E | Gaps |
|--------|-----|------|
| General / account | `settings-shell`, `user-profile` | — |
| Language (UI locale) | `ui-locale` | — |
| Voice | `voice`, `media-settings` | — |
| Goal / intake copy (DE) | **`onboarding-intake-de`** (focus fact in profile) | Extend locale matrix beyond DE |
| Privacy / delete account | `settings-privacy` | — |
| Model connection | `model-settings`, `models-managed` | Offline model banner |
| Notifications / quiet hours | `notification-settings` | — |
| Messaging | `messaging-settings` | — |
### Account & space deletion
| Journey | E2E / PG | Gaps |
|--------|----------|------|
| Delete account (API) | PG journey 11, **`account-delete-sandbox.postgres`** | — |
| Delete empty space | authorization PG | — |
### Empty, error, offline
| Journey | Coverage | Gaps |
|--------|------------|------|
| No model connected | `model-settings`, composer banner | — |
| Server unreachable | **`session-unavailable`** | — |
| Empty inbox / no bots | partial | — |
| Teaching blocks composer | **`teaching-composer`** | — |
### Navigation, keyboard, a11y
| Journey | E2E | Gaps |
|--------|-----|------|
| Escape closes mobile drawer | `first-run-onboarding-ux` mobile | — |
| Command palette | `command-palette` | — |
| Mention picker keyboard | `mention-picker-keyboard` | — |
| Collapsible sidebar | `collapsible-sidebar-sections`, `mobile-sidebar-swipe` | — |
| Mobile 390px overflow | **`mobile-layout-audit`** | Broader route list |
| Touch targets ≥44px | **`mobile-layout-audit`** | Broader control list |
| Focus management on drawer close | mobile esc test | Broader focus trap review |
### Integrations & MCP
| Journey | E2E | Gaps |
|--------|-----|------|
| Integration setup | `integration-setup` | — |
| MCP OAuth callback | `mcp-oauth` | — |
| GraphQL integrations | `graphql-integrations` | — |
## Prioritized defect list
Severity: **P0** ship blocker · **P1** major UX/trust · **P2** polish · **P3** nice-to-have
### Fixed in Phase 1 ([PR #175](https://github.com/cynarAI/evOo-local/pull/175))
| ID | Sev | Issue | Fix |
|----|-----|-------|-----|
| PR1-1 | P1 | Reload during onboarding lost look/name/step | Session draft via `onboarding-draft.ts` |
| PR1-2 | P1 | DE UI: goal placeholder / discovery “Chief of Staff” English | Lingui for discovery; intake DE E2E |
| PR1-3 | P1 | Account delete left team sandbox + homes | Scoped `destroySpaceSandboxes` on account delete |
| PR1-4 | P1 | Chat felt idle while queued / slow send | Composer `sending`+run chrome; queued label; 120s watchdog |
| PR1-5 | P2 | Console errors on main journeys | Initial `journey-console-errors` spec |
### Fixed in Phase 2 (this PR)
| ID | Sev | Issue | Fix |
|----|-----|-------|-----|
| R2-1 | P1 | Profile focus fact values stored in English for DE users | `focusFactValue()` in `onboarding-intake.ts`; DE assertion in `onboarding-intake-de` |
| R2-2 | P1 | PG journey: account delete sandbox/home teardown | Phase 1 `account-delete-sandbox.postgres` + journey 11 |
| R2-3 | P2 | Persona-test console/network errors | Extended `journey-console-errors` + deep-link matrix; benign React Router / source-map filtering |
| R2-4 | P2 | 390px layout overflow | Heute header wrap; shell safe-area; **`mobile-layout-audit`** |
| R2-5 | P2 | Touch targets &lt;44px | `rk-touch-target` on mobile nav/back; **`mobile-layout-audit`** |
| R2-6 | P2 | Deep link / reload matrix | **`route-deep-link-matrix`** |
| R2-7 | P2 | Empty project / SessionUnavailable / teaching composer E2E | **`empty-project-state`**, **`session-unavailable`**, **`teaching-composer`** |
### Fixed in Phase 3 (real code work)
| ID | Sev | Issue | Fix |
|----|-----|-------|-----|
| R3-git | P0 | Computer image missing git/gh/ssh/curl | `infra/sandboxes/computer/Dockerfile` + `computer-image-tools.test.sh` in validate CI |
| R3-mount | P0 | `Space.workspacePath` not mounted in Oo | Bind at `/home/rakazo/project` for deployment owner project spaces |
| R3-path | P0 | `/home/rakazo/...` file paths doubled | `stripVirtualWorkspaceRoot` + tests |
| R3-branch | P0 | Default branch from checkout not remote | `readGitCheckout` uses `origin/HEAD` |
| R3-push | P0 | Unrestricted git push / force-push | Shell policy + **supervisor GitHub broker** (host-side git/gh with enforced policy) |
| R3-token | P0 | GitHub PAT reachable inside computer env | Ciphertext in `deployment_settings`; PAT stays on supervisor/API broker only |
| R3-sudo | P0 | NOPASSWD sudo in computer image | Removed; dev tools preinstalled in image |
| R3-own | P0 | Project bind mount root-owned files | Container runs as host uid/gid + ownership integration test |
| R3-runaway | P1 | Runaway tool loops | Default 48 tool fuse → `need-user` ask; 15m stall ask |
| R3-ux | P1 | Narration flood / stream fragments | Activity line + streaming text merge in `progressMessageText` |
| R3-i18n | P2 | English run chrome in DE | Lingui for awareness strip + working narration |
| R3-name | P2 | Duplicate “Chief” in project sidebar | Project main Oo defaults to **Lead** |
| R3-flake | P1 | `evo-deploy.test.ts` grep flake | Fixed `--filter` pattern in stack fixture |
**Evo ops:** Rebuild `rakazo/computer:local` on Evo after merge (`pnpm sandbox:build` or compose `computer-image` target). Server deploy via `ops/evo-deploy/deploy.sh` updates app images; the computer image is separate—rebuild and restart sandboxes so new containers pick up git tools.
**Security (GitHub broker):** The deployment owner stores a PAT as ciphertext (`ownerGithubTokenCipher` via `deployment.update`). Project computers never receive `GITHUB_TOKEN`/`GH_TOKEN`. Instead they get a run-scoped broker URL + opaque run token; `/usr/local/lib/rakazo/bin/git` and `gh` forward network operations to the sandbox supervisor (`/internal/github-broker`), which holds the PAT and runs git/gh on the host workspace path. The broker enforces: allowlisted remote only, no default-branch push, no force-push, and the same per-run first push/`gh pr` approval flag as the shell tool. Configure `SANDBOX_GITHUB_BROKER_URL` (reachable from bot containers) and register sessions when runs start.
### Remaining — Phase 3+
| ID | Sev | Issue | Notes |
|----|-----|-------|-------|
| R3-1 | P2 | Offline / model-not-connected on mobile web | Dedicated E2E with RPC fixtures |
| R3-2 | P2 | Intake card rhythm/quiet option labels server-side for non-EN history | Cards localized in web; stored English option labels for agent history |
| R3-3 | P3 | Mobile native parity for onboarding reload | Expo secure store draft |
| R3-4 | P3 | Intake card English questions in non-EN tests | Extend locale matrix beyond DE |
| R3-5 | P3 | NPS/SUS follow-ups from persona round 2 | Product research, not CI |
| R3-6 | P3 | Tailscale / exotic auth cookie edge cases | Manual / staging |
## CI gates (Product)
Lint · Typecheck · Unit · Postgres journeys · Production builds · Web E2E (Playwright). Desktop Electron E2E runs in CI virtual display only.
## How to extend this doc
When adding a journey spec, update the table row and move defects from **Remaining** to **Fixed** with PR link. Keep user-visible copy quoted in PR descriptions when introducing new strings.