* docs(b3-3): mark B3-3 in progress and record HP-3's merge SHA HP-3 (#1461) merged as `52601114`; B3-3 (lifecycle extraction into `Server/internal/app/`) starts on `feat/b3-3-lifecycle`. Status line only — no step-table or scorecard edits. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011EvaP8XtuTJD86eSeuJrce * refactor(b3-3): move the process lifecycle into Server/internal/app (pure move) Every run* block, the healthcheck CLI, the banner and disk helpers, the seq seeding, the bind-retry listener and the restart coordinator move out of `Server/main.go` into a new `Server/internal/app` package, verbatim. `main.go` keeps the CLI dispatch, the log sinks, the `version` symbol `-ldflags` names and the restart handoff, and calls `app.Run`. Behaviour-neutral. The only substitutions are the package clause, `run` -> `Run` and `runHealthcheckCLI` -> `RunHealthcheckCLI` (the two entry points main() calls), the five restart-coordinator identifiers main() still names (`RestartCoordinator`, `NewRestartCoordinator`, `RestartBackstopDelay`, `PerformRestartHandoff`, `Disarm`), and `version` becoming a parameter of `Run`/`runServeAndWait` instead of a package-level var — it has to stay in package main because `-X main.version` is what the Makefile, `release.yml` and the Dockerfile inject. Normalised-diff proof (HP-1's shape): undoing those substitutions over the whole Server diff and running `sort | uniq -u` leaves 45 unpaired lines, all of them comment prose or the new import — no code line is unpaired. `DBImportAllow` swaps its `main.go` row for the four `internal/app` files that now own the handle (all `boundary`); `docs/architecture/server-boundaries.md` is regenerated from it (50 -> 53 importers, boundary 7 -> 10; the summary table's stale 6 is corrected to match the generated line). Full gate green: four tag variants, vet, `go test -race ./...` with coverage (aggregate 80.1%, unchanged), coverage floor, `-tags deadlock ./ws/`, golangci-lint v2.11.3 (0 issues), genprotocol/sqlc/gendocs drift, check:docs, check:hygiene. `TestAuthCharacterization` green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011EvaP8XtuTJD86eSeuJrce * refactor(b3-3): one App with one composite close, replacing run()'s defer stack `type App` threads the dependencies through fields instead of run() locals. Every stage registers exactly one close step as it comes up, in start order, and `App.Close(ctx)` walks them backwards — so there is a single teardown path, taken on a failed start, a serve error and a clean shutdown alike, where run() had a LIFO `defer` stack and an early return that skipped whatever it had not reached. `main.go` is 1,019 -> 99 lines: the CLI dispatch, the log sinks, the restart handoff, and `cfg := app.LoadConfig(...); a := app.New(cfg, ...); a.Run(ctx)`. The three ordering facts the inventory records are preserved, and are now what the reverse walk is FOR rather than emergent from where a `defer` happened to sit: the audit writer and event persistence both stop before `database.Close`, and the hub's GracefulStop runs on every return from Run so a supervised LiveKit process is never orphaned (OC-0027). Test-first. Three RED rows, each with a negative control on this branch: | Property | Mutation that must fail it | Result | | ----------------------------------------------------- | -------------------------------------- | ------ | | close order is the reverse of start order | walk the closers forward | FAIL | | first error returned, every later close still runs | return on the first error | FAIL | | hub stops when a stage after the router fails | skip teardown on a failed start | FAIL | Deliberate, documented changes that come with the contract: * `Run(ctx)` is real: bgCtx and the serve context both descend from it, so cancelling the caller's context stops the server the way a signal or a restart request does (`context.AfterFunc` joins the coordinator's context to it). * the four stop steps that used to build a fresh `context.Background()` with their own 5s cap now take Close's budget as their parent, so a wedged step cannot push teardown past the 30s the operator was told about; * `database.Close`'s error is reported instead of discarded; * the ACME start moves one stage later, after the maintenance loop, which is what makes "reverse of start" equal the order run()'s explicit shutdown call used to impose by hand (drain in-flight HTTP handlers first); * `internal/app/app.go` gains a `DBImportAllow` row; the inventory doc is regenerated (54 importers, boundary 11). Full gate green, aggregate coverage 80.2% (floor 79.8%). `TestAuthCharacterization` green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011EvaP8XtuTJD86eSeuJrce * refactor(b3-3): the hub has one owner — construction moves to internal/app `api.NewRouter` gains an `api.Runtime` parameter and stops returning a hub. `app.StartRuntime` (`Server/internal/app/hub.go`) now builds the rate limiter, the service layer and the hub, applies every pre-Run setter and starts the dispatch goroutine — the `ws.NewHub` call that was at `router.go:106` and the plugin and LiveKit setters that were at `:325-360`. Before this, the hub had two owners: the router built and wired it, and `main.go` set the event persister and the event store after `NewRouter` returned. Both now sit inside `internal/app`, which is what gives B3-4 one place to turn the required setters into validated `HubOptions`. The limiter and the service layer move with the hub because it needs the SAME instances — the limiter persists auth lockouts and the services hold the permission cache the hub invalidates, so a second copy of either would silently split that state. `Runtime` carries them plus `VoiceEnabled`, which is the `lkErr == nil` guard the voice routes were already mounted behind; `routerVoiceRoutes` keeps only the mounting half, `routerPluginWiring` becomes `app.wirePlugins`, and the LiveKit client and companion process are built by `app.startVoice` with its OC-0019 fail-closed ordering unchanged. The hub is its own lifecycle stage now, started before the router, so `App.Close` stops it through the "hub" step exactly as before. Call sites updated at the call site only, wiring with no assertion changes: six `api_test` files and `cmd/gendocs`. `gendocs` produces a byte-identical route index (its drift check is part of the gate). Full gate green: four tag variants, vet, `go test -race ./...` with coverage (aggregate 80.2%), coverage floor, `-tags deadlock ./ws/`, golangci-lint v2.11.3 (0 issues), genprotocol/sqlc/gendocs drift, check:docs, check:hygiene. `TestAuthCharacterization` green. `internal/app/hub.go` gains a `DBImportAllow` row; the inventory doc is regenerated (55 importers). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011EvaP8XtuTJD86eSeuJrce * test(b3-3): fail every lifecycle stage in turn and assert what teardown releases `Server/internal/app/lifecycle_failure_test.go` is the failure-injection report the B3 exit gate asks for. Each of the fourteen stages `App.start` brings up is made to fail in turn, and every row asserts the same four properties: the returned error names the stage, no goroutine is left running (`goleak`), the database handle is closed so the SQLite process lock is released for the successor a restart handoff is about to start, and the listener is not left bound so that successor can take the port. The table is generated from `App.stages()` rather than written out, so a stage added later is covered the day it is added. Two rows are not injected. A real out-of-range port drives the genuine listener-bind failure (the OC-0027 path). And a run that is cancelled while actually serving is the control: the same four properties on the path where nothing fails, with a nil error — so the injected rows are not passing merely because something went wrong. Negative controls on this branch: | Assertion under test | Mutation applied | Result | | --------------------------------- | ------------------------------- | -------------- | | the database handle is closed | drop the `database` close step | FAIL, 11 rows | | the hub's dispatch loop is stopped| drop the `hub` close step | FAIL, 12 rows | Green under `go test -race ./internal/app/`. Full gate green, aggregate coverage 80.2% (floor 79.8%), `internal/app` 66.1%. `TestAuthCharacterization` green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011EvaP8XtuTJD86eSeuJrce * docs(b3-3): evidence block, after-state lifecycle rows, status line and step row Plan §B3-3 gains its evidence block: pre-squash SHA per numbered item with the gate and `TestAuthCharacterization` result for each, `main.go`'s 1,019 → 99 lines, the normalised-diff proof for the pure move (the exact command and its 45 unpaired lines, all comment prose or the new import, broken down per file), the composite-close negative controls, the failure-injection table (sixteen rows × four assertions), the hub-ownership before/after, the build-and-packaging check, the gate list and the coverage figures. `docs/architecture/server-boundaries.md`'s hub lifecycle inventory keeps its before-state tables and gains the after-state: where each of the seven setters is called now, and the fifteen-stage start list with the close step each registers, plus why the resulting close order is what makes the three ordering facts hold by rule rather than by `defer` placement. One correction the check turned up and the evidence records: a plain `go build .` from `Server/` produces a binary named `Server`, not `chatserver` — that comes from the module path and is unchanged by B3-3. Every packaging path (`Makefile`, `Dockerfile`, `release.yml`, all untouched) passes `-o` explicitly, and `-X main.version` still resolves. Status line, step-table row and the `docs/plans/README.md` B3 row point at PR #1464; the squash SHA lands here at merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011EvaP8XtuTJD86eSeuJrce * fix(b3-3): keep the background workers alive through the HTTP drain Codex (P2) caught a defect in B3-3's own new `Run(ctx)` contract: `bgCtx` was derived from the caller's context, so cancelling that context stopped the event persister, the audit writer and the maintenance loop immediately — before `Close` ran its HTTP-first drain. That drain exists precisely so in-flight handlers' broadcasts still reach a live hub and event persister and their audit records still reach a live writer; with the consumers already gone, both are dropped. It also made caller-context shutdown behave unlike the SIGTERM and restart paths, which cancel only the serve context. `run()` had this right for free by rooting `bgCtx` at `context.Background()`. `main.go` passes `context.Background()`, so no released build was affected — the defect is in the new contract this PR introduces. `context.WithoutCancel(ctx)`: `bgCtx` inherits the caller's values but not its cancellation. Cancelling ctx still stops serving, because the serve context descends from it in `startSignals`; when the background work stops stays `Close`'s decision, which is what the ordering rule promises. Test-first. `TestAppRun_CallerCancel_KeepsBackgroundWorkersAliveThroughTheDrain` records `bgCtx.Err()` as each close step runs — a new test-only `onCloseStep` seam makes the teardown walk observable — and requires bgCtx still live at `signals`, `http`, `maintenance` and `audit-writer`, and already cancelled by `database` (the `event-persistence` step is what cancels it and joins the pruner). RED on all four rows before the fix; the negative control, restoring `context.WithCancel(ctx)`, fails it again. Full gate green on the merged tree, including `dev`'s new `errorlint`, `exhaustive` and `durationcheck` linters: golangci-lint v2.11.3, 0 issues. Aggregate coverage 80.2% (floor 79.8%). `TestAuthCharacterization` green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011EvaP8XtuTJD86eSeuJrce --------- Co-authored-by: J3vb <dragon613gaming@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
18 KiB
Plan index
Closes G-04. Historical plans are kept at their existing paths — links from audits and commit messages must keep resolving — so status is recorded here rather than by moving or rewriting them.
A plan's own header can drift out of date after its table is updated in place. Where that has happened it is called out below, and this index is the authority.
Active — these drive current work
| Plan | State |
|---|---|
| beta-product-requirements-2026-08-23 | Approved beta scope, frozen. 57 BPR-* requirements. |
| repo-health-roadmap-2026-08-23 | Phase order and gates, B0–B10. B0, B1 and B2 complete (HP-0, HP-1 and HP-2 all accepted); B3 is next — opens with the layout-refactor first slice. B3–B10 not started. Amended 2026-08-28: dated lines in B3–B10, a "Phase execution pattern" section, and a truthful status header. Amended 2026-08-29: B3/B7/B9 lines binding the layout-refactor supplement below; current slice updated for B2-2. |
| repo-health-issue-register-2026-08-23 | 88 planning rows. Public-safe; not a replacement for the ledger. |
| beta-requirements-traceability-2026-08-23 | Requirement → phase → evidence map. No row is release-qualified. |
| b0-baseline-2026-08-25 | Supersedes the roadmap's "current evidence snapshot." B0 measurements and dispositions. |
| b1-repository-foundation-2026-08-25 | B1-0 through B1-8 all done. B1 execution plan. Re-verifies every RL-* claim against HEAD; several are refuted. |
| hp-0-scorecard-2026-08-25 | HP-0 accepted 2026-08-25. The single baseline-acceptance artifact. Part-closes R-08. |
| hp-1-scorecard-2026-08-27 | HP-1 accepted 2026-08-27. Structural-diff proofs for the flatten and module rename, plus the B1 exit gate. |
| b2-protocol-trust-compat-2026-08-28 | B2 complete — HP-2 accepted 2026-08-29. B2-0, B2-1, B2-8 done 2026-08-28; B2-2 (B2-3/B2-4 folded in), B2-5, B2-6, B2-7, B2-9 done 2026-08-29. Scorecard below. |
| hp-2-scorecard-2026-08-29 | HP-2 accepted 2026-08-29. Seven questions answered with commands; B2 exit gate, nine conditions met (1 at the slim epoch scope, 4 with one E2EE gap disclaimed). Owner follow-ups that do not gate B3: BPR-051 reader line, SEC-01/SEC-04 advisory IDs. |
| b3-server-architecture-guardrails-2026-08-29 | B3 in progress from 2026-08-29. Execution plan: B3-0 inventory → B3-1/B3-2 auth slice → HP-3 → lifecycle, hub options, ws split, families; guardrails and the alpha dataset beside the slice. B3-0 (inventory + db-import-boundary rule) and B3-1 (auth characterization) merged 2026-08-29; B3-2 (auth vertical slice) merged 2026-08-30; B3-9 (five of the six B3-tagged findings; OC-0323 rides B3-8) merged 2026-08-30; HP-3 accepted 2026-08-30; B3-3 (lifecycle extraction into Server/internal/app/, one composite close, hub construction out of api.NewRouter) is PR #1464, opened 2026-08-30 — B3-4 next. |
| hp-3-scorecard-2026-08-29 | HP-3 accepted 2026-08-30 by the owner. Five questions on the auth vertical slice answered with commands: frozen set green at every pre-squash SHA, api db importers 12 → 10, B2 contracts unchanged, the pattern written as D4 in server.md, guardrails as they exist. |
| b3-bench-baseline-2026-08-30 | Recorded 2026-08-30, not gated. The six B3-6 Benchmark* through benchstat at ec8ef24a, produced by make bench-baseline. Nothing in CI reads these numbers; the performance gate is B6's. Regenerating writes a new dated file — replace this row and delete the superseded document, so only the newest baseline is kept. |
| audit-2026-08-19-remediation | Phases 1–6 done 2026-08-20; phase 7 pending. Its header still reads "in progress 2026-08-19" — stale; the phase table is correct. |
Partially implemented
| Plan | State |
|---|---|
| bug-detection-improvements | Tier 1a (make fuzz) and Tier 2 (five ESLint rules) shipped 2026-08-08. Remaining tiers open. |
Design only — not implemented
| Plan | State |
|---|---|
| developer-experience-layout-refactor-2026-08-29 | Draft, not started. Implementation supplement bound into the roadmap 2026-08-29: B3 workstream 17 (Phases 1–3, auth slice → HP-3), B7 workstream 16 (Phases 4–6), B9 workstream 11 (CSS split). Nothing starts before HP-2. |
| slash-commands | Design only. No implementation; not in beta scope. |
Shipped — kept for history, do not use as current status
| Plan | Shipped |
|---|---|
| audit-2026-07-19-decisions | Decisions recorded; greenlit items implemented through 2026-07-23. |
| channel-visibility-unification | 2026-07-20 (D9), re-verified 2026-08-04. |
| v2-dispatch-migration | 2026-07-20 (D10), re-verified 2026-08-04. |
| tauri-capability-narrowing | 2026-07-20, re-verified 2026-08-04. |
| http-tofu-proxy | 2026-07-19, re-verified 2026-08-04. |
| permission-middleware-consolidation | 2026-07-23 (D13), re-verified 2026-08-04. |
| security-hardening-remediation | 2026-07-23, re-confirmed 2026-08-04. |
| security-scan-2026-07-22-remediation | All 8 findings F1–F8 closed, verified 2026-08-04. |
| sqlc-adoption | Shipped, verified 2026-08-04. |
| discord-parity | Phases 1–6 complete, verified 2026-08-04. Phase 1's table reads as a gap list but every row shipped. |
| infrastructure-roadmap | 2026-08-15, with two recorded leftovers (TOTP persister seam; published capacity numbers). |
Where status actually lives
Planning documents are not trackers. Do not read a defect count out of one.
| Concern | Source of truth |
|---|---|
| Defect status | .superpowers/findings-ledger.json (FINDINGS.md is rendered from it) |
| Security-sensitive defects | Private GitHub Security Advisories |
| Product scope | beta-product-requirements-2026-08-23 |
| Phase order and gates | repo-health-roadmap-2026-08-23 |
| Current measured baseline | b0-baseline-2026-08-25 |
Ledger at 2026-08-30: 320 fixed / 54 open / 3 declined / 1 duplicate = 378
(B3-9, PR #1454, closed the five B3-tagged records).
All 38 open records still resolved to a live file:line at
5cc0888964e26276d1aca145e83270a2c1b9febd when that sweep was run — it was a
manual pass, not something a command reproduces. What the tooling does check:
node .superpowers/render-ledger.mjs --check # the ledger's schema is valid
node scripts/check-doc-counts.mjs # documents agree with it, and
# FINDINGS.md is not stale
Adding a plan
- Give it a
**Status:**line with a date, and update that line — not only the phase table — when it changes. - Add a row here. A plan absent from this index has no recorded status.
- Mark a superseded plan here; leave it at its path so existing links resolve.