instar 1.3.309 → 1.3.311

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 (34) hide show
  1. package/dist/commands/init.d.ts.map +1 -1
  2. package/dist/commands/init.js +32 -0
  3. package/dist/commands/init.js.map +1 -1
  4. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  5. package/dist/config/ConfigDefaults.js +17 -0
  6. package/dist/config/ConfigDefaults.js.map +1 -1
  7. package/dist/core/BootSelfKnowledge.d.ts +121 -0
  8. package/dist/core/BootSelfKnowledge.d.ts.map +1 -0
  9. package/dist/core/BootSelfKnowledge.js +285 -0
  10. package/dist/core/BootSelfKnowledge.js.map +1 -0
  11. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  12. package/dist/core/PostUpdateMigrator.js +104 -0
  13. package/dist/core/PostUpdateMigrator.js.map +1 -1
  14. package/dist/core/types.d.ts +26 -0
  15. package/dist/core/types.d.ts.map +1 -1
  16. package/dist/core/types.js.map +1 -1
  17. package/dist/monitoring/delivery-failure-sentinel.d.ts.map +1 -1
  18. package/dist/monitoring/delivery-failure-sentinel.js +15 -0
  19. package/dist/monitoring/delivery-failure-sentinel.js.map +1 -1
  20. package/dist/scaffold/templates.d.ts.map +1 -1
  21. package/dist/scaffold/templates.js +8 -0
  22. package/dist/scaffold/templates.js.map +1 -1
  23. package/dist/server/routes.d.ts.map +1 -1
  24. package/dist/server/routes.js +135 -0
  25. package/dist/server/routes.js.map +1 -1
  26. package/package.json +1 -1
  27. package/src/data/builtin-manifest.json +72 -64
  28. package/src/scaffold/templates.ts +8 -0
  29. package/src/templates/scripts/secret-get.mjs +143 -0
  30. package/upgrades/1.3.310.md +20 -0
  31. package/upgrades/1.3.311.md +20 -0
  32. package/upgrades/sentinel-immediate-first-tick.eli16.md +7 -0
  33. package/upgrades/side-effects/sentinel-immediate-first-tick.md +38 -0
  34. package/upgrades/side-effects/session-boot-self-knowledge.md +78 -0
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * secret-get.mjs — read ONE secret value from the agent's encrypted SecretStore
4
+ * vault and stream it to stdout for piping. The retrieval affordance promised by
5
+ * the Session Boot Self-Knowledge block (docs/specs/session-boot-self-knowledge.md):
6
+ * a session that boots knowing `github_token` is in the vault uses THIS script to
7
+ * fetch it — never `node -e` ad-hoc reads, never asking the user to re-send it.
8
+ *
9
+ * Containment contract (sibling of secret-drop-retrieve.mjs, same rules):
10
+ * - The VALUE goes to stdout ONLY — single write, no trailing newline — so it
11
+ * can pipe straight into the consuming command without ever being echoed
12
+ * into a terminal, chat, or transcript.
13
+ * - ALL diagnostics go to stderr and are limited to key NAMES, lengths, and
14
+ * error categories — never values.
15
+ * - On ANY error, stdout receives ZERO value bytes (stderr-only, non-zero exit).
16
+ *
17
+ * Usage:
18
+ * node .instar/scripts/secret-get.mjs <keyPath>
19
+ * → prints the value to stdout. Pipe it: `... github_token | gh auth login --with-token`
20
+ *
21
+ * node .instar/scripts/secret-get.mjs --names
22
+ * → prints vault key paths + value lengths to stderr; nothing to stdout.
23
+ *
24
+ * node .instar/scripts/secret-get.mjs <keyPath> --run -- <cmd...>
25
+ * → runs <cmd> with the value piped to its stdin (atomic handoff — the value
26
+ * never touches a shell variable or the transcript). Exits with <cmd>'s code.
27
+ *
28
+ * Vault access:
29
+ * Loads the instar SecretStore implementation from the local install and reads
30
+ * the vault at `.instar/secrets/config.secrets.enc` relative to cwd (run from
31
+ * the agent home). Keychain-backed master key by default — the same read path
32
+ * the server uses.
33
+ *
34
+ * Exit codes:
35
+ * 0 — value printed (or --run command succeeded, or --names listed)
36
+ * 1 — key not found, vault absent/undecryptable, or --run command failed
37
+ * 2 — usage error (missing args, cannot resolve the instar dist)
38
+ */
39
+
40
+ import * as fs from 'node:fs';
41
+ import * as path from 'node:path';
42
+ import { spawnSync } from 'node:child_process';
43
+ import { createRequire } from 'node:module';
44
+
45
+ const args = process.argv.slice(2);
46
+
47
+ // `<keyPath> --run -- <cmd...>`: everything after the first `--` (which must
48
+ // follow `--run`) is the command to receive the value on stdin.
49
+ const runIdx = args.indexOf('--run');
50
+ const dashIdx = args.indexOf('--');
51
+ let runCmd = null;
52
+ if (runIdx !== -1) {
53
+ if (dashIdx === -1 || dashIdx < runIdx || dashIdx === args.length - 1) {
54
+ process.stderr.write('usage: secret-get.mjs <keyPath> --run -- <cmd...>\n');
55
+ process.exit(2);
56
+ }
57
+ runCmd = args.slice(dashIdx + 1);
58
+ }
59
+ const positional = (dashIdx === -1 ? args : args.slice(0, dashIdx)).filter((a) => !a.startsWith('--'));
60
+ const namesOnly = args.includes('--names');
61
+ const keyPath = positional[0];
62
+
63
+ if (!namesOnly && !keyPath) {
64
+ process.stderr.write('usage: secret-get.mjs <keyPath> | secret-get.mjs --names\n');
65
+ process.exit(2);
66
+ }
67
+
68
+ // Resolve the instar dist: deployed agents run from the shadow-install; the dev
69
+ // checkout falls back to its own dist. Never print resolution paths on success.
70
+ const require = createRequire(import.meta.url);
71
+ const candidates = [
72
+ path.resolve('.instar/shadow-install/node_modules/instar/dist/core/SecretStore.js'),
73
+ path.resolve('dist/core/SecretStore.js'),
74
+ ];
75
+ let SecretStore = null;
76
+ for (const c of candidates) {
77
+ if (fs.existsSync(c)) {
78
+ try {
79
+ ({ SecretStore } = require(c));
80
+ break;
81
+ } catch {
82
+ // try the next candidate; details are not value-bearing but stay off stdout
83
+ }
84
+ }
85
+ }
86
+ if (!SecretStore) {
87
+ process.stderr.write('secret-get: cannot resolve the instar SecretStore module (run from the agent home)\n');
88
+ process.exit(2);
89
+ }
90
+
91
+ const stateDir = path.resolve('.instar');
92
+ if (!fs.existsSync(path.join(stateDir, 'secrets', 'config.secrets.enc'))) {
93
+ process.stderr.write('secret-get: no vault on this machine (.instar/secrets/config.secrets.enc absent)\n');
94
+ process.exit(1);
95
+ }
96
+
97
+ let secrets;
98
+ try {
99
+ secrets = new SecretStore({ stateDir }).read();
100
+ } catch {
101
+ process.stderr.write(
102
+ 'secret-get: vault exists but could not be decrypted (master-key mismatch?). ' +
103
+ 'Do NOT repair/rotate/delete — surface to the operator.\n',
104
+ );
105
+ process.exit(1);
106
+ }
107
+
108
+ // Flatten to dot-notation leaves — names + lengths only, mirroring the vault's
109
+ // own get/set addressing. Values never leave this function except via stdout.
110
+ function leaves(obj, prefix = '') {
111
+ const out = [];
112
+ for (const [k, v] of Object.entries(obj)) {
113
+ const p = prefix ? `${prefix}.${k}` : k;
114
+ if (v && typeof v === 'object' && !Array.isArray(v)) out.push(...leaves(v, p));
115
+ else out.push([p, v]);
116
+ }
117
+ return out;
118
+ }
119
+ const entries = leaves(secrets);
120
+
121
+ if (namesOnly) {
122
+ for (const [name, value] of entries) {
123
+ process.stderr.write(`${name} (${String(value ?? '').length} chars)\n`);
124
+ }
125
+ if (entries.length === 0) process.stderr.write('(vault is empty)\n');
126
+ process.exit(0);
127
+ }
128
+
129
+ const hit = entries.find(([name]) => name === keyPath);
130
+ if (!hit) {
131
+ process.stderr.write(`secret-get: no key "${keyPath}" in the vault. Known keys:\n`);
132
+ for (const [name] of entries) process.stderr.write(` ${name}\n`);
133
+ process.exit(1);
134
+ }
135
+ const value = typeof hit[1] === 'string' ? hit[1] : JSON.stringify(hit[1]);
136
+
137
+ if (runCmd) {
138
+ const r = spawnSync(runCmd[0], runCmd.slice(1), { input: value, stdio: ['pipe', 'inherit', 'inherit'] });
139
+ process.exit(r.status ?? 1);
140
+ }
141
+
142
+ process.stdout.write(value);
143
+ process.exit(0);
@@ -0,0 +1,20 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ DeliveryFailureSentinel.start() performs an immediate awaited first drain (startup tick) before relying on the 5-minute watchdog interval. Watchdog cadence, event-driven ticks, per-topic pacing, backoff, and purge rules unchanged.
9
+
10
+ ## What to Tell Your User
11
+
12
+ Messages that queued up while your agent's server was restarting now deliver the moment it comes back, instead of waiting up to five extra minutes — which under back-to-back update restarts could mean never, as the next restart landed first.
13
+
14
+ ## Summary of New Capabilities
15
+
16
+ - A started delivery sentinel means a drained backlog: recovery no longer depends on the watchdog timer surviving the up-window.
17
+
18
+ ## Evidence
19
+
20
+ Live (2026-06-05 ~15:40-16:50Z, echo): a release-train restart cascade produced up-windows shorter than boot-time + the 5-min first tick; five queued operator messages survived FOUR up-windows undelivered while fresh sends in the same windows succeeded. Pinned by a new integration test (pre-existing http_code-0 backlog delivered by start() alone, no explicit tick) plus the re-pinned recovery happy path (start drains; subsequent tick finds zero) — 71/71 across the sentinel suites.
@@ -0,0 +1,20 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ New `src/core/BootSelfKnowledge.ts` (block builder: sanitization, depth-2 collapse, alphabetical + capped + byte-bounded rendering, mtime+size-keyed module cache); three routes in `routes.ts`; one fetch block in `getSessionStartHook()`; `selfKnowledge` config surface (defaults backfilled by `migrateConfig`, `enabled` left unset for the developmentAgent gate); CLAUDE.md template section + `migrateClaudeMd` parity; `secret-get.mjs` shipped via `migrateScripts` + init. Spec: `docs/specs/session-boot-self-knowledge.md` (converged, 3 iterations, cross-model codex-cli:gpt-5.5).
9
+
10
+ ## What to Tell Your User
11
+
12
+ Nothing user-visible yet — the feature ships dark on the fleet (live on the development agent for the bake). When the fleet flip lands it gets its own note: "your agent now remembers what credentials it holds across sessions — it won't ask you to re-send a key it already has."
13
+
14
+ ## Summary of New Capabilities
15
+
16
+ - `GET /self-knowledge/session-context` (Bearer; `enabled ?? !!developmentAgent`): a bounded, sanitized `<session-self-knowledge>` block with the agent's vault secret NAMES (never values; same `secretKeyPaths()` derivation as `/secrets/sync-status`, depth-capped) + self-asserted operational facts. `?full=1` bypasses the display caps. A vault that exists but won't decrypt is reported honestly as DECRYPT-FAILED with hands-off guidance — never as an empty vault.
17
+ - `POST/DELETE /self-knowledge/facts`: agent-driven writer for durable per-machine operational facts (auto-stamped `{fact, updatedAt, machine}`; duplicate/cap/ambiguity guarded; atomic temp+rename config write).
18
+ - `.instar/scripts/secret-get.mjs` (always-overwrite installed): hardened vault retrieval — value streams to stdout for piping straight into the consuming command, names/diagnostics to stderr, zero value bytes on any error path.
19
+ - The session-start hook injects the block at every boot (fail-open: dark/unreachable/version-skew → silent skip), placed after the org-intent and preferences blocks.
20
+ - Structural test guard: `MasterKeyManager` forces the file key under vitest — no test can ever read or overwrite the machine-global OS keychain master key again (the 2026-06-05 bifurcated-key incident class is closed).
@@ -0,0 +1,7 @@
1
+ # Queued messages now deliver the moment the server comes back — not five minutes later
2
+
3
+ The delivery recovery engine drains the queue of undelivered outgoing messages on a five-minute timer. The timer's FIRST firing is five minutes after the engine starts — and the engine restarts with the server. That was fine when restarts were rare. Today the fleet shipped ten-plus releases, the server restarted roughly every ten minutes, and each up-window between restarts was shorter than boot time plus five minutes. The result, observed live for over an hour: five queued messages to the operator — including the run summary he had just asked for — survived four separate up-windows undelivered, while freshly-sent messages in those same windows went through fine. The queue protected the messages perfectly (that part of the week's fixes worked) and then never got a chance to deliver them.
4
+
5
+ The fix is one step in the engine's startup: drain the queue immediately when the engine starts, before the timer ever matters. A booted engine now means a drained backlog — the user's queued messages go out the moment the server is reachable, instead of betting on the up-window outlasting a timer. The startup drain is awaited, so "started" deterministically means "backlog handled once," which also makes the behavior directly testable.
6
+
7
+ Nothing else changes: the five-minute watchdog, the event-driven kicks, per-topic pacing, retry backoff, and the loud-purge rules all behave exactly as before. Three integration tests pin the new contract — a pre-existing backlog of connection-refused messages (the cascade signature) is delivered by startup alone, with the existing recovery and mismatch paths re-pinned around it.
@@ -0,0 +1,38 @@
1
+ # Side-Effects Review — DeliveryFailureSentinel startup tick
2
+
3
+ **Version / slug:** `sentinel-immediate-first-tick`
4
+ **Date:** `2026-06-05`
5
+ **Author:** `instar-echo`
6
+
7
+ ## Summary
8
+
9
+ start() runs one awaited tick() after arming the watchdog (and after the existing one-shot restore-purge and event-listener registration). The tick is the same code path the watchdog and delivery_failed events already invoke; its own try/catch contains failures (logged, start() still completes).
10
+
11
+ ## Decision-point inventory
12
+
13
+ - start() — modified — awaited first drain; 'sentinel:started' now emitted AFTER the initial drain completes.
14
+ - tick(), watchdog cadence, event kicks, per-topic rate limit, backoff, stampede digest, purge — untouched.
15
+
16
+ ## Direction of failure
17
+
18
+ - Old: recovery waited ≥5 min after every boot; under restart cascades (up-window < boot + 5 min) the queue never drained — live 70+ min user-facing outage of QUEUED (safe but stuck) messages.
19
+ - New: backlog drains at boot. A failing startup tick logs and start() completes — degraded to exactly the old behavior (watchdog picks it up).
20
+ - Conservative direction: messages deliver sooner; nothing new is dropped or retried beyond existing budgets.
21
+
22
+ ## Side-effects checklist
23
+
24
+ 1. **Boot latency:** start() now includes one drain pass. Per-delivery I/O is already bounded (postReply timeouts); a large backlog is paced by the existing per-topic rate limit — residual items wait for the watchdog exactly as before. Boot-blocking risk bounded by the same budgets every tick obeys.
25
+ 2. **Ordering with restore-purge:** unchanged — purge (one-shot) still precedes the first drain, so the startup tick never delivers rows the purge policy retires.
26
+ 3. **Double-tick race:** the in-flight/lease machinery already serializes concurrent ticks (watchdog + event kicks coexist today); the startup tick is just one more entrant.
27
+ 4. **Test contract change:** "start() then explicit tick()" tests now see the backlog consumed by start — the recovery happy path was re-pinned to assert the NEW contract explicitly (start drains; next tick returns 0).
28
+ 5. **External surfaces:** none — no routes/config/schema.
29
+ 6. **Rollback cost:** revert the commit.
30
+
31
+ ## Scope not taken
32
+
33
+ - No watchdog-interval retune (5 min stays right for steady state).
34
+ - No restart-cascade dampener changes (separate system; its own review queued from today's observations).
35
+
36
+ ## Rollback
37
+
38
+ Revert. Recovery returns to first-drain-at-+5min.
@@ -0,0 +1,78 @@
1
+ # Side-Effects Review — Session Boot Self-Knowledge
2
+
3
+ **Version / slug:** `session-boot-self-knowledge`
4
+ **Date:** `2026-06-05`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `spec-converge multi-reviewer panel (3 rounds: security/adversarial/integration/scalability/lessons-aware internal + Standards-Conformance Gate + codex-cli:gpt-5.5 external each round)`
7
+
8
+ ## Summary of the change
9
+
10
+ Adds the boot self-knowledge block: a bounded, sanitized `<session-self-knowledge>` context block (vault secret NAMES — never values — plus self-asserted operational facts) built server-side by the new `src/core/BootSelfKnowledge.ts`, served by `GET /self-knowledge/session-context` (with `?full=1`), written by `POST/DELETE /self-knowledge/facts`, injected by a new fetch block in `getSessionStartHook()`, retrieved-from by the new hardened `secret-get.mjs` script, and configured by the new `InstarConfig.selfKnowledge` surface (defaults via `ConfigDefaults` + `migrateConfig`; CLAUDE.md template + `migrateClaudeMd`; script via `migrateScripts` + init). Includes the structural `MasterKeyManager` VITEST constructor guard. Files: `src/core/BootSelfKnowledge.ts` (new), `src/core/SecretStore.ts`, `src/server/routes.ts`, `src/core/PostUpdateMigrator.ts`, `src/core/types.ts`, `src/config/ConfigDefaults.ts`, `src/scaffold/templates.ts`, `src/commands/init.ts`, `src/templates/scripts/secret-get.mjs` (new), plus 4 test files, the spec + ELI16 + convergence report, and the release fragment. Spec: `docs/specs/session-boot-self-knowledge.md` (converged + approved).
11
+
12
+ ## Decision-point inventory
13
+
14
+ This change adds NO decision point with blocking authority — it is a pure signal producer (read-only context injection; per `docs/signal-vs-authority.md`). Conditionals it adds are availability switches and writer-input validation, not behavior gates:
15
+
16
+ - `GET /self-knowledge/session-context` enabled-resolution (`enabled ?? !!developmentAgent`) — add — availability switch (graduated rollout), not a behavior gate.
17
+ - Facts writer validation (400 empty/oversize; 409 duplicate/cap/ambiguous/expect-mismatch) — add — input validation on an agent-driven write surface.
18
+ - Pass-through: SecretStore read path (read-only), session-start hook (additive fetch block, fail-open), config write path (new atomic helper for one array).
19
+
20
+ ---
21
+
22
+ ## 1. Over-block
23
+
24
+ The enabled-resolution 503s the route on fleet agents (flag unset, `developmentAgent` false) — by design (graduated rollout), not an over-block: the hook fail-opens silently. The facts writer rejects: empty/oversize facts (legitimate long facts >256 chars must be split — accepted cost, keeps the boot block bounded), exact duplicates, adds past the 50-fact cap, ambiguous `match` deletes, and stale `index+expect` deletes. Each 4xx carries an actionable message. No legitimate session-context READ is ever rejected beyond auth + the availability switch. No other block/allow surface.
25
+
26
+ ## 2. Under-block
27
+
28
+ - A fact that is misleading-but-validly-shaped (≤256 chars, unique) is stored and injected every boot — mitigated by the self-asserted/unverified labeling, per-index render for one-call removal, and the per-serve audit line; residual risk accepted for v1 and explicitly watched during the bake (spec §Threat model).
29
+ - The last-writer-wins window between the facts writer and the pre-existing NON-atomic config writers (PATCH /config, telemetry) remains — bounded to the handler's microseconds by re-read-before-write, pinned by the interleaving migration test; accepted and documented.
30
+ - Names already written into past transcripts are not retroactively scrubbed if the feature is later disabled.
31
+
32
+ ## 3. Level-of-abstraction fit
33
+
34
+ Right layer. The names derivation stays in `secretKeyPaths()` (shared with `/secrets/sync-status` — no logic fork); the block-building/presentation is a new module rather than overloading sync-status (which 503s when secret-sync is dark — wrong availability semantics for a boot surface) or the SelfKnowledgeTree (LLM search over AGENT.md — different system, noted in code comments on both). The hook injection rides the existing org-intent/preferences pattern rather than inventing a new injection mechanism. Rejected alternatives (pull surfaces: /capabilities, MCP resources, memory files) are analyzed in the spec — the failure class is "agent doesn't know to look," which only push-at-boot removes.
35
+
36
+ ## 4. Signal vs authority compliance
37
+
38
+ Compliant — pure signal producer. The block is wrapped in an envelope that explicitly subordinates it to org-intent constraints, safety rules, and user instructions. The guidance line is signal-shaped ("retrieve rather than re-ask, unless you have evidence it is invalid") not absolute. A deterministic block on credential re-asks was considered and rejected as brittle-authority (spec §Why guidance stays a signal); the designed escalation if the bake shows non-compliance is a smart-gate signal feed, not a regex block. The VITEST keychain guard is a test-environment safety rail, not a runtime decision point.
39
+
40
+ ## 5. Interactions
41
+
42
+ - Coexists with the org-intent and preferences injections: placed after both in the hook (authoritative contract first); envelope states precedence. No shadowing — different routes, different envelopes.
43
+ - `/self-knowledge/*` namespace shared with the SelfKnowledgeTree routes (search/validate/health) — no path collision; comments mark which system serves which path.
44
+ - The names cache keys on the vault file path + (mtimeMs,size); secret-sync writes go through the same in-process server, so a peer-pushed secret invalidates the cache on its atomic write. No double-fire: the route is the only consumer.
45
+ - `migrateConfig`'s recursive add-missing merge interacts with operator-set `selfKnowledge` values — partial-override case pinned by test.
46
+ - The VITEST guard interacts with every existing test that constructs SecretStores — it can only make them SAFER (file-key instead of keychain); tests that explicitly pass `forceFileKey: true` (e.g. SecretMigrator's) are unchanged.
47
+
48
+ ## 6. External surfaces
49
+
50
+ - Vault key NAMES become visible in: the Bearer-gated route response, the agent's session context, and therefore on-disk session transcripts (which can travel further than vaults — debug bundles, provider retention; spec §Threat model). This is the feature's one genuinely new exposure and the reason it ships dark-fleet with the live flip as an explicit follow-up decision.
51
+ - No cross-agent or cross-machine surface changes: facts are per-machine (config doesn't sync); names reflect the local vault (which secret-sync may populate). Nothing here changes timing-sensitive behavior visible to other systems; the hook fetch is fail-open with `--max-time 4 --connect-timeout 1`.
52
+
53
+ ## 7. Rollback cost
54
+
55
+ Low. Per-agent: `selfKnowledge.sessionContext.enabled: false` (route 503s, hook silently skips — no restart needed; the flag is fresh-read). Fleet: revert the PR — no data formats change; the only state this feature writes is the `operationalFacts` array via explicit calls, which survives or is hand-removable. The VITEST guard's rollback is one constructor line. Names already in transcripts are the only non-revertible residue (documented).
56
+
57
+ ## Deferred / follow-ups (all tracked)
58
+
59
+ - Live-fleet flip (`enabled: true` in ConfigDefaults) — rides PR #800's merge or explicit approver direction (spec §Availability Resolution rule; the approver was asked directly in the approval request).
60
+ - Session-start hook's pre-existing uncapped sibling curls — framework-issues ledger `session-start-hook-uncapped-curls`.
61
+ - `/secrets/sync-status` rendering decrypt-failure as an empty vault — framework-issues ledger `sync-status-decrypt-fail-reads-empty`.
62
+ - Per-agent keychain accounts + key-id header + dual-key read fallback — pre-existing commitment from the 2026-06-05 incident (CMT lineage in topic 13481), unchanged by this PR.
63
+
64
+ ## Post-review fix round (fresh-eyes code review, 2026-06-05)
65
+
66
+ The independent code review of the feature commit found ONE real bug, fixed before PR: the names cache (keyed on the vault file) cached the `decrypt-failed` outcome — but a decrypt failure is almost always a MASTER-KEY problem (a separate file the cache key cannot see), so a recovered key kept serving the stale hands-off warning until a restart. Fix: only the healthy outcome is cached; a failed state is re-tried on every read (cheap relative to lying about recovery). Plus hardening: backticks are stripped from rendered names/facts (a hostile name can no longer break the inline-code span). Two regression tests added (decrypt-recovery-heals-without-restart; backtick-inertness).
67
+
68
+ ## CI-fix round (post-PR, 2026-06-05)
69
+
70
+ Three CI failures owned per Zero-Failure: (1) `ConversationStore.test.ts` time-bomb — the test anchored retirement at fixed `2026-05-30`, and its 25h-stale entries crossed the store's 7-day expiry on 2026-06-05 (main's last CI run squeaked under the boundary by an hour; this PR's run detonated it) — re-anchored at real now, semantics unchanged; (2) no-silent-fallbacks ratchet — the four new BootSelfKnowledge catch blocks annotated `@silent-fallback-ok` with per-catch justifications (never a baseline bump); (3) docs-coverage route floor — the three new routes documented in the site API reference + a new features page (which also satisfies the route ratchet for the existing tree routes' namespace).
71
+
72
+ ## Compaction-parity round (approver design review, 2026-06-05)
73
+
74
+ Justin's review surfaced the long-session gap: the block injected at session start survives compaction only if the summary carries it. Fixed in-PR: the compaction-recovery hook now carries the same fail-open fetch (re-injection after every compaction — refreshed, not merely preserved), with a Phase-3 e2e running the real compact-hook block against a live server. Collateral: org-intent + preferences share the boot-only gap (filed as `session-context-injectors-lack-compaction-parity`); a "Compaction Parity" constitution amendment is proposed separately. His scale concern is answered by the existing hard 2KB byte-cap (pointer-not-payload design); the AGGREGATE boot-budget concern across all injectors filed as `boot-context-aggregate-budget`.
75
+
76
+ ## Post-merge-conflict round (2026-06-05 AM)
77
+
78
+ Rebase onto the post-#848 main + CI surfaced the feature-delivery-completeness registry: the new CLAUDE.md section is now tracked in `featureSections` AND mirrored to the framework-shadow markers (`migrateFrameworkShadowCapabilities`) — Codex/Gemini agents learn the capability too (the Secret Drop lesson: an unshadowed capability gets improvised around). Local pre-push had skipped the smoke ("CI is the authority"), which is why CI caught it.