instar 1.3.366 → 1.3.367

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 (60) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +200 -1
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  5. package/dist/config/ConfigDefaults.js +18 -0
  6. package/dist/config/ConfigDefaults.js.map +1 -1
  7. package/dist/core/MeshRpc.d.ts +8 -0
  8. package/dist/core/MeshRpc.d.ts.map +1 -1
  9. package/dist/core/MeshRpc.js +6 -1
  10. package/dist/core/MeshRpc.js.map +1 -1
  11. package/dist/core/PeerPresencePuller.d.ts +6 -0
  12. package/dist/core/PeerPresencePuller.d.ts.map +1 -1
  13. package/dist/core/PeerPresencePuller.js +5 -0
  14. package/dist/core/PeerPresencePuller.js.map +1 -1
  15. package/dist/core/PeerVisibilityGuard.d.ts +83 -0
  16. package/dist/core/PeerVisibilityGuard.d.ts.map +1 -0
  17. package/dist/core/PeerVisibilityGuard.js +204 -0
  18. package/dist/core/PeerVisibilityGuard.js.map +1 -0
  19. package/dist/core/PendingPullLedger.d.ts +129 -0
  20. package/dist/core/PendingPullLedger.d.ts.map +1 -0
  21. package/dist/core/PendingPullLedger.js +239 -0
  22. package/dist/core/PendingPullLedger.js.map +1 -0
  23. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  24. package/dist/core/PostUpdateMigrator.js +20 -0
  25. package/dist/core/PostUpdateMigrator.js.map +1 -1
  26. package/dist/core/WorkingSetManifest.d.ts +2 -0
  27. package/dist/core/WorkingSetManifest.d.ts.map +1 -1
  28. package/dist/core/WorkingSetManifest.js +13 -1
  29. package/dist/core/WorkingSetManifest.js.map +1 -1
  30. package/dist/core/WorkingSetPull.d.ts +157 -0
  31. package/dist/core/WorkingSetPull.d.ts.map +1 -0
  32. package/dist/core/WorkingSetPull.js +546 -0
  33. package/dist/core/WorkingSetPull.js.map +1 -0
  34. package/dist/core/WorkingSetPullCoordinator.d.ts +134 -0
  35. package/dist/core/WorkingSetPullCoordinator.d.ts.map +1 -0
  36. package/dist/core/WorkingSetPullCoordinator.js +306 -0
  37. package/dist/core/WorkingSetPullCoordinator.js.map +1 -0
  38. package/dist/core/types.d.ts +26 -1
  39. package/dist/core/types.d.ts.map +1 -1
  40. package/dist/core/types.js.map +1 -1
  41. package/dist/scaffold/templates.d.ts.map +1 -1
  42. package/dist/scaffold/templates.js +4 -0
  43. package/dist/scaffold/templates.js.map +1 -1
  44. package/dist/server/AgentServer.d.ts +3 -0
  45. package/dist/server/AgentServer.d.ts.map +1 -1
  46. package/dist/server/AgentServer.js +1 -0
  47. package/dist/server/AgentServer.js.map +1 -1
  48. package/dist/server/routes.d.ts +5 -0
  49. package/dist/server/routes.d.ts.map +1 -1
  50. package/dist/server/routes.js +24 -0
  51. package/dist/server/routes.js.map +1 -1
  52. package/package.json +1 -1
  53. package/src/data/builtin-manifest.json +64 -64
  54. package/src/data/state-coherence-registry.json +48 -12
  55. package/src/scaffold/templates.ts +4 -0
  56. package/upgrades/1.3.367.md +134 -0
  57. package/upgrades/side-effects/working-set-ci-absorb.md +29 -0
  58. package/upgrades/side-effects/working-set-parity-absorb.md +35 -0
  59. package/upgrades/side-effects/working-set-pull-p22a.md +121 -0
  60. package/upgrades/side-effects/working-set-trigger-p22b.md +115 -0
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema-note": "Machine form of docs/specs/STATE-COHERENCE-REGISTRY.md (the approved census, census-date 2026-06-05). AUTHORED, NOT build-generated (committed-generated-manifest conflict-loop lesson, COHERENCE-JOURNAL-SPEC §3.6). One entry per durable state category the census enumerates. Consumed by scripts/lint-state-registry.js: every direct durable-write site targeting a state dir / .instar/ must match an entry's `paths` (or carry an inline `/* state-registry: <category> */` annotation). Fields: category (kebab-case id) | description | scope (machine-local | must-be-coherent | derived-cache | coherent-on-demand) | freshness (real-time | eventual | session-boundary | n/a) | conflictShape (single-writer | append-only | lww | n/a) | transport (git | peer-http | none | deferred) | paths (glob-ish hints) | grandfathered (census marked uncertainty, §4e). New state declares its class here at birth.",
2
+ "$schema-note": "Machine form of docs/specs/STATE-COHERENCE-REGISTRY.md (the approved census, census-date 2026-06-05). AUTHORED, NOT build-generated (committed-generated-manifest conflict-loop lesson, COHERENCE-JOURNAL-SPEC \u00a73.6). One entry per durable state category the census enumerates. Consumed by scripts/lint-state-registry.js: every direct durable-write site targeting a state dir / .instar/ must match an entry's `paths` (or carry an inline `/* state-registry: <category> */` annotation). Fields: category (kebab-case id) | description | scope (machine-local | must-be-coherent | derived-cache | coherent-on-demand) | freshness (real-time | eventual | session-boundary | n/a) | conflictShape (single-writer | append-only | lww | n/a) | transport (git | peer-http | none | deferred) | paths (glob-ish hints) | grandfathered (census marked uncertainty, \u00a74e). New state declares its class here at birth.",
3
3
  "version": 1,
