Decision Log
July 1, 2026 · View on GitHub
Status: draft Date: 2026-06-12
This log records product and architecture decisions for striatum.
Decisions are made through interview rounds and updated as the shared
understanding sharpens.
Status values:
proposed
accepted
deferred
rejected
superseded
Supersession rule: use superseded when a later accepted decision or RFC
replaces the earlier artifact's operational rule. Do not delete the old
artifact; it remains provenance. For partial supersession, keep the status
that reflects the live part and name the successor in the decision, reason,
or consequences. RFC status changes must be reflected both at the top of the
RFC file and in docs/rfcs/README.md.
Decision-id correction note: D119-D123 are correction IDs assigned on 2026-05-20 to historical rows that had accidentally reused D080-D084. The original row text is preserved; only the identifiers and direct references were disambiguated.
Decisions
| ID | Status | Decision | Reason | Consequences | Revisit Trigger |
| D277 | accepted | ACCEPT RFC 0165: Claude provider credential freshness and spawn-time projection. The accepted form is the v5 design integrated from run_efde0bcac1a8712b90c94e22e9f5db97 plus the cycle-3 accept_with_findings ledger. Source build is delegated to RFC 0173 and the successor rfc-0165-build workflow. | The v5 falsification gate discharged the load-bearing design constraints for #583: Claude-specific access-token-only projection, launch-bound recovery freshness, projection-off fail-closed handling for missing/unknown/unmodeled credential kind, and credential-domain-before-same-user precedence. The remaining finding is implementation scope and verification shape, not a reason to keep the product decision proposed. | RFC 0165 is marked accepted. No source behavior lands with this decision. RFC 0173 owns the build contract: generated credential-projection contract, pure pre-spawn planner/refusal witness, immutable launch credential binding, provider-auth debt accounting, redacted operator surfaces, and TTL-spanning verification. The build must update product docs, command authority matrix, route guardrail tests, SQL/generated contract surfaces, and operator state docs in the same slices that change behavior. | Revisit if RFC 0173 cannot implement access-token-only projection without lane refresh-token custody, if production launch cannot consume the same side-effect-free planner used by refusal witnesses, or if RFC 0169 supersedes the Claude-specific assurance class before the build starts. |
| D276 | accepted | Split striatum doctor's top-level non-red findings into actionable warnings and advisory notices. warnings now means "operator work remains": recoverable debris, stale skills/config, incomplete scans, live stuck-job signals, lock convoys, and similar non-red findings with a concrete remediation path. notices preserves non-actionable advisory facts that should stay visible but should not keep the operator warning budget nonzero: D204/D205 historical artifact legibility classes (artifact_legacy_unverifiable, artifact_superseded_on_default_branch, artifact_acknowledged_loss), archived terminal fan-in barrier debris (barrier_debris_terminal_run), and codex_token_env_absent when the daemon runtime token exists but the current shell did not export it. | D204/D205 restored ok=true by moving historical provenance out of problems, but leaving permanent baseline rows in top-level warnings made operator bootstrap report a nonzero warning count even after all daemon-recoverable work was complete. That trains operators to ignore warnings, weakening the same stop-and-fix discipline D204/D205 were meant to protect. The operator needs a bounded, actionable warning channel and a separate advisory channel for visible-but-not-recoverable history. | HandleDoctor performs the split at the final aggregation boundary: subsystem checks still compute their local advisory warning counts and records, but the daemon response moves the selected classes from top-level warnings/warning_records into additive notices/notice_records. Existing problems, problem_records, and ok semantics are unchanged. operator bootstrap keeps counting warnings, so its warning count now tracks actionable work while JSON consumers can still inspect notices explicitly. | Revisit if notice volume grows enough to need pagination/grouping, if a notice class gains a daemon recovery command and should return to warnings, or if consumers need a formal severity enum instead of the two-list compatibility shape. |
| D275 | accepted | ACCEPT RFC 0172: proof-only multi-campaign supervision defines campaign arcs, authority receipts, fresh-context replay, quarantine/refusal records, cross-surface contradiction reports, and a read-only portfolio status surface for supervising many RFC arcs without making status permission. V1 is proof-only or recommendation-only: tracker rows, dashboard rows, replay passes, receipts, and contradiction reports are provenance and stop pressure, not authority to launch, sequence, promote, update acceptance state, or seal done. | The MULTI_CAMPAIGN_SUPERVISION falsification gate cleared only the RFC/product-decision step and left the architecture/build path unaccepted. The content review was ACCEPT_WITH_FINDINGS; the live health gate that blocked acceptance closeout later recovered (operator bootstrap, doctor --json, and daemon status all returned successfully). The accepted RFC preserves the two binding floors that make the direction safe to explore: provenance is not permission, and replay/done proof must be same-boundary fresh. | Build may proceed only in later roadmap-sequenced slices that preserve the proof-only boundary. This decision does not authorize daemon schema, RPC, route-map, CLI, dashboard, tracker-backend, workflow-launch, sequencing, promotion, or done-seal authority by itself. Any future RPC or route-map work must update docs/reference/command-authority-matrix.md and the authority guardrail tests; any tracker integration remains a mirror unless a later accepted decision grants exact daemon-reconciled mutation authority. | Revisit if a useful implementation cannot preserve proof-only/recommendation-only semantics, if operators need tracker/dashboard rows to become authoritative, if same-boundary freshness is too expensive for advancement/done checks, or if a future version seeks workflow-launch or promotion authority. |
| D274 | implemented | IMPLEMENT the verdict-capable durable-artifact recovery extension to D200. recovery complete-stalled still refuses review / phase_synthesis jobs by default, but --force may recover a dead or blocked verdict-capable job only when its current attempt already has a required, body-reconstructable finding or synthesis artifact. The daemon validates the artifact front matter, derives the verdict from verdict_intent (finding) or status (synthesis), and routes through applyVerdict with review_provenance_override=true and basis daemon_auto_finalized_from_published_artifact. The autonomous #308 published-but-unsealed path uses the same helper instead of requeueing into an immutable artifact-author conflict. | A live falsification run produced the exact D200 gap: the final summary synthesis artifact was durably published, the lane died before sealing, and the retry correctly blocked on immutable byline provenance. Hand-finishing would hide a runner defect; override-verdict did not apply to the blocked job; default complete-stalled correctly refused direct completion. The safe recovery is not to mark the job completed from an artifact, but to record the verdict already present in the durable artifact with explicit recovery provenance. | Adds recovery_verdict_artifact.go, wires it into tryFinalizeUnsealedFromDurableArtifact and HandleRecoveryCompleteStalled --force, and updates the CLI/authority docs. New pgtests cover forced recovery from a durable synthesis verdict artifact and autonomous sweep recovery of an unsealed published verdict synthesis. No schema or RPC shape change. | Revisit if synthesis verdict status needs a first-class artifact schema field instead of relying on tolerated standard metadata, if a non-finding/non-synthesis verdict artifact type is introduced, or if recovery provenance needs an operator decision id rather than an explicit daemon recovery basis. |
| D273 | accepted | ACCEPT RFC 0171: generated operator records and run-shaped bodies move toward daemon-indexed blob storage with git-retained dockets, pointer manifests, striatum:// virtual records, materialization, historical inventory/import proofing, and doctor/check-docs integrity gates. | The 2026-06-28 architecture review found provenance accumulation is now a primary architectural drag, and the user explicitly instructed this session to create an accepted RFC first and then build it. RFC 0072 and RFC 0123 already provide the blob/placement foundation; RFC 0171 preserves git review by replacing bulky bodies with compact dockets rather than removing the review surface. | Build is authorized in focused slices: record index schema, docket model and read surface, resolver/materializer, blob-required publish posture, generator defaults, inventory/import verifier, doctor/check-docs support, hygiene guard, and concise docs. Broad historical deletion remains gated on byte-identical reconstruction proof. | Revisit if dockets cannot preserve reviewability, if blob storage is unavailable for a blob-required posture, if historical reconstruction cannot prove exact manifests, or if record classes that must remain durable git provenance are discovered. |
| D272 | accepted_with_follow_up | ACCEPT RFC 0168 P0 - per-lane pooled OS uid as the lane security principal - via operator override after the v6 falsification gate converged on a secure design. The run-level decision artifact body was docs/operator/artifacts/rfc-0168-design-v6/decision/DECISION_override_to_build.md; it is now a generated record in RFC 0171 import batch rfc0171-bulk-artifacts-20260629. | The v3-v6 design chain discharged the security-critical concerns: fail-closed scrub classification, bearer-path re-rooting under .striatum/scratch/<supervisor_id>/, credential-cache ancestry, and fail-closed provider credential selector completeness. The only standing v6 finding was over-broad refusal of legitimate non-credential lane env such as AGY_HOME/FIXTURE_CONFIG_DIR, which is a usability/build-refinement issue, not a security bypass. | Build is authorized from the v6 HOLDER spec plus this binding follow-up: the OQ4.1.2 coverage-gap gate must discriminate provider-owned credential selectors from ordinary lane env selectors, keep the typed lane_uncovered_credential_selector_inside_repo refusal for uncovered provider credential directories resolving in-repo, and add a positive-control test that legitimate in-repo non-credential lane env still launches. The v6 design run was canceled after the decision to clear the runner wedge; the build stage must proceed from the accepted spec and D272 rather than waiting for a PROPOSAL.md. | Revisit if the build cannot implement a provider-owned credential-selector discriminator without reopening the security boundary, if the positive-control test exposes an unresolved fail-open/fail-closed tradeoff, or if RFC 0143 Slice B needs a narrower security-principal primitive than per-lane pooled uid. |
| D271 | accepted | RATIFY the RFC 0170 P0 design — the read-only Tier-1 self-culling substrate (a runtime cullable_entity candidacy ledger + a read-only DecayTickSweep riding the recovery sweep off the wait-gating path; observe-only: no deletion, no page, no run-admission) — under a RE-SCOPED P0 acceptance bar. G1: the Tier-1 supersession predicate is MECHANICAL and SOUND (pure grep, fixed closure lexicon, fully-static tree-local protected pathspec; no LLM, no external/mutable-outside-tree state) and passes a KNOWN-SET corpus test — zero false POSITIVES on the known preserved set (rfc:0097/0027/0039/0041, D174, the backup/rfc-* banked branches, docs/records/_frozen/**, RFC-0170 prose), with the known-dead D267 nominated. G2: the cull fold is READ-ONLY SAFE — recovery loop never blocked, persisted scheduler-cursor refresh not deferred, no torn write (skip-on-overrun + L4 compute-then-commit), panic/error isolated. Cleared SPEC: docs/operator/artifacts/rfc-0170-p0-design-v5/commit/proposal/PROPOSAL.md (5-round falsification_gate v1→v5, verdict accept_with_findings). | The falsification gate proved the architecture in four rounds, then kept surfacing ever-finer whole-tree corpus-exactness edge cases with near-zero blast radius for an observe-only P0 that nothing acts on. For P0 a sound mechanical predicate + a known-set corpus test + read-only safety is sufficient; full whole-tree exactness only matters at P1, when nominations drive deletion. A conservative false NEGATIVE (e.g. D081 withheld because a status: frozen audit outside docs/records/_frozen/ cites it) is the safe direction for a cull system. | Exhaustive whole-tree Tier-1 exactness — treat all status: frozen records as non-live citation sources wherever they live — is deferred to P1 as #618 (a safe-direction false negative, not a false positive). Bounding a non-cooperative (ctx-ignoring) filesystem hang — cull-slot liveness + a late-writer generation fence — is deferred to P1 as #619 (read-only, restart-recoverable). The downstream rfc-0170-p0-build code_change run implements the SPEC: migration 0045_cullable_entity.sql (both read+write authority-inventory rows, GRANT no owner DDL/FK ≥27, no SELECT *) + the read-only DecayTickSweep; verify with sealed receipts. | Revisit the re-scoped bar when P1 introduces a cull ACTION (tombstone/reaper) that consumes nominations — at that point #618 whole-tree exactness becomes load-bearing and must close before any deletion. |
| D270 | implemented | DELETE the cross-repo product surface and supersede D267 plus the crossrepo clause of D231. Remove go/pkg/crossrepo, the cross_repo.* RPC registry entries, the striatum cross-repo ... CLI routes, daemon handlers, generated method tables, and current reference docs. Keep RFC 0128's single-repo write-scope refusal guardrail because it protects the current single-repo invariant. Do not add a forward schema drop in this slice; historical migrations and live DBs may still contain cross_repo_* tables/columns, but no supported operator feature exposes them. | Maintainer has not used cross-repo and does not expect to in the near term; maintenance and front-door complexity now exceed value. | Current Striatum orchestration is single-repository per run, with fail-fast refusal/warnings for accidental foreign-repo reach. Historical RFC/schema references remain provenance, not supported product surface. | Reopen only with a concrete second-adopter workflow and a fresh RFC that deliberately reintroduces daemon-supported multi-repo scheduling. |
| D269 | implemented | IMPLEMENT the RFC 0133 fan-in barrier cutover (#527 / #354): STRIATUM_BARRIER_FANIN is now live by default, and STRIATUM_BARRIER_FANIN=0 is the recoverable kill switch back to the D206 per-completion merge. Confirmed fan-in runs record freeze points at materialization; completing declared siblings stage attempt-addressed contributions and keep exact job pins without advancing the run branch; the downstream gate waits for the ready fan-in barrier, assembles through the two-phase runBarrierAssembly path, commits barrier_state, then queues the join job. fanInIntegrateRunBranch remains only for kill-switch, unconfirmed-branch, and non-declared-fan-in fallback paths. | The 2026-06-24 audit correctly found the D254/D246 fan-in path still shadowed and parallel to production. The same-final-tree fixtures already proved the assembly tree matches D206; the missing behavior was the live wiring: no per-completion merge for declared fan-in siblings, downstream gating on the committed barrier, and default-live polarity with a rollback switch. | Code paths: barrierFaninAssemblyEnabled defaults on; anchorActiveWorktreeForJob routes declared fan-in siblings through stage+pin; dependenciesSatisfied calls ensureFaninBarrierCommittedForGate before enqueueing the downstream join. New regression TestFaninCutoverStagesPinsAndAssemblesBeforeDownstreamQueues; existing fan-in/barrier PG suites passed. No schema or RPC change. The live deployment proof remains deferred while striatum doctor is red; this closeout used PG/unit integration proof only. | Revisit if the inline downstream-gate assembly needs to become a first-class claimable barrier_assembly job lifecycle, if live deployment equivalence after a green doctor diverges from the PG fixtures, or if STRIATUM_BARRIER_FANIN=0 is exercised in production long enough to justify restoring shadow default. |
| D268 | accepted | KEEP the auto-finalize circuit breaker after the #599 evidence pass. The retained incident class is repeated live recovery.auto_finalize failure on the same workflow job: an open breaker makes the sweep skip the candidate with an explicit circuit_breaker_open cause instead of retrying a publish/verdict/finalize failure until the recovery loop becomes its own outage. Evidence pass: rg -n 'auto.?finalize|recovery_auto_finalize|circuit.?breaker' go docs found the runtime table, read/write authority inventory, status/next-action surfaces, and recovery handler; go test ./pkg/mutations ./pkg/recovery ./pkg/reads -run 'AutoFinalize|Circuit|Recovery' -count=1 passed. | D231 already rejected appearance-based deletion. The dedicated pass found the surface still protects recovery from repeated failure amplification, and existing focused tests cover the breaker config, reset guard, skip shape, and failure-cause classification. | No schema/runtime cut. docs/reference/spec.md now marks the breaker as an intentional recovery guard, so future audits do not classify it as inert without rerunning the evidence pass. | Revisit only if live recovery telemetry shows the breaker never opens across a full release cycle and the deletion pass proves no sweep/status/doctor/operator path still depends on it. |
| D267 | superseded | SUPERSEDED by D270. This row formerly kept go/pkg/crossrepo/ and the cross-repo CLI/read surface after the #598 evidence pass. | D270 records the later maintainer decision that cross-repo has no near-term adopter and its maintenance/front-door cost now exceeds value. | D270 removes the package, routes, RPC entries, daemon handlers, tests, and current reference docs. Historical RFC/schema references remain provenance. | Use D270's revisit trigger. |
| D266 | accepted | KEEP the supervisor table split for #608: process_supervisors owns the repo-scoped supervised lane record, process_supervisor_pointers owns the mutable run/session binding plus delivery/liveness metadata, and daemon_supervisors owns the daemon-instance launch envelope and command hash. Collapse is not authorized while recovery, attestation, heartbeat coalescing, and terminal-supervisor cleanup query the three roles independently. Evidence pass: rg -n 'process_supervisors|process_supervisor_pointers|daemon_supervisors|supervision_control' go/pkg/db go/pkg/mutations docs found independent schema, mutation, recovery, and test references. | The greenfield "one supervisor table" shape is plausible, but the current split separates repo provenance, live pointer metadata, and daemon-process custody. Merging it would be a behavior-changing migration near recovery/liveness code, not a cleanup. | docs/reference/spec.md now names the table-level invariants. No schema migration. The baseline deploy/supervisor test run also exposed unrelated current-head failures that this closeout fixes separately. | Revisit only with a migration plan and equivalence tests proving stop/reap, heartbeat/progress, attestation, and recovery liveness behave identically after collapse. |
| D265 | accepted | KEEP the three deploy substrates for #606, with explicit ownership: runtime migrations are the daemon startup schema-evolution path for runtime-owned objects; owner bundles are owner/admin DDL, SECURITY DEFINER, grant/revoke, and owner-held table changes; striatum daemon deploy with deploy_cursor/deploy_plan/deploy_receipt is the one-shot ordered transcript for decoupled high-risk deploys such as the staged DDL-revoke. | The substrates overlap in prose but not in authority. Collapsing owner bundles into runtime migrations would reintroduce the two-role 42501 crash-loop class; collapsing deploy receipts into ordinary migrations would lose the resumable/ordered transcript required by RFC 0142 P4. | docs/how-to/postgres-transition.md now includes the ownership matrix. No schema/runtime code changed for this decision. | Revisit if a future deploy path can preserve owner/runtime ordering, immutable deploy receipts, and boot refusal semantics with fewer substrates and equivalent tests. |
| D264 | accepted | Close #603/#604/#605 with a single operator-visible verification budget and subtraction-release gate. While striatum doctor is red, no new feature-wave RFC design/build starts; only runner-integrity, recovery, source-of-truth documentation, or closeout work that reduces the red state or audit ambiguity may consume operator attention. After doctor is green, the active RFC design budget is at most two new in-flight design runs per wave; existing in-flight Wave 0/Wave 1 reliability designs are grandfathered and should be finished or canceled before starting more. Self-hosting tax is tracked separately from adopter-critical reliability: self-hosting tax protects Striatum's own multi-lane dogfood economics, while adopter-critical reliability blocks a fresh target repo/operator on the documented single-box path. Wave 4 feature work is gated on a subtraction release that closes or explicitly quarantines README/front-door truth, root record cleanup, fan-in #527, crossrepo #598, auto-finalize #599, recovery split #600, branch pruning #602, WIP budget #603, self-hosting classification #604, feature gate #605, deploy substrate #606, TODO/changelog tax #607, and supervisor split #608. | The audit found RFCs, branches, decisions, and docs accumulating faster than verification/subtraction retires them. A gate in the roadmap is cheaper and more enforceable than launching another RFC to discuss the gate. | docs/operator/rfc-roadmap.md now carries the budget, classifications, examples, and subtraction checklist before the wave sequence; docs/operator/BRIEF.md points cold-start operators at it. | Revisit when the checklist is complete and doctor is green; then either reopen Wave 4 with a new budget row or keep the gate if accretion continues. |
| D263 | accepted | LAND RFC 0167 P0 (operator identity & run attribution, per the falsification-cleared v4 SPEC docs/operator/artifacts/rfc-0167-p0-design-v4/commit/proposal/PROPOSAL.md). The owner-bundle is 0022 (0022_operator_identity_run_attribution.sql), NOT 0021: ordinal 0021 is reserved by the RFC 0142 P4 staged DDL-revoke (DDLRevokeOwnerBundleVersion = 21), so the next free embedded ordinal is 22 and LatestOwnerBundleVersion/RequiredOwnerBundleVersion advance to 22. The load-bearing build decision: a normal apply-eligible bundle (0022) now sits above the staged revoke (0021), which breaks RFC 0142 P4's frontier assumption (>= 21 ⟺ the revoke). The resolution is to retarget the revoke predicates from frontier (< 21 / >= 21) to the EXACT version (!= 21 / == 21): isNonRevokeBundle, RevokeBundleEmbedded, and a new IsOwnerBundleApplied(version) helper replacing the connection.go boot barrier's applied >= 21 (which would false-positive at watermark 22 with 21 absent) — so 0022 applies, the revoke stays deploy-plan-terminal, and a no-revoke binary still refuses a genuinely post-revoke DB. C2" composed-route read closure: runs REVOKE SELECT + column re-GRANT excluding created_by_principal_id, operator_handles/operator_sessions column-scoped grants excluding principal_id(+client_id), identity reads via the run_origin_identity/runs_for_origin_client/runs_missing_origin SECURITY DEFINER projections, registered in readScopeReasserts for drift repair. The two binding §F constraints are discharged: F-1/A44 — spawn_authorization_grants.owner_principal_id is enumerated as a bare client-id column with no FK to principals (the composed third route reconstructs client_id → client_id, with a fail-loud control); F-2/A45 — the blast-radius is recorded as static-segregated-plus-narrowed-routine (the operator-bootstrap path presents the session-bound {admin, read} operator token; the static 8-capability bootstrap-admin token is segregated to the daemon-root surface, not injected for routine repo-admin). | The roadmap's Design→Build→Verify ship path requires P0 landed contract-first against the cleared SPEC; the ordinal collision with the concurrently-landed RFC 0142 P4 revoke is the one build-time discovery the SPEC anticipated (use 0022 if 0021 taken), and the frontier-predicate retarget is the necessary, minimal consequence of placing a normal bundle above a staged terminal revoke. | RFC 0142 P4's revoke machinery is touched only to make the exclusion exact-version rather than frontier (the invariant "the revoke 0021 is the sole deploy-plan-terminal exclusion; revoke-applied means version 21 is recorded, not MAX≥21" is preserved); the owner_revoke_filter_test/reservations contiguity guard are updated for the 22 frontier + the 21 staged gap. The PG-backed two-role pgtests (10 named) are proven in the verifier stage. The striatum operator bootstrap CLI local-command rewire is IMPLEMENTED in P0, not deferred: the command is the client of the operator.bootstrap RPC — it mints + leases + presents the session-bound {admin, read} operator token (raw token written 0600 to .striatum/scratch/operator-token, consumed via STRIATUM_MCP_TOKEN_FILE at higher precedence than the static runtime token; never embedded in the packet), so the static bootstrap-admin credential is used only to call the RPC and is structurally absent from the routine repo-admin path (A45 / §F F-2). Doc reconcile per DECISION-rfc-0167-p0-build-doc-reconcile (spec.md cold-start text, the local-command rationale, and the route-freshness test message updated to match the implemented rewire). | If the verifier stage falsifies a composed-route control (A35/A36/A37/A44) or the credential-segregation control (A45), if the frontier-predicate retarget surfaces a deploy-activation regression the RFC 0142 P4 tests did not cover, or if a future concurrent owner bundle re-collides on an ordinal. |
| D262 | accepted | RATIFY the RFC 0142 P4 design (the one-shot striatum daemon deploy that lifts schema mutation out of serve-boot, decouples serve-boot, and revokes serving-role DDL) — closing the Wave-0 #1 design stage. The falsification_gate design run (run_365daa96…, v9 / cycle-1) cleared accept_with_findings: the single binding v8 finding M7 (the §3.5/§4.5/F18 row-16 complete/decoupled/revoke-embedding cell asserted unconditionally where A's decoupled-complete branch is fingerprint-conditional and reads neither applied_owner nor revokeEmbedded) is resolved via Option 1 — row 16 ==0/==20/>=21 become conditional "SERVE-verify if in-sync, else awaiting_deploy", identical to the M6 fix for rows 13/15, so every A-reaching complete-row cell is now derived from the W→A predicates and F18 is parametric over all seven such cells. Both falsifiers (decoupling-boundary lens + carry-forward/regression lens) independently conceded M7 resolved with no carry-forward regression across M6/M5/M3/M4/M1/M2/BC-N1/BC-N2/C1/C2/C3; the adjudicator source-verified the orthogonality + RequiredOwnerBundleVersion=20 watermark against the run worktree HEAD. The build-ready spec is docs/operator/artifacts/rfc-0142-p4-design-v9/commit/proposal/PROPOSAL.md (12 fixes carried as first-class design); the v9 dialogue + FINAL_SUMMARY.md are banked to main. Finding B1 is non-blocking, build-phase-only, folded into PROPOSAL §6.5 as binding acceptance criteria the rfc-0142-p4-build run MUST honor: (B1.1) T-deploy-bootpath-decision-table must construct the row-16 in-sync AND out-of-sync sub-cases for applied_owner ∈ {0, 20, >=21} and assert in-sync cells serve verify-only without firing the ApplyMigrations/RecordSchemaFingerprint spies; (B1.2) table-drive each concrete cursor-state enum (none/in_progress/step_committed/finalizing/complete/aborted), not the grouped shorthand. P5 stays out of scope (D258 / RFC 0142). | The roadmap's Design→Build→Verify ship path requires every RFC's design hardened into falsifiable, verification-gated acceptance criteria via a Striatum design run before Build; P4 is the deployment-safety linchpin (Wave 0 #1) the rest of the 0142 chain (P3-arm, P5, then RFC 0136) builds on, and an un-derived complete-row cell would let a build smuggle an unstated boot-time guard past F18. | RFC 0142 itself was already accepted (D258); this ratifies P4 only and does not reopen the five-layer design. The design run is complete-as-ratified: its terminal final_summary wrap-up job wedged on an artifact_immutable_mismatch retry collision (the summary artifact art_56d223a9… was already git-anchored at 83652fef when a retry re-published divergent bytes); the daemon-routed clean finish was recovery resolve-blocker + run cancel (all six artifacts remain durably anchored — the cancel only tears down the wrap-up loop + its dead supervisor, restoring doctor green). Next stage: rfc-0142-p4-build (a code_change run, contract-first/TDD per PROPOSAL). Roadmap Wave 0 #1 Design marked done. | If the build run cannot make row 16 executable against the same predicate inputs the design claims (B1 unsatisfiable), if the per-step resumable-cursor contract proves insufficient under some owner+runtime interleaving (D258 Open Q3), or if the shadow-first STRIATUM_DEPLOY_DECOUPLED default-OFF rollout surfaces a serve-boot regression P4's tests did not cover. |
| D261 | accepted | SPLIT RFC 0143 — lane credential survival across a daemon boot-epoch rotation (#512) — into Slice A (ship now, decoupled) + Slice B (blocked on a structural prerequisite), and OPEN RFC 0168 — per-lane OS user as the lane security principal. A seven-cycle falsification_gate design run (v1→v7, banked under docs/operator/artifacts/rfc-0143-design-v{6,7}/) proved the authenticated-reseal channel (options 2/3) is unsolvable while every lane shares the striatum-lane uid (BC1-W1-ORACLE: the production tmux control surface runs as the shared uid with a deterministic session name and no private socket, so a same-uid sibling can respawn-pane-replace the pane the daemon launched and the daemon — whose only handle is a post-launch tmux query — authenticates the replacement; a 0600 reseal file is the same same-uid replay surface). A /adhd analysis selected per-lane OS uid (pre-provisioned pool) as the only structural, host-independent fix that survives a daemon restart and dissolves the whole class (rejected: namespace-inode — not structural under Yama ptrace_scope=1; AppArmor-hat — host-dependent; private-socket-alone — insufficient under a shared uid). Slice A (option 4, the typed session_unrecoverable_across_rotation floor) ships now and is decoupled from the channel: the SEED's OQ1 "Slice A must route over the structurally-bound channel" is overridden as documentation, not mechanics, after source-verifying that Slice A's deliverable-complete + lane-lost predicates are computed entirely from daemon-side durable state (tmux #{pane_dead}/#{pane_dead_status} + /proc/kill(0) liveness, striatumd.artifacts rows via verifyRequiredArtifacts, git-blob reconstructability via verifyRequiredArtifactReconstructable in go/pkg/mutations/recovery_complete_stalled.go) with no dependency on an authenticated inbound frame. Slice B (the CapabilityReseal authority + channel) is gated on RFC 0168 landing, under which option 2 reduces to a safe lane-uid-owned 0600 reseal token. | The shared lane uid is the irreducible root the 0143 gate converged on across seven cycles; a finer authentication handshake cannot fix a same-uid replay surface, so the structural move is to stop sharing the uid. Slice A is pure observability that removes the misleading silent-unsealed-exit dead-end (#512) without touching the credential trust model, and its source-verified independence from the W1 channel lets it ship immediately instead of waiting on the whole Slice-B prerequisite chain. | Direction maintainer-ratified live 2026-06-24 (per-lane uid + decouple-and-record). RFC 0143 re-statused proposed→accepted(split); RFC 0168 opened proposed (direction ratified, spec pending its own falsification_gate design run). No credential code lands with this decision. A tracked GH blocker (#585) — "RFC 0143 Slice B blocked on per-lane security principal (RFC 0168)" — records the dependency. Slice A design/build is the next pipeline stage. | If the per-lane-uid provisioning cost (pool size, lease lifecycle, per-uid credential stores + ACLs) proves disproportionate to the reseal benefit and the maintainer prefers status-quo + operator requeue; if Slice A's typed floor cannot be wired without a new boot-time guard that itself reaches the credential surface; or if RFC 0168's design gate falsifies pooled per-lane uid as the principal. |
| D260 | accepted | ACCEPT RFC 0167 — operator identity & run attribution: a single human driving ~15 concurrent terminal operators against the one daemon cannot tell which operator owns which run. Drafted via the /adhd divergent loop; the load-bearing reframe is that operator-id is not new — it is a principals.principal_id of kind human (RFC 0107 substrate already has principals + principal_clients + sessions.last_session_heartbeat_at), so the memorable handle (maya#7f3, the #suffix being the disambiguating truth) is a daemon-leased rendering layer, reaped via the existing session heartbeat, unique-per-repo-while-live. Adds a write-once runs.created_by_principal_id stamp, an append-only run_custody_log (origin vs current holder), bylines keyed on the immutable id (rename cannot launder provenance), and the read surfaces (striatum whose <run-id>, status --mine manifest, pre-attentive chips, operator-named striatum-handoff files). Phased P0 (identity+stamp+whose+manifest, alone retires the problem) → P1 custody → P2 honest bylines+handoff naming → P3 lineage. | Operator legibility under concurrency is a real, repeated friction (this very session juggled cross-repo runs and parked design runs with no fast attribution); riding the existing principal model avoids a second, divergent identity scheme and inherits its attestation/rotation honesty. | RFC re-statused proposed→accepted directly by the human principal, directing autonomous landing — defer nothing: the implementing agent scaffolds and drives the Striatum design→build→verify workflows for it. Design-run falsification is the first pipeline stage (hardens P0's owner-bundle migration under the RFC 0142 P0 two-role pgtest), not an acceptance gate. The proposal review branch rfc/0167-* is superseded by this accepted copy on main and deleted. | If the design run falsifies the principal-id-is-operator-id reframe (e.g. a single OS user / shared socket cannot bind a per-terminal principal at token-mint time — the named load-bearing risk), or if the handle pool / cross-repo board / @handle#suffix byline open questions need a product call before P2. |
| D259 | accepted_with_follow_up | ACCEPT RFC 0162 — lane-auth silent-failure observability, the credential-dimension sibling of RFC 0091/0131 session liveness: a lane's provider-auth can silently stop succeeding and go unnoticed for hours (#556/#567 made preflight behave but added no alert). Root reframe: alert on the absence of expected success, never on the presence of errors. The 3-layer design (L1 expiry/renewal-health telemetry ⭐; L2 cross-lane differential + negative probe; L3 per-lane dead-man's heartbeat; + a signed roster backbone) is accepted after a falsification_gate design committee (claude holder/adjudicator + codex falsifiers). Cycle-2 landed two material over-claim findings — F1 (the scalar census cannot honestly cover non-expiring api_key lanes and drops the lane label) and F2 (L1's same-credential read is unproven for non-codex lanes; the resolver is codex-only) — folded into the committed PROPOSAL.md via operator override DECISION-rfc-0162-design-override-fold-f1-f2. MVP (build-order steps 1–5) is implemented + verified + banked to main @ 418bd1d6: backbone roster + per-lane striatum_lane_auth_expected{lane,provider,kind} census vector (label-preserving unless on(lane)); F2 fail-closed credential-resolver contract (codex+claude) → lane_cred_resolver_mismatch (never a green gauge from a HOME decoy); Layer 1 expiry sampler → seconds_to_expiry/age/sample_present (OAuth; api_key census-covered only — explicit accepted risk); codex-scoped Layer 3 lane.auth_success heartbeat at supervision_provider_auth.go:56; doctor reconciliation; 8 families in DefaultRegistry() + regenerated metrics_allowlist.json; 6 proximal alerts vendored to halbritt/proximal @ 87458e9. Verified green: go build/vet/golangci-lint clean; the FA-5/FA-7 boundary tests pass and the existing laneproviderauth/supervision_provider_auth suites pass unchanged (read-only telemetry, no preflight-behavior change — that is RFC 0143). Layer 2 active prober (striatum-lane-auth-prober.timer) is the deferred follow-up; the live game-day fire test is the remaining acceptance gate to fully implemented. | The RFC 0162 failure mode was confirmed live, twice, this session — the striatum-lane claude OAuth credential was found expired ~14h with zero signal, then expired again mid-dogfood and wedged a committer lane — exactly the silent absence-of-success the RFC targets. Layer 1 expiry telemetry (a provider-agnostic file read) is the on-target MVP backbone; because laneproviderauth.Check() is codex-only, a synthetic non-codex heartbeat would be a lie, so non-codex coverage is L1-expiry + the lane-labeled census, with L3 honestly codex-scoped. | RFC re-statused proposed→accepted. MVP shipped to main (418bd1d6) + proximal (87458e9); rules not yet installed to /etc/prometheus (the absent() shared-fate rule would false-page before striatumd emits the new families — install+reload is the game-day deploy step). Design+build dogfooded via rfc-0162-{design,build} runs; a workflow-shape limitation surfaced (the falsification_gate revision edge routes to the falsifier, not the holder, so the spec was re-attacked but never revised → operator override). | If the live game-day shows an alert does not fire within its documented window; if the non-codex api_key positive-validity gap (the accepted risk) proves material before Layer 2 ships; if LaneCredRenewalStalled's single [6h] window is too coarse for divergent lane cadences; or if the roster should become a daemon-DB table rather than config. |
| D258 | accepted_with_follow_up | ACCEPT RFC 0142 — safe-by-construction database-change deployment, the closure of the #442/D248 two-role 42501 crash-loop class (a runtime migration that ALTERs/FK-references an owner-held table dies at boot under striatumd_rw and crash-loops the single writer, invisible to single-role pgtest). The five-layer defense-in-depth design is accepted as written after a falsification_gate design committee (claude holder/adjudicator + agy/codex falsifiers) hardened P0 into five build-bearing constraints (C1 escape-proof non-superuser LOGIN SUT role; C2 bootstrap reproduces prod per-table relowner; C3 non-superuser bootstrap; C4 role-isolation self-check; C5 search_path) — see docs/operator/workflows/rfc0142-design-falsification/committee-output/. P0 (the two-role pgtest fixture / 42501 oracle, Layer 1a) is implemented + deployed (PR #553, live; verified by sealed go-build/vet/test receipts + live 8/8 two-role tests under a non-superuser owner DSN). P1–P2 (ownership pre-flight lint + Layer 0 RESERVATIONS.toml ledger + CI collision guard; owner-bundle watermark interlock + clean awaiting_owner_ddl halt) follow as direct runner-fix PRs; P3–P5 (schema-fingerprint drift gate; one-shot striatum daemon deploy; rehearsal receipt + expand/contract) are tracked as sliced issues. | The recurring prod incident class (#442/D248) wedges the single-writer daemon irreversibly, the two-role 42501 is invisible to single-role pgtest, owner-bundle-before-restart ordering is a foot-gun, and RFC 0136 P2/P3's hash-chained reshape is un-rehearsable under the current model — all reduce to one coupling (the serving daemon mutates its own schema on restart, irreversibly, as the least-privileged role). The committee found no shape-level falsification; the landed+verified P0 proves the foundation. | RFC re-statused proposed→accepted. P0 shipped (#553); design dogfood surfaced + fixed runner defects #551/#554/#555. No new migration/owner-bundle from P0 (test-harness only). P1–P5 are downstream tracked work; deploy of later phases gated on their own PRs. | If the per-step-resumable-cursor contract (Open Q3) proves insufficient for some owner+runtime interleaving, if the full-data clone mechanism (Open Q1) or tier-down prescreen (Open Q2) needs an operator/host call before P5, or if "deploy as a Striatum run" (Open Q4 / Provocation) is adopted. |
| D257 | implemented | IMPLEMENT RFC 0154 / D250 (#476) — the opt-in final-review fan-in debounce. A multi-reviewer panel routes the author revision on the first gating needs_revision by default; the new opt-in cycles[].debounce_cohort field buffers the route until the gating cohort reports, then routes once consuming a single max_iterations slot. debounce_cohort: all waits for every gating seat that feeds the same downstream gate; a positive integer waits for that many seats. The cohort is the frozen gating-seat denominator the accept-path panel-quorum barrier already freezes (resolveQuorumDeclaration; advisory seats excluded) — referenced, not restated. Implemented daemon-side in applyVerdict (go/pkg/mutations/review.go case "needs_revision") via gatingCohortDebounceSatisfied (go/pkg/mutations/revision_routing.go): when the cohort is not yet at quorum the dissenting seat completes (its verdict + dissent-ledger row stay durable), a revision.cycle_debounced event is emitted, and the route is deferred to the last reporting seat. Late-straggler supersession is unchanged from D194/D216 (a post-route verdict is non-current by review_generation mismatch). Lint: validateDebounceCohort (go/pkg/workflowauthoring/workflow.go) accepts absent/""/"all"/positive-int and rejects malformed values. | First-dissent routing discards the other reviewers' verdicts on a multi-reviewer panel and risks a second straggler needs_revision burning the bounded (default 2) revision budget on a moving target (#476); the operator playbook's "wait for all verdicts, then one consolidated pass" was a load-bearing human heuristic the engine did not encode. | No schema change/migration (a workflow_json field, per the D212 pattern). Default sentinel (field absent) preserves today's first-dissent routing for every existing workflow — no wire/timing change for non-opted-in runs. New tests TestNeedsRevisionDebouncesUntilGatingCohortReports (mutations) + TestValidateCycleDebounceCohort (workflowauthoring); the negative control (default sentinel keeps first-dissent) is held by the unchanged TestNeedsRevisionRoutesToMatchingCycle / TestNeedsRevisionRecordsInFlightSiblingGatingSeats. RFC 0154 re-statused accepted→implemented; alternative A (legibility, PR #549) shipped earlier. #476 stays open as the impl tracker until the operator merges. | If the all-N/quorum shape proves insufficient (e.g. a dissent-threshold early route, RFC 0154 option C, is wanted), if cohort-denominator semantics need revision, or if a global default flip away from first-dissent is desired. |
| D255 | implemented | IMPLEMENT RFC 0152 / D249 (#478) — the lane-kind-differentiated agent_exited_unsealed requeue budget. A READ-ONLY reviewer lane (a job that does not repo-write) now recovers on the larger maxReviewerUnsealedRequeues (default 2, == maxRequeues, clamped >= maxUnsealedRequeues and <= maxRequeues); a STATEFUL repo-write lane keeps the tight maxUnsealedRequeues (default 1). The selector (recoveryPolicy.unsealedRequeueBudget(repoWrite)) keys on isRepoWrite(row) in recovery_decision_tree.go. The global defaultMaxUnsealedRequeues constant does not move, so the pinned defaultMaxUnsealedRequeues < defaultMaxRequeues invariant (dx_289_test.go::TestRecoveryPolicyUnsealedBudget) holds; a new TestRecoveryPolicyReviewerUnsealedBudgetExceedsStateful pins reviewer-bound > stateful-bound, and TestSweepReviewerUnsealedExitRequeuesNotEscalates proves a reviewer lane requeues at count 1 where the byte-identical stateful fixture escalates. New per-workflow override recovery_policy.max_reviewer_unsealed_requeues. | A single transient unsealed exit on a stateless reviewer lane (the common cause is transient Anthropic-API unavailability during end-of-session wind-down, after the review was produced) is a clean fresh-session retry; escalate-after-one made it the most frequent committee needs_operator cause (#478). D249 resolved this lane-scoped to avoid reversing D198 or breaking the invariant. | No schema change (the budget shares the requeue_count counter; lane kind is read from the existing write_scope_json). D198's revisit cell updated to record the revisit fired. RFC 0152 re-statused accepted→implemented. #478 stays open as the impl tracker until the operator merges. | If the read-only-vs-repo-write distinction proves too coarse and a finer per-job-type or fresh-attempt-grant policy is wanted. |
| D256 | implemented | IMPLEMENT the #530 written-but-unsealed artifact salvage (the salvage leg of RFC 0152's option-5 idea, realized without forging attestation). A claude_code (or any per-job) lane can WRITE and commit its required deliverable into its isolated per-job worktree, then exit unsealed BEFORE artifact.publish (e.g. a transient API outage during optional end-of-session memory wind-down). The recovery auto-publish pass and recovery complete-stalled previously scanned ONLY the main run repo_root and found nothing — so the finished, expensive work was discarded and complete-stalled had no row to finalize. Recovery now also scans the job's most-recent non-removed per-job worktree (job_worktrees.worktree_path, jailed under repo_root/.striatum/worktrees/), and when a matching-byline body is found there it publishes the missing required artifact ROW(s) from the worktree-committed body (anchored via the worktree commit the sweep already pins), making the deliverable reconstructable and finalizable. Wired into autoPublishableArtifacts/publishRecoveredArtifact (a per-item source_root), the autonomous #308 finalize (tryFinalizeUnsealedFromDurableArtifact), and complete-stalled (salvagePublishMissingRequiredArtifacts, surfaced as salvaged_artifact_count). | Recoverability was worst exactly when the API was flaky — the moment redoing 15–28 min of work was most likely to fail again (#530). | No schema change (reads the existing job_worktrees table; publishes a normal artifact row). The salvaged byline must still match the lane's expected author line, so a forged file cannot be adopted; the negative-control test (TestCompleteStalledStillRefusesWhenNoArtifactAnywhere) confirms an empty job still refuses. The scaffold/skill seal-before-wind-down ordering side of #530 is owned separately. #530 stays open until the operator merges. | If lanes routinely write to a non-worktree scratch path, extend the search-root list; if a stronger byline-independent salvage signal is wanted. |
| D254 | implemented | WIRE recordFaninFreezePoint into a LIVE FAN-OUT (#527, the last build leg of #354 / RFC 0133), in SHADOW. D246 landed the staging-at-completion hook + the barrier_assembly dispatcher but left recordFaninFreezePoint with no production caller, so no run ever declared a fan-in barrier and the staging hook no-oped on every run. This change adds recordRunFaninFreezePoints (go/pkg/mutations/barrier_fanin_fanout.go), called once at run materialization (runPrepare, run.go) after the job-dependency edges are inserted: it groups the dependency edges by downstream join seat, and for every downstream seat with two or more upstream siblings (a fan-in; a single-upstream chain edge is skipped) records one immutable freeze point declaring the sorted sibling set against the confirmed run-branch tip. It is a STRICT NO-OP unless STRIATUM_BARRIER_FANIN=1 (the existing shadow opt-in, default OFF) AND the run branch is already confirmed (no frozen tip ⇒ no ghost base ⇒ record nothing). It is ADDITIVE: even when opted in it only WRITES the freeze record the staging hook keys on — it does NOT replace the shipped D206 per-completion merge (fanInIntegrateRunBranch) and does NOT make the downstream gate wait on the barrier. The STRIATUM_BARRIER_FANIN default is NOT flipped (rollback = leave the flag unset). | #527 named exactly this as the last leg: "a live fan-out wires recordFaninFreezePoint". With it wired, an opted-in run finally exercises the fan-in barrier end-to-end (recorder → staging hook → dispatcher → assembly), so the same-final-tree equivalence fixture can be confirmed against a real deployment — the precondition D246's revisit trigger named for the operator default flip. Doing it in shadow keeps the high-blast-radius default-flip out of this change (the runner's fan-in path is unchanged for every non-opted-in run), per the RFC 0133/0135 cutover discipline (D233). | New file go/pkg/mutations/barrier_fanin_fanout.go (the recorder + faninBarrierID); runPrepare (run.go) collects edgeDependencyPairs once and calls the recorder after the dependency-edge inserts. New PG tests barrier_fanin_fanout_pg_test.go (shadow-default-no-op; opt-in records the declared sibling set for the fan-in seat only and the staging predicate faninBarrierForSeat resolves it; no-op without a confirmed tip). No new migration / owner bundle (reuses fanin_freeze_points; owner bundle 0013 for the barrier_assembly job_type stays an operator deploy step the dispatcher still gates on). No new RPC/route. #354 stays OPEN: the only remaining work is the operator go-live flip — apply owner bundle 0013, confirm the same-final-tree fixture against a real deployment, set STRIATUM_BARRIER_FANIN=1, then retire fanInIntegrateRunBranch. | Flip STRIATUM_BARRIER_FANIN=1 (and apply owner bundle 0013) only after the same-final-tree fixture (TestFaninAssemblyDispatchSameFinalTreeAsPerCompletion) is confirmed against a real deployment exercising a live fan-in run; then retire fanInIntegrateRunBranch and close #354. If the freeze-point-at-prepare base proves wrong for manual-branch-confirmation runs, move the recorder to the branch-confirmation transition. |
| D253 | accepted | ACCEPT RFC 0161 (#506) -- code_change revision-cycle policy. Resolve cheapest-first: calibrate reviewer posture/prompt before any graph change, and add an asymmetric concession-only author-rebuttal alternative (extra iteration only when the reviewer concedes; bounded against attempt/lease/fresh-session and the D158 cycle router). Do NOT change the max_revision_cycles default (stays 1, RFC 0034 V1 contract). | code_change reviewer over-rejection (cycle too tight) surfaced by the RFC 0137 dogfood (D247); part-b operator-readable findings already shipped (PR #525). | No default change lands without a recorded decision; a rebuttal node needs bounded semantics before any code; impl tracked by #506. | If reviewer calibration alone does not resolve over-rejection, escalate to the bounded rebuttal node. |
| D252 | accepted | ACCEPT RFC 0158 (#483) -- verifier drift doctor classes. Ship slices 1+2 (builtin_selfpin_drift read-only warning + pin-drift doctor classes) now; idea-3 (verified_stale) premise corrected -- re-based on builtin self-pin / build-identity re-seal on a version bump (StriatumVersion is sealed only into builtin receipts, already capped at ASSERTED), not a phantom mass external VERIFIED-to-ASSERTED event -- and gated on a persisted-staleness-basis sub-decision before slice 3. | The original mass-downgrade premise was inverted vs gate code; the real skew is make-install-without-restart self-pin drift. | Child-issue split (slices 1+2 first, slice 3 gated); preserves D227 and D243/#482; reconcile with the #528 verifier nested-module fix; impl tracked by #483. | When the persisted-staleness-basis decision is made (unblocks slice 3), or if #528 changes the self-pin identity basis. |
| D251 | accepted | ACCEPT RFC 0157 (#481) -- canonical operator read-surface state projection. Add an additive state_projection {run_state, jobs:[{id,state}]} to run.summary/dashboard/status plus the dashboard top-level state (Alternatives 1 + 2). jobs[] is minimal (id+state only, no attempt/role_id); run_id-omitted yields null run_state and an empty/omitted block; additive only (no breaking key change, no schema_version bump). | The three read surfaces expose run/job state in three shapes, forcing AFK scripts to special-case each verb (#481). | One canonical cross-surface contract; contract/golden-shape test plus spec.md and cli-reference updates as DoD; impl tracked by #481. | If a consumer needs richer per-job fields, extend the minimal shape additively. |
| D250 | accepted | ACCEPT RFC 0154 (#476) -- final-review to revision routing debounce. Resolve as option B (opt-in, default-preserving): the default keeps today's first-dissent routing; opt-in runs wait for a configurable final-review cohort (all-N or quorum) before routing to revision; gating seats only; new opt-in cycles[].debounce_cohort field (no DDL); generation-based late-straggler supersession. | First-dissent routing discards the other reviewers' verdicts on multi-reviewer panels (#476); opt-in avoids changing wire/timing for existing runs. | New opt-in workflow field; daemon-side debounce in applyVerdict (not the driver); impl tracked by #476. | If the opt-in shape proves insufficient or a global default change is wanted, or cohort-denominator semantics need revision. |
| D249 | accepted | ACCEPT RFC 0152 (#478) — recovery budget policy for agent_exited_unsealed reviewer lanes — and resolve it as option 3: lane-kind-differentiated unsealed-requeue budget. Raise the agent_exited_unsealed requeue budget only for a lane-scoped kind (read-only reviewer lanes — e.g. claude_code review_final, and the broader stateless review lanes #478 also hits such as review_design), not the global default defaultMaxUnsealedRequeues=1. A transient unsealed exit on a stateless reviewer lane (the comment's root cause is transient Anthropic-API unavailability during end-of-session wind-down, after the review work was done) is a clean fresh-session retry; for stateful repo-write lanes a repeated unsealed exit still signals systematic failure and must escalate sooner than a hard crash. Raising only a lane-scoped bound keeps the pinned invariant defaultMaxUnsealedRequeues < defaultMaxRequeues (go/pkg/mutations/dx_289_test.go::TestRecoveryPolicyUnsealedBudget) intact because the global constant does not move. Rejected: option 2 (raise the global default) reverses D198 and breaks the invariant; option 4 (global auto-grant one fresh-attempt) loosens stateful lanes too; option 5 (detect a complete-but-unsealed deliverable) would not have self-healed either #478 case — both left no REVIEW.md to detect. | #478 supplies the counter-evidence D198's own "Revisit" trigger anticipated: for a stateless reviewer lane a transient unsealed exit is common and a fresh session almost always succeeds, making escalate-after-one respawn the most frequent committee needs_operator cause (it took down two concurrent committee runs in ~35 min during one API-flaky window). D198's smaller-budget rationale is correct for the stateful lanes it was written for, so the resolution preserves it there and loosens only the lane kind where a fresh session is a clean retry — without reversing the ratified decision or the pinned invariant in a triage edit. RFC_REVIEW (run 2026-06-20_9e1b6475, independent falsification review) returned ACCEPT_WITH_FINDINGS (blockers 0, serious 0, minor 2); both MINOR doc nits (the #381 cross-reference mislabel and the option-5 no-deliverable framing) were fixed in this accept edit. | RFC 0152 re-statused proposed→accepted (D249; option 3) (top line + docs/rfcs/README.md). No code lands in this decision — the lane-kind budget is implemented in a follow-up code_change impl run that (a) adds the lane-kind / job-type-scoped unsealed-requeue budget in go/pkg/mutations/recovery_decision_tree.go (larger budget for the read-only reviewer lane kind; tight default unchanged for stateful repo-write lanes), (b) updates go/pkg/mutations/dx_289_test.go — keep TestRecoveryPolicyUnsealedBudget green on the global default AND add a new assertion pinning reviewer-lane budget > stateful-lane budget, and (c) updates D198's "Revisit Trigger" cell to record this revisit fired and was resolved lane-scoped by D249. Interim mitigation (already shipped, no code change): recovery_policy.max_unsealed_requeues is a per-workflow override today, so a committee workflow can set max_unsealed_requeues: 2 in its workflow JSON to unblock #478's runs immediately. #478 stays open as the implementation tracker; this RFC + D249 are its disposition. | When the lane-kind budget impl lands (then re-status RFC 0152 → implemented and update D198's revisit cell); if the lane-kind distinction proves too coarse and a finer per-job-type or fresh-attempt-grant policy is wanted; if a forgery-resistant complete-but-unsealed signal (option 5) later makes artifact-detection routing viable. |
| D248 | implemented | FIX the D242 / RFC 0136 P1 runtime migration 0041_event_chain_segments: remove the repository_id ... REFERENCES striatumd.repositories(repository_id) foreign key. The runtime role striatumd_rw applies runtime migrations (D215 / RFC 0110) and has no REFERENCES privilege on owner-held tables (repositories/runs/etc. are owned by the owner role), so the inbound FK failed with ERROR: permission denied for table repositories (SQLSTATE 42501) the first time the new binary tried to migrate a real two-role production daemon — the daemon crash-looped and could not advance past schema 40. Repository existence is enforced in Go in the sealing path (SealEventChainSegment can only seal a repository that already has an event chain head), mirroring the boundary-event RI against owner-held events. The 0041 ownership test now FORBIDS REFERENCES striatumd.repositories (the lone runtime-migration test that had allowed it), matching every other runtime-migration ownership test. | The original D242 ship contradicted its own D215 comments by adding an owner-table FK; pgtest runs against a single-role DB where striatumd_rw effectively owns everything, so the privilege failure was invisible in CI and only surfaced on the live two-role deploy (proven: SET ROLE striatumd_rw + the old CREATE TABLE ... REFERENCES repositories reproduces 42501; the FK-free form succeeds). This is the exact two-role-vs-single-role gap D236/#442 already flagged. | 0041 SQL drops the FK (comment documents why); TestMigration41EventChainSegmentsIsOwnershipSafe adds REFERENCES striatumd.repositories to the forbidden set. No schema/RPC/owner-bundle change beyond the corrected runtime DDL; the daemon now migrates 40→42 on restart (event_chain_segments + verifier_attestations). Follow-up filed: a generic guard so any runtime migration's CREATE TABLE referencing ANY owner-held table fails the build (today each migration hand-writes its own forbidden list, which is how 0041 slipped through). | When the generic owner-table-FK guard lands; if a future runtime table genuinely needs an owner-table FK, it must be granted REFERENCES via an owner bundle FIRST. |
| D247 | implemented | LAND RFC 0137 (striatumd Prometheus exporter) A→D in full, dogfooded through four sequential Striatum code_change impl-runs (author=claude / reviewer=codex) driven by the AI operator. A read-path skeleton: go/pkg/metrics MetricsSnapshot + package atomic.Pointer published from the resident sweep tick (startRecoveryScheduler), /metrics mounted in newDaemonHTTPHandler, golden-file/forbidden-regex redaction test + zero-PG + concurrent-scrape-identity tests. B failure-mode taxonomy: Origin/apoptosis/necrosis closed enums anchored to source constants with a union guardrail, 6 families folded from the durable event ledger (tx-safe/restart-consistent), F-A6 liveness_deadline_events counter excluded from the necrosis conservation law, TestLivenessMissCanRecoverWithoutNecrosis. C enforced cardinality/privacy contract: Classification + Register() Forbidden-refusal (boot abort), per-family LRU series budget → cardinality_clipped_total, boot-time metrics_allowlist.json hash check (VerifyAllowlist), doctor_problems{class} collector pinned to static problem_records[*].check codes + TestDoctorClassRejectsDynamicIdentifiers (F-A8). D multi-tenant hardening: capability-scoped /metrics filtering reusing the RFC 0043 boundary (exact repo-id filtering before lossy-bucket rendering + colliding-bucket cross-repo isolation test), opt-in per-repo metrics_repo_consent gating Provenance families (persisted in repositories.settings_json), tick_status/staleness publish-on-errored-tick, version-controlled Prometheus recording+alert rules grounded in metric type, all five Open Questions decided in/out-for-V1. | The maintainer directed the full no-deferral implementation of the proposed RFC; the design gate was already answered (F-A6/F-A8 folded into the spec). Building it through Striatum dogfooded the runner on a real multi-phase feature and surfaced runner defects (recorded as issues). | New go/pkg/metrics/ package (snapshot/render/collector/taxonomy/registry/budget/allowlist/consent/surrogate/doctor/semantics/rules + tests + metrics_allowlist.json + rules/*.yml) and /metrics + capability-scope wiring in go/cmd/striatumd/. builtin:go-build verifier sealed green; CI green (full PG suite + lint). No migration (consent reuses repositories.settings_json). Not yet deployed — restarting the daemon to serve /metrics live is a separate operator step. | Deploy the new daemon binary to serve /metrics live; graduate any V1-deferred Open Question (cold tier, event-sourced replay, flora_diversity) if operational need arises. |
| D246 | implemented | LAND the RFC 0133 (#354) barrier_assembly dispatcher + staging-at-completion in SHADOW behind a same-final-tree equivalence fixture, completing the P1/P2 fan-in fold's implementation without flipping any default (D233 keeps fan-in shadow). The fan-in barrier machinery (recordFaninFreezePoint/stageFaninContribution/assembleFaninBarrier/runBarrierAssembly + the two-phase journal + the doctor checks + TestFaninBarrierSameFinalTreeAsPerCompletion) was already built but had no production caller — runBarrierAssembly and stageFaninContribution were dead code reachable only from unit tests. This change wires the two missing seams: (a) stageFaninContributionAtCompletion (a flag-gated staging-at-completion hook added to anchorActiveWorktreeForJob, additive to — never a replacement for — the legacy per-completion merge) and (b) DispatchBarrierAssembly (the exported barrier_assembly job dispatcher, the sole production caller of runBarrierAssembly, locking the run + asserting readiness + owner-bundle-0013 permission before assembling). Both gate on a NEW opt-in flag STRIATUM_BARRIER_FANIN (default OFF / shadow) — the inverse polarity of the P4/P6 kill switches, because the staging path changes how every fan-in sibling's work reaches the run branch (highest blast radius). A new end-to-end equivalence fixture TestFaninAssemblyDispatchSameFinalTreeAsPerCompletion proves the wired stage→dispatch→assemble path produces the byte-identical run-branch tree the shipped D206 per-completion merge produces for disjoint siblings; TestFaninAssemblyDispatchShadowDefaultIsNoOp proves the default (flag off) stages nothing and the dispatcher refuses. | #354 is the largest barrier-fold item; the BRIEF/audit RSA-005 called for landing this behind its equivalence gate as the safe shipped state. The remaining gap after #333/D233 was precisely "the barrier_assembly job dispatcher + staging-at-completion wiring is unbuilt" — the predicate, assembly, journal, and doctor were done. Shipping the wiring in shadow turns an unreachable-shadow implementation into a flag-flippable one (operator-gated by the equivalence fixture) without touching prod behavior, per the cutover discipline RFC 0133/0135 mandate. The fold's per-caller predicate retirement for quorum/run-entity/revision was already accomplished by D216's P4/P5/P6; this entry completes the fan-in (P1/P2) leg only. | New files go/pkg/mutations/barrier_fanin_dispatch.go + barrier_fanin_dispatch_pg_test.go; one staging hook spliced into anchorActiveWorktreeForJob (worktree.go). No new runtime migration (reuses fanin_freeze_points/barrier_staged_contributions/barrier_state); no new owner bundle (0013 already exists — applying it is an operator deploy step). The shared doctor (doctor_barrier.go) already covers the assembly/staging tables this path uses, so no new doctor blind spot. TestBarrierPredicateHasNoRefCount stays green (the new file carries no barrier-readiness-shaped COUNT/recency SQL). #354 stays OPEN: the operator go-live flip (apply owner bundle 0013 → set STRIATUM_BARRIER_FANIN=1) and the wiring of recordFaninFreezePoint into a live fan-out (no run declares a fan-in barrier yet, so the staging hook no-ops on every current run) remain. | Flip STRIATUM_BARRIER_FANIN=1 (and apply owner bundle 0013) only after the same-final-tree fixture is confirmed against a real deployment AND recordFaninFreezePoint is wired into a live fan-out path so a real fan-in run exercises the barrier end-to-end. (The live fan-out wiring half landed in D254 / #527, in shadow; the same-final-tree-against-a-real-deployment confirmation + the operator flip remain.) |
| D245 | accepted | DOCUMENTATION-CURRENCY PASS (audit recommendation RSA-007): re-status RFC 0061, RFC 0062, RFC 0069, and RFC 0070 from partially implemented/mostly implemented to implemented (residual: optional polish). The load-bearing daemon/web/service boundary each describes has shipped: 0061 = daemon-first web/API routes + /v1/invoke RPC routing (go/pkg/webservice/); 0062 = the real escalation inbox (list/show/resolve, striatum inbox, typed striatumd.escalation_inbox, striatum.escalation.v1, artifact linkage; artifact-only creation closed link-only by D130); 0069 = PG-backed daemon-global reads/sweeps (startup/health/audit/doctor/dashboard.all/Go status/MCP resources) with the SQLite registry fail-closed; 0070 = daemon-side repo.resolve + /v1/invoke daemon routing + local API/MCP CLI-alias quarantine + dogfood-composite disposition. The promote was conditional on a verifying grep for any live registry/Python fallback in error paths — rg -i 'python\|\\.py\|registry fallback\|legacy registry\|CLI_ROUTES' go/ --glob '!**/*_test.go' plus rg fallback go/pkg/webservice go/pkg/rpc go/pkg/cli — which came back clean: no CLI_ROUTES identifier exists in any Go file, no src/striatum Python tree exists (retired under RFC 0078), all python mentions are parity-provenance comments (mirrors reads/*.py) or "python_dependency": false status flags, and every fallback hit is capability-token discovery/repair or a wake-timeout — none a live registry/Python error path. 0070's named Python-surface cleanup remainder is therefore resolved by the RFC 0078 retirement, not pending. | RSA-007 flagged these four as load-bearing-shipped but stale-statused (partially/mostly implemented), understating delivered work. The product-boundary rule (a doc claim that disagrees with current source behavior is fixed) applies to status framing too. The grep was required so the promote rests on confirmed source state, not on RFC self-report; it confirmed the legacy fallback the old statuses implied is gone. | RFC file headers + docs/rfcs/README.md index rows for 0061/0062/0069/0070 re-statused to implemented (residual: optional polish; D245); each RFC keeps its named remainder documented as optional polish (service-modularity for 0061, schema strictness for 0062, contract-generated diagnostics for 0069, legacy-fixture cleanup for 0070) without claiming the polish is done. Docs-only change; no code or deploy. CHANGELOG.md [Unreleased] grooming line added. | Revisit a status if a future change reintroduces a live registry/Python or non-daemon fallback into any of these surfaces, or if one of the named optional-polish remainders is itself promoted to required work. |
| D244 | accepted | RESOLVE the residual RFC 0096 / #87 host-isolation-gate ambiguity: the green host-isolation gate (make lane-isolation-check, the RFC 0110 T-LANE-ISOLATION-NEG negative control invoked by scripts/check_lane_isolation_neg.sh) is operator-provisioned hardening surfaced via a conditional CI job, NOT a mandatory-in-CI check. The gate fundamentally requires host provisioning a stock GitHub Actions runner cannot have — a dedicated PG-less lane OS user, passwordless sudo -n -u <lane-user>, and PostgreSQL pg_hba.conf reject rules (per docs/how-to/lane-sandbox.md) — and the negative control cannot meaningfully run unprovisioned (it asserts the lane user is denied, which presupposes the lane user and the reject rules exist). Making it unconditionally mandatory would therefore either break CI or, worse, pass vacuously on a host where the lane user does not exist. Mechanism: a new make lane-isolation-check-ci target (wrapping scripts/check_lane_isolation_ci.sh) is the legible CI entry point, driven by a new .github/workflows/ci.yml job lane-isolation-gate that ALWAYS appears in the checks list but runs the real negative control ONLY when the host advertises provisioning via the STRIATUM_LANE_ISOLATION_HOST=1 guard variable; otherwise it skips loudly (prints SKIPPED: host not provisioned …, exit 0, stating the gate did NOT run) so a green CI never falsely implies the isolation gate executed. Once a host sets the guard, a missing probe URL / lane user / sudo rule is a loud failure (exit 2), never a silent skip — the negative-control assertion is never weakened when provisioned. | The audit (RFC disposition pass, 2026-06-20) flagged this as the one open ambiguity in an otherwise-implemented RFC: make lane-isolation-check existed but was wired into neither release-check nor any CI workflow, and the RFC/README still said "#87 still requires host adoption and a green lane-isolation gate." The engineering-correct call is forced by the check's own preconditions — it is OS/PostgreSQL-configuration-dependent by construction, so it can only be authoritatively green on a provisioned host, and an unprovisioned mandatory run would be either a CI break or a dishonest pass. The skip-loud-vs-fail-loud split keeps coverage honest in both directions without breaking stock CI. The provisioned path was verified end-to-end on the proximal workstation (lane user striatum-lane present, passwordless sudo OK, pg_hba rejects over both the UNIX socket and loopback TCP): the real gate ran and returned T-LANE-ISOLATION-NEG: ok. | RFC 0096 re-statused partially implemented→implemented (top line + docs/rfcs/README.md row); the "#87 still requires …" sentence replaced with the disposition; a disposition block added before the RFC's Acceptance Criteria. New Makefile target lane-isolation-check-ci; new scripts/check_lane_isolation_ci.sh; new lane-isolation-gate job in .github/workflows/ci.yml. docs/how-to/lane-sandbox.md Verify section gains the STRIATUM_LANE_ISOLATION_HOST guard pointer. CHANGELOG Unreleased updated. #87 stays CLOSED on GitHub; the RFC ambiguity is now resolved. No schema/RPC/route/migration/owner-bundle change; nothing deployed. Direct PR off origin/main, not a dogfood. | If a future CI provider can supply the lane-user/sudo/pg_hba posture cheaply (then the gate could become mandatory on that runner via the same guard), or if the negative control's preconditions change such that a safe portion can run unprovisioned. |
| D243 | implemented | GRADUATE RFC 0141 (#482) from experimental to supported by closing the documented forge-able attestation gap with gate-side, daemon-authoritative attestation enforcement, fail-closed (the daemon-owned-PG + operator-RPC option, the product-boundary-correct one). (1) Authoritative store: runtime migration 0041 adds striatumd.verifier_attestations (repository_id, check_id, binary_sha256, attested_by, attested_at, revoked_at, revoke_reason) with a partial unique index on the active (repository_id, check_id, binary_sha256) and a striatumd_rw GRANT (CREATE-only, ownership-safe). (2) Operator-token minter: new RPC verifier.attest (CapabilityAdmin, single_repo) that REFUSES any session-bound capability token (capability_denied — the DB session_id flag, RFC 0096 V2 / #135, not an env var a lane could unset), so the verified lane can never bless its own pins; HandleVerifierAttest upserts the active row idempotently. The striatum verifier attest verb resolves the pinned sha locally and dispatches this RPC to record the authoritative row (unreachable/no-token/unregistered-repo → loud warning, sidecar cache only; a daemon REFUSAL fails the verb closed). (3) The security fix: evaluateRunClaimVerification / effectiveStatusForVerifiedClaim now refuses VERIFIED for an external (non-builtin) claim whose backing receipt (check_id, binary_sha256) lacks an un-revoked attestation row for the run's repository — fail-closed to ASSERTED (basis attestation_missing); ReceiptSignals gains BinarySHA256; the gate query fails CLOSED on any DB read error. The repo-file allowlist.pins.<fp>.attest.json sidecar is now a cache/projection (it still drives the lane-side intent⋈pins⋈attest join at verifier run), not the trust source. (4) Negative-control rigor MANDATORY: ParseIntent now rejects a negative_control that omits mutation_of (the mutation-of-a-paired-passing-fixture form), resolving that Open Question for the supported tier. | RFC 0141 shipped at experimental (D239) with one deliberate scope cut: attestation was enforced only at the verb level (lane-context refusal) and the run-completion gate never re-verified it, so a non-compliant lane could forge the repo-file sidecar, join→VERIFIABLE, run the real pinned external check under a strict two-signal envelope, author the claim VERIFIED, and the daemon gate would honor it — attestation was lane-side honesty, not a gate-enforced trust boundary (#482, the highest-security graduation blocker). Moving the authoritative attestation into daemon-owned PG (live state, RFC 0033 / D094) read by the gate makes the forge buy nothing: the gate consults PG, not the lane-writable file. D227 is preserved — the gate read consults PG rows + sealed receipt bytes and executes nothing. TDD: the fail-closed regression (TestRunClaimVerificationDegradesWhenAttestationMissing/Revoked/AttestationBoundToExactBytes) was written first, then the gate enforcement made it green; positive + operator-RPC + session-bound-refusal tests added. | RFC 0141 re-statused implemented (D239)→implemented (D243; graduated experimental→supported) (top line + docs/rfcs/README.md), RFC body gains a "Graduation (D243, GH #482)" block + the mutation_of Open Question marked RESOLVED. Runtime migration 0041 (LatestDaemonDBVersion 40→41 + migrations_test/migrations_integration + read/write authority inventories). New RPC verifier.attest added to contracts/daemon_methods.json (methods) + regenerated registry_methods.go/daemon-method-tables.md; docs/reference/command-authority-matrix.md row added. CHANGELOG Unreleased. NOTE the pre-assigned migration number was 0043, but the daemon migration list is CONTIGUOUS (TestMigrationsAreOrdered requires version == index+1 and count == LatestDaemonDBVersion), so 0043 is structurally impossible without gaps — used the next contiguous 0041; a sibling branch taking 0041 will renumber at merge. No daemon redeploy in this change (branch + PR only; deploy applies migration 0041 then restarts). | When the doctor self-pin / pin-drift classes (#483) land; when the daemon-restart attestation-cache rehydrate is wired; when a green RFC 0105 unattended-reliability fixture exists, after which the interim examples/verification-gate-flow/ is regenerated or retired; if the exact-bytes attestation binding proves too brittle across a host/CI matrix (then revisit a verifier pin --from-receipt cross-host fast-path). |
| D242 | accepted | IMPLEMENT RFC 0136 P1 (#387) — chain-segment sealing for the per-repository event chain — as a DIRECT runner-fix PR (D208/D210 pattern; runner code is not dogfooded). P1 lands FIRST and is independently valuable: it gives the event chain the retention-readiness the audit chain already has (a sealed, hash-witnessed boundary the future partition-DROP can prove continuity across), so retention has a chain-safe boundary from day one. Q5 (D241) = generalize: the audit_segments "seal + boundary-hash + retention_state" model is generalized to events as runtime migration 0041_event_chain_segments.sql — a per-repository_id striatumd.event_chain_segments ledger (the audit-segment columns keyed per repo, with state IN (open,sealed,purged), first/last_event_id+first/last_created_at+first/last_hash, the cross-segment witnesses previous_segment_last_hash/next_segment_first_previous_hash, and retention_state). Per D215 it is a RUNTIME table, NOT an owner bundle: striatumd_rw-owned, explicit GRANT SELECT,INSERT,UPDATE + REVOKE DELETE, append-only via refuse-triggers (a sealed/purged row is frozen; no DELETE), and — critically — it carries NO foreign key into the owner-held events table (the D215 / RFC 0136 §"chain-head FK is the sharp edge" trap). The referential integrity between a sealed segment's boundary event ids/hashes and the actual events rows is enforced in Go, in the new sealing path pkg/mutations.SealEventChainSegment (FOR-UPDATE on the one-open-segment-per-repo + the chain head, Go-side boundary existence/hash-match check, atomic close-then-open-successor). Ships with the event_chain_segment_seam_unproven doctor invariant (go/pkg/reads/doctor_event_chain_segment.go) over the seam continuity P1 produces (seal-boundary completeness + previous/next cross-segment hash-witness match; hard RED; skips cleanly pre-0041). Stale owner-bundle reference corrected: the RFC named owner bundle 0016 for the P2/P3 reshape; 0016–0019 are now taken (latest on main is 0019), so P2/P3 are renumbered to owner bundle 0020 in the RFC (phase table + prose + OQ4). P2/P3/P4 are NOT implemented here. | The RFC's P1 is the one slice that is pure runtime (D215: integrity-in-Go, explicit GRANT, no owner DDL) and that the higher-risk owner-DDL reshape (P2/P3) structurally depends on — without a sealed-segment ledger a dropped partition is indistinguishable from tampering (RFC §"Preserving the hash-chain contracts"). Generalizing the existing audit_segments shape (Q5) rather than minting a divergent table avoids drift. Enforcing the events-boundary RI in Go rather than a SQL FK is forced by the same constraint that breaks the repo_event_chain_heads FK under the P2 PK reshape, so building it in Go now keeps P2 from having to undo a runtime FK later. | Runtime migration 0041 registered (LatestDaemonDBVersion 40→41 + label + TestMigration41EventChainSegmentsIsOwnershipSafe); event_chain_segments added to the read/write authority inventories (RFC 0110 §13 / #164 completeness guards). New files: go/pkg/db/sql/0041_event_chain_segments.sql, go/pkg/mutations/event_chain_segments.go (+ _pg_test.go), go/pkg/reads/doctor_event_chain_segment.go (+ _pg_test.go, wired into HandleDoctor as the event_chain_segment_seams block). RFC 0136 P1 row marked ✅ DONE (2026-06-20) with the migration/sealing/doctor pointers; 0016→0020 corrected. CHANGELOG Unreleased/Added updated. No owner-bundle, no RPC/route, no daemon restart — runtime migration applies on the next daemon migrate-db; the daemon was NOT redeployed by this PR. #387 stays open as the P2–P5 tracker. | When P2 lands (the owner-bundle 0020 PK reshape consumes this ledger as the retention seam and moves the repo_event_chain_heads RI into append_event_row); when P4's retention executor wires partition-DROP to flip event_chain_segments.state→purged/retention_state and adds the partition_dropped_without_sealed_segment doctor invariant; if the seal cadence (who calls SealEventChainSegment, and when) needs to be daemon-scheduled rather than retention-driven. |
| D241 | accepted | RESOLVE the RFC 0136 (#387) P0 policy knobs against measured prod velocity. Grounding read on striatum_daemon 2026-06-19: events 14.0M rows / 20 GB and audit_log 17.3M rows / 8.8 GB, both over a ~5-week span (2026-05-14→06-19) ⇒ ~468k events/day + ~576k audit/day, ~14 GB/month combined and accelerating (June ~4× May daily). Q1 granularity = WEEKLY (~2.0 GB events / ~1.2 GB audit per chunk, ~52/yr — a monthly chunk at this rate is ~9 GB ≈ the whole current table, barely improving per-chunk VACUUM, so the RFC's reflexive monthly default is overridden). Q2 retention = events 3 months (drop older, ~27 GB steady-state cap) / audit_log ∞ (partitioned-but-never-dropped — forensic/compliance record; keeps the VACUUM/read win, P4 never carves it). Q3 = (a) sole-writer invariant accepted (no per-partition/BRIN/constraint-trigger). Q4 = sibling owner-bundle slices (events first, then audit on its own verified cutover). Q5 = generalize the audit_segments seal/boundary-hash model into a shared chain-segment abstraction for events. | P0 is a pure product/operations call everything downstream depends on, and the RFC's "monthly is the natural default" assumed a lower velocity than what prod actually shows; grounding the call in real row/size/velocity data flipped the granularity pick to weekly and made the no-retention path a real ~170 GB/yr disk problem rather than academic. Maintainer pinned the knobs via a structured decision after being shown the measured numbers. | RFC 0136 P0 row marked ✅ DONE; Open Questions 1–5 resolved in-doc (the "P0 RESOLVED" block). P1+ (the event_chain_segments sealing, then the P2/P3 owner-DDL PK/UNIQUE reshape, then the P4 retention executor) stays ready-for-human — the reshape is the highest-risk owner-DDL slice and lands on a deliberate owner-bundle cutover. The owner-bundle number 0016 named in the RFC's P2–P4 is now STALE (latest on main is 0019); use next-free at implementation time. #387 stays open as the implementation tracker. | Re-evaluate granularity (weekly vs daily) if event/audit velocity materially shifts; set a finite audit_log horizon (enabling its DROP path) only on an explicit future compliance/disk decision. |
| D240 | implemented | IMPLEMENT the actionable design-complete issues from the 2026-06-19 handoff as DIRECT runner-fix PRs (D208/D210 pattern), one worktree-isolated agent per issue. (a) RFC 0138 (#453, PR #488): strict fan-in terminal-gap recovery — Option A unconditional (doctor reason strict_fanin_required_seat_unrecoverable + sharper needs_operator) + Option B opt-in fanin_tolerates_sealed_gap/max_sealed_gaps sealed on the fanin_freeze_points record, admitting a gap ONLY for a provably-dead required seat (reusing the quorum supervisedAgentConfirmedDead oracle, no new liveness check), OR'd into RFC 0135's is_terminal_gap predicate disjunct with no predicate fork (TestBarrierPredicateHasNoRefCount stays green), recording status=terminal_gap+damage_code in the join manifest (never silently forging completeness); runtime migration 0039. (b) RFC 0139 (#421, PR #489): supervisor reconcile write-amplification — Direction 1 Go heartbeat coalesce floor (STRIATUM_SUPERVISOR_HEARTBEAT_COALESCE, ~30 s, computed from the already-read row) in refreshSupervisorHeartbeat+refreshReportSupervisorHeartbeat; Direction 2 drop state from idx_process_supervisor_pointers_run (runtime migration 0040 + owner bundle 0019 transferring the three supervisor tables to striatumd_rw FIRST, since migration 0005 left them bootstrap-owned). (c) RFC 0140 (#457, PR #486): attestation honesty — client-side work.heartbeat local_work=true keepalive from the agent loop + a liveness-truthful classifier (wedged_no_tool_progress on a PID-alive, identity-matched lane → alive_but_silent, KEEPS attestation; byline-forgery guard RFC 0026/D080 preserved — a confirmed-dead lane still loses attestation + is reaped). (d) RFC 0094 residual extras (#402, PR #487): Check-B correspondence rubric + collaboration_ledger v1.1 per-entry fields + second-adjudicator-on-disagreement gate, additive at the publisher exit-6 front-matter contract. | These were the handoff's design-complete YOUR-FOCUS set: the RFCs (proposed, design-of-record) and merged PR #432 supplied the design; the prior remediation campaign (D236) deferred them as implementation trackers. Each is a runner fix, so the direct-PR-not-dogfood rule (D208/D210) applies; agents skipped CHANGELOG + decision-log so a single consolidated entry (this one) avoids the #1 merge-conflict source. | RFC 0138/0139/0140 re-statused proposed→implemented (top line + docs/rfcs/README.md). Runtime migration sequence 38→40 (0039 fan-in sealed-gap tolerance / 0040 supervisor run-index state drop) + owner bundle 0019; #453 and #421 both first drafted migration 0039, #421 renumbered to 0040 at merge (serialize-same-file discipline). CHANGELOG Unreleased/Fixed + Added entries. #453/#457/#402 PRs merged; #489 (#421) carries the owner bundle so it deploys with owner-ddl apply 0019 BEFORE systemctl --user restart striatumd. Issues #453/#421/#457/#402 closed on merge. | Verify #421's acceptance targets on prod after deploy (≥80% fewer timestamp writes, new-page 20%→≤5%, HOT→≥92%); exercise #453 Option B with a real degraded-fire fixture before any caller opts in; #402 second-adjudicator path wants a live disagreement run. |
| D239 | implemented | IMPLEMENT RFC 0141 (#473) at the experimental tier — all three pillars + acceptance criteria land. Pillar 1: go/pkg/verifier/intent.go (striatum.verifier_allowlist_intent.v1, hashless; ParseIntent REJECTS a stray binary_sha256 and requires backs_claim+negative_control), the pure JoinIntentPins three-valued join (NAMED-but-unpinned / PINNED-but-unattested / VERIFIABLE) + typed Unpinned sentinel, attest.go (striatum.verifier_allowlist_attest.v1 sidecar keyed by id→sha), and verifier pin --host-here / verifier attest verbs — attest REFUSES inside a supervised lane (STRIATUM_SESSION_ID/STRIATUM_LANE_ID present), so the verified lane cannot bless its own pins. Pillar 2: builtin.go (builtin:go-test/go-vet/go-build/artifact-anchor-integrity, self-pinned to the striatum binary, BuiltinID+StriatumVersion sealed) — the HARD CAP is enforced authoritatively at the daemon gate read (EffectiveStatusFromReceipt returns ASSERTED for any builtin_id receipt regardless of strict posture+agreement) and at lane classification; the generator REFUSES a gate_floor=verified gate composed only of builtins. Pillar 3: verifier.EvaluateAllowlistTemplate (pure read, no execution) hard-blocks workflow validate (exit 8) and daemon run start on an UNFILLED gate, naming the entry + the literal verifier pin --host-here fix (self-updating — pinning clears it); a mandatory negative_control runs FIRST and voids the receipt if the known-bad passes. The verification_gate shape is registered in workflowgenerate + catalog.json at experimental (reconcile green), scaffolding a real type: verify job → claim_ledger gate, the hashless intent template (in forbidden_paths), a .gitignore for allowlist.pins.*, and role/prompt stubs. Resolved Open Questions: default gate floor = ASSERTED (VERIFIED is the opt-in external road); attestation lives in a sidecar; builtin+toolchain:* NOT shipped; mutation-of-paired-fixture rigor stays an opt-in. | The RFC was accepted at D238 with no code; #473 tracked the build. The implementation followed the RFC's three pillars verbatim, keeping D227's cardinal rule intact (the daemon executes nothing — every check, builtin or pinned, runs only in the off-gate-path verifier lane; evaluateRunClaimVerification still shells out to nothing). The one deliberate scope cut: attestation is enforced at the operator-token verb level (lane-context refusal) rather than re-verified at the daemon completion gate, so at experimental tier a forged sidecar could in principle reach VERIFIED — acceptable for experimental with the threat documented and filed as the graduation blocker. | RFC 0141 re-statused accepted (D238)→implemented (D239) in the top line + README index. CHANGELOG Unreleased entry added. Graduation follow-ups filed: gate-side daemon-authoritative attestation enforcement (#482, security), doctor self-pin/pin-drift classes + version-skew resweep (#483). No owner-bundle/migration change (adds no job_type — verify already live at owner bundle 0016); no new RPC (the verifier verbs are lane-side/operator-side local CLI commands, not daemon routes — no command-authority-matrix change). | On graduation from experimental to supported once #482 (gate-side attestation) lands AND a green RFC 0105 unattended-reliability fixture exists — then regenerate or retire the interim examples/verification-gate-flow/. |
| D238 | accepted | ACCEPT RFC 0141 (#473 — the RFC 0134 follow-up): build a generatable verification_gate workflow shape on the three pillars the RFC specifies. (1) Two-layer allowlist — a committed, hashless, reviewable intent layer (schema striatum.verifier_allowlist_intent.v1: the existing AllowlistEntry minus binary_sha256, plus backs_claim) emitted into forbidden_paths so the verified lane cannot sanction its own checks, overlaid by a gitignored per-host pins layer (striatum.verifier_allowlist.v1, keyed by host+arch fingerprint) the operator NEVER hand-types: a new striatum verifier pin --host-here runs IN the disposable verifier lane, observes each sanctioned binary's sha256, and writes the pins with a drift-refusing per-entry diff. The PINNED→VERIFIED promotion requires a SEPARATE striatum verifier attest gated on an operator daemon-minted capability token (NEVER a lane/session token), because the trust signal must come from a principal the verified lane cannot impersonate; the claim_ledger records a three-valued status NAMED-but-unpinned / PINNED-but-unattested / VERIFIED. (2) Built-in, Striatum-pinned check library (builtin:go-test/go-vet/go-build/artifact-anchor-integrity) so the generated shape is runnable out of the box for the common case, with BuiltinID+StriatumVersion sealed into receipt.v1 — but CAPPED at ASSERTED on the striatum self-pin alone (it proves WHICH harness invoked the tool, not WHICH tool ran), reaching VERIFIED only via an explicit opt-in builtin+toolchain:* that also seals a sandbox-captured toolchain fingerprint; the generator REFUSES a VERIFIED-threshold gate composed only of builtin:* checks. (3) The gate cannot lie green — a typed unfilled-sentinel (Unpinned{host_fingerprint, fix_command, entry_pointer}, never a magic string) hard-blocks at workflow validate and run start with a named fix command, and a mandatory per-check negative_control (run FIRST; receipt voided if the known-bad passes) plus a scaffold-time "thymic" all-red/all-green graduation dry-run void a vacuous check. Ships at the experimental tier (no RFC 0105 unattended-reliability fixture yet); the interim hand-authored examples/verification-gate-flow/ stays until graduation. The daemon NEVER executes a check (D227 cardinal rule preserved): builtins and pins all execute only in the off-gate-path verifier lane; the completion gate stays a pure read. | The RFC 0134 graduation (D237) left a tracked gap (#473): the executable verification gate is implemented but has no generatable shape, and the only realization is a hand-authored today-primitives example. The obstacle that kept it out of D237 is structural — a runnable allowlist cannot be a committed fixture because binary_sha256 content-addresses host/distro-specific bytes — so the design had to dissolve that tension, not paper over it. An /adhd divergent pass (regulator / attacker / 3am-on-call / remove-the-load-bearing-assumption / biology frames) converged on commit-the-policy-observe-the-bytes (the intent/pins split), surfaced the load-bearing honesty constraints (attestation must come from a principal the lane cannot impersonate; a builtin self-pin attests the wrong link in the chain so must cap at ASSERTED), and pruned the traps (SLSA/Nix provenance and cross-host quorum as infra-heavy for a single-operator local-first tool; execution-shape vacuity heuristics and claim↔check coverage-binding as flaky / requiring the semantic LLM judgment 0134 exists to avoid). Accepting at experimental mirrors the D234 precedent for a new shape without an unattended-reliability fixture. | RFC 0141 re-statused proposed→accepted (D238) in the RFC top line + docs/rfcs/README.md index. Human principal ratified the acceptance; recorded here. The build is a follow-up implementation slice (generator shapes[] registration + catalog reconcile, the intent/pins schemas + pure JoinIntentPins, the verifier pin/attest verbs, the builtin registry capped at ASSERTED, the unfilled-sentinel + negative_control gate predicate) — NOT landed by this decision. No code, schema, RPC, route, migration, or owner-bundle change ships with the acceptance. | On implementation (graduate from experimental to supported once a green RFC 0105 unattended-reliability fixture exists, then upgrade or retire the interim example); on the RFC 0141 Open Questions as they are decided — notably whether the DEFAULT generated gate should target ASSERTED with VERIFIED as an opt-in release tier, and whether the builtin+toolchain:* tier is worth shipping given it reintroduces a lane-computed per-host hash. |
| D237 | accepted | Graduate RFC 0134 (executable verification gate + claim status-provenance) from accepted-with-revisions to implemented under D227's validate-not-execute accepted form. Both build halves are on main (lattice slice 13909f7f, executable slice 2b182cf4, CI-portability f484996c; #394/#395 closed), owner bundle 0016 is LIVE (owner DB at bundle 18; verify is in jobs_job_type_check), and every accepted-form path is exercised: (a) live mint — striatum verifier run under a strict bubblewrap envelope classifies a passing check verified_eligible (strict + two-signal agreement + exit-0) and a failing check asserted; (b) a NEW connected regression TestRunClaimVerificationEndToEndRealReceiptMint wires the real sandboxed mint (verifier.ExecuteCheck) through the real daemon gate read (evaluateRunClaimVerification) — strict host reads VERIFIED (two_signal_sealed_receipt), a re-mint over a changed worktree tree auto-decays to ASSERTED (receipt_seal_mismatch), and a degraded host asserts the non-strict fail-safe; (c) the validation half (provenance lint exit-6 refusal of hand-authored VERIFIED, monotonic non-self-promotable writer, receipt schema+seal round-trip) is green; (d) operator legibility — the verified-vs-asserted ledger is frozen on the run_completion_record (run.summary projects it) and now renders as a deterministic ## Claim Verification section in the evidence export (renderProvenanceSections). DECIDE item 5 (interim example): KEEP examples/verification-gate-flow/ as the portable today-primitives demonstration and do NOT inline-upgrade it to a runnable real-verify fixture — the allowlist content-addresses exact binary bytes (host/distro-specific by design), so a committed runnable example would be non-portable; a generatable verification_gate shape that scaffolds the workflow + a template/placeholder allowlist is filed as a tracked follow-up, separate from graduation. | The RFC's design and both build halves already shipped under D227; the remaining work was verify-and-graduate, not re-design or re-build. Under the accepted validate-not-execute form the completion gate is a PURE READ that never blocks, so there is no run-blocking behavior to dogfood — the entire 0134-specific surface is the off-gate-path verifier-lane mint and the daemon-side gate read, and both are proven (live + a connected regression that uses no fabricated seal). A full supervised verify-job dogfood would re-exercise only the generic lane plumbing every other dogfood already covers, at the cost of the documented lane-self-hosting fragility on a shared checkout — net-negative evidence value for net-positive risk. The original RFC §6 Acceptance Criteria ("a run cannot complete with an unmet check", "blocks run completion through the RFC 0118 gate") describe the REJECTED §2–§3 daemon-executes form and are superseded by D227; they are NOT graduation criteria. | RFC 0134 top line + the docs/rfcs/README.md index row re-statused to accepted-with-revisions / implemented, and the index description corrected from the rejected engine-executes/blocking framing to the shipped validate-not-execute/non-blocking form. RFC body gains a "Graduation status (D237)" block. New regression TestRunClaimVerificationEndToEndRealReceiptMint (pkg/mutations) + an extended TestEvidenceExportRendersProvenanceSections asserting the ## Claim Verification section (pkg/reads). The change is test + reads-render + docs only — NO schema/RPC/route/migration/owner-bundle change, so no deploy is required (owner bundle 0016 has been live since 2026-06-19 08:09). Lands via PR off origin/main; daemon NOT redeployed. | If a sandboxed verifier lane proves insufficient for a real verification need, or the two-signal VERIFIED requirement is too strict in practice (D227's own triggers); when the generatable verification_gate shape follow-up is built (then upgrade or supersede the interim today-primitives example). |
| D236 | accepted | Remediate the 2026-06-19 failure-mode audit (../audits/STRIATUM_FAILURE_MODE_AUDIT_OPUS_4_8_2026-06-19.md, verdict MIXED_RECOVERY: 0 BLOCKER, 4 SERIOUS + 5 MINOR availability/liveness gaps) and clear the open-issue triage backlog in one campaign. Implementable findings landed as DIRECT runner-fix PRs: #451 (background-sweep panic → degraded-cursor backoff, no daemon crash-loop), #452 (atomic migration DDL+stamp in one tx + in-progress hash verify), #454 (blob PutBytes content-readback hash), #455 (auto-finalize per-job durability floor), #456 (durable + replayed supervised_push packets via migration 0038), #458 (legible + self-healing owner-bundle cross-dependency reconcile), plus the prod-critical #442/#441 owner-DDL crash-loop (owner bundle 0018 transfers the pre-split runtime-table cohort to striatumd_rw before runtime ALTERs + removes the unsound allowlist for an ownership-aware owner-DDL guard), #445 (RFC 0015 lane skill-bundle install), #446 (cannot_advance_blocked dependency-blocked vs seal-failed legibility), #447 (deterministic RFC 0135 barrier-assembly commit). The three DESIGN-CLASS findings get RFC PROPOSALS rather than unilateral fixes: RFC 0138 (#453 strict fan-in terminal-gap seal policy), RFC 0139 (#421 supervisor reconcile-loop write-amplification), RFC 0140 (#457 attestation for honest long local work). Blocked/parked/gated/already-covered issues stay OPEN with recorded dispositions: #354 (blocked on #333/#346), #380 (gated on Repro B + #372), #381 (parked — only bites a >1-job-per-lane workload), #387 (RFC 0136 is its design-of-record; impl deferred), #402 (residual RFC 0094 adjudicator-reliability extras after the merged #432). | The audit verified that no data-corruption BLOCKER survives tracing (the per-run advisory lock serializes run-scoped mutations, work.complete is one transaction, the audit chain holds a FOR UPDATE singleton head, and the run-completion reconstructability gate independently blocks an unreconstructable required body), so what remained was availability/unattended-liveness hardening — runner defects, fixed DIRECTLY as PRs per D208/D210 (runner fixes are not dogfooded). The design-class findings each change a policy or a semantic boundary (auto-sealing a REQUIRED barrier seat could forge completeness; reshaping indexes/cadence on the daemon's hottest write path near the #417 stabilization; relaxing the attestation security gate), so each warrants a reviewed RFC rather than a unilateral change. | 10 fix PRs merged (#459/#461/#462/#463/#465/#466/#467/#469/#470/#471) closing #442/#445/#446/#447/#451/#452/#454/#455/#456/#458 (#441 closed as a #442 duplicate); 3 RFC-proposal PRs opened for review (#460/#468/#464 → RFC 0138/0139/0140 for #453/#421/#457). CHANGELOG Unreleased updated. No release was cut and the prod daemon was NOT redeployed: owner bundle 0018 is deploy-ordering-load-bearing — it must be applied via striatum daemon owner-ddl apply --owner-url … BEFORE the daemon restart on the next two-role prod deploy. pgtest is single-role, so #442's end-to-end two-role bootstrap path is proven only by the ownership-aware guard + ownership-transfer unit tests, not a live two-role bootstrap (the residual the PR flags). | When RFC 0138/0139/0140 are reviewed (accept → implement, or decline); on the next prod deploy (apply owner bundle 0018 before restart); when a >1-job-per-lane workload makes #381's in-txn cap guard live; when #333/#346 unblock #354's shared-barrier migration; #387/#402 per their own triggers. |
| D235 | accepted | Fix #431 (the self-hosting crux): a process-adapter lane whose command is a bare interactive agent CLI (claude/codex/agy, i.e. agentloop.BootstrapDeliveryModeFor == argv) is AUTO-PROMOTED to the self-driving agent loop at supervise start when it does not explicitly set agent_loop; an explicit agent_loop:false on such a command is REFUSED with a legible error. A bare agent CLI cannot consume stdin-FIFO push packets, so in the default supervised_push mode it read the pushed packet as conversational input, never called work.await_packet/work.heartbeat/work.complete, and died at session.liveness_deadline_missed with zero work.* events and no sealed artifact — blocking every RFC → design → build dogfood. The daemon-side promotion mirrors workflowgenerate.defaultAgentLoopLane (which already set agent_loop at authoring time), so authoring and launch now agree and hand-edited / copied / pre-generate snapshots also self-drive. | The failure was a silent misconfiguration: an operator who pastes a real agent command over the local parking fixture (or copies a lane) without adding agent_loop gets a lane the daemon happily launches in a mode it cannot possibly drive, surfacing only as an opaque ~5-minute liveness timeout. Push mode is only viable for an OS-level FIFO consumer (argv0 sh/bash/python/…, never a bare agent CLI), so a bare agent CLI in push mode is always wrong — auto-promotion can only help (the affected lanes always failed before, so there is no working behavior to break) and the explicit-false refusal turns a guaranteed silent failure into an actionable start-time error (RFC 0111 legibility, symmetric with requireSupportedAgentLoopAdapter). The self-driving loop teaches the protocol via the injected bootstrap prompt + MCP config, so the lane does NOT depend on the striatum-claim-loop skill being installed for the lane user. | Implemented in go/pkg/mutations/supervision_lane_config.go (loadSupervisionStartConfig resolves the mode via new helpers laneAgentLoopSetting + commandSelfDrivesViaArgv; promotion mutates the decoded snapshot lane so the transport/stdin defaults follow → pty_helper) and surfaced via config.AgentLoopAutoPromoted on the supervisor.starting/supervisor.started events + the supervise start result (go/pkg/mutations/supervision_control.go). Regression tests TestSuperviseStartAutoPromotesBareAgentCLILaneToSelfDriving + TestSuperviseStartRefusesExplicitlyDisabledAgentLoopOnAgentCLI. Live-verified: a bare ["claude","--model","claude-opus-4-8","--permission-mode","bypassPermissions"] lane (no agent_loop) auto-promoted to self_driving, drove draft → publish → work.complete, and sealed an artifact (run_9bfb076214120f1fa18dcc080fcea523, state=completed, 0 new doctor problems). No schema, migration, or RPC change. | If a future push lane legitimately needs a bare-agent-CLI argv0 (none exists today), add an explicit opt-out path rather than relying on the refusal. The lane skill-bundle gap (the striatum-claim-loop / striatum-mcp skills are never installed into the lane user's home, only matters for the CLI-fallback path) is a separate concern tracked apart from this self-driving fix; likewise the #389 cannot_advance_blocked drive-legibility message that over-states "lane finished but the seal failed" for merely dependency-blocked jobs. |
| D234 | accepted | Graduate RFC 0094's deferred collaboration shapes (#402): ship fog_of_war_review + synaptic_prune at the experimental tier, together with their two enabling primitives — the conversation post_dialog_hook (close-time emit-before-teardown; shared with RFC 0095 Phase 3) and generator work-packet type sequencing (a shape may withhold a typed job such as proposal until a gate clears). Reverses the D155 deferral of fog_of_war_review / synaptic_prune / post_dialog_hook. The adjudicator-reliability extras (Check-B rubric + collaboration_ledger v1.1 + the second-adjudicator-on-disagreement gate) are the only remaining deferred slice, now un-deferred and tracked active in #402. | RFC 0093 / D155 deferred these three names as out of scope for the V1 collaboration family; they are now built on existing primitives — post_dialog_hook reuses conversation.close + the work-packet delivery path (no new daemon method) and type sequencing is a generator capability — so the deferral no longer holds. Graduated at experimental rather than supported because neither shape yet has an RFC 0105 unattended-reliability fixture. | Implemented via #402 / PR #432: go/pkg/workflowgenerate/shapes_fog_synaptic.go (+ generate.go / workflowauthoring/lint.go / phases.go), post_dialog_hook in go/pkg/mutations/conversation.go, example flows fog-of-war-review-flow / synaptic-prune-flow, and the workflow catalog (go/pkg/workflowtemplates/catalog.json + the generated docs/reference/workflow-catalog.md, both shapes at experimental). No new RPC, route, or schema migration. Prereq tracking issues #433 / #434 were closed as duplicates of this shipped work. | Graduate each shape to supported once it has a green RFC 0105 unattended-reliability fixture; revisit the deferred adjudicator-reliability extras (#402) when a second-adjudicator-on-disagreement gate is needed. |
| D233 | accepted | Close #351 (RFC 0133 OQ2 / RFC 0135 OQ2, the run-branch-as-projection wildcard) AND record the RFC 0135 FULL barrier-cutover outcome (#354 / D216). (a) The run branch stays an AUTHORITATIVE git ref advanced by the barrier CAS, NOT demoted to a projection-of-the-barrier-chain. The projection form #351 floated (every run-branch advance is a committed barrier_state row whose first parent is the prior barrier's commit, so the branch becomes a chain doctor walks newest-first and an out-of-band hand-commit is a structural chain break) is DECLINED as the default: it is strictly more machinery (a parallel commit chain + a chain-walk doctor) for a property the existing surfaces already cover — barrier_state two-phase journaling + the barrier_integrity doctor invariant (D216 P3) + the worktree-ref-safety / artifact-anchor doctor checks already detect an out-of-band hand-commit (worktree_head_unreachable, job_completed_without_anchor). The authoritative-ref form is what the P1/P2/P6 CAS plumbing (advanceRunBranchToAssembled, HandleRunIntegrate) already builds on, so it is the simpler invariant to keep coherent. (b) Cutover outcome: P4 (quorum) and P6 (run.integrate) are FLIPPED LIVE as the default behind recoverable env kill switches (STRIATUM_BARRIER_QUORUM, STRIATUM_BARRIER_RUN_ENTITY); P5 (revision coherence) is confirmed ALREADY live (the seal IS review_generation); P1/P2 (fan-in deferred-join + recoverable assembly) STAY SHADOW — the per-completion D206 merge remains the default fan-in path. | The projection form is an elegant idea but buys no integrity property the shipped journal + doctor invariants do not already provide, while adding a second source of truth (the commit chain) that must be kept consistent with the ref and the barrier_state rows — three stores instead of two. The authoritative ref + journaled barrier_state is the minimal coherent form. For the cutover: P4 at default budget 0 is byte-identical to the edge-by-edge gate (and STRICTER only where it kills the stale-seal-accept trap), and P6 reduces to the terminal-state check for every run on the live path (no run declares a job-level barrier yet), so both flip with proven equivalence; P1/P2 require a barrier_assembly job dispatcher + staging-at-completion + downstream-gate-waits-on-barrier wiring that does not exist, so flipping them is an unproven behavior change and must stay shadow. | Implemented on feat/issue-354-barrier-full-cutover. (a) Run branch stays an authoritative ref; RFC 0133 gains a one-line OQ2 note pointing here; no code change for the ref decision. (b) P4: dependenciesSatisfied (go/pkg/mutations/mutations.go) routes a GATING review panel through panelQuorumSatisfied (now runner any), retiring the edge-by-edge latestVerdict default for paneled gates; equivalence fixed by TestPanelQuorumCutoverEqualsEdgeByEdge. P6: HandleRunIntegrate (go/pkg/mutations/integrate.go) gates on runEntityBarrierReady, retiring the bare terminal-state default; equivalence fixed by TestRunIntegrateRunEntityBarrierGate (+ the prior TestRunIntegrateIsTheRunEntityBarrier). P1/P2 unchanged (fanInIntegrateRunBranch stays the default). Both flips carry a =0 kill switch. RFC 0135 phase table + D216 outcome note + CHANGELOG updated. No new RPC/route. | Build the projection-of-the-barrier-chain form only if an out-of-band hand-commit slips past the journal + doctor invariants in practice (then the chain walk earns its second store); flip P1/P2 live once a barrier_assembly job dispatcher + staging-at-completion wiring lands behind its own same-final-tree end-to-end equivalence run; re-key revision coherence away from review_generation only under D216's own revisit trigger. |
| D232 | accepted | Close #361 (the roadmap/todo truth-layer question): ARCHIVE docs/reference/roadmap.md and docs/reference/todo.md behind a pointer to docs/operator/BRIEF.md + striatum operator bootstrap, rather than CI-mechanizing their freshness. Keep thin pointer stubs at the original paths so live inbound links (README.md, docs/index.md, docs/readme.md, the AGENTS.md cold-start reading order, docs/reference/prd.md) keep resolving and make check-docs stays green; flip todo.md's Status to superseded. The actual stub/archive edits land on a SEPARATE branch (chore/archive-roadmap-todo); this row only RECORDS the decision. | Two hand-maintained truth layers (a forward-sequencing roadmap and a status board) drifted faster than they were curated; the bootstrap packet + operator brief are the live, daemon-grounded surfaces operators already cold-start from, so a maintained roadmap/todo pair was duplicating state rather than adding signal. Mechanizing their freshness in CI would lock in the duplication; a pointer stub keeps inbound links resolving without pretending the board is current. | Recorded here; the edits are made on chore/archive-roadmap-todo. Stubs preserve the original paths so no inbound doc link breaks and make check-docs stays green; todo.md Status becomes superseded. No code, schema, or RPC change. | Restore a maintained, curated forward-sequencing roadmap if operators find the bootstrap/brief pair misses the forward planning the old roadmap provided. |
| D231 | accepted | Close #362 (the four §H product/architecture cut-or-keep calls): KEEP the remaining three surfaces — the one-shot migration RPCs (corpus_migrate / artifact_backfill_blob), the auto-finalize circuit breaker (runtime table go/pkg/db/sql/0014_auto_finalize_circuit_breakers.sql + the recovery_auto_finalize reads/status surface), and conversation.* (RFC 0086 / D144 / D145). The earlier go/pkg/crossrepo/ keep clause is superseded by D270. The remaining surfaces are not cut without a dedicated evidence pass proving zero current AND near-term consumers; the obvious dead-code framing was already refuted twice in the #357 pass. | A "looks unused, delete it" framing was floated for each surface and falsified on inspection: each either has a live caller, a one-shot migration role, or a documented future-consumer contract. Cutting on appearance alone risks deleting a load-bearing or near-term surface; the bar to remove is an evidence pass, not a code-reading impression. Crossrepo's later removal is recorded separately in D270. | Recorded here; no surface removed by this row. The auto-finalize circuit breaker is a RUNTIME migration (0014_auto_finalize_circuit_breakers.sql), NOT owner bundle 0014 (which is 0014_chain_lock_wait_gauges.sql); the #362 framing of "owner table 0014" was inaccurate and is corrected here. Crossrepo removal is D270. | Per remaining surface: the corpus/blob migrations are confirmed permanently complete (then retire the one-shot RPCs); or conversation choreography is proven unused (then an explicit product decision may remove conversation.*). |
| D230 | accepted | Close #348 (RFC 0131 OQ3): pursue the confidence-model path (RFC 0131 layers 131-A..D, accepted D211) as the near-term direction; do NOT build the PTY-shim-all-pipe-lanes alternative now. Keep the shim as a tracked alternative to EVALUATE after 131-A + 131-C land, when the actual pipe-lane misclassification rate can be measured. | 131-A is the cheaper first step and is already specced; the PTY shim would obsolete liveness layers 2-4 but its per-lane cost (wrapping every pipe lane so confirmedDead() becomes universal) is unknown. Choosing the confidence model now keeps the cheap incremental path moving while preserving the dissolve-the-problem option behind a measurement gate rather than discarding it. | Recorded against RFC 0131 (D211). No new code in this decision; the shim stays a documented alternative in 0131's OQ3, to be re-decided with data. | Re-evaluate the PTY-shim alternative after 131-C lands and the pipe-lane misclassification rate is measured; if the confidence model misclassifies often enough, the shim's universal confirmedDead() may be worth its per-lane cost. |
| D229 | accepted | Close #343 (RFC 0132 OQ): adopt the artifact + escalation_inbox BLOCKER form (the live dissent_ledger from #339 plus the #341/#342 surfacing) as the v1 dissent-protection mechanism; the heavier dissent_quarantine run-state form is NOT planned unless override-rate fatigue is observed in practice. | The append-only ledger plus inbox surfacing already protects dissent without taking on a new owner-held runs.state CHECK and its deploy-ordering cost; a run-state quarantine form is only justified if operators actually suffer override-rate fatigue, which has not been observed. | Recorded against RFC 0132 OQ. The #343 body is stale: dissent_quarantine no longer maps to owner bundle 0013/0014 (taken by barrier_assembly and #372/#379 per D215/D218); if ever built it must use the next free owner bundle. No code change in this decision. | Build the dissent_quarantine run-state form (on the next free owner bundle) only if production override-rate fatigue appears. |
| D228 | accepted | Close #340 (RFC 0132 OQ): DESCOPE the daemon-signed abstention-stub ARTIFACT. D214(b)'s skip-only-provably-dead-seat is already satisfied by the live oracle probe at evaluation time (go/pkg/mutations/barrier_quorum.go seatStructurallyUnrecoverable, bound to supervisedAgentConfirmedDead) — the verdict-less seat-holding semantics + dead-only skip are implemented and tested. The separately-specified signed stub artifact (HMAC, content_withheld class, boot-epoch replay defense) is NOT required for the ratified mechanism. | The ratified D214 mechanism holds a seat and skips only a provably-dead one without ever needing a durable signed stub: occupancy is computed from live job/lease/oracle state at quorum-evaluation time, so the HMAC-signed artifact would add a persistence and replay-defense surface that buys nothing the live probe does not already give. | Recorded against RFC 0132 OQ. The dead-only skip is implemented at go/pkg/mutations/barrier_quorum.go (seatStructurallyUnrecoverable → supervisedAgentConfirmedDead); no signed-stub artifact, HMAC, or boot-epoch replay machinery is added. | File a focused follow-up if durable audit/replay of abstentions is later wanted (e.g. to reconstruct a past quorum decision offline); only then does the signed stub artifact earn its surface. |
| D227 | accepted-with-revisions | ACCEPT RFC 0134 WITH REVISIONS (#394), and explicitly REJECT daemon-gate-path execution that inherits the lane's process.run/write_scope posture (that is daemon-reach RCE/exfil against the PG socket, the runtime token, and other repos). Adopt this architecture: (1) ship the CLAIM-STATUS LATTICE half FIRST — claim_ledger with VERIFIED > ASSERTED > DESIGNED as a first-class artifact, monotonic + append-only at the daemon writer, demotable-but-never-self-promotable, auto-decaying VERIFIED → ASSERTED when bound input hashes change — pure validation, NO execution; (2) any executable verification runs in a DISPOSABLE sandboxed verifier LANE (reusing the lane sandbox + supervisor-helper machinery) OFF the gate path — verify is its own job whose durable receipt the completion gate merely READS; a missing/wedged verify degrades the claim to ASSERTED, never blocks completion on engine liveness; (3) the daemon VALIDATES, never executes — checks[] are content-addressed against an operator-curated, git-tracked allowlist (a lane NAMES but never AUTHORS the executed bytes), and the daemon only validates a tamper-evident transcript receipt (argv + resolved binary hash + exit code + stdout digest + cwd tree-sha) bound to the worktree tree-sha; VERIFIED (top rung) requires TWO signals (sealed receipt + independent re-execution agreement), a lone exit-0 earns only ASSERTED; timeout / envelope-violation / network-touch → INDETERMINATE, never VERIFIED, with cgroup-enforced caps so a runaway check kills its scope, not the daemon. This re-scopes build issue #395: lattice + ledger + provenance-lint first, sandboxed off-gate-path verifier second — NOT a daemon-gate-path executor. | The cam-analyzer dogfood found documentation outrunning implementation; a verification gate is the right instrument, but executing arbitrary lane-named commands ON the daemon's completion path inherits the daemon's PG socket, runtime token, and cross-repo reach — a remote-code-execution/exfil surface, not a gate. An /adhd divergent pass (regulator / attacker / 3am-on-call / remove-assumption / biology frames) converged independently on validate-not-execute: the daemon should validate tamper-evident receipts and curate the executed bytes, while any execution happens in a disposable sandboxed lane off the gate path, so engine liveness can never block completion and a runaway check can never kill the daemon. | Accepted with the revisions above; binds RFC 0134 implementation and re-scopes #395. The skip-only-provably-dead oracle and lane-sandbox/supervisor-helper machinery the verifier lane reuses already exist; the daemon adds a content-addressed allowlist check and a receipt validator, not a command executor. RFC 0134 header is re-statused accepted-with-revisions with an "Accepted form" note summarizing validate-not-execute. | Reopen if a sandboxed verifier lane proves insufficient for a real verification need, or if the two-signal VERIFIED requirement (sealed receipt + independent re-execution agreement) is too strict in practice. |
| D226 | deferred | Close #404 (RFC 0115, token-usage telemetry): DEFER. Owner is the proposing author (codex, proposer-codex-gpt-5-001); the work is low/med priority and depends on dashboard-ingest. As proposed it is product-boundary-clean (local scrubbed JSONL, no hosted or DB telemetry). Disposition: defer; do not schedule now. | The telemetry is genuinely useful but not urgent, and its natural consumer (dashboard-ingest) has not landed, so building the producer now would ship a source with no reader. The proposal already respects the product boundary, so the only open question is timing, and timing says wait. | Recorded as deferred; RFC 0115 stays proposed/deferred with a D226 pointer. No code, schema, or RPC change. | Schedule when dashboard-ingest lands or when token-usage telemetry becomes a priority. |
| D225 | deferred | Close #403 (RFC 0052, committee deliberation): the committee-deliberation dogfood is UNBLOCKED (RFC 0048 completed) but DEFERRED / unscheduled. RFC 0052 still owns full committee-deliberation semantics (cf RFC 0074, which carries only the catalog/generator slice). Disposition: keep RFC 0052 live as a tracked future capability; schedule the design→build dogfood when committee semantics are next prioritized. | The blocker (RFC 0048) is gone, so the capability is now buildable, but it is not the next priority; keeping the RFC live-but-unscheduled preserves the design intent without committing dogfood time now, and avoids RFC 0074 (catalog slice) being mistaken for the full semantics. | Recorded as deferred; RFC 0052 stays proposed (unblocked, unscheduled) with a D225 pointer. No code change. | Reopen to schedule the committee-deliberation design→build dogfood when committee workflow semantics become the next priority. |
| D224 | accepted | Close #400 (RFC 0042, run-list workflow identity): RE-SCOPE RFC 0042 to the live Go SSE UI; its Phase A targeted the now-deleted Python UI. The problem is still real — go/pkg/webassets/templates/page.html renders only run_id + branch_name, with no workflow.name. Strip the stale src/striatum/... Context paths from the RFC. The actual run-list workflow.name surfacing + workflow link is the re-scoped RFC's implementation work, NOT this decision row. | The original RFC named Python templates (run_list.html, service.py) that no longer exist, so it read as stale even though the operator-triage problem it describes (an unnavigable run list with a — workflow cell) persists in the Go SSE UI. Re-pointing the RFC at the live surface keeps the real problem tracked instead of letting the RFC rot against deleted paths. | Re-scoped, not retired; RFC 0042 Context paths are corrected to the Go SSE UI and the stale src/striatum/... references are stripped. The implementation (surfacing workflow.name + a workflow link in page.html) remains the RFC's open work, not part of this decision. | n/a (re-scope). |
| D223 | accepted | Close #401 (RFC 0101): RE-STATUS RFC 0101 as the UMBRELLA-OF-RECORD for robust autonomous execution and map its five defense layers to shipped slices — L1 go/pkg/lanehealth; L2 the RFC 0096/0101 go/pkg/adapterconformance fixture; L3 RFC 0095 attempt primitives + recovery verbs; L4 needs_operator (RFC 0099); L5 go/pkg/adapterconformance/chaos_test.go (promoted by RFC 0105). Reconcile with RFC 0103 (accepted, W1–W7) so the two stop double-tracking the residual defense tail. | RFC 0101's five recurring failure-mode layers were each addressed by a later, separately-tracked slice, leaving the umbrella RFC reading as "in progress" while its layers had shipped; mapping each layer to its landed slice makes the umbrella honest, and reconciling with RFC 0103 stops two RFCs from each claiming the leftover hardening work. | Re-statused as the umbrella-of-record with the layer→slice map above. All five named landing sites exist (lanehealth, adapterconformance incl. chaos_test.go, RFC 0095/0099 recovery surfaces). No code change in this decision. | Spin a layer out into its own slice-RFC if a defense layer needs further dedicated work beyond the residual tail RFC 0103 already tracks. |
| D222 | accepted | Close #399 (RFC 0098 ACE): the ACE shape is ALREADY graduated to supported — go/pkg/workflowtemplates/catalog.go lists adjudicated_constraint_extraction in supportedShapes, backed by the green reliability fixture go/pkg/adapterconformance/ace_interrogation_test.go and enforced by go/pkg/adapterconformance/shape_tier_guard_test.go. That deliverable is DONE. Slice-4 first-class constraint.* objects are DEFERRED per RFC 0098 §6 (justified only once a second workflow reads constraints across runs); the §7 coverage metrics (observability-only) are optional-future. | The graduation work the issue asked for has shipped and is tier-guarded, so the only live questions are the deferred slice-4 and the optional metrics; first-class cross-run constraint objects are premature until a second consumer actually reads constraints across runs, so deferring them avoids building a persistence surface with one writer and no second reader. | Recorded; RFC 0098 header re-statused to reflect ACE graduated supported + slice-4 deferred. The shape id in supportedShapes is adjudicated_constraint_extraction (not a bare ace), backed by ace_interrogation_test.go and guarded by shape_tier_guard_test.go. | Build slice-4 first-class constraint.* objects when a second workflow needs to read constraints across runs. |
| D221 | accepted | Close #398 (RFC 0066): the augmentation-by-reference model was already decided (D126); the optional augmentation-reference FETCH surface is DEFERRED build-on-demand. Any future build MUST be reference-only / optional / local, with an augmentation-boundary regression proving Striatum runs with the source unavailable, and MUST NOT add import engram or a memory.* capability. | The replay/archive/corpus-V2 core has shipped; the only residual is an optional fetch surface that has no in-repo consumer yet, so building it now would add an augmentation entry point ahead of demand and risk eroding the hard augmentation-not-dependency boundary. Deferring build-on-demand keeps the boundary intact until a real consumer needs it. | Recorded; RFC 0066 header notes the fetch surface deferred build-on-demand with a D221 pointer. The non-negotiable invariants (reference-only/optional/local, augmentation-boundary regression, no import engram, no memory.* capability) bind any future build. No code change in this decision. | Build the augmentation-reference fetch surface when a concrete in-repo consumer needs it. |
| D220 | accepted | Close #397 (RFC 0064): RFC 0064 is accepted/implemented — durable accepted-risk persistence shipped (runtime migration go/pkg/db/sql/0013_workflow_accepted_risks.sql, the workflow.accept_risk RPC + CLI + lint rule-set; cf D157). No further authoring-helper polish is planned; the obsolete "Blocked Policy" clause is RESOLVED. | The durable-authority question the RFC's "Blocked Policy" section raised (where accepted-risk evidence lives) was answered by shipping an append-only daemon-owned table, so the section is now historical; no specific lint/diversity gap is open, so no further authoring-helper polish is scheduled. | Recorded; RFC 0064 already carries Accepted / implemented and a Blocked Policy (RESOLVED 2026-06-18) note pointing at #397 — this decision confirms that disposition and supplies the D220 pointer. The persistence table is the runtime migration 0013_workflow_accepted_risks.sql. No code change in this decision. | Reopen only if a specific lint or review-diversity gap is found in practice. |
| D219 | accepted | Close #396 (RFC 0102 lever-2): FOLD the attention-view principle rather than build a net-new verb. Lever-1 is realized indirectly via run drive (RFC 0116 / D175) + auto-drive (RFC 0124 / D191); the Track-2 attention frame already exists as striatum dashboard --run-id/--once and the blocking-frontier reads in go/pkg/reads/status.go + go/pkg/reads/concurrent_runs.go. No new persistence or verb. | The attention-economy levers RFC 0102 named are already served by existing surfaces — run/auto-drive cover lever-1, and the dashboard plus blocking-frontier reads cover the Track-2 attention frame — so adding a dedicated attention verb would duplicate state and surface area without adding signal. Folding the principle into the existing surfaces is the smaller, honest move. | Recorded; RFC 0102 header notes lever-2 folded (D219), no net-new attention verb. The named surfaces exist (dashboard --run-id/--once, reads/status.go, reads/concurrent_runs.go). No code, schema, or RPC change. | If the dashboard proves insufficient, file a ready-for-agent spec for a named attention view (the OQ4 single-next-action ranking). |
| D218 | accepted | #372/#379 add chain-head lock-wait observability without changing authority or indexing posture. Owner bundle 0014 adds nullable lock_wait_us gauges to striatumd.events and striatumd.audit_log and restates the existing SECURITY DEFINER append functions to measure the FOR UPDATE wait on repo_event_chain_heads and audit_chain_head; the gauge is excluded from row-hash inputs. doctor reports event_chain_head_lock_convoy and audit_chain_head_lock_convoy as warning-only, sampled by bounded tails rather than a new index. Missing 0014 columns are a skipped check, not a red doctor result, so binary-before-owner-bundle deploys are tolerated. | The performance review identified event/audit chain-head lock waits as the blind spot, but adding write-path indexes or making missing observability a hard failure would add cost or break deploy ordering. The useful operator signal is "a convoy happened recently" rather than an exhaustive table scan. | Implemented by owner bundle 0014 and go/pkg/reads/doctor_lock_wait.go. Event sampling is scoped by recent/active candidate runs plus per-run descending event tails; audit sampling uses a newest-audit_id tail. No runtime migration, no CLI/API flag, no new index. The earlier D215/RFC 0132 dissent_quarantine 0014 reservation is superseded; that optional run-state form must use the next available owner bundle if it ships. | Revisit if operators need quantile history beyond bounded doctor sampling, if the event/audit chain heads are sharded, or if a future owner-bundle metadata system reserves versions explicitly instead of relying on shipped SQL files. |
| D217 | accepted | #378 P1.2: supervisor.progress is a chain-exempt liveness sample. supervise.report still accepts helper progress reports and meaningful progress still updates supervisor heartbeat, session PTY liveness, and the active lease when present, but the raw report no longer appends supervisor.progress to the durable per-repo event hash chain. No new off-chain table is introduced; existing liveness projections keep reading process_supervisors, process_supervisor_pointers, session activity, and lease state. | The concurrency review found the per-repo chain head itself was not the broken primitive, but supervisor.progress dominated event volume as pure liveness chatter. Chaining it made volume/timing evidence behave like durable provenance and amplified chain-head churn without serving production readers; dashboard/status projections already derive supervisor progress from supervisor/session/lease tables rather than events.event_type = 'supervisor.progress'. | Implemented in go/pkg/mutations/supervision.go. Lifecycle supervisor reports still append supervisor.<event> rows, and a meaningful progress report that refreshes an active lease still appends the derived lease.heartbeat event because that is the durable lease-state transition. No migration, no RPC shape change, no new table. Tests pin meaningful-progress liveness/lease updates and assert no supervisor.progress insert for meaningful, plain, or no-lease progress reports. | Revisit only if a future product requirement needs tamper-evident volume or timing evidence for raw progress samples; that would require a fresh provenance decision rather than silently reusing the event chain. |
| D216 | accepted | Ratify the full-span shared barrier (#354) and accept RFC 0135. BUILD THE SHARED ATTEMPT-SEALED BARRIER PRIMITIVE FIRST, spanning ALL FOUR callers — fan-in (RFC 0133), RFC 0132 quorum, RFC 0095 revision-coherence, RFC 0108 run.integrate — rather than four ad-hoc predicates each re-discovering the stale-attempt trap (RFC 0133 OQ3 / RFC 0132 OQ2). The load-bearing design: key the primitive on (stable entity id, monotonic SEAL counter), NOT raw attempt. attempt (fan-in/0132), review_generation (0095/0126), and run-completion (0108) are PROJECTIONS of the same (entity, seal) abstraction; readiness JOINs each in-edge's staged contribution against the entity's LIVE seal (staging.seal = entity.live_seal), never counting refs per entity (RFC 0133 synthesis-trap-#1 killer, generalized). A monotonic seal does not churn under recovery, so RFC 0095/0126 folds in WITHOUT regressing off review_generation (review_generation BECOMES the seal for review entities); RFC 0108 folds in with entity=run (the primitive is generic over entity ∈ {job, run}; job barriers compose into a run barrier). | The maintainer chose the full-span option (#354) with full knowledge of the source finding that the mechanism is NOT natively shared — RFC 0126/D194 deliberately rejected attempt-keying because recovery churns attempt, and RFC 0108 is run_id-keyed at a higher layer. Designing the barrier once (one audited named predicate, one doctor check, one chaos suite) before the four callers each ship a predicate avoids reconciling four trap-rediscoveries later. The seal-not-attempt keying is the abstraction that lets the generation-keyed caller fold in without regression. | Accepted as design; IMPLEMENTED P0–P6 (v2.34.0) — predicate db.BarrierReadySQL, callers barrier_{fanin,assembly,quorum,run_entity}.go, doctor barrier_integrity, striatum join verify, migrations 0029–0032, owner bundle 0013; P5 revision-coherence is the live active path while P6/run.integrate + the fan-in/quorum folds ship opt-in/shadow behind D206. RFC 0135 re-casts the RFC 0133 slice issues #344–#347 as the primitive's job-entity callers and notes #338–#343 (RFC 0132) consume the same predicate; P5 (revision coherence) and P6 (run.integrate) are the bet-folds, shipped last behind same-final-tree equivalence fixtures. Schema per D215. #354 becomes the RFC-0135 umbrella. make check-docs green. FULL CUTOVER OUTCOME (D233, #354): P4 (quorum) and P6 (run.integrate) FLIPPED to the LIVE default behind recoverable env kill switches (STRIATUM_BARRIER_QUORUM, STRIATUM_BARRIER_RUN_ENTITY) with proven same-decision equivalence fixtures; P5 confirmed already live; P1/P2 (fan-in deferred-join + recoverable assembly) STAY SHADOW (D206 per-completion merge remains the default fan-in path) because the live wiring — a barrier_assembly dispatcher + staging-at-completion + downstream-gate-waits-on-barrier — is not built, so flipping it is an unproven behavior change, not a provable equivalence flip. The four-way abstraction held for three callers; the fan-in fold's assembly is proven byte-identical (TestFaninBarrierSameFinalTreeAsPerCompletion) but its live wiring is not, so the partial proven cutover is the correct outcome per the equivalence-fixture discipline. | Reopen if the (entity, seal) abstraction proves wrong for P5/P6 (the primitive then degrades to fan-in+quorum sharing a predicate — still a win, not the four-way win); or if a future change makes review_generation churn on recovery (which would silently re-key revision coherence onto a churning counter and re-open the RFC 0095/0101 stale-verdict wedge). |
| D215 | accepted | Ratify the RFC 0133/0135 schema-ownership split per-object (#333). barrier_assembly job_type CHECK widening => OWNER BUNDLE 0013 (mandatory): jobs is owner-held, so a runtime ALTER of its CHECK fails the build guard TestFutureRuntimeMigrationsDoNotCarryOwnerDDL (go/pkg/db/migrations_test.go:423) AND crash-loops a two-role production daemon (D187 / #244); mirror go/pkg/db/sql/owner/0012_job_quarantine_state.sql. New tables (freeze / staging / barrier_state) => RUNTIME migrations with NO SQL FOREIGN KEY to striatumd.jobs (the FK-to-owner-table trap): keep the (repository_id, run_id, workflow_job_id, attempt/seal) key as bare columns and enforce integrity in Go; each new table needs its own explicit GRANT (pgtest masks an omitted grant). join_manifest.v1 => no DDL (an artifactcontracts registration). The original 0013/0014 coordination with RFC 0132's reserved dissent_quarantine bundle has since resolved: barrier_assembly took 0013, #372/#379 took 0014, and dissent_quarantine must use the next available owner bundle if it ships. | The schema for the shared barrier touches an owner-held CHECK, new runtime tables, and an artifact contract — three different ownership regimes. A blanket "all owner bundle" or "all runtime" rule is wrong: a runtime ALTER of the owner CHECK crash-loops prod, while an FK from a runtime table into the owner jobs table re-creates the cross-role problem, and the manifest needs no DDL at all. The per-object split is the only form that satisfies the D187 boundary and the build guard simultaneously. | Accepted; binds RFC 0133 and RFC 0135 implementation. The owner-DDL build guard and the FK-trap discipline are recorded in RFC 0135's Schema section verbatim; the explicit-GRANT-per-table requirement is load-bearing because the default pgtest harness (single-role, runtime-only) cannot catch an omission. | Reopen if generated migration metadata can tag owner-only bundles (removing the path/guard convention reliance, per D187's own revisit trigger), or if a two-role test harness lands that catches a missing GRANT so the masking risk is closed. |
| D214 | accepted | Ratify RFC 0132's open questions strictly (#332). (a) The daemon-authored abstention stub HOLDS A SEAT / raises the frozen denominator but carries NO verdict value — no verdict field (RFC 0132 OQ1 ratified strictly: the daemon may never cast a vote, only refuse to vacate a seat; the abstention budget is the only thing that can clear an unfilled seat). (b) Skip-only-provably-dead-seat: a panel may NOT finalize-by-quorum while any gating seat is LIVE; quorum may skip ONLY a structurally_unrecoverable seat, bound to the existing forgery-resistant supervisedAgentConfirmedDead oracle (go/pkg/mutations/recovery_decision_tree.go:983) — quorum may skip a DEAD seat, never a slow one (RFC 0132 OQ2; collapses the content_withheld rule into the stronger "never race a live reviewer"). The quorum-shape generalization (k_of_n) is DEFERRED to lint, NO schema change (RFC 0132 OQ3). | Finalize-by-quorum must never collapse a missing voice into false unanimity (the never-fabricate-a-vote axiom keeps it inside the RFC 0118 attestation gate) and must never let a hostile/slow scheduler reach quorum past a live dissenter (the inversion in OQ2 — the threat is the SLOW reviewer, not only the dead one). The verdict-less stub and the dead-only skip are the load-bearing protections; deferring k_of_n to lint keeps the predicate simple and schema-stable. | Accepted; binds RFC 0132 implementation and the RFC 0135 quorum instance (#338–#343 / P4). The stub is seat-occupancy state, not a sealed contribution, so it cannot satisfy staging.seal = entity.live_seal; structurally_unrecoverable is the only is_terminal_gap. k_of_n desugars in go/pkg/workflowauthoring/lint.go. | Reopen if the liveness-confidence model (RFC 0131/D211) cannot reliably distinguish a slow live seat from a dead one (the dead-only-skip rule then needs the two-independent-dead-signals classifier RFC 0132 names), or if k_of_n quorum shapes turn out to need a schema column rather than lint desugaring. |
| D213 | accepted | Accept RFC 0133 — graduate fan-in from the shipped D206 per-completion run-branch merge to a deferred post-completion join barrier + join manifest (#319). The barrier predicate JOINs each in-edge's staging ref against the LIVE attempt (staging.attempt = jobs.attempt, keyed by the existing UNIQUE (repository_id, run_id, workflow_job_id, attempt)), never counting staged refs per job — so a requeued/resumed/complete-stalled attempt's stale ref is structurally invisible and cannot re-strand the real output behind a successful join (synthesis trap #1). Slices: manifest-first (join_manifest.v1 provenance artifact, can land on today's merge); attempt-addressed staging refs + the live-attempt JOIN barrier (immutable freeze record, requeue tombstone, recovery/-prefix exclusion, merge-base contamination check, advisory-lock fire serialization, quarantine-as-terminal-in-edge); and assembly as a recoverable barrier_assembly job with two-phase journaling, routing N=1 through the one path. | #319 was kept open as the robust graduation of the already-fixed D206 stranding bug: one-merge history, one doctor invariant, linear+fan-in unified. The (entity, attempt) barrier predicate is the load-bearing correctness property; the captured per-job ref-count approach would re-introduce the stranding bug. | Accepted as design; IMPLEMENTED via RFC 0135 P1 (#345, v2.34.0) — barrier_fanin.go (entity=job, seal=attempt) over db.BarrierReadySQL, migration 0029, recoverable barrier_assembly job (P2/#346), join_manifest.v1; ships opt-in/shadow behind D206, and RFC 0135 (D216) generalizes it to the shared primitive while 0133 stays the authoritative fan-in design-of-record. Implementation questions were resolved by D215/D216: the barrier_assembly job_type CHECK ships in owner bundle 0013, and freeze/staging/barrier_state tables ship as runtime migrations with no SQL FK to owner-held jobs and explicit grants. make check-docs green. | Resolve the cross-cutting Open Question: ship a general attempt-sealed barrier primitive shared with RFC 0132 / RFC 0095 / RFC 0108 instead of a fan-in-only predicate; and the run-branch-as-projection-of-the-barrier-chain wildcard. |
| D212 | accepted | Accept RFC 0132 — gating/advisory reviews + quorum with dissent protection (#311 P2). Per-reviewer panel_role: gating\|advisory (default gating, lint-validated); a panel finalizes on a quorum of gating seats over a FROZEN declared-seat denominator (quorum reframed as a ceiling on tolerated silence; max_gating_abstentions default 0). Dissent is FORWARD-WRITTEN to an append-only dissent_ledger keyed on the stable workflow_job_id (the captured backward verdicts-at-attempt query was fragile — recovery churns job_id/session_id, so a transferred reject reads as absent). Attestation axiom: the daemon may only assert "seat unfilled", never fabricate a vote, and may quorum-skip only a provably-DEAD seat (two independent dead-signals), never a live-but-silent one (silence is not consent). Advisory votes are non-binding but never silent (loud unanimous-reject + a mandatory advisory_minority_report.v1 artifact). | One flaky reviewer wedging an otherwise-complete committee run (the #311 incident) needs panel-level quorum, but finalize-by-quorum must never collapse a real disagreement into false unanimity nor deadlock forever; the forward-written ledger + the never-skip-a-live-seat rule are the load-bearing protections, and the never-fabricate-a-vote axiom keeps it inside the RFC 0118 attestation gate. | Accepted as design; IMPLEMENTED via RFC 0135 P4 (#338/#339/#340, v2.34.0) — panel_role validated in workflowauthoring, quorum evaluator barrier_quorum.go over the frozen declared-seat denominator, migration 0032 dissent_ledger, verdict-less abstention stub + skip-only-provably-dead-seat (D214); ships opt-in/shadow behind D206. panel_role/quorum_json are workflow_json (no DDL); dissent_ledger is a new runtime table; the optional dissent_quarantine run-state form requires an owner bundle for the owner-held runs.state CHECK and must use the next available owner bundle number. Sliced into issues (label rfc-0132). | Resolve OQ1 (ratify the never-fabricate-a-vote axiom strictly so a stub never carries a verdict value) and OQ2 (forbid finalize-by-quorum while any gating seat is live-and-working — tightens quorum semantics, interacts with RFC 0131). |
| D211 | accepted | Accept RFC 0131 — transport-aware liveness confidence + escalation gating (#311 P1; composes with the shipped D209 P0 quarantine). sessionliveness.Classify() becomes transport-aware, emitting TransportType + a typed probe_basis (pty_confirmed_dead vs deadline_elapsed_only); a deadline_elapsed_only stall on a pipe lane with no confirmed-dead oracle is confidence-gated (compounding misfire_evidence_score + a two-sweep debounce + free cohort cross-lane corroboration from recoverStuckJobs' existing FOR UPDATE scan) before it may escalate the run. The load-bearing invariant: a finite escape-valve cap keyed off forgery-resistant SEALED-WORK PROGRESS (artifact-anchor / worktree-HEAD / sealed-verdict advancement), NOT raw output — a #324-style dead-but-spinning loop can forge chatter but not progress — so a genuinely-hung pipe lane is escalatable in bounded time and never un-escalatable. | A pipe lane (agy/Gemini, no pty_helper oracle) read as stuck too easily, restart-looped to budget_exhausted, and pre-P0 discarded a whole run; P1 gates the DECISION to escalate while P0 contains the consequence. | Accepted as design; Layer 1 (131-A, #334) IMPLEMENTED — sessionliveness.Classify() now stamps a typed Result.ProbeBasis (deadline_elapsed_only for every stall the pure classifier produces) and threads Activity.Transport (pty_helper/pipe/unknown, derived in ActivityFromRow from the supervisor pointer metadata); the recovery decision tree upgrades a pty_helper stall to pty_confirmed_dead via UpgradeProbeBasisConfirmedDead once supervisedAgentConfirmedDead() fires, and carries transport + probe_basis onto the recovery.budget_exhausted event + requeue/transfer action records (OUTPUTS only, no migration). Layer 2 (131-B, #335) IMPLEMENTED — Classify()'s pipeMidRPCFresh rung reads a pipe lane mid-RPC (fresh last_mcp_request_at) as working_local rather than stalling it on the stale await-packet/ack rungs, scoped to pipe transport and preserving dead-lane detection. Layers 3+4 (131-C, #336) IMPLEMENTED — migration 0035_job_recovery_confidence_gate.sql (substrate_version → 35, runtime-owned job_recovery_state, allowlisted past the floor-27 owner-DDL guard) adds misfire_evidence_score/consecutive_silent_sweeps/last_probe_basis; recoverStuckJobs confidence-gates a budget-exhausted, still-present, non-confirmed-dead deadline_elapsed_only lane (forgery-resistant sealed-artifact/sealed-verdict progress via jobSealedProgressAt + cohort corroboration reset, else two-consecutive-sweep debounce emitting recovery.escalation_debounced) with a Layer-4 escape-valve cap (maxRequeues*2)+3 (floored at 3, recovery_policy.max_silent_sweeps override) that fires regardless of confidence — so a hung pipe lane is escalatable in bounded time, never un-escalatable; degrade-safe behind the migration. 131-D (#337, doctor/legibility) remains. Sliced into issues (label rfc-0131). | Resolve OQ3: dissolve the problem by wrapping every pipe lane in a thin PTY shim so confirmedDead() is universal (would obsolete the confidence model); and whether the escape-valve cap should be topology-adaptive. |
| D210 | accepted | Wave 2 of the 2026-06-17 open-issue triage (sequel to D208/D209). (1) #298 — new recovery quarantine-lane <run-id> <job-id> verb (capability recovery) snapshots a TERMINAL run's dirty lane worktree to an auditable refs/striatum/quarantine/<run>/<job>/<attempt> ref + append-only recovery.lane_quarantined event (daemon-owned scratch-index write-tree → commit-tree, never disturbing the lane) BEFORE removing it; worktree gc is hardened to SKIP (reason dirty_uncommitted_work), never silently --force-discard, a dirty terminal-run worktree. (2) #316 — boot-epoch identity: the daemon mints a per-process boot-epoch (distinct from the restart-stable instance id), injects it into the lane (STRIATUM_MCP_BOOT_EPOCH → alias-agnostic X-Striatum-Boot-Epoch header), and rejects any MCP request whose epoch differs from the live daemon's — before bearer validation/dispatch — with the distinct stale_daemon_identity code, closing the #296 recycled-port misroute; backward-compatible (no-epoch requests allowed). (3) #306 — the gated DEEPENED picks in divergent_ideation are git-retained (git_publication placement) for git-only auditability (the diverge IDEAS the issue named were already git-retained; the DEEPENED picks were the real blob-routed gated inputs). (4) #299 — confirmed ALREADY FIXED by the merge-based run integrate (3-way merge preserves intervening main work; landed 2026-06-04, before the report) + added the missing regression test. (5) #305 — RESOLVED wontfix-by-design (see Reason). | The terminal run-state legibility ask (#305) conflicts with the RFC 0033/0043 boundary: PostgreSQL is the authoritative live workflow state and repository files are durable ARTIFACT provenance, not a run-state mirror. The terminal disposition is authoritative in the run_completion_record (now also carrying #311's quarantine manifest + stop_reason) and is exposed for live audit via striatum status / dashboard / run.summary and for OFFLINE/git-only audit via archive export (the sanctioned offline-audit artifact). Committing a terminal-state marker to the run branch would duplicate authoritative state into provenance, creating a second source of truth that drifts (a later recovery transition would not update a committed marker) — exactly the coupling the boundary forbids. The #298/#316 gaps were genuine (silent dirty-worktree data loss; recycled-port misroute) and provenance/security-delicate, so implemented directly with per-fix tests. | Per-fix commits on wave2-2026-06-17; owner bundle 0012 (#311) applied out of band before the new daemon image; make -C go vet lint check-tests green. New RPCs recovery.quarantine_lane, recovery.accept_quarantined (#311) + the distinct stale_daemon_identity error code, all in the authority matrix + generated routes. #305 closed without code (decision). | Reopen #305 if a maintainer decides git-only terminal legibility is worth a single committed disposition marker despite the duplication; graduate #319 (fan-in join barrier) and #311 P1/P2 (transport-aware liveness, quorum) from their captured designs; revisit #316's no-epoch-allowed posture once all lanes are known to carry the epoch (then enforcement can become mandatory). |
| D209 | accepted | #311 P0 — per-job quarantine + run finalize-the-majority. When a SINGLE job exhausts its autonomous-recovery budget but its downstream is clear, the recovery decision tree now moves ONLY that job to a new non-terminal quarantined state and lets the run finalize on its already-completed deliverables (recording a quarantine manifest), instead of flipping the WHOLE run to needs_operator and discarding every completed sibling's work (the #311 incident: one flaky reviewer wedged a run whose other 8 jobs completed). A job is quarantine-eligible only when ALL hold: (a) no unfinished job transitively (multi-hop) depends on it, (b) it is NOT a provenance-required reviewer the RFC 0118 run-completion gate would refuse, (c) the per-run cap recovery_policy.max_quarantinable_jobs (default 1) is not exceeded, and (d) the quarantined job state is permitted by the live jobs_state_check (owner bundle 0012 applied). If ANY guard fails the job falls through to the UNCHANGED whole-run needs_operator escalation. The quarantined job is NEVER completed and NEVER has an artifact sealed on its behalf — it is the one narrow thing surfaced to the operator, who terminalizes it with the new recovery accept-quarantined <run-id> <job-id> verb (resolves the blocker + marks the job canceled-by-operator; idempotent). The run reaches completed (no new run state) carrying the manifest and stop_reason='quarantined_jobs'; a recovery.job_quarantined event names the offending job + lane + stall_class. | A single flaky lane (the #311 agy/Gemini reviewer) flipping the entire run to needs_operator threw away the durable, attested work of every other completed job and forced a full re-drive. Finalize-the-majority preserves the deliverables while surfacing exactly the one job that flaked; the default cap of 1 keeps a genuinely large-scale failure (multiple simultaneous flaky jobs) escalating rather than being silently swallowed. The transitive-downstream and RFC 0118 provenance-gate guards are load-bearing: a job some other work still needs, or a provenance gate the completion gate would refuse, must NOT be quarantine-finalized. | New owner bundle 0012 adds quarantined to the owner-held striatumd.jobs jobs_state_check (idempotent DROP+re-ADD; bundle 0011 reserved for #330, so this lands as 0012 — apply with striatum daemon owner-ddl apply before the new daemon image; a deployment behind on the bundle keeps the pre-#311 whole-run escalation via guard d, never crashing the sweep). escalateExhaustedJobs takes the recovery policy and decides quarantine per exhausted job; maybeCompleteRun excludes quarantined from the non-terminal remaining set and records the manifest into the terminal event + run_completion_record. New RPC recovery.accept_quarantined (capability recovery, single_repo, metadata) wired through the generated routes/registry + authority matrix. Tests in recovery_quarantine_test.go cover the leaf-finalize, downstream-dependent, provenance-reviewer, cap, and accept-quarantined paths; the deployment-safety guard keeps the existing single-leaf escalation tests (e.g. dx_289) green when the bundle is unapplied. No runtime migration; no run-state enum change. | Revisit to raise the default cap, to add a queryable quarantine table over the event manifest, or to let recovery accept-quarantined optionally re-prepare the job as a fresh attempt instead of canceling; and graduate #316/#319 (boot-epoch identity, fan-in join barrier) from D208's captured designs. |
| D208 | accepted | Reliability/legibility cluster from the 2026-06-17 open-issue triage. Three policy-level decisions plus four bug fixes: (1) #326 — in-scope write_scope source publish is now the DEFAULT for any bounded repo-write scope (was opt-in publish_source_changes, on only for code_change); the write_scope is the publication contract and expected_artifacts are required-presence assertions, not an allowlist, so undeclared in-scope files and edits to pre-existing tracked files land on the run branch instead of stranding (regression of #297). (2) #322 — the shared launch predicate runreconcile.PlanLaunch enforces parallelism.max_active_jobs AND an implicit per-lane in-flight cap of 1, so run drive and the auto-spawn scheduler both stop fanning every unblocked job in one tick (the #290/#302 re-trigger). (3) #303 — new recovery prune-debris <run-id> verb prunes terminal-run artifact debris via append-only recovery.debris_pruned tombstone events (the doctor pass suppresses tombstoned rows), honoring the append-only/owner-owned artifacts boundary (no hard delete, no migration); eligibility reuses the exact doctor classifiers. Bug fixes: #330 (owner bundle 0011 hot event-read covering index on events), #327 (fan-in stops mislabeling a 0-conflict merge-tree failure as a disjoint-scope violation; byte-identical sibling paths filtered; same guard on run integrate), #317 (a same-attempt requeue of a published-but-non-durable job is reopened on a fresh attempt rather than wedging on artifact_immutable_byline_mismatch), #323 (the agent loop re-resolves the rotated MCP endpoint+token after a mid-run daemon restart). Doc fix #313 (the non-functional operator-by-hand path is no longer advertised under RFC 0088). | The triage surfaced a cluster of reliability defects that make autonomous multi-lane dogfooding wedge (publication drops in-scope work, an unenforceable serial cap, fan-in/requeue wedges with no recovery, lane orphaning on restart) plus operational legibility gaps (un-prunable debris keeping doctor degraded, a CPU-bound hot query, a falsely-advertised manual authoring path). These are runner/tooling fixes (the operator-implements exception) and several are provenance-delicate, so they were implemented directly with per-fix tests rather than dogfooded on the very runner they repair. | Per-fix commits on fix/reliability-cluster-2026-06-17b; owner bundle 0011 applied out of band (striatum daemon owner-ddl apply) before the new daemon image; make -C go vet lint check-tests green; CHANGELOG updated. Behavior changes: in-scope publish is now default-on (opt out with publish_source_changes:false); max_active_jobs/per-lane cap now actually constrain launches. New RPC recovery.prune_debris (authority matrix + generated routes updated). | Revisit #322 if a workflow legitimately needs >1 job per lane concurrently; #326 if a job needs artifact-only publication (use the opt-out); #303 if a queryable tombstone table is wanted over event tombstones; and graduate #311/#316/#319 (flaky-lane quarantine, boot-epoch identity, fan-in join barrier) from their captured designs. |
| D207 | accepted | repo-write artifact.publish HARD-REFUSES when a per-job-isolated lane has no active per-job worktree, instead of silently falling back to writing the operator's shared, tracked repo_root (#310). The claim packet resolves a job's lane via a session fallback (claim.go: when job.lane_selector_json carries no lane_id, use the owning session's lane_id), so a lane with an empty job selector still believes it is per-job isolated and is supervised as striatum-lane. The publish-time worktree gate (worktreeRequirementForJob) historically read ONLY the job selector via jobLaneID, so an empty selector resolved isolation to off, made the per-job worktree NOT required, and artifactSourcePath then resolved the write target to repoRoot itself — the lane wrote the operator's tracked checkout (files owned by striatum-lane, blocking operator git pull/reset) and bypassed the RFC 0125 daemon-porter, which only writes INSIDE the per-job worktree. The gate now resolves the lane with the SAME session fallback (jobLaneIDWithSessionFallback, a non-locking leases JOIN sessions read), so an isolated repo-write job is correctly required=true and the existing requireActiveWorktreeForJob gate refuses publish with the actionable worktree_required rpc.Error rather than the silent repoRoot write. Invariant: a per-job repo-write lane never writes the shared tracked tree directly; only the daemon porter advances the run branch. | A lane writing the operator's shared repo_root as striatum-lane left the operator unable to git pull/reset without a sudo chown, and it bypassed the porter-anchored provenance the product depends on (the AGENTS.md "do not paste over a broken runner" / lane-sandbox boundary). The claim-vs-publish lane-resolution asymmetry was the root cause: the lane thought it was isolated; the publish gate did not. | go/pkg/mutations/worktree.go: new jobLaneIDWithSessionFallback (prefer job selector, else the active-lease session's lane_id; no active lease keeps the empty value, preserving prior behavior); worktreeRequirementForJob uses it. The refusal reuses the existing worktree_required gate in artifact.go; no new error type. DELIBERATE behavior change: an in-flight per_job repo-write job that relied on the silent repoRoot fallback now refuses. Scoped to worktree_isolation: per_job — the explicit allow_shared_checkout_repo_write shared-checkout path still writes repoRoot as intended. Tests: artifact_worktree_boundary_pg_test.go — refuse-without-worktree (teeth-verified by reverting the fallback), succeed-with-worktree, shared-checkout-still-writes-repoRoot; golangci-lint clean; full pkg/mutations green. No schema/migration/RPC change. | Revisit if a legitimate workflow needs a per-job-isolated lane to publish to the shared tree (today refused), if the lane-resolution fallback should also cover non-lease publish paths, or when RFC 0127 plain-dir workspaces change how repo-write publish resolves its target. |
| D206 | accepted | parallel fan-in siblings are INTEGRATED into the run branch at completion, not stranded under a pin (#290). When N author jobs fan in to a downstream job, only the first to complete fast-forwarded the run branch; each later sibling's worktree had forked from the pre-FF tip, so its HEAD could no longer FF and anchorWorktreeCommitStack only pinned it under refs/striatum/<run>/<job>/<attempt> — durable but unreachable, so the downstream worktree (seeded from run.branch_name) never saw it. The anchor now, when a HEAD cannot fast-forward, INTEGRATES it via a conflict-free object-DB content merge (git merge-tree --write-tree → commit-tree with an explicit committer identity → compare-and-swap update-ref, retried against concurrent sibling movement — the same plumbing as run.integrate/D-RFC-0108), then still pins the exact stack for provenance (now reachable as the merge commit's second parent). Parallel fan-in lanes write disjoint subtrees (parallelism.require_disjoint_write_scopes), so the merge has no overlap; an overlap is surfaced as a LOUD git_commit_apply_failed naming the conflicting paths, never silently resolved to a last writer (which would re-strand a sibling — the exact bug). The FF fast-path (linear / last completer) and revision behavior (a revision forks from the run tip carrying its prior attempt, so its rewrite is a clean 3-way modify) are unchanged. Doctor gains a fanin_sibling_unintegrated WARNING (not an ok-reddening problem; scoped to RUNNING runs) for a completed repo-write job whose HEAD is reachable only via a pin, so the green D205 baseline and historical/terminal runs are untouched and only a live integration regression fires. This is the per-completion (smallest correct) slice of the divergent_ideation #290 design synthesis (docs/campaigns/issue-290-parallel-fanin-design/); the deferred post-completion join barrier and the join manifest remain follow-ups. | Artifacts were durable but not reachable: under any non-first completion order a fan-in sibling's work never reached the run branch, so downstream consumers and run.integrate silently lost it. The campaign dogfooded the bug (a deepen pick had to pull CONVERGENCE.md from the blob store because it was not on the fresh worktree HEAD). The run branch is the single integration point, so integrating every sibling there makes the linear and fan-in cases one mechanism with a doctor-checkable invariant: every completed sibling's stack is reachable from the run-branch HEAD. | go/pkg/mutations/worktree.go: new fanInIntegrateRunBranch (object-DB merge, CAS-retry, loud overlap error reusing parseMergeTreeConflicts); anchorWorktreeCommitStack's diverged branch now integrates+pins (anchor="run_branch_fanin_merge") instead of pin-only; FF fast-path unchanged. go/pkg/reads/worktree_refs.go: the run-branch-first probe order makes a job_pin classification mean "not on the run branch", emitted as the fanin_sibling_unintegrated warning for a completed job in a running run. Tests: merge of a diverged sibling (both files at the tip), overlap-errors-loudly, N-sibling-all-reachable-any-order (the core invariant), realistic-revision attempt-namespacing, and a doctor invariant test; golangci-lint clean; mutations anchor/worktree/integrate/reconstruct/sweep + full reads packages green. No schema/migration/RPC change. | Revisit to graduate to the deferred post-completion join barrier (one merge after ALL siblings reach complete-and-staged, evaluated against each in-edge's LIVE attempt — the synthesis's #1 trap is counting staged refs by job not attempt) and to add the join manifest; or if a fan-in workflow legitimately needs overlapping write scopes (today the loud conflict error assumes disjoint scopes, which the scheduler enforces). |
| D205 | accepted | doctor integrity legibility P1 — take striatum doctor from ok=false (42 actionable-but-historical artifact problems left after D204) to ok=true by adding the two preservation signals D204 deliberately left out, plus a curated baseline for genuinely-lost-but-immaterial content, while keeping ANY uncatalogued real gap red. Three additive rules in the read-only artifact-integrity check (go/pkg/reads/), applied in a fixed order after the existing default-branch-tip match: (Rule A) default-branch history awareness — content whose content_sha256 matches repo_path's blob at ANY reachable revision of the resolved default branch (not only its tip) is durably preserved (merged, then the path was deleted/edited later) and is fully clean for a git-anchor artifact / an artifact_legacy_unverifiable warning for an empty-blob_key legacy artifact; bounded to --max-count=200 revisions of that path, ctx-cancellable, memoized per root\|ref\|path\|sha, safe-degrades to "not preserved". (Rule B) superseded-on-default-branch — when content is unverifiable on every ref/tip/history but repo_path is still live on the default-branch tip (any content), the deliverable landed and only the recorded draft sha is unverifiable: emit a new artifact_superseded_on_default_branch warning (git cat-file -e <tip>:<path>), not a problem. (Rule C) curated acknowledged-loss baseline — for a genuine loss (path absent from the default branch, content on no ref) the daemon consults an optional, tracked docs/operator/doctor-acknowledged-loss.json (schema striatum.doctor.acknowledged_loss.v1, new reader doctor_acknowledged_loss.go, loaded once per repo root); a matching entry whose content_sha256 equals the row's downgrades the problem to an artifact_acknowledged_loss warning carrying the operator's reason/acknowledged_by. The match is sha-bound so a stale/wrong entry can never mask a different future problem at the same artifact id; a missing/unparseable file safe-degrades to an empty set (acknowledged_loss_status: absent\|loaded\|parse_error block field), never an abort. Check order: tip-exact → Rule A history → terminal-debris → Rule B superseded → Rule C acknowledged → genuine-loss problem. The live 16-entry baseline file is NOT authored here: it is operator-curated provenance the operator commits separately after verifying the real residual against the deployed code; this change ships only the reader + schema + fixtures + tests, and the reader safe-degrades while the baseline is absent. | Continues D204: a red doctor is only a usable stop-and-fix signal (the AGENTS.md "Do not paste over a broken runner" guardrail) if ok=false means an actionable, genuine durability gap. D204 reclassified the un-actionable findings but deliberately scoped preservation to the default-branch tip and left no path for merged-then-edited content or for reviewed-immaterial losses, so doctor stayed red on 42 historical artifacts (14 recoverable via history, 12 superseded-at-tip, 16 genuine-but-immaterial dogfood losses) — exactly the residual D204's own Revisit Trigger anticipated ("if 'preserved' should also consider default-branch history (not just tip)"). Restoring a green doctor that still flips red on any uncatalogued real gap is what makes the guardrail enforceable again without operators learning to ignore a permanently-red doctor. | Read-only diagnostic change, no schema/migration/RPC: go/pkg/reads/doctor_artifact_anchor.go (new artifactContentInDefaultRefHistory/pathExistsOnRef helpers, artifact_superseded_on_default_branch/artifact_acknowledged_loss warning codes, a per-pass artifactAnchorPass bundling the default-ref/history/ack caches threaded through checkArtifactAnchor/checkBlobExhaustArtifact, artifactContentPreserved now also consults history, extended artifactWarning switch + acknowledgedLossWarning) and new go/pkg/reads/doctor_acknowledged_loss.go. New block field acknowledged_loss_status and the two warning codes are additive; problems/problem_records/existing JSON consumers (redaction.go) are unchanged. Tests in go/pkg/reads/doctor_integrity_legibility_p1_test.go cover Rule A (history clean), Rule B (superseded warning), Rule C accept, the load-bearing safety cases (not-in-baseline and id-match-but-sha-mismatch genuine loss still red ok), and reader safe-degrade; two pre-existing anchor-mismatch tests were updated to put the artifact on a ref-only path absent from the default branch so they still exercise the genuine artifact_anchor_hash_mismatch problem branch under the new ordering. | Revisit if the per-path history scan cap (200) is ever exceeded by a real artifact path (raise or make it a tunable), if failed-run findings warrant a disposition distinct from canceled, if the acknowledged-loss baseline grows large enough to want its own provenance tooling/validation verb, or once RFC 0127 plain-dir workspaces change how artifact durability is probed. |
| D204 | accepted | doctor integrity legibility — preserved-on-default-branch / terminal-run / legacy artifacts are warnings, not ok=false problems. striatum doctor's artifact/worktree integrity checks (go/pkg/reads/) previously resolved durability ONLY from the run branch + refs/striatum/<run>/* pins, never the repository default branch, and ignored run terminality and pre-blob-storage legacy — so normal post-merge run-branch deletion (which AGENTS.md encourages — "Do not strand pushed branches") and canceled-run debris produced permanent, un-actionable problems (~276 observed). Reclassify the un-actionable findings to a new, additive warnings channel (ok is unchanged: still len(problems)==0), keeping ONLY genuine loss as a problem, via three rules: (1) default-branch preservation — a worktree HEAD reachable from the resolved default branch becomes a worktree_unanchored_on_default_branch warning (the operator should still create the pin, so it stays visible) and a git-anchor artifact whose content_sha256 matches its repo_path at the default-branch tip is fully clean; (2) terminal-run reclassification — worktree/artifact findings whose run is in a terminal debris state (canceled/failed, NOT successful completed) become worktree_debris_terminal_run / artifact_debris_terminal_run warnings; (3) legacy pre-blob-storage — a blob-placement artifact with an empty blob_key becomes an artifact_legacy_unverifiable warning unless its content is also absent from every durable ref and the default branch (then it stays an artifact_blob_metadata_missing problem). The default branch is resolved without hardcoding "main" (readGitDefaultBranchRef: git symbolic-ref refs/remotes/origin/HEAD, then common remote/local main/master), memoized per repo root, and degrades safely to "" (run-branch/pin-only behavior) on any failure. | A red doctor is only a usable stop-and-fix condition if ok=false means an actionable, genuine durability gap. A permanent red built from preserved-on-main, canceled-run, and pre-RFC-0125 artifacts is exactly why operators stopped trusting doctor and silently worked around the few real gaps — the failure mode AGENTS.md → "Do not paste over a broken runner" forbids. The flagged artifacts' files are present on origin/main, and the 150 artifact_blob_metadata_missing are blob-placement artifacts that predate blob storage; none is genuine loss. Restoring doctor's signal makes the guardrail enforceable again. | Read-only diagnostic change in go/pkg/reads/{worktree_refs.go,doctor_artifact_anchor.go,doctor.go}: both check functions now also return (warnings, warningRecords) appended next to the existing warnings; new helpers readGitDefaultBranchRef/resolveDefaultRefCached/terminalDebrisRunState/artifactContentPreserved; the worktree + artifact SQL adds r.state AS run_state; verbose mode adds an additive warning_records (and warning_count in the artifact block) parallel to problem_records — problems/problem_records and existing JSON consumers (redaction.go) are unchanged. Tests in go/pkg/reads/doctor_integrity_legibility_test.go cover each rule plus the load-bearing safety cases (worktree + artifact + legacy artifact content on no ref and not on the default branch still red ok) and readGitDefaultBranchRef safe-degradation. No schema change, no migration, no new RPC method. Tied to the AGENTS.md "Do not paste over a broken runner" guardrail. | Revisit if "preserved" should also consider default-branch history (not just tip) so a legitimately-merged-then-edited artifact path never reds; if failed-run findings warrant a distinct disposition from canceled; or once RFC 0127 plain-dir workspaces change how worktree/artifact durability is probed. |
| D203 | accepted | Accept the #297 loud-by-default stranded-file outcome. At work.complete, after the declared-artifact porter and the opt-in source-change publish (D197) run, the daemon computes the in-scope, attempt-authored files that are NEITHER declared as expected_artifacts NOR published to the run branch and surfaces them prominently: it adds stranded_in_scope_paths (exact path list) + a human-readable warnings entry to the work.complete result and emits a durable job.in_scope_paths_stranded provenance event (bounded payload, same shape as job.source_changes_published). The detection reuses the existing collectInScopeAuthoredPaths model (the precise complement of the write-scope guard's violation set, with sibling-published and forbidden paths excluded) and subtracts the declared expected_artifacts (attempt-resolved) ∪ the source-published paths. It is non-breaking by design — it WARNS, it never refuses: the job still completes, so legacy/custom workflows without the D197 opt-in keep working but the silent drop becomes loud. Scoped to per-job-isolated repo-write jobs (a fresh detached worktree, so every in-scope change is unambiguously this attempt's write) with a non-empty allowed_paths; shared-checkout and unbounded-scope jobs report nothing. | Issue #297: a multi-file code slice that declares only its headline module(s) stranded the tests/migrations/secondary modules it genuinely wrote — they passed every gate (the write-scope guard never flags an in-scope untracked file; verifyRequiredArtifacts checks only declared artifacts; the default porter commits only declared paths) and were committed only if the agent happened to git add them, dropped untracked otherwise. The reporter's run lost 3 of 4 slices' tests+migrations this way; recoverable only by hand-harvesting each worktree. D197's publish_source_changes opt-in fixes the commit half but leaves non-flagged jobs silently dropping; the reporter's second suggestion (warn/refuse on undeclared in-scope dirt) was unimplemented. Warn — not refuse — by default because refusing would break every pre-existing non-flagged completion flow (and the safe remediation, publish_source_changes=true, already exists); the requirement was "no silent drop," and a named-path warning + durable event meets it without a breaking change. | detectStrandedInScopePaths + strandedInScopeAuthoredPaths + publishedAndDeclaredPathSet (artifact_source_publish.go, reusing collectInScopeAuthoredPaths); the work.complete wiring + job.in_scope_paths_stranded event + stranded_in_scope_paths/warnings result fields (lifecycle.go). Pgtests prove the warning+field+event populate with the exact stranded paths (declared/out-of-scope excluded), and that the declared-artifact path and the D197 opt-in path produce no warning; unit tests cover the pure set-difference and the covered-set construction. No schema change (event is a runtime-table row; result fields are RPC-only), no new RPC method, no front-matter artifact kind. | Revisit if a STRIATUM_*-gated refuse mode is wanted for hardened pipelines (the detection already computes the set; only the disposition would change), if RFC 0127 plain-dir workspaces change how the authored change-set is diffed (the daemon then diffs the plain dir against the recorded base tree sha instead of git status), or if the warning should also fire for shared-checkout jobs once a reliable attempt-attribution baseline exists there. |
| D202 | accepted | Fix the run start / run drive auto-drive lifecycle (#295, #293). (a) run start auto-drive derives the run id from BOTH the --run-id flag and the bare positional <run-id> form via runStartRunID (the run_start ParamsGroup maps the first positional to run_id and --help advertises <run-id>); the flag form wins, otherwise the first non-flag token is taken, skipping a value-flag's argument. (b) run drive now REFUSES (exit 2, ConcurrentDriveError → "stop pid N first") to start when a live drive for the same run already holds the .striatum/scratch/run-drive-<run>.pid advisory marker, instead of warning-and-coexisting behind the daemon's double-claim guard; a stale dead-pid marker is reaped (overwritten); a new --force-concurrent flag (Options.ForceConcurrent) opts into the documented background-unit + foreground-waiter co-drive. (c) The auto-drive stderr stop hint and docs/how-to/daemonize-run-drive.md (and the refactoring-campaign skill REFERENCE) now name striatum run drive --run-id <id> as the resume command after systemctl --user stop. | (#295) run start <id> (the positional form params.go already accepts) set runID == "" in the auto-drive derivation — which only read --run-id — so auto-drive silently returned: the run sat running with a claimable job and zero lanes, no transient unit, no stderr hint, no error. An AFK operator trusting auto-drive returned to a stalled run. (#293 claim 1) The unit is registered --collect, so systemctl --user stop removes it; the stop hint implied a restartable service, but systemctl start then fails Unit ... not found — the real resume is run drive. (#293 claim 2) claimAdvisoryMarker only warned on a live duplicate and proceeded, so a stop/re-drive left a stray duplicate drive an operator had to hunt down and kill; correctness held (double-claim guard) but the UX was confusing. | runStartRunID + corrected stop/resume hint (go/cmd/striatum/run_start.go); claimAdvisoryMarker returns (func(), error) and refuses/reaps, ConcurrentDriveError + Options.ForceConcurrent (go/pkg/cli/rundrive/rundrive.go); --force-concurrent CLI flag wired through runRunDrive + exit-2 mapping + help (go/cmd/striatum/main.go). Tests: positional-run-id regression + runStartRunID table (run_start_test.go); live-duplicate refusal, dead-pid reap, and force-concurrent co-drive (rundrive_test.go). Docs: daemonize-run-drive.md, skills/optional/refactoring-campaign/REFERENCE.md, CHANGELOG. No schema change, no new daemon method (purely CLI-side + advisory-marker behavior). | Revisit if the strict live-drive refusal proves too aggressive for a legitimate transient-overlap window (relax toward auto-reap-after-grace), or once RFC 0122 daemon-owned auto_spawn removes the operator-held drive process entirely (the advisory marker then becomes moot). |
| D201 | accepted | Accept the #291 hung-supervised-session stall detection + recovery. A queued/claimable job whose bound supervised session is hung — a dead tmux_pane_dead pane still marked active, or an active session that never claimed its packet and holds no_lease — previously stalled a run indefinitely with supervisor_stalls.stalled_count:0 and no decision-tree action, because both the dashboard projection (dashboardAllSupervisorStalls) and the autonomous recovery decision tree (recoverStuckJobs) hard-filtered to j.state IN ('claimed','running'[,'stale_lease']) and resolved the bound session only via the lease (which a never-claimed job lacks). Fix, minimally: (1) the decision tree also scans queued jobs and, for a leaseless queued job (l.owner_session_id IS NULL), resolves the bound supervised session by the SAME role+lane eligibility the claim path uses (lane-matched, non-terminal process_supervisor_pointers state, bound only when EXACTLY one such session matches so a multi-lane run never mis-binds job A to session B), with a mandatory guard that a queued job is acted on ONLY when its bound session is still active (skipping both no-session queued jobs and already-converged dead-lane requeues, preserving idempotence); the existing sessionliveness.Classify then routes the honest-stall case to CASE 2 transfer and the dead-pane case to the confirmed-dead default, and the hung owning session is closed (the manual supervise stop the reporter ran by hand) even when the requeue is already-reclaimable — closing the hung owner IS the recovery action for a leaseless queued job. (2) The dashboard projection LEFT-joins leases, includes queued, and surfaces a leaseless bound session as stalled once its session/supervisor heartbeat ages past stall_after_seconds (new leaseless_count), so the wedge is legible. | Supervised/AFK lanes exist to run without operator babysitting, but a run could dead-stall with ZERO operator signal: two parallel build jobs hung (run_a378328f…) for ~80 minutes at completed:9, queued:2, running:0, stalled_count:0, process_health all zero — recovery stale-leases found 0 (no lease was taken) and recovery process-reconcile returned checked_count:0 (no externally-killed process). Both telltales were already correctly classified by sessionliveness.Classify and the confirmed-dead probe; the ONLY gap was the over-narrow job-state filter and the lease-anchored session resolution, so the existing recovery machinery never saw the job. | recovery_decision_tree.go (scan queued; lane-matched leaseless-session fallback LATERAL join; the active-session guard; close the hung owner on the already-reclaimable path); dashboard_all.go (LEFT-join leases + queued + leaseless stall signal + leaseless_count). No schema change (all runtime-table state). Pgtests: leaseless-hung recovery (close owner, job stays claimable, convergent second sweep), dead-pane queued recovery, and TWO negative safety tests (within-deadline active session and no-bound-session queued job are NOT acted on); plus dashboard surfacing + within-deadline-ignored tests. | Revisit if role-only (NULL lane_id) queued jobs need binding when more than one same-role session exists (currently conservatively skipped), if the leaseless stall threshold (defaultSupervisorStallAfterSeconds) wants its own knob distinct from the lease-age threshold, or once RFC 0127 plain-dir workspaces change how a hung lane is detected. |
| D200 | accepted | Accept the #292 stalled-job finalize path. Add a new recovery.complete_stalled daemon verb (CLI recovery complete-stalled <run-id> <job-id>) that non-destructively completes a job whose agent published its required artifacts (durably) and then died before work.complete, leaving the run needs_operator behind an open recovery_exhausted blocker. It verifies the job's required artifacts are present (verifyRequiredArtifacts) AND body-reconstructable from their declared placement (verifyRequiredArtifactReconstructable, RFC 0125 P0-3 — worktree-independent), then drives the same server-side completion work.complete would have, reusing resolveAutonomousBlockersOnCompletion (#207) to resolve the now-moot recovery_exhausted blocker + escalation and restore the run from needs_operator to running, before enqueuing downstream + re-checking run completion. It refuses verdict-capable jobs (review/phase_synthesis) by default so it cannot directly bypass the RFC 0118 verdict-attestation gate, refuses a job whose lane still holds a live active lease (it finalizes a dead lane only), and is keyed on an open recovery_exhausted blocker (--force relaxes that precondition; --dry-run previews the gate). D274 later adds the narrow --force verdict-artifact path, which records a recovery-provenance verdict instead of directly completing a verdict-capable job. A NEW verb (not an extension of recovery.auto_finalize) because auto-finalize is structurally for a LIVE lane idling on a gate re-publishing from an on-disk file on a running run, whereas this is a DEAD lane with already-published+durable artifacts on a needs_operator run. | The #289 work gave the unsealed-exit dead-end a distinct class + smaller budget + an inspect-the-worktree escalation, but NOT a finalize path. From needs_operator every pre-existing verb fails: auto_finalize requires state=running + a live lease/session; resume handles only process-adapter/write-scope blockers; reseal requeues the same attempt (the agent re-dies); cancel-job marks a SUCCESSFUL job canceled (misrepresenting provenance) and leaves the blocker open. A job whose declared, durable deliverable already exists is in substance complete and must be finalizable from the artifact — without forging a verdict or yanking a live lane. | New handler recovery_complete_stalled.go (HandleRecoveryCompleteStalled + finalizeStalledJob + openRecoveryExhaustedBlockerForJob); registered in mutations.go; contract recovery.complete_stalled + recovery_complete_stalled params group (regenerated routes/registry/method-tables); command-authority-matrix.md row. Five pgtests (durable-finalize happy path incl. the needs_operator→running restore + blocker/escalation resolution, unreconstructable-body refusal, verdict-capable default refusal, recovery_exhausted-precondition + --force, live-lease refusal). No schema change (all keyed on runtime-table state). | Revisit if the live-lease guard proves too strict for a partially-released lease edge, if D274's verdict-artifact recovery needs a broader artifact contract, or once RFC 0127 plain-dir workspaces change how durability is probed. |
| D199 | accepted | Graduate the divergent_ideation shape (RFC 0087 + RFC 0129) from experimental to supported per RFC 0106. It now has a green RFC 0105 unattended-reliability fixture (go/pkg/adapterconformance/divergent_ideation_test.go) registered in ReliabilityFixtureShapes and reconciled with workflowtemplates.supportedShapes by the shape-tier graduation guard (so the tier cannot lie). The fixture proves the shape's distinctive double fan-out/join lifecycle (diverge→converge, then deepen→final_synthesis) drives to completed unattended through the production handlers, and — the load-bearing new coverage no existing fixture provides — that a hard dead-lane fault injected into a branch of the SECOND fan-out (deepen), after the first join already fired, self-recovers on the same attempt via mutations.SweepRun without the final join firing early, losing the branch, or escalating. | RFC 0106 gates supported on a green RFC 0105 fixture; the shape was implemented and live-verified (a 3-model Opus/GPT-5.5/Gemini inaugural run produced genuine cross-family divergent ideation end-to-end) but carried experimental for lack of an unattended fixture. Its double fan-out/join is structurally distinct from implementation_panel's single fan-out/join + linear tail — a fault in a second serial fan-out (after an earlier join fired) was untested across the whole suite until now. | divergent_ideation_test.go (happy double-join cell + diverge-branch-death + deepen-branch-death cells, all driven through work.claim_next/ack/complete/session.close + the real recovery sweep against pgtest); divergent_ideation added to adapterconformance.ReliabilityFixtureShapes and workflowtemplates.supportedShapes; docs flipped experimental→supported (spec, workflow-types, regenerated workflow-catalog, RFC 0087). No change to the shape's product code, no new daemon method, no schema change. | Revisit if the fixture's branch_count=3/deepen_count=2 instantiation misses a higher-fan-out failure mode, or if a future assertion on the multi-model convergence signal (cross-family agreement) warrants its own fixture cell beyond the structural DAG/recovery coverage here. |
| D198 | accepted | Accept the #289 unsealed-agent-exit recovery policy. A confirmed-dead supervised agent that engaged the work protocol (made an MCP tool call) and emitted PTY output but never called work.complete is classified agent_exited_unsealed — distinct from a hard crash (agent_pid_dead). It keeps the same recovery mechanism (respawn via requeue_same_attempt — the daemon cannot seal on the agent's behalf without violating attestation) but gets a smaller requeue budget (recovery_policy.max_unsealed_requeues, default 1 vs 2, clamped to ≤ max_requeues) and a class-specific escalation remediation that points the operator at the per-job worktree (the deliverable may be complete-but-unsealed). A confirmed-dead agent is also excluded from the CASE 2 stalled-but-alive transfer path, so a delayed sweep cannot misroute it to the larger transfer budget once its frozen activity ages past the liveness deadlines. | A self-driving agent-loop lane (e.g. claude --dangerously-skip-permissions) that finishes emitting work then exits at turn-end / context-budget / rate-limit death is, to recovery, indistinguishable from a crash (run_806f8cc1: the apply lane emitted ~273KB then exited unsealed, burned the full requeue budget, escalated recovery_exhausted). A systematic unsealed exit rarely self-heals on repeat respawn, so burning the full budget wastes compute and delays the operator; and the operator's right first move is to inspect the worktree, not assume the work is lost. The recovery machinery (dead-PID detection, requeue, escalation) was already correct — only the classification, budget, and remediation legibility were missing. | New recovery-specific stall classes (stallClassAgentExitedUnsealed/agent_pid_dead) and recoveryPolicy.maxUnsealedRequeues in recovery_decision_tree.go; suggestedOperatorActions(stallClass) in recovery_escalation.go; the CASE 2 guard now excludes supervisedAgentConfirmedDead. No schema change (stall class is a runtime-table string; the budget shares the requeue_count counter). Pgtests cover the distinct classification, the smaller-budget divergence, the confirmed-dead reroute, and the inspect-the-worktree payload. | Revisit FIRED and was resolved lane-scoped by D249/D256 (#478/#530). The "default unsealed budget too aggressive for transient unsealed exits" trigger fired for read-only reviewer lanes; D249/RFC 0152 raised the unsealed-requeue budget only for a non-repo-write reviewer lane kind (maxReviewerUnsealedRequeues), keeping the tight maxUnsealedRequeues default — and the pinned defaultMaxUnsealedRequeues < defaultMaxRequeues invariant — intact for stateful repo-write lanes. The "auto-detect a complete-but-unsealed deliverable" idea was partly realized by D256/#530 WITHOUT forging attestation: recovery now salvages a written-but-unsealed artifact from the per-job worktree and publishes the durable on-disk row, rather than synthesizing one. Revisit further only if the lane-kind distinction proves too coarse (a finer per-job-type policy) or a stronger forgery-resistant deliverable signal than tool-call+PTY is wanted. |
| D197 | accepted | Accept the #287 opt-in source-change publish. When a repo-write job sets write_scope.publish_source_changes=true, work.complete commits the lane's in-scope source edits in the per-job worktree to the run branch alongside its declared markdown artifacts (before anchorActiveWorktreeForJob advances the run ref). It reuses the RFC 0125 daemon-as-porter plumbing (force-add past .gitignore + commit as the operator on the detached worktree HEAD) and commits exactly the attempt-authored in-scope paths — the precise complement of the write-scope guard's violation set, so out-of-scope, forbidden, and sibling-published paths are excluded. Scoped to per-job-isolated jobs (a fresh detached worktree, so every changed path is unambiguously this attempt's write) and requires a non-empty allowed_paths. The generator opts code_change repo-write jobs in by default. | A code_change dogfood's whole point is reviewable code on the run branch, but only the declared markdown artifacts (DRAFT/REVIEW/SUMMARY) landed there; the lane's actual source edits stayed in the per-job worktree (operator-captured out of band, and at risk of worktree gc on finalization — run_806f8cc1). The daemon already computes the in-scope changed-path set for write-scope enforcement and the porter already commits declared paths; publishing the in-scope source diff is the small missing step. This is the git-worktree form of RFC 0127 P1's daemon-owned change-set commit, brought forward opt-in so the dogfood pipeline works before the plain-dir workspace lands. | publishWorktreeSourceChanges + collectInScopeAuthoredPaths (artifact_source_publish.go); the work.complete hook + bounded job.source_changes_published provenance event (lifecycle.go); generator enableSourceChangePublish for code_change (shapes_basic.go). Opt-in (legacy default unchanged — only declared artifacts land without the flag). Pgtests prove the in-scope edit reaches the run branch via the anchor, out-of-scope is excluded, and the opt-in gates it. No new front-matter artifact kind (RFC 0106 freeze holds); no new RPC. | Supersede with RFC 0127 P1 once the plain-dir workspace lands (the daemon then diffs the plain dir against the recorded base tree sha instead of git status on a worktree); the publish_source_changes opt-in carries forward. Revisit the default-on generator behavior if it surprises operators, or whether the opt-in should move from write_scope to a job-level field. |
| D196 | accepted | Accept RFC 0128 (cross-repo run boundary, #280). The single-repo run is the invariant: a run writes exactly its one registered target repository. Ship the missing fail-fast guardrail (validate-time lint fails exit 7 when a lane write_scope.allowed_paths resolves outside the registered repo root, warns when a free-text prompt carries a foreign path token or org/repo slug; dispatch-time scope_violation terminal state so a lane can no longer silently narrow scope) plus read-only artifact federation (a secondary repo registered as a read-only input source serves its refs/striatum/ artifacts as named inputs with no lane write ACL). Cross-repo writes are achieved by decomposition — a coordinator spawns one single-repo satellite run per repo under a shared campaign id, wired by typed cross-run handoff artifacts (commit SHA + summary). Decline first-class multi-repo atomic writes; record the secondary_repos manifest + per-repo peer_write_acl consent + saga settlement design as a deferred option behind an explicit product trigger. | The concrete #280 failure was silent scope narrowing, not a missing transaction: a cross-repo prompt produced a run that wrote one repo and quietly dropped the others, invisible until a retrospective. The daemon cannot provision or preflight secondary-repo writes because cross-repo intent lived only in free text; provenance, durability, and authority are per-repo by construction (one run branch, one repo's refs/striatum/, one repo's PG-scoped state, one repo's lane ACLs). First-class multi-repo writes would need per-repo tokens, per-repo provenance, and cross-repo atomicity — a large surface that brushes the standing anti-bet against scope creep without an explicit decision. The Hippo S12 case showed decomposition is adequate; the failure was the silence. | Phasing: P0 validate-time lint (closes the legible half of #280); P1 dispatch-time scope_violation; P2 read-only artifact federation; P3 decomposition ergonomics (campaign-id annotation + typed cross-run handoff + a to-issues-style splitter). No hosted/multi-tenant coordination service, no distributed transaction across git repos, no new persistence beyond the campaign-id annotation. A guard test asserts no secondary_repos manifest is honored until the trigger fires. New validate/dispatch surfaces update command-authority-matrix.md + guardrails. | Revisit the product Open Question — is single-run multi-target-repo writing ever in scope (recommended no) — if many real cross-repo atomic jobs emerge, at which point the deferred first-class manifest + per-repo consent + saga design activates. |
| D195 | accepted | Accept RFC 0127 (retire the lane git identity, RFC 0125 P2-2 end-state). The per-job workspace becomes a plain daemon-owned directory (workspace_kind: plain_dir), not a git worktree; the lane is a pure byte producer that reads/writes plain files or publishes bodies over the body_base64 MCP envelope, and the daemon owns all git — staging the base content into the plain dir (recording the base tree sha in job_workspaces before the lane starts), daemon-side change-set diff for write-scope enforcement, the RFC 0125 porter commit (commitArtifactBodyToRunRef) sourcing the plain dir, and refs/striatum/ anchoring. Opt-in and reversible: the legacy per_job git-worktree path keeps working until plain-dir is proven across the adapter conformance matrix. | The git worktree leaks git into the lane sandbox — safe.directory config, per-worktree write ACLs, traversal, detached-HEAD understanding — all pure liability now the daemon does the committing (RFC 0125 / D192 porter). It is also attack surface (lane-planted .git/hooks, index poisoning, reflog reads, refs/striatum/* spoofing, git push) and it is the cause of the detached-HEAD (#281), gitignored-declared-path (#278), and lane-unreadable-worktree (#272) failure classes RFC 0125 had to remediate at the durability boundary. Removing the worktree from the lane's hands removes the cause and collapses the provenance trust surface from N lane git identities to one daemon identity. | Phasing: P0 workspace.create plain-dir path + base-tree-sha; P1 daemon-side diff + write-scope + porter commit from the plain dir; P2 overlayfs/fanotify change-capture optimization + grounding read-only checkout for git-context workflows (option A); P3 flip default + retire the git-worktree lane path (keep worktree gc for legacy refs). Five test obligations incl. a durable run-branch commit + pin with no git in the lane and a daemon-side write-scope refusal. PG-as-workspace and required privileged mounts are rejected. | The load-bearing open question is the git-context decision — option A (no git in the lane + read-only grounding checkout, recommended) vs option B (read-only git view); revisit if a flagship adapter genuinely needs in-workspace git blame. Interaction with RFC 0117 worktree gc + the existing refs/striatum/ anchors must be pinned before P1. |
| D194 | accepted | Accept RFC 0126 (multi-reviewer revision coherence, #282 follow-up). Replace the DELETE-on-revision of reviewer verdicts with a build-owned monotonic review_generation stamped on every verdict at the write boundary. The generation is incremented in the same transaction as reopenJobForAttempt (the attempt bump); applyVerdict stamps each verdict with the reviewed build job's current generation; the dispatch packet carries the generation and work.complete/review.submit reject a verdict for a superseded generation (re-queuing the reviewer). Finalization (verifyRunCompletionProvenance) asserts, per build job, that every required reviewer obligation at the build's current generation has a non-superseded accepting verdict — a set-difference routed through the existing failing[] → escalateProvenanceGateFailure path with key review_generation_incomplete. Stale verdicts become structurally invisible (generation non-match); verdict history is preserved (no DELETE). | After a build revision the downstream reviewers are reset by clearing their attempt-scoped verdict rows, and that clear races the reviewers' independent lifecycles: a reviewer that fails to start, is slow, or records on the wrong side of the reset can leave a stale prior-attempt needs_revision as the latest non-superseded verdict — silently blocking finalization while every job shows completed (the Hippo S12 codex review; the long-running design-panel revision re-open wedge). #282 shipped only the legibility half (RFC 0125: status flags upstream_revised_after_verdict). This is the coherence half — making the stale block impossible at the write boundary rather than merely legible, while preserving the audit trail a DELETE destroys. | Phasing: P0 verdicts.review_generation column + build-job generation + stamp in applyVerdict + bump in reopenJobForAttempt + stop the verdict DELETE; P1 work-packet generation stamp + write-boundary rejection; P2 per-generation obligation set + generation-scoped completion-gate assertion + structured refusal + the #282 regression pgtest; P3 retire the upstream_revised_after_verdict heuristic for the authoritative generation comparison. Four pgtest obligations (regression fence, write-boundary, no-regression, history-preserved). No new graduated shape (RFC 0106 freeze holds); reviewer independence (RFC 0002) unchanged. New param surfaces update command-authority-matrix.md + guardrails. | Reviewer-silence-as-reaffirmation is rejected (reintroduces the silent-skip bug); content-hash verdict reuse is deferred; the obligation set derives from the workflow snapshot (drift-proof). Interaction with RFC 0108 multi-run isolation and the RFC 0104 per-run lock must be pinned before P2. |
| D193 | accepted | Finalize the RFC 0119 / D179 git-eviction taxonomy (hippo campaign S12 hardening). Durable provenance is never evictable: RFCs and the decision log (git documents, durable by construction) plus the artifact kinds decision, finding, synthesis, operator_brief, work_plan, escalation, and handoff. Eviction-eligible run exhaust and unsynthesized intermediates are progress_note, operator_report, test_report, auto_finalize_gate_evidence, findings_ledger, support_ledger, action_item_ledger, collaboration_ledger, and harness_improvement_proposal. Classification is fail-safe -- an unrecognized kind defaults to durable (non-evictable). Enumerated in go/pkg/artifactcontracts/eviction.go (IsDurableProvenanceKind/IsEvictableKind), pinned by eviction_test.go, and documented in docs/reference/spec.md section "Warm-Tier Memory". | D179 accepted "an explicit git-eviction allowlist initially limited to progress_note" with enforcement deferred to S12, and its revisit trigger named "progress_note too narrow." S12 (hippo PLAN section S12; the hippo D003 three-axis model) finalizes the policy: per-run reports, working ledgers, gate evidence, and unsynthesized proposals are run exhaust, while ratified decisions, accepted findings/syntheses, governance briefs/plans, escalations, and build handoffs are durable provenance. The eviction axis is independent of the RFC 0123 placement axis (a blob_exhaust finding is still durable provenance). This does not re-ratify RFC 0119 and moves no durable-provenance kind out of git. | The taxonomy is code-plus-test, not yet a runtime evictor: actually removing exhaust from the working tree remains future work gated on this classification. The warm-tier boundary stays pinned by memory_boundary_test.go, external_memory_import_test.go, and the new warm_tier_adjunct_boundary_test.go (no memory.*, no external-consumer import with the adjunct present on the host). | Revisit if a runtime evictor needs additional kinds classified; if a kind must move from durable to evictable (requires a new human decision and must never include RFCs, the decision log, or accepted decisions/findings); or if RFC 0123 placement and this eviction axis are unified into one policy surface. |
| D192 | accepted | Accept RFC 0125 (durable gate artifact provenance, #284 / #270–#283), born from the Hippo remaining-campaign retrospective where a run finalized completed while 7 of 12 required gate artifacts were unreconstructable. RFC 0118 gates completion on verdict-attestation + artifact-row presence; RFC 0123 routes bodies to blob/git; neither guarantees the body is reconstructable. Four mechanisms: (1) daemon-as-porter — body arrives via an artifact.publish body_base64 envelope and the daemon commits it by git plumbing (hash-object -w→commit-tree→CAS update-ref), needing no checked-out branch, ignoring .gitignore, never touching the lane-unreadable worktree; (2) body-reconstructability completion gate — verifyRequiredArtifactReconstructable re-reads + hash-verifies every required body per placement, orthogonal to the verdict path so RFC 0118's unattested-neutral admission does not regress; (3) content-addressed RUN_LEDGER extending the write-once run_completion_record; (4) same-attempt recovery.reseal that re-probes durability and requeues the same attempt with no attempt inflation. | A published artifact row + size_bytes can survive while its body is gone (artifact.get_content → "body file does not exist on disk"), because the only durable path ran through a per-job worktree that could be detached-HEAD (#281), gitignored (#278), or lane-unreadable (#272); operator remediation then bumped the attempt past max_attempts (#271/#273). The finalizer counted those gates as passed → false process confidence. Moving the last-mile commit to the daemon dissolves all three worktree hazards at once; gating completion on body reconstructability closes the audit gap; a committed self-contained ledger makes a retrospective reconstruct the run offline. | New mechanisms: commitArtifactBodyToRunRef (worktree.go) with per-job pin + closeout-fold concurrency (RFC 0117); artifact.publish body_base64; verifyRequiredArtifactReconstructable wired at verifyRunCompletionProvenance with failure key required_artifact_unreconstructable; RUN_LEDGER artifacts[]; recovery.reseal (CapabilityRecovery) reusing requeueJobSameAttempt; shift-left declared-path validation at prepare/start. Rejects blob-only durability (git anchoring stays mandatory for git_publication kinds), apoptosis/void-attempts, PTY-streamed bodies, and any new graduated shape (RFC 0106 freeze holds). New RPC/param surfaces update command-authority-matrix.md + authority-guardrail tests; six hard test obligations incl. the #275 regression fence and the RFC 0118 no-regression test. Closes #270–#275, #278, #281; companion workstreams cover #276/#277/#279/#280/#282/#283. | Revisit the porter concurrency model (pin-fold vs CAS-under-lock) against RFC 0108 multi-run isolation; whether the lane git identity is retired entirely (P2-2); whether a durable worktree_durability blocker row replaces the keyed-by-attempt reseal; and whether the RUN_LEDGER is always emitted as a git_pointer_manifest artifact. |
| D191 | accepted | Accept RFC 0124 (auto-drive on run start, #212) — the operator-side shim toward RFC 0122, split out of D189. striatum run start launches a detached run drive for the started run in a transient systemd-run --user unit (striatum-drive-<run-id>), default-on, removing the operator model from the mechanical loop while spawn authority still rides the operator principal via the normal supervise.start RPC. Opt out per-call with --no-drive or globally with STRIATUM_RUN_DRIVE_AUTO=0. Idempotent (unit-name = run id; live-session check), best-effort (degrades to a printed manual-drive hint without systemd-run), and non-invasive (notices to stderr; --json stdout and the start exit code unchanged). | The do-now half of #212 (operator-model token burn) deserved its own design record rather than riding in under RFC 0122's "do-now" label, which hid operator-facing decisions. Default-on serves the zero-operator-touch product goal; a default-off --drive would reintroduce the "remember to drive" step auto-drive removes, and the self-driving harnesses (scripts/dod/driver.py, explicit run drive flows) are covered by the opt-outs. The shipped code was provisional on main pending this ratification. | Auto-drive is a CLI client of existing audited mutations — no new RPC, no hosted service, no new authoritative state. The C5 gate was discharged before acceptance: run drive's reconcile now holds a paused run (cleanup but no new lanes, stays non-terminal, announces once, resumes on unpause — isPausedRun + TestRunDriveHoldsPausedRun), fixed in run drive so a foreground driver gets the same guarantee. Coverage: run_start_test.go (C1–C4, opt-outs, exit passthrough), TestRunDriveHoldsPausedRun (C5). When RFC 0122 Phase 3 lands, auto-drive is either retargeted to flip the daemon auto_spawn grant (deleting the standing operator credential) or kept as the non-daemon fallback. | Revisit the default if on-by-default surprises operators who drive by hand (flip to opt-in --drive), if the standing-credential lifetime proves too broad before RFC 0122 lands, or if the transient-unit approach needs an installed template for inspection. |
| D190 | accepted | Implement RFC 0123 artifact placement as an additive artifact contract with blob_exhaust, git_publication, and git_pointer_manifest, using owner bundle 0007 for the artifacts-table placement column and append-function overload. | Kind-only artifact routing made synthesis and other reused kinds ambiguous and caused doctor to apply git-anchor checks to lane-exhaust bodies that correctly belonged in blob storage. Explicit placement lets workflow authors state the storage/review surface while preserving old workflow compatibility through one legacy resolver. | expected_artifacts[].placement validates as an optional closed enum; generated workflows emit placement; artifact.publish resolves placement from the expected artifact before routing blob uploads; artifact read/list/detail/export surfaces project placement; doctor narrows git-anchor checks to git-retained placements and reports blob-exhaust metadata/body failures separately. Databases without owner bundle 0007 keep legacy fallback behavior until the owner/admin bundle is applied. | Revisit when pointer-manifest body validation needs a stricter schema, when historical artifact rows should be bulk-backfilled with explicit placement, or if regular migrations move fully behind owner/admin invocation. |
| D189 | accepted | Accept RFC 0122 (scheduler principal for daemon-side supervision.auto_spawn, #212), superseding the D175 deferral of auto_spawn only. The authorizing principal is the run owner's capability, captured at run start as a durable, run-scoped, revocable spawn-authorization grant and replayed by the daemon scheduler for that run's DAG — deferred human authorization, not a new non-human actor — so RFC 0110 attestation/run-as are preserved unchanged. The scheduler reuses run drive's exact reconcile predicate (one algorithm, two homes) and ships behind the RFC 0105 gate extended to the scheduler-spawn path. The operator-side auto-drive shim (run start backgrounds a run drive) is split out to RFC 0124 (accepted separately as D191) and is not part of this decision. | The D175 deferral assumed a free/human operator; two forces it did not weigh are now load-bearing. (1) Operator-model token burn: when the operator is an expensive frontier model, every cycle it wakes to do mechanical orchestration is recurring cost; the endgame of "stop spending model tokens on spawning" is for the spawn to need no operator process. (2) Control-surface reduction for a yolo operator: keeping a --dangerously-skip-permissions model in the spawn loop forces it to hold spawn capability, so moving spawn authority into the daemon is least-privilege — inverting the RFC 0103 W7 objection. The deferral's real gate (no contemporaneous principal) is closed by run-owner pre-authorization. Latency is no longer load-bearing: RFC 0120's wake bus already took the cheaper event-driven step. | Design only for the daemon-side path — no go/ scheduler code lands with acceptance; phased implementation (predicate extraction, grant capture + run-as, scheduler mint/attribute, RFC 0105 gate extension) is authorized behind contracts C1-C6 in the RFC. The operator-side auto-drive shim (the run start interceptor + detached driver, default-on) is ratified separately under RFC 0124 / D191, not by this decision. No new client RPC, no hosted service, no new authoritative state; claim_next stays the sole authoritative transition. When the daemon scheduler path is built it adds a non-client invoker to the spawn path, so the command-authority matrix and authority-guardrail tests must gain a scheduler-invoker case. | Revisit the daemon-side build trigger if the auto-drive wiring proves sufficient (token burn resolved without the scheduler), if the standing-credential residual is judged acceptable, or per the RFC's C2/C4 guardrails. Auto-drive defaults (on-by-default vs opt-in) are adjudicated in RFC 0124. |
| D188 | accepted | Accept the #247 operator/coordinator content-neutrality policy: workflow lint warns when the workflow coordinator lane uses the same model family as a synthesis, phase-synthesis, collaboration adjudicator, or final-review content gate. The warning is advisory, not a hard error, and a workflow may record operator_content_neutrality_override_rationale to suppress it with an explicit reason. | The coordinator/operator role controls framing, sequencing, and acceptance posture; letting it use the same model family as the final synthesizer or adjudicator can collapse independent judgment without any visible signal. The risk is real but context-dependent, so it should be surfaced, not globally forbidden. | Lint now emits operator_content_role_model_overlap; workflow validation requires a non-empty string when the override rationale field is present. Existing same-model reviewer/adjudicator hard-refusal behavior remains unchanged. | Revisit if content-neutrality warnings are routinely ignored, if generated workflows need a stricter default, or if future lane attestation can distinguish operator-only and content-authoring turns more precisely. |
| D187 | accepted | Accept the #244 migration ownership boundary: regular daemon/runtime migrations after schema 26 must not carry owner-table ALTER TABLE or DROP TABLE DDL against striatumd.*; owner-table DDL belongs in owner/admin bundles or owner-applied helpers. Historical migrations 24-26 remain deployed and hash-stable. | Two-role PostgreSQL deployments start the daemon under the runtime role, so regular startup migrations that alter owner-owned tables can crash-loop despite correct owner/admin bootstrap tooling. Future migrations need an executable guard that preserves the split-role boundary. | Tests now scan future regular migrations for owner-table DDL while leaving historical SQL untouched. The PostgreSQL transition runbook and spec describe owner bundles as the path for SECURITY DEFINER, grant/revoke, and owner-table shape changes. | Revisit if regular migrations are moved fully behind owner/admin invocation, or if generated migration metadata can tag owner-only bundles instead of relying on path and guard conventions. |
| D186 | accepted | Accept the #242 autonomous repo-write isolation policy: supervised or agent-loop repo-write lanes must declare worktree_isolation: "per_job" before validation, prepare, or start; a non-isolated lane is allowed only for explicit interactive-human compatibility with allow_shared_checkout_repo_write: true and non-empty shared_checkout_repo_write_rationale. Plain operator-by-hand repo-write lanes remain warning-only. | Autonomous lanes can write the operator's primary checkout even when no sibling run is active, so the old concurrency-only refusal still allowed destructive single-run collisions. At the same time, some hand-driven compatibility fixtures intentionally use the shared checkout and should not be banned without an audited escape hatch. | workflow validate, run.prepare, and run.start refuse unsafe autonomous shared-checkout repo-write lanes. run.start returns autonomous_worktree_isolation_required; workflow lint still emits the shared-checkout risk and adds an explicit override warning when the compatibility escape hatch is used. | Revisit if interactive compatibility usage remains common enough to need a narrower workflow type, or if all supported lane adapters can be made per-job isolated by generation defaults. |
| D185 | accepted | Accept the #257 mid-run write-scope drift policy: the effective write scope is frozen per job attempt, and artifact.publish and work.complete must validate against the same frozen attempt scope. Recovery or override is allowed only through an audited daemon-backed path; it may resolve or supersede the blocked attempt, but it must not silently mutate the historical attempt scope. Both publish and complete expose compatible typed failures that point to the same recovery path. | The observed write-scope drift blocker showed a dangerous split-brain: artifact publication could accept work under one effective scope while completion later refused the same attempt after context or workflow edits. Re-deriving scope from mutable current context hides what a lane was actually authorized to touch and makes recovery ambiguous. A frozen attempt scope gives publication, completion, review, and post-incident audit one stable contract. | Implementation must persist or reconstruct one attempt-scope snapshot and use it consistently for artifact.publish, repository mutation surfaces, scope diagnostics, and work.complete. Mid-run workflow or context edits prepare replacement work or an explicit audited recovery/override; they do not rewrite old packets, artifacts, or attempt records. The compatible typed error shape must include enough structured detail for operators and agents to find the recovery path without direct PostgreSQL edits. | Revisit if replacement-run friction is too high for common benign scope additions, if per-attempt scope persistence cannot be made compatible with existing attempt records, or if a future workflow-versioning model can make scope evolution explicit without weakening provenance. |
| D184 | accepted | Accept the #246 abandoned-run policy: the resident recovery sweep may auto-cancel a running run after a default 24h abandonment threshold when there are no live sessions, no live supervisors, no active leases, and no progress or durable events inside the threshold window. There is no needs_operator intermediate when these predicates are met. The predicate fails closed whenever live work evidence exists. | Real runs were stuck in state=running for multiple days after their lanes and processes disappeared, cluttering run views and adding scheduler/recovery load. Earlier RFC 0020 policy deferred destructive auto-cancel because stale work could still be live; this decision narrows the authority to runs with no live execution evidence and no recent activity. | The terminal disposition is canceled with an abandonment stop reason and audit-visible recovery event/completion record. Scheduler and explicit recovery.sweep behavior must use the same predicate. Any active session, active supervisor/process, active lease, recent work/session/supervisor/progress event, or uncertainty while probing liveness blocks auto-cancel and leaves the run non-terminal for operator inspection. Existing historical abandoned runs may be remediated by the new path once they satisfy the predicate. | Revisit if the 24h default is too short for legitimate disconnected work, if large deployments need repository/workflow-specific thresholds, or if a distinct terminal state becomes necessary for abandoned infrastructure separate from operator cancellation. |
| D183 | accepted | Extend D178's worktree cleanup contract for #259: worktree release --force may retire an active row whose path is already missing on disk when the owning job is terminal, and worktree gc may retire the same terminal missing-on-disk rows. Both paths record missing_on_disk: true in their audit-visible event payloads. | A missing per-job worktree path cannot be inspected or anchored, so preserving an active database row does not preserve artifact durability. The old guard made terminal missing-path rows permanent even though no supported surface could recover the vanished directory. | Non-forced release still refuses missing paths, and forced release still refuses non-terminal jobs. On-disk worktrees keep the existing reachability and artifact-durability checks. Missing-on-disk terminal cleanup marks the row removed, sets release/removal timestamps, and emits worktree.force_released or worktree.gc_removed with head: null, reachable: false, and missing_on_disk: true. | Revisit if missing-on-disk cleanup needs a stricter operator acknowledgment than --force, or if blob-backed artifact durability creates a better forensic path for rows whose directories are gone. |
| D182 | accepted | Correct the RFC 0121 Codex provider-auth gate so a zero-exit smoke is provider-auth success even if the bounded --output-last-message signal is missing, empty, or mismatched. Keep the signal state as a private-safe diagnostic (success_signal) instead of a blocking lane_provider_preflight_unexpected_result. | Issue #260 showed Codex CLI output/message-file drift can make the bounded success signal unreliable while the provider CLI still authenticates and exits successfully. Treating that drift as auth failure wedged run drive before launch despite valid credentials. Exit status is the stable auth conclusion; the bounded message is diagnostic. | supervise.start and run drive no longer refuse a supported Codex lane solely because the success-signal file format drifted after a zero exit. doctor --lane-provider-auth codex --json and failure details expose only safe fields: probe name, exit code, stdout/stderr byte counts, success-signal state, and raw_output_returned=false. Nonzero exits, launch failures, missing binaries, timeouts, provider unavailable, and unsupported providers still block according to the RFC 0121 gate mode. | Revisit if Codex exit 0 no longer means the provider-auth smoke reached an authenticated model turn, or if a future provider needs a stronger non-text success proof than exit status. |
| D181 | accepted | Accept RFC 0121 for #252 as a launch gate: add a shared lane_provider_auth preflight primitive, enforce it from supervise.start before supported Codex agent-loop lane launches, expose provider_auth_gate modes auto, required, and off, have run drive only forward that mode and stop fast on a sanitized blocking refusal, and add an explicit doctor --lane-provider-auth codex diagnostic that ordinary doctor never runs by default. | The failing condition is lane-user provider auth, not operator-side Codex MCP drift. A doctor-only or separate preflight is forgettable and does not protect manual supervise.start; a run-drive-owned gate misses other launch paths. supervise.start is the existing authority that every fresh supervised lane launch crosses, and it can run the check after lane config/run-as resolution but before lane token injection, supervisor rows, scratch, tmux/helper processes, or the real provider process. | Implementation keeps the smoke closed and provider-specific, runs it as the lane OS user with sanitized env -i, serializes checks per provider auth home, bounds timeout/cost/network exposure, and returns only safe classification fields with raw_output_returned=false. No raw stdout/stderr/final text, auth files, token values, provider account ids, environment values, PTY logs, provider SDK, database migration, durable event, artifact kind, or transcript store is authorized. CLI help, RPC params/error catalog, command-authority matrix, CLI reference, spec, lane sandbox docs, and tests cover classifier, redaction, doctor opt-in, supervise.start no-side-effect refusal, and run drive stop-fast behavior; #250 host-gated smoke coverage remains host-dependent. | Revisit if Codex CLI flags or auth storage change enough that the closed smoke no longer proves the lane auth home, if false provider outages block launches too often under auto, if provider cost becomes material, if same-user lanes need a stricter default, or when additional providers need supported preflight smokes. |
| D180 | accepted | Accept RFC 0120 for #248 in two phases. Phase 1: terminal idle work.await_packet envelopes carry idle_behavior=exit_session, the agent-loop bootstrap stops instructing lanes to poll after no_work, and the PTY daemon receiver treats explicit idle as a clean lane exit. Phase 2: add a notify-only wake bus for durable enqueue/requeue and agent-directed message/turn availability so run drive and related waiters can block on local wake hints instead of fixed sleeps. Daemon-side auto-spawn stays deferred to #212. | The issue's mined evidence proves the model-side no_work polling loop is high waste, and the follow-up evidence points at daemon-side fixed-interval waits as the next compatible inefficiency. Prior art separates durable queue state from notification/wake delivery, and D175 already accepts run drive as the operator-authorized wake surface. The two-phase design eliminates model-turn waste first, then reduces driver/waiter polling through hints that never become authoritative workflow state. | claim_next remains the authoritative state transition. idle_behavior is advisory envelope metadata for lane process behavior after no work is available. Phase 2 wake events are hints over already-committed daemon/PostgreSQL state: they may wake a waiter to relist/reconcile, but they do not claim, lease, complete, verdict, spawn, or otherwise mutate workflow state by themselves. Existing claim, lease, ack, complete, review, and supervised_push semantics remain unchanged. Phase 1 and Phase 2 are implemented; coverage includes post-commit wake emission, run drive wake behavior, and missed-notification fallback. No hosted queue, external scheduler, telemetry, or daemon auto-spawn path is authorized by this decision. | Revisit if the notify-only wake bus cannot be implemented without a new authoritative queue, if run drive poll cadence remains a measured bottleneck after Phase 2, or when #212 supplies a scheduler-principal model for daemon-side spawn. |
| D179 | accepted | Accept RFC 0119 (warm-tier memory boundary and Striatum hot tier) with C1-C4 discharged in the RFC text. Authorize a separate local-first warm-tier adjunct reached only through delivered fornix content, plus a Striatum-native read-only hot tier design: recall.*/RecallMemory over the daemon's own immutable artifact stream, scaffold-time digest injection after authoritative transitions commit, a default-off deterministic-redacted lane_trajectory corpus export class, and an explicit git-eviction allowlist initially limited to progress_note. No memory.* daemon capability, external-consumer import, or retrieval-dependent state transition is authorized. | The RFC 0119 ratification gate (run_b089555bd70cd2dc2dc1d13c3cc35b53) cleared with findings, not a clean accept. The binding findings were dischargeable without weakening corpus invariants: recall can run on a separate connection outside claim/worktree transactions; worktree digest rendering can be ordered after the job_worktrees row or made non-fatal; exhaust eviction can be an explicit per-kind allowlist instead of *_ledger/operator-report globs; and lane_trajectory can be a narrow D028/path-guard supersession with deterministic redaction-normalization to bytes. D178 was already taken by the RFC 0117 worktree-gc decision, so this acceptance uses D179. | Design accepted only. Before Go implementation lands, tests must prove: recall read failure does not prevent claim or worktree-create commit; digest render failure cannot leave an unrecorded worktree; no memory.* enters the daemon method registry; no external memory-consumer import enters daemon source; all state transitions succeed with the warm tier absent; lane-trajectory export is default-off, byte-stable, redacted, and manifest-hashed; and only allowlisted progress_note artifacts are eviction-eligible. Update spec.md, contracts/daemon_methods.json, generated route tables, and docs/reference/command-authority-matrix.md with the implementation, not with this decision-only acceptance. | Revisit if implementation cannot meet C1/C2 without changing claim/worktree transaction boundaries, if progress_note is too narrow an eviction allowlist, if lane-trajectory redaction cannot be made deterministic and privacy-safe, if operators need scaffold digest in the bootstrap packet before worktree files, or if a later decision broadens/supersedes the warm-tier boundary. |
| D178 | accepted | Implement the RFC 0117 #213 companion cleanup surface as striatum worktree gc [--run-id <id>] / daemon method worktree.gc. It removes only on-disk job worktrees whose jobs are terminal and whose HEAD is reachable from the run branch or a refs/striatum/ pin, reports skipped rows with typed reasons, marks removed rows removed, and emits worktree.gc_removed. | RFC 0117 deliberately made completed and abandoned worktree HEADs durable before release, but recovery still leaves abandoned worktree directories on disk for inspection. Long campaigns need a bounded operator cleanup verb that never discards the last reachable copy of a job commit stack. | worktree.gc is a write-capability, single-repo daemon method over PostgreSQL worktree rows plus git ref/worktree probes. It does not prune refs/striatum/ pins, does not remove non-terminal worktrees, and does not bypass the RFC 0117 release guard; skipped rows stay inspectable. worktree list and doctor remain the read-only observability surfaces. | Revisit with the separate pin lifecycle/sweep decision (#214), attempt-namespaced pin decision (#215), or if operators need an explicitly forceful GC/discard surface beyond worktree release --force. |
| D177 | accepted | Supersede D174 and re-promote the agy (Antigravity) adapter seat to supported for the RFC 0109 two-turn installed-CLI seat. The current installed agy CLI 1.0.6 in this operator environment is authenticated enough to satisfy the ordinary P3 gate: direct agy --print smoke returned AGY_SMOKE_OK, and TestInstalledCLISeatAgyTwoTurn passed with one attested agy session completing both jobs and publishing both artifacts. Restore agy to both workflowtemplates.supportedSeats and adapterconformance.InstalledCLISeatFixtures; keep login-picker detection in the installed-CLI tests as an unauthenticated-environment skip, not as a production degraded-seat classification. The stricter TestInstalledCLISeatAgyRestartWhileLeased gate is not support-tier evidence today: it passed once in this environment, then failed on rerun when agy stopped at the "How's the CLI experience so far?" survey prompt before claiming work. | D174 recorded a real failed local observation, but it had become stale and contradicted the current runner/CLI behavior for normal supervised two-turn use. Leaving it as current truth made operators avoid agy panels unnecessarily and made workflow.lint warn on a seat that now holds the P3 gate. The restart prompt failure is a separate robustness caveat, not the OAuth/login-picker condition D174 used to demote the seat. | Supported seats are now agy and codex. Workflows declaring agy agent-loop lanes no longer emit degraded_seat_lane; the degraded-seat warning path remains covered through workflowtemplates.RegisterDegradedSeatForTest so a future bad seat can still be surfaced without depending on a currently-broken production adapter. The agy login-picker detector was narrowed to avoid treating ordinary non-login CLI text as authentication failure. D174 remains provenance but is superseded. | Revisit immediately if TestInstalledCLISeatAgyTwoTurn goes red on a CLI, auth, or config bump; if scheduled CI cannot maintain an authenticated agy environment for the P3 tier; if the survey prompt blocks ordinary agy panel runs; or when the restart-while-leased stress gate is made prompt-robust and consistently green. |
| D176 | accepted | Accept RFC 0117 (per-job worktree & branch ref-safety; authored as RFC 0115 pre-renumber, renumbered at landing because a concurrent session holds 0115 for token-usage telemetry). Core invariant: a completed repo-write job's commit stack is always reachable from a durable git ref before its worktree can be released, and the daemon never moves the operator's primary checkout HEAD. work.complete fast-forwards the run branch when ancestry permits (compare-and-swap update-ref, the RFC 0108 integrate.go pattern) and otherwise pins refs/striatum/<run_id>/<job_id> with a job.commits_anchored event; non---force worktree.release refuses an unreachable HEAD (worktree_head_unreachable); stale-lease recovery anchors under refs/striatum/ before marking a worktree abandoned; branch.confirm and worktree create create branch refs via git branch/update-ref (never checkout -b), subsuming the standalone #183 stopgap. Fulfills RFC 0008's unimplemented "automatic cleanup / collect artifacts back to provenance" clause. Accepted by the owner after review of branch rfc/0117-worktree-branch-ref-safety. | #186 was silent, severe data loss: with no blob store configured, git's object store held the only copy of completed, reviewed-against work; releasing the per-job worktree left the whole slice stack dangling (recovered only by git fsck forensics — one aggressive gc from permanent destruction). #184 burned a full needs_revision cycle on publication mechanics and left the operator's primary checkout parked on the run branch; recovery.go:1731-1757 is a third orphan path (abandon with no git operation). Nothing in the lifecycle ever attaches a per-job detached HEAD to a durable ref — the daemon creates detached state and ref-naming records but never closes the loop. | Design only at acceptance — no go/ change lands with this decision. Implementation is the RFC's gate-first phased plan: Phase 0 extracts ref helpers + the RED gate TestWorktreeCompleteAnchorsCommitStack (must fail on origin/main first), then FF-or-pin at complete, reachability-gated release + worktree.force_released audit, ref-only branch creation (#183), recovery anchoring, doctor/worktree list anchor surface. No new daemon method, no new persistence, no auto-merge to mainline (integration stays RFC 0108's gated run.integrate). Companion issues (worktree gc verb, pin lifecycle/sweep policy, attempt-namespaced pins, a guard against git checkout/merge in daemon mutation code, blob-store anchored-commit cross-check) filed at acceptance. #186/#184 stay open until implementation lands. | Per the RFC's open questions: pin retention once run.archive/closeout exists (unbounded refs/striatum/ growth), attempt-namespaced pins if run.retry_job requires per-attempt provenance, the doctor cross-check when an RFC 0072 blob store is configured, and the checkout/merge guard's scope if mutation code legitimately needs a working-tree git verb. |
| D175 | accepted | Accept RFC 0116 (zero-operator-touch sequential DAG execution, for #178 + #188's policy half). Accept striatum run drive: a foreground, idempotent, killable operator-side reconcile loop that watches one run's DAG via daemon reads and performs register-session + supervise start (and supervise stop of completed lanes) as jobs unblock, using only normal lifecycle verbs (no rescue), reaching terminal state or escalating loud (RFC 0105). It productizes the proven scripts/dod/driver.py (the 10/10 unattended-DoD driver), composes with the refactoring-campaign skill, and is per-run only (respects RFC 0108 multi-run isolation). Separately accept the fresh-reviewer policy fix (lifecycle.go:101-118): an active author session blocks a reviewer_context_policy: fresh registration only if it holds a live lease OR its role has remaining pending work — an idle, lease-less, drained author no longer blocks; --force-non-fresh semantics unchanged. Daemon-side supervision.auto_spawn is analyzed and explicitly DEFERRED behind a three-part evidence trigger. Accepted by the owner after review of branch rfc/0116-zero-operator-touch-dag. | The daemon already execs lanes on every supervise.start RPC, so run drive changes spawn cadence, not the product boundary — every spawn remains a capability-authenticated RPC from the operator principal, and the loop is trivially killable. Auto-spawn, by contrast, would make the daemon an autonomous actor initiating work without contemporaneous operator authorization (attestation/run-as/restart consequences not justified by current evidence). The freshness concern is context contamination, and a parked, lease-less, drained author holds no contaminable work — the current predicate conflates "an author session row is active" with "the author is doing contaminable work" (#188 observed the stop→register→start dance at all 8 role transitions across four code_change runs; the #178 smoke test needed ~10 register+start cycles for one 11-job panel). | Design only — no go/ change lands with acceptance. Phase 1 = the freshness predicate fix (five TestFreshReviewer*/TestForceNonFresh* gates; coordinate with the #188 text-fix batch) + the run drive MVP (--interval/--once/--json, idempotent re-drive, fresh-policy close-and-retry, concurrent-driver advisory marker; TestRunDrive* gates + the RFC 0105 fixture driven by run drive as the behavioral gate). run drive is a local CLI verb over existing audited mutations — no new RPC, no command-authority-matrix RPC row, no new persistence or audit class. Companion issues filed at acceptance (drive MVP, policy fix, refactoring-campaign doc fix, driver.py retirement, parked auto_spawn tracking issue). #178 and #188 stay open until implementation lands. | The three-part auto_spawn evidence trigger: run drive routinely daemonized in practice, the poll cadence a measured bottleneck, and a non-human scheduler principal model in hand (RFC 0107 successor) — auto_spawn then needs its own RFC + an RFC 0105 gate extension to the scheduler-spawn path. Also revisit on Phase-1 evidence for richer lane→job resolution or event-driven ticking. |
| D174 | superseded | Demote the agy (Antigravity) adapter seat supported → degraded (#190), reversing the RFC 0109 Phase B graduation (D-era #149). Remove agy from BOTH workflowtemplates.supportedSeats AND adapterconformance.InstalledCLISeatFixtures together (the bidirectional reconcile guard TestSupportedSeatsHaveInstalledCLIFixture requires lockstep), add an agy entry to degradedSeats citing the cause, and make the RFC 0109 P3 gate fixtures (TestInstalledCLISeatAgyTwoTurn, TestInstalledCLISeatAgyRestartWhileLeased, run via make -C go installed-cli-check with STRIATUM_P3_INSTALLED_CLI=1) detect the interactive login-picker output and skip-with-reason instead of burning the run timeout and reporting a false seat failure. codex remains the only supported seat. Maintainer pre-authorized this demotion on 2026-06-06; not re-litigated. | The installed agy CLI (Antigravity 1.0.6) is OAuth-only — it has no headless/--login/API-key path — so under the non-interactive RFC 0109 P3 conformance harness it stalls on an interactive login/account picker at the conformance step and never reaches work.claim. The seat tier "cannot lie": with no way to produce a green installed-CLI fixture, supported would be a false claim, so the honest classification is degraded (a known, tracked defect prevents a reliable supervised multi-turn seat), surfaced by the degraded_seat_lane lint so operators declaring an agy panel lane see the gap they are accepting. | Superseded by D177 after the direct agy smoke and TestInstalledCLISeatAgyTwoTurn passed in the current operator environment. | Historical provenance only; revisit through D177 if the agy installed-CLI two-turn gate goes red again. |
| D173 | accepted | Accept RFC 0114 (runtime read-scope least privilege successor for #164: close the remaining runtime-owned identity surfaces — principals, principal_clients, client_sessions — in owner bundle 0006). The accepted design: transfer table ownership FIRST (ALTER TABLE ... OWNER TO CURRENT_USER) then apply owner-owned SECURITY DEFINER projections + revokes (Option A); principal_clients keeps a column gate (principal_id denied) because admin/tokens.go's live UPDATE ... WHERE still reads client_id/unlinked_at; client_sessions is fully denied (zero runtime Go consumers); doctor pg_read_scope.posture becomes derived from stamps + privilege/ownership probes and flips broad_runtime_select → partial_projection_gated. Produced by a cross-family design dogfood (run run_bd0e359aa8d14c888da71e1356adb021, claude author + codex reviewer, verdict accept_with_findings, no revision cycle) and accepted by the owner after review of branch rfc/0114-read-scope-principals-sessions. | These three tables are owned by striatumd_rw (created by runtime migrations), so a plain REVOKE SELECT is NOT a security boundary — the owner role can self-re-grant; ownership transfer is the only durable gate, consistent with the RFC 0079 §5 owner-applied-migration discipline and the owner bundle 0005 precedent. The principal_clients write-entanglement column gate was discovered against live source, not assumed. | Design only — no go/ change lands with acceptance; owner bundle 0006 is owner-applied out-of-band in the implementation PR (routed through a dogfood per project discipline). GH #164 stays open until the implementation lands; private_read_denial stays false until RFC 0113 R2/R3 close the remaining surfaces. | Revisit if implementation finds additional runtime write paths into the gated columns (forcing a wider column gate or a projection-backed write helper); when RFC 0113 R2/R3 candidates are scoped; or per the RFC's own revisit triggers. |
| D172 | accepted | Graduate the adjudicated_constraint_extraction (ACE) shape experimental → supported (the FOURTH distinct-fixture shape to clear the RFC 0106 / D162 graduation gate, after D166 implementation_panel, D168 falsification_gate, and the D169/D170 isomorphic co-graduation of cross_examination). Add it to workflowtemplates.supportedShapes + adapterconformance.ReliabilityFixtureShapes (the bidirectional guard TestSupportedShapesHaveReliabilityFixture reconciles them) on the strength of the RFC 0112 explicit-interrogation-consumer fixture, adapterconformance/ace_interrogation_test.go, that drives the ACE choreography (convener_draft interrogable → convener_synthesis phase gate → fan-out of cross-examiners → cross_exam_synthesis join → adjudicate gate, cycle adjudicate --needs_revision--> convener_draft) through the production claim/ack/complete + review.verdict + interrogation.open/ask/answer/close handlers + the real mutations.SweepRun, across four cells, all green vs live PostgreSQL: a HAPPY cell proves both cross-examiners open a genuine interrogation against the convener target and retrieve a convener-only secret that is NEVER written to any artifact (the answer is not artifact-derivable), the window closing exactly once after the last consumer terminalizes; a REVISION-REOPEN cell proves adjudicate needs_revision retires the prior target session (revision_reopened), re-blocks the fan-out, and forces a fresh attempt-aware target; a DEAD-LANE-DURING-RE-CASCADE cell proves the sweep requeues a re-opened cross-examiner same-attempt (zero escalations) while the join stays blocked and the target stays active, then the run reaches completed; and an ADVISORY-EVIDENCE/WAITING-HUMAN cell proves a waiting_human consumer keeps the target alive and exercises the V1 interrogation.required_skipped and interrogation.unavailable_signaled events. The graduation was adjudicated through a dogfood decision panel (run run_7a2ff4a8bdfd5347ee8679c9962ffc2a, author + claude reviewer + codex reviewer + synthesis): unanimous accept. | ACE is the only remaining shape with genuinely NEW structure vs the four already-supported fixtures: it is the ONLY fixture that drives interrogation at all (open/ask/answer/close through production handlers), and the only one that composes a fan-out of interrogating consumers + a join (implementation_panel's axis) WITH a recursive revision re-cascade (falsification_gate's axis) across a phase-synthesis gate (RFC 0112's explicit-consumer mechanism). This discharges the historical gating risk that kept ACE experimental and that D171 explicitly deferred ("ACE graduation remains out of scope until the RFC 0105 fixture lands") — whether an interrogable job + interrogation window drives cleanly without wedging on the RFC 0095 revision reopen (#65 family). The preserved-context secret discipline makes the non-shallowness machine-checked, not merely asserted. Its four cells exceed the two-cell depth on which D166 and D168 each graduated. | adjudicated_constraint_extraction renders supported in docs/reference/workflow-catalog.md (regenerated) and stops emitting the experimental_shape lint warning (the lint test repointed to the still-experimental iterated_interrogating_panel); the RFC 0106 new-shape FREEZE remains in force — this graduates an EXISTING shape, it does not lift the freeze. Released as a minor bump (v2.28.0). The remaining experimental shapes (conversation, iterated_interrogating_panel, evidence_backed, multi_phase, human_checkpoint, custom) stay experimental; human_checkpoint (blocks for a human) and custom (unfixturable by definition) likely need a different graduation criterion or stay permanently experimental. | Revisit if a cycle-count-accounting reliability regression must be caught (the fixture drives one revision cycle, not the configured max_revision_cycles=2; the codex reviewer flagged this as non-blocking F1 because the re-cascade path is cycle-invariant and is already driven reopen→re-cascade→clearing-accept) by adding a second-cycle cell; if multi-target interrogation consumers (>1 interrogation_target) or the >3-target lint path warrant their own reliability cell beyond the existing validation-surface coverage; if the RFC 0112 V2 required: true hard completion gate lands and needs a fixture; or to graduate the next experimental shape (each still needs its OWN genuine RFC 0105 fixture) or to lift the freeze once the catalog has substantially graduated. |
| D171 | accepted | Accept RFC 0112 (explicit interrogation consumers for phase-gated collaboration shapes) after the owner-requested follow-up edits from design-panel recommendation dec_84e8f185604900a12982e453246fdfd1. The accepted design is the lifecycle-first plan: snapshot-derived interrogation_targets extend the pending-consumer relation without fake graph edges or stored consumer rows; terminal consumer release is centralized through markJobTerminal; work-packet projection is attempt-aware; required: true remains advisory in V1 with evidence events and a pre-declared V2 hard-gate predicate; ACE cross-examiners target convener_draft; ACE graduation remains out of scope until the RFC 0105 fixture lands. | ACE's convener_draft preserved context closed at the convener_synthesis phase gate before cross-examiners could interrogate it. The design panel resolved the open questions (multiple targets allowed with lint above three; packet projection writes no durable state; unavailable/required-skip events are emitted only from real lifecycle actions; conversation/floor-control consumers stay deferred) and converted the RFC from directionally correct to implementation-ready. | RFC 0112 status moves to accepted in the RFC file and index. Implementation is follow-up work, not shipped by this decision: add workflow validation/lints, one snapshot-derived resolver, markJobTerminal + terminal-release guard, attempt-aware packet projection, ACE generator declarations, conformance fixtures, and spec/ubiquitous-language docs. No new table, migration, RPC family, transcript capture, fake dependency edge, hosted service, or ACE support-tier graduation is authorized by this decision alone. | Revisit if V1 advisory required must become a hard completion gate, if chained interrogable consumers become necessary, if multi-target semantics above the lint threshold need a hard cap, if conversation/floor-control consumers should share this model, or if the terminal-path guard exposes lifecycle paths that cannot be routed through markJobTerminal. |
| D170 | accepted | Implement RFC 0113 R1 as a narrow #164 read-scope reduction: striatumd.clients.token_hash and striatumd.clients.token_salt move behind owner-owned SECURITY DEFINER projections (authorize_capability, load_token_for_update) guarded by assert_daemon_authority(), while non-secret client metadata remains directly selectable by the runtime role. Keep pg_read_scope.posture=broad_runtime_select and private_read_denial=false; expose the landed reduction as a partial_projection_gates doctor entry rather than claiming full private-read denial. | A broad REVOKE SELECT would break production read handlers. Token hashes/salts are the smallest high-value read surface with an existing daemon-authority primitive and clear handler chokepoints, so closing them first reduces leaked-live-runtime-credential impact without changing status/dashboard/artifact read behavior. | Owner bundle 0005_token_read_scope.sql; LatestOwnerBundleVersion -> 5; SupportedAuthorityCapabilities += auth_projection_read; PostgresAuthorizer prefers the authorized projection using bound extended-protocol arguments and falls back only when the function is absent; token revoke/rotate fall back to load_token_for_update after direct 42501; ReassertReadRevokes closes grant drift. Guards cover column denial, direct runtime 42501, unauthorized projection 28000, authorized token validation, and grant-drift repair. RFC 0113 is indexed as partially implemented; #164 remains open for the remaining sensitive read surfaces. | When R1 expands to principals/client sessions, when any read handler still needs direct secret columns, when the doctor posture can move from broad_runtime_select to partial_projection_gated, or when all sensitive direct SELECT is gone and private_read_denial=true can be claimed. |
| D169 | accepted | Amend RFC 0106's graduation rule for provably-isomorphic generated workflow shapes: a shape may co-graduate to supported from another shape's RFC 0105 reliability fixture only when an explicit graph-isomorphism guard proves the generated structural graph is identical after intentionally ignoring role, job, artifact, and prose naming differences. Apply this narrowly to cross_examination, which co-graduates from the falsification_gate fixture. | cross_examination and falsification_gate exercise the same runner lifecycle when their challenger-chain lengths match: static source artifact → linear challenger chain → optional scribe → adjudicator collaboration-ledger gate → commit → final, with a needs_revision cycle back to the first challenger. Requiring a separate cross_examination fixture would be a shallow renamed replay of falsification_gate_test.go, adding no new unattended-reliability coverage and violating D162's "tier cannot lie" discipline in spirit. | workflowtemplates.supportedShapes and adapterconformance.ReliabilityFixtureShapes now include cross_examination; go/pkg/workflowgenerate/generate_test.go::TestCrossExaminationIsStructurallyIsomorphicToFalsificationGate is the shared-fixture drift guard; docs/reference/workflow-catalog.md renders cross_examination as supported; the experimental-shape lint regression moved to adjudicated_constraint_extraction. The RFC 0106 new-shape FREEZE remains in force — this graduates an EXISTING shape by proof of structural equivalence, not a new choreography. | If either generator changes so the isomorphism guard fails, either restore structural equivalence, demote cross_examination to experimental, or add its own genuinely novel RFC 0105 fixture. Revisit for other naming-variant duplicates only with a dedicated isomorphism guard; do not generalize this into fixture-free graduation. |
| D168 | accepted | Graduate the falsification_gate shape experimental → supported (the SECOND shape to clear the RFC 0106 / D162 graduation gate, after D166 implementation_panel). Add it to workflowtemplates.supportedShapes + adapterconformance.ReliabilityFixtureShapes (the bidirectional guard TestSupportedShapesHaveReliabilityFixture reconciles them) on the strength of a NEW RFC 0105 reliability fixture, adapterconformance/falsification_gate_test.go, that drives the dialogue-chain graph (holder → falsifier_1 → falsifier_2 → adjudicator gate → commit → final) through the production claim/ack/complete + review.verdict handlers + the real recovery sweep: a happy cell proves a needs_revision verdict on the gate transitively re-blocks the WHOLE downstream chain — falsifier_2 (depth 1) AND the adjudicator gate itself (depth 2), both attempt-bumped — then the chain re-cascades to a clearing verdict and the run completes unattended; a fault cell injects a hard dead lane into a MIDDLE dialogue node (falsifier_2) DURING the revision re-cascade and proves the sweep requeues it on the same attempt while the gate correctly stays blocked (the re-cascade is not lost), then a fresh lane finishes it and the run reaches completed with zero escalations. | falsification_gate is the next defensible non-shallow graduation: its needs_revision cycle re-opens a multi-job linear dialogue chain, so its fixture exercises resetDownstreamForRevision's RECURSIVE downstream re-block past the depth-1 base case the single-job review-cycle fixture (lifecycle_revision_test.go) stops at — the exact "re-open EVERY downstream job, not just the one that never voted" mechanism the RFC 0095 panel-revision wedge family (#65) regressed on — plus fault recovery of a middle chain node mid-revision, coverage neither the review-cycle nor the forward-only implementation_panel fixtures reach. The adjudicator gate is seeded as the stored job_type='review' shape that production's run.prepare maps a phase_synthesis workflow-job def to (HandleRecordVerdict accepts both; the DB jobs_job_type_check forbids phase_synthesis). | falsification_gate renders supported in docs/reference/workflow-catalog.md (regenerated) and stops emitting the experimental_shape lint warning (the lint test repointed to cross_examination); the RFC 0106 new-shape FREEZE remains in force — this graduates an EXISTING shape, it does not lift the freeze. Released as a minor bump (v2.26.0). D169 later made cross_examination a narrow co-graduation case using this fixture plus an isomorphism guard. | Revisit for the harder interrogation/ACE shapes; human_checkpoint (blocks for a human) and custom (unfixturable by definition) likely need a different graduation criterion or stay permanently experimental. |
| D166 | accepted | Graduate the implementation_panel shape experimental → supported (executing the RFC 0106 / D162 graduation gate) — the first shape to clear it. Add it to workflowtemplates.supportedShapes + adapterconformance.ReliabilityFixtureShapes (the bidirectional guard TestSupportedShapesHaveReliabilityFixture reconciles them) on the strength of a NEW RFC 0105 reliability fixture, adapterconformance/implementation_panel_test.go, that drives the real fan-out/join graph (frame → 3 parallel proposals → scorecards join → arbitration → dissent → decision) through the production claim/ack/complete handlers + the real recovery sweep: a happy cell asserts the fan-out enqueues all three proposals and the join holds until the last completes, and a fault cell injects a hard dead lane into ONE parallel proposal and proves the sweep requeues it on the same attempt while the join correctly stays blocked (never losing the recovered branch), then a fresh lane finishes it and the run reaches completed unattended with zero escalations. | The graduation gate D162 designed needed its first real exercise, and implementation_panel is the highest-value experimental shape (contested-design / architecture forks) whose distinctive lifecycle — 3-way fan-out + multi-predecessor join + parallel-branch fault recovery — none of the four already-supported shapes' fixtures covered; its forward-only DAG (no needs_revision cycle) sidesteps the multi-reviewer revision-reopen incoherence (RFC 0095 / RFC 0109 panel wedges) that still blocks a clean fixture for the other panel/interrogation shapes, making it the one defensible non-shallow graduation now. | implementation_panel renders supported in docs/reference/workflow-catalog.md (regenerated) and stops emitting the experimental_shape lint warning; the RFC 0106 new-shape FREEZE remains in force — this graduates an EXISTING shape, it does not lift the freeze. Released as a minor bump. | Revisit to graduate the next shape (each needs its own genuine RFC 0105 fixture — do not author a shallow one to satisfy the guard), or to lift the freeze once the catalog has substantially graduated. |
| D167 | accepted | RFC 0110 implementation Phase 2 full (the last L1 phase): promote striatumd.events to a SECURITY-DEFINER-only write surface (owner bundle 0004, append_event_row), so all three durable append-only surfaces (audit_log, artifacts, events) are SD-only and the doctor pg_write_boundary=full posture licenses the sole-durable-write-path claim ("the daemon's durable write paths are DB-enforced"). Load-bearing sub-decision: the event chain hash is computed entirely in-DB with no Go counterpart (the append_event_row SD fn assigns event_id/created_at/previous_hash/row_hash, computing a v3 length-prefixed canonical via event_v3_row_hash reusing bundle 0001's audit_v3_enc_text, folding payload_json in as its canonical jsonb text). Unlike the audit chain — whose doctor verifier recomputes row hashes in Go and therefore needs the byte-identical V3RowHash builder — nothing in Go ever recomputes an event row hash (verified: canonicalEventHash is write-only and the only event-chain verifier, assertEventChainLinear, checks chain linkage previous_hash == prior row_hash, not hash content). So an in-DB-only hash satisfies G1 (the only write path computes the hash and holds the chain lock in-DB) with zero Go↔PL/pgSQL porting hazard. Trusted caller-supplied hashing was rejected for the same reason audit rejected it (it would not be DB-enforced). Transcript exclusion (C-EVENT-NO-TRANSCRIPTS/T-EVENT-NOTRANSCRIPT, §12): the SD fn DB-rejects any payload with a top-level stdout/stderr/transcript/raw_output/provider_output key, or a payload over a 256 KiB cap, via RAISE SQLSTATE 23514 (distinct from the events FK 23503) before any row lands. repo_event_chain_heads stays runtime-writable for parity with audit_chain_head (the SD fn advances it as owner; a derived pointer, not the protected surface). RLS row-scoping on leases/sessions (§4.6) remains deferred as defense-in-depth only — never the trust boundary. | Events is the hottest durable write path (every state transition emits one), so closing it is what completes the L1 write contract and unlocks the P2 claim. The in-DB-only hash is the rigorous choice that avoids the byte-for-byte Go/PL-pgSQL canonicalization risk the audit chain carried, because the precondition that forced audit's dual builder (a Go verifier) does not exist for events. The forbidden-key check is the primary, low-false-positive transcript guard (verified no appendEvent payload uses those top-level keys); the size cap is a backstop far above any coordination event. | Owner bundle 0004_phase2_events.sql; LatestOwnerBundleVersion→4; SupportedAuthorityCapabilities += event_sd_append (RequiredAuthorityCapabilities already required it at PhaseFull, capabilityProtectedTable already maps it); events→ClassSDGated in the write-authority inventory; db.AppendEventRowSD + db.EventRow; both event chokepoints (mutations.appendEvent, reads.appendResolveEvent) route through the SD fn at ActiveWriteBoundary().AtLeast(PhaseFull), byte-identical direct INSERT otherwise. Gates GREEN vs live PG: T-42501-P2 + T-GRANT-DRIFT, T-EXEC-AUTH (events), T-EVENT-NOTRANSCRIPT (forbidden-key + oversize, both directions, zero rows), positive end-to-end (real event lands + chain advances in-DB), routing units, T-SD-HARDEN (+append_event_row). Deploy order is load-bearing: binary (supports event_sd_append) BEFORE owner bundle 0004; --pg-write-boundary full in lockstep. Rollback: GRANT INSERT ON striatumd.events TO striatumd_rw (as owner) + remove the full drop-in + restart. Version bump + tag. L2/#87 (lane PG-unreachability) and the read-scope successor (#164) remain. | If a future event-chain doctor verifier needs Go-side recomputation, expose the in-DB hash via a SQL call rather than porting the format to Go; if the 256 KiB transcript cap proves too tight (a legitimate large structured event) or too loose; when RLS row-scoping on leases/sessions lands; per the remaining L2 gates for #87. |
| D165 | accepted | Accept RFC 0111 (in-band failure legibility + self-heal across the MCP boundary). P1 render code + message (and suggestion once P2 lands) into the MCP content text block on failure instead of the bare method name (toolResult, go/pkg/mcp/tools.go:106), keeping structuredContent.error unchanged for back-compat. P2 extend rpc.Error with a first-class Suggestion (+ optional machine recovery hint reusing the RFC 0029 recovery_commands shape), threaded ErrorResponse → toolResult, populated for the high-traffic code families (lifecycle, lease/session, capability, confirmation gates). P3 enumerate the error codes as a closed, guard-tested catalog in pkg/rpc + a catalog section in docs/reference/command-authority-matrix.md. | The typed envelope (rpc.Error{Code,Message,Details}) and a stable code vocabulary already exist, but the legible signal never reaches the channel an agent reads: a failed MCP state-changer surfaces a contentless <error>method</error> and the lane must re-run the equivalent CLI verb to learn why — the recurring opaque-MCP-error friction (comparison §4.3/§6.1). Under the minimal-human-intervention envelope an agent that cannot read a failure in-band stalls or burns a turn; ethanhq/cc-fleet's delegation loop demonstrates the dispatchable-code + Suggestion pattern, adapted here to the existing rpc.Error rather than copied. | RFC status proposed → accepted (file + README index). Build path: P1 lands direct (gate-first TDD); P2/P3 (contract-changing) land via a dogfood run. The comparison's §6.3 "daemon-not-required verb tier" stays explicitly declined — it regresses D094/RFC 0043 and RFC 0110 (D164); any revisit must be its own RFC. Companion chores tracked as #160 (distribution) and #161 (architecture map), not RFC-gated. | Revisit if the code catalog churns enough that the guard test becomes friction, when a self-heal loop wants machine-actionable recovery hints beyond the suggestion string, or if MCP clients start dispatching on content text instead of structuredContent.error (the structured field stays the stable contract). |
| D164 | accepted | Accept RFC 0110 (daemon → PostgreSQL authentication + database-enforced write boundary) as a design RFC; phased implementation follows. Layered model under the rule "make a leaked runtime credential uninteresting": L0 ephemeral, owner-PEER-bootstrapped, RAM-only striatumd_rw password that re-rotates every daemon restart (STRIATUM_OWNER_DB_URL / systemd encrypted credential for the remote-PG case); L1 PG enforces the write contract — revoke direct DML, writes only via owner-owned SECURITY DEFINER functions + RLS, phased audit_log→artifacts→events with a pgtest 42501 negative-path CI gate; L2 dedicated PG-less lane OS user + 0700 unix_socket_directories as the hardened default (escalate the lane_pg_reachable doctor warning to a startup block behind a default-false security.pg_socket_hardened flag); L3 pgxpool.BeforeAcquire SET LOCAL striatum.rpc_id/principal_id attribution + a daemon_auth_log table with doctor owner-fallback read. Future: local self-signed-CA client certs for the multi-host path. Amended 2026-06-03 (spec publication, run_8e14cb48): the 3-model panel narrowed and hardened the design — docs/operator/artifacts/rfc-0110-pg-auth/spec_publication/synthesis/SPEC_PUBLICATION.md is the authoritative implementation spec. Deltas: (a) a non-spoofable daemon-authority gate (assert_daemon_authority() over a RAM-only crypto/rand secret whose digest lives in an owner-only daemon_auth_registry the runtime role cannot read) is the L1 trust boundary and replaces GUC-as-attribution as authority; the L3 attribution prelude is in-transaction after BeginTx (corrected from pgxpool.BeforeAcquire, where a SET LOCAL invariant cannot hold) and rides the extended protocol so the secret never appears in pg_stat_activity query text, with striatum.rpc_id/principal_id/session_id as labels only, never authority. (b) The chain hash moves in-DB through a new v3 length-prefixed bytea canonical (escaping-free, key-order-free, byte-identical in Go and PL/pgSQL) — NOT a port of Go encoding/json; V2RowHash is preserved permanently as the reader of pre-cutover rows; the cutover is one release gate behind a default-v2 flag. (c) Claims are narrowed: G1 invariant-integrity vs G2 daemon-issuance; the "daemon's durable write paths are DB-enforced" claim is reserved to phase P2 full (P0 audit_only → P1 audit_artifacts → P2 full, keyed to the doctor pg_write_boundary posture string); the earlier "a leaked DSN is uninteresting" framing is retracted as overbroad — read confidentiality against a live runtime credential is NOT claimed this phase, and a read-scope least-privilege successor issue (#164) is filed before the first behavior-changing PR merges. (d) #87 status: "mitigated, pending lane-OS-user default" — it closes only when L2's four gates are live (PG-less lane OS user, 0700 startup-asserted socket dir, T-LANE-ISOLATION-NEG green, blocking doctor under the secure profile). | The "artifact/RPC API is the sole write path" invariant is enforced today only in the daemon process: the live PEER-auth posture conflates the daemon's identity with its OS user, so a same-user supervised lane can open PostgreSQL directly and bypass the artifact/RPC API + hash-chained audit log (GH #87), and password auth merely trades that for a standing file secret. Authentication should not be the only thing between an untrusted same-host process and the audit chain. The option space was generated by the adhd divergent-ideation skill (5 frames × 6 ideas → score/cluster/trap → deepen top 3); the deepen pass read live source and surfaced the load-bearing L1 risk (the chain hash is computed in Go with alphabetical JSON key order — moving it into PL/pgSQL must match exactly or VerifyRows flags every chain). | New docs/rfcs/0110-…md (status accepted) + README index row. Stock PL/pgSQL only — NO C extension, NO from-scratch PG wire proxy (both recorded as rejected alternatives with the rest of the ADHD trap log in the RFC appendix). L3 + the L0 doctor posture probe are cheapest and land first (no behavior change; immediate RFC 0107 attribution value); L0 rotation + L1 Phase 0 (audit_log) are the security core; L2 ships behind the upgrade-safety flag (default-on is a separate announced minor). Sequenced AFTER the RFC 0104/0105 reliability foundation; does not block RFC 0103's remaining work. Supplies the credential/enforcement/isolation/attribution substrate RFC 0107 builds the principal layer on. A design-panel dogfood is scaffolded to drive the implementation. No version bump (design acceptance, no code shipped yet). | Revisit per phase as each layer lands (re-record implementation Ds), if the L1 PL/pgSQL hash-canonicalization risk forces keeping hash computation in Go behind a trusted-parameter function, when a real multi-host deployment makes the cert path concrete, or if the L2 enforcement flag should flip to default-on. |
| D163 | accepted | Accept RFC 0109 (make the agy lane a first-class seat) and CLOSE it per its scope guard: P3 — the standing installed-CLI conformance gate (#149) — landed alongside P1 (the four defects #95/#85/#76/#139), and the agy seat graduated degraded→supported. The gate (adapterconformance.RunInstalledCLI over a harness that now serves a unix-socket RPC listener, + the ctx-threaded agentloop.RunContext seam) drives the REAL agy CLI through a two-turn claim → publish → claim and asserts ONE attested session across both turns; gated behind STRIATUM_P3_INSTALLED_CLI (release-blocking scheduled tier; skips in the default suite and when the CLI or STRIATUM_PG_TEST_URL is absent). Graduation flips adapterconformance.InstalledCLISeatFixtures[agy] + workflowtemplates.supportedSeats, guard-reconciled in both directions. | P2 (D162 lineage, v2.10.0) counted the agy seat as degraded, but RFC 0109 closes only when P3 lands alongside P1 — P2 alone resets the clock. Building the gate first (to reproduce #95) produced the plan-altering finding: #95/#139/#85 do NOT reproduce against the currently-installed agy CLI — agy holds the two-turn seat (green ×3), launches past the folder-trust/telemetry prompts, and reaches work.claim without a discovery stall. So P1's defects are resolved-as-of-current-CLI; the gate's enduring value is the anti-re-rot purpose the RFC names (a CLI bump / config drift that breaks the seat now fails CI, not a live panel weeks later). | Harness gains a unix-socket RPC listener (the production rpc.Server.Serve pair) so the real agent-loop receive loop is driven, not the in-process testagent (which reuses its session by construction and is immune to #95). At D163 time codex stayed experimental; later gates graduated codex, and current supported seats are codex and agy under D177. degradedSeats is now EMPTY; workflowtemplates.RegisterDegradedSeatForTest keeps the degraded_seat_lane warning path under test. Live-corroborated on run_139c5981: the 3-lane interrogating panel held the agy seat across a needs_revision cycle (agy voted needs_revision att1 → presenter revised → agy re-reviewed + accepted att2 under a fresh ATTESTED session), and the panel SURVIVED a mid-run systemctl restart striatumd and finalized (codex + interrogable presenter resumed post-restart via daemon.recovery_sweep). Lint 0 issues; CI green (def95426, df93c0f3). | Revisit if the agy or codex installed-CLI P3 gates go red on a CLI/config/auth bump, or if a live panel exposes a seat-holding failure not represented in the ordinary two-turn gate or restart stress gate. |
| D162 | accepted | Accept RFC 0106 (workflow-shape support tiers + graduation) AND freeze new-shape authoring until the catalog graduates. Stamp support_tier: supported \| experimental on every catalog shape (workflowtemplates, single-source supportedShapes = {minimal, review, code_change, multi_review_synthesis}); workflow.lint emits a NON-blocking experimental_shape warning when a run declares an experimental shape (yolo may opt in knowingly); a shape graduates to supported only with a green RFC 0105 reliability fixture, guard-tested so the tier cannot lie. | New shapes kept landing while existing ones still wedged under the multi-lane revision lifecycle, so the operator faced a menu of choreographies of which an unknown subset survive an unattended run — a silent trap under yolo. The fix is governance, not subtraction: the collaboration/anti-hallucination shapes ARE the product value (the architecture review's "prune to three" call was wrong under the real mission), so tell the truth about which run unattended and stop widening until the catalog catches up. | No shape removed. SupportTierForShape + stampSupportTiers + the regenerated docs/reference/workflow-catalog.md tier badges; lintExperimentalShape (silent when no shape is declared); graduation guard TestSupportedShapesHaveReliabilityFixture reconciles the catalog's supported set with adapterconformance.ReliabilityFixtureShapes in BOTH directions (drift either way fails CI). Honest initial classification: minimal / review / code_change / multi_review_synthesis supported; the 10 collaboration/interrogation/multi_phase/custom shapes experimental. FREEZE: no new generator shape is authored until the existing catalog graduates (reversible by a later decision). Lint 0 issues. | Revisit to graduate a shape (add its RFC 0105 fixture, then move it into supportedShapes), or to lift the new-shape freeze once the catalog has graduated. |
| D161 | accepted | Accept RFC 0105 (standing unattended-reliability harness): extend the RFC 0101 in-process conformance/chaos harness from single-job recovery to the FULL two-lane, review-gated, needs_revision-cycle lifecycle (implement att1 → review → needs_revision → re-open att2 → re-implement → re-review → accept → completed), driven through the production mutation handlers, under the fault matrix; assert each cell self-recovers-or-escalates-loud within budget, never a silent wedge. Expose ReliabilityFixtureShapes as the per-shape graduation entry point RFC 0106 consumes. | The yolo mission makes "a run completes or fails loudly, unattended" existential, yet the multi-lane revision lifecycle (#65/#84/#120/#121/#131 family) had no standing hermetic gate — the load-bearing logic fails in integration, which the 20% unit-coverage floor cannot catch. RFC 0103 proved the fault matrix once; this keeps it proven on every commit. | New go/pkg/adapterconformance/lifecycle_revision_test.go: TestRevisionLifecycleHappyPathCompletes (the full cycle, hermetic), TestRevisionLifecycleLaneDeathSelfRecovers (att2 lane dies mid-revision → sweep requeues same-attempt → fresh lane finishes → accept → completed, no operator), TestRevisionLifecycleUnrecoverableEscalatesLoudly (repeated death past budget → needs_operator + recovery_exhausted escalation). Runs under make check (go test ./...) and CI's PG tier — a standing gate by placement, no new wiring. Builds on RFC 0104 (a reverted run lock turns the paired deadlock regression red); deterministic (completed lanes close their session so the recovery tree's dead-lane resolution is unambiguous). Full adapterconformance suite green; lint 0 issues. | Revisit when RFC 0106 graduates more shapes (add their fixtures here), or when a new fault class (W2 agy seat, transport churn beyond the time-warp) needs a cell. |
| D160 | accepted | Accept RFC 0107 (multi-principal trust model): make multi-user a deliberate, bounded design over the EXISTING trust substrate, explicitly NOT a hosted/tenanted SaaS. A principal (kind ∈ human|ai_operator|service) is a named identity holding capability tokens; it sits ABOVE the clients/client_capabilities tables and owns one or more clients (the principal→client link survives token rotation, which mints new client rows). The human kind generalizes RFC 0053's single escalation-only human to several humans. Per-principal capability + repository scoping REUSES RFC 0028 client_capabilities.repository_id and RFC 0096 session-binding unchanged (a principal holds capabilities on a subset of repos; a session-bound token acts only as its own session — principal A's token cannot act for principal B's session). The daemon-global hash-chained audit log already records client_id per mutation; resolving it through principal_clients attributes every mutation to a principal. No new wire RPC method: daemon.token.create gains optional principal_id/principal_kind/principal_display_name (attributes the minted client in the issuing tx), daemon.token.rotate carries the principal across rotation, daemon.token.revoke leaves historical links intact. | The trust substrate (per-client tokens, per-repo scoping, session-bound tokens, audit chain) was already load-bearing for the real self-hosted multi-repo deployment, but the MODEL still assumed one operator + one escalation human (RFC 0053). Without an explicit principal model, multi-user usage would accrete ad hoc on the single-operator assumptions — the drift this project avoids. The non-SaaS boundary is reaffirmed: principals are local capability grants on the operator-owned daemon + PostgreSQL, not cloud accounts; no hosted control plane, tenant provisioning, external IdP/SSO, or telemetry. | New ownership-safe migration 0023_principals.sql (LatestDaemonDBVersion 22→23): two NEW tables striatumd.principals + striatumd.principal_clients (the latter's client_id is a bare column with NO FK to the owner-held clients table — attribution enforced in Go, like audit_log.client_id — so the runtime role can apply it; includes the GRANT … TO striatumd_rw block). New go/pkg/admin/principals.go (CreatePrincipal/LinkClientToPrincipal/ResolvePrincipalForClient/ListPrincipals + token-attribution helpers in tokens.go); new rpc.AuthContext.MayActAsSession canonical per-session predicate (go/pkg/rpc/principal_session.go). daemon doctor gains a principals block (kind/display-name/client-count/repositories/effective capability scope; never token material). docs/reference/spec.md carries a new "Multi-Principal Trust Model" subsection with the non-SaaS boundary. Tests prove isolation + attribution: TestCrossPrincipalSessionIsolation, TestCrossRepoIsolationIsEnforcedAndAttributed, TestPrincipalLinkResolvesAuditAttribution, TestPrincipalLinkRejectsSecondActivePrincipal, TestListPrincipalsSurfacesPerPrincipalCapabilityRepoScope, TestCreateTokenAttributesClientToPrincipal/TestCreateTokenRejectsUnknownPrincipalWithoutKind (pkg/admin, PG-gated), TestMayActAsSession (pkg/rpc, hermetic), TestHandleDoctorSurfacesPrincipalsAndScope + TestMigrationTwentyThreePrincipalsIsOwnershipSafe; pkg/admin/pkg/rpc/pkg/reads/pkg/db green, lint 0 issues. | Revisit if a principal must span a per-principal grant the current token model cannot express (e.g. group/role inheritance), if principal disablement should cascade to active tokens, or if an external identity provider is ever in scope (would require a new product decision — out of bounds here). |
| D159 | accepted | Accept RFC 0104 (per-run serialization invariant): generalize RFC 0101 Phase 0a's lockRunInterrogation into lockRun (a per-(repository_id, run_id) pg_advisory_xact_lock) and take it as the FIRST statement of every per-run mutation transaction — claim/await, submit-review/verdict/override, lifecycle completion (work.complete, run.cancel, run.retry_job, checkpoint.resolve), the interrogation handlers, and the per-run recovery sweep/handlers (auto, resume, stale-leases, requeue-stale, cancel-job, process-reconcile, auto-finalize). | Two hot per-run paths locked {sessions, runs} in opposite order (claim sessions→runs→jobs vs recordVerdict→maybeCompleteRun→closeRemainingSessions jobs→runs→sessions), with the recovery sweep a third concurrent party; withTxRetryOnDeadlock only tolerated the 40P01 (#98/#103/#137) and exhausts under multi-lane/multi-repo concurrency — fatal under yolo where no operator retries by hand. Explains why single-lane self-hosting passed while multi-lane panels wedge. | No schema change. New lockRun/lockRunForJob/lockRunForSession/lockRunForBlocker helpers; withTxRetryOnDeadlock kept as defense-in-depth (a surfaced 40P01 is now a should-never-happen signal); db.JSONBArg gained an additive JSONBTextEncoder opt-in for SQL-recording test runners. PG-gated regression TestRunLockClaimVerdictSweepDeadlock is RED→GREEN (reproduced a 40P01 at iteration 0 pre-fix; 240 iterations clean post-fix); guard TestPerRunHandlersTakeLockRunFirst asserts the advisory lock precedes any run-scoped FOR UPDATE; pkg/mutations + RFC 0101 chaos suites green; lint 0 issues. docs/reference/spec.md Run Lifecycle carries the invariant. Foundation for RFC 0105. | Revisit if a per-run handler is added without lockRun (the guard test fails by construction), or if a cross-run/global mutation path ever needs its own serialization key. |
| D158 | accepted | Review-verdict semantics hardening (#127/#132/#140). (a) A reject verdict on a review job whose workflow declares a bounded revision cycle (on_verdict: needs_revision from that job) is refused at recordVerdict with a clear correction — submit needs_revision (recoverable), or use override-verdict to force terminal rejection — instead of failReviewJob failing the whole run with no recovery path. (b) Reviewer-natural verdict synonyms are normalized onto the canonical vocabulary {accept, accept_with_findings, needs_revision, reject} at both review.verdict and review.submit entry (accepted_with_follow_up→accept_with_findings, approve→accept, request_changes/changes_requested→needs_revision, rejected→reject). (c) work.complete on a verdict-completed review job returns an idempotent already_completed no-op rather than a misleading lease is not active. | Supervised reviewers (esp. codex, #140) conflate "request changes" with terminal reject, wedging a live RFC 0043 run twice non-recoverably; the canonical vocab has no request_changes token. Reviewers also reach for accepted_with_follow_up/approve synonyms review.submit rejected, causing duplicate artifacts + control-plane probing (#132). A lane following the generic complete instruction after a verdict hit lease is not active, looking like a failure though the job was done (#127). | Workflows with no revision cycle keep historical terminal-reject behavior; override-verdict stays the explicit force path; unknown verdict tokens still surface "unknown verdict". Regression tests TestRejectWithRevisionCycleRefused/TestRejectWithoutCycleStillFails/TestCompleteAfterVerdictIsIdempotent/TestNormalizeVerdictSynonyms (pkg/mutations, green; full package suite green). | Revisit if an in-cycle reviewer must force terminal rejection without override-verdict, or if the synonym map should be workflow-configurable. |
| D157 | accepted | Add a checkpoint.resolve action override (#63 F2) for revision_routing human-checkpoints: it requires --decision-id referencing a recorded run-level striatum.decision (RFC 0047) whose outcome is accepting (accepted/accepted_with_follow_up), resolves the checkpoint, completes the stalled review job (settled by the decision, not re-run), records a superseding clearing verdict (accept_with_findings, posture=override) under a minted operator-labeled reviewer session, and makes the downstream gate reachable. No new override authority and no new RPC method/route — the audit/rationale live entirely in the decision artifact (same model as D099/D100, RFC 0064 workflow.accept_risk). | A needs_revision verdict with no productive cycle left opened a revision_routing checkpoint whose only actions were continue (re-queues the same review → re-reproduces needs_revision → loops) and cancel (kills the work). There was no operator disposition for "I accept this verdict; proceed downstream," wedging the run (the #63 F1/F3 cycle router covers the in-budget case; override covers the exhausted/no-cycle case). | The clearing verdict cannot reuse the reviewer's session (verdicts session_id is NOT NULL with a per-job UNIQUE), so it mints a fresh reviewer session via the existing review.override resolveOverrideSession; the stale needs_revision row is preserved and flagged superseded_by_decision_id/superseded_at. created_at is stamped strictly after the newest existing verdict so latestVerdict deterministically returns the clear (avoids the whole-second nowString tie). Surfaced in status per-checkpoint resolve_actions/resolve_action_hints, the CLI --action override enum, and the recover skill templates. Default gates (requires_verdict ⊇ accept_with_findings) reopen; a strict accept-only gate intentionally stays shut. Landed via the #63 follow-up integration; whole-repo lint gate 0 issues, pkg/mutations+pkg/reads green. | Revisit if a strict accept-only gate must be overridable, if override should record a configurable clearing verdict, or if the same-second verdict ordering should be fixed structurally (the review.override path shares the latent tie). |
| D156 | accepted | Retire the agy one-shot pipe lane shape, resolving D148's deferred follow-up (#51, #63 F5). The live templates/examples that configured agy as a one-shot pipe lane (agy … --print with an IFS= read -r prompt stdin shim or supervision.stdin_delivery: one_shot_eof) are migrated to the agent-loop shape command: ["agy", "--dangerously-skip-permissions"] + adapter_capabilities: {"agent_loop": true}. A new workflow lint warning (agy_one_shot_pipe_lane) fires when a lane invokes the agy binary with --print and omits adapter_capabilities.agent_loop. Historical RFC-0088/0089 run records and their provenance are left intact. | D148 deferred retiring the agy one-shot pipe lane until agy reliably completed claimed packets; #51 (closed via #52/#55) landed the agy agent-loop submit driver (agy --prompt-interactive argv) and proved claim+completion, so the one-shot pipe path — which lacks the agent-loop MCP config and preserved interactive context; later supervised-push auto-dispatch can deliver an initial packet, but it does not make the agy one-shot shape autonomous — is now strictly dead for autonomous agy. | Live configs migrated: examples/{rfc-ledger-cleanup,three-lane-design-build-review,iterated-interrogating-panel}/workflow.json and docs/operator/workflows/conversation-3way.json (+ its README). workflowauthoring.Lint emits agy_one_shot_pipe_lane (warning, not a hard refusal; scoped to lanes whose command names the agy binary, so claude/codex/other one-shot pipe lanes are unaffected). docs/how-to/writing-workflows.md documents the agy-agent_loop-only requirement. | Revisit if Antigravity grows a viable one-shot/headless claim path, or if the warning should become a hard validate-time refusal. |
| D155 | accepted | Accept RFC 0093 V1 (structured live-collaboration workflow shapes): land the striatum.collaboration_ledger.v1 artifact contract (Check A structural substance check + Check B trajectory-ref validation), the cycle router that honors declared needs_revision workflow cycles (the shared fix for #63 F1/F3), the adjudicator role + phase_synthesis substance gate, and the generator shapes falsification_gate + cross_examination with a scribe participant modifier. The gate is verified-not-theater: a clearing ledger must carry referenced claim/challenge/rebuttal entries; the recorded verdict must equal the ledger front-matter verdict on BOTH review.submit and the primitive recordVerdict path; and cycle-scoped (cycle_<attempt>) ledger naming lets revision iterations re-publish without a content-hash collision. fog_of_war_review, synaptic_prune, and post_dialog_hook deferred; no new daemon method. | RFC 0083's interrogating-panel review was a one-off shape; the substance gate + ledger generalize it into a reusable anti-theater family. The cycle router was a prerequisite — without it recordVerdict("needs_revision") always opened a human checkpoint (it never consulted declared cycles at all). | Built via a 3-lane design+build dogfood whose interrogating panel caught the cycle-ledger deadlock and the verdict-bypass before merge. Landed on main 7d465fd (impl 81a746b); the #63 F1/F3/F4 daemon fixes landed first (4c1a5b4) and were validated live in the run (a needs_revision routed back to implement and re-blocked the reviews; the budget-exhaustion fallback fired after 2 iterations). Full go test ./pkg/... green; whole-repo lint gate 0 issues. | Revisit when fog_of_war_review/synaptic_prune are built, or if a second-adjudicator-on-disagreement (RFC 0093 OQ2) is needed. |
| D154 | accepted | Supersede D028 and D151 for RFC 0092: Narrow the raw transcript capture restriction to permit ephemeral, real-time Server-Sent Events (SSE) streaming of active supervisor PTY logs and real-time dialogue message queues to the Web UI. Raw stdout/stderr terminal streams remain strictly ephemeral scratch data: they are tail-read on-demand from local .striatum/scratch/ log files and are never saved as durable PostgreSQL records, committed to database archives, or used as workflow-control authority. | Operators monitoring multi-agent runs remain completely blind to intermediate agent reasoning, PTY startup errors, or TUI submit driver diagnostic logs in the Web UI. Streaming active PTY logs ephemerally directly in the operator's browser provides critical real-time visibility without cluttering the database or compromising the durable privacy guarantees of D028. | New SSE endpoints /v1/runs/{runID}/live-dialogue and /v1/sessions/{sessionID}/live-pty in go/pkg/webservice/service.go; real-time chat panel and scrollable dark-mode terminal console viewer components integrated into the Web UI. Ephemeral PTY tailing is resource-bounded and loopback-only. PostgreSQL schema remains unmodified. | Revisit if transcript retention, post-run tail replay, or a React-based SPA terminal component are requested. |
| D153 | accepted | Accept the lane-health deepening (RFC 0091): a new go/pkg/lanehealth module computes composite lane health (lane-bound, alive, lane-liveness attestation, delivery liveness, stall class) once behind a small Checker.Check seam, with a pure Classify(Facts, now) core as the only test surface. Attested = Bound && Alive && start-token-verified preserves today's value exactly; Bound (rows-only) and Alive (probe) become first-class primitives. The liveness probe is the one injected port; db.Runner stays unported (tested via pgtest plus the pure classifier), explicitly rejecting a RowStore-style DB port; supervisor.TmuxMeta becomes the single typed metadata codec, kept in package supervisor to avoid an import cycle. | The attestation rules had no home: the start_token_unverified ⇒ unattested rule was duplicated verbatim across the mutation path (mutations/mutations.go:709) and the read path (reads/supervision.go:813), the metadata["tmux"] shape was implicit across five files, and answering "is this lane bound, alive, attested, and deliverable?" required bouncing across mutations, reads, supervisor, and sessionliveness. The pure attestation logic was only testable through real Postgres plus real tmux. | New go/pkg/lanehealth; glossary gains lane-bound and lane health. sessionLaneAttestation becomes a thin wrapper over Check + LegacyMap (wire-identical); applySupervisorLaneAttestation and tmuxStartTokenUnverified are deleted; reconcileSupervisorForDelivery reads .Alive/.Deliverable and folds the supervision_control.go:859 raw-pgx duplicate read into the loader while keeping its in-transaction FOR UPDATE lock (lanehealth owns derivation, not locking); interrogation.requireLiveTarget uses Check(...).LiveTarget() with the RFC 0084 awaiting_interrogation fallback preserved. A pure table-test over Classify (no DB/tmux) plus a thin pgtest Load test replace the read-side recomputation tests and the supervisionTmuxRunner attestation global-mock. Fast-follow: migrate the three TmuxMeta writers onto TmuxMeta.Marshal. | Revisit if a dashboard probe-cost profile justifies a batch AssessMany; if the agent-loop earns first-class byline attestation (would add or change an axis); or if delivery's transactional consistency check should move behind the module. |
| D152 | accepted | Accept RFC 0089 Phase 1: tmux-backed supervised lanes track liveness from the tmux session and pane process identity instead of the transient tmux attach-session client. Tmux pane text remains private diagnostics only and never becomes workflow state, durable provenance, byline input, verdict input, or export content. | RFC 0088/RFC 0075 made long-lived PTY lanes operator-observable, but the helper treated tmux attach-session as the supervised process. Detaching or losing that observer could mark a still-running lane lost, downgrade bylines, and block packet delivery. | Tmux metadata records session/window/pane identity, pane pid/start token, attach command, and diagnostic attach client pid. supervise.status, supervise.send, reattach status, recovery reconcile, dashboard/status reads, and doctor projections use the tmux liveness classes; supervise.stop kills the tmux session for tmux-backed lanes. Raw pane text and PTY logs stay outside daemon state and artifacts. | Revisit if Phase 2 adds a daemon-owned reattach command, if packet delivery moves from attach-PTY writes to tmux buffer paste, or if tmux should become required by default for all agent-loop profiles. |
| D151 | accepted | Narrow D028 for RFC 0088: operator-local PTY logs under .striatum/scratch/<supervisor_id>/pty.log are permitted as private diagnostics (default-on, overridable/disablable via STRIATUM_AGENT_LOOP_DEBUG_LOG), while raw provider transcripts remain forbidden as daemon/PostgreSQL state, workflow artifacts, corpus/archive/evidence exports, verdict inputs, byline inputs, or workflow-control signals (RFC 0088 Decision 6). | Hardening per-adapter submit drivers and provider TUIs needs operator-inspectable terminal trajectories; the codex and agy submit-driver fixes were diagnosed precisely from pty.log. The forbidden thing is durable transcript capture, not local scratch. | PTY logs are 0600, never published, redacted, or treated as provenance; deleting them does not change workflow truth. They surfaced the codex initial-prompt-arg fix and the agy .gemini/settings.json findings under closeout run run_b5dfd0e3c280c19fcc13bf69801eacca (session sess_5449a2366336892192e8e35a86aaa539). | Revisit if a later decision accepts retention, redaction, export, or UI viewing of PTY logs. |
| D150 | accepted | Replace the gemini_cli family with agy (Antigravity) as the canonical family; drop the duplicate antigravity tool-family entry; the agy installer reuses the claude bundle (agy plugin import claude). MCP config is generated fresh at launch and never persisted as a stale rotating port (RFC 0088 Decisions 4-5): claude via ephemeral --mcp-config, codex via -c mcp_servers.striatum.url + STRIATUM_MCP_TOKEN, agy via a fresh project-level .gemini/settings.json (gemini httpUrl schema) restored/removed on teardown. Retires F42-F45 (the gemini turn-driver) and the single_shot adapter capability. | gemini was the only adapter whose agent-loop never worked natively; agy is the claude-shaped replacement. The F45 stale-.gemini/settings.json port is made structurally avoidable by fresh-per-launch generation. | gemini_cli/gemini_default and the agent-loop turn-driver + single_shot are deleted from code. agy MCP injection via .gemini/settings.json proven live (such as reviewer session sess_de327dfe7c3cf024df49bfb6f896bd13 in run run_b5dfd0e3c280c19fcc13bf69801eacca). Follow-up: the token-bearing .gemini/settings.json must be removed on supervise stop regardless of signal (#51 cleanup-on-kill). | Revisit if Antigravity changes its config surface or grows an --mcp-config flag. |
| D149 | accepted | Daemon-owned long-lived interactive PTY sessions earn first-class lane-byline attestation via the same pid-start-time + launch-command-snapshot binding the --print wrapper had; owned-PTY sessions derive the author: <role>-<model>-<ordinal> byline rather than downgrading to author: operator (RFC 0088 Decision 2). Attestation remains an anti-fabrication guardrail (friction), not cryptographic non-repudiation; --continue turn-to-turn context drift does not weaken it. Extends D080/D141. | Removing -p would otherwise silently downgrade all artifact provenance to author: operator, since -p was the only attested authoring path. | Owned-PTY sessions across claude, codex, and agy attest in live runs this session (e.g. the Phase 1 codex live-verify session sess_5449a2366336892192e8e35a86aaa539 under closeout run run_b5dfd0e3c280c19fcc13bf69801eacca). Lane-attestation forgery hardening (verifying the supervised subprocess produced the artifact) remains tracked. | Revisit if attestation must become non-repudiation, or if the agent-loop self-attest path needs a distinct byline axis. |
| D148 | accepted | Deprecate the -p/--print/exec one-shot launch mechanism for agent lanes; every lane becomes a daemon-owned long-lived interactive PTY/agent-loop session driven by an adapter-specific bootstrap (per RFC 0088 Decisions 1, 3), completing D140 unconditionally. The per-adapter submit driver is: claude PTY stdin-submit; codex initial-prompt argv; agy --prompt-interactive argv. | The --print wrapper spawns a fresh process per packet with no preserved context, which interrogation (RFC 0082/0083) is meant to replace; -p was the only attested authoring path but the one that cannot preserve context. | The turn-driver, single_shot, and the --print/exec supervised wrapper are deleted. codex and claude owned-PTY agent-loops are live-verified end-to-end (Phase 1 codex live-verify session sess_5449a2366336892192e8e35a86aaa539 under closeout run run_b5dfd0e3c280c19fcc13bf69801eacca); agy is verified through MCP discovery + work.claim (#52). Remaining agy follow-up before its one-shot pipe lane is retired: reliable packet completion (agy currently over-builds a poll loop instead of executing a claimed packet) — tracked in #51. | Revisit when agy reliably completes claimed packets, allowing the agy one-shot pipe lane to be retired too. |
| D147 | accepted | Extend the RFC 0085 (D143) tailnet-identity read-route allowlist to serve the read-only HTML dashboard: GET /, GET /run (the server-rendered status page) and GET /static/{asset} (its CSS/JS) are now permitted over the web-ui.sock identity socket, in addition to the existing /v1/... JSON read routes. Still GET-only (the identity socket rejects all non-GET); the page renders the same status read already exposed at /v1/runs; path.Clean prevents /static/ traversal. IdentityReadRoutes + PermitIdentityRoute updated together; the normative TestIdentityRouteAuditMatchesAllowlist keeps them in sync. | The browsable HTML UI is served at / on the loopback bind but required a bearer token, so it was unreachable from a tailnet browser; only the /v1 JSON routes were on the identity allowlist, so tailscale serve users hitting / got route_forbidden. The human dashboard should be viewable over the private tailnet, read-only, without a token. | A tailnet user (identity-allowlisted) can now open https://<magicdns-host>:9443/ and see the dashboard. No new bind, no mutation surface (verb gate unchanged), D143's read-only invariant preserved by construction. | Revisit if the dashboard gains client-side fetches to non-allowlisted routes (they would need explicit allowlisting), or if a richer SPA bundle replaces the server-rendered page. |
| D146 | accepted | Accept F44 supervised turn-driver hardening: daemon-spawned supervised lanes append existing operator-local bin dirs ($HOME/.local/bin, $HOME/.npm-global/bin, or STRIATUM_SUPERVISED_PATH_DIRS) after the inherited system PATH, dedupe to one effective PATH, and never source those dirs from repository/workflow inputs. turndriver.Loop now treats generator failure, including exec-not-found, as report-and-clean-exit when OnFailure succeeds; it remains fatal when no reporter is configured or reporting fails. Supervised pipe launches capture the PID start token before an async cmd.Wait reaper, and read-side supervision liveness is zombie-aware, start-token-aware, and surfaces the latest session.report escalation for operator discoverability. | F42/D145 proved the turn-driver model but live daemon verification showed systemd PATH could not find user-installed generators, generation failures crashed the driver instead of taking the existing report path, and exited children could remain zombie/stale-alive in status. The fix had to stay generic by supervised process semantics and preserve the content-only child boundary. | Removes the need for an operator-local striatumd.service.d/path.conf drop-in for common local installs, keeps system PATH precedence over user-local fallback dirs, preserves D145 by stripping STRIATUM_* from content generator env, and makes failed generator turns visible through durable session.report plus supervise.status/dashboard read projections. Durable unexpected-exit terminal-state persistence is deferred; the reaper does not author supervisor state. | Revisit if a real daemon-level parked-floor state is added for resident retry-next-turn behavior, if non-standard install layouts need a richer path policy than STRIATUM_SUPERVISED_PATH_DIRS, or if unexpected child exits should persist terminal supervisor state instead of remaining a read projection. |
| D145 | accepted | Accept F42 turn-driver: non-self-driving process lanes may declare adapter_capabilities.single_shot: true or self_driving: false, causing supervise.start to run striatumd -agent-loop -turn-driver around the lane command. The driver, not the child agent, is the MCP client: it calls work.await_packet, conversation.show, and conversation.say; the child receives only topic plus transcript as a per-turn content prompt. The child environment strips all STRIATUM_* variables by default and F42 does not create a new child-visible token file. | RFC 0086 proved the conversation primitive but gemini-cli could not reliably hold the stateful await→say→await loop. The fix must be generic by adapter capability, not by model name, and must avoid regressing into the prohibited packet-spoon-feeding proxy pattern. | Adds pure Go turndriver loop tests and supervised driven-mode wiring. conversation.say remains the only mutation fed by generator output; workflow-control packets, leases, and tokens are never passed into the generator seam. supervise.status/dashboard projections surface agent_loop_mode=turn_driver. Residual v1 risk: a same-user local child might still discover daemon material outside its environment; deeper process isolation remains follow-up platform work. | Revisit for stronger child non-discoverability (socket namespaces or memory-only token topology), an operator-facing debug striatum conversation drive command, and broader adapter metadata validation/generator defaults. |
| D144 | accepted | Accept RFC 0086: multi-party conversation on the MCP agent-loop. A symmetric N-party live conversation (round-robin floor, shared turn-ordered transcript, bounded by max_rounds) generalizing RFC 0082 interrogation (the 1→1 asymmetric special case). conversation.{open,say,close,list,show}; a new conversation_message envelope on work.await_packet; turns reuse the message bus; lifecycle/floor in a plain new conversations table (migration 0017, no owner-table FKs, runtime-role-applicable; LatestDaemonDBVersion 16→17). Delivery is floor-derived from durable floor_index (idempotent, crash-safe), NOT a consumable message. | The goal was three frontier models holding a live 3-way interactive conversation in reasonable time on the same harness (the agent-loop). Interrogation was 1→1 and the "Conversation" workflow type was sequential fresh jobs; neither is a live N-party exchange. | Proven end-to-end: Claude Opus + Codex GPT-5.5 + Gemini 2.5 Pro held a 9-turn, 3-round conversation at ~16s/turn (models pinned per lane; the preview-model default caused gemini slowness, fixed by pinning gemini-2.5-pro). The crash-safe floor-derived delivery fixed a round-robin stall the live run surfaced. Regression test TestConversationRoundRobin. | Revisit for: a moderator/nominator floor model vs round-robin; rendering conversations in the chat UI (F43); gemini-cli's unreliable await/say loop needing an operator driver (F42); concurrent multi-conversation participation. |
| D143 | accepted | Accept RFC 0085: tailnet-identity UI authentication, loopback-preserving. The daemon HTTP bind stays 127.0.0.1 (unchanged); opt-in --web-tailscale (default off) starts a dedicated 0600 unix socket (web-ui.sock) serving the web UI in a Tailscale-identity auth mode — the stable target for tailscale serve. A request authenticates iff Tailscale-User-Login ∈ STRIATUM_DAEMON_WEB_TAILSCALE_USERS (empty/unset/whitespace → deny all, fail closed); read-only is enforced by an explicit audited GET route allowlist (NOT the HTTP verb), denying everything else incl. POST /v1/invoke; a normative route-audit test asserts no mutating route is reachable over the socket. tailscale serve (tailnet-private) only; funnel (public) prohibited as out-of-boundary. | RFC 0084 D1 mounted the web service but it is loopback + bearer only, unreachable from a tailnet browser (no cookie/session auth). The operator wants to view the UI over their private tailnet without exposing the daemon or pasting a token. Tailscale serve runs locally (dials loopback, so the bind is preserved) and supplies authoritative tailnet identity. | New read-only tailnet UI path; loopback/bearer/MCP unchanged and identity-blind. Run through the interrogating panel: the design interrogation (intg_4b69c562) returned needs_revision rejecting verb-based "GET means safe", so the read-only model became an explicit route allowlist + audit test before implementation; the build interrogation accepted; live-verified over the socket (allowed GET 200 + chat renders 6 turns, deny cases 403, no-identity 401, MagicDNS-Host 200). | Revisit for: WhoIs LocalAPI peer verification vs header trust; per-run repo resolution (F38) over the tailnet; deriving the route-audit allowlist from the dispatch table (F41); any scoped write access over the tailnet. |
| D141 | accepted | Accept RFC 0084 part 1: interrogable agent-loop attestation. requireLiveTarget (interrogation.open) now accepts a target that is active AND either wrapper-attested (unchanged D026 path) OR in the RFC 0082 §5 awaiting_interrogation window (it completed an interrogable job, evidenced by a session.awaiting_interrogation event). Artifact byline attestation (D080) is unchanged — the second path does not grant lane/model bylines (sessions still publish author: operator), so the forgery surface is not enlarged. | The live RFC 0083 run proved that interrogation required a wrapper-attested target (D026/D080), but attestation is bound to the --print supervised wrapper, which has no preserved context — while the headless MCP agent-loop sessions that DO preserve the context interrogation queries are never attested. The only sessions worth interrogating could not be interrogated, directly blocking RFC 0083/D140 --print deprecation for interrogable runs. | Genuine model-to-model interrogation works on agent-loop targets (proven end-to-end: a codex reviewer interrogated a headless claude synthesizer over 3 threat-model rounds, 6 turns persisted, catching a real html/template vs text/template XSS hazard). Regression test TestInterrogationOpenAcceptsAwaitingInterrogationTarget; TestInterrogationOpenRequiresLiveTarget still rejects a bare active non-awaiting unattested target. | Revisit if the agent-loop should earn first-class byline attestation (self-attest via owned PTY/pid), or if interrogation liveness needs an idle/heartbeat timeout beyond state=active. |
| D142 | accepted | Accept RFC 0084 part 2: interrogation-log chat UI. The Go web service serves a run's interrogation thread as a server-rendered chat at /v1/runs/{runID}/interrogations (list) and /v1/runs/{runID}/interrogations/{id} (chat page), rendered with html/template (not text/template, not client JS), backed by the existing reads.HandleInterrogationShow / interrogation.list path, with run-ownership 404 enforcement and only curated turn fields (D028). | Designed by the 3-model design loop + interrogation; the interrogation directly shaped the implementation (server-side html/template escaping for attacker-influenced turn bodies; run-ownership 404 to close IDOR). Closes the presentation-layer half of F36 for interrogation threads. | New web routes + webassets.RenderInterrogation + templates/interrogation.html; escaping tests in go/pkg/webassets and go/pkg/webservice pin that <script>/<img onerror> bodies render escaped. No new migration, RPC, or interrogation data-model change. Markdown, SSE live updates, export, and stricter multi-session tenancy are deferred. | Revisit if the React-island bundle (F36 part 2) should serve this view, or if Markdown/live-update/export are needed. |
| D139 | accepted | Accept RFC 0083: iterated panel review with interrogation. A reusable workflow shape with two structurally identical loops (design, build) chained design → build, each being fan-out (3 lanes) → synthesis → interrogating panel review with a bounded revision cycle. Two distinct bounded-iteration budgets are kept separate: interrogation rounds (≤3 per reviewer, early exit on resolved findings, enforced by the reviewer role prompt) and the revision cycle (cycles with on_verdict: needs_revision, max_iterations: 2). Build fan-out is on the review side (single-author implementation, 3-wide panel), not duplicate builds. | Artifact-mediated review (the prior-art three-lane-design-build-review and implementation-panel-flow examples) cannot reach reasoning-level defects; RFC 0082 interrogation sessions made preserved-context review possible but were not yet composed into a reusable shape. | A reusable example lands at examples/iterated-interrogating-panel/; the shape uses only existing striatum.workflow.v1 fields (parallel_group, interrogable, reviewer_access_scope: document_only, distinct review_posture, --capability interrogate, cycles) with no engine changes. First run: design + build the interrogation-log UI feature under docs/operator/workflows/interrogating-panel-2026-05-25/. | Revisit if the ≤3 interrogation-round cap should become an engine-enforced bound, or if concurrent vs serial interrogation of one reviewed target needs a policy. |
| D140 | accepted | Accept the conditional deprecation of the --print supervised wrapper for new workflows, gated on the MCP agent-loop functioning per adapter (claude / codex / gemini). The deprecation takes effect only for adapters whose agent-loop is proven empirically. Adapters whose agent-loop does not function fall back to --print for fan-out / review authoring only and MUST NOT be the interrogation target; the interrogation target is always an agent-loop lane (claude is the known-good baseline). Phase A finding (2026-05-25): the daemon-side agent-loop substrate works end-to-end with a real headless claude lane (claude -p drove await_packet→write→artifact.publish→ack→work.complete), but the striatumd -agent-loop PTY launcher does not submit its bootstrap prompt to an interactive TUI agent (claude buffers it unsubmitted). Workable substrate today = headless claude -p "<bootstrap>"; the PTY launcher needs a submit fix before it drives TUI agents. | Truthful interrogation requires preserved context (RFC 0082 §5); the --print wrapper spawns a fresh process per packet with no preserved memory, so a --print author can only answer from its published artifact — the cold-context review interrogation is meant to replace. An unconditional deprecation would strand any adapter whose agent-loop is unproven. | New workflows targeting interrogation use the agent-loop executor (headless launch); the conditional gate remains conditional per adapter, with the authoring-only fallback preserving non-interrogated lanes. | Revisit after codex/gemini headless-loop validation: do they graduate to interrogation targets; and fix the PTY launcher prompt-submit so interactive TUI lanes work. |
| D138 | accepted | Accept RFC 0082: interrogation sessions. A bounded, multi-turn, peer-addressed Q&A construct bound to a live target worker session (interrogation.{open,ask,answer,close,list,show}), targeted delivery via a typed work.await_packet envelope, an awaiting_interrogation context-preservation window on the MCP agent-loop, and curated provenance (D028) in the RFC 0081 dialogue trajectory. | The 2026-05-25 event-bus assessment found the original bus intent unrealized: review was artifact-mediated, the bus could not address a peer, and fresh_session_required reset context per job, so no worker could interrogate another's preserved reasoning. | Reviewers can iteratively interrogate a builder's preserved context (proven by the end-to-end intention test); the message bus gains session-addressed delivery; the MCP agent-loop is the executor for interrogable runs. All 9 Required Tests pass under live PostgreSQL. The new interrogations migration is ownership-safe but still needs owner application on the production daemon until RFC 0079 §5 / TODO F34 lands. | Revisit if interrogation answers need richer artifact references, push delivery (LISTEN/NOTIFY vs MCP SSE), or non-live (replay) targets. |
| D135 | accepted | Accept RFC 0079: make install and daemon lifecycle a designed surface. striatum daemon install generates a portable systemd user unit (%h/%t, no hardcoded paths), make install places binaries + unit + skills and verifies doctor, an operator daemon runbook documents the runtime layout and the daemon.toml DSN, and PostgreSQL migrations are applied by an owner/admin connection rather than the runtime role. | Post-RFC-0078 the Go binaries existed but install/lifecycle was hand-rolled cutover residue (a broken unit pointing at the deleted Python launcher, an ad-hoc socket symlink, an undocumented DSN). The 2026-05-25 run also surfaced that the daemon auto-migrating as striatumd_rw fails on any DDL against owner tables. | Operators go clone→make install→doctor with no tribal knowledge; the socket is canonical daemon-go.sock; migrations run as owner with runtime grants. | Revisit if a non-systemd deployment target or a multi-user/system service mode is needed. |
| D136 | accepted | Accept RFC 0080: test & build hardening. A reusable Go PostgreSQL harness (go/pkg/pgtest), live-PG tests running in CI, restored load-bearing coverage RFC 0078 had pragmatically retired, go vet/-race/lint/coverage gates, and a complete write-scope guard that baseline-diffs instead of flagging pre-existing untracked paths. | RFC 0078 reached closure under a pragmatic bar that left live-PG tests skipping and no race/lint gates; the parallel-gate write-scope deadlock showed the guard's whole-tree diff was wrong. | go test/-race/vet/lint are enforced; live-PG behavior is exercised; the guard no longer forces out-of-scope commits. | Revisit if CI database flakiness or linter noise becomes chronic. |
| D137 | accepted | Accept RFC 0081: conversation trajectories as a read-model over existing daemon events. striatum trajectory export/watch with dialogue/provenance profiles, ordering DERIVED at read time (ROW_NUMBER over created_at + source class + primary key — no stored column, no new authority, no ALTER of owner tables), a narrow trajectory_segments table for export/checkpoint metadata only, a tmux monitor, and a conversation workflow type. | A two-model claude↔codex conversation over the message bus (recorded under docs/operator/artifacts/two-model-conversation/) converged on this design; the spike showed the bus worked but trajectories had no read/export surface. An initial implementation that stored run_event_seq via ALTER crash-looped the daemon (owner-restricted tables) and was reworked to the read-derived design. | Trajectories are observable and exportable as curated structured provenance, never raw provider transcripts (D028); verified by reproducing the recorded conversation. | Revisit if a redaction tier beyond field-allowlisting is needed for shareable exports, or if ordering needs a stronger cross-lane clock. |
| D134 | accepted | Accept RFC 0078: Go is the only Striatum runtime language in active HEAD. The Python CLI, local web/service, workflow-authoring helpers, corpus tooling, installers, generators, packaging, and the Python test suite are removed; their behavior is ported to Go or explicitly retired. The temporary Python-trace guardrail is retired after final deletion because historical archive text is provenance, not an active runtime surface. | The Go daemon, CLI, MCP, and web service reached parity (RFC 0068/0070/0050/0075); the mixed-language surface was pure maintenance cost. The 2026-05-25 closure run ported the corpus redaction tier, the skills/plugin installers, and the two contract generators into Go, retired the scaffold flag and chat/dogfood web routes, deleted all tracked Python source/tests/packaging/scripts, and rewrote current operator guidance to Go-only. | Repository HEAD has no tracked Striatum *.py/pyproject.toml; release is Go binary archives. Supersedes D018's Python-V1 preference and the RFC 0068/0070 Python-client carve-outs. Daemon-owned PostgreSQL remains the sole live substrate. Historical decision/roadmap text retains descriptive Python mentions as provenance; target-repository Python examples remain allowed. | Revisit only if a Go parity gap surfaces that a removed Python surface silently covered, or if a new Striatum runtime language is ever proposed. |
|---|---|---|---|---|---|
| D133 | accepted | Flip global auto-finalize live allowance after the D125 evidence gate. recovery.auto_finalize and recovery.sweep may run live by default for workflows that do not explicitly opt out; the standalone CLI/RPC command still defaults to dry-run preview unless --live/dry_run=false is requested. | The D125 gate is satisfied by three operator-declared live successes across review, build, and synthesis lane shapes with zero current contested audit-chain events, and the user explicitly accepted the default-live decision on 2026-05-24. Keeping workflow opt-in as the live default after the gate was satisfied left TODO 56's final policy cutover unresolved. | D125's dry-run-default operational rule is superseded. Workflows may opt out with recovery.auto_finalize.enabled=false; --force remains an operator override for explicit opt-out. Status/dashboard/web projections remain read-only dry-run previews but report global_default_mode="live" and the satisfied D125 gate. Circuit breakers, stable-artifact validation, lane evidence checks, and no-partial-publish guards remain mandatory. | Revisit if default-live auto-finalize produces contested audit-chain events, repeated false positives, or a workflow class that needs a richer per-job opt-out policy than the current workflow-level enabled=false. |
| D132 | accepted | Accept RFC 0074 for the current workflow shape, role-pack, and adversary-pack catalog expansion, including Phase B generator support for the lightweight implementation_panel shape. | Phase A catalog metadata and the runnable implementation-panel example were already accepted as read-only authoring vocabulary; Phase B now compiles the same bounded shape through both Python and Go generators without adding RFC 0052 committee semantics or runtime state. | RFC 0074 and the RFC index are accepted. workflow generate --shape implementation_panel supports --role-pack, --adversary-pack, proposal_count, and score_dimensions; generated workflows remain ordinary validated V1 workflow trees. Full RFC 0052 debate/panel artifacts and richer chooser cost UX remain separate future work. | Revisit when adding more generated panel/catalog shapes, promoting code_doc_audit into the generator, or implementing RFC 0052 typed committee deliberation. |
| D131 | accepted | Accept RFC 0075's current tmux-observable MCP session contract as implemented. | The source now contains the scoped RFC 0075 surfaces: Go agent-loop PTY bootstrap, session.report for structured pre-work ready/heartbeat/question/escalation, RFC 0077 daemon-owned protocol liveness timestamps and deadline classifications, tmux attach metadata projected through status/dashboard/supervise reads, web run-detail session rendering for liveness and attach metadata, fail-closed supervision.require_tmux for PTY-helper lanes, and guardrails preventing terminal output or transcripts from becoming workflow state. | RFC 0075 is accepted for the current local-first contract. Live workflow control remains MCP/UI first; tmux is inspection metadata only. Universal tmux-by-default policy for every possible future live-interactive profile is not a hidden open implementation gate; it needs a later explicit profile/default-policy decision if the product wants to tighten beyond the current supervision.require_tmux fail-closed opt-in. | Revisit if a future live-interactive lane profile needs tmux required by default, if an equivalent local PTY multiplexer is accepted, or if operators need historical session-activity timelines beyond current per-session metadata. |
| D130 | accepted | Keep escalation artifact publication link-only. Publishing a striatum.escalation.v1 artifact must reference an existing escalation-class blocker in the same repository/run; artifact publication does not synthesize live blockers or inbox rows. | Current code and SPEC already enforce link-only behavior, and allowing artifact.publish to create live escalation state would blur provenance publication with workflow-state mutation. A dedicated escalation.create method or typed escalation table needs a separate accepted design if the product later needs artifact-originated live escalation creation. | RFC 0062's artifact-only creation policy question is closed for the current product: AI lanes create live escalations through work.block using an escalation-class blocker kind, then publish an escalation artifact linked to that blocker. TODO 53 remains partially open only for stricter blocker payload schema, a possible typed escalation table, and the eventual packet-helper rename. | Revisit only through a new RFC or decision that defines escalation.create, typed escalation storage, capability requirements, audit semantics, and UI/MCP behavior. |
| D129 | accepted | Accept RFC 0077 as the daemon-owned MCP activity liveness slice. | RFC 0075's first slice gave agents session.report, but operators still needed PostgreSQL-owned protocol timestamps and deadline classes that do not depend on tmux pane text or transcripts. | Migration 0012 adds session MCP/activity timestamp columns and current stall projection fields. Go MCP tools/list, work-packet lifecycle mutations, work heartbeats, and session.report record session activity. Status/dashboard/supervise reads project protocol liveness, and the resident recovery sweep persists stall transitions with metadata-only session.liveness_deadline_missed / session.liveness_recovered events. D131 later accepts the broader RFC 0075 tmux-observable session contract for the current scoped implementation. | Revisit if workflow/lane-level liveness policy overrides need to supersede daemon defaults, or if operators need append-only historical activity rows instead of per-session current timestamps. |
| D128 | accepted | Accept RFC 0076 as the reusable three-lane code and documentation audit workflow shape. | The first runnable operator workflow completed on 2026-05-22 and produced independent authority/runtime, docs/decision-drift, and operator/adoption findings plus synthesis and a remediation plan. The run found no critical authority regression; one Claude lane required operator recovery, which is recorded as run evidence rather than a change to the workflow authority model. | RFC 0076 and the RFC index are accepted. Hand-authored code_doc_audit workflows may use the shape before generator/catalog support lands. The remediation follow-up is scaffolded under docs/operator/plans/rfc-0076-audit-remediation.md and docs/operator/workflows/rfc-0076-audit-remediation/. Generator/catalog integration, dedicated audit-finding schemas, and UI issue-queue projection remain future follow-up decisions. | Revisit after multiple completed audit runs or when RFC 0074 catalog/generator work is ready to make code_doc_audit a generated template. |
| D127 | accepted | Keep optional Git/PR integration out of Striatum core's autonomous authority. Read-only local Git snapshots may proceed first; durable commit-request and PR-request artifacts may be added; local commit-apply may create a local commit only after explicit operator confirmation; hosted provider actions stay out of core and require a later human-principal-confirmed optional plugin decision. | The local-first product boundary forbids autonomous commits, pushes, hosted provider calls, provider SDK imports, telemetry, or external persistence without explicit authority. TODO 60 needed a boundary decision before implementation could move beyond read-only snapshots. | RFC 0067 is unblocked for read-only snapshot implementation and artifact contracts. Commit application must be represented as an explicit, confirmed local mutation; PR/provider behavior remains out of core and cannot land as implicit GitHub/GitLab/etc. integration. | Revisit only through a new accepted RFC or decision that defines optional provider plugin behavior, credential handling, confirmation semantics, and the exact hosted operations in scope. |
| D126 | accepted | Adopt Corpus Contract V2 and replay/archive foundations with composite corpus_id identity (slug:sha256), graduated redaction tiers, workflow opt-in augmentation by reference with agent-side fetch, hybrid archive bundles, verification replay by default, read-only semantic inspection, no comparative replay, deep-chain verification always, and optional daemon audit-chain cross-check. | TODO 59 / RFC 0057 / RFC 0066 had replay/archive foundations but lacked the product choices needed for a V2 schema and optional augmentation boundary. The accepted shape preserves local-first operation and the augmentation-not-dependency invariant while giving consumers stable identity, privacy, and verification semantics. | Implement V2 manifest/schema fields, archive defaults, verification depth, and workflow augmentation references. Striatum still runs when augmentation sources are missing, slow, unreachable, or unconfigured; no external memory service becomes live workflow authority. | Revisit if V2 consumers cannot validate the schema without Striatum source, if the composite identity proves unstable under repository moves/re-registration, or if a future augmentation channel needs stronger packet-budget or capability constraints. |
| D125 | superseded | Keep global auto-finalize default as dry-run projection until three successful live dogfoods across at least two lane shapes with zero contested audit-chain events satisfy the default-on evidence gate. | TODO 56 needed a product answer for default policy after the bounded daemon method slice landed. Default-on behavior carried recovery and provenance risk until live dogfood evidence proved the safety envelope. | The 2026-05-24 D125 evidence gate is satisfied by three live successes across review, build, and synthesis lane shapes with zero current contested audit-chain events; lane-finalization visibility, skipped-candidate cause classes, and the consecutive-failure circuit breaker have also landed. D133 supersedes the dry-run/workflow-opt-in default and flips live allowance on by default with explicit workflow opt-out. | Revisit through D133 if default-live auto-finalize needs rollback, additional gates, or finer-grained opt-out controls. |
| D124 | accepted | Make daemon-core workflow lint the authoritative accepted-risk surface. Lint overrides may write durable accepted-risk state only through daemon-backed CLI/UI/MCP clients; each accepted-risk record must cite a decision artifact and bind to an immutable workflow snapshot or fingerprint. Workflow-file metadata is not live authority. | TODO 55 had strict lint and decision-id references but no durable authority for accepted risks. Binding the record to daemon state and immutable workflow identity keeps the live authority in PostgreSQL while preserving decision-artifact provenance. | Implement daemon-core lint evaluation plus accepted-risk override mutation surfaces through clients. Existing CLI-local lint can remain advisory, but durable accepted-risk persistence must be append-oriented daemon state and decision-linked. | Revisit if daemon-core lint becomes too expensive at run preparation, if workflow snapshot identity changes, or if accepted-risk records need cross-repository scope. |
| D118 | accepted | Mark fully replaced RFCs and decisions as superseded instead of leaving them as plain accepted. Use successor links to distinguish obsolete operational guidance from historical provenance. | The docs now contain several long-lived decisions whose implementation context was later replaced by daemon-owned PostgreSQL, mandatory daemon MCP, Go daemon authority, or CLI retirement. Without an explicit supersession status, operators and agents can follow historically correct but operationally false guidance. | Future RFC and decision updates must update both the replaced artifact and the successor artifact. Full replacement changes status to superseded; partial replacement keeps the original status but names the superseded clause and successor. Historical artifacts stay in place unless a separate cleanup decision removes them. | Revisit if a generated decision/RFC index can enforce successor links and status drift automatically. |
| D117 | accepted | Retire the CLI-side legacy daemon registry wrapper and smoke-script SQLite fallback. Production daemon CLI/admin dispatch uses striatum.daemon_pg.client_admin; dashboard --all must route through daemon RPC; direct --daemon local-read fallback is refused; fresh-clone/package smoke scripts skip when PostgreSQL setup is unavailable instead of entering paired SQLite test-harness mode. | D107/D111 make Go/PostgreSQL the production daemon target, D113 closed writable SQLite import windows, and D114 removed no-PostgreSQL daemon MCP resource fallback. Keeping a CLI wrapper around striatum.daemon and smoke scripts that created .striatum/retired-local-state preserved a second state path after the operator requested Python-daemon and SQLite retirement. | src/striatum/legacy_sqlite/daemon_registry.py and src/striatum/daemon.py are removed. Production source guardrails expect no imports of striatum.daemon and assert the retired module remains absent. The broad legacy tests/test_daemon.py fixture file is deleted after PG-side tests preserve current behavior for status privilege hints, repo path hardening, revoked repo-scoped capabilities, and daemon sweep degradation/audit rows; daemon security fixture coverage now targets current runtime/MCP/capability helpers. | Revisit only if a future accepted fixture strategy needs a non-production compatibility wrapper that cannot be represented by direct legacy tests and does not restore SQLite as an operator or smoke-test path. |
| D116 | accepted | Make corpus export manifests substrate-explicit instead of SQLite-schema-shaped. Shared manifest construction now accepts authority metadata and emits state_authority; PostgreSQL corpus export reads daemon/repository schema metadata directly instead of faking SQLite PRAGMA user_version. | D107 requires SQLite eradication from compatibility paths, and corpus manifests were still coupled to a SQLite connection even on the PostgreSQL handler. The old repo_local_schema_version field overfit pre-D094 state and forced the PG path to emulate SQLite just to build a bundle. | src/striatum/corpus/manifest.py no longer imports sqlite3; repo_local_schema_version is retired from new manifests; bundle_sha256 is written into manifest.json and excludes generated_at from the digest so unchanged bundles keep stable content hashes. The legacy direct exporter has been deleted; corpus export now runs through the daemon PostgreSQL handler. | Revisit only if Corpus Contract V2 changes the manifest envelope or if a future non-PostgreSQL authority is accepted without restoring SQLite-shaped metadata. |
| D115 | accepted | Make the workflow-upgrade running-run guard PostgreSQL-only. workflow upgrade and workflow upgrade --add-phases now refuse when daemon PostgreSQL state is unknown; they do not inspect repo-local SQLite even under the paired legacy test-harness escape. | D107 requires SQLite eradication from compatibility paths, D113 closed writable SQLite import windows, and the remaining workflow-upgrade fallback was only a historical fixture bridge. Allowing a local-file authoring command to reopen repo-local SQLite kept an avoidable production-module sqlite3 import and weakened the daemon/PostgreSQL authority boundary. | src/striatum/cli/workflow.py no longer imports sqlite3 or striatum.db; workflow-upgrade tests must model running-run state through the PostgreSQL guard seam. Legacy fixtures that need workflow upgrades must register daemon PostgreSQL state or expect fail-closed behavior. | Revisit only if a future offline workflow-authoring mode is accepted with a non-SQLite running-run authority and explicit safety constraints. |
| D114 | accepted | Retire the no-PostgreSQL daemon MCP resource fallback. resources/list and resources/read on DaemonRpcServer now require a daemon PostgreSQL connection; without one they fail closed instead of importing the legacy Python daemon or opening the SQLite daemon registry. | D107/D111 make the Go/PostgreSQL daemon the production authority, D113 closed writable SQLite import windows, and Python-daemon module removal has landed; remaining work is legacy fixture cleanup. Keeping a test-harness-only MCP resource fallback preserved avoidable connect_registry() callers and made MCP resource behavior depend on the old SQLite registry. | Daemon MCP resources are PostgreSQL-only. The Python-daemon daemon_mcp_resources, daemon_mcp_read_resource, and related helper functions are removed; the retired src/striatum/daemon.py module is deleted. Architecture guardrails no longer classify daemon MCP registry fallback callers; no-pg_conn MCP resources return a fail-closed JSON-RPC error. | Revisit only if a future non-PostgreSQL MCP resource mode is accepted as a new product surface that does not restore SQLite registry access. |
| D113 | accepted | Close writable SQLite import windows for production and operator paths. striatum daemon migrate and striatum daemon migrate-repo-local remain parser-compatible retired spellings, but they refuse with exit code 12 before importing or opening SQLite migration code; direct repo-local import helpers require the explicit STRIATUM_LEGACY_SQLITE_IMPORT=1 fixture escape. | D107 requires SQLite eradication, D111 made Go the only daemon core, D110 removed the SQLite-bound import RPC from the production contract, and the operator asked to eliminate SQLite everywhere rather than preserve migration convenience indefinitely. Keeping live import commands encouraged new operational dependence on a retired substrate. | Day-zero adoption, repo registration, and repo_not_migrated errors no longer point operators at a live migration command. Operators must archive/remove retired local-state files and register with striatum adopt or striatum repo add --init; the retired Python daemon module, Python MCP wrapper, legacy local-state package/facades/fixtures, direct corpus exporter, and V1 local-state schema module are deleted. | Revisit only if an explicit archival recovery product decision reopens a read-only export/import tool that does not restore SQLite as a supported workflow-state substrate. |
| D112 | accepted | Remove apply.reviewed_patch from the production daemon RPC contract rather than keeping it as a fail-closed retirement blocker. | D107/RFC 0068 made the Go daemon the production authority and converted missing parity into explicit contract decisions. The reviewed-patch apply mutation is still product-incomplete: local Ed25519 fallback key rotation and apply receipts exist, but the full apply gate and stronger key custody remain deferred. Keeping an unusable production RPC name implied an operator-supported apply path that does not exist. | apply.reviewed_patch is no longer listed in the daemon method contract, generated Go registry, MCP discovery, command authority matrix, or RFC 0068 retirement ledger. Stale direct calls return and audit as method_unknown. Existing apply receipt read/verify surfaces and daemon key rotation remain supported. | Revisit only through a new accepted sealed-apply RFC or product decision that defines the full apply gate, authority model, key custody, and operator UX before reintroducing a production mutation. |
| D111 | accepted | Retire the operator-facing Python daemon core selector. striatum daemon start and striatumd always launch the Go daemon; --core go is a deprecated no-op compatibility flag, and --core python / STRIATUM_DAEMON_CORE=python no longer select a daemon. The multi-repo harness and CI default to the Go daemon. | D109 made Go the default but preserved a Python escape hatch. The operator clarified that the Python constraint should be eliminated, and the Go conformance gate is now the production daemon gate. Keeping a Python selector creates false support expectations and slowed deletion of the Python daemon module. | Python remains a CLI/web client implementation language, not a daemon core. The striatumd console script is a Go-daemon launcher shim; src/striatum/daemon.py is deleted, the Python MCP wrapper is deleted, and the legacy local-state package/facades/fixtures are gone. make test-multi-repo and CI no longer run a Python-core lane. | Revisit only if a Go conformance regression cannot be fixed safely or represented as a removed unsupported method that returns method_unknown. |
| D110 | accepted | Remove the SQLite-bound daemon.migrate_repo_local, dogfood.publish_on_behalf, and dogfood.surgical_recovery RPC methods from the production daemon contract instead of keeping them as fail-closed registered methods. | D107 makes SQLite eradication and Python daemon deletion the target architecture. Keeping SQLite-bound composites and import helpers as production RPC names inflated the cutover ledger without providing a supported production path. | The daemon contract, generated Go registry, MCP discovery, and command authority matrix no longer list those three methods. Calls to those names are method_unknown at MCP/RPC boundaries. D112 applies the same production-contract removal policy to apply.reviewed_patch; D113 closes the operator-facing writable SQLite import commands. The legacy Python dogfood operator helper code is historical/direct-mode compatibility debt, not production daemon surface. | Revisit only if a PostgreSQL-native operator composite is accepted as a new method. |
| D109 | accepted | Make the Go daemon the default for striatum daemon start; the temporary --core python escape recorded here was later retired by D111. | RFC 0068 active contract-method parity is now executable: the Go daemon has no missing or generic not_implemented active handlers, make daemon-go-conformance is the release gate, and D110/D112 remove unsupported method names from the production contract instead of carrying a broad parity ledger. The operator has repeatedly clarified that the Python-primary constraint should be eliminated. | Go is the default daemon core, D111 removes Python core selection, and docs treat Python-daemon cleanup as deletion work. Packaging and stale-binary checks remain mandatory because a missing or stale Go binary is now a startup blocker rather than an opt-in experiment failure. | Revisit if make daemon-go-conformance cannot stay green on supported platforms, or if a production-only behavior gap appears that cannot be safely removed from the production contract and represented as method_unknown. |
| D108 | accepted | Keep docs/reference/command-authority-matrix.md as the curated authority/status matrix, while generated contract tables and executable drift tests own contract-derived facts such as CLI route labels, capabilities, scopes, and CLI fallback state. | The matrix combines generated metadata with human architectural classification: authority path, SQLite dependency, Go-port status, and transition notes. Fully generating it would require a parallel override metadata format without eliminating the need for review. | RFC 0071's generated-versus-curated matrix question is resolved. docs/reference/daemon-method-tables.md remains generated from contracts/daemon_methods.json via go:generate in go/pkg/rpc/registry.go. After the RFC 0078 Go-only cutover the retired tests/architecture/test_authority_guardrails.py is replaced by Go guardrails over the contract-derived facts: go/pkg/rpc/registry_contract_test.go fails when the generated registry drifts from the contract's capabilities/scopes, go/pkg/cli/routestest/routes_freshness_test.go fails when generated CLI routes drift from the contract, go/pkg/rpc/error_catalog_test.go fails when the curated matrix's error-code catalog omits a cataloged code, and go/pkg/cli/routes/routes_test.go fails when a verb's help falls back to the matrix doc instead of rendering locally. The matrix's human-classified columns (authority path, transition notes) are review-owned and not directly drift-tested. | Revisit if the curated columns repeatedly drift despite tests, or if a compact checked-in override schema can generate the matrix without hiding architecture status from review. |
| D107 | accepted | Supersede D105 and restore the Go production daemon port as the target architecture. Python may remain the CLI/web client layer, but the Python daemon is transitional and should retire after Go reaches contract, Postgres, audit, authorization, MCP, service, recovery, and packaging parity. SQLite must be removed from production and compatibility paths, surviving only as bounded one-way import fixture material until that migration support is retired. | The operator clarified that the Python-primary constraint was accidental product encoding. The desired product is one local audited daemon authority implemented in Go, with PostgreSQL as the sole live substrate and no SQLite compatibility runtime. | RFC 0068 owns the Go port. Roadmap/TODO reopen full Go daemon parity, stale Go binary/schema checks become production gates, and docs must stop presenting Go as a permanent support track. Python daemon cleanup becomes retirement work, not a permanent boundary. | Revisit only if the Go port cannot meet the conformance gate, or if the Python CLI/web layer becomes the larger maintenance burden than the daemon port itself. |
| D106 | accepted | Shelve RFC 0049's interactive Claude lane via MCP as a capability experiment, not active backlog. | The v1.48.1 wrapper-auth fix eliminated the urgent no-publish stall that made RFC 0049 feel release-critical. The remaining motivation is Anthropic billing/quota economics for interactive Claude Code after June 15, 2026, but the proposal still depends on PTY/MCP stability and external billing interpretation that Striatum cannot verify from local code alone. | TODO item 37 is marked shelved. No implementation should proceed without an explicit spike. Existing per-packet supervised wrappers stay the production path. Work on PTY supervision may continue under the generic supervisor/runtime roadmap, but not as a Claude-specific billing workaround. | Revisit if Anthropic billing terms change materially, a supported interactive-PTY billing answer is obtained, or an operator explicitly funds the PTY/MCP spike with measurable success criteria. |
| D105 | superseded | Historical superseded decision: temporarily keep the Python daemon as the primary production core and narrow Go to PTY/process supervision plus packaging-sensitive subprocess work, superseding D084's full replacement expectation while preserving the language-neutral RPC/audit contract. | This was a short-lived remediation-focus decision. D107 supersedes it: the operator wants the production daemon ported to Go, the Python daemon retired after parity, and SQLite removed from production and compatibility paths. | Historical context only. Focused support work remains useful, but it no longer blocks or replaces full Go daemon parity. | Revisit through D107/RFC 0068 only. |
| D104 | accepted | Daemon and daemon MCP are mandatory for operator-driven Striatum runs. D094 already made the daemon/PostgreSQL substrate a hard prerequisite; this decision also supersedes the optional-MCP portions of D009 and D013. The CLI remains acceptable only as a daemon-backed client or bootstrap/admin shell; direct repo-local mode and optional local MCP wrappers are not valid production run surfaces. | Human operator decision: the control plane should have one audited daemon authority boundary and one tool interface that agents can use without hand-copying lease/session ids through shell commands. Keeping MCP optional preserves the same drift D094 removed from the state substrate. | Operator initialization requires daemon, PostgreSQL, and daemon MCP readiness before workflow mutation. Workflows should prefer approved daemon MCP/chat tools for runner mutations; any CLI fallback must route through the daemon and be recorded in the operator report. The STRIATUM_DAEMON_REQUIRED=0 STRIATUM_TEST_HARNESS=1 escape is test-only, not an operator run mode. Docs, prompts, and future workflow scaffolds must stop presenting direct mode or optional MCP as normal choices. | Revisit only if daemon MCP proves unreliable across supported agent lanes, or if a replacement tool interface offers the same audited daemon boundary with less operator friction. |
| D103 | accepted | Make the human principal an escalation-only role and make the AI operator the default driver for routine claim, ack, publish, verdict, and complete work. Escalation triggers are the declared blocker classes (ambiguous_goal, missing_authority, contradicting_decisions, no_available_reviewer_lane, committee_stalemate, override_required) plus AI-authored escalation artifacts. | Dogfoods 028-056 already used humans only for stalls and overrides. The old "human operator" framing in SPEC, GETTING_STARTED, and HOW_TO_HUMAN no longer matched RFC 0015, RFC 0040, RFC 0046, or RFC 0052 committee-stalemate handling. | RFC 0053 specifies the principle and updates operator docs. The escalation artifact-kind schema, daemon projections, and inbox commands landed later under remediation Phase 5. Deferred follow-ups remain the human_checkpoint rename, waiting_human state rename, and prompt-string sweep. | Revisit if AI operators hit recurring blockers outside the declared classes, if deployments require a reliably human principal, or if the blocker taxonomy cannot keep up. |
| D102 | accepted | Override dogfood-048 / RFC 0043 V1 cycle exhaustion and ship the Postgres-as-sole-substrate, daemon-required slice with follow-up findings. | Codex and Gemini both returned needs_revision with real findings, while Claude accepted with findings. The run had no remaining revision cycle, but the implementation closed the core substrate transition and the findings were bounded enough to land as RFC 0043 V1.5 follow-up work. | This precedent is distinct from D095-D101: it is not same-model reviewer co-blindness. It records a real-findings override and folds crash-recovery, CLI escape closure, and migration-command findings into TODO item 31 / later RFC 0043 hardening. | Revisit if real-finding cycle-exhaustion overrides become common enough to require a different workflow shape or automatic follow-up workflow creation. |
| D101 | accepted | Override dogfood-047 / RFC 0039 V1.5 codex needs_revision through 2-of-3 cross-lane consensus and defer the codex findings to the Go daemon follow-up. | The implementer was Claude and the codex reviewer found high-severity issues, but Claude and Gemini both accepted with findings. This is the codex-reviewer-of-claude-implementer pattern, related to but distinct from the codex/codex co-blindness seen in D095-D098 and D100. | Codex findings on Go dependency pinning, unauthenticated fallback, skipped Go matrix coverage, denial checks, and audit race tests were absorbed into RFC 0039 V1.6 / later RFC 0068 Go daemon hardening. | Revisit when cross-lane majority overrides with a single harsh reviewer recur outside the known codex-reviewer pattern. |
| D100 | accepted | Override dogfood-046 / RFC 0044 Striatum-side corpus-export cycle exhaustion and ship the Striatum-side V1 slice. | Codex needs_revision repeated the codex/codex co-blindness pattern, while Gemini's needs_revision focused on out-of-scope Engram-side surfaces. Claude accepted the in-scope Striatum-side corpus export with findings. | Striatum keeps only the redacted corpus export and augmentation-boundary tests. Engram-side ingester, MCP server, tenant/corpus isolation, and memory capability findings remain external follow-up work. | Revisit only if Engram-side concerns start requiring changes to Striatum's export contract or augmentation boundary. |
| D099 | accepted | Override dogfood-045 / RFC 0038 V1.5 codex reject through 2-of-3 cross-lane consensus and land the web UI integration-gap slice. | The run deliberately avoided codex/codex pairing by using a Claude implementer, but the codex threat-model reviewer still rejected on critical findings while Claude and Gemini accepted with findings/low risk. | Codex critical findings on placeholder bundles and supply-chain polish were absorbed into RFC 0038 V1.6 follow-up, which later landed the real bundle and dependency hygiene. | Revisit if reject overrides happen again without a fresh workflow that directly resolves the rejected findings. |
| D098 | accepted | Override dogfood-044 / RFC 0040 V1.5 codex needs_revision and land daemon-dispatch, composite-tool, and watcher work with follow-ups. | This was the fourth codex/codex implementer-plus-reviewer anti-pattern instance after D095-D097. Claude accepted with findings and Gemini accepted, while codex findings were bounded to follow-up work. | Composite failure observability and packet-evidence debt were tracked under RFC 0040 V1.6 / TODO item 28. The repeated pattern contributed to the same-model pairing guardrail later enforced by workflow validation. | Revisit if same-model reviewer findings are no longer predictable enough to be handled by lane-diversity policy. |
| D097 | accepted | Override dogfood-043 / RFC 0045 V1 codex needs_revision and land the multi-phase workflow schema/editor slice with V1.5 follow-up. | This was the third codex/codex implementer-plus-reviewer anti-pattern instance. Claude and Gemini accepted, while codex findings clustered around phase validation and editor edge cases that could be handled as a follow-up. | RFC 0045 V1.5 later closed phase-jump validation, phase field compatibility, synthesis-job metadata, frontend drag-drop phase bypass, and invalid/unknown phase display tolerance. | Revisit if phase-schema work needs stricter mandatory re-review before landing. |
| D096 | accepted | Record dogfood-042 Track C as a cycle-exhaustion override and second codex/codex co-blindness precedent. | The Track C review exhibited the same implementer/reviewer same-model blind spot later named in D095-D098. The accepted path was to land the bounded slice and carry the findings into the next remediation/follow-up workflow instead of reopening an exhausted cycle. | This decision is retained mainly as anti-pattern provenance for review-diversity policy and committee-deliberation motivation. | Revisit only if the original Track C artifacts are restored and need a fuller row. |
| D095 | accepted | Override dogfood-042 Track A / RFC 0039 Go daemon core cycle exhaustion and record the first codex/codex co-blindness precedent. | Codex implementer and codex reviewer converged on the same blind spots, producing a needs_revision verdict that the other review lanes did not share. The run had exhausted its revision cycle. | Codex findings were carried into the RFC 0039 V1.5 follow-up. D095 became the first named precedent for same-model reviewer co-blindness, later motivating workflow validator warnings/refusals and RFC 0052 committee-deliberation work. | Revisit if same-model review is reintroduced under an explicit accepted-risk policy. |
| D094 | accepted | Supersede the local-SQLite authority from D006, D007, D009, and D036. Repo-local workflow state moves to the daemon-owned PostgreSQL instance from RFC 0033; the daemon becomes mandatory for Striatum operations; direct repo-local CLI mode retires. .striatum/ remains operational scratch only. D028 and D009's "agents call the CLI, not raw SQL" rule remain intact. | The daemon, RPC, supervision, cross-repo, and multi-repo PG harness work made the dual SQLite/Postgres substrate pure transition debt. Keeping both blocks multi-tenancy, hosted mode, cross-repo coordination, and the Go daemon plan while adding no durable operator value. | RFC 0043 originally defined per-repo migration, daemon-required CLI behavior, .striatum/ scratch semantics, and RFC 0030 method-registry coverage for repo-local mutations; D113 later retired operator-facing writable SQLite imports in favor of archive/remove plus registration. src/striatum/db.py and src/striatum/migrations.py retire behind daemon-mediated schema management; historical dogfood scaffolds stay frozen. Multi-tenancy and hosted mode become follow-up RFCs. | Revisit if Postgres setup remains untenable after daemon doctor, if a credible embedded substrate is selected, or if multi-tenancy and hosted mode are abandoned. |
| D093 | accepted | Accept RFC 0040 V1 operator-side slice: twelve dogfood-lifecycle chat tools over striatum.api.invoke, per-tool-family harness-profile fragments in the bundled template catalog, generator enrichment, and striatum workflow upgrade <path> (refuse-on-conflict + --force + --dry-run + refuse-on-running-workflow). Composite tools and daemon-side supervised-progress heartbeat land in the systems half. | Operator was making ~20-30 mechanical bash CLI invocations per dogfood, copying lease/session/message ids by hand. The four friction patterns (036 strategy-then-exit, 037 ask-and-exit, 038 lease-expiry, 038/039 front-matter) each recur across dogfoods. | See RFC 0040 and docs/HARNESS_FRICTION_PATTERNS.md. v1.29.0 shipped the local owner-side chat surface. D107/RFC 0070 supersede the api.invoke production-authority detail: daemon MCP/chat mutations dispatch through daemon RPC, and SQLite-bound composites are retired/fail-closed until a PostgreSQL-native replacement is accepted. | Revisit when workflow upgrade should cover non-profile fields, when RFC 0035 should extend with composite-tool e2e coverage, or when new harness semantics require a broader chat-tool contract. |
| D092 | accepted | Supersede D073's implicit "no node toolchain" rule. The web UI may adopt Vite + a framework + TypeScript + npm-managed dependencies as a contributor-side build-time dependency. Operator-side install stays pip-only; bundled output ships inside the Python package. | D073 set the rule when the UI had ~6 pages. After RFC 0023/0024/0036/0037 the UI now spans ~15 pages plus chat + MCP tools + visual builder + SVG graph, and the queued feature set (tree browser, syntax highlighting, drag-drop graph editor, chooser wizard) scales poorly with pure vanilla JS. D073's stated revisit trigger ("when the SPA grows complex enough to justify a tiny framework") has arrived. Same logic D084 used for a Go daemon: second toolchain allowed where the operator-surface value exceeds the contributor-side toolchain tax. | RFC 0038 picks the toolchain (Vite + React + TypeScript) + deployment shape (islands architecture, not full SPA). Contributor-side tooling lands under src/striatum/web/frontend/. CI gains a node-22-LTS step + bundle-hash check. Packaging still ships single-artifact. CSP, JSON API, SSE feed, mutation gate, audit chain unchanged. | Revisit if CI build flakiness becomes chronic, if npm supply-chain incidents force a security review, or if UI ambition retracts. |
| D091 | accepted | Refine D089: OPERATOR_REPORT.md is written incrementally during the run, not only at the end. The operator appends to it as decisions and interventions occur and finalizes it before the dogfood commit. | The narrative degrades fast: details of why a particular publish-on-behalf happened, which specific gate denied, which sub-agent stalled, and what the operator chose are easiest to recover while the run is still in working memory. Waiting until end-of-run risks compaction or context truncation erasing the lived state. | Operator drafts the report once run prepare succeeds (header, run id, branch, scope, deferral list) and appends a dated line per intervention (publish-on-behalf, stale-lease recovery, cycle-exhaustion override, etc.) at the moment it occurs. The final commit step does the wrap-up sections (notable wins, recorded risks, verification artifacts, "deliberately left out"). The byline remains author: operator. Implementations that produce only an end-of-run report are non-compliant. | Revisit when a runner-managed operator-narrative export (audit chain operator section, RFC 0033 substrate notes view, or a striatum operator note add verb) replaces the manual append loop. |
| D090 | accepted | Accept RFC 0034 V1 as the local workflow generator and template catalog slice. Ship generator core, bundled package-data catalog, CLI workflow templates list/show, CLI workflow generate, local service catalog and generation endpoints, custom-plan compilation, immediate validation of generated workflow.json, and workflow init --style as compatibility sugar over the generator. | Operators need a first-class path from intent, shape, lane topology, and artifact root to valid workflow JSON without duplicating JSON assembly in CLI, service, web, and future chat surfaces. The generator contract settled before UI chooser work, which later landed in RFC 0038, and chat-assisted scaffolding landed in RFC 0036. | No database migration. Generated workflows remain ordinary striatum.workflow.v1 JSON. Catalog data is local package data only; no hosted marketplace, telemetry, or remote fetch. Service writes require --allow-mutations and confirm_write: true; previews are read-only. TODO item 18 is complete; future target-repo catalog extensions or overwrite/force semantics require a separate decision. | Revisit when target-repo catalog extensions, overwrite/force semantics, or a richer AI workflow-planning surface is scheduled. |
| D089 | accepted | Every dogfood run produces docs/dogfood/<NNN>/OPERATOR_REPORT.md alongside RUN_SUMMARY.md, BUILD_HANDOFF.md, and EVIDENCE.md. The operator writes it before the dogfood commit lands. | Dogfoods 030–034 had operator decisions and interventions scattered across commit messages, audit rows, and agent log excerpts. The runner exports cover mechanical state, implementer view, and artifact provenance, but not the operator's own narrative. | Report covers operator decisions (cycle continues, verdict overrides, scope cuts), interventions (publish-on-behalf, manual model invocations, byline fixes), deferred risks, and a "Deliberately Left Out" section. Byline is author: operator per RFC 0026. Agents do not author the report. Dogfood scaffolds list it as a required deliverable; commit-readiness refuses runs without one. Dogfoods 028 and 029 may retro-fit. | Revisit when the daemon audit chain (RFC 0033 + RFC 0030) or run-summary export grows operator-section support that replaces the file. |
| D088 | accepted | Accept RFC 0031 as the V2 foundation for daemon-owned supervision and sealed-apply authority. Daemon-global metadata for supervisors and apply receipts lives in the daemon DB, repo-local .striatum/retired-local-state keeps pointer/workflow truth during the transition, and sealed apply remains an AI guardrail rather than third-party non-repudiation. | RFC 0026 lane attestation and RFC 0027 sealed-patch guardrails needed a daemon-owned authority boundary before stronger provenance claims could be honest. | Adds daemon DB migration v2 tables for rpc_methods, daemon_supervisors, and apply_receipts; repo-local migration v13 adds process_supervisor_pointers; RPC method registry reserves supervise.* and apply.*; apply helpers fail closed without signing-key/apply authority. D094/RFC 0043 later supersede the repo-local pointer-truth placement for production: current workflow and supervisor pointer state is daemon/Postgres-owned under a registered repository scope. | Revisit when the production apply mutation path is enabled end-to-end, when reattach/recovery needs cross-platform process identity beyond /proc/start time, or when sealed mode needs stronger containment than local daemon authority. |
| D087 | accepted | Accept RFC 0030 as the daemon V2 RPC foundation: envelope-v1 JSON over owner-local transports, daemon.hello / daemon.welcome, daemon.describe, a capability-bound method registry, and PostgreSQL request/audit helper wiring. Direct repo-local CLI mode remains a compatibility path during transition only. | D082 made daemon-with-CLI-client the long-term product shape; RFC 0033 supplied the storage substrate. The next safe step is a language-neutral RPC contract before moving more mutations behind the daemon. | Adds src/striatum/daemon_rpc/, closed capabilities {read, write, review, claim, apply, admin}, exit-code-10 version/refusal semantics, method etags, and request/audit hashing helpers. CLI routing can move method by method without changing the envelope. D094/D104 later close the production direct-mode transition; the paired STRIATUM_DAEMON_REQUIRED=0 STRIATUM_TEST_HARNESS=1 escape is test-only. | Revisit as the D107 / RFC 0068 Go daemon/client port advances against the registry fixtures, or when transport needs expand beyond Unix socket and opt-in loopback HTTP. |
| D086 | accepted | The daemon V2 stack will not stay on SQLite. Plan a non-SQLite storage substrate (event-sourced log, libSQL/Turso, embedded Postgres, RocksDB/BoltDB, or similar) and pick the specific substrate in RFC 0033. RFC 0033 lands first because RFC 0030's wire protocol, schema migrations, and audit-chain format key off the substrate choice. Resolves RFC 0028 OQ#3 given D082 (daemon-first product) and D084 (Go core planned). | A daemon-first product with a Go core has heavier coordination needs than V1's repo-local SQLite: long-running concurrency, streaming projections, multi-tenant queries, supervisor liveness checkpoints, and audit-chain append performance all benefit from a substrate designed for daemon workloads. Keeping SQLite would constrain those choices and force per-bottleneck workarounds. | RFC 0033 evaluated options and selected PostgreSQL for daemon-owned state. D094/RFC 0043 later extended that substrate to per-repository workflow state, retiring repo-local SQLite as production authority; .striatum/retired-local-state survives only as migration source, tombstone, or fixture material. | Revisit if PostgreSQL setup remains untenable after daemon doctor, if a credible embedded substrate is selected, or if multi-tenancy and hosted mode are abandoned. |
| D085 | accepted | Daemon logs default to the metadata-only hash-chained audit. Operational logs are opt-in via --verbose-log, which writes to a session-scoped file with an automatic deletion timer and is explicitly documented as not-for-production. Resolves RFC 0028 OQ#9. | Default-quiet keeps daemon evidence narrow and avoids drift toward transcript-like sensitive material. An opt-in escape hatch lets operators debug production incidents without making verbose capture the standing default. | Audit table remains metadata-only (no request/response bodies, no agent stdout/stderr, no tracebacks, no token secrets). --verbose-log flag on striatum daemon start produces a rotated session log under the runtime dir with a documented retention window; default is off. Boot/crash/restart events go to journald/file via stdout/stderr only, never carry agent output. | Revisit if forensic incident response repeatedly fails for lack of structured operational logs, or if the opt-in verbose mode starts being left on as a habit. |
| D084 | superseded | Plan a Go-language core for the daemon. D105 temporarily narrowed this to support/runtime work, but D107 restores the Go production daemon port as the target architecture. | Daemon-first product positioning made long-running process supervision, signal handling, packaging, and single-binary distribution first-class concerns. Go looked like the likely fit before the Python Postgres daemon completed the production domain path. | RFC 0030's wire framing, request id semantics, version handshake, capability binding, and audit contracts remain language-neutral. RFC 0068 now owns the concrete Go production port and Python-daemon retirement sequence. | Revisit through D107/RFC 0068. |
| D083 | accepted | Local multi-user mode is out of scope for daemon V2 (RFC 0030/0031/0032). Daemon V2 assumes one OS user per machine; sockets are owner-only and capability tokens scope to the daemon-installed user. Multi-user (multiple OS users on one machine, per-user capability scopes, repo-level ACLs, per-client session expiry) is deferred to a dedicated future RFC. Resolves RFC 0028 OQ#6. | A single-user trust boundary keeps the RPC server and supervision migration small enough to review. Multi-user adds OS-UID mapping, lockout/rotation, and access-control modelling that would distract from the daemon-first transition. Deferring keeps the door open without expanding scope. | RFC 0030 may assume the daemon process owns its socket and registry with owner-only permissions. Capability tokens scope to repository, not to OS user, in V2. Documentation must say multi-user is not supported and refuse misleading shared-workstation framing. | Revisit when a shared-workstation or CI-runner scenario produces a concrete user story that single-user cannot serve. |
| D082 | accepted | The long-term product shape is "daemon with CLI client": daemon mode becomes the primary product surface for sealed provenance, cross-repository coordination, supervised agent ownership, and MCP mutation. Direct repo-local CLI mode becomes a compatibility shim that may be retired by a future RFC once daemon-mediated paths are stable. Resolves RFC 0028 OQ#1. | RFC 0028 V1 deliberately scoped the daemon as opt-in registry + sweep, but the deferred items (RPC server, sealed apply, mutation MCP, cross-repo workflows) only fit cleanly behind a real daemon control plane. Naming daemon-first lets the next RFCs assume daemon mediation instead of relitigating it each time. | RFC 0030 (RPC server) became the spine of daemon V2; D094/RFC 0043 and D104 later completed the retirement of production direct repo-local mode. CLI verbs are now daemon clients; direct-mode escapes are test-only. | Revisit if daemon adoption proves untenable (packaging, distribution, supervision instability) or if a credible alternative coordination plane emerges. |
| D081 | superseded | Accept RFC 0028 V1 as optional registry-backed multi-repository coordination plus a local foreground sweep process for read visibility, daemon registry state, metadata-only audit, resources-only daemon MCP, and recovery sweeping. Ordinary workflow mutations, a daemon RPC server, daemon-owned supervision, MCP mutation tools, sealed apply, signing keys, cross-repository workflows, hosted semantics, Windows daemon support, and operator tenancy remain deferred. | The accepted dogfood-031 synthesis narrowed the daemon to the acceptance-criteria slice that improves operator visibility and recovery sweeping without moving workflow truth out of repo-local SQLite or overclaiming provenance; revision round 3 makes explicit that V1 is a shared owner-only registry SQLite plus sweep loop, not daemon-mediated RPC. | Adds striatumd / striatum daemon start as a foreground sweep process, daemon registry storage, repo add/list/remove with explicit --init, explicit registry-backed --daemon read mode for status/doctor/why/dashboard-all only, dashboard --all, token/capability checks (read, admin), metadata-only hash-chained audit with segment-manifest doctor checks, resources-only daemon MCP with explicit token parameters and repo-scope filtering, and daemon recovery-sweep events bylined striatumd-<instance-id>. Superseded by D087/D094/D104 for current production behavior: daemon RPC, daemon-owned PostgreSQL, and mutation-capable MCP are mandatory surfaces. | Historical V1 reference only; revisit through the current daemon/MCP decisions, not by restoring optional registry mode. |
| D080 | accepted | Accept RFC 0026 V1 plus RFC 0027 Phase 2 guardrails: lane-liveness attestation controls byline derivation and optional review gates; sealed_patch is schema-visible but refuses to start until hard containment exists. | Dogfood-030 exposed false provenance risk from operator-asserted lane bylines and overclaim risk around sealed provenance. The accepted implementation closes the low-friction false-byline path while preserving local-first operator authority, and it names sealed mode without silently downgrading to advisory behavior. | Migration v12 adds sessions.operator_label and process_supervisors.pid_start_time. Unattested sessions publish as author: operator; attached supervisors restore lane/model bylines only when pid identity and snapshot command binding match. Review jobs may set require_attested_lane: true; non-review use is rejected in V1. provenance_mode accepts advisory, attested_bylines, and sealed_patch, but run start refuses sealed runs with an unsupported-containment error. | Revisit when RFC 0027 patch artifacts/hash-bound verdicts/apply receipts are implemented, when a first containment mechanism is selected, or if attested_bylines needs a less author-like mode name in a workflow-schema revision. |
| D001 | accepted | Start striatum with a PRD backed by a decision log. Use RFCs for contested architectural branches, then write the implementation spec after product boundaries stabilize. | The largest current uncertainty is product shape, not implementation mechanics. A specification too early would harden the wrong abstraction; RFCs too early would scatter the design before the core job is agreed. | docs/PRD.md becomes the active design artifact. docs/DECISION_LOG.md records decisions. docs/SPEC.md stays pending until the PRD and early contested decisions settle. | Revisit if the PRD becomes too broad to drive implementation or if a single architecture question blocks product definition. |
| D002 | accepted | Build striatum as a generic local terminal-agent orchestrator from the start, with Engram workflows as reference fixtures. | The reusable product is the coordinator, message bus, PTY adapter, review gates, and artifact publishing model. Engram marker names, prompt ordinals, and review paths should not be baked into core logic. | Core behavior is workflow-config driven. Engram Phase 3 and RFC-ledger workflows can validate the product, but project-specific paths stay in examples/config. | Revisit if generic workflow configuration delays a useful MVP more than expected. |
| D003 | accepted | Model portability is a core design goal. striatum must not privilege one provider, one model family, or one CLI beyond adapter defaults. | The tool's value comes from orchestrating the latest useful models while preserving exact model control. Frontier quality changes quickly, and workflows should survive model/provider swaps. | Agent lanes are configuration, not product identity. Coordinator role assignment is selectable per workflow/phase. Prompts, state, artifacts, and review gates must avoid provider-specific assumptions unless declared by an adapter. | Revisit only if a provider-specific feature is required for a critical MVP behavior and cannot be abstracted cleanly. |
| D004 | accepted | Use a hybrid coordinator: deterministic control plane plus exactly one selected portable AI coordinator lane. The AI coordinator is instantiated with a project-manager prompt and stays focused on moving the workflow toward the stated outcome; synthesis is assigned as an explicit job, not a default coordinator responsibility. | Deterministic orchestration is required for state, gates, process launch, retries, stop conditions, message routing, and write-scope safety. An AI coordinator is useful for conversational control and judgment, but combining process management with artifact synthesis risks attention dilution. | Coordinator prompts emphasize outcome tracking, blockers, routing, human checkpoints, and next actions. Synthesis jobs remain separate workflow nodes with their own prompts, artifacts, and review gates. | Revisit if workflows become too small to justify separate synthesis jobs or if coordinator handoffs create more overhead than they remove. |
| D005 | accepted | The AI coordinator owns conversational control, outcome tracking, next-action selection, blocker triage, human checkpoint escalation, state summarization, and work assignment through deterministic workflow commands. It does not synthesize major artifacts, write source patches, bypass gates, edit workflow state directly, or replace reviewer judgment unless assigned an explicit workflow job. | The coordinator must stay attention-focused on moving the workflow to the stated outcome. Mixing coordination with artifact authorship or ungated edits invites attention dilution and unsafe shortcuts. | Coordinator prompts and capabilities are constrained. Synthesis, review, and implementation remain explicit jobs with their own prompts, lanes, artifacts, and gates. | Revisit if practical workflows require the coordinator to perform small inline edits or syntheses often enough to justify a separate "coordinator micro-task" policy. |
| D006 | superseded | Use SQLite as the v1 live coordination layer and state store, with the striatum CLI as the first interface. Repository files are durable artifacts, not the live message bus. | SQLite gave V1 local durability, queryability, acknowledgement/retry semantics, process state, verdict history, and inspectability without a hosted service. D094/D104 supersede the production runtime substrate with daemon-owned PostgreSQL. Repo files remain excellent for committed findings and handoffs but are not the live message bus. | Historical V1 reference only. Current live coordination is daemon-owned PostgreSQL under registered repository scopes; .striatum/retired-local-state remains migration input/tombstone or test fixture material only. | Revisit through D094/D104, not by restoring production SQLite authority. |
| D007 | superseded | Store v1 run state inside the target repo under .striatum/, ignored by default. | Repo-local state kept V1 runs close to the worktree, easy to inspect, easy to delete, and naturally scoped to the project. D094/D104 supersede that placement for production runtime. | Historical V1 reference only. Current .striatum/ is operational scratch; authoritative run state lives in daemon-owned PostgreSQL. | Revisit through D094/D104 if Postgres setup remains untenable. |
| D008 | superseded | Model the SQLite coordination layer as both an append-only event log and a lightweight local message queue. | SQLite queue semantics served V1 without Redis, RabbitMQ, NATS, or hosted infrastructure. D094/D104 supersede the production event log and message queue with daemon-owned PostgreSQL rows. | Historical V1 reference only. Current events, messages, blockers, leases, and audit surfaces are daemon/Postgres-backed under the daemon method boundary. | Revisit through D094/D104 if a new local substrate is selected. |
| D009 | superseded | Agents update orchestration state through the striatum binary/CLI. The binary owns SQLite writes, schema invariants, leases, acknowledgements, state transitions, verdict rules, and artifact validation. MCP is optional adapter surface over the same semantics, not the core contract. | The CLI-control rule remains useful, but D094/D104 supersede the SQLite/direct-mode and optional-MCP portions. Current CLI/MCP/web surfaces are daemon clients over the same audited invariants. | Agents still use striatum verbs or approved daemon MCP/chat tools; production mutations route through daemon RPC and daemon-owned PostgreSQL. Direct repo-local mode is test-only. | Revisit only if the daemon tool interface changes, not by restoring direct SQLite mutation. |
| D010 | accepted | Agents must identify themselves through a registered agent session before claiming work. claim-next returns a work packet based on the session's role, lane, capabilities, run, and current workflow state. | Work assignment depends on who is asking. Anonymous claims cannot safely match tasks to roles, avoid duplicate work, preserve session context, or audit responsibility. | V1 adds agent/session registration and identity-aware queue claims. Commands that mutate job state require a session id or equivalent identity token. | Revisit if one-shot headless jobs need a simpler anonymous path; even then, the runner should create an ephemeral session record. |
| D011 | accepted | Prefer persistent agent sessions until the assigned role expires; allow workflows to request fresh sessions when context reset or independent review matters. | Preserving session context can improve phase-level situational awareness and reduce repeated setup. Some jobs still need fresh context to avoid contamination. | Sessions can receive multiple work packets while their role is active. Workflow config can mark jobs as fresh_session_required. Role expiration ends or rotates the session. | Revisit if persistent sessions prove too hard to control across CLIs or if context accumulation harms review quality. |
| D012 | accepted | Use an opaque stable session_id for database identity plus a human-readable session slug for tmux windows, dashboards, logs, and prompts. | Human-readable names improve operator understanding, but behavior should not depend on parsing a slug. | Store role, lane, ordinal, capabilities, and slug as structured fields. Use session_id for mutations and audit. D041 distinguishes runtime session slugs from artifact author bylines. | Revisit if slugs become too long or if users need custom display names. |
| D013 | superseded | Historical V1 decision: use the striatum CLI as the primary agent control surface, with MCP added later as an adapter over the same semantics. | D104 made daemon MCP mandatory for operator-driven runs, and RFC 0130's active HTTP/SSE MCP roadmap replaces CLI-primary control with daemon MCP and operator UI surfaces. The original CLI-first rule was useful for V1 prompting/debugging, but it is no longer the target control plane. | Historical context only. Current workflow-control mutations route through the daemon boundary, and current roadmap work removes the need for human or AI operators to invoke CLI verbs for live workflow control. Remaining CLI commands are bootstrap, diagnostics, or temporary compatibility until MCP/UI parity exists. | Revisit through RFC 0130 and later CLI-retirement decisions, not by restoring CLI-primary workflow control. |
| D014 | accepted | Use a mostly DAG-shaped workflow graph with explicit bounded cycles for retry, revision, re-review, and human checkpoints. | Workflows need safe loops, but unbounded autonomous cycles increase risk and make state hard to reason about. | Workflow config supports declared cycles with limits and stop conditions. | Revisit if a future autonomous repair loop needs richer cyclic semantics. |
| D015 | accepted | V1 parallelism must be declared in workflow config and limited to disjoint write scopes or review-only jobs with unique artifacts. AI-inferred build parallelization is deferred. | Build parallelization requires dependency analysis, file ownership, test impact, conflict risk, and human judgment. | Coordinator may schedule declared parallel work but does not infer build splits in v1. | Revisit for v2 with write-scope analysis and human-approved parallelization proposals. |
| D016 | accepted | Make build processes idempotent by treating prompts, findings, syntheses, and related handoff artifacts as durable repo-published outputs. | Agents need stable artifacts that other agents can read, and repeated runs should converge rather than depend on hidden session memory. | Workflow jobs declare durable artifacts; the publisher records provenance in daemon-owned PostgreSQL under the registered repository scope and writes durable artifacts into configured repo paths. | Revisit if artifact churn becomes too high or if some transient findings should stay local. |
| D017 | accepted | Coordinator starts or selects a branch for workflow work and requests commits from the human rather than committing autonomously by default. | Branch isolation protects the main worktree and keeps final commit authority with the human. | V1 includes branch setup/check commands and commit-request messaging, but not automatic commits as the default. | Revisit if users explicitly opt into trusted auto-commit mode. |
| D018 | superseded | Implement v1 in Python. | Python had batteries-included local storage, subprocess handling, straightforward CLI tooling, and was easy to iterate while the product shape was still settling. D107/D111 supersede this for the daemon core. | Historical V1 implementation note only. Python may remain the CLI/web client layer, but the production daemon core is Go and the live substrate is daemon-owned PostgreSQL. | Revisit only if the Python CLI/web layer itself becomes a blocker or if a future client rewrite is accepted. |
| D019 | accepted | Role definitions are reusable artifacts. Generic context docs provide project/process orientation. Task prompts may reference roles and add job-specific emphasis. | Roles should compose across workflows and avoid being buried in one-off prompts. Context docs should stay generic so they can orient many roles without role-specific contamination. | Workflow jobs reference role definitions, context docs, and task prompts separately when assembling work packets. | Revisit if role/context separation becomes too verbose for simple workflows. |
| D020 | accepted | Coordinator chat commands like "read prompt foo" are coordinator skills backed by deterministic CLI operations. The skill resolves the prompt artifact, loads allowed context, assembles or enqueues a work packet, checks write scope and confirmation requirements, and dispatches only through the control plane. | The prompt should contain specific task directions, but execution should remain gated and auditable. Coordinator skills avoid free-form coordinator action while preserving conversational ergonomics. | V1 defines coordinator skills for prompt resolution and dispatch. Risky actions require confirmation according to workflow policy. | Revisit if natural-language command parsing becomes too brittle or if users prefer explicit CLI-only dispatch. |
| D021 | accepted | Native sub-agents spawned by a supported agent CLI are internal to the parent agent session in v1. The parent session remains accountable for write scope, artifacts, completion, messages, and audit responsibility. | Agent CLIs may spawn sub-agents, but letting hidden sub-agent trees independently own queue work would create double orchestration and unclear accountability. | striatum tracks the parent session unless a sub-agent is explicitly registered as a first-class session. Parent job contracts apply to all native sub-agent work. | Revisit if CLI sub-agent APIs expose enough structured identity and lifecycle data to register them automatically. |
| D022 | accepted | The minimum common integration contract across supported agent CLIs is process-based: launch command, cwd, environment, stdin/context, stdout, stderr, exit code, and optional PTY. | This is the only universally reliable substrate across terminal agents. Higher-level capabilities vary by provider and adapter. | Core adapters are built around process/PTY execution. Model-specific adapters may add richer features without changing the core contract. | Revisit if all supported agents converge on a better common protocol. |
| D023 | accepted | Start with a provisional mutation command set: register-session, claim-next, ack, send, block, complete, verdict, publish-artifact, heartbeat, and release. | The exact command design is still foggy, but the MVP needs a concrete control surface for sessions, queue claims, messages, blockers, completions, verdicts, artifacts, liveness, and lease release. | The design team may refine names and arguments, but these capabilities are in MVP scope. | Revisit during CLI/API design review. |
| D024 | accepted | A design team should define the work packet schema and the SQLite schema before implementation. | Work packets and schema shape determine queue semantics, prompt assembly, persistent sessions, claims, artifacts, and status views. They deserve a focused design/review pass rather than ad hoc implementation. | The one-shot MVP prompt must produce reviewed design artifacts before code changes. | Revisit only if the MVP is intentionally reduced to a spike. |
| D025 | accepted | V1 interaction surfaces are CLI plus tmux introspection. TUI follows. Slack and web dashboard/chat are later surfaces. | CLI and tmux match the immediate terminal-agent workflow and keep scope small. TUI/web/Slack depend on the same state and message bus but should not block MVP. | MVP exposes CLI commands and tmux-friendly status. TUI/Slack/web remain roadmap items. | Revisit if Slack becomes required for actual daily use before MVP validation. |
| D026 | accepted | Creating or selecting a workflow branch is confirmation-gated at run start. | Branching is safe and useful, but the human should know when the runner is changing git state. | Coordinator proposes branch creation/selection and asks for confirmation before running branch-affecting commands. | Revisit for explicit auto-branch mode. |
| D027 | accepted | Workflow configuration uses JSON. YAML is rejected. | JSON is boring, machine-friendly, and avoids YAML's ambiguity and surprising parsing rules. | Workflow examples, validation, and parser target JSON. | Revisit only if a strongly justified alternate format emerges. |
| D028 | accepted | Artifact policy: decisions, prompts, findings, syntheses, markers, handoffs, and other idempotent build artifacts are repo-published; raw provider transcripts are not published as artifacts, stored in daemon/PostgreSQL state, exported as corpus/archive evidence, or used as workflow authority by default. Operator-local PTY logs may exist under .striatum/scratch/ as private diagnostics only. | Durable artifacts make runs inspectable and repeatable. Broad durable transcripts create noise and privacy exposure, but interactive PTY lanes need local inspectability when submit drivers, MCP startup, or provider TUIs fail. Decisions in particular must be recorded as curated artifacts, not inferred from terminal text. | Artifact publisher supports curated outputs and decision records. Local PTY logs are operational scratch: 0600 where Striatum creates them, ignored by durable evidence paths, never parsed for state, never used for bylines/verdicts, and removable without changing workflow truth. | Revisit if transcript logs need retention policy, redaction, export, UI viewing, or any durable evidence role. |
| D029 | accepted | Fresh context means new role instantiation. Multi-pass reviews until consensus and builds until complete remain persistent by default. | Freshness should be explicit and tied to role/session lifecycle. Reviews and builds benefit from persistence when the role remains valid; independent review requires a new role instantiation. | Workflow config can request fresh role instantiation for specific jobs. Persistent role sessions continue across multi-pass consensus/build loops. | Revisit if role persistence causes review contamination. |
| D030 | accepted | The first validation workflow is RFC-ledger cleanup, not a full Engram Phase 3-style build. | RFC-ledger cleanup exercises design/review/synthesis/artifact flow without the risk and complexity of a major implementation phase. | The one-shot MVP prompt uses RFC-ledger cleanup as the first fixture after core design/build. | Revisit after MVP can run a small fixture end to end. |
| D031 | accepted | Project layout, development discipline, and testing should emulate Engram's Python project shape where appropriate. | Engram already has familiar conventions for docs, decisions, prompts, source layout, tests, and Makefile-driven development. | Use a Python package under src/striatum, tests under tests, docs under docs, prompts under prompts, and a Makefile for common commands. | Revisit if packaging requirements point elsewhere. |
| D032 | accepted | The one-shot MVP process must include design input from all three frontier model lanes before synthesis or implementation. | The project confidence model depends on productive disagreement across the latest available Claude, Codex, and Gemini lanes. A single model wearing multiple hats is not enough for the initial design. | P001 requires separate design notes from Claude, Codex, and Gemini, or a clear stop if a lane is unavailable. Synthesis must cite all three inputs before build begins. | Revisit after the design process proves whether three-model input adds enough value for smaller changes. |
| D033 | accepted | Incubate striatum inside the Engram repository through MVP design/build, then split it into a standalone project after validation. | Engram contains the real workflow pain and context that motivated the tool. Keeping the project here through MVP gives design agents concrete context while preserving the long-term generic product boundary. | striatum/ is the canonical incubation directory. Core product logic remains generic and extractable. Engram-specific paths stay in examples/fixtures/context. | Revisit after MVP validation, when extraction should become the default next step. |
| D034 | accepted | Port the Engram Phase 3 tmux runner into scripts/ only as a temporary bootstrap harness for collecting the three required V1 MVP design inputs and handing off to synthesis. | The runner is useful immediately for model-lane orchestration, but striatum still needs its own reviewed SQLite-backed control plane, CLI contract, and generic tmux/PTY adapter. | scripts/striatum_tmux_design.sh starts Claude, Codex, and Gemini design-input panes. The three design input artifacts are the watched completion markers. This script is not product architecture and should be retired or replaced after the MVP runner exists. | Revisit after the MVP can represent and run this workflow through striatum itself. |
| D035 | accepted | For the V1 MVP one-shot prompt, branch setup is pre-confirmed and fixed to branch striatum, and build review must be delegated to fresh independent reviewers rather than performed by the builder/coordinator. | The one-shot needs less ceremony around branch setup, while build confidence still depends on independent review. Letting the builder self-review would weaken the multi-model process that motivated the tool. | P001 tells the coordinator to create or switch to striatum without asking again, while design-lane panes are forbidden from switching branches. P001 requires separate Claude, Codex, and Gemini build review artifacts plus synthesis before declaring the build complete. | Revisit after the first one-shot run, especially if branch reuse or reviewer availability creates friction. |
| D036 | accepted | V1 uses lazy lease expiry rather than a background queue daemon. Expired review-only work may be requeued; expired repo-write work becomes stale or blocked until coordinator or human inspection. In the daemon-required PostgreSQL runtime, recovery.sweep and the Go resident scheduler apply the same safety policy. | The MVP must include leases and heartbeats, but a background supervisor would expand scope and automatic requeue of write-capable jobs is unsafe on one branch. | Historical SQLite-era commands reaped expired leases lazily; current daemon-backed recovery sweeps record the same decisions in daemon-owned PostgreSQL. Stale repo-write jobs still require explicit recovery. | Revisit when worktree-isolated repo-write recovery becomes safe to autonomously requeue. |
| D037 | accepted | Process and tmux adapters are launch boundaries only; terminal output is not parsed as workflow state. The original SQLite-authority consequence is historical and superseded by D094/D107. | Model portability depends on explicit CLI state transitions, not provider-specific terminal conventions. | Lane config contains command arrays and capabilities. Current production scheduling state is daemon-owned PostgreSQL under the daemon method boundary; historical SQLite wording survives only as pre-D094 context. Core scheduling matches roles/capabilities and ignores provider stdout for state transitions. | Revisit if all supported CLIs converge on a reliable structured event protocol. |
| D038 | accepted | striatum RFCs live under docs/rfcs/; Engram RFCs remain reference fixtures, not the runner's product decision record. | The runner is incubated inside Engram but has a distinct product boundary. Mixing runner design RFCs into Engram's RFC directory blurs project ownership and makes extraction harder later. | New contested runner changes get RFCs in the runner docs tree. Accepted runner RFCs update this decision log and docs/SPEC.md when they change behavior. | Revisit only if striatum is split out and the path changes with the standalone repository layout. |
| D039 | accepted | Human decision: evidence export identifies jobs by stable author identity, not workflow job title prose. The identity is role_id, lane_id, declared model display name, and workflow_job_id. | The owner chose this unilaterally during the RFC 0014 dogfood rerun blocker triage to preserve auditability without exporting arbitrary workflow title text. | Default evidence export omits job titles and includes stable author identity for jobs and artifacts. | Revisit if model display names need stricter validation or redaction classes. |
| D040 | accepted | Human decision: workflow-authored artifacts should carry a visible author: <role-name>-<model-name>-<ordinal> line in the artifact title block. | The owner chose a compact human-facing byline so durable files can be inspected without relying only on daemon/PostgreSQL evidence, while avoiding arbitrary workflow title prose that may leak project or corpus details into artifacts. | Work packets expose the exact lowercase author: line, and workflow prompts should instruct agents to include it. The publisher records artifacts and does not mutate files to add headers. | Revisit if non-Markdown artifacts need metadata front matter, publisher-side validation, or model display names need stricter normalization. |
| D041 | accepted | Runtime session identity, evidence-export author identity, and durable artifact author bylines are distinct. | Daemon-owned scheduling state needs stable structured identity; evidence export needs audit-stable metadata; readers of committed artifacts need the role and actual model identity in a short byline. | Runtime session slugs are display labels and must not be parsed as database identity. Evidence export uses D039's stable identity tuple. Durable Markdown artifacts use D040's author: <role-name>-<model-name>-<ordinal> title-block convention. | Revisit when artifact front matter becomes structured schema or when author bylines need collision-proof validation. |
| D042 | accepted | Rename the standalone product to Striatum and split it from Engram with history preserved. | The owner selected the final product name during extraction. The generic orchestrator needs its own product boundary, package metadata, CI, and repository while retaining Engram dogfood history as validation evidence. | The distribution, module, CLI, and workflow schema are striatum; .striatum/ is operational scratch next to target repositories, while authoritative workflow state lives in daemon-owned PostgreSQL. Engram remains an external reference fixture and historical incubation source. | Revisit only if the package name conflicts with publication constraints or the owner chooses a different license/repository policy. |
| D043 | accepted | License Striatum under Apache-2.0. | The owner accepted Apache-2.0 as the open-source license after comparing permissive options. It preserves low adoption friction while providing explicit patent terms that are useful for infrastructure tooling. | LICENSE uses the Apache License 2.0 text, package metadata uses SPDX Apache-2.0, and contribution guidance states that contributions are Apache-2.0 unless noted otherwise. | Revisit only if the owner chooses a different publication or commercialization policy. |
| D044 | accepted | Add workflow graph export as a workflow-authoring aid. | Workflow JSON can become difficult to audit by inspection alone, and graph export helps operators review dependencies, declared parallel groups, review gates, and bounded revision cycles before preparing a run. | striatum workflow graph <workflow.json> validates the workflow and exports Mermaid by default or JSON graph data on request. This is a read-only workflow-file command and does not create or mutate runner state. | Revisit if run-state visualization, Graphviz output, or richer lint integration becomes a priority. |
| D045 | accepted | Owner choices are first-class decision artifacts. decision record writes durable Markdown with machine-checkable striatum.decision.v1 front matter and records it as run-level artifact kind decision without requiring job ownership or an active lease. | Human checkpoint and owner-choice outcomes need durable provenance, but they are not agent work products and should not require a live session lease. | Decision artifacts support accepted, rejected, and accepted_with_follow_up outcomes. They are local repository files plus append-only artifact/event records; D094 moved current production records from SQLite to daemon-owned PostgreSQL. | Revisit when Striatum adds fuller artifact front-matter schemas or an explicit human-checkpoint resume command. |
| D046 | accepted | Adapter constraint enforcement is explicit in workflow validation and work packets. Lanes may declare required_enforcement for requested adapter constraints, and validation rejects lanes whose adapter cannot provide the required level. | Workflow authors need to distinguish real guarantees from advisory policy text before dispatching terminal agents. Failing fast avoids false confidence about network, transcript, repository-scope, or sandbox behavior. | Work packets expose requested constraints, required levels, actual adapter enforcement, and satisfaction status. The process adapter enforces transcript-off by default and reports other known constraints as advisory until richer launchers or sandboxes exist. | Revisit when adding worktree isolation, sandbox-backed process launching, or provider adapters with stronger native controls. |
| D047 | accepted | Track Striatum's pre-D094 local SQLite schema version with PRAGMA user_version and apply pending migrations from a registered migration list for historical repo-local databases. Refuse to import when the database is newer than the runner supports. | Hand-editing SCHEMA_SQL and asking users to delete .striatum/retired-local-state to upgrade contradicted the V1 local-first durable-state promise. A small forward-only migration registry keyed off PRAGMA user_version preserved run history before D094 moved production state to PostgreSQL. | Superseded for production by D094/RFC 0043 and narrowed again by D113. Current production workflow state is daemon-owned PostgreSQL; local SQLite schema code remains only as guarded legacy migration-fixture material until those tests are converted or deleted. | Revisit only when deleting the guarded legacy migration fixtures and remaining local SQLite schema code. |
| D048 | accepted | Per-job git worktree isolation is opt-in per lane via worktree_isolation: per_job (RFC 0008). Work packets advertise worktree_required: true and a worktree create command for repo-write jobs in isolated lanes; the agent must call striatum worktree create itself, the runner does not auto-create. publish-artifact reads from the active worktree but records the logical repo-relative path. Lease expiry marks the row abandoned and leaves the directory on disk. | Disjoint write scopes on a single worktree cannot stop parallel agents from clobbering each other's node_modules, build artifacts, or partial files, but auto-creating worktrees for every claim would surprise existing workflows and complicate recovery. Opt-in lane configuration plus an explicit agent command keeps the contract auditable while solving the parallel-write footgun. | Historical SQLite migration version 2 added the job_worktrees table; current production job_worktrees state is daemon-owned PostgreSQL. CLI subcommands `worktree create | release |
| D049 | accepted | Long-lived process supervision is a separate adapter mode from single-shot adapter run (RFC 0009). `striatum supervise start | send | stop | status |
| D050 | accepted | Accept RFC 0001 (Run Recovery and Dogfood Fixes) and RFC 0007 (Workflow Visualization). RFC 0001 closes out as already implemented on main: evidence export, the extended status --json (blockers, checkpoints, non-accepting verdicts, claimable work, downstream blocked, deterministic next actions), the extended why <id>, atomic submit-review, lane-level adapter constraint enforcement, branch confirm --json records-only reporting, and review_revision_policy.root_review_needs_revision. RFC 0007 is accepted with the new `striatum run graph --run-id | json]command, which adds stateful highlighting on top of the existing staticworkflow graph` export. | Both RFCs were behaviorally complete in code but still labeled proposed. Closing them out keeps the RFC index honest and lets future contributors find the accepted decisions. RFC 0007 needed one small addition (the stateful run graph) to match its proposal; rather than carry it as a stale RFC, we paired the closure with the missing implementation. | RFC 0001 and RFC 0007 are now accepted and the index in docs/rfcs/README.md reflects this. The new run graph command is documented in docs/SPEC.md and README.md, and exercised by tests/test_cli_mvp.py:test_run_graph_highlights_job_states_in_mermaid and :test_run_graph_json_includes_current_state. The Mermaid palette (MERMAID_STATE_FILLS) is treated as a stable contract that downstream Markdown/Mermaid renderers can rely on. |
| D051 | accepted | Accept RFC 0002 (Reviewer Independence Policy). Review jobs may declare two optional fields, reviewer_access_scope (document_only | artifact_augmented | repo_level) and reviewer_context_policy (fresh | cross_round). Validation rejects unknown values, rejects the fields on non-review jobs, and rejects the explicit reviewer_context_policy: "fresh" + fresh_session_required: false conflict. reviewer_context_policy: "fresh" without an explicit fresh_session_required is silently stored as fresh_session_required = 1. Work packets gain a review_policy block with access_scope, context_policy, and a deterministic instruction string only when the workflow declared one of the fields. | Existing freshness and access scope were partly implicit, so independent fresh document-only review and cross-round repo-level review looked the same to reviewers. Making the two axes explicit lets workflows declare reviewer independence as a contract that the runner can validate and that work packets can communicate to model lanes. | The examples/rfc-0014-operational-artifact-home/workflow.json fixture labels its three independent root reviews as document_only and fresh. docs/SPEC.md, docs/UBIQUITOUS_LANGUAGE.md, and the RFC itself document the two axes, the implication rule, and the work-packet exposure. Workflows that do not declare the fields are unaffected. | Revisit when adding cross-model-family policy as a first-class concept, when reviewers need a richer per-input access scope, or when the runner needs to enforce fresh by rejecting non-fresh session claims rather than relying on the work packet instruction. |
| D052 | accepted | Accept RFC 0003 (Support Ledgers and Evidence Audits). Drop the SQL CHECK (artifact_kind IN (...)) clause on the historical artifacts table via migration version 5 and move artifact-kind validation into Python under striatum.artifacts.ALLOWED_ARTIFACT_KINDS. Add support_ledger as an allowed kind, register striatum.support_ledger.v1 in the front-matter schema registry, and ship examples/support-ledger-flow/ as the reference fixture. The fixture's evidence-audit job uses job_type: review with the workflow-level convention name "evidence audit"; no new job_type is added. | RFCs 0003/0004/0005 each add a new artifact kind, and the original SQL CHECK froze the kind vocabulary into schema. Opening the column and centralizing validation in Python kept the kind list discoverable, allowed future RFCs to add kinds without another local-schema rebuild, and made the runner reject unknown kinds at both the workflow-validation boundary (WorkflowError, exit code 8) and the publish boundary (ArtifactError, exit code 6). | Historical migration version 5 rebuilt the SQLite artifacts table without the kind CHECK and reinstalled append-only update/delete triggers. Current artifact provenance and artifact-kind enforcement live in daemon-owned PostgreSQL plus shared Python validation. publish-artifact rejects unknown kinds with artifact kind <kind> is not in the allowed kinds list: .... validate_workflow rejects expected_artifacts with kinds outside the set. RFC 0003 transitions to accepted and docs/rfcs/README.md reflects it. Tests live in tests/test_artifact_schemas.py. | Revisit if striatum ever needs structured claim rows in daemon-owned PostgreSQL, if support ledgers should become mandatory for syntheses recommending RFC acceptance, or if a future reviewer policy needs evidence-audit gating beyond the existing review job verdicts. |
| D053 | accepted | Accept RFC 0004 (Critique-To-Action Loop). Add action_item_ledger as an allowed artifact kind under striatum.artifacts.ALLOWED_ARTIFACT_KINDS and register striatum.action_item_ledger.v1 in the front-matter schema registry. Required front-matter fields are schema_version, artifact_kind, source_review_artifact, and revision_round (non-negative integer); optional total_items (non-negative integer). | Implementation/repair workflows need stable action-item ids tied to source reviews, not just narrative handoffs. Anchoring the ledger to a source_review_artifact and a non-negative revision_round keeps re-review jobs honest and survives multi-cycle revisions. Ledger row content remains body Markdown; if ledgers later need structured claim rows, they belong in daemon-owned PostgreSQL runner state rather than a repo-local file store. | Workflow validation accepts expected_artifacts.kind: action_item_ledger and publish-artifact validates the v1 schema when front matter is present. RFC 0004 transitions to accepted and docs/rfcs/README.md reflects it. tests/test_artifact_schemas.py:test_action_item_ledger_front_matter_validates covers the round trip. | Revisit if action items need structured rows in daemon-owned PostgreSQL, if reviewers should be allowed to emit proposed action ids before the ledger job runs, or if unresolved high-severity items should automatically force needs_revision. |
| D054 | accepted | Accept RFC 0005 (Harness Meta-Optimization). Add harness_improvement_proposal as an allowed artifact kind under striatum.artifacts.ALLOWED_ARTIFACT_KINDS and register striatum.harness_improvement_proposal.v1 in the front-matter schema registry. Required front-matter fields are schema_version, artifact_kind, target (one of prompt, workflow, spec, defaults, documentation), and expected_benefit; optional risk and rollback. The runner never auto-applies harness changes. | Meta-analysis output should be machine-checkable provenance, not free-text notes scattered across reviews. Constraining target to a small enum keeps proposals routable while remaining advisory; reviewer and human disposition still gate any prompt, workflow, spec, defaults, or documentation change. | Workflow validation accepts expected_artifacts.kind: harness_improvement_proposal and publish-artifact validates the v1 schema when front matter is present. RFC 0005 transitions to accepted and docs/rfcs/README.md reflects it. tests/test_artifact_schemas.py:test_harness_improvement_proposal_front_matter_validates covers the round trip. | Revisit when meta-analysis is wired into a default maintenance workflow or when proposals start to need richer structured fields. |
| D123 | accepted | Add override-verdict as an explicit operator recovery command for completed or waiting_human review jobs with non-accepting latest verdicts. | Operators sometimes need to accept with findings after a reviewer recorded needs_revision; the previous choices were to run an unwanted revision loop or mutate SQLite directly. | The command requires a fresh active session on the same run, appends a newer accept/accept_with_findings verdict instead of editing history, resolves revision-routing human checkpoints when present, emits verdict.overridden, and re-evaluates downstream gates. | Revisit when verdict disposition needs a richer operator-decision artifact, when the web UI should expose this as a guarded button, or when policy should restrict who may override. |
| D122 | accepted | Accept RFC 0029 V1 core: striatum recovery resume --blocker-id <id> resolves remediated process-adapter blockers and can optionally complete the job with --complete --session-id <id>. | RFC 0014 made missing process outputs deterministic but stranded remediated repo-write jobs in blocked; operators could publish the artifact or intend accept_with_findings, but the normal verdict and complete paths refused blocked jobs. | recovery resume revalidates required artifacts, extends the preserved process-adapter lease, resolves the blocker, returns the job to running, and emits recovery.process_blocker_resolved. Exit/timeout blockers require --force; review blockers with artifacts present can resume and then record the normal verdict. The process-adapter envelope now advertises recovery resume instead of the repo-write-inapplicable stale requeue path. See RFC 0029. | Revisit when dashboard/web UI blocker actions should expose resume directly, when expired process-adapter leases need explicit resurrection semantics, or when recovery automation should include a dry-run/audit-only mode. |
| D121 | accepted | Accept RFC 0025 V1 Steps 2+3: codex and gemini plugin profiles. Codex ships a 14-file bundle (.codex-plugin/plugin.json + 5 byte-shared skills + 5 byte-shared commands + hooks/mcp/README/manifest); Gemini ships a 14-file extension (gemini-extension.json + GEMINI.md + 5 byte-shared skills + 5 fresh TOML commands + sub-agent + README/manifest). --profile all aggregates all three. | RFC 0025 V1 Step 1 shipped claude_code; Steps 2+3 complete the three-profile coverage and promote gemini from RFC 0015's generic fallback. | See RFC 0025 and dogfood-029. v1.20.0; F1-F3 from design review adopted; F1 byte-match test now covers all three profiles. | Revisit for V2: cross-target install; hosted marketplace; codex apps//assets/; per-target git-repo extension format for gemini. |
| D121 | accepted | Accept RFC 0118: gate run completion on attested provenance (umbrella for #240 / the run_45aa8852 PROCESS_UNRELIABLE incident). Migrations 0024-0026: frozen per-verdict provenance stamp (lane_attestation_at_record + review_provenance_override/decision_id + supervisor_id_at_record, threaded from the admission gate, TOCTOU-free); posture=override + override basis forced on every operator/recovery verdict surface incl. the recovery auto-finalize bypass; maybeCompleteRun re-verifies every provenance-required review gate against the frozen stamp before clean completion (NULL stamp fail-closed), failing gates route to needs_operator/provenance_gate_failed with a resolvable escalation whose resolve re-drives completion; runs.completion_mode {lanes_attested, operator_override} (advisory); write-once runs.completion_record_json snapshotted before teardown at every terminal transition with its sha256 anchored in the terminal event; recovery.invalidate_job supersedes (never deletes) a compromised verdict as a durable receipt and reopens the job on a live run. | A run could reach completed although no independent reviewer process ever ran: per-job admission gates were each satisfied or bypassed, verdict attestation was a live probe never frozen, and nothing re-asserted the invariant at the run-completion boundary. | See RFC 0118 and GH #240. Landed on main as P0-1 69971701, P0-2 7e6e38e7, P0-3 8405d958, P0-4 b4a5e866, P1-5 2b8b70ef, P1-6 a73fddfe; operator decisions 2026-06-10: NULL-stamp fail-closed, completion_mode advisory, completion record as a write-once runs column. | Revisit for P2-7 (descendant-process reaping; needs a process-group/cgroup/start-token safety boundary) and P2-8 (detached-lane auto-disposition), and if operator_override outputs are observed propagating provenance degradation (would harden completion_mode into a downstream gate). |
| D120 | accepted | Accept RFC 0025 V1 Step 1: agent-CLI plugin bundles for claude_code. New striatum plugin install/uninstall CLI; init --with-plugins; doctor checks plugin_missing/plugin_outdated; marketplace fixture writer; 14-file Claude Code plugin bundle layout per RFC § 2.1; manifest at <bundle>/.manifest.json self-describes the bundle. | RFC 0015 ships loose skill files; the three target agent CLIs all moved to plugin packaging since 0015 V1 landed. Step 1 ships claude_code first-class; Steps 2 (codex) and 3 (gemini promote) follow. | See RFC 0025 and dogfood-028. v1.19.0; mirrors RFC 0015 install pipeline; helper expansion reused via skills.install._expand_helpers; design-review F1 (skill template byte-match) / F2 (doctor walks bundles) / F3 (URL-leak whitelist) / F4 (idempotency excludes manifest timestamp) all addressed. | Revisit for Step 2 (codex), Step 3 (gemini promotion), --profile all, cross-target install, hosted marketplace. |
| D119 | accepted | Accept RFC 0024 V4: pause/resume + per-job mutations. Migration v11 adds runs.paused_at + runs.paused_reason. claim_next gates on the column; pause/resume mutations are idempotent and orthogonal to the run state machine. retry_job resets failed/canceled/blocked jobs and revives canceled/failed runs (loud run.revived event). Per-job cancel route wraps existing recovery.cancel_job. | V3 added run-level cancel; operators still couldn't pause a run, kill one stuck job, or retry a failed job from the UI. V4 closes those gaps with the smallest schema delta (one migration adding two nullable columns). | See RFC 0024 and dogfood-027. v1.18.0; mutation-gated; CSP unchanged; design-review F1 option C (loud revive); F2/F3/F4 adopted. | Revisit for V5: pause-with-deadline; per-lane pause; recovery integration; consolidate JSON-body helpers. |
| D079 | accepted | Accept RFC 0024 V3: run-mutation surface — top-down cancel_run mutation (db.py) + CLI striatum run cancel + HTTP POST /run/<id>/cancel + UI Cancel button. Run-now dirty-tree failures now return 409 with git status --short (closes V2 F3). | Operators driving runs through the web UI need to stop them; without that, a misclicked Run-now sits live until manually cleaned. The dirty-tree visibility closes the most common Run-now failure mode. | See RFC 0024 and dogfood-026. v1.17.0; mutation-gated; idempotent; refuses terminal completed/failed; CSP unchanged; design-review F1 (drop redundant Re-run button) adopted; F2/F3 transitively covered. | Revisit for V4: pause/resume; auto-branch suffix; per-job mutation buttons (kill, retry); programmatic re-run with overrides; recovery integration. |
| D078 | accepted | Accept RFC 0024 V2: workflow visual-builder additions — POST /workflows/run/<path> mutation-gated lifecycle, If-Match: <sha256> concurrency guard on POST /workflows/edit/<path>, and structured per-field validation errors via an extended WorkflowError(field_path=...). | V1.5 left three deferrals; V2 ships the trio that materially advances editor UX without scope creep. Run-now closes the "edit then click around to launch" gap; If-Match closes the concurrent-edit gap; field-level errors close the "scroll the banner to find the bad field" gap. | See RFC 0024 and dogfood-025. v1.16.0; mutation-gated; CSP unchanged (new island in /static/workflow_run.js); 8 raise sites tagged with field_path; missing-If-Match preserves V1.5 backward compat. | Revisit for V3: drag-and-drop graph editor; workflow templates; AI-assisted scaffolding; multi-error reporting; field-path coverage for the remaining ~22 raise sites; flock() hard concurrency; 409 body carrying git status --short. |
| D077 | accepted | Accept RFC 0024 V1.5: workflow visual builder. JS-island form-driven editor at /workflows/edit/<path> with sections for header, roles, lanes, jobs, edges, cycles. POST validates via validate_workflow; atomic write on success; 422 on validation failure (file unchanged). | V1 was browse-only; the visual builder closes the V1.5 deferral and gives operators an interactive way to author workflow.json without hand-editing JSON. | See RFC 0024 and dogfood-024. v1.15.0; mutation-gated; 1 MB body cap; localStorage backup; new-vs-existing affordance per design-review F2. | Revisit for V2: drag-and-drop graph editor; workflow templates; "diff against another workflow"; field-level error highlighting (requires validate_workflow API change); If-Match precondition for concurrent edits. |
| D076 | accepted | Accept RFC 0024 V1: workflow browser (read-only). GET /workflows lists all workflow.json files in repo; GET /workflows/<path> renders SVG graph + tabular jobs/lanes/roles/edges/cycles; invalid workflows render error inline (no 500). New chat tool list_workflows extends the RFC 0023 V1.5 closed set. | Authoring/inspecting workflow.json was friction — operators were navigating via file tree to read JSON. The browser surface centralizes discovery + validation status + graph preview. | See RFC 0024 and dogfood-023. v1.14.0; reuses RFC 0022 V1's SVG renderer; no new runtime deps; path safety mirrors /view/<path>. | Revisit for V1.5: form-driven visual builder at /workflows/edit/<path>. V2: drag-and-drop graph editor; workflow templates; "diff against another workflow"; full lifecycle "run this workflow now". |
| D075 | accepted | Accept RFC 0023 V1.5: chat tool use (six closed-set read-only tools) + system-prompt briefing on chat-session creation. Bundles three v1.12.0 ride-along fixes: SVG graph-node click 404, doctor page empty problem list, chat double-render. | V1 chat had no codebase context (model said "I can't access your files"); V1.5 closes that with tools + briefing. The three fixes were operator-validated regressions worth bundling. | See RFC 0023 and dogfood-022. v1.13.0; tools all read-only; tool results wrapped in delimiters for prompt-injection defense; loop iteration cap = 10. | Revisit for V2: tool that mutates with explicit per-tool gating; web-search/fetch tools; approval-required tools; per-tool budgets; briefing refresh per turn. |
| D074 | accepted | Accept RFC 0023 V1: web chat (provider-neutral, two API flavors), /view/<path> file rendering, inline artifact Markdown. Documents the explicit "outbound HTTP from striatum to operator-configured endpoint" carve-out from AGENTS.md "no cloud APIs without product decision." | The browser-vs-terminal split was friction; chat in the UI closes it. CSP unchanged; no hosted service; transcripts stay ephemeral in scratch JSONL per a narrow D028 carve-out. | See RFC 0023 and dogfood-021. v1.12.0; markdown-it-py added as 2nd runtime dep; URL-scheme validation refuses non-loopback http://. | Revisit for V1.5: tool use, supervised-CLI backend, file-tree browser UI, syntax highlighting, OS-keyring API-key storage, chat→artifact promotion. |
| D073 | accepted | Accept RFC 0022 V1: web UI redesign — server-rendered Jinja2 multi-page UI, refreshed CSS palette + dark mode, layered SVG dependency graph with click-navigate. | The hash-routed SPA was scrappy; visual debt + text-only graph were the operator-validated primary pain points; URLs didn't copy/paste cleanly. | See RFC 0022 and dogfood-020. v1.11.0; Jinja2 added as the project's first runtime dependency; CSP unchanged; JSON API + SSE unchanged. | Revisit when V1.5 wants inline Markdown rendering on artifact pages, when V2 wants SVG zoom/pan, or when posture vocabulary growth needs a different graph layout. |
| D072 | accepted | Accept RFC 0021 V1.5: --ddd-layout-force and --ddd-layout-dry-run flags on striatum init --with-ddd-layout. | V1 reserved both kwargs in the API but ignored them; operators iterating on a target repo had to delete files manually before re-running. | See RFC 0021 and dogfood-019. v1.10.0; new statuses overwritten + would_*; non-regular-file targets still error under force; prior_sha256 records audit trail. | Revisit when V1.6 wants parameter substitution, multi-layout profiles, or a scaffold sync upgrade verb. |
| D071 | accepted | Accept RFC 0018 step 3 (V1.5): verdicts.posture column + introspection surfacing across status, run-summary, evidence-export, run-graph json, dashboard, and web UI. | V1 (D069) recorded posture on review jobs but operators couldn't see which posture caught what — verdicts were posture-blind in every read surface. | See RFC 0018 and dogfood-018. v1.9.0; migration v10 backfills existing rows to 'neutral'; web UI chip class is posture-chip with ellipsis truncation. | Revisit when V2 wants cross-run posture analytics, when posture vocabulary needs to grow beyond the V1 nine-entry closed set, or when web UI posture filtering is wanted. |
| D070 | accepted | Accept RFC 0021 V1: striatum init --with-ddd-layout scaffolds the seven canonical human-facing DDD documents into a target repo via setuptools package-data. | The striatum init flow stopped at .striatum/; operators adopting striatum had no anchor for the load-bearing UBIQUITOUS_LANGUAGE / SPEC / DDD / DECISION_LOG documents that RFC 0019 says are the model. | See RFC 0021 and dogfood-017. v1.8.0; opt-in; idempotent; non-file targets surface as per-file errors not silent skips. | Revisit when V1.5 ships --force / --dry-run / parameter substitution / multiple layout profiles, or when a doctor check for missing layout is wanted. |
| D069 | accepted | Accept RFC 0018 V1 (steps 1+2): review_posture validator + packet exposure, required_review_postures validator + workflow-validation reachability gate. | The RFC's runtime build-completion gate as originally written deadlocks against striatum's lifecycle; the validation-time re-cast delivers the same operator value (catches mis-wired posture coverage at workflow validate / run prepare) without the deadlock. | See RFC 0018 and dogfood-016. v1.7.0; runtime enforcement preserved by existing edge-verdict gate + run-completion semantics. | Revisit when V1.5 ships verdicts.posture column + introspection (RFC 0018 step 3), when operators want explicit run-level "blocked on missing posture" surfacing, or when posture vocabulary needs to grow beyond the V1 nine-entry closed set. |
| D068 | accepted | Accept RFC 0020 step 3: recovery watch --run-id <id> long-lived foreground scheduler with pidfile single-instance, signal-driven shutdown, and JSONL emission. | Cron + recovery auto works for periodic operators, but anyone running striatum interactively wanted a one-command "watch this run" pattern without coordinating timers. | See RFC 0020 and dogfood-015. v1.6.0 introduced the SQLite-era loop; in the daemon-required runtime the CLI scheduler calls recovery.sweep for each mutation and recovery.watch is not a registered RPC method. | Revisit when operators want a "watch every active run" supervisor, when systemd unit / launchd plist scaffolding is requested, or when web-UI control of the watcher is wanted. |
| D067 | accepted | Accept RFC 0019: docs/DDD.md documents the domain-driven framing the codebase already has. | First-time readers were reading the CLI as "yet another workflow runner" and missing the load-bearing vocabulary. | See RFC 0019 and DDD.md. v1.5.0; documentation only. | Revisit when the docs split into a published site, a DDD tactical-patterns pass is wanted, or readers conflate DDD.md with SPEC. |
| D066 | accepted | Accept RFC 0020 V1 (steps 1+2): recovery auto sweeper, recovery_policy workflow block, and escalation hooks. | D036's lazy-on-CLI policy left overnight runs vulnerable to silent stalls when no operator was poking the CLI. | See RFC 0020 and dogfood-014. v1.5.0; defaults preserve today's flow; the daemon-required PG runtime retains hook execution in recovery.sweep. Step 3 later landed under D068; in daemon-required runtime recovery watch is a foreground scheduler over daemon recovery.sweep. | Revisit when worktree-isolated repo-write recovery becomes safe to autonomously requeue, or retry-budget exhaustion should auto-cancel. |
| D065 | accepted | Accept RFC 0013 step 7: web UI mutation buttons (verdict, decision, checkpoint resolve continue/cancel, requeue stale review-only) over the existing RFC 0012 mutation gate. | RFC 0013 V1 explicitly deferred mutations; the gate already gives us a safe surface. | See RFC 0013 and dogfood-013. v1.4.0; CSP unchanged; runner-side gate stays authoritative. | Revisit when operators want a claim-next or publish-artifact button (need session ownership / file upload UX), or when the gate needs CSRF for non-loopback bridges. |
| D064 | accepted | Accept RFC 0016 step 3: Unicode fancy graph style + --graph-orient {tb, lr} on the dashboard graph panel. | The fancy choice was a placeholder that fell back to layered; LR orient is the right answer for long workflow chains that don't fit a 30-line frame. | See RFC 0016 and dogfood-012. v1.3.0; deterministic fallbacks (fancy → layered, lr → tb) keep CI snapshots stable. | Revisit when fancy needs proper join glyphs at multi-source convergence, terminals without UTF-8 locales appear, or per-frame memoization becomes worth the cost. |
| D063 | accepted | Accept RFC 0015 step 3: codex + gemini skill profiles + --profile all fan-out. | Codex CLI users were routed at --profile generic as a footnote; the fan-out makes the agent CLI ↔ profile mapping explicit. | See RFC 0015 and dogfood-011. v1.2.0; --profile all order is deterministic. | Revisit when Gemini CLI's skill convention stabilizes (graduate gemini to a five-file shape), parser-walked verb tables are wanted, or slash-command emission is requested. |
| D062 | accepted | Accept RFC 0017 V1: slim README.md to ~125 lines and split human / coding-agent quick starts into first-class headings; everything else moves under docs/. | The README had grown to 1,012 lines and was duplicating SPEC, mixing audiences, and burying the agent-facing onboarding path. | See RFC 0017 and dogfood-010. v1.1.0; tests/test_doc_links.py enforces line budget + heading split + relative-link integrity. | Revisit when the README starts drifting back past 500 lines, a CONTRIBUTING.md becomes necessary, or docs/CLI_REFERENCE.md should be auto-generated. |
| D061 | accepted | Accept RFC 0015 V1: striatum skills install writes a self-contained agent skill bundle (Claude Code + generic profiles) into the target tree. | Agents driving a target repo had no way to learn the runner's contract without the source repo on disk; AGENTS.md was a contributor doc, not an agent doc. | See RFC 0015 and dogfood-009. v0.5.0; codex + gemini profiles deferred to step 3 (later landed under D063). | Revisit when slash-command emission alongside skills is wanted, when the workflow JSON schema should be embedded, or when auto-regeneration on init/upgrade is justified. |
| D060 | accepted | Accept RFC 0016 V1: layered ASCII graph panel on the dashboard frame for wide TTYs (auto when width >= 100, height >= 30, ≥ 1 edge). | Operators were copy-pasting run graph --format mermaid into separate renderers because the dashboard was silent about workflow shape. | See RFC 0016 and dogfood-008. v0.4.0; ANSI 16-color palette mirrors MERMAID_STATE_FILLS; fancy + LR step 3 deferred (later landed under D064). | Revisit when graph rendering is wanted in the web UI, per-frame rendering becomes hot enough to warrant memoization, or mouse-driven node selection is requested. |
| D059 | accepted | Accept RFC 0013 V1: read-only vanilla-JS SPA at src/striatum/web/static/ served by striatum serve --web. | RFC 0012 V1 shipped the API; the web UI is the operator-facing consumer that closes D006's "Slack/TUI/web dashboards" promise without an npm install. | See RFC 0013 and dogfood-007. v0.3.0; CSP set on every static + artifact-raw response; mutation buttons (step 7) deferred (later landed under D065). | Revisit when full CommonMark + embedded Mermaid is wanted, when browser notifications on terminal transitions become useful, or when the SPA grows complex enough to justify a tiny framework. |
| D058 | accepted | Accept RFC 0012 V1: striatum serve exposes a loopback HTTP / Unix-socket service over striatum.api.invoke, with SSE for events and --allow-mutations gating writes. Current production service routes daemon-mapped reads and mutations through daemon RPC; api.invoke remains a compatibility/local-authoring path. | The dashboard, in-process API, and MCP wrapper shipped; the network surface for adapters (Slack, web, future TUI) was the missing piece. | See RFC 0012 and dogfood-006. v0.2.0; non-loopback hosts refused with exit 8 (preserves D020); whitelist of read verbs is conservative. | Revisit when WebSocket bidirectional control is needed, long-poll fallback for proxy environments is wanted, or per-endpoint mutation overrides become useful. |
| D057 | accepted | Accept RFC 0014 V1: process adapter completion guarantees — every adapter run exit validates required artifacts and review verdicts; gaps insert a structured blockers.payload_json envelope. | A child could exit 0 without producing the artifact and the workflow would sit silently in running until lease expiry. | See RFC 0014 and dogfood-005. Migration v8/v9; new recovery process-reconcile; closes #1. | Revisit when the default timeout should drop from unbounded to ~1800s, when heartbeat-based timeouts are needed, or when an "agent writes a result envelope file" contract is wanted. |
| D056 | accepted | Accept RFC 0010 V1: optional harness_profiles workflow map + per-lane harness_profile_id reference, surfaced verbatim on work packets. | Native sub-agent guidance was scattering across prompts and operator memory; an additive schema made it auditable without forcing every workflow to declare it. | See RFC 0010 and dogfood-003. Closed tool-family set {generic, codex, claude_code, gemini_cli} keeps V1 vocabulary small; unknown sibling fields surface as lint warnings. | Revisit when warnings should graduate to errors, profile reference by file path is wanted, job-level overrides become necessary, or first-class native-sub-agent registration needs a decision. |
| D055 | accepted | Accept RFC 0011: striatum session close + automatic close-on-run-terminal so the active_session_on_terminal_run doctor warning clears by construction. | Sessions had no path out of active; the warning fired permanently on every clean-finish run (dogfood-001 evidence). | See RFC 0011 and dogfood-002. Migration v7 adds closed state + closed_at/close_reason columns; the doctor check stays as a residual warning for genuinely anomalous skipped transitions. Three follow-ups (run-canceled state, reject-verdict test, rebuild_table helper) landed on striatum/d055-followups. | Revisit when sessions need a richer terminal-state vocabulary, when explicit close should also fire on run-prepare-stage cleanup, or when the striatum.migrations.rebuild_table() pattern is needed for non-self-referential FKs. |
Open Questions
| ID | Status | Question | Recommended Answer | Owner Answer | Resolution |
|---|---|---|---|---|---|
| Q001 | accepted | Should the project start from a PRD, RFCs, or a specification? | Start with a PRD, backed by a decision log, then derive RFCs for contested architecture branches and a spec after product shape stabilizes. | Agreed. | Promoted to D001. |
| Q002 | accepted | What is the v1 product boundary: a generic local terminal-agent orchestrator, or an Engram-specific Phase 3 runner extracted later? | Build striatum as a generic local terminal-agent orchestrator from the start, with Engram Phase 3 and the RFC-ledger workflow as reference fixtures. | Generic from the start. | Promoted to D002. |
| Q003 | accepted | What is the coordinator: a deterministic orchestrator, an AI agent, or a hybrid? | Use a hybrid: deterministic local coordinator owns state, gates, process launching, message routing, and safety; exactly one selected, portable model lane provides chat and project-manager style workflow control. Keep synthesis as an explicit workflow job to avoid coordinator attention dilution. | Hybrid accepted, with coordinator focused on project management rather than primary synthesis. | Promoted to D004. |
| Q004 | accepted | What are the first-class coordinator responsibilities and explicit non-responsibilities? | The AI coordinator should own conversational control, goal tracking, next-action selection, blocker triage, human checkpoint escalation, state summarization, work assignment, and invoking workflow commands. It should not directly synthesize major artifacts, write source patches, bypass deterministic gates, edit workflow state directly, or replace reviewer judgment unless assigned a specific job. | Agreed. | Promoted to D005. |
| Q005 | accepted | What should be the v1 live coordination layer: repo files, SQLite-backed local message bus, filesystem queue outside the repo, or local HTTP/Unix-socket service? | Use SQLite as the local state store/message bus, with a CLI API as the primary interface and an optional Unix-socket/local HTTP API later for Slack/TUI/web adapters. Repo files remain durable artifacts only. | Accepted enthusiastically. | Promoted to D006. |
| Q006 | accepted | Where should the SQLite state store live by default: inside each repo under .striatum/, in a user-level directory keyed by repo path, or configurable per workflow? | Store v1 state inside the target repo under .striatum/, ignored by default, because it makes runs portable with the worktree and keeps project state easy to inspect. Allow an override for user-level state later. | Accepted. | Promoted to D007. |
| Q007 | accepted | What is the minimum common integration contract across supported agent CLIs? | Treat the POSIX-like process boundary as the minimum contract: launch command, cwd, env, stdin prompt/context, stdout/stderr, exit code, and optional PTY. Model-specific adapters can add MCP, JSON events, session resume, hooks, and sandbox controls. | Accepted. | Promoted to D022. |
| Q008 | accepted | What is the default agent lifecycle: one process per job, persistent session per lane/phase, or both? | Support persistent sessions as the preferred interactive mode, with explicit task envelopes and deterministic completion handshakes; keep one-process-per-job as a clean fallback for headless runs, flaky sessions, or jobs that require fresh context. | Persistent sessions preferred until role expires. | Promoted to D011. |
| Q009 | accepted | How should agents signal completion, take new work, and communicate with the coordinator: CLI commands, MCP tools, terminal text conventions, or provider-specific hooks? | Use the striatum CLI as the primary control surface. MCP tools may wrap the same commands later, terminal text is for humans, and provider-specific hooks are adapter enhancements rather than portable core. | CLI is cleaner than MCP. | Promoted to D013. |
| Q010 | accepted | Should agents interact with SQLite directly, or only through striatum APIs/tools that own the schema and invariants? | Agents should not write SQLite directly. They should interact through MCP tools and CLI commands that enforce leases, acknowledgements, transitions, and artifact validation. Read-only SQL or exported views can be allowed for debugging/introspection. | Create a binary that agents interact with to make updates. | Promoted to D009. |
| Q011 | accepted | What should the striatum binary's minimum mutation command set be? | Start with: register-session, claim-next, ack, send, block, complete, verdict, publish-artifact, heartbeat, and release. Commands that claim or mutate work require session identity. Keep admin/status commands separate from agent mutation commands. | Good enough for now; details still foggy. | Promoted to D023. |
| Q012 | accepted | How should agent sessions be named and identified? | Use an opaque stable session_id as the database identity, plus a human-readable session slug for tmux windows, dashboards, logs, and prompts. Store role, lane, and ordinal as separate fields; artifact bylines are handled separately by D040/D041. | Accepted. | Promoted to D012. |
| Q013 | accepted | Does striatum need a cyclic state graph, or is an acyclic workflow DAG with explicit retry/revision edges enough? | Use a workflow graph that is mostly DAG-shaped but permits explicit bounded cycles for retry, revision, re-review, and human checkpoint loops. Avoid unbounded autonomous cycles in v1. | Accepted. | Promoted to D014. |
| Q014 | accepted | How should the coordinator decide whether to run multiple agents with the same role in parallel? | For v1, require workflow-declared parallelism with disjoint write scopes or review-only scopes. Defer AI-inferred build parallelization to v2, where it can be mediated by dependency/write-scope analysis and explicit human approval. | Accepted; build parallelization is likely v2. | Promoted to D015. |
| Q015 | deferred | What exactly belongs in a work packet, and where does role definition live? | A work packet should include a context doc reference, role reference, prompt/task instructions, expected artifacts, allowed write scope, completion commands, stop conditions, and relevant prior messages. Role definition should live in workflow config or reusable role docs; the task prompt can add job-specific role emphasis. | Hand this to a design team. Figure it out during design. | Deferred to D024 design-team output. |
| Q016 | accepted | Should build outputs be idempotent and durable? | Yes. Prompts, findings, syntheses, markers, and handoff artifacts should be durable repo-published outputs so repeated runs can converge and other agents can inspect them. | Owner wants build process idempotent and includes prompts/findings. | Promoted to D016. |
| Q017 | accepted | How should coordinator chat commands like "read prompt foo" work? | Treat them as coordinator skills backed by deterministic CLI operations: resolve prompt artifact, load allowed context, create or enqueue a work packet, and ask for human confirmation when the action starts workflow execution or writes files. | Agreed. | Promoted to D020. |
| Q018 | accepted | What interaction surfaces are v1 and later? | V1 CLI plus tmux introspection; TUI next; Slack and web dashboard/chat later. | Accepted. | Promoted to D025. |
| Q019 | accepted | How should branches and commits work? | Coordinator starts/selects a branch and requests a commit from the human. It does not commit autonomously by default. | Owner wants coordinator to start a branch and request commit from human. | Promoted to D017. |
| Q020 | accepted | What implementation language should v1 use? | Python. | Owner prefers probably sticking with Python. | Promoted to D018. |
| Q021 | accepted | Where should role definitions and context docs live conceptually? | Roles should be reusable artifacts. Task prompts can reference roles. Context docs should be generic. | Accepted. | Promoted to D019. |
| Q022 | accepted | How should striatum handle native sub-agents spawned by supported agent CLIs? | Treat native sub-agents as an internal implementation detail of the parent agent session in v1. The parent session remains accountable for write scope, artifacts, completion, and messages. If a sub-agent needs independent auditability, queue claims, or parallel scheduling, striatum should register it as a first-class session instead. | Parent agent is responsible. | Promoted to D021. |
| Q023 | accepted | Should branch creation happen automatically or with confirmation? | Confirm branch creation or branch selection at run start. | Confirm branch on start. | Promoted to D026. |
| Q024 | accepted | What format should workflow configuration use? | JSON. Reject YAML. | JSON forever; no YAML. | Promoted to D027. |
| Q025 | accepted | What is the repo artifact policy? | Publish decisions, prompts, findings, syntheses, markers, and handoffs; do not capture/publish transcripts by default. | Agreed; not even transcripts. Decisions for sure. Coordinator records decisions. | Promoted to D028. |
| Q026 | accepted | When should fresh sessions be used? | Fresh context means new role instantiation. Multi-pass reviews until consensus and builds until complete are persistent by default. | Accepted. | Promoted to D029. |
| Q027 | accepted | What should the first validation workflow be? | RFC-ledger cleanup. | Accepted. | Promoted to D030. |
| Q028 | accepted | What project structure should striatum emulate? | Emulate Engram's Python project discipline where appropriate. | Accepted. | Promoted to D031. |
| Q029 | accepted | Should the one-shot MVP process require design input from all three model lanes? | Yes. Require separate Claude, Codex, and Gemini design inputs before synthesis/build. | The one-shot should involve design input from all 3 models. | Promoted to D032. |
| Q030 | accepted | Should the Engram tmux runner be reused as bootstrap orchestration for the striatum one-shot? | Allow reuse or adaptation of the Engram tmux runner as a temporary bootstrap harness for the three-model design/build pass, but keep it out of the product core. The product should implement its own generic tmux/PTY adapter after design review. | Owner asked to port the tmux runner for the design-input pass. | Promoted to D034. |
| Q031 | accepted | Should striatum be incubated inside Engram before being split out? | Yes. Commit it under engram/striatum for MVP context, then split it into a separate project after MVP validation. | Accepted. | Promoted to D033. |