instar 1.3.365 → 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 (70) 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/IntentTestHarness.d.ts +27 -0
  8. package/dist/core/IntentTestHarness.d.ts.map +1 -1
  9. package/dist/core/IntentTestHarness.js +91 -0
  10. package/dist/core/IntentTestHarness.js.map +1 -1
  11. package/dist/core/MeshRpc.d.ts +8 -0
  12. package/dist/core/MeshRpc.d.ts.map +1 -1
  13. package/dist/core/MeshRpc.js +6 -1
  14. package/dist/core/MeshRpc.js.map +1 -1
  15. package/dist/core/PeerPresencePuller.d.ts +6 -0
  16. package/dist/core/PeerPresencePuller.d.ts.map +1 -1
  17. package/dist/core/PeerPresencePuller.js +5 -0
  18. package/dist/core/PeerPresencePuller.js.map +1 -1
  19. package/dist/core/PeerVisibilityGuard.d.ts +83 -0
  20. package/dist/core/PeerVisibilityGuard.d.ts.map +1 -0
  21. package/dist/core/PeerVisibilityGuard.js +204 -0
  22. package/dist/core/PeerVisibilityGuard.js.map +1 -0
  23. package/dist/core/PendingPullLedger.d.ts +129 -0
  24. package/dist/core/PendingPullLedger.d.ts.map +1 -0
  25. package/dist/core/PendingPullLedger.js +239 -0
  26. package/dist/core/PendingPullLedger.js.map +1 -0
  27. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  28. package/dist/core/PostUpdateMigrator.js +20 -0
  29. package/dist/core/PostUpdateMigrator.js.map +1 -1
  30. package/dist/core/WorkingSetManifest.d.ts +2 -0
  31. package/dist/core/WorkingSetManifest.d.ts.map +1 -1
  32. package/dist/core/WorkingSetManifest.js +13 -1
  33. package/dist/core/WorkingSetManifest.js.map +1 -1
  34. package/dist/core/WorkingSetPull.d.ts +157 -0
  35. package/dist/core/WorkingSetPull.d.ts.map +1 -0
  36. package/dist/core/WorkingSetPull.js +546 -0
  37. package/dist/core/WorkingSetPull.js.map +1 -0
  38. package/dist/core/WorkingSetPullCoordinator.d.ts +134 -0
  39. package/dist/core/WorkingSetPullCoordinator.d.ts.map +1 -0
  40. package/dist/core/WorkingSetPullCoordinator.js +306 -0
  41. package/dist/core/WorkingSetPullCoordinator.js.map +1 -0
  42. package/dist/core/types.d.ts +43 -1
  43. package/dist/core/types.d.ts.map +1 -1
  44. package/dist/core/types.js.map +1 -1
  45. package/dist/redteam/ScenarioPack.d.ts +30 -3
  46. package/dist/redteam/ScenarioPack.d.ts.map +1 -1
  47. package/dist/redteam/ScenarioPack.js +46 -1
  48. package/dist/redteam/ScenarioPack.js.map +1 -1
  49. package/dist/scaffold/templates.d.ts.map +1 -1
  50. package/dist/scaffold/templates.js +4 -0
  51. package/dist/scaffold/templates.js.map +1 -1
  52. package/dist/server/AgentServer.d.ts +3 -0
  53. package/dist/server/AgentServer.d.ts.map +1 -1
  54. package/dist/server/AgentServer.js +1 -0
  55. package/dist/server/AgentServer.js.map +1 -1
  56. package/dist/server/routes.d.ts +5 -0
  57. package/dist/server/routes.d.ts.map +1 -1
  58. package/dist/server/routes.js +47 -1
  59. package/dist/server/routes.js.map +1 -1
  60. package/package.json +1 -1
  61. package/src/data/builtin-manifest.json +64 -64
  62. package/src/data/state-coherence-registry.json +48 -12
  63. package/src/scaffold/templates.ts +4 -0
  64. package/upgrades/{1.3.365.md → 1.3.366.md} +35 -0
  65. package/upgrades/1.3.367.md +134 -0
  66. package/upgrades/side-effects/intent-llm-judge.md +94 -0
  67. package/upgrades/side-effects/working-set-ci-absorb.md +29 -0
  68. package/upgrades/side-effects/working-set-parity-absorb.md +35 -0
  69. package/upgrades/side-effects/working-set-pull-p22a.md +121 -0
  70. 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.)
@@ -5,6 +5,17 @@
5
5
 
6
6
  ## What Changed
7
7
 