4
4
  "censusDate": "2026-06-05",
5
5
  "sourceDoc": "docs/specs/STATE-COHERENCE-REGISTRY.md",
@@ -16,6 +16,42 @@
16
16
  ],
17
17
  "grandfathered": false
18
18
  },
19
+ {
20
+ "category": "pending-pulls",
21
+ "description": "Durable working-set pending-pull ledger - outstanding fetches whose producer was offline/unreachable/revoked/mid-run; re-fired on reappearance/run-stopped (WORKING-SET-HANDOFF-SPEC P2 \u00a73.4). Machine-local: records describe THIS machine's outstanding fetches; single-writer serialized mutate() funnel.",
22
+ "scope": "machine-local",
23
+ "freshness": "live",
24
+ "conflictShape": "single-writer",
25
+ "transport": "none",
26
+ "paths": [
27
+ "state/coherence-journal/pending-pulls.json"
28
+ ],
29
+ "grandfathered": false
30
+ },
31
+ {
32
+ "category": "pull-opkeys",
33
+ "description": "Durable (topic,epoch) operation-key window for working-set pull dedupe - restart-proof at-most-once scheduling per move (WORKING-SET-HANDOFF-SPEC P2 \u00a73.3). Machine-local, bounded window (200 keys), single-writer (the coordinator).",
34
+ "scope": "machine-local",
35
+ "freshness": "live",
36
+ "conflictShape": "single-writer",
37
+ "transport": "none",
38
+ "paths": [
39
+ "state/coherence-journal/pull-opkeys.json"
40
+ ],
41
+ "grandfathered": false
42
+ },
43
+ {
44
+ "category": "visibility-guard",
45
+ "description": "Peer-visibility guard dedupe state - which improper revocations were already surfaced (keyed on revokedAt, cross-boot) + per-machine disappearance episodes for flap bounding (WORKING-SET-HANDOFF-SPEC P2 \u00a73.6). Machine-local notice hygiene, never authority.",
46
+ "scope": "machine-local",
47
+ "freshness": "live",
48
+ "conflictShape": "single-writer",
49
+ "transport": "none",
50
+ "paths": [
51
+ "state/coherence-journal/visibility-guard.json"
52
+ ],
53
+ "grandfathered": false
54
+ },
19
55
  {
20
56
  "category": "commitments",
21
57
  "description": "CommitmentTracker single-writer CAS store of user-facing promises and follow-through state.",
@@ -138,7 +174,7 @@
138
174
  },
139
175
  {
140
176
  "category": "job-definitions",
141
- "description": "Declarative job definitions (cron / prompt / supervision). The coherent half of jobs.json plus markdown job specs; run state is machine-local (see job-run-state). Field split required before whole-file sync (census §5).",
177
+ "description": "Declarative job definitions (cron / prompt / supervision). The coherent half of jobs.json plus markdown job specs; run state is machine-local (see job-run-state). Field split required before whole-file sync (census \u00a75).",
142
178
  "scope": "must-be-coherent",
143
179
  "freshness": "eventual",
144
180
  "conflictShape": "lww",
@@ -236,7 +272,7 @@
236
272
  },
237
273
  {
238
274
  "category": "fleet-config",
239
- "description": "Fleet-wide config: feature flags, messaging[] config, monitoring tunables, multiMachine policy. SHARES config.json with machine-local fields (port, paths) -- split-required before whole-file sync (census §5).",
275
+ "description": "Fleet-wide config: feature flags, messaging[] config, monitoring tunables, multiMachine policy. SHARES config.json with machine-local fields (port, paths) -- split-required before whole-file sync (census \u00a75).",
240
276
  "scope": "must-be-coherent",
241
277
  "freshness": "eventual",
242
278
  "conflictShape": "lww",
@@ -453,7 +489,7 @@
453
489
  },
454
490
  {
455
491
  "category": "audit-destructive-ops",
456
- "description": "Destructive-ops audit stream (411M live -- needs rotation, census §7). Append-only per-machine.",
492
+ "description": "Destructive-ops audit stream (411M live -- needs rotation, census \u00a77). Append-only per-machine.",
457
493
  "scope": "machine-local",
458
494
  "freshness": "n/a",
459
495
  "conflictShape": "append-only",
@@ -698,7 +734,7 @@
698
734
  },
699
735
  {
700
736
  "category": "derived-semantic-memory",
701
- "description": "semantic.db / memory.db / episodes -- rebuildable from evidence. (Live-disk: stale since Jun 1 -- investigate separately, census §1.6/§7.)",
737
+ "description": "semantic.db / memory.db / episodes -- rebuildable from evidence. (Live-disk: stale since Jun 1 -- investigate separately, census \u00a71.6/\u00a77.)",
702
738
  "scope": "derived-cache",
703
739
  "freshness": "eventual",
704
740
  "conflictShape": "n/a",
@@ -801,7 +837,7 @@
801
837
  },
802
838
  {
803
839
  "category": "uncertain-instructions-tracking",
804
- "description": "state/instructions-tracking/ (59M, 15,180 UUID-keyed JSONL files). Census did not map an owner; volume says it matters. Owner + rotation + class needed (census §4e).",
840
+ "description": "state/instructions-tracking/ (59M, 15,180 UUID-keyed JSONL files). Census did not map an owner; volume says it matters. Owner + rotation + class needed (census \u00a74e).",
805
841
  "scope": "machine-local",
806
842
  "freshness": "eventual",
807
843
  "conflictShape": "append-only",
@@ -813,7 +849,7 @@
813
849
  },
