instar 1.3.269 → 1.3.270

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +9 -1
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/coordination/MandateAudit.d.ts +48 -0
  5. package/dist/coordination/MandateAudit.d.ts.map +1 -0
  6. package/dist/coordination/MandateAudit.js +95 -0
  7. package/dist/coordination/MandateAudit.js.map +1 -0
  8. package/dist/coordination/MandateGate.d.ts +48 -0
  9. package/dist/coordination/MandateGate.d.ts.map +1 -0
  10. package/dist/coordination/MandateGate.js +104 -0
  11. package/dist/coordination/MandateGate.js.map +1 -0
  12. package/dist/coordination/MandateStore.d.ts +60 -0
  13. package/dist/coordination/MandateStore.d.ts.map +1 -0
  14. package/dist/coordination/MandateStore.js +106 -0
  15. package/dist/coordination/MandateStore.js.map +1 -0
  16. package/dist/coordination/conditions.d.ts +32 -0
  17. package/dist/coordination/conditions.d.ts.map +1 -0
  18. package/dist/coordination/conditions.js +54 -0
  19. package/dist/coordination/conditions.js.map +1 -0
  20. package/dist/coordination/types.d.ts +74 -0
  21. package/dist/coordination/types.d.ts.map +1 -0
  22. package/dist/coordination/types.js +17 -0
  23. package/dist/coordination/types.js.map +1 -0
  24. package/dist/core/ApprovalLedger.d.ts +132 -0
  25. package/dist/core/ApprovalLedger.d.ts.map +1 -0
  26. package/dist/core/ApprovalLedger.js +191 -0
  27. package/dist/core/ApprovalLedger.js.map +1 -0
  28. package/dist/core/SessionBuildContextStore.d.ts +33 -0
  29. package/dist/core/SessionBuildContextStore.d.ts.map +1 -0
  30. package/dist/core/SessionBuildContextStore.js +107 -0
  31. package/dist/core/SessionBuildContextStore.js.map +1 -0
  32. package/dist/core/SessionManager.d.ts +4 -0
  33. package/dist/core/SessionManager.d.ts.map +1 -1
  34. package/dist/core/SessionManager.js +44 -3
  35. package/dist/core/SessionManager.js.map +1 -1
  36. package/dist/core/types.d.ts +11 -0
  37. package/dist/core/types.d.ts.map +1 -1
  38. package/dist/core/types.js.map +1 -1
  39. package/dist/feedback-factory/migration/immutableGuard.d.ts +87 -0
  40. package/dist/feedback-factory/migration/immutableGuard.d.ts.map +1 -0
  41. package/dist/feedback-factory/migration/immutableGuard.js +163 -0
  42. package/dist/feedback-factory/migration/immutableGuard.js.map +1 -0
  43. package/dist/feedback-factory/migration/importIntegrity.d.ts +112 -0
  44. package/dist/feedback-factory/migration/importIntegrity.d.ts.map +1 -0
  45. package/dist/feedback-factory/migration/importIntegrity.js +202 -0
  46. package/dist/feedback-factory/migration/importIntegrity.js.map +1 -0
  47. package/dist/feedback-factory/monitor/parityMonitor.d.ts +75 -0
  48. package/dist/feedback-factory/monitor/parityMonitor.d.ts.map +1 -0
  49. package/dist/feedback-factory/monitor/parityMonitor.js +98 -0
  50. package/dist/feedback-factory/monitor/parityMonitor.js.map +1 -0
  51. package/dist/feedback-factory/monitor/parityMonitorStore.d.ts +44 -0
  52. package/dist/feedback-factory/monitor/parityMonitorStore.d.ts.map +1 -0
  53. package/dist/feedback-factory/monitor/parityMonitorStore.js +80 -0
  54. package/dist/feedback-factory/monitor/parityMonitorStore.js.map +1 -0
  55. package/dist/scaffold/templates.d.ts.map +1 -1
  56. package/dist/scaffold/templates.js +11 -0
  57. package/dist/scaffold/templates.js.map +1 -1
  58. package/dist/server/AgentServer.d.ts +6 -0
  59. package/dist/server/AgentServer.d.ts.map +1 -1
  60. package/dist/server/AgentServer.js +94 -1
  61. package/dist/server/AgentServer.js.map +1 -1
  62. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  63. package/dist/server/CapabilityIndex.js +29 -0
  64. package/dist/server/CapabilityIndex.js.map +1 -1
  65. package/dist/server/routes.d.ts +11 -0
  66. package/dist/server/routes.d.ts.map +1 -1
  67. package/dist/server/routes.js +255 -0
  68. package/dist/server/routes.js.map +1 -1
  69. package/package.json +1 -1
  70. package/src/data/builtin-manifest.json +48 -48
  71. package/src/scaffold/templates.ts +11 -0
  72. package/upgrades/1.3.270.md +106 -0
  73. package/upgrades/approval-ledger-phase2.eli16.md +56 -0
  74. package/upgrades/feedback-phase3-monitor.eli16.md +51 -0
  75. package/upgrades/feedback-phase4-integrity.eli16.md +54 -0
  76. package/upgrades/respawn-build-context-reestablishment.eli16.md +5 -0
  77. package/upgrades/side-effects/approval-ledger-phase2.md +82 -0
  78. package/upgrades/side-effects/coordination-mandate-enforcement.md +86 -0
  79. package/upgrades/side-effects/feedback-phase3-monitor.md +37 -0
  80. package/upgrades/side-effects/feedback-phase4-integrity.md +35 -0
  81. package/upgrades/side-effects/respawn-build-context-reestablishment.md +81 -0
