instar 1.3.511 → 1.3.513

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 (91) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +168 -2
  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 +42 -0
  6. package/dist/config/ConfigDefaults.js.map +1 -1
  7. package/dist/core/CoherenceJournal.d.ts +31 -1
  8. package/dist/core/CoherenceJournal.d.ts.map +1 -1
  9. package/dist/core/CoherenceJournal.js +49 -2
  10. package/dist/core/CoherenceJournal.js.map +1 -1
  11. package/dist/core/JournalSyncApplier.d.ts.map +1 -1
  12. package/dist/core/JournalSyncApplier.js +18 -0
  13. package/dist/core/JournalSyncApplier.js.map +1 -1
  14. package/dist/core/MeshRpc.d.ts +6 -0
  15. package/dist/core/MeshRpc.d.ts.map +1 -1
  16. package/dist/core/MeshRpc.js +1 -0
  17. package/dist/core/MeshRpc.js.map +1 -1
  18. package/dist/core/PeerPresencePuller.d.ts +19 -0
  19. package/dist/core/PeerPresencePuller.d.ts.map +1 -1
  20. package/dist/core/PeerPresencePuller.js +7 -0
  21. package/dist/core/PeerPresencePuller.js.map +1 -1
  22. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  23. package/dist/core/PostUpdateMigrator.js +57 -0
  24. package/dist/core/PostUpdateMigrator.js.map +1 -1
  25. package/dist/core/PreferencesManager.d.ts +34 -0
  26. package/dist/core/PreferencesManager.d.ts.map +1 -1
  27. package/dist/core/PreferencesManager.js +74 -0
  28. package/dist/core/PreferencesManager.js.map +1 -1
  29. package/dist/core/PreferencesSync.d.ts +167 -0
  30. package/dist/core/PreferencesSync.d.ts.map +1 -0
  31. package/dist/core/PreferencesSync.js +307 -0
  32. package/dist/core/PreferencesSync.js.map +1 -0
  33. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  34. package/dist/core/devGatedFeatures.js +5 -0
  35. package/dist/core/devGatedFeatures.js.map +1 -1
  36. package/dist/core/types.d.ts +34 -0
  37. package/dist/core/types.d.ts.map +1 -1
  38. package/dist/core/types.js.map +1 -1
  39. package/dist/monitoring/GreenPrAutoMerger.d.ts +188 -0
  40. package/dist/monitoring/GreenPrAutoMerger.d.ts.map +1 -0
  41. package/dist/monitoring/GreenPrAutoMerger.js +393 -0
  42. package/dist/monitoring/GreenPrAutoMerger.js.map +1 -0
  43. package/dist/monitoring/GuardLatchStore.d.ts +134 -0
  44. package/dist/monitoring/GuardLatchStore.d.ts.map +1 -0
  45. package/dist/monitoring/GuardLatchStore.js +255 -0
  46. package/dist/monitoring/GuardLatchStore.js.map +1 -0
  47. package/dist/monitoring/MergeRunner.d.ts +126 -0
  48. package/dist/monitoring/MergeRunner.d.ts.map +1 -0
  49. package/dist/monitoring/MergeRunner.js +296 -0
  50. package/dist/monitoring/MergeRunner.js.map +1 -0
  51. package/dist/monitoring/floorDriftCanary.d.ts +88 -0
  52. package/dist/monitoring/floorDriftCanary.d.ts.map +1 -0
  53. package/dist/monitoring/floorDriftCanary.js +89 -0
  54. package/dist/monitoring/floorDriftCanary.js.map +1 -0
  55. package/dist/monitoring/greenPrAutomergeWiring.d.ts +73 -0
  56. package/dist/monitoring/greenPrAutomergeWiring.d.ts.map +1 -0
  57. package/dist/monitoring/greenPrAutomergeWiring.js +273 -0
  58. package/dist/monitoring/greenPrAutomergeWiring.js.map +1 -0
  59. package/dist/monitoring/greenPrLogic.d.ts +121 -0
  60. package/dist/monitoring/greenPrLogic.d.ts.map +1 -0
  61. package/dist/monitoring/greenPrLogic.js +198 -0
  62. package/dist/monitoring/greenPrLogic.js.map +1 -0
  63. package/dist/monitoring/guardManifest.d.ts.map +1 -1
  64. package/dist/monitoring/guardManifest.js +12 -0
  65. package/dist/monitoring/guardManifest.js.map +1 -1
  66. package/dist/scaffold/templates.d.ts.map +1 -1
  67. package/dist/scaffold/templates.js +1 -0
  68. package/dist/scaffold/templates.js.map +1 -1
  69. package/dist/server/AgentServer.d.ts +5 -0
  70. package/dist/server/AgentServer.d.ts.map +1 -1
  71. package/dist/server/AgentServer.js +3 -0
  72. package/dist/server/AgentServer.js.map +1 -1
  73. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  74. package/dist/server/CapabilityIndex.js +16 -0
  75. package/dist/server/CapabilityIndex.js.map +1 -1
  76. package/dist/server/routes.d.ts +10 -0
  77. package/dist/server/routes.d.ts.map +1 -1
  78. package/dist/server/routes.js +137 -1
  79. package/dist/server/routes.js.map +1 -1
  80. package/dist/server/stopGate.d.ts +45 -0
  81. package/dist/server/stopGate.d.ts.map +1 -1
  82. package/dist/server/stopGate.js +74 -0
  83. package/dist/server/stopGate.js.map +1 -1
  84. package/package.json +1 -1
  85. package/scripts/safe-merge.mjs +449 -59
  86. package/src/data/builtin-manifest.json +64 -64
  87. package/src/scaffold/templates.ts +1 -0
  88. package/upgrades/1.3.512.md +65 -0
  89. package/upgrades/1.3.513.md +61 -0
  90. package/upgrades/side-effects/green-pr-automerge-enforcement.md +171 -0
  91. package/upgrades/side-effects/multi-machine-seamlessness-ws21-preferences-pool.md +116 -0
