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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +168 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +42 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/CoherenceJournal.d.ts +31 -1
- package/dist/core/CoherenceJournal.d.ts.map +1 -1
- package/dist/core/CoherenceJournal.js +49 -2
- package/dist/core/CoherenceJournal.js.map +1 -1
- package/dist/core/JournalSyncApplier.d.ts.map +1 -1
- package/dist/core/JournalSyncApplier.js +18 -0
- package/dist/core/JournalSyncApplier.js.map +1 -1
- package/dist/core/MeshRpc.d.ts +6 -0
- package/dist/core/MeshRpc.d.ts.map +1 -1
- package/dist/core/MeshRpc.js +1 -0
- package/dist/core/MeshRpc.js.map +1 -1
- package/dist/core/PeerPresencePuller.d.ts +19 -0
- package/dist/core/PeerPresencePuller.d.ts.map +1 -1
- package/dist/core/PeerPresencePuller.js +7 -0
- package/dist/core/PeerPresencePuller.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +57 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/PreferencesManager.d.ts +34 -0
- package/dist/core/PreferencesManager.d.ts.map +1 -1
- package/dist/core/PreferencesManager.js +74 -0
- package/dist/core/PreferencesManager.js.map +1 -1
- package/dist/core/PreferencesSync.d.ts +167 -0
- package/dist/core/PreferencesSync.d.ts.map +1 -0
- package/dist/core/PreferencesSync.js +307 -0
- package/dist/core/PreferencesSync.js.map +1 -0
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +5 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +34 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/GreenPrAutoMerger.d.ts +188 -0
- package/dist/monitoring/GreenPrAutoMerger.d.ts.map +1 -0
- package/dist/monitoring/GreenPrAutoMerger.js +393 -0
- package/dist/monitoring/GreenPrAutoMerger.js.map +1 -0
- package/dist/monitoring/GuardLatchStore.d.ts +134 -0
- package/dist/monitoring/GuardLatchStore.d.ts.map +1 -0
- package/dist/monitoring/GuardLatchStore.js +255 -0
- package/dist/monitoring/GuardLatchStore.js.map +1 -0
- package/dist/monitoring/MergeRunner.d.ts +126 -0
- package/dist/monitoring/MergeRunner.d.ts.map +1 -0
- package/dist/monitoring/MergeRunner.js +296 -0
- package/dist/monitoring/MergeRunner.js.map +1 -0
- package/dist/monitoring/floorDriftCanary.d.ts +88 -0
- package/dist/monitoring/floorDriftCanary.d.ts.map +1 -0
- package/dist/monitoring/floorDriftCanary.js +89 -0
- package/dist/monitoring/floorDriftCanary.js.map +1 -0
- package/dist/monitoring/greenPrAutomergeWiring.d.ts +73 -0
- package/dist/monitoring/greenPrAutomergeWiring.d.ts.map +1 -0
- package/dist/monitoring/greenPrAutomergeWiring.js +273 -0
- package/dist/monitoring/greenPrAutomergeWiring.js.map +1 -0
- package/dist/monitoring/greenPrLogic.d.ts +121 -0
- package/dist/monitoring/greenPrLogic.d.ts.map +1 -0
- package/dist/monitoring/greenPrLogic.js +198 -0
- package/dist/monitoring/greenPrLogic.js.map +1 -0
- package/dist/monitoring/guardManifest.d.ts.map +1 -1
- package/dist/monitoring/guardManifest.js +12 -0
- package/dist/monitoring/guardManifest.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +1 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +5 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +3 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +16 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +10 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +137 -1
- package/dist/server/routes.js.map +1 -1
- package/dist/server/stopGate.d.ts +45 -0
- package/dist/server/stopGate.d.ts.map +1 -1
- package/dist/server/stopGate.js +74 -0
- package/dist/server/stopGate.js.map +1 -1
- package/package.json +1 -1
- package/scripts/safe-merge.mjs +449 -59
- package/src/data/builtin-manifest.json +64 -64
- package/src/scaffold/templates.ts +1 -0
- package/upgrades/1.3.512.md +65 -0
- package/upgrades/1.3.513.md +61 -0
- package/upgrades/side-effects/green-pr-automerge-enforcement.md +171 -0
- 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.
|