Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Roadmap

Canonical sequencing for project-agent-manager (binary pam). Each phase delivers a working increment and ends at a gate expressed as commands to run or outcomes to observe — never as a vague quality adjective. Beyond Phase 0, dates are deliberately omitted: the contract here is ordering and gates, not scheduling.

Companion doc: 071-risks-and-open-questions.md (risk register, open questions, decision schedule).

1. Product frame (context only)

pam is a multi-host, central-orchestrated platform that combines hierarchical project/task management (Project → Milestone/Epic → Task → Subtask; board columns todo/doing/done/cancelled) with AI task orchestration driving subscription LLM CLIs (claude/codex/grok/gemini) inside tmux sessions on each execution host (dedicated socket pam, session names pam-<8hex>), live-observable from a web dashboard embedded in the orchestrator binary via rust-embed. A central orchestrator (pam serve) owns the API, dashboard, and PostgreSQL; workers (pam worker) on Unix hosts (macOS primary, Linux secondary — ADR-011) execute the sessions over outbound-only connections (ADR-012). Windows and mobile are excluded. Headless -p execution is excluded by design. Details live in 001-system-overview.md and 030-http-api.md / 040-cli.md — this file does not restate them.

2. Phase overview

Status legend: planned / in-progress / done / superseded.

3. Dependencies

graph LR
    P0["Phase 0 — scaffold + docs"] --> P1["Phase 1 — PM domain"]
    P1 --> P2["Phase 2 — tmux executor v1"]
    P1 --> P3["Phase 3 — web SPA v1"]
    P2 --> P4["Phase 4 — provider adapters"]
    P2 --> P5["Phase 5 — interactive attach"]
    P3 --> P5
    P5 --> P6["Phase 6 — desktop shell + packaging"]

In words:

  • Phase 1 requires Phase 0 (scaffold to build on).
  • Phase 2 requires Phase 1 (runs reference tasks/work items in the store).
  • Phase 3 requires Phase 1 (SPA renders PM data). It does NOT require Phase 2: the live-pane gate can be exercised against any session on socket pam, including one started by hand — though in practice Phase 2 will usually be available.
  • Phase 4 requires Phase 2 (adapters execute through the worker’s executor).
  • Phase 5 requires Phases 2 + 3 (worker sessions to attach to; SPA to host xterm.js).
  • Phase 6 requires Phase 5 (the shell wraps the full interactive product; the shell choice is settled — Tauri v2, ADR-006 — but is not exercised before then).

4. Phase details

Phase 0 — Foundation: docs, scaffold, CI (current)

Status: in-progress (started 2026-08-29).

Note (2026-08-29, later same day): the design pivoted to the multi-host topology — PostgreSQL backend, central orchestrator + per-host workers (ADR-012). Phase 0 deliverables (docs, compiling scaffold, CI) keep their shape; the store crate adopts PostgreSQL and the worker command appears at Phase 1/2 as written below.

Scope:

Gate (runnable, all must exit 0):

cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo build --workspace
cargo test --workspace

Plus observable outcomes: docs link/frontmatter checks pass; git log --oneline main shows the Phase 0 commits. Commits are local only — push is deferred to September 2026 (OQ-1 in the risks doc); the remote URL is unconfirmed until then.

Phase 1 — PM domain

Scope:

  • pam-core: domain types for the hierarchy (Project → Milestone/Epic → Task → Subtask) and statuses (todo/doing/done/cancelled); board column semantics (a move is a status change).
  • pam-store: sqlx migration 0001 implementing the PostgreSQL schema from the 002-data-model.md; Store trait; PgStore; MemStore gated behind a test-support feature.
  • pam-cli + pam-api: pam serve, pam migrate, pam project add/list/show/archive, pam task add/list/show/move/board/done — all commands through the central API (ADR-007).

Gate (runnable):

  • Against a PostgreSQL instance (ephemeral container acceptable): pam task addpam task listpam task movepam task done round-trips; pam task show reflects each transition.
  • Parity: the shared, parameterized Store test suite runs against both PgStore and MemStore and passes identically.
  • pam task board renders the todo/doing/done/cancelled columns with tasks placed by status.

Exit note: the pam binary/crate prefix freezes at Phase 1 exit (OQ-2); blocked-as-label and hierarchy depth defaults (OQ-6) must hold or be overturned by then.

Phase 2 — worker + tmux executor v1

Scope:

  • Worker agent: pam worker registers outbound with the orchestrator (worker token), heartbeats, and executes dispatched runs; pam host add/list/disable/enable manages the registry.
  • Spawn: tmux -L pam new-session -s pam-<8hex> with pam exec run <id> as the session’s foreground wrapper process; the wrapper fetches its run spec from the central API and spools signals across partitions.
  • Persisted session registry mapping runs to hosts and session names; reconciliation on worker start adopts known sessions and flags unknown pam-* ones; orchestrator marks hosts offline on heartbeat loss without force-failing their runs.
  • pam run start/list/stop/logs/pane and web-relayed attach; local CLI attach only for runs on the local host.
  • tmux safety 4-rules enforced in code, per the 010-tmux-executor.md: dedicated socket pam only (never the default socket); session-name regex ^pam-[0-9a-f]{8}$ validated before any tmux command; cleanup limited to own sessions on socket pam; send-keys allowlist.

