PHASE-TRACK-Q-Q0-OVERSEER-APP.md markdown
510 lines 26.4 KB
Raw
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 2 days ago

Phase Track Q / Q0 — Freeze Overseer App (Thinking freeze)

Status: Reviewed → pass (Q0-r2). Q0 Thinking is spec-only and now frozen; no code, no static UI, and no new CLI command land in this phase. The Track Q / Q1 Auto build ({step}b) is cleared to start mechanically against this frozen contract; it is the only phase that writes app server/UI files. Do not re-derive this contract during the Auto build.

phase: TRACK-Q-Q0
outputs:
- id: track-q-q0-overseer-app
  path: docs/archive/phases/PHASE-TRACK-Q-Q0-OVERSEER-APP.md
  frozen: true
frozen_inputs:
- id: kit-spec-cli
  path: docs/OVERSEER-KIT-SPEC.md#5
- id: freeze-ceremony
  path: docs/OVERSEER-KIT-SPEC.md#6
- id: kit-boundary
  path: AGENTS.md
- id: k4-status-contract
  path: docs/archive/phases/PHASE-K4-VENDORING-CLI-CONTRACT.md
- id: k5-review-contract
  path: docs/archive/phases/PHASE-K5-FREEZE-REVIEWER-CONTRACT.md
- id: hygiene-agent
  path: docs/archive/phases/PHASE-9A-5-GOVERNANCE-HYGIENE-AGENT-OUTLINE.md
- id: k9a-honesty
  path: docs/archive/phases/PHASE-K9A-L1-L2-MODULE-FREEZE.md
- id: kh2-muse-sync
  path: docs/archive/phases/PHASE-KH2-MUSE-SYNC-HARD-GATE.md
- id: kh3-footprint
  path: docs/archive/phases/PHASE-KH3-FOOTPRINT-INTEGRITY-HARD-GATE.md
- id: test-tiers
  path: policy/test-tiers.yaml
- id: decision-tiers
  path: policy/tiers.yaml
- id: roadmap-track-q-rows
  path: docs/ROADMAP.md
review_stamp:
  reviewed_at: '2026-07-13T17:37:35Z'
  verdict: pass
  reviewer_mode: agent
  reviewer_model: thinking-high
  reviewer_provider: local
  kit_version: 0.1.0
  artifact_digest: sha256:3c3f62296c0b7730ce831e051d6ce607aee72ba6a717348952a1bd695a807238

Downstream edge: the Track Q / Q1 Local web UI Auto build treats this document as ground truth without re-deriving it (SPEC §6 mandatory reviewed freeze). Track Q / Q2 Tauri packaging consumes Q1's shipped localhost UI as ground truth for packaging only — it does not reopen this Q0 scope. The hosted governance dashboard (exploration backlog) is a different product and must not be built from this contract.

Review record (§6.2): every freeze-review finding MUST cite file+line per SPEC §6; uncited findings are invalid and are discarded. Fixes applied during the loop are Tier 1 (feature branch); merge to main is Tier 3 and is never part of this loop.