8
+ Phase-2 LLM judge for ORG-INTENT governance (CMT-1128): when the
9
+ keyword-overlap refusal test misses, POST /intent/org/test-action (and the
10
+ red-team harness expectation resolver) can now escalate to one bounded LLM
11
+ call that judges whether a constraint forbids the action by meaning rather
12
+ than wording — closing the keyword matcher's false-negative side that the
13
+ first boundary-map run exposed. Verdicts carry their method: llm-judge is
14
+ claimed only for a real, parsed LLM verdict; a judge problem keeps the
15
+ heuristic verdict and flags judgeUnavailable. Ships dark behind
16
+ monitoring.orgIntentLlmJudge.enabled and requires a configured intelligence
17
+ provider; with the flag off the route response is byte-for-byte unchanged.
18
+
8
19
  First slice of the converged Working-Set Handoff spec (P2 of multi-machine
9
20
  coherence): a machine can now compute, on demand and from durable evidence,
10
21
  "what files make up topic T's workspace here" — never from anyone remembering
@@ -31,6 +42,12 @@ receiver trigger, and pending-pull ledger wire it into the move path next.
31
42
 
32
43
  ## What to Tell Your User
33
44
 
45
+ Nothing — this ships dark and is an internal governance-testing improvement.
46
+ If they previously saw an intent boundary report calling something
47
+ "ungoverned," that verdict was keyword-based and conservative; once this
48
+ feature is enabled, those misses get a semantic second opinion before being
49
+ reported as candidate gaps.
50
+
34
51
  Nothing user-visible yet. This is the foundation slice of "moving a
35
52
  conversation between your machines moves its working files too" — the part
36
53
  that lets a machine reliably answer what those files are. The transfer itself
@@ -38,6 +55,15 @@ lands in the next slice.
38
55
 
39
56
  ## Summary of New Capabilities
40
57
 
58
+ - judgeRefusal in IntentTestHarness: one bounded fast-model call (temperature
59
+ 0, 8s timeout, attributed IntentLlmJudge/gate for /metrics/features)
60
+ judging an action against the constraints by meaning.
61
+ - resolveExpectationJudged in the red-team ScenarioPack: keyword pre-filter,
62
+ LLM decision, honest judgeUnavailable signal.
63
+ - POST /intent/org/test-action escalates keyword misses to the judge when
64
+ monitoring.orgIntentLlmJudge.enabled is true; refusal verdicts then carry
65
+ their method.
66
+
41
67
  - `computeWorkingSet(opts)` (`src/core/WorkingSetManifest.ts`) — pure,
42
68
  bounded, jailed manifest computation for one topic's working files, with
