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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +9 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/coordination/MandateAudit.d.ts +48 -0
- package/dist/coordination/MandateAudit.d.ts.map +1 -0
- package/dist/coordination/MandateAudit.js +95 -0
- package/dist/coordination/MandateAudit.js.map +1 -0
- package/dist/coordination/MandateGate.d.ts +48 -0
- package/dist/coordination/MandateGate.d.ts.map +1 -0
- package/dist/coordination/MandateGate.js +104 -0
- package/dist/coordination/MandateGate.js.map +1 -0
- package/dist/coordination/MandateStore.d.ts +60 -0
- package/dist/coordination/MandateStore.d.ts.map +1 -0
- package/dist/coordination/MandateStore.js +106 -0
- package/dist/coordination/MandateStore.js.map +1 -0
- package/dist/coordination/conditions.d.ts +32 -0
- package/dist/coordination/conditions.d.ts.map +1 -0
- package/dist/coordination/conditions.js +54 -0
- package/dist/coordination/conditions.js.map +1 -0
- package/dist/coordination/types.d.ts +74 -0
- package/dist/coordination/types.d.ts.map +1 -0
- package/dist/coordination/types.js +17 -0
- package/dist/coordination/types.js.map +1 -0
- package/dist/core/ApprovalLedger.d.ts +132 -0
- package/dist/core/ApprovalLedger.d.ts.map +1 -0
- package/dist/core/ApprovalLedger.js +191 -0
- package/dist/core/ApprovalLedger.js.map +1 -0
- package/dist/core/SessionBuildContextStore.d.ts +33 -0
- package/dist/core/SessionBuildContextStore.d.ts.map +1 -0
- package/dist/core/SessionBuildContextStore.js +107 -0
- package/dist/core/SessionBuildContextStore.js.map +1 -0
- package/dist/core/SessionManager.d.ts +4 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +44 -3
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/types.d.ts +11 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/feedback-factory/migration/immutableGuard.d.ts +87 -0
- package/dist/feedback-factory/migration/immutableGuard.d.ts.map +1 -0
- package/dist/feedback-factory/migration/immutableGuard.js +163 -0
- package/dist/feedback-factory/migration/immutableGuard.js.map +1 -0
- package/dist/feedback-factory/migration/importIntegrity.d.ts +112 -0
- package/dist/feedback-factory/migration/importIntegrity.d.ts.map +1 -0
- package/dist/feedback-factory/migration/importIntegrity.js +202 -0
- package/dist/feedback-factory/migration/importIntegrity.js.map +1 -0
- package/dist/feedback-factory/monitor/parityMonitor.d.ts +75 -0
- package/dist/feedback-factory/monitor/parityMonitor.d.ts.map +1 -0
- package/dist/feedback-factory/monitor/parityMonitor.js +98 -0
- package/dist/feedback-factory/monitor/parityMonitor.js.map +1 -0
- package/dist/feedback-factory/monitor/parityMonitorStore.d.ts +44 -0
- package/dist/feedback-factory/monitor/parityMonitorStore.d.ts.map +1 -0
- package/dist/feedback-factory/monitor/parityMonitorStore.js +80 -0
- package/dist/feedback-factory/monitor/parityMonitorStore.js.map +1 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +11 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +6 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +94 -1
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +29 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +11 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +255 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +48 -48
- package/src/scaffold/templates.ts +11 -0
- package/upgrades/1.3.270.md +106 -0
- package/upgrades/approval-ledger-phase2.eli16.md +56 -0
- package/upgrades/feedback-phase3-monitor.eli16.md +51 -0
- package/upgrades/feedback-phase4-integrity.eli16.md +54 -0
- package/upgrades/respawn-build-context-reestablishment.eli16.md +5 -0
- package/upgrades/side-effects/approval-ledger-phase2.md +82 -0
- package/upgrades/side-effects/coordination-mandate-enforcement.md +86 -0
- package/upgrades/side-effects/feedback-phase3-monitor.md +37 -0
- package/upgrades/side-effects/feedback-phase4-integrity.md +35 -0
- 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.
|