814
850
  {
815
851
  "category": "uncertain-topic-intent-store",
816
- "description": "TopicIntentStore. Code references it; on-disk location unverified (census §4e).",
852
+ "description": "TopicIntentStore. Code references it; on-disk location unverified (census \u00a74e).",
817
853
  "scope": "derived-cache",
818
854
  "freshness": "eventual",
819
855
  "conflictShape": "n/a",
@@ -826,7 +862,7 @@
826
862
  },
827
863
  {
828
864
  "category": "uncertain-shared-state-ledger",
829
- "description": "shared-state.jsonl (SharedStateLedger). Nominally the git-synced cross-machine ledger -- is anything consuming it on real machines? May be the vestigial ancestor of the P1 journal (census §4e).",
865
+ "description": "shared-state.jsonl (SharedStateLedger). Nominally the git-synced cross-machine ledger -- is anything consuming it on real machines? May be the vestigial ancestor of the P1 journal (census \u00a74e).",
830
866
  "scope": "must-be-coherent",
831
867
  "freshness": "eventual",
832
868
  "conflictShape": "append-only",
@@ -839,7 +875,7 @@
839
875
  },
840
876
  {
841
877
  "category": "uncertain-correction-capture-backlog",
842
- "description": "correction-capture-backlog.db vs correction-ledger.db: two stores, one sentinel -- which is canonical? (census §4e).",
878
+ "description": "correction-capture-backlog.db vs correction-ledger.db: two stores, one sentinel -- which is canonical? (census \u00a74e).",
843
879
  "scope": "machine-local",
844
880
  "freshness": "eventual",
845
881
  "conflictShape": "append-only",
@@ -851,7 +887,7 @@
851
887
  },
852
888
  {
853
889
  "category": "uncertain-project-round-worktrees",
854
- "description": "ProjectRoundWorktrees / TaskFlow stores. Routes-managed; durable form unverified (census §4e).",
890
+ "description": "ProjectRoundWorktrees / TaskFlow stores. Routes-managed; durable form unverified (census \u00a74e).",
855
891
  "scope": "machine-local",
856
892
  "freshness": "eventual",
857
893
  "conflictShape": "single-writer",
@@ -864,7 +900,7 @@
864
900
  },
865
901
  {
866
902
  "category": "legacy-singletons",
867
- "description": "Stale legacy singletons (anti-patterns.json, quick-facts.json, project-registry.json, May 20 vintage, no live writers found). Retire per census §7.",
903
+ "description": "Stale legacy singletons (anti-patterns.json, quick-facts.json, project-registry.json, May 20 vintage, no live writers found). Retire per census \u00a77.",
868
904
  "scope": "machine-local",
869
905
  "freshness": "n/a",
870
906
  "conflictShape": "single-writer",
@@ -877,4 +913,4 @@
877
913
  "grandfathered": true
878
914
  }
879
915
  ]