@@ -0,0 +1,61 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ - **Learned preferences replicate across your machines.** A preference the
9
+ Correction & Preference Learning loop captures on machine A (e.g. "lead with the
10
+ action, no preamble") is now honored on machine B. New `preferences-sync` mesh verb
11
+ (read/observe class, any registered same-operator peer), riding the existing
12
+ authenticated MeshRpc envelope and the PeerPresencePuller cadence exactly like
13
+ `commitments-sync`.
14
+ - **Serve side** — `buildPreferencesSyncPage`: seq-windowed delta pages
15
+ (`lastMutatedSeq > sinceSeq`, exclusive cursor, byte-capped), incarnation-fenced
16
+ (a restored/rewound store re-mints its incarnation so peers re-pull wholesale), and
17
+ the free-text `learning` is credential-shape-redacted at serve time. The local-only
18
+ `violationPattern` (the user's self-violation detection regex) is NEVER replicated.
19
+ - **Receive side** — `PreferenceReplicaStore`: one single-writer file per peer,
20
+ first-hop sender-binding (a row claiming a third machine is rejected + counted),
21
+ corrupt→quarantine+re-pull, per-peer bound (default 500).
22
+ - **Merged read** — `GET /preferences/session-context?` collapses own + replicas by
23
+ `dedupeKey` (the same lesson seen on two machines is ONE row): newest `recordedAt`
24
+ wins the fields, `dedupeCount` sums across machines. A grossly-future timestamp
25
+ (beyond a 24h skew tolerance) is treated as untrustworthy and loses, so a
26
+ clock-skewed peer can't silently override your real preferences.
27
+ - Ships **DARK** behind `multiMachine.seamlessness.ws21PreferencesPool` (default
28
+ false). Single-machine / flag-off = byte-identical to today (own-only). CLAUDE.md
29
+ template + idempotent PostUpdateMigrator bullet so deployed agents learn it.
30
+
31
+ ## What to Tell Your User
32
+
33
+ When you run the agent on more than one computer, the little things it learns about
34
+ how you like to work now follow you to every machine — you don't have to re-teach it
35
+ on each one. It's careful about it: a machine with a wrong clock can't hijack your
36
+ preferences, the agent's own private detection rules never leave the machine they're
37
+ on, and if the same lesson was learned in two places you see it once, not twice. Off
38
+ by default; a single-machine setup is completely unaffected.
39
+
40
+ ## Summary of New Capabilities
41
+
42
+ - Cross-machine replication of learned preferences (`preferences-sync` mesh verb).
43
+ - `GET /preferences/session-context` returns the merged pooled view when the pool flag
44
+ is on, each lesson de-duplicated across machines with a clock-skew-resistant winner.
45
+
46
+ ## Evidence
47
+
48
+ - `tests/unit/PreferencesSync.test.ts` (18) — serve fence/delta/byte-cap/redaction,
49
+ receive forged-row/incarnation/quarantine/per-peer-bound, merge collapse-by-dedupeKey
50
+ + dedupeCount-sum + recency, and the security-review fixes (violationPattern never
51
+ replicated; future-skewed peer loses; legit-newer peer still wins).
52
+ - `tests/unit/PreferencesManager-replication.test.ts` (6) — seq machinery, advert,
53
+ highWaterSeq rewind detection, legacy-store seeding, end-to-end getAllForSync→serve.
54
+ - `tests/unit/ws21-preferences-pool-wiring.test.ts` (11) — serve handler registered,
55
+ puller drives, flag default, MeshRpc verb+RBAC, the flag-gated union over the live
56
+ route (off→own-only; on+replicas→merged `scope:mesh`; on+no-replicas→own-only), and
57
+ the real-`meshSelfId` guard.
58
+ - `tests/integration/preferences-sync-roundtrip.test.ts` (3) — real signed MeshRpc
59
+ round-trip: paged delta, dedupeKey-collapse, mixed-version 501.
60
+ - 72 WS2.1 tests green; `pnpm build` clean. Reviewed by a 4-dimension adversarial
61
+ Workflow (16 agents) — 7 confirmed findings fixed before ship.
@@ -0,0 +1,171 @@
1
+ # Side-Effects Review — Green-PR Auto-Merge Enforcement (Phase 7 becomes machinery)
2
+
3
+ **Version / slug:** `green-pr-automerge-enforcement`
4
+ **Date:** `2026-06-12`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `green-pr-automerge-secondpass subagent (high-risk: merge authority + gate + watcher)`
7
+
8
+ ## Summary of the change
9
+
10
+ A background watcher (`GreenPrAutoMerger`) that merges a green, mergeable, non-held PR
11
+ this agent authored — so the merge survives session death (the prose "Phase 7" rule died
12
+ with the session that read it; this is machinery). It rides on a hardened
13
+ `scripts/safe-merge.mjs` (the act-time re-verifier), a pool-visible kill-switch
14
+ (`GuardLatchStore` on a new replicated `guard-latch` coherence-journal kind), a per-PR
15
+ failure ladder + circuit breaker, an identity contract, a floor-drift canary, and a Layer-2
16
+ session-exit nudge. Off fleet-wide (`DARK_GATE_EXCLUSIONS: deliberate-fleet-default`),
17
+ flipped on per dev agent with `expectedGhLogin`, repo-gated to an instar checkout with
18
+ `scripts/safe-merge.mjs` present. Spec: `docs/specs/green-pr-automerge-enforcement.md`
19
+ (converged round 7, approved). New modules: `src/monitoring/{GreenPrAutoMerger,MergeRunner,
20
+ GuardLatchStore,greenPrLogic,floorDriftCanary,greenPrAutomergeWiring}.ts`; `guard-latch` kind
21
+ in `CoherenceJournal.ts` + `JournalSyncApplier.ts`; Layer-2 helpers in `stopGate.ts`; six
22
+ routes in `routes.ts`; boot wiring in `commands/server.ts`; config/registry/migration in
23
+ `ConfigDefaults.ts`, `devGatedFeatures.ts`, `guardManifest.ts`, `PostUpdateMigrator.ts`,
24
+ `types.ts`.
25
+
26
+ ## Decision-point inventory
27
+
28
+ - `GreenPrAutoMerger.tick` candidate gate — **add** — decides which PRs are merge-eligible
29
+ (namespace + hold + mergeable + settled-green + protected-paths). Fail-toward-skip.
30
+ - `GuardLatchStore.isMergeAllowed` (dual-latch gate) — **add** — rollback + emergency-pause
31
+ must BOTH be open; unreadable peers → arrive-disabled.
32
+ - `safe-merge` required-contexts cross-check — **add** — producer-bound floor; refuses on any
33
+ unverifiable/missing/wrong-producer context.
34
+ - Layer-2 `greenPrBlock` (stop-gate hot path) — **add** — blocks a session ending with a
35
+ green unmerged PR on its branch, ONCE, mode-independently. No runnable command in any variant.
36
+ - The existing stop-gate router hook — **modify** — gains a mode-independent greenPrBlock
37
+ branch before the `mode === 'off'` early return (always-overwritten by migrateHooks).
38
+
39
+ ## Over-block — legitimate inputs this rejects that it shouldn't
40
+
41
+ - A genuinely-green PR whose required-contexts cannot be FETCHED (gh/API hiccup) is refused
42
+ (`refused:contexts-unverifiable`) rather than merged. Intended: fail-toward-skip. The PR
43
+ stays open; the next tick retries; the hold-age backstop surfaces it if it rots.
44
+ - A PR whose CI floor was renamed out-of-band is refused until the floor-drift canary names
45
+ the drift. Intended over-block (a drifted floor must not be merged around).
46
+ - A PR touching protected paths is never auto-merged even when green — routed to the operator.
47
+ Intended (those PRs can mint hollow floor contexts; human eyes required).
48
+
49
+ ## Under-block — failure modes this still misses
50
+
51
+ - **Stale-base merges (Decision 9, ratified):** `--admin` bypasses the up-to-date-branch
52
+ requirement, so a PR tested against an older `main` can merge an untested integration.
53
+ Bounded: textual conflicts never merge (`mergeable == MERGEABLE`), `main` re-runs full CI
54
+ after every merge (Zero-Failure makes breakage owned + the audit names the merge), and this
55
+ relocates the EXISTING manual-merge residual unchanged. A strict-base-freshness mode is on
56
+ the fleet-promotion checklist.
57
+ - **Branch namespace is a filter, not provenance (Decision 8):** anyone holding the shared gh
58
+ credential can name a branch `echo/…`; the floor's producer-bound CI contexts are what make
59
+ a candidate "pre-approved by construction."
60
+ - **Multi-machine peer-latch READ is single-machine in this PR:** `readPeerLatches: () => []`.
61
+ Outbound replication (the journal emit) works; the merged peer READ surface is a follow-up.
62
+ Honest consequence: on a multi-machine pool a rollback set on machine A replicates outbound
63
+ but machine B does not yet MERGE A's latch into its gate. The durable local file is the
64
+ authoritative gate per-machine, and the lease serializes the ACT — so two machines never
65
+ both merge — but a cross-machine STOP is not yet honored on the non-originating holder. This
66
+ is disclosed; the dev agent runs effectively single-machine for merge authority today.
67
+
68
+ ## Level-of-abstraction fit
69
+
70
+ Correct layer: a monitoring watcher (the ReleaseReadinessSentinel precedent) that drives the
71
+ blessed `safe-merge` wrapper. The watcher holds NO independent merge path and never trusts
72
+ safe-merge's exit code (independent `gh pr view` confirm, B10). The kill-switch rides the
73
+ existing coherence-journal replication + fenced lease (no new distributed primitive).
74
+
75
+ ## Signal vs authority compliance
76
+
77
+ The watcher is Tier-0: the ONLY discretionary classification is hold/candidate status, and its
78
+ failure direction is fail-toward-skip (audited), never fail-toward-merge. Everything that
79
+ decides "is this change good" already happened upstream (spec process + gates + CI); safe-merge
80
+ re-verifies at act time. The Layer-2 `greenPrBlock` blocks ONCE on objective replicated state
81
+ (green candidate + exact branch match + staleness gate + armed gate) and fails open on every
82
+ error — it does not repeat the brittle-substring-with-blocking-authority pattern of the
83
+ stated-continuation guard it sits beside (that foundation tension is recorded in the spec, not
84
+ inherited).
85
+
86
+ ## Interactions
87
+
88
+ - Shares the stop-gate hot-path response with the UnjustifiedStopGate; greenPrBlock is computed
89
+ LAZILY (only when the snapshot has candidates) and is mode-independent, so it does not depend
90
+ on the UnjustifiedStopGate mode (which ships `off`).
91
+ - Adds a journal kind: the `guard-latch` kind is closed-schema in both the writer
92
+ (`CoherenceJournal`) and the receiver (`JournalSyncApplier`) — a peer's forged guard-latch
93
+ entry is schema-rejected like any other kind.
94
+ - One aggregated attention item with a machine-stable id (`green-pr-automerge:aggregate`) — does
95
+ NOT create per-PR topics (the notification-flood burst-invariant bound).
96
+
97
+ ## External surfaces
98
+
99
+ - Merges PRs on GitHub (the one durable external side-effect — ratified at the approval gate;
100
+ the operator-token admin merge already happened manually via the same script since June 9).
101
+ - Six new HTTP routes (`/green-pr-automerge*`); `/enable` + `/pool-disarm` are dashboard-PIN
102
+ gated (operator authority), `/rollback` + `/hold` + `/tick` are Bearer.
103
+ - Reporter hook payload gains `cwd` (always-overwritten template; free fleet-wide).
104
+
105
+ ## Multi-machine posture (Cross-Machine Coherence)
106
+
107
+ | Surface | Posture | Why |
108
+ |---|---|---|
109
+ | `state/green-pr-automerge.json` | machine-local BY DESIGN | attempt ledger; the ACT is lease-serialized |
110
+ | `state/green-pr-automerge-latches.json` | machine-local authoritative + journal-replicated | durable STOP survives restart; emit replicates outbound |
111
+ | `guard-latch` journal kind | pool-replicated (outbound) | a STOP/marker propagates to peers |
112
+ | peer-latch READ (merged gate) | single-machine in this PR (follow-up) | disclosed under-block above |
113
+ | Layer-2 snapshot | machine-local (holder only) | sessions on a non-holder get no belt; Layer 1 on the holder is the guarantee |
114
+ | Aggregated attention id | machine-stable | lease makes dual-raise impractical; stable id makes it harmless |
115
+
116
+ ## Rollback cost
117
+
118
+ - Runtime: `POST /green-pr-automerge/rollback` (Bearer, anyone can STOP) disarms instantly;
119
+ `dryRun: true` and `enabled: false` are config levers. Fleet default is off regardless.
120
+ - Code: revert the PR. No data migration — the new state files are additive and machine-local;
121
+ the new `guard-latch` journal kind is additive (readers ignore unknown kinds). The hook
122
+ template changes are always-overwritten, so a revert restores the prior templates on the next
123
+ migration.
124
+
125
+ ## Tests
126
+
127
+ - Unit (123 new): safe-merge hardening (27), guard-latch store (13), pure logic (31),
128
+ orchestrator (14), merge runner (12), floor-drift (9), Layer-2 helpers (11), + the journal
129
+ suites pass with the new kind (35).
130
+ - Integration (6): the routes over the real HTTP pipeline — 503 unwired → 200 wired
131
+ (feature-alive), rollback gate closing, PIN gating, the warm-up→merge tick flow.
132
+ - Honest gap vs the spec's full test list: a real-AgentServer-boot E2E and a dedicated
133
+ chaos-interleaving integration test are NOT in this PR — the route-level integration proves
134
+ the feature is alive through the real `createRoutes` pipeline, and the restart/orphan and
135
+ latch-partition paths are covered at the unit tier (MergeRunner orphan reap; GuardLatchStore
136
+ absorbing/unreadable). Flagged for a follow-up E2E hardening pass.
137
+
138
+ ## Second-pass review
139
+
140
+ **Verdict: CONCUR.** An independent reviewer audited the artifact against the code (safe-merge,
141
+ GreenPrAutoMerger, GuardLatchStore, MergeRunner, stopGate, the boot wiring), adversarially
142
+ hunting for a path to an unintended merge. Findings:
143
+ - No path to an unintended merge. The act path is gated in strict order every tick
144
+ (lease → single-flight → dual-latch gate → breaker → warm-up → candidate → identity →
145
+ contract probe → live re-fetch → spawn); safe-merge is the independent act-time authority and
146
+ refuses on red/missing-e2e/wrong-producer-floor/head-moved/unverifiable. The empty-rollup edge
147
+ (`statusCheckRollup:[]` passing the cheap list filter) is caught and refused at act time by
148
+ safe-merge — the non-authoritative filter never causes a merge.
149
+ - Fail direction is fail-toward-SKIP on every traced path (candidate, protected-paths-unverifiable,
150
+ identity unconfigured/unresolved/mismatch, gather/runner exceptions, every safe-merge error exit).
151
+ - Identity contract enforced before acting (default `expectedGhLogin:''` → skip every act).
152
+ - Dual-latch read every tick; unreadable peers arrive-disabled; rollback Bearer, re-arm PIN-gated.
153
+ - B10 independent confirmation at both safe-merge and the watcher.
154
+ - Layer-2 is signal-not-authority, fails open, no variant emits a runnable command.
155
+ - Wiring integrity confirmed (repo-gate → 503; real deps; same latch instance backs act + Layer 2).
156
+ - Two non-blocking observations, both fail-safe: the Layer-2 hot path hardcoded `tickIntervalMs`
157
+ (FIXED — now read from config) and a rollback landing mid-spawned-attempt does not abort that one
158
+ bounded attempt (disclosed; lease + single-flight bound it to one).
159
+
160
+ ## CI hardening (registry + ratchet parity)
161
+
162
+ Full-suite triage surfaced four registry/parity gates, all addressed: the new `guard-latch`
163
+ journal kind updated the `CoherenceJournal.getOwnAdvert` test enumeration; the six routes were
164
+ registered in `CapabilityIndex` (`/capabilities` discoverability); the migrator CLAUDE.md section
165
+ was tracked in the feature-delivery-completeness parity test (migrator-only, like Release
166
+ Readiness); and the ~20 new fail-toward-skip `catch` blocks were annotated `@silent-fallback-ok`
167
+ (with the dead `isFile` probe in `resolveBranchFromCwd` removed) so the no-silent-fallbacks ratchet
168
+ stays at its baseline. Three other full-suite reds were confirmed NOT from this change
169
+ (sharedStateRoutes passes in isolation — cross-talk; sign-instar-lockfile + the npm-pack
170
+ packageTemplateShape fail on local signing-key / `proper-lockfile` resolution; two e2e narratives
171
+ are pre-existing). CI's clean room is the authority.
@@ -0,0 +1,116 @@
1
+ # Side-Effects Review — WS2.1 preferences pool: learned preferences replicate across machines
2
+
3
+ **Version / slug:** `multi-machine-seamlessness-ws21-preferences-pool`
4
+ **Date:** `2026-06-13`
5
+ **Author:** `Instar Agent (echo)`
6
+ **Second-pass reviewer:** `4-dimension adversarial-review Workflow (16 agents) — security / correctness / integration / Phase-C; verdict + fixes appended below`
7
+ **Parent principle:** Cross-Machine Coherence — One Agent, Robust Under Degraded Conditions
8
+
9
+ ## Summary of the change
10
+
11
+ WS2.1 of the merged MULTI-MACHINE-SEAMLESSNESS-SPEC: the correction-learning
12
+ preference store (`PreferencesManager`, `.instar/preferences.json`) becomes
13
+ cross-machine. A preference learned on one machine is honored on the others. The
14
+ design mirrors the proven `commitments-sync` read-replication pattern: a new
15
+ `preferences-sync` MeshRpc verb (read/observe class), seq-windowed incarnation-fenced
16
+ delta serve, single-writer per-peer replica store with first-hop sender binding, and a
17
+ flag-gated union into `GET /preferences/session-context`. Preferences are ADVISORY
18
+ session-start signals (never authority), so this is read replication only — no
19
+ write-back, no election, no quorum.
20
+
21
+ The one genuine design fork vs. commitments (resolved): commitments union on composite
22
+ (origin,id) with no cross-origin merge; preferences COLLAPSE by `dedupeKey` (the same
23
+ lesson observed independently on two machines is ONE row) so the injected session
24
+ block never double-injects the same guidance.
25
+
26
+ ## Decision-point inventory
27
+
28
+ - Merge winner: newest `(recordedAt, originMachineId)` — HLC-light. `dedupeCount` sums
29
+ across distinct origins.
30
+ - Clock-skew: a `recordedAt` beyond `now + 24h` is untrustworthy → loses (treated as
31
+ oldest), not clamped-to-near-now. Within tolerance → normal recency.
32
+ - Replication scope: own records only on serve; `learning` redacted; `violationPattern`
33
+ stripped (local-only); per-peer record bound (default 500).
34
+ - Gate: `multiMachine.seamlessness.ws21PreferencesPool === true` AND a resolved
35
+ `meshSelfId` AND replicas present → merged; else the untouched own-only path.
36
+
37
+ ## 1. Over-block
38
+ None. The feature gates ADD reach (replicated preferences) and never block a message,
39
+ a route, or a session. Flag-off / single-machine = strict no-op.
40
+
41
+ ## 2. Under-block (the real risk surface)
42
+ The risk is the inverse: replicated preference TEXT is injected into the session-start
43
+ block. Mitigations: (a) `learning` is credential-shape-redacted at serve time; (b) the
44
+ block already wraps learned preferences as advisory, non-authoritative signals;
45
+ (c) first-hop binding rejects forged-origin rows; (d) replicas come only from
46
+ registered same-operator Ed25519-authenticated peers. The adversarial review probed
47
+ prompt-injection-via-preference-text — accepted residual: a compromised same-operator
48
+ machine could inject advisory text, but it cannot escalate to authority, and the
49
+ blast radius is one operator's own pool. Recorded as a known bound, not a blocker.
50
+
51
+ ## 3. Level-of-abstraction fit
52
+ The engine (`PreferencesSync.ts`) is pure/seam-injected (build/apply/merge are
53
+ unit-testable with in-memory fakes). Wiring sits in server.ts/routes.ts/
54
+ PeerPresencePuller exactly where `commitments-sync` does. No new transport, no new auth.
55
+
56
+ ## 4. Signal vs authority compliance
57
+ Fully signal-only. Preferences never gate or rewrite anything; the merged view only
58
+ changes which advisory lessons appear in the session-start block. The mesh verb is
59
+ read/observe (adds reach, never authority); the receiver re-binds origin.
60
+
61
+ ## 5. Interactions
62
+ - Rides the existing MeshRpc envelope + PeerPresencePuller cadence (no new tick).
63
+ - Independent `coherenceJournal.preferences` config section (does NOT couple to
64
+ commitments' tuning — review finding #4/#7).
65
+ - Coexists with #1095 (drain) and #1096 (attention pool) — rebased onto both; MeshRpc
66
+ carries `drain` + `preferences-sync` side by side.
67
+
68
+ ## 6. External surfaces
69
+ `GET /preferences/session-context` gains an optional `scope:'mesh'` marker + merged
70
+ block when the pool is on. `preferences-sync` mesh verb is internal machine-to-machine
71
+ (authenticated). No new user-facing HTTP auth surface.
72
+
73
+ ## 7. Multi-machine posture (Cross-Machine Coherence) + Phase C
74
+ - **N-machine, no 2-peer assumption:** one replica file per peer; the merge is O(total
75
+ records) across N peers; per-peer bound keeps the merged set bounded; the formatter is
76
+ byte-bounded so the session block can't blow up with N peers.
77
+ - **No LAN assumption:** per-peer HTTP over the existing pool fabric; an offline/slow
78
+ peer fails that peer gracefully (the puller doesn't block the tick).
79
+ - **Headless / cloud cold-start:** a fresh VM with no replicas pulls fully from seq 0
80
+ (incarnation seeded), no strand.
81
+ - **Degraded conditions:** corrupt replica → quarantine + re-pull; rewound store
82
+ (restore) → incarnation re-mint → peers re-pull wholesale.
83
+
84
+ ## 8. Rollback cost
85
+ Trivial: flip `ws21PreferencesPool` off (or leave default) → own-only, byte-identical
86
+ to pre-feature. The replica files under `state/preference-replicas/` are inert when the
87
+ flag is off. No migration of existing data; additive config + entry fields only.
88
+
89
+ ---
90
+
91
+ ## Second-pass review — 4-dimension adversarial Workflow (MANDATORY)
92
+
93
+ Ran a 16-agent adversarial review (4 parallel dimension reviewers → per-finding
94
+ skeptical verification, trust-model-aware). 12 raw findings → **7 confirmed**, ALL
95
+ FIXED + tested before ship:
96
+
97
+ 1. **HIGH (security) — violationPattern replicated.** The serve `...r` spread leaked
98
+ the user's local-only self-violation detection regex to peers. **Fix:** explicitly
99
+ strip `violationPattern` from `ReplicatedPreference`. Test: served row has no
100
+ `violationPattern`.
101
+ 2. **HIGH (security) — clock-skew recency manipulation.** `isNewer` used raw
102
+ `Date.parse` with no bounds; a future-skewed peer won every collision (the spec's
103
+ clock-skew requirement was unshipped). **Fix:** a timestamp beyond `now + 24h` is
104
+ untrustworthy → loses. Tests: future peer loses; legit-newer peer still wins.
105
+ (Full HLC logical counters tracked as follow-up: `<!-- tracked: WS2.1 HLC counters -->`.)
106
+ 3. **HIGH (correctness) — `meshSelfId ?? 'local'` fallback.** The 'local' sentinel
107
+ mismatched a peer's named origin and corrupted the own-echo filter. **Fix:** the
108
+ merge requires a real `meshSelfId`, else own-only. Test: source-asserted guard.
109
+ 4/5/7. **MEDIUM/LOW — wrong config section.** preferences-sync read
110
+ `coherenceJournal.commitments` for page sizing. **Fix:** dedicated
111
+ `coherenceJournal.preferences` section + type; both reads repointed.
112
+ 6. **LOW — meta-sidecar ordering.** Reviewed: `writeAtomic` fsyncs the store fd and
113
+ renames it BEFORE writing the sidecar, so the sidecar is never ahead of the store
114
+ (the rewind fence can't false-trip). No change needed; documented.
115
+
116
+ Post-fix: 72 WS2.1 tests green; build clean; dark-gate lint clean.