mirror of
https://github.com/nexu-io/open-design.git
synced 2026-09-20 06:15:06 +08:00
6b90486c97
* fix(docs): align question guidance with form flow * docs: reconcile stale documentation with current code * fix(docs): remove retired template metadata from guides Generated-By: looper 0.10.6 (runner=fixer, agent=codex) * fix(e2e): stabilize visual workspace helper Generated-By: looper 0.10.6 (runner=fixer, agent=codex) * fix(docs): align localized template guide metadata Generated-By: looper 0.10.6 (runner=fixer, agent=codex)
13 KiB
13 KiB
Maintainability Roadmap
Purpose
This document captures the maintainability risks in the current apps/web + apps/daemon architecture and the recommended optimization path.
The architectural boundary stays unchanged:
apps/web: Next.js frontend and thin BFF/proxy layer.apps/daemon: local runtime/backend for SQLite, daemon-managed filesystem state, AI agent CLI processes, and SSE streaming. This roadmap MUST NOT define daemon data paths; read rootAGENTS.md→ Daemon data directory contract before documenting storage.
The first-principles maintainability goals are:
- Understandability: engineers can locate behavior quickly and reason about data flow.
- Changeability: common changes can be made with bounded blast radius.
- Verifiability: contracts, tests, and types catch regressions early.
- Isolation: high-risk capabilities are contained behind explicit boundaries.
- Recoverability: failures produce actionable state, logs, and cleanup behavior.
Priority Scale
| Priority | Meaning |
|---|---|
| P0 | Blocks safe evolution or creates high-risk runtime/security failure modes. |
| P1 | Major maintainability risk that increases regression and debugging cost. |
| P2 | Medium-term risk that affects reliability, portability, or architecture clarity. |
| P3 | Supporting documentation/process improvement. |
Risk List and Optimization Plan
| ID | Priority | Risk | Evidence | Impact | Optimization Plan |
|---|---|---|---|---|---|
| R1 | P0 | Daemon TypeScript enforcement still has suppression gaps. | apps/daemon typechecks, but @ts-nocheck remains in high-risk core modules (server.ts, agents.ts, projects.ts, runtimes/runs.ts, cli.ts), newer brand/memory modules, and several tests. |
Payload, DB row, process, and event changes inside suppressed modules can bypass the guarantees reported by package typecheck. | Keep TypeScript as the source default, prevent new suppressions, remove existing suppressions owner-by-owner, and route shared API/SSE/error shapes through packages/contracts. |
| R2 | P0 | Shared web/daemon contracts need complete adoption. | packages/contracts now owns shared API DTOs, error shapes, and SSE unions consumed by both runtimes, while some legacy daemon handlers still shape JSON locally. |
New or legacy endpoints that bypass the package can still drift from web and CLI consumers at runtime. | Extend packages/contracts before changing shared wire shapes, use the daemon HTTP response helpers at route boundaries, and add contract-focused tests for every migrated endpoint. |
| R3 | P0 | Runtime validation is incomplete at the daemon boundary. | Daemon requests can trigger local filesystem access, SQLite writes, and child_process.spawn(). |
Type correctness alone cannot protect against malformed runtime input, path traversal, invalid agent IDs, or unsafe args. | Add schema validation at HTTP boundaries with Zod/TypeBox; centralize validation for workspace paths, task IDs, agent IDs, models, reasoning options, uploaded files, and command arguments. |
| R4 | P0 | Local capability security boundary needs explicit rules. | Daemon owns high-permission capabilities: local files, daemon-managed storage, project workspaces, agent CLIs, and logs. | Unsafe path handling, broad command execution, token leakage, and unintended workspace access become possible failure modes. | Treat daemon as a capability server: bind to localhost, use workspace/path allowlists, normalize and jail paths, allowlist agent commands, and redact sensitive output. |
| R5 | P0 | Agent process lifecycle needs a first-class manager. | /api/chat spawns multiple agent runtimes and streams output to the frontend. |
Zombie processes, cancellation gaps, orphaned tasks, inconsistent exit handling, and concurrent process conflicts. | Introduce a process/task manager with task state machine, cancellation, timeout, cleanup, exit code capture, signal handling, and concurrency limits. |
| R6 | P1 | server.ts remains oversized after partial modularization. |
Many domain registrars now live under apps/daemon/src/routes/ and shared HTTP primitives under apps/daemon/src/http/, but server.ts still owns substantial legacy routes and run orchestration alongside composition. |
Unrelated changes can still collide in the composition root, and large lifecycle paths remain difficult to isolate in tests. | Continue extracting touched domains into narrow route registrars and services while keeping server.ts as the composition root; avoid a broad mechanical rewrite. |
| R7 | P1 | Error handling is only partially centralized. | packages/contracts/src/errors.ts and apps/daemon/src/http/ provide typed errors and response adapters, while legacy handlers still contain local try/catch blocks and ad hoc JSON failures. |
Consumers can still receive inconsistent status/envelope semantics, and local catches can lose context or leave partial state. | Migrate touched routes to the shared error/response boundary, preserve named recovery paths, and add adapter-level mappings for domain failures. |
| R8 | P1 | Versioned SSE contracts need consistent end-to-end use. | packages/contracts now defines versioned chat/proxy SSE unions and the critique event union, but legacy producers and consumers can still emit or parse locally shaped events. |
A bypass can reintroduce disconnect, terminal-event, replay, and error-semantic drift despite the shared types. | Keep canonical event names/payloads in contracts, route producers and consumers through typed helpers, and pin replay/disconnect/terminal semantics with contract tests. |
| R9 | P1 | SQLite schema and migration lifecycle need stronger guarantees. | apps/daemon/src/db.ts owns local better-sqlite3 tables and migrations. |
Local user data upgrades can fail unpredictably; schema drift is hard to diagnose and recover. | Add explicit migration table, ordered forward migrations, startup migration checks, schema version logging, backup-before-migrate strategy, and migration tests. |
| R10 | P1 | Broad daemon coverage is not yet organized as an explicit test pyramid. | apps/daemon/tests/ now covers routes, DB behavior, agents, SSE, critique, media, connectors, config, and filesystem boundaries, but layer ownership and some lifecycle/migration gaps remain uneven. |
Large suites can still hide which boundary failed, while unmodeled migration or process-lifecycle paths retain regression risk. | Make the layers explicit: shared contract tests, route integration suites, service units, migration tests, canonical SSE tests, and mocked process-lifecycle tests. |
| R11 | P1 | Logging and observability are insufficient for local runtime debugging. | Agent execution involves long-lived tasks, subprocess output, filesystem state, and frontend SSE consumption. | User issues are hard to reproduce; failures lack correlated context. | Add structured logs with requestId, taskId, agentId, workspace, exit code, and duration; separate app logs from agent output; redact secrets. |
| R12 | P2 | Configuration, port, and readiness behavior remains only partially centralized. | Web proxies /api/* to the daemon, tools-dev coordinates the two listeners, and GET /api/health exposes basic process health but not full dependency readiness. |
Port conflicts, daemon-not-ready states, and mismatched environment variables can still break startup or distribution. | Centralize config resolution, enrich health with readiness checks, and make port selection plus UI fallback deterministic. |
| R13 | P2 | Cross-platform behavior is a recurring risk. | Daemon uses filesystem paths, SQLite native bindings, shell/process behavior, and signals. | macOS, Linux, and Windows/WSL can differ in path normalization, quoting, permissions, and process termination. | Use Node path APIs consistently, avoid shell string composition, isolate platform-specific process logic, and add CI coverage for supported platforms. |
| R14 | P2 | Framework migration can distract from core maintainability issues. | Current complexity is concentrated in FS/spawn/SSE/SQLite and module boundaries. | A framework rewrite can consume time while preserving the risky domain logic. | Keep Express for now; revisit Fastify only after TS, contracts, validation, tests, and modularization are in place and Express becomes a clear limiter. |
| R15 | P2 | Web/daemon boundary can erode over time. | Next.js has BFF capability and daemon has backend capability; future edits may blur ownership. | High-permission local runtime logic may leak into apps/web; deployment and security assumptions become unclear. |
Document and enforce ownership: web handles UI/BFF/proxy; daemon owns local runtime capabilities; shared code contains contracts and pure logic only. |
| R16 | P3 | Operational documentation is incomplete. | Local-first daemon behavior depends on ports, daemon-managed storage, agent CLIs, runtime logs, and recovery flows. | Onboarding and support costs rise; troubleshooting relies on oral knowledge. | Document daemon architecture, API/SSE contract, task lifecycle, storage contract index, agent dependency checks, and common recovery procedures. |
Optimization Dependencies
The optimization work should proceed in dependency order. Some items can run in parallel once their prerequisites are stable.
| Workstream | Status | Optimization | Covers | Depends on | Output |
|---|---|---|---|---|---|
| W1 | Completed | Confirm architecture and capability boundaries | R4, R15 | — | Written ownership rules for web, daemon, shared contracts, and dangerous local capabilities. See specs/current/architecture-boundaries.md. |
| W2 | Completed | Define API, SSE, and error contracts | R2, R7, R8 | W1 | packages/contracts now provides shared request/response types, SSE event unions, and error model helpers consumed by web and daemon. |
| W3 | Partial | Migrate project-owned code to TypeScript | R1 | W2 for highest-value shared types | TypeScript is the project-owned source default and the daemon package typechecks, but suppressions remain in core runtime files, newer brand/memory modules, and some tests. Remove them in owner-sized slices, then run pnpm --filter @open-design/daemon typecheck, pnpm typecheck, and pnpm guard. |
| W4 | Planned | Add runtime validation at daemon boundaries | R3, R4 | W2 | Schemas for HTTP requests, paths, agents, models, uploads, task IDs, and command args. |
| W5 | Partial | Modularize server.ts |
R6 | W2, W3, W4 | Domain registrars, typed route dependencies, and shared HTTP helpers now live under apps/daemon/src/routes/, server-context.ts, route-context-contract.ts, and apps/daemon/src/http/. Remaining work is to extract substantial legacy route/orchestration blocks from server.ts without mixing broad moves with behavior changes. |
| W6 | Partial | Introduce agent process/task manager | R5, R8, R11 | W2, W5 | apps/daemon/src/runtimes/runs.ts now provides an in-memory chat run service with run states, event replay, SSE streaming, cancellation, waiting, terminal cleanup, and exit metadata; critique also has an in-process run registry for interrupts. Remaining work is a unified agent process manager with explicit concurrency limits, stronger timeout/cleanup policy, and consistent lifecycle ownership across agent surfaces. |
| W7 | Planned | Strengthen SQLite migrations | R9 | W5 or a clear DB adapter boundary | Migration table, ordered migrations, startup checks, backup strategy, migration tests. |
| W8 | Partial | Build the daemon test pyramid | R10 | W2, W4, W5 | Daemon now has broad Vitest coverage under apps/daemon/tests/, including route, agent, DB, SSE, critique, live-artifact, connector, config, and filesystem behavior. Remaining work is to make the layers explicit: shared contract tests, route integration suites, service unit tests, migration tests, canonical SSE protocol tests, and mocked agent-process lifecycle tests. |
| W9 | Planned | Add structured logs and observability | R11 | W2, W6 | Correlated request/task logs, sanitized agent output, durations, exit status, and diagnostic context. |
| W10 | Partial | Harden config, port, and readiness behavior | R12 | W1 | Daemon exposes GET /api/health with basic { ok, version } health data. Remaining work is centralized config resolution, richer readiness checks, deterministic port behavior, and UI-visible daemon-not-ready handling. |
| W11 | Partial | Harden cross-platform behavior | R13 | W4, W6, W5 | Some process and path hardening exists, including shared platform command invocation and Windows command-line budget checks for agent CLIs. Remaining work is to formalize platform-specific process handling, path normalization rules, and supported-platform CI coverage. |
| W12 | Planned | Revisit HTTP framework choice | R14 | W2, W3, W4, W5, W8 | Evidence-based decision on whether Express remains adequate or Fastify provides clear net value. |
| W13 | Partial | Complete operational documentation | R16 | W1 through W11 as sections stabilize | Boundary and ownership documentation exists in AGENTS.md, apps/AGENTS.md, packages/AGENTS.md, and specs/current/architecture-boundaries.md. Remaining work is current-state daemon docs, API/SSE lifecycle docs, runbooks, troubleshooting guides, and recovery procedures. |
Recommended Execution Order
Phase 1: W1 -> W2 -> W3 -> W4
Phase 2: W5 -> W6 -> W7 -> W8
Phase 3: W9 -> W10 -> W11 -> W13
Phase 4: W12
The core principle is to reduce risk before changing framework foundations: establish contracts, types, validation, and module boundaries first; then evaluate whether Express remains the right transport layer.