43
69
  honest per-entry flags and counted degradations (`jailRejected`,
@@ -51,6 +77,15 @@ lands in the next slice.
51
77
 
52
78
  ## Evidence
53
79
 
80
+ tests/unit/intent-llm-judge.test.ts (16, including the boundary-map replay
81
+ that first proves the keyword miss and then proves the judge closes it),
82
+ tests/integration/intent-llm-judge-route.test.ts (5, full HTTP pipeline both
83
+ flag states), tests/e2e/intent-llm-judge-lifecycle.test.ts (3, real
84
+ AgentServer.start with the provider injected through the production seam).
85
+ Regression canaries IntentTestHarness + redteam-scenario-pack +
86
+ org-intent-routes + mtp-protocol lifecycle + no-silent-fallbacks all green;
87
+ clean tsc; docs-coverage floors hold.
88
+
54
89
  - `tests/unit/WorkingSetManifest.test.ts` — 18 passing: sources + canonical
55
90
  dedupe, jail matrix (escape-shaped path, final-component symlink,
56
91
  symlinked-parent escape), caps (tooLarge listed+hashed, headline exemption,
@@ -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,94 @@
1
+ # Side-effects review — Phase-2 LLM judge for ORG-INTENT governance (CMT-1128)
2
+
3
+ ## What this change does
4
+
5
+ Adds the semantic resolver the keyword matcher's honesty work (#899) pointed
6
+ at: when the keyword-overlap refusal test MISSES, one bounded LLM call judges
7
+ whether any ORG-INTENT constraint forbids the action by MEANING. Surfaces:
8
+ (1) `judgeRefusal()` in IntentTestHarness.ts (the judge primitive),
9
+ (2) `resolveExpectationJudged()` in ScenarioPack.ts (the red-team harness
10
+ Phase-2 resolver), (3) POST /intent/org/test-action escalates a keyword miss
11
+ to the judge when `monitoring.orgIntentLlmJudge.enabled` is true AND an
12
+ intelligence provider is configured. Ships DARK (default false).
13
+
14
+ ## Decision boundary (both sides tested)
15
+
16
+ - Keyword MISS + judge says forbidden → governed/refused with
17
+ `method: 'llm-judge'` and the semantically-matched constraint (the live
18
+ boundary-map replay: "unverified work as completed" governs "estimates as
19
+ confirmed numbers"; the unit test FIRST proves the keyword matcher misses
20
+ this exact pair, then proves the judge closes it).
21
+ - Keyword MATCH → short-circuit: heuristic verdict returned, provider never
22
+ called (call-count asserted). The pre-filter contract from types.ts:
23
+ heuristics narrow candidates, the provider decides.
24
+ - Judge requested but unavailable (provider throws / circuit open / malformed
25
+ reply) → heuristic verdict stands with `judgeUnavailable: true`; route
26
+ still 200 (unit + integration + e2e).
27
+ - Judged not-forbidden → `method: 'llm-judge'` ungoverned with
28
+ judgment-not-ground-truth framing in the reason string.
29
+ - Flag OFF (or no provider) → byte-compatible Phase-1 response, zero provider
30
+ calls (integration + e2e).
31
+
32
+ ## Blast radius
33
+
34
+ - `src/core/IntentTestHarness.ts`: additive exports (`judgeRefusal`,
35
+ `JudgeMethod`, `JudgedRefusalResult`, `JudgeOptions`); the existing class
36
+ and its keyword logic are untouched (its 9 tests pass unmodified).
37
+ - `src/redteam/ScenarioPack.ts`: `ResolvedExpectation.method` widened to the
38
+ `'keyword-heuristic' | 'llm-judge'` union (additive — the sync
39
+ `resolveExpectation` still always reports `'keyword-heuristic'`; its
40
+ honesty test passes unmodified); new optional `judgeUnavailable` field; new
41
+ async `resolveExpectationJudged`.
42
+ - `src/server/routes.ts`: the test-action handler computes the heuristic
43
+ verdict exactly as before, then — only inside the flag+provider guard —
44
+ escalates a miss. Flag-off path emits the identical response object.
45
+ - `src/core/types.ts`: new optional `monitoring.orgIntentLlmJudge` config
46
+ block. No migrateConfig (absent → off, the principalCoherence sibling
47
+ convention).
48
+ - LLM spend: at most ONE fast-model call per keyword-missed test-action
49
+ request (or per scenario hint in the red-team resolver), temperature 0,
50
+ maxTokens 250, 8s timeout, attributed `IntentLlmJudge`/`gate` so
51
+ /metrics/features and burn detection see it. No background loop — the
52
+ judge only runs inside an explicit request.
53
+ - No new routes (route floor untouched), no new core class files (class
54
+ floor untouched), no migration surface, no subprocess.
55
+
56
+ ## Migration parity
57
+
58
+ No agent-installed files change (no hooks, no config defaults written, no
59
+ CLAUDE.md template text, no skills). The config flag is opt-in and absent
60
+ means off — nothing to migrate.
61
+
62
+ ## Framework generality
63
+
64
+ Framework-agnostic: the judge consumes the IntelligenceProvider interface,
65
+ so per-component framework routing (`IntentLlmJudge` under category `gate`)
66
+ applies — the judge can run on whichever framework the routing table says.
67
+
68
+ ## Tests
69
+
70
+ - `tests/unit/intent-llm-judge.test.ts` — 16 tests: judgeRefusal verdict
71
+ parsing both sides, prose-wrapped JSON, malformed/typed-wrong replies,
72
+ provider throw, no-constraints short-circuit, out-of-range index tolerance,
73
+ call-options pinning (fast/temp-0/attribution/timeout);
74
+ resolveExpectationJudged: the boundary-map replay (keyword miss proven,
75
+ judge governs), keyword-match short-circuit (zero provider calls), judge
76
+ unavailable honesty, judged-ungoverned framing, multi-hint iteration,
77
+ mixed failure+verdict semantics.
78
+ - `tests/integration/intent-llm-judge-route.test.ts` — 5 tests over the full
79
+ HTTP pipeline: judged governance over the wire, match short-circuit,
80
+ unavailable honesty, flag-off byte-compatibility, flag-on-without-provider.
81
+ - `tests/e2e/intent-llm-judge-lifecycle.test.ts` — 3 tests on a REAL
82
+ AgentServer.start(): feature-alive (200 + llm-judge verdict through the
83
+ production options seam), judge problem never breaks the route, dark
84
+ server byte-compatible.
85
+ - Regression canaries green: IntentTestHarness (9), redteam-scenario-pack
86
+ (26), org-intent-routes (21), mtp-protocol-test-action-lifecycle (4),
87
+ no-silent-fallbacks (5); tsc clean; docs-coverage --check passes (floors
88
+ hold — no new routes or class files).
89
+
90
+ ## Rollback
91
+
92
+ Delete the judge block in the test-action route, the two new functions, the
93
+ type widening, and the config block. No state, no migrations, no on-disk
94
+ artifacts to unwind (verdicts are request-scoped).
@@ -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).