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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +200 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +18 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/IntentTestHarness.d.ts +27 -0
- package/dist/core/IntentTestHarness.d.ts.map +1 -1
- package/dist/core/IntentTestHarness.js +91 -0
- package/dist/core/IntentTestHarness.js.map +1 -1
- package/dist/core/MeshRpc.d.ts +8 -0
- package/dist/core/MeshRpc.d.ts.map +1 -1
- package/dist/core/MeshRpc.js +6 -1
- package/dist/core/MeshRpc.js.map +1 -1
- package/dist/core/PeerPresencePuller.d.ts +6 -0
- package/dist/core/PeerPresencePuller.d.ts.map +1 -1
- package/dist/core/PeerPresencePuller.js +5 -0
- package/dist/core/PeerPresencePuller.js.map +1 -1
- package/dist/core/PeerVisibilityGuard.d.ts +83 -0
- package/dist/core/PeerVisibilityGuard.d.ts.map +1 -0
- package/dist/core/PeerVisibilityGuard.js +204 -0
- package/dist/core/PeerVisibilityGuard.js.map +1 -0
- package/dist/core/PendingPullLedger.d.ts +129 -0
- package/dist/core/PendingPullLedger.d.ts.map +1 -0
- package/dist/core/PendingPullLedger.js +239 -0
- package/dist/core/PendingPullLedger.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +20 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/WorkingSetManifest.d.ts +2 -0
- package/dist/core/WorkingSetManifest.d.ts.map +1 -1
- package/dist/core/WorkingSetManifest.js +13 -1
- package/dist/core/WorkingSetManifest.js.map +1 -1
- package/dist/core/WorkingSetPull.d.ts +157 -0
- package/dist/core/WorkingSetPull.d.ts.map +1 -0
- package/dist/core/WorkingSetPull.js +546 -0
- package/dist/core/WorkingSetPull.js.map +1 -0
- package/dist/core/WorkingSetPullCoordinator.d.ts +134 -0
- package/dist/core/WorkingSetPullCoordinator.d.ts.map +1 -0
- package/dist/core/WorkingSetPullCoordinator.js +306 -0
- package/dist/core/WorkingSetPullCoordinator.js.map +1 -0
- package/dist/core/types.d.ts +43 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/redteam/ScenarioPack.d.ts +30 -3
- package/dist/redteam/ScenarioPack.d.ts.map +1 -1
- package/dist/redteam/ScenarioPack.js +46 -1
- package/dist/redteam/ScenarioPack.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +4 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +3 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +1 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +5 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +47 -1
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +64 -64
- package/src/data/state-coherence-registry.json +48 -12
- package/src/scaffold/templates.ts +4 -0
- package/upgrades/{1.3.365.md → 1.3.366.md} +35 -0
- package/upgrades/1.3.367.md +134 -0
- package/upgrades/side-effects/intent-llm-judge.md +94 -0
- package/upgrades/side-effects/working-set-ci-absorb.md +29 -0
- package/upgrades/side-effects/working-set-parity-absorb.md +35 -0
- package/upgrades/side-effects/working-set-pull-p22a.md +121 -0
- 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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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).
|