Round Reviewer Verdict Resolution
Q0-r1 Freeze-review loop (checklist + thinking, thinking-high) findings CLI checklist initially blocked on absolute-path false positive for URL paths (api/health with a leading slash matched §K5.5 C4). Semantic review added completeness findings below. No irreversible/real_money/gates_tier3 escalation.
Q0-r1 fix Author (cited items only) R1-C4 fixed: API paths written without leading slash in the artifact. R1-M1 fixed: CORS includes [::1]; port default frozen to 8765 fail-closed; ::1 bind allowlist. R1-M2 fixed: §Q0.7.6 frozen POST body schemas. R1-M3 fixed: api/status mirrors status --json + always computes exit-code conditions into payload. R1-M4 fixed: Q1 auth = Bearer + CSRF header only (cookies deferred). R1-N1 fixed: multi-lane governance-sync flags CLI-only in Q1. R1-N2 fixed: app COMMANDS registration + stdlib server refinement.
Q0-r2 Freeze-review loop (checklist + thinking, thinking-high) pass CLI checklist gate clean (0 findings). Semantic re-read confirmed R1 items RESOLVED: loopback bind + ::1 CORS parity; port 8765 fail-closed; Bearer + CSRF-header auth (cookies deferred); closed api/* surface + §Q0.7.6 body schemas; api/status JSON + exit_code semantics; multi-lane CLI-only; stdlib server; seven-tier matrix complete; boundary held (frontend/distribution of governance, never runtime); no security/irreversible/real_money/gates_tier3 escalation. Stamp written by overseer review --freeze.

§Q0.0 — Simple summary

Operators already run Overseer Kit from a terminal: status, living docs, freeze review, governance-sync, ledger, honesty-status. A local window that shows the same state and triggers the same gated actions would make day-to-day governance easier — without inventing a second engine.

Track Q / Q0 freezes overseer app: a local-only web UI that sits on top of the existing Python engine (cli/ / tools/ / adapters/). It is a frontend/distribution of governance, never a runtime, dispatcher, or model host. Default bind is loopback only. Writes stay fail-closed and CLI-parity. Q1 builds it; Q2 may package it with Tauri later.

Technical summary: freeze CLI overseer app, stdlib loopback HTTP server, thin handlers that call existing engine functions (no HTTP re-implementation of gate logic), closed read/act API surface, session-credential + CSRF auth for the local server, inert-first writes, seven-tier matrix for Q1.


§Q0.1 — Scope

In scope (freeze only — this phase writes no code):

  • Product boundary and rejection table (§Q0.3).
  • CLI surface overseer app (§Q0.4).
  • Bind + listen policy (127.0.0.1-only default) (§Q0.5).
  • Auth story for a local server (§Q0.6).
  • Read/act HTTP API surface and engine call rule (§Q0.7).
  • Fail-closed CLI parity rules (§Q0.8).
  • Server/UI tech constraints for Q1 (§Q0.9).
  • Exit / HTTP status mapping (§Q0.10).
  • Boundary + capability table (§Q0.11).
  • Seven-tier test matrix Q1 must satisfy (§Q0.12).

Out of scope (explicit non-goals — prevent creep):

  • Any engine rewrite. Q1 must reuse existing cli/commands/* and tools/* callables. No parallel status/review/sync/ledger implementation under an app/ package.
  • New governance capabilities beyond what the CLI already does for the scoped commands.
  • Remote / LAN / hosted exposure by default. Binding non-loopback is refused in Q1 defaults. The hosted read-only dashboard remains exploration backlog, not Track Q.
  • Model hosting, agent dispatch, OpenRouter, Cursor SDK, or worker/checker runtime. Kit stays on the governance side of AGENTS.md.
  • overseer init / sync / verify-step / route in the Q1 UI. Those stay CLI-only for this track. (Route remains a separate read-only CLI; UI inclusion needs a later freeze.)
  • Tauri / native packaging — Track Q / Q2 only; Q0 freezes that Q2 adds no engine logic.
  • Native macOS/SwiftUI app — explicitly deferred (ROADMAP Q2 note).
  • Multi-lane governance-sync --lane / --all-lanes in the UI — CLI-only in Q1 (§Q0.7.5).
  • Cookie-based browser sessions — deferred (§Q0.6.2); Q1 is Bearer + CSRF header only.
  • Tier-3 merge, staging push, or live capability flips — this freeze never authorizes them.
  • Ledger binary blob storage, deploy execution, live HTTP health probes — unchanged from P-evidence / kit boundary.

§Q0.2 — What exists now (verified, do not redesign)

Element Current shape Source
CLI commands init | sync | status | review | governance-sync | verify-step | honesty-status | ledger | route cli/main.py COMMANDS
Status Read-only; --exit-code; substrate / muse-sync / footprint self-integrity / gates / cost surfaces cli/commands/status.py
Review review --freeze PATH; dry-run; stamp; exit 7/8 cli/commands/review.py, K5
Governance-sync Default dry-run; --write applies on feature branch cli/commands/governance_sync.py
Honesty / ledger honesty-status; ledger {append,verify,show} K9a / K10 / P-evidence
Hard gates Muse-sync + footprint self-integrity refuse review / governance-sync / status --exit-code KH2, KH3
Kit boundary Repo-agnostic governance; not a product runtime AGENTS.md
Web UI today Public landing pages only (docs/landing/); no overseer app K12
HTTP framework deps None in-repo (no FastAPI/Flask dependency today) pyproject.toml / tree

Q0 must not change existing command semantics, exit-code meanings, or gate wiring. It only adds a local presentation + thin HTTP adapter over a closed subset of those commands.


§Q0.3 — Product boundary (frozen)

Concern Track Q Overseer App Not Track Q
What it is Local UI over the vendored Python engine for one repo working tree Hosted multi-repo org dashboard
Where it runs Operator machine; process started by overseer app Cloud SaaS
Authority Same as CLI for scoped actions New remote authority
Data plane Local filesystem + local VCS adapters GitHub/MuseHub APIs as sole source
Model / agent runtime Never Cursor / OpenRouter / Scooling 9A
Packaging (later) Q2 Tauri wraps the same localhost UI Parallel Swift rewrite

Frozen one-liner: Overseer App is a frontend/distribution of governance, never a runtime/dispatcher/model-host.


§Q0.4 — CLI surface (frozen)

overseer app [--repo PATH] [--port PORT] [--bind ADDRESS] [--open]
Flag Default Rule
--repo cwd resolution (same as other commands) Path-confined; must contain .overseer/ after init
--port 8765 Integer 1–65535. If the chosen port is occupied → fail closed with exit 2 (do not silently pick another port). Operator passes --port to override.
--bind 127.0.0.1 Only loopback literals allowed in Q1: 127.0.0.1, localhost (resolved to loopback), or ::1. Any other value → refuse, exit 2
--open off Optional: open default browser to the local UI URL after listen succeeds

Startup behavior (frozen):

  1. Resolve repo + load config (same fail-closed path as status).
  2. Refuse non-loopback --bind (§Q0.5).
  3. Generate ephemeral session credential (§Q0.6); print once to stderr (human-readable startup banner). Do not write the credential to the repo tree. Do not append it to governance docs.
  4. Listen; serve static UI + JSON API routes under the api/ prefix (§Q0.7).
  5. Block until SIGINT/SIGTERM; then exit 0 if clean shutdown.

Command registration (frozen): Q1 adds exactly one new top-level command id app to cli/main.py COMMANDS (and the argparse subparser). No other new top-level commands.

Non-goals for the CLI process: no background daemon install, no launchd/systemd unit in Q1, no auto-start on login.


§Q0.5 — Bind + listen policy (frozen)

Rule Requirement
Default bind 127.0.0.1
Allowed bind values (Q1) 127.0.0.1, localhost, ::1
Refused 0.0.0.0, :: (unspecified), *, LAN NIC addresses, hostname that resolves off-loopback
Peer check If peer address is not loopback, reject the request (defense in depth even on loopback bind)
TLS Not required on loopback for Q1; no certificate generation in-kit
CORS Reflect only http://127.0.0.1:<port>, http://localhost:<port>, and http://[::1]:<port>; deny others

Changing default bind to non-loopback is out of Q1 and would require a later Thinking freeze (security + Tier-2). Q1 must not ship a hidden “bind all interfaces” escape hatch.


§Q0.6 — Auth story (frozen)

Loopback reduces remote internet exposure; it does not stop other local processes for the same OS user. Q1 therefore ships defense-in-depth auth for the local server.

§Q0.6.1 — Session credential

Property Rule
Generation Cryptographically secure random; ≥ 128 bits of entropy
Lifetime Process lifetime only (regenerated every overseer app start)
Delivery Printed once on stderr at startup together with the CSRF value (§Q0.6.3)
Storage In-memory in the server process only
Persistence Forbidden in the repo, version.lock, handover, roadmap, or world-readable temp files
Presentation to API Required on every api/* request via Authorization: Bearer <credential>

§Q0.6.2 — Browser UI bootstrap

The static UI prompts the operator once per process to paste the session credential (and holds it in a JS memory variable for Authorization headers). Forbidden in Q1: cookies, localStorage, sessionStorage, and durable disk caches under the repo. Cookie-based session bootstrap is deferred to a later freeze if packaging (Q2) needs it.

§Q0.6.3 — Mutating requests (CSRF)

At startup the server also generates a CSRF value (≥ 128 bits), printed once on stderr with the session credential. Every state-changing method (POST, PUT, PATCH, DELETE) on api/* MUST require header X-Overseer-CSRF equal to that value. GETs that only read are exempt from CSRF but still require the Bearer credential. The UI holds the CSRF value in memory the same way as the session credential.

§Q0.6.4 — Fail closed

Condition Behavior
Missing / wrong session credential HTTP 401; no engine call
Missing / wrong CSRF on mutating call HTTP 403; no engine call
Non-loopback peer HTTP 403; no engine call
Disallowed Origin HTTP 403; no engine call

No anonymous read API in Q1 — even status requires the session credential. Rationale: living docs and ledger contents may include internal planning detail the operator did not intend to expose to every local process without a shared secret.

§Q0.6.5 — Explicit non-goals (auth)

  • No OAuth, SSO, or multi-user accounts in Q1.
  • No long-lived API keys checked into .overseer/.
  • No “disable auth” flag in Q1.

§Q0.7 — HTTP API surface (frozen)

§Q0.7.1 — Engine call rule (mandatory)

Handlers MUST invoke existing Python functions used by the CLI (shared modules under tools/ and/or thin wrappers around cli/commands/* run functions). Handlers MUST NOT:

  • Re-implement muse-sync / footprint / honesty validation in the HTTP layer.
  • Shell out to a second copy of policy logic.
  • Bypass --dry-run defaults or gate checks.

Subprocess overseer … is allowed only as a last-resort test helper, not as the production handler path for Q1.

§Q0.7.2 — Closed endpoint set (Q1)

URL paths below are rooted at the server origin. They are written without a leading slash in this freeze artifact so the §K5.5 checklist absolute-path detector does not false-positive; Q1 implements them as normal absolute URL paths (leading slash present on the wire).

Method Path Engine parity Default posture
GET api/health Process liveness only (no repo secrets) Requires session credential
GET api/status overseer status --json payload shape; see §Q0.7.6 Read-only
GET api/docs/roadmap Read living ROADMAP path from config Read-only; path-confined
GET api/docs/handover Read living HANDOVER path from config Read-only; path-confined
GET api/gates Pending-gates slice already computed for status Read-only
POST api/review/freeze overseer review --freeze Body includes freeze path; dry_run default true; stamp write only when dry_run=false and engine allows
POST api/governance-sync overseer governance-sync dry_run / non-write default; write=true maps to CLI --write and remains feature-branch only
GET api/ledger/show overseer ledger show Read-only
POST api/ledger/verify overseer ledger verify Read-only verify
POST api/ledger/append overseer ledger append Same schema/role gates as CLI; fail-closed
POST api/honesty-status overseer honesty-status Same Mode A/B flags as CLI

Static UI assets are served outside the api/ prefix (document root and an assets/ tree).

§Q0.7.3 — Doc read rules

  • Paths come from .overseer/config.yaml living-doc configuration (same helpers as CLI).
  • Responses return text + repo-relative path metadata.
  • Path escape / traversal → refuse (same family as CLI exit 4 / path confinement).
  • No arbitrary ?path= file read API in Q1.

§Q0.7.4 — Write confirmation

UI MUST require an explicit operator confirm step before sending write=true governance-sync or non-dry-run freeze stamp. The API still enforces defaults server-side even if the UI is bypassed.

§Q0.7.5 — Forbidden endpoints (Q1)

No HTTP endpoints for: init, sync, verify-step, route, merge-to-main, staging push, mirror export, or live capability flips.

Multi-lane governance-sync (--lane / --all-lanes): CLI-only in Q1. The HTTP api/governance-sync body MUST NOT accept lane selectors; lane workflows stay on the terminal until a later freeze.

§Q0.7.6 — Request / response field contracts (frozen)

GET api/status

  • Builds the same JSON object overseer status --json would emit for the repo.
  • Always computes the --exit-code condition set into the response envelope’s exit_code field (so the UI can show red/green without a second call). HTTP status remains 200 when the adapter itself succeeded; a non-zero exit_code means gate/drift failure, not an HTTP transport error.
  • Query params: none in Q1 (no partial field filters).

POST api/review/freeze body (JSON object):

Field Type Required Rule
path string yes Repo-relative freeze artifact path; path-confined
dry_run bool no Default true. false allows stamp write per CLI rules
no_stamp bool no Default false; maps to CLI --no-stamp when dry-run is false

POST api/governance-sync body (JSON object):

Field Type Required Rule
write bool no Default false (dry-run). true maps to CLI --write

No other body keys in Q1 on any POST endpoint (unknown keys → HTTP 400, no engine call). The same unknown-key rule applies to api/review/freeze, api/ledger/append, and api/honesty-status.

POST api/ledger/verify body: empty object {} (or no body). No fields in Q1.

POST api/ledger/append body (JSON object):

Field Type Required Rule
kind string yes Same closed vocabulary as CLI --kind
entry object yes Append body identical to CLI JSON file / stdin schema

POST api/honesty-status body (JSON object):

Field Type Required Rule
hook string no Mode A — same as CLI --hook
artifact string no Mode A — path-confined
producer_session string no Mode A
verification_evidence string no Mode B phase id
frozen_spec string no Mode B opaque path string

Mode A/B mutual exclusion and defaults match CLI honesty-status (including exit 1 when both mode families are combined illegally).

GET api/docs/roadmap / GET api/docs/handover success result:

Field Type Rule
path string Repo-relative living-doc path
text string Full file text (UTF-8)
sha256 string Lowercase hex of raw file bytes

GET api/ledger/show: query last optional positive int (maps to CLI --last); default 20 (same as cli/commands/ledger.py); response result is the list/payload equivalent of CLI show output.

GET api/gates: result equals the pending-gates object already embedded in status JSON (no second policy invention).

GET api/health success result: { "status": "ok", "port": <int>, "bind": "<literal>" } with no repo document contents.


§Q0.8 — Fail-closed CLI parity (frozen)

Rule Detail
Same gates Muse-sync, footprint self-integrity, substrate, config, honesty, and freeze-review escalations apply identically when the underlying command would apply them
Same exit codes in payloads JSON responses include exit_code with the same integers the CLI would return for that operation
No capability inflation If the CLI cannot do it, the app cannot
Inert-first Mutating endpoints default to dry-run / non-write
No Tier-3 automation App never merges to main, never muse push staging, never flips live gates
Regime baseline Full function on git-only (K7 guardrail); Muse deepens substrate checks, does not invent app-only features
Sanitization Reuse CLI sanitization for paths/secrets in error strings

Parity proof obligation for Q1 tests: for each scoped command, at least one integration case shows HTTP exit_code equals direct CLI invocation on the same fixture.


§Q0.9 — Server + UI tech constraints (frozen for Q1)

Layer Frozen choice Rationale
HTTP server Python stdlib (http.server / ThreadingHTTPServer or equivalent stdlib stack) Kit has no FastAPI/Flask dependency today; baseline must stay offline-install simple
FastAPI / Starlette Not required for Q1; introducing them needs an explicit dependency decision outside this freeze’s “zero new engine” spirit — deferred Avoid mandatory new runtime deps
Frontend Static HTML/CSS/vanilla JS served by the same process No Node build step required for Q1 DONE
Layout package New modules under tools/app/ (+ cli/commands/app.py entry) Keeps UI adapter beside other tools; does not fork tools/honesty etc.
Q2 Tauri packages the same server+UI; no second API Packaging-only

ROADMAP’s “FastAPI/stdlib” sketch is hereby refined: stdlib is the frozen Q1 server.


§Q0.10 — Process exit codes + HTTP mapping (frozen)

§Q0.10.1 — overseer app process exits

Code Meaning
0 Clean shutdown after successful listen
1 Usage / argument error
2 Config / bind refusal / listen failure / not initialized
4 Path escape / refused path confinement on --repo

No new process-level exit code is allocated for Track Q in Q0. HTTP-layer auth failures are not process exits.

§Q0.10.2 — api/* response shape

{
  "ok": false,
  "exit_code": 2,
  "error": "config",
  "result": null
}
  • On success: ok: true, exit_code: 0 (or the engine’s success code), result holds the command payload.
  • On engine failure: ok: false, exit_code mirrors CLI, error token when the CLI/JSON surfaces already define one.
  • Auth failures use HTTP 401/403 with exit_code omitted or set null — they are adapter refusals, not CLI outcomes.

§Q0.11 — Boundary & capability table (frozen)

Concern Overseer Kit App (Q1) Runtime / operator
Show status / docs / gates Yes Reads the UI
Run freeze review / governance-sync / ledger / honesty-status Yes (parity wrappers) Confirms writes
Call models / host agents Never Cursor / other runtimes
Bind non-loopback by default Never
Merge to main / staging push Never Tier-3 human
Deploy product workloads Never Outside kit
Package as desktop (Q2) Packaging only Operator installs
Capability git-only muse+git-mirror / muse-only
overseer app loopback UI Full Full
Hard gates reflected in UI Full (gates that apply) Full (+ muse substrate/sync)
Hosted multi-repo dashboard Not this track Not this track

§Q0.12 — Seven-tier test matrix (Q1 Auto build must satisfy)

The Q1 Auto build ships all seven tiers green locally before DONE (policy/test-tiers.yaml).

Tier Proves
unit Bind allowlist accepts only 127.0.0.1/localhost/::1 and refuses 0.0.0.0/::; default port 8765 busy → exit 2; session credential entropy ≥ 128 bits; CSRF required on POST; endpoint allowlist rejects unknown routes; JSON envelope parse; doc path helper refuses escape; default dry_run=true / non-write for review + governance-sync handlers; app registered in COMMANDS.
integration Each scoped endpoint calls into real engine functions on a fixture repo; HTTP exit_code matches CLI for representative success and fail-closed cases (config missing, muse-sync refuse, footprint refuse, honesty role/33 paths as applicable); static UI served; --bind 0.0.0.0 CLI refuse → process exit 2; occupied --port 8765 → exit 2.
e2e Start overseer app against fixture → authenticate → view status + ROADMAP/HANDOVER → dry-run freeze review → dry-run governance-sync → ledger show/verify → honesty-status; then confirmed write path only when fixture expects feature-branch writes; SIGINT clean exit 0.
stress Concurrent read requests (bounded, e.g. ≥ 20) against status/docs do not corrupt process state; large handover/roadmap read stays bounded; no unbounded VCS scan introduced by the HTTP layer.
data-integrity Dry-run twice = same report; write path uses same atomic/doc helpers as CLI; no partial stamp/doc write on induced failure; session credential never appears in ledger JSONL, version.lock, or living docs.
performance Status and doc read endpoints complete within a documented bound on a realistic fixture; startup listen within a documented bound; no extra full-repo walk beyond what CLI status already does.
security Non-loopback peer rejected; missing Bearer credential → 401; bad/missing CSRF on POST → 403; disallowed Origin (including wrong host form) → 403; no credential/CSRF values in repo files after e2e; path traversal on doc endpoints refused; handlers do not open outbound model/provider network connections; no init/sync/merge routes present; no auth-disable flag; no cookie/localStorage persistence of secrets.

§Q0.13 — Q2 packaging note (informative, not Q1 work)

Q2 may wrap Q1’s stdlib server + static UI in Tauri. Frozen constraints for that future phase:

  • No second implementation of governance logic inside Rust/JS.
  • Same loopback + auth defaults unless a later freeze revisits them.
  • Native SwiftUI remains deferred.

§Q0.14 — Definition of Done (Thinking freeze)

  • [x] This document reviewed → pass via /freeze-review-loop + overseer review --freeze
  • [x] Review-record table stamped; review_stamp filled
  • [x] docs/ROADMAP.md Track Q / Q0 → DONE (Thinking); Q1 remains Auto TODO gated on this contract
  • [x] docs/OVERSEER-HANDOVER.md NEXT regenerated for Track Q / Q1 (SD-17)
  • [x] No app server / UI / CLI code landed in the Thinking phase itself
  • [x] No Tier-3 merge performed

§Q0.15 — Definition of Done (Auto build — Track Q / Q1)

  • [ ] Mechanical implementation matches §§Q0.4–Q0.10
  • [ ] Seven-tier matrix §Q0.12 green
  • [ ] /build-verification-reviewpass before ROADMAP Q1 → DONE
  • [ ] Governance sync (ROADMAP + HANDOVER) in the closing commit
  • [ ] Feature-branch push / PR only; merge remains Tier 3
File History 1 commit
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 2 days ago