Gate (runnable):

  • Start a run with a stub echo-CLI provider: pam run list shows it with its host, pam run pane shows its output, tmux -L pam list-sessions on that host shows exactly the pam-<8hex> session.
  • Crash path: kill -9 the worker, restart it; pam run list reconciles — every run appears exactly once (no duplicates) and every pam-* session on socket pam is accounted for by the registry or cleaned up (no orphans).
  • Partition path: stop the orchestrator mid-run, restart it; the wrapper’s spooled run.started/run.finished signals replay and the run record converges without manual repair.
  • Duplicate-delivery path: POST the same one-shot signal (e.g. run.finished) twice; the run record shows exactly one status transition and the events journal holds exactly one journalled event for it — signal delivery is idempotent, no phantom transition.
  • Stale-attempt path: POST a signal stamped with an attempt older than the run’s current runs.attempt (a dead incarnation’s late replay); afterwards both the runs row and the events journal are unchanged — no status write, no event emitted.
  • Safety path: with a decoy session present on socket pam under a non-conforming name, and an unrelated session on the default socket, pam run stop/cleanup leaves both decoys untouched (verify with tmux -L pam list-sessions and tmux list-sessions).

Phase 3 — web SPA v1

Scope:

  • Leptos scaffold under web/ (a Cargo workspace member); trunk build --release output web/dist embedded into pam-api via rust-embed.
  • axum serves the SPA, the HTTP API per 030-http-api.md, and an SSE endpoint streaming the read-only pane.
  • UI: projects list, board, task detail, live read-only pane.
  • Auth posture: bearer token over TLS (users/CLI) + separate worker token (OQ-7 superseded by ADR-012; see OQ-10) — no accounts.

Gate (runnable/observable):

  • cargo build --workspace succeeds with web/dist embedded: run the built binary with web/dist renamed away on disk — the SPA still serves (proves embedding, not static-file serving).
  • Browser at 127.0.0.1:PORT shows the board populated with real data and a live-updating pane from a running session (a session started via pam run start, or any session on socket pam created by hand).

Phase 4 — provider adapters

Scope:

  • Provider catalogs for claude/codex/grok confirmed + gemini optional if installed as TOML (argv templates, env mapping, hook config, version constraints), per the 020-provider-adapters.md.
  • Env normalization + proxy injection applied by the pam exec run wrapper pre-spawn: ANTHROPIC_BASE_URL / OPENAI_BASE_URL / GEMINI_BASE_URL plus HTTP(S)_PROXY.
  • Completion ladder in code: manual > wrapper journal > provider-native hooks POSTing /api/internal/runs/{id}/signals; the pane-idle heuristic is a UI hint only. Provider hooks ship behind a config flag (off by default).
  • Restart policy: bounded retries with backoff, configurable per provider.
  • pam provider doctor: CLI presence/version against catalog constraints, catalog validity, tmux >= 3.3.

Gate (runnable):

  • Two real providers each complete a scripted task end-to-end from pam run start to a recorded completion, with DISTINCT completion signals: one run’s completion captured via wrapper exit (journal), the other via a provider-native hook POST — the signal source is visible and different in the two run records.
  • pam provider doctor exits 0 on the dev machine.

Exit note: task-status auto-transition on a trusted signal stays config-gated and OFF by default unless OQ-5 is overturned by this gate.

Phase 5 — interactive attach

Scope:

  • WS attach endpoint + PTY relay: portable-pty hosting tmux -L pam attach for the target session.
  • Single-writer lease per run: acquire/renew/release; lease state visible via the API; clear rejection error for a second writer.
  • xterm.js client rendering bidirectional IO; read-only viewers multiplex the same outbound stream with no input path at the protocol level; activity feed UI.

Gate (runnable/observable):

  • Browser A attaches to a run and types; the keystrokes are visible in the tmux pane (verify via tmux -L pam capture-pane -p or by observing output) and stream to Browser B, which remains read-only.
  • While A holds the lease, an attach attempt from Browser C (or a re-attach attempt from B) is rejected with a clear lease error (observable HTTP/WS error) and no input flows from the rejected client.

Phase 6 — desktop shell + packaging

Scope:

  • Implement the Tauri v2 shell (decision accepted 2026-08-29 — monitoring thin client, never an execution node; comparison record in 050-ui-dashboard.md; formerly OQ-4, resolved). Shell targets macOS and Linux desktops, matching the execution node (ADR-011); the macOS build ships first and the Linux build follows the Linux execution-node verification (OQ-9).
  • Implement the chosen shell as a wrapper: sidecar daemon or linked core — the web UI codebase stays single-source.
  • cargo-binstall metadata; release polish (app icon, naming, packaging defaults).

Gate (runnable/observable):

  • The built .app (or dmg) launches on the dev Mac, starts or attaches to the same core, and passes the Phase 3 gate through the shell webview: board and a live-updating pane visible in the shell window.
  • cargo-binstall metadata is present and validates locally (dry run, no network publish required).

5. Deferred / backlog (explicitly out of scope for now)

6. Gate conventions

  • All commands run from the repository root on the dev machine (currently macOS — the primary worker platform per ADR-011; the secondary Linux target is verified per OQ-9 when a Linux environment is available).
  • From Phase 1 on, gates assume a reachable PostgreSQL (a local ephemeral container is acceptable) and a locally running orchestrator + worker (pam serve + pam worker on the same machine is the dev shape of the multi-host topology).
  • Where a gate is an observable outcome (browser, tmux pane), the observation is the verification; record the result by updating the phase status and last_verified in this frontmatter.
  • CI runs the headless subset of gates (fmt, clippy, build, test, docs checks). Browser- and tmux-interactive checks are verified manually at phase exit.
  • A phase flips to done only when every item in its gate has passed; partial completion keeps it in-progress with a note.

References