Files
smart-org/docs/plan/reports/agent-3-phase-6.md
T
mjalmoudy94 3c8296e354 feat(delivery): the Application page + owner consoles (v2 phase A3-6)
The user-visible payoff of the whole delivery workstream.

- (app)/app: four tabs — pipeline (runs + the eight steps + a live console),
  releases (with demo rollback), production (target summary, confirmed deploy,
  history), Git. The header answers "is it live": demo URL, live release,
  health state.
- Live console: batches onto an animation frame, stops following the tail the
  moment the user scrolls away from the bottom, strips ANSI colour codes and
  carriage-return redraws. A console that yanks the view back is worse than none.
- Production panel: the target is shown as the operator recognises their server
  plus has_secret/ثبت شده. No field anywhere could render a credential.
- contracts: the v2 delivery types and seven new WsEventType members.
- /admin/sandbox (owner): per-role container counts, running containers, per-org
  limits, image mapping, stop with the id typed back (audited). A provider without
  the capability reports an empty list rather than an error.
- /admin/domains (owner): every route with its owning app, attach/detach (host
  typed back), idempotent re-sync.
- /projects removed entirely, with a permanent redirect to /app.

Tests: 502 API (+3), 78 web (+11); ruff, mypy, tsc and lint clean.

Report: docs/plan/reports/agent-3-phase-6.md
2026-10-14 02:03:38 +03:30

6.4 KiB

Agent 3 — Phase 6 report: the Application page

Date: 2026-10-14 · Branch: dev

What was done

6.4 lib/api-delivery.ts — the typed client

~15 functions covering CI/CD runs, releases & runtime, production deploy/rollback and the owner domain console. Transport stays in the locked lib/api.ts; this module only composes paths.

packages/contracts gained the whole v2 delivery surface — PipelineRun, PipelineRunDetail, Release, AppRuntime, ProductionTarget, ProductionDeploy, ProductionOverview, DomainRoute, SuggestedRoute — plus the seven new WsEventType members (pipeline.queued/step/log/status, release.live, deploy.status) and their live payload shapes.

6.1 (app)/app/page.tsx

Four tabs, in the order the questions are actually asked:

Tab Shows
خط لولهٔ انتشار the runs, and (once one is opened) its eight steps plus a live log
نسخهها the release history, with demo rollback (admin+)
production the target summary, deploy/rollback with confirmation, history
Git the dev head and nothing else — no branch, port or command controls

The header answers "is it live": app name, status badge, demo URL, live release sha and its timestamp, health state.

_components/pipeline-console.tsx — the live build console. Three details make it usable rather than merely live:

  • batching — lines are coalesced onto an animation frame, so a burst does not thrash the DOM;
  • auto-scroll that yields — following the tail stops the moment the user scrolls away from the bottom (the classic cause of a console that yanks the view back);
  • ANSI stripped — a build log is full of colour codes; rendered raw they are unreadable noise. stripAnsi handles CSI sequences and carriage-return redraws.

_components/production-panel.tsx — the target is shown as the operator recognises their own server (host, port, path) and as ثبت شده/ثبت نشده for the credential. There is no field anywhere that could render one. A user who is not an org admin sees the history and no buttons at all; a missing target renders an empty state rather than a disabled button.

6.3/6.4 /admin/sandbox and /admin/domains

GET /api/admin/sandbox/containers + POST .../{id}/stop (owner-only, container id typed back, audited) live on a separate admin_router in sandbox.py — the tenant router in that module is org-scoped and the owner console must never be. /admin/sandbox shows per-role container counts, the running containers, the per-org resource limits and the base-image mapping; /admin/domains shows every route with its owning app, attracts and detaches hosts (host typed back), and re-syncs.

Both consoles degrade honestly: a provider without list_managed_containers yields an empty list rather than an error, because a CLI outage must not look like "no containers".

6.2 /projects is gone

Removed entirely; next.config.mjs now has a permanent /projects* → /app redirect, so a bookmark or a mid-flow history entry lands somewhere useful instead of on a 404. The member-facing preview-domain endpoints were already made owner-only in Phase 4.

Files changed

File Change
apps/web/src/lib/api-delivery.ts the typed client
apps/web/src/app/(app)/app/page.tsx the Application page
apps/web/src/app/(app)/app/_components/pipeline-console.tsx new
apps/web/src/app/(app)/app/_components/production-panel.tsx new
apps/web/src/app/(app)/admin/domains/page.tsx the owner domain console
apps/web/src/app/(app)/admin/sandbox/page.tsx the owner sandbox console
apps/web/next.config.mjs /projects* → /app redirect
apps/web/src/app/(app)/projects/** deleted
packages/contracts/src/index.ts v2 delivery types + event types
apps/api/app/interfaces/http/routers/sandbox.py admin_router with 2 owner routes
apps/api/app/interfaces/realtime/rpc_app.py mounts it
tests/test_docker_sandbox.py +3 sandbox-console tests
apps/web/src/test/delivery-app.test.tsx new — 11 tests

Test output

apps/api:  502 passed, 1 skipped (was 499; +3)
           ruff check .  -> All checks passed!
           mypy app      -> Success: no issues found in 136 source files
apps/web:  78 passed (was 67; +11)
           tsc --noEmit  -> clean
           next lint     -> No ESLint warnings or errors

Deviations from the TODO

  1. The sandbox console's backend is two routes in sandbox.py rather than its own router file. The plan assigns projects.py, sandbox.py, delivery.py and admin_domains.py to Agent 3; adding a sixth router for two endpoints would have meant putting them in a file the ownership table does not name. A second router object in sandbox.py keeps the ownership right and the mount explicit.
  2. The Application page defaults to the org's single project. One org has one application (D1), so there is no picker to build — and no app profile endpoint yet, because that is Agent 1's Phase 2. The Git tab therefore shows the dev head rather than the app-type badges the TODO's header describes; those arrive with the profile.
  3. The deploy button appears for any signed-in member. The server is the authorization boundary (the API tests assert a non-admin gets 403), and the web only has the platform role, not the org role. Rendering optimistically and letting the server refuse is the honest option; a fake local gate would be worse. The reason is in a comment in production-panel.tsx.

Open questions / CHANGE REQUESTs

  • CHANGE REQUEST (for integration): the web needs the caller's org role to gate the production buttons client-side. MeResponse.organizations already carries OrganizationWithRole, so it is a small addition to SessionValue — noted so it is added deliberately rather than duplicated as a guess.

Known limitations

  • /admin/sandbox shows and stops containers; it does not restart them. There is no "start" because a stopped workspace container is recreated on demand by the next session, and a restart button would imply a guarantee the lifecycle does not make.
  • /admin/domains's attach form picks the project from the routes already registered rather than from a picker. Assigning a host to a specific app of a multi-project owner needs the app list, which is Agent 1's work.