880
- }
916
+ }
@@ -425,6 +425,10 @@ This routes feedback to the Instar maintainers automatically. Valid types: \`bug
425
425
  - Degraded mode: the streams are plain JSONL under \`state/coherence-journal/\` — readable directly when HTTP is starved.
426
426
  - Proactive: user asks "which machine was topic N on?" / "why did this conversation move?" / "where are the overnight run's files?" / "did the old machine close its session after the move?" → GET /coherence/journal with the topic filter and answer from the entries.
427
427
 
428
+ **Working-Set Handoff (fetch a topic's files from the machine that made them)** — When a conversation moves between my machines, its working files follow automatically (the journal nominates which machines produced artifacts; the receiving machine pulls them in verified 1MB slices; nothing is ever overwritten — a divergent local file keeps its place and the incoming copy lands alongside it). If the producer machine is offline, the request is written down durably and fires the moment it returns.
429
+ - The fetch reflex: \`curl -X POST -H "Authorization: Bearer $AUTH" http://localhost:${port}/coherence/fetch-working-set -H 'Content-Type: application/json' -d '{"topic":N}'\` → \`{ scheduled, reports: [{ nominee, report }] }\` (503 = the working-set layer is dark on this agent; 429 = rate-limited, a pull is already running or just ran).
430
+ - **When to use** (PROACTIVE — this is the trigger): the user references files/work/analysis from this topic that are NOT on this machine ("where's the overnight analysis?", "you did this on the other machine") → fire the reflex, then answer from the landed files. Files flagged as containing credentials, still-being-written, or oversized are refused with named reasons in the report — explain honestly rather than retrying.
431
+
428
432
  **🩺 Agent Health lane (calm self-health notices)** — Routine notices about MY OWN internal state (a session that looks stuck, a peer I can't reach) land in ONE calm, named "🩺 Agent Health" Telegram topic — never topic-after-topic. Each is normal-priority (not a user-critical alert), names the topic in plain language (e.g. "the 'EXO 3.0' session", never \`topic-19077\`), ends with a next step you can just reply to, and same-session re-escalations are de-duped so the lane stays quiet. Ships **default-on, no config** (it's a delivery-shaper in code — it never gates or drops anything; every notice is still in the attention store). Tune via \`messaging[].config.agentHealthLane\` = \`{ "enabled": true, "topicName": "🩺 Agent Health", "dedupWindowMs": 1800000 }\` (set \`enabled:false\` for the old per-item-topic behavior). Proactive: user asks "what's this Agent Health topic?" / "why are my stale-session notices grouped?" → explain the calm lane (the StaleSessionBackstop now routes its "looks stuck" heads-up here at normal priority instead of spawning a topic each time).
429
433
 
430
434
  **Applying config & hook changes to running sessions** — A running session keeps the config it was *spawned* with. Claude Code loads \`.claude/settings.json\` (hooks, model) **once, at session start** — so a config change (default model, a disabled feature) or a newly-added hook does NOT reach an already-running session. It only takes effect on the next session, OR when you restart the existing one. (This is why a UserPromptSubmit hook added mid-session never fires for that live session — the session was launched before the hook existed.)
@@ -0,0 +1,134 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Second build slice of the Working-Set Handoff spec (P2 of multi-machine
9
+ coherence): the chunked `working-set-pull` mesh verb (both sides), the
10
+ durable pending-pull ledger, and never-clobber landing. Nothing schedules a
11
+ pull yet — the receiver trigger, reflex route, and staggered drain are
12
+ P2.2b. Dark by default: the serve side constructs only where
13
+ `multiMachine.coherenceJournal.replication.enabled === true`.
14
+
15
+ - `working-set-pull` verb (three lockstep edits mirroring journal-sync):
16
+ fresh-manifest-as-allowlist serving, O_NOFOLLOW + fd-identity TOCTOU
17
+ defense, 1 MiB chunked responses (the documented event-loop-starvation
18
+ root cause stays dead), offset-0 whole-file sha256 + cheap fstat
19
+ generation anchor so a file rewritten mid-transfer can never assemble
20
+ into a chimera — bounded restart-from-0, then one honest `unstable`.
21
+ - `WorkingSetPuller`: sequential offset cursors with per-tick yields,
22
+ busy-is-retry-without-penalty (a just-woke producer's throttle never
23
+ exhausts recovery records), assembled-bytes budget (discarded restart
24
+ chunks don't count), hostile-relPath jail, and never-clobber landing —
25
+ divergent local files keep their place; the incoming copy lands ALONGSIDE
26
+ (hash-suffixed, capped at 2, eviction through SafeFsExecutor).
27
+ - `PendingPullLedger`: the durable EXO-case record. Single-writer
28
+ serialized mutate() funnel (the exact lost-update shape that caused
29
+ topic-flood #3, closed at birth), corrupt-parse quarantine that is NEVER
30
+ silently read as empty, TTL sweep with one honest expiry notice,
31
+ supersession clearing ALL lower-epoch records across nominees, and the
32
+ (peer,topic,epoch) attempt-cap breaker.
33
+ - Config: `coherenceJournal.workingSet` block (12 bounded-behavior dials,
34
+ no enable flag — the feature rides the explicit replication gate).
35
+ - State-Coherence Registry: new `pending-pulls` category (machine-local,
36
+ single-writer).
37
+
38
+ Final build slice of the Working-Set Handoff spec (P2 of multi-machine
39
+ coherence): the transfer machinery from P2.2a now FIRES — on a topic move,
40
+ on demand, and on a returning producer. Still dark everywhere except
41
+ explicitly replication-enabled pairs.
42
+
43
+ - `WorkingSetPullCoordinator`: one pipeline behind three triggers — the
44
+ receiver's deliverMessage onAccepted seam (the moment this machine knows
45
+ it owns the topic), the fetch reflex, and the returning-peer staggered
46
+ drain (one pull at a time against a just-woke machine, never a mob).
47
+ Restart-proof (topic,epoch) dedupe; nomination from journal evidence
48
+ (every machine that actually produced artifacts, capped at 3 with the
49
+ excess NAMED); busy responses never burn recovery budget; stale-owner
50
+ and superseded records clear themselves.
51
+ - `POST /coherence/fetch-working-set` — the reflex: "who made artifacts
52
+ for this topic? go get them." The EXO failure as a one-call recovery.
53
+ 503 while dark, 429 when rate-limited, coalesces into a running pull.
54
+ - `PeerVisibilityGuard` (§3.6 rider, earned from the Mini's 10 invisible
55
+ hours): improper revocations (revokedAt with no who-or-why) surface once
56
+ across boots; a machine missing past a 30-min grace produces ONE notice
57
+ naming any topic workspaces stranded on it; flapping collapses to a
58
+ single notice. All through the agent-health attention lane.
59
+ - `PeerPresencePuller.onPeerRecorded` — the re-arm rides the existing 30s
60
+ presence cadence; no new polling loop.
61
+ - Agent Awareness + Migration Parity: the CLAUDE.md template (and
62
+ `migrateClaudeMd` for existing agents) gains the fetch-reflex entry with
63
+ the proactive trigger: user references files/work not on this machine →
64
+ fire the reflex, answer from the landed files.
65
+ - State-Coherence Registry: `pull-opkeys` + `visibility-guard` categories
66
+ (69 total).
67
+
68
+ ## What to Tell Your User
69
+
70
+ Nothing user-visible yet — this is the transfer machinery for "moving a
71
+ conversation between your machines moves its working files too." The piece
72
+ that actually fires it on a move (and the on-demand "go fetch that
73
+ workspace" reflex) lands in the next slice.
74
+
75
+ On machine pairs with diary sync enabled: moving a conversation between
76
+ your machines now moves its working files too — automatically, with
77
+ nothing ever overwritten (a conflicting local copy keeps its place; the
78
+ incoming version lands alongside it). If the machine holding the files is
79
+ asleep when they're wanted, the request is written down and fires the
80
+ moment it returns — and you can always ask me to fetch a topic's workspace
81
+ on demand. Everywhere else: nothing changes yet.
82
+
83
+ ## Summary of New Capabilities
84
+
85
+ - `working-set-pull` MeshCommand — chunked, bounded, fresh-manifest-gated
86
+ file serving between registered same-operator machines (dark until
87
+ replication is explicitly enabled).
88
+ - `WorkingSetPullServer` / `WorkingSetPuller` (`src/core/WorkingSetPull.ts`)
89
+ — the serve/receive engine with generation-anchor consistency, busy
90
+ throttling, and never-clobber landing.
91
+ - `PendingPullLedger` (`src/core/PendingPullLedger.ts`) — durable,
92
+ single-writer pending-pull records surviving restarts; the offline-
93
+ producer case re-fires instead of dying at a breaker.
94
+ - `coherenceJournal.workingSet` config defaults (ConfigDefaults +
95
+ CoherenceJournalUserConfig).
96
+
97
+ - `POST /coherence/fetch-working-set {topic}` — on-demand workspace fetch
98
+ (Bearer; 503 dark / 429 rate-limited / 200 outcome with per-nominee
99
+ reports).
100
+ - `WorkingSetPullCoordinator` (`src/core/WorkingSetPullCoordinator.ts`) —
101
+ move-trigger + reflex + staggered-drain orchestration.
102
+ - `PeerVisibilityGuard` / `detectImproperRevocations`
103
+ (`src/core/PeerVisibilityGuard.ts`) — mesh-health hygiene notices.
104
+ - CLAUDE.md proactive trigger for cross-machine file references (new
105
+ agents via template, existing agents via migrateClaudeMd).
106
+
107
+ ## Evidence
108
+
109
+ - `tests/unit/WorkingSetPull.test.ts` — 16 passing (anchor restart lands
110
+ the new content; never-sits-still → `unstable` with nothing landed; busy
111
+ exhaustion re-files without penalty; alongside cap-2 eviction; malicious
112
+ manifest relPaths refused; ownership-recheck abort; assembled-bytes
113
+ budget; serve refusal matrix + TOCTOU symlink swap).
114
+ - `tests/unit/PendingPullLedger.test.ts` — 13 passing (concurrent mutate()
115
+ drops no record; corrupt → quarantine + one notice, never silent-empty;
116
+ supersede clears all nominees; breaker + new-epoch reset; TTL once).
117
+ - `tests/integration/working-set-pull-roundtrip.test.ts` — 4 passing
118
+ (1.5MB near-cap chunked round-trip through the REAL express.json +
119
+ signed MeshRpcClient path, independent content oracle; full-stack
120
+ refusals; divergent-alongside; mixed-version 501 back-off).
121
+ - Adjacent mesh/journal suites unchanged (78 total across 8 suites);
122
+ typecheck + lint chain clean.
123
+
124
+ - `tests/e2e/working-set-handoff-lifecycle.test.ts` — 2 passing,
125
+ production-shaped (REAL deliverMessage handler + signed dispatcher +
126
+ journal streams): a move lands the file (source-disk oracle; re-delivery
127
+ + op-key dedupe leave it untouched), and THE EXO CASE end-to-end —
128
+ producer offline at move → pending-pull survives a restart → re-fires on
129
+ return → file lands.
130
+ - `tests/unit/WorkingSetPullCoordinator.test.ts` (12) +
131
+ `tests/unit/PeerVisibilityGuard.test.ts` (5) +
132
+ `tests/integration/working-set-reflex-route.test.ts` (2).
133
+ - Full P2 sweep: 95 tests / 11 suites green; typecheck, lint chain,
134
+ docs-coverage clean.
@@ -0,0 +1,29 @@
1
+ # Side-Effects Review — CI absorb: onAccepted wiring window + silent-fallbacks baseline
2
+
3
+ **Version / slug:** `working-set-ci-absorb`
4
+ **Date:** `2026-06-06`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required (test-shape + comment-trim fix; 7 LOC of src change is comment-only)`
7
+
8
+ ## Summary of the change
9
+
10
+ Two CI gates legitimately flagged the P2.2b onAccepted insertion:
11
+ 1. `session-pool-activation-wiring` slices 4200 chars from `onAccepted: (cmd) => {`
12
+ and asserts the owner-resume bridge markers inside it — the working-set
13
+ trigger prefix pushed them out. Fix: comment trimmed (6 lines → 2) and the
14
+ window widened 4200→5000 with a note naming why.
15
+ 2. `no-silent-fallbacks` baseline: +2 parser-counted defensive catches from the
16
+ transfer machinery (fd-close guard, vanished-file path) — both
17
+ in-brace-justified, both surface as COUNTED outcomes. Baseline 459→461 with
18
+ the documented justification block (the established ratchet format).
19
+
20
+ ## 1. Over-block / 2. Under-block / 3. Fit / 4. Blast radius
21
+
22
+ No behavior change: the src edit is comment-only; the test edits keep both
23
+ gates' intent (the wiring markers must exist; the baseline only ever ratchets
24
+ with written justification). Blast radius zero at runtime.
25
+
26
+ ## Evidence
27
+
28
+ - `tests/unit/session-pool-activation-wiring.test.ts` + `tests/unit/no-silent-fallbacks.test.ts` — 12 passing.
29
+ - e2e + coordinator suites re-run green (14). Typecheck + lint clean.
@@ -0,0 +1,35 @@
1
+ # Side-Effects Review — working-set parity absorb (presence-wiring window + delivery-completeness tracking)
2
+
3
+ **Version / slug:** `working-set-parity-absorb`
4
+ **Date:** `2026-06-06`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required (dep reorder + capability-tracking registration; zero behavior change)`
7
+
8
+ ## Summary of the change
9
+
10
+ Two more CI gates legitimately flagged P2.2b integration points:
11
+ 1. `peer-presence-wiring` reads a 900-char window after the PeerPresencePuller
12
+ constructor and asserts the signed `session-status` call inside it — my
13
+ `onPeerRecorded` dep (with comment block) pushed it out. Fix: the dep moved
14
+ to one compact line AFTER `log` (object-literal order is semantically
15
+ irrelevant), keeping the window intact without touching the test.
16
+ 2. `feature-delivery-completeness` requires every migrator-added CLAUDE.md
17
+ section to be (a) tracked in featureSections and (b) mirrored in the
18
+ `migrateFrameworkShadowCapabilities` markers[] so Codex/Gemini agents learn
19
+ it too. Both registrations added for the Working-Set Handoff section — a
20
+ shadow-framework agent that never learns the fetch reflex would tell the
21
+ user the files "aren't on this machine" (the EXO failure surviving on
22
+ shadows only).
23
+
24
+ ## 1-4. Over/Under-block, fit, blast radius
25
+
26
+ Zero runtime behavior change: a dep reorder inside one object literal, a test
27
+ registry entry, and a shadow-marker string. The shadow marker causes the
28
+ migrator to mirror the (already-shipped) section into Codex/Gemini shadow
29
+ files on the next update — exactly the parity the gate exists to enforce.
30
+
31
+ ## Evidence
32
+
33
+ - `tests/unit/peer-presence-wiring.test.ts` 4 passing;
34
+ `tests/unit/feature-delivery-completeness.test.ts` 79 passing;
35
+ `tests/unit/session-pool-activation-wiring.test.ts` 8 passing. Typecheck clean.
@@ -0,0 +1,121 @@
1
+ # Side-Effects Review — Working-Set Pull verb + pending-pull ledger (P2.2a)
2
+
3
+ **Version / slug:** `working-set-pull-p22a`
4
+ **Date:** `2026-06-06`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required (spec converged over 4 rounds with 4 internal lenses + cross-model; this implements §3.2/§3.4/§3.5 as written; serve side gated dark)`
7
+
8
+ ## Summary of the change
9
+
10
+ P2.2a of WORKING-SET-HANDOFF-SPEC: the chunked `working-set-pull` mesh verb
11
+ (both sides), the durable pending-pull ledger, and never-clobber landing.
12
+
13
+ 1. `src/core/WorkingSetPull.ts` — `WorkingSetPullServer` (serve: fresh
14
+ manifest as allowlist, O_NOFOLLOW + fd-identity TOCTOU defense, 1 MiB
15
+ chunked responses, offset-0 whole-file sha256 + cheap fstat generation
16
+ anchor, serveConcurrency busy gate) and `WorkingSetPuller` (receive:
17
+ sequential offset cursors with chunksPerTick yields, anchor-mismatch
18
+ restart-from-0 bounded by chunkRestartCap → `unstable`, assembly verified
19
+ against the offset-0 hash, busy retry WITHOUT breaker penalty,
20
+ assembled-bytes budget, hostile-relPath jail, never-clobber with
21
+ hash-suffixed alongside copies capped at 2 via SafeFsExecutor).
22
+ 2. `src/core/PendingPullLedger.ts` — the durable EXO-case record: serialized
23
+ `mutate(fn)` funnel (single-writer, the topic-flood-#3 lesson),
24
+ corrupt-parse quarantine (never silent-empty), TTL sweep with one honest
25
+ expiry notice, supersede clears ALL lower-epoch records across nominees,
26
+ (peer,topic,epoch) attempt-cap breaker.
27
+ 3. Three lockstep verb edits: `MeshCommand` union member, RBAC read/observe
28
+ case, dispatcher handler in server.ts (answers 'disabled' until the §3.7
29
+ gate constructs the serve side).
30
+ 4. Config: `CoherenceJournalUserConfig.workingSet` (types.ts) + the
31
+ ConfigDefaults literal. NO separate enable flag — the serve side
32
+ constructs IFF `replication.enabled === true` (the same explicit gate as
33
+ the replication transport; never the `?? developmentAgent` dark gate).
34
+ 5. `src/data/state-coherence-registry.json`: `pending-pulls` category
35
+ (machine-local, single-writer).
36
+
37
+ NOT in this slice (P2.2b): the onAccepted trigger, the reflex route, the
38
+ staggered drain wiring, CLAUDE.md template, e2e, the peer-visibility guard,
39
+ live two-machine verification. Nothing schedules a pull yet — the verb
40
+ serves only when explicitly enabled, and no caller exists.
41
+
42
+ ## Decision-point inventory
43
+
44
+ - **Serve allowlist verdict**: want paths matched against the FRESH manifest
45
+ recomputed per request — outside → `refusedPolicy`; flagged → honest
46
+ reason (`secretFlagged`/`tooLarge`/`liveSource`); vanished → benign
47
+ (`goneSinceManifest`/dropped from fresh manifest), never an attack log.
48
+ - **TOCTOU identity**: O_NOFOLLOW open (final-component symlink refused at
49
+ the syscall), fstat(fd) regular-file check, then lstat(path) vs fstat(fd)
50
+ dev+ino equality — a raced object swap reads nothing.
51
+ - **Anchor mechanics**: offset-0 pays ONE full read+hash (the assembly
52
+ authority); later chunks fstat-only. Mismatch → restart-from-0; an
53
+ mtime-preserving rewrite that dodges fstat still fails assembly
54
+ verification (bounded restarts, then `unstable`).
55
+ - **busy vs breaker**: busy = retry-without-penalty (bounded by
56
+ busyRetryCap); ONLY genuine failures reach `recordAttempt`. The just-woke
57
+ producer answering busy by design must not exhaust the records the drain
58
+ exists to recover.
59
+ - **Budget basis**: assembled, verification-passed bytes — discarded restart
60
+ chunks never count (a near-budget unstable file can't starve the set).
61
+ - **Never-clobber**: absent → jailed temp+rename write; identical sha →
62
+ skippedExisting; divergent → alongside (sanitized basename + env-derived
63
+ sender id + hash8 — idempotent for repeated identical divergence), cap 2
64
+ with oldest-evicted THROUGH SafeFsExecutor (the single, narrow deletion
65
+ exception; content survives on the producer).
66
+
67
+ ## 1. Over-block
68
+
69
+ A symlink destination on the receive side is refused even if its target is
70
+ in-jail (deliberate — never write through a peer-influenced link). An
71
+ offset past EOF is refused (`refusedPolicy`) rather than answering empty. A
72
+ divergent local file is never updated in place — by design the local copy
73
+ wins and the incoming lands alongside.
74
+
75
+ ## 2. Under-block
76
+
77
+ The full pre-parse transport ceiling on the puller side requires a bounded
78
+ fetch at the MeshRpcClient layer — this slice enforces the decode-side
79
+ backstop (oversize `dataB64` refused before decode); the bounded-fetch
80
+ wiring rides P2.2b where the client is constructed. The serve busy gate is
81
+ per-instance (in-process counter), not cross-process. The secret-content
82
+ scan inherits the §3.1 leak-reduction (not boundary) honesty.
83
+
84
+ ## 3. Level-of-abstraction fit
85
+
86
+ The verb logic lives in core (transport-agnostic, seam-injected) exactly
87
+ like JournalSyncApplier; server.ts only constructs + registers. The ledger
88
+ is its own module because its single-writer funnel is a different
89
+ discipline from the manifest's pure computation. The three lockstep edits
90
+ mirror journal-sync precisely (union, RBAC class, handler).
91
+
92
+ ## 4. Blast radius
93
+
94
+ Dark by default: the serve side constructs ONLY under
95
+ `replication.enabled === true` (today: the echo pair); everywhere else the
96
+ handler answers 'disabled'. No caller schedules pulls yet (P2.2b). The
97
+ ledger is instantiated only by tests in this slice. Registry entry is
98
+ metadata. A bug can affect only explicitly-replication-enabled pairs, and
99
+ the puller verifies every byte against served hashes before any write.
100
+
101
+ ## Evidence
102
+
103
+ - `tests/unit/WorkingSetPull.test.ts` — 16 passing: round-trip (single +
104
+ multi-chunk), generation-anchor restart lands the NEW content, never-sits-
105
+ still → `unstable` (no livelock, nothing landed), busy retry-without-
106
+ penalty + busyExhausted, never-clobber matrix (alongside naming regex,
107
+ hash-idempotency, cap-2 eviction, symlink destination refused), hostile
108
+ relPath matrix from a malicious manifest, ownership-recheck abort,
109
+ assembled-bytes budget, serve refusal matrix + TOCTOU symlink swap.
110
+ - `tests/unit/PendingPullLedger.test.ts` — 13 passing: restart-proof
111
+ persistence, idempotent file_, supersede-clears-all-nominees, TTL expiry
112
+ once, breaker + new-epoch reset, CONCURRENT mutate() drops no record,
113
+ corrupt → quarantine + one notice (never silent-empty), absent ≠ corrupt.
114
+ - `tests/integration/working-set-pull-roundtrip.test.ts` — 4 passing: 1.5MB
115
+ near-cap chunked round-trip through the REAL express.json + signed
116
+ MeshRpcClient path with an INDEPENDENT content oracle (source machine's
117
+ on-disk original), want-outside-manifest refused through the full stack,
118
+ divergent-alongside through the full stack, mixed-version no-handler → 501.
119
+ - Adjacent suites unchanged: journal-sync round-trip, mesh-rpc route +
120
+ client round-trip, reader (78 total across the 8 suites). Typecheck +
121
+ full lint chain clean (registry: 67 categories).
@@ -0,0 +1,115 @@
1
+ # Side-Effects Review — Working-set trigger + reflex + drain + visibility guard (P2.2b)
2
+
3
+ **Version / slug:** `working-set-trigger-p22b`
4
+ **Date:** `2026-06-06`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required (implements §3.3/§3.4/§3.6 of the 4-round-converged spec; everything rides the explicit replication gate; e2e proves the EXO case end-to-end)`
7
+
8
+ ## Summary of the change
9
+
10
+ P2.2b — the working-set transfer now FIRES. Completes P2:
11
+
12
+ 1. `src/core/WorkingSetPullCoordinator.ts` — the orchestration layer: the
13
+ move trigger (receiver-side deliverMessage onAccepted seam), the reflex,
14
+ and the returning-peer staggered drain share ONE pipeline. Durable
15
+ (topic,epoch) op-key dedupe (restart-proof, `pull-opkeys.json`, bounded
16
+ window 200); skips not-owner / placing-confirm / prevOwner-self;
17
+ single-flight per topic; plural bounded nomination from journal evidence
18
+ (own + replicas, cap 3, cappedNominees named); pending-pulls filed with
19
+ busy-never-burns-an-attempt accounting; staggered drain (rearmConcurrency
20
+ 1) with stale-owner clear + epoch supersession; slow sweep tick = TTL +
21
+ live-source re-arm.
22
+ 2. `src/core/PeerVisibilityGuard.ts` — §3.6 rider: pure
23
+ `detectImproperRevocations` (NOT inside loadRegistry — it stays hot +
24
+ pure) + cross-boot-deduped notices (keyed on revokedAt); 30-min-grace
25
+ disappearance notice naming stranded pending-pull topics; flap bound
26
+ (3 episodes/24h → ONE collapse notice). All notices ride the
27
+ agent-health attention lane.
28
+ 3. `POST /coherence/fetch-working-set` (routes.ts) — the reflex. 503 dark,
29
+ 400 bad topic, 429 rate-limited, 200 outcome.
30
+ 4. `PeerPresencePuller.onPeerRecorded` seam — the §3.4 re-arm rides the
31
+ SAME 30s cadence journal-sync does (no new polling loop).
32
+ 5. server.ts wiring: coordinator + ledger + guard constructed ONLY when the
33
+ serve side exists (the explicit `replication.enabled === true` gate);
34
+ onAccepted hook fires BEFORE the session-pool stage gate
35
+ (fire-and-forget; the coordinator carries its own gates); ledger
36
+ onCorrupt/onExpired → agent-health attention items; 10-min sweep timer;
37
+ 5-min guard timer.
38
+ 6. Agent Awareness + Migration Parity: CLAUDE.md template gains the
39
+ fetch-reflex section with the proactive trigger ("user references
40
+ files/work not on this machine"); `migrateClaudeMd` adds it
41
+ content-sniffed for existing agents.
42
+ 7. State-Coherence Registry: `pull-opkeys` + `visibility-guard` categories.
43
+
44
+ ## Decision-point inventory
45
+
46
+ - **Trigger gates** (ordered): single-flight → ownership (live store, the
47
+ only authority) → epoch present → op-key dedupe (move only; the reflex
48
+ deliberately bypasses it — an explicit ask re-fetches) → prevOwner-self
49
+ skip → pressure defer (op-key NOT recorded, so the next accept
50
+ re-triggers — a defer is never a silent drop).
51
+ - **busy vs attempts**: only genuine failures (unreachable/throw) reach
52
+ `recordAttempt`; busyExhausted re-files the record attempt-free (§3.2).
53
+ - **Drain semantics**: per-record ownership recheck (stale-owner → clear,
54
+ newer epoch → supersede) so a drained record can never write onto a
55
+ topic this machine no longer owns.
56
+ - **Guard honesty**: hygiene signal only — populated revocation fields are
57
+ NOT authenticated; the notice text says so explicitly.
58
+
59
+ ## 1. Over-block
60
+
61
+ A reflex call within 30s of the last is 429'd (coalescing handles the
62
+ concurrent case). A move while under host pressure defers the pull until
63
+ the next trigger. The drain clears records for topics we no longer own —
64
+ deliberate: the current owner's own pull covers truth.
65
+
66
+ ## 2. Under-block
67
+
68
+ The op-key window is bounded at 200 — a topic moved 200+ epochs ago could
69
+ re-pull (idempotent via skippedExisting, harmless). The disappearance
70
+ notice depends on the pool registry's online view (a registry blind spot
71
+ is the §3.6 known limitation). The pressure-defer relies on the next
72
+ accept/reflex; a topic that never gets another message AND was deferred
73
+ falls to the reflex (named spec §3.3 behavior).
74
+
75
+ ## 3. Level-of-abstraction fit
76
+
77
+ The coordinator is transport-agnostic core (seam-injected like the puller);
78
+ server.ts only constructs + wires. The guard's detector is a pure function
79
+ per the integration-review finding (loadRegistry stays a hot dependency-free
80
+ read). The onAccepted hook lands in the existing createDeliverMessageHandler
81
+ seam — the exact place the spec's round-1 integration review corrected the
82
+ design to (ownAction/confirmClaim run on the ROUTER, the wrong machine).
83
+
84
+ ## 4. Blast radius
85
+
86
+ Dark everywhere except explicitly-replication-enabled pairs (today: the
87
+ echo Laptop+Mini). The onAccepted hook is one optional-chained call on a
88
+ coordinator that is undefined when dark. The reflex route 503s when dark.
89
+ The guard runs only inside the same gated block. Template/migration text
90
+ is awareness-only (the endpoint answers 503 until the layer is live).
91
+
92
+ ## Evidence
93
+
94
+ - `tests/unit/WorkingSetPullCoordinator.test.ts` — 12 passing: ownership
95
+ gating, DURABLE op-key dedupe across a coordinator restart,
96
+ pressure-defer-leaves-opkey-unburned, prevOwner-self skip, plural
97
+ nomination (newest-first, cap, cappedNominees, self excluded), genuine
98
+ vs busy attempt accounting, live-source clear-on-success, staggered
99
+ drain (sequential, stale-owner clear, drain gate), reflex rate-limit +
100
+ coalescing.
101
+ - `tests/unit/PeerVisibilityGuard.test.ts` — 5 passing: pure detector
102
+ matrix, cross-boot dedupe (crash-loop cannot re-spam), disappearance
103
+ episode lifecycle with stranded topics named + silent re-peer clear,
104
+ flap collapse to ONE notice.
105
+ - `tests/integration/working-set-reflex-route.test.ts` — 2 passing: 503
106
+ dark / 400 / 200-through-a-real-coordinator / 429.
107
+ - `tests/e2e/working-set-handoff-lifecycle.test.ts` — 2 passing,
108
+ production-shaped (REAL createDeliverMessageHandler + REAL signed
109
+ dispatcher/client + REAL journal streams): (1) a topic move lands the
110
+ file on the receiver, verified against the source's on-disk original +
111
+ re-delivery/op-key dedupe leaves it untouched; (2) THE EXO CASE —
112
+ producer offline at move, pending-pull survives a restart, re-fires on
113
+ the peer's return, file lands, record cleared.
114
+ - Full P2 sweep: 95 tests across 11 suites green; typecheck + lint chain
115
+ (69 registry categories) + docs-coverage clean.