@@ -0,0 +1,51 @@
1
+ # ELI16 — Phase-3 parity monitor + cutover gate
2
+
3
+ ## What problem does this solve?
4
+
5
+ Before we flip the feedback system from Dawn's Portal to Instar (the "cutover" — a one-way
6
+ door), we run both processors side by side over real traffic for a while and check they
7
+ agree. That side-by-side check ("the parity comparison") already exists. What was missing is
8
+ the part that watches it *over time* and decides when it's genuinely safe to flip.
9
+
10
+ You can't trust a single green check. Traffic is bursty; one quiet minute proves nothing. We
11
+ need to see a real, sustained window where the two systems agreed on every report — and only
12
+ *then* allow the cutover. And crucially, the decision to flip must not come from an agent
13
+ "feeling confident" — it must come from an objective gate that an agent cannot fake.
14
+
15
+ ## What this change adds
16
+
17
+ A small, pure `ParityMonitor`:
18
+
19
+ - You feed it each comparison pass (how many clusters were compared, how many divergences).
20
+ - It tracks the current **zero-divergence streak**. The moment any pass diverges, the streak
21
+ resets to zero — so a single disagreement, even after a long clean run, sends us back to
22
+ the start. No "mostly agreed" hand-waving.
23
+ - It exposes one method, `gate(now)`, that returns **cleared** or **blocked** with a reason.
24
+ It clears only when the trailing run of passes is (a) all clean, (b) at least N passes
25
+ long, (c) spanning at least a real time window (default one hour), and (d) covering enough
26
+ clusters to be a meaningful sample. All four must hold.
27
+
28
+ That `cleared` boolean is the objective `parity-zero-divergence` condition. It's exactly what
29
+ the Coordination Mandate's "execute-cutover" authority reads: the agents can only flip the
30
+ one-way door when this gate — not their judgment — says the window is satisfied.
31
+
32
+ ## Why it's safe
33
+
34
+ Pure logic, no database, no network, no boot wiring. It only ever *reads* comparison results
35
+ and *computes* a yes/no; it changes nothing and triggers nothing on its own. It's the
36
+ building block the cutover executor will consult. 9 unit tests cover every gate boundary:
37
+ too-few passes, too-short window, too-few clusters, the satisfied window, and the streak
38
+ reset on any divergence (including re-clearing only after a fresh full clean window).
39
+
40
+ ## Plain-language risk
41
+
42
+ Near-zero, and the failure mode is conservative: if the policy is too strict, the gate just
43
+ stays *blocked* longer (cutover waits) — never the dangerous direction of clearing early.
44
+
45
+ ## Durability note
46
+
47
+ The "they agreed" window can take hours to build up. If we kept it only in memory, restarting
48
+ the server in the middle would silently wipe the streak and we'd have to start over (or worse,
49
+ mis-measure the window across the restart). So each check result is appended to a small log file
50
+ and reloaded when the monitor starts — the window survives restarts. A half-written final line
51
+ from a crash is skipped cleanly, so one bad line never corrupts the rest of the history.
@@ -0,0 +1,54 @@
1
+ # ELI16 — Phase-4 feedback-migration integrity tooling
2
+
3
+ ## What problem does this solve?
4
+
5
+ We're moving the "feedback factory" (the thing that catches user bug reports, groups
6
+ duplicates into clusters, and tracks each cluster's lifecycle) from Dawn's Portal over to
7
+ Instar. The cluster records aren't just data — they hold human and AI *judgment*: which
8
+ bug is which, what was already triaged, what was shipped. That curated judgment is
9
+ irreplaceable. If the move corrupts it, or if the new processor later overwrites it, we
10
+ lose work nobody can recreate.
11
+
12
+ Two specific dangers:
13
+
14
+ 1. **A silent bad copy.** "The row counts match" is far too weak a check — a single
15
+ cluster could come across with its status flipped or its governance note dropped and a
16
+ count check would never notice. We need to prove *every curated field of every row*
17
+ survived the move byte-for-byte.
18
+
19
+ 2. **A wrong-date backfill overwriting curated history.** After cutover, the processor is
20
+ only supposed to touch *new* reports. But if someone ever re-runs it over old data, it
21
+ would happily "re-cluster" the curated history and discard every triage decision.
22
+
23
+ ## What this change adds
24
+
25
+ Two small, pure, well-tested building blocks (no database, no network — just logic, so
26
+ they're fully unit-testable):
27
+
28
+ - **`importIntegrity.ts`** — the import gate. It computes a per-row checksum over every
29
+ curated field and compares source-vs-imported (catching silent corruption), scans the
30
+ source for two clusters sharing a fingerprint *before* importing (which would otherwise
31
+ abort the transaction or silently merge them), asserts the two schemas accept the same
32
+ status values and field types, checks that no feedback row points at a missing cluster
33
+ (referential integrity), and computes the auto-increment sequence reset so the next new
34
+ insert can't collide. `runIntegrityGate()` rolls all of that into one pass/fail.
35
+
36
+ - **`immutableGuard.ts`** — the structural "never re-derive" guard. Instead of *trusting*
37
+ the processor to only run over new traffic, we wrap the store: a cluster that predates
38
+ cutover, or that carries any governance note, is **immutable**, and any attempt to merge
39
+ into it, reopen it, or upsert over it is physically refused and recorded. A wrong-date
40
+ backfill therefore *cannot* overwrite curated state — the write simply does not happen.
41
+
42
+ ## Why it's safe
43
+
44
+ Both modules are pure functions plus one store decorator. The guard only ever *refuses*
45
+ writes (it never invents or changes data), and reads pass straight through. Nothing is
46
+ wired into the running server yet — these are the building blocks the Phase-4 cutover will
47
+ use. 36 new unit tests cover both sides of every boundary, including the processor running
48
+ over a guarded store and leaving a curated cluster's count, status, and note untouched.
49
+
50
+ ## Plain-language risk
51
+
52
+ Near-zero. Worst case if the guard is mis-tuned: a *legitimate* post-cutover merge is
53
+ refused and shows up in the violations list (loud, not silent) — far better than the
54
+ alternative of silently overwriting curated judgment.
@@ -0,0 +1,5 @@
1
+ # Respawn build-context restore
2
+
3
+ Developer agents can now remember the build checkout they were using before a respawn. When the feature is enabled and a resumed session had been working inside an agent worktree, the resumed conversation starts with a `[BUILD-CONTEXT RESTORE]` note pointing back to that worktree.
4
+
5
+ This is meant for fleet PR build sessions that die or restart mid-task. It does not move the tmux session's home directory, and it does nothing for normal home-only conversations. Stale or removed worktrees are skipped.
@@ -0,0 +1,82 @@
1
+ # Side-effects review — Approval-as-Data ledger (Phase 2)
2
+
3
+ Spec: `docs/specs/AUTONOMOUS-OPERATION-JUDGMENT-AND-APPROVAL-AS-DATA-SPEC.md` (Part B).
4
+ Change: new `src/core/ApprovalLedger.ts` + `POST/GET /approvals`, `GET /approvals/summary`
5
+ wired through `AgentServer` → `RouteContext`; Agent-Awareness blurb in the CLAUDE.md template.
6
+
7
+ ## 1. Blast radius
8
+ Additive only. A new module, three new route handlers, one new always-constructed field
9
+ on `AgentServer`, one new `RouteContext` field, one template blurb. No existing route,
10
+ store, sentinel, or gate is modified. The ledger never gates, blocks, throttles, or
11
+ mutates any other state — read paths are pure reads; the one write path appends to its
12
+ own JSONL file.
13
+
14
+ ## 2. State / data
15
+ New on-disk file `.instar/state/approval-ledger.jsonl` (append-only, signed). No schema
16
+ migration — the file is created on first write and absent until then. No existing file is
17
+ touched. Torn-trailing-line tolerant on read. Rows are HMAC-signed over `authToken`; an
18
+ empty/missing file yields an empty summary (no crash).
19
+
20
+ ## 3. Interactions
21
+ - `RouteContext` gains a REQUIRED `approvalLedger` field. The only production assembler is
22
+ `AgentServer` (updated). Route tests build `ctx: any` partials, so they are unaffected
23
+ (tsc confirms the whole tree compiles).
24
+ - Construction rides the existing `stateDir` availability (like `tokenLedger`); own
25
+ try/catch so an init failure can never cascade into the other ledgers.
26
+ - Shares no lock, queue, or budget with any other component.
27
+
28
+ ## 4. Failure modes
29
+ - No `authToken` → falls back to a fixed dev key for signing (integrity-only; documented).
30
+ Signing is never load-bearing for correctness — the operator-authoritative-source rule is.
31
+ - `stateDir` absent → `approvalLedger` is null → routes 503 (handled + tested).
32
+ - Inconsistent row (as-is WITH a divergence, or a change WITHOUT one) → `recordApproval`
33
+ throws → the route returns 400, not 500 (tested). Bad data cannot pollute ratios.
34
+ - Disk-full / write error on append → throws to the caller (route 400/500); no partial
35
+ corrupt state (single `appendFileSync`).
36
+
37
+ ## 5. Security / authority
38
+ - The load-bearing invariant is **operator-authoritative `mode` + `divergences`**: the
39
+ agent must not self-classify intent. Enforced by documentation + the design (the route
40
+ records what the caller passes; the caller must pass an explicit operator statement) and
41
+ backstopped by operator-correctable rows. HMAC signing gives integrity, NOT correctness —
42
+ it stops tampering, not ratio inflation; the authority rule stops inflation.
43
+ - All routes are Bearer-gated (standard middleware). Read routes never gate behavior.
44
+
45
+ ## 6. Performance
46
+ - O(rows) summary computation over a small append-only file read on each summary call.
47
+ At realistic volumes (tens to low-hundreds of approvals) this is negligible. No polling,
48
+ no background work, no LLM calls. Phase-3 (auto-approval, digest) is out of scope here.
49
+
50
+ ## 7. Migration parity
51
+ - No `.claude/settings.json` hook, `.instar/config.json` default, or hook-script change →
52
+ no `PostUpdateMigrator` entry required. The feature is server-side code that activates on
53
+ the next server start after update; existing agents get it by updating, with no on-disk
54
+ migration. Agent-Awareness IS satisfied: the CLAUDE.md template gained an Approval-as-Data
55
+ blurb (`generateClaudeMd`).
56
+ - Always-on when `stateDir` exists (read-only, low-cost) — no enable flag to migrate.
57
+
58
+ ## Tests
59
+ - Unit (`tests/unit/ApprovalLedger.test.ts`, 18): record/read/verify, tamper-reject, every
60
+ consistency-guard throw + its passing counterpart, ratio + streak (reset on change/reject),
61
+ auto-eligibility threshold edges, surface generality, corrections, torn-line tolerance.
62
+ - Integration (`tests/integration/approvals-routes.test.ts`, 6): full HTTP round-trip over a
63
+ real ledger; summary + surface breakdown; the 400 + 503 paths.
64
+ - E2E (`tests/e2e/approval-ledger-lifecycle.test.ts`, 5): real AgentServer boot, alive
65
+ (200 not 503), record→summary→list lifecycle, Bearer-gate, and a WIRING-INTEGRITY proof
66
+ that the production HMAC signer is real (persisted row verifies; a tamper fails).
67
+
68
+ ## Post-review amendments (same PR, pre-merge)
69
+
70
+ - **State-path fix:** the ledger file follows the `.instar/state/` convention —
71
+ `config.stateDir` IS `.instar`, durable state lives under `state/`
72
+ (`.instar/state/approval-ledger.jsonl`, matching the spec's stated path). The e2e
73
+ wiring-integrity test proves the corrected path end-to-end.
74
+ - **Ratchet exemptions:** the three deny-safe/default-empty catches carry
75
+ `@silent-fallback-ok` markers INSIDE the catch blocks (missing-file → empty
76
+ history; malformed signature → verifies false; init failure → reported via
77
+ console.warn + ledger null → routes 503).
78
+ - **Docs coverage:** the three `/approvals` routes are documented in
79
+ `site/src/content/docs/reference/api.md` (route-floor gate).
80
+ - **Discoverability:** the `/approvals` prefix is classified in
81
+ `CapabilityIndex.ts` (CAPABILITY_INDEX → surfaced in `/capabilities` with its
82
+ three endpoints), satisfying the capabilities-discoverability gate.
@@ -0,0 +1,86 @@
1
+ # Side-effects review — Coordination Mandate enforcement (spec §4, G2.2)
2
+
3
+ Spec: `docs/specs/coordination-mandate.md` (approved by Justin, A/A/B, 2026-06-05).
4
+ Change: new `src/coordination/` module (types, MandateStore, MandateGate, MandateAudit,
5
+ ConditionsRegistry) + six `/mandate*` routes wired through `AgentServer` → `RouteContext`.
6
+
7
+ ## 1. Blast radius
8
+ Additive only. No existing route, gate, sentinel, or store is modified. The engine is
9
+ **deny-by-default and inert**: with no mandate issued (the universal state at deploy),
10
+ every `/mandate/evaluate` call denies — so shipping this changes NO behavior anywhere
11
+ until the operator issues the first mandate through the PIN-gated route. It never
12
+ weakens an existing safety check; it adds a controlled delegation surface beside them.
13
+
14
+ ## 2. State / data
15
+ Two new files under the `.instar/state/` convention, created on first use:
16
+ `coordination-mandates.json` (the signed mandates) and `mandate-audit.jsonl`
17
+ (append-only, hash-chained). No schema migration; absent files mean an empty store and
18
+ an empty audit. Torn-trailing-line tolerant reads.
19
+
20
+ ## 3. Security model (the load-bearing dimension)
21
+ - **requester ≠ authorizer preserved:** the human-authored mandate is the authorizer.
22
+ The agent's Bearer token CANNOT issue or revoke — `/mandate/issue` and
23
+ `/mandate/:id/revoke` require the operator's dashboard PIN (sha256 + timingSafeEqual +
24
+ per-IP attempt limiting, mirroring `/dashboard/unlock`). Tested at all three tiers.
25
+ - **Authorship proof (T1/T2):** HMAC over the canonical authored bytes (key-order-stable
26
+ serialization), produced only at issuance. A forged or widened mandate fails
27
+ `verifyAuthorship` → the gate denies. `revoked` is excluded from the proof so the
28
+ kill switch works post-issuance; revocation is checked on every evaluation (T5).
29
+ - **Conditions (T7/T10):** objective resolvers from real state; an unregistered or
30
+ throwing resolver evaluates FALSE (deny-safe). The first mandate (per decision 3B)
31
+ carries no conditioned authority; `integrity-gate-pass` and `parity-zero-divergence`
32
+ are registered deny-safe stubs until the real state is wired for a future cutover
33
+ authority — which per decision 1A is not delegated anyway.
34
+ - **Audit (T3/T8):** every decision (allow AND deny) appends a hash-chained entry;
35
+ `verifyChain()` detects any edit/deletion and is surfaced on `GET /mandate/audit`.
36
+ - **Stated trust boundary (T12):** an attacker with local disk write access could edit
37
+ server-managed state — the same trust root as today; the proof stops forged AUTHORED
38
+ content, and the audit chain makes tamper detectable. Out of scope per the spec.
39
+
40
+ ## 4. Failure modes
41
+ - No `stateDir` → engine is null → all `/mandate*` routes 503 (tested).
42
+ - Engine init failure → own try/catch, never cascades to other inits.
43
+ - Malformed issuance body → 400 with a specific reason (agents pair, non-empty
44
+ authorities, future expiry — each tested both sides).
45
+ - No `dashboardPin` configured → issuance/revocation 503 (PIN auth unavailable) — the
46
+ gate cannot be bypassed by the PIN simply being absent.
47
+
48
+ ## 5. Interactions
49
+ - `RouteContext` gains a required `coordination` field; the only production assembler
50
+ (`AgentServer`) is updated. Route tests use partial `ctx: any` → unaffected (tsc green).
51
+ - Express route-order hazard handled: `/mandate/audit` and `/mandate/evaluate` are
52
+ registered BEFORE `/mandate/:id` (tested explicitly).
53
+ - No locks, queues, LLM calls, or budgets shared with other components.
54
+
55
+ ## 6. Performance
56
+ O(mandates) reads of a small JSON file per evaluation + one appendFileSync per decision.
57
+ Evaluation volume is a handful of A2A actions per migration step — negligible.
58
+
59
+ ## 7. Migration parity
60
+ No `.claude/settings.json` hook, config default, or hook-script change → no
61
+ `PostUpdateMigrator` entry needed. Server-side code activates on the next server start
62
+ after update. Agent-Awareness satisfied: CLAUDE.md template gains a Coordination
63
+ Mandate blurb (`generateClaudeMd`). The feature is inert-by-default everywhere (deny
64
+ without a mandate), so fleet exposure is zero until an operator issues one.
65
+
66
+ ## Tests
67
+ - Unit (`tests/unit/coordination-mandate.test.ts`, 17): authorship valid/forged/widened,
68
+ revoke-preserves-proof, canonical key-order stability, audit chain intact/tampered,
69
+ conditions (true/false/unknown/compound/throwing), the gate's FULL deny ladder
70
+ (missing, forged, expired, revoked, non-party, unlisted action, out-of-bounds params,
71
+ unmet condition) + both allow paths, bounds helper both sides.
72
+ - Integration (`tests/integration/mandate-routes.test.ts`, 9): PIN-less issue refused,
73
+ wrong PIN refused, issue/list/evaluate/revoke round-trip, deny-by-default audited,
74
+ route order, validation 400s, 503s.
75
+ - E2E (`tests/e2e/coordination-mandate-lifecycle.test.ts`, 5): real AgentServer boot,
76
+ alive + Bearer-gated, deny-by-default on fresh boot, full PIN-issue→allow→deny→
77
+ revoke→deny lifecycle with chain-verified audit, and the production-HMAC
78
+ wiring-integrity proof (persisted authProof verifies; a widened mandate fails).
79
+
80
+ ## Post-review amendment (same PR, pre-merge)
81
+
82
+ - **Discoverability:** the `/mandate` prefix is classified in `CapabilityIndex.ts`
83
+ (CAPABILITY_INDEX → surfaced in `/capabilities` with all six endpoints + the
84
+ evaluate-before-acting + PIN-gating semantics), satisfying the
85
+ capabilities-discoverability gate. Agents discover the gate; they still cannot
86
+ issue through it.
@@ -0,0 +1,37 @@
1
+ # Side effects — Phase-3 parity monitor + cutover gate
2
+
3
+ ## New files (additive only)
4
+ - `src/feedback-factory/monitor/parityMonitor.ts` — `ParityMonitor` (records passes, tracks
5
+ the zero-divergence streak, exposes the `gate()` cutover signal) + `DEFAULT_GATE_POLICY`.
6
+ - `tests/unit/feedback-factory/parity-monitor.test.ts` (9 tests).
7
+
8
+ ## Runtime impact
9
+ - **None at boot / on the live server.** Not imported by `server.ts`, any route, job, or
10
+ hook. No new endpoint, no config key, no `PostUpdateMigrator` entry. No new dependency
11
+ (pure logic over the existing `ParityResult` type).
12
+
13
+ ## Behavioral guarantees
14
+ - `gate()` clears ONLY when all four conditions hold: ≥ requiredCleanPasses consecutive clean
15
+ passes, clean window ≥ minWindowMs of real time, ≥ minClustersObserved clusters across the
16
+ streak, and zero divergence in the streak. Any divergent pass resets the streak.
17
+ - The monitor never mutates anything and never triggers cutover itself — it only computes a
18
+ cleared/blocked verdict that a downstream executor reads.
19
+ - Conservative failure mode: an over-strict policy keeps the gate BLOCKED longer (cutover
20
+ waits); it can never clear early.
21
+
22
+ ## Reversibility
23
+ - Fully reversible: delete `monitor/parityMonitor.ts` + its test. Nothing references it yet.
24
+
25
+ ## Follow-on wiring (not in this PR)
26
+ - The Coordination Mandate condition registry (G2.2) maps `parity-zero-divergence` →
27
+ `monitor.gate(now).cleared`. The Phase-4 cutover executor (G2.4) consults it before the flip.
28
+ - A durable wrapper (persist passes to JSONL so the window survives a restart) + a read-only
29
+ `/parity/status` route land with the cutover-executor wiring.
30
+
31
+ ## Durability (added)
32
+ - `parityMonitorStore.ts` — `DurableParityMonitor` + `JsonlPassPersistence`. The zero-divergence
33
+ window spans HOURS; without persistence a restart silently resets the streak. Passes are
34
+ appended to an append-only JSONL and reloaded on construction, so the window survives a
35
+ restart. Torn final line (crash mid-append) is skipped on reload, never corrupting the prior
36
+ window. Injectable `PassPersistence` keeps the logic unit-testable without disk. Still not
37
+ wired into the running server (the cutover executor constructs it with the live path).
@@ -0,0 +1,35 @@
1
+ # Side effects — Phase-4 feedback-migration integrity tooling
2
+
3
+ ## New files (additive only)
4
+ - `src/feedback-factory/migration/immutableGuard.ts` — `GuardedFeedbackStore` decorator +
5
+ `isClusterImmutable` / `hasGovernanceNotes` invariants.
6
+ - `src/feedback-factory/migration/importIntegrity.ts` — pure import-gate core (checksums,
7
+ fingerprint-uniqueness scan, schema-equivalence, referential integrity, sequence reset,
8
+ `runIntegrityGate`).
9
+ - `tests/unit/feedback-factory/immutable-guard.test.ts` (15 tests).
10
+ - `tests/unit/feedback-factory/import-integrity.test.ts` (21 tests).
11
+
12
+ ## Runtime impact
13
+ - **None at boot / on the live server.** These modules are NOT imported by `server.ts`, any
14
+ route, job, or hook. They are library building blocks the Phase-4 cutover execution will
15
+ call. No new endpoint, no new config key, no migration to `PostUpdateMigrator`.
16
+ - No new dependencies (uses `node:crypto` only).
17
+
18
+ ## Behavioral guarantees
19
+ - `GuardedFeedbackStore` only ever REFUSES a mutation of an immutable cluster (pre-cutover
20
+ createdAt or non-null governance note) and records a `GuardViolation`; it never alters
21
+ data and passes all reads + non-curated writes straight through to the inner store.
22
+ - A failing `onViolation` audit sink can never break the store or surface as a mutation
23
+ (try/caught, annotated `@silent-fallback-ok`).
24
+ - Checksums collapse null/undefined/"" so the import gate does not flap on the
25
+ null-vs-empty governance-note distinction (that distinction is asserted separately by
26
+ schema-equivalence).
27
+
28
+ ## Reversibility
29
+ - Fully reversible: delete the two `migration/` files + their tests. Nothing else
30
+ references them yet.
31
+
32
+ ## Follow-on wiring (not in this PR)
33
+ - Phase-4 cutover executor (G2.4) constructs the `GuardedFeedbackStore` around the real
34
+ Prisma-backed store with the cutover timestamp from config, and runs `runIntegrityGate`
35
+ over the export/import pair as the Phase-2 gate.
@@ -0,0 +1,81 @@
1
+ # Side-Effects Review — Respawn build-context re-establishment
2
+
3
+ **Version / slug:** `respawn-build-context-reestablishment`
4
+ **Date:** `2026-06-04`
5
+ **Author:** `instar-codey`
6
+ **Second-pass reviewer:** `instar-codey second-pass checklist`
7
+
8
+ ## Summary of the change
9
+
10
+ This change hardens the interactive SessionManager respawn path for developer build sessions. A new sidecar store records each running session's live tmux pane working directory, using the generic state layer's temp-file-plus-rename atomic write. When a resumed interactive session has a fresh saved cwd under an agent worktree, SessionManager prepends a `[BUILD-CONTEXT RESTORE]` continuation note to the resumed message. Normal home-only sessions, stale records, missing worktrees, and non-enabled agents receive no note.
11
+
12
+ The feature is dark by default for normal agents. Server boot resolves the session-level feature flag as an explicit override if present, otherwise the standard development-agent gate.
13
+
14
+ ## Decision-point inventory
15
+
16
+ - `SessionBuildContextStore` — add — owns sidecar state validation, crash-safe persistence through StateManager, cwd eligibility, staleness, missing-path checks, branch enrichment, and restore-note formatting.
17
+ - `SessionManager.monitorTick` — modified — records pane cwd after liveness is confirmed, without changing session lifecycle decisions.
18
+ - `SessionManager.spawnInteractiveSession` — modified — prepends a restore note only for resume spawns with an eligible saved worktree context.
19
+ - `server` startup wiring — modified — resolves the feature dark by default and live for development agents unless explicitly overridden.
20
+ - Session-management e2e — modified — proves a real tmux pane under a fixture worktree gets the restore note while a home-only control stays unchanged.
21
+
22
+ ## 1. Over-block
23
+
24
+ The restore can skip a legitimate build context if the tmux cwd is not under a `.worktrees` path. That is intentional. The spec's blast-radius guard is more important than restoring every possible hand-navigated directory. Agents can still recover manually from conversation context; the automated note is limited to the known build-worktree convention.
25
+
26
+ The restore can also skip when the sidecar is stale or the worktree directory was removed. That prevents a respawn from sending the agent back into a deleted or long-finished checkout.
27
+
28
+ ## 2. Under-block
29
+
30
+ The sidecar records cwd for running sessions when the feature is enabled, including home-only sessions. Home-only records are not eligible for restore, so this is additional local state but not additional behavior. The record contains local paths and an optional branch name; it does not include prompt content or credentials.
31
+
32
+ The branch name is best-effort enrichment. If Git cannot read the branch, the restore note still works with the worktree path alone. Failure to read branch never blocks monitoring or respawn.
33
+
34
+ ## 3. Level-of-abstraction fit
35
+
36
+ SessionManager owns tmux lifecycle, liveness checks, interactive respawn, and initial-message injection, so it is the correct place to sample pane cwd and prepend the resume note. The sidecar store keeps persistence and eligibility logic out of the large SessionManager class and makes the boundary unit-testable.
37
+
38
+ The change does not force tmux to spawn inside the worktree. That matches the design spec: the session home remains stable, and the resumed agent receives deterministic instruction to return to the build checkout only when appropriate.
39
+
40
+ ## 4. Signal vs authority compliance
41
+
42
+ Required reference: [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
43
+
44
+ - [x] Not applicable to conversational/product judgment — this is structural session lifecycle restoration.
45
+
46
+ The cwd sample is a mechanical tmux fact. The restore decision is a deterministic filesystem/scope guard: fresh, existing, different from spawn cwd, and under a worktree path. It does not judge user intent or message meaning.
47
+
48
+ ## 5. Interactions
49
+
50
+ - **Shadowing:** The restore note is prepended before the normal initial-message injection. Existing InputGuard and injection mechanics still handle the resulting message through the same path.
51
+ - **Double-fire:** The note is computed once per resumed spawn. Reusing an already-live tmux session does not inject a restore note.
52
+ - **Races:** A worktree can be removed between sidecar read and agent acting on the note. The respawn path checks existence immediately before injecting; after that, the note is advisory and the agent can report if the path disappears.
53
+ - **Feedback loops:** Monitoring persists only when cwd changes, avoiding a write every tick for steady sessions.
54
+
55
+ ## 6. External surfaces
56
+
57
+ The visible behavior is a new continuation preamble for eligible resumed developer sessions. The preamble is intentionally plain text and starts with `[BUILD-CONTEXT RESTORE]` so the agent can act on it deterministically. Normal topic sessions that remain in agent home see no new text.
58
+
59
+ The persistent surface is a local sidecar state record keyed by tmux session name. It stores spawn cwd, current cwd, optional branch, and update time.
60
+
61
+ ## 7. Rollback cost
62
+
63
+ Rollback is a code revert of the sidecar store, SessionManager wiring, server gate wiring, tests, and artifacts. The sidecar state file can remain harmlessly on disk; without the code path it is ignored. No migration or state repair is required.
64
+
65
+ ## Conclusion
66
+
67
+ The change addresses the respawn amnesia failure without changing normal spawn cwd, without broadening behavior for home-only sessions, and without relying on conversation memory to rediscover the checkout. The test set covers pure eligibility/staleness behavior, mocked tracker-to-respawn wiring, and a real tmux e2e with both worktree and home-only controls.
68
+
69
+ ## Second-pass review
70
+
71
+ **Reviewer:** `instar-codey separate pass`
72
+ **Independent read of the artifact:** concur
73
+
74
+ The risky surface is SessionManager. The implementation keeps the new behavior behind a feature gate, samples only after liveness is confirmed, writes through crash-safe state persistence, and injects only a continuation note for eligible resumed spawns. The scope guard is the core safety property and is covered in all three test tiers.
75
+
76
+ ## Evidence pointers
77
+
78
+ - `npm test -- --run tests/unit/session-build-context-store.test.ts tests/integration/session-build-context-respawn.test.ts` passed.
79
+ - `npm test -- --run tests/e2e/session-management-e2e.test.ts -t "restores a worktree build context"` passed.
80
+ - `npm test -- --run tests/unit/no-silent-fallbacks.test.ts tests/unit/session-build-context-store.test.ts` passed after marking best-effort branch enrichment as an intentional fail-open.
81
+ - `npm run lint` passed.