instar 1.3.497 โ†’ 1.3.499

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 (34) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +68 -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 +8 -0
  6. package/dist/config/ConfigDefaults.js.map +1 -1
  7. package/dist/core/PostUpdateMigrator.d.ts +2 -0
  8. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  9. package/dist/core/PostUpdateMigrator.js +147 -0
  10. package/dist/core/PostUpdateMigrator.js.map +1 -1
  11. package/dist/monitoring/CommitmentTracker.d.ts.map +1 -1
  12. package/dist/monitoring/CommitmentTracker.js +7 -1
  13. package/dist/monitoring/CommitmentTracker.js.map +1 -1
  14. package/dist/monitoring/PresenceProxy.d.ts +10 -0
  15. package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
  16. package/dist/monitoring/PresenceProxy.js +11 -0
  17. package/dist/monitoring/PresenceProxy.js.map +1 -1
  18. package/dist/monitoring/PromiseBeacon.d.ts +8 -0
  19. package/dist/monitoring/PromiseBeacon.d.ts.map +1 -1
  20. package/dist/monitoring/PromiseBeacon.js +13 -2
  21. package/dist/monitoring/PromiseBeacon.js.map +1 -1
  22. package/dist/monitoring/SpeakerElection.d.ts +75 -0
  23. package/dist/monitoring/SpeakerElection.d.ts.map +1 -0
  24. package/dist/monitoring/SpeakerElection.js +132 -0
  25. package/dist/monitoring/SpeakerElection.js.map +1 -0
  26. package/package.json +1 -1
  27. package/skills/instar-dev/SKILL.md +2 -1
  28. package/skills/instar-dev/templates/side-effects-artifact.md +30 -1
  29. package/skills/spec-converge/SKILL.md +1 -1
  30. package/src/data/builtin-manifest.json +19 -19
  31. package/upgrades/1.3.498.md +51 -0
  32. package/upgrades/1.3.499.md +58 -0
  33. package/upgrades/side-effects/multi-machine-posture-review-dimension.md +76 -0
  34. package/upgrades/side-effects/multi-machine-seamlessness-ws3.md +125 -0
@@ -0,0 +1,58 @@
1
+ # Upgrade Guide โ€” vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ On a multi-machine pool, two of the agent's background voices had no machine
9
+ coordination: the ๐Ÿ”ญ standby notices (PresenceProxy) had no machine-ownership gate at
10
+ all โ€” every machine could answer for the same conversation โ€” and the โณ commitment
11
+ heartbeats (PromiseBeacon) HAD a gate, but it compared against a field
12
+ (`ownerMachineId`) that nothing ever populated, so it was silently inert on every
13
+ deployed agent (audit findings F18/F19, MULTI-MACHINE-SEAMLESSNESS-SPEC WS3.1/3.2).
14
+
15
+ This ships the one-voice layer: a shared deterministic `SpeakerElection`
16
+ (`src/monitoring/SpeakerElection.ts`) consulted by both sentinels at their single
17
+ emission chokepoints. The topic's owner machine speaks; unknown ownership FAILS TOWARD
18
+ SPEECH (lease-holder, then a deterministic lowest-online-id tiebreak) so the pool is
19
+ never silent โ€” the failure mode the convergence review proved is worse than
20
+ double-voice. A lease-stability dwell defers decisions during a lease flap (bounded,
21
+ then tiebreak) and holds a chosen speaker's identity so the voice can't alternate.
22
+ Commitments now record `ownerMachineId` at creation, the beacon re-resolves the LIVE
23
+ owner at speak time (the stamp is only a fallback), and a PostUpdateMigrator backfill
24
+ stamps existing open commitments on deployed agents (idempotent; skipped-not-marked
25
+ when no machine identity exists yet).
26
+
27
+ Ships DARK behind `multiMachine.seamlessness.ws3OneVoice` (default false โ€” verdicts
28
+ are byte-for-byte today's behavior). Single-machine agents are structurally inert at
29
+ every layer (election never engages below 2 online machines), locked by tests.
30
+
31
+ ## What to Tell Your User
32
+
33
+ - "When your agent runs on more than one machine, exactly one machine now speaks for
34
+ each conversation's background notices โ€” no more two machines answering the same
35
+ question, and (just as important) never both staying silent because each assumed
36
+ the other had it."
37
+
38
+ ## Summary of New Capabilities
39
+
40
+ | Capability | How to Use |
41
+ |-----------|-----------|
42
+ | One-voice gating for standby + commitment notices | Enable `multiMachine.seamlessness.ws3OneVoice` (multi-machine pools only; dark by default) |
43
+
44
+ ## Evidence
45
+
46
+ - `tests/unit/SpeakerElection.test.ts` โ€” 13 tests including the exactly-one-speaks
47
+ invariant simulated across whole pools (owner known, owner unknown, no lease-holder,
48
+ OFFLINE lease-holder, flapping lease with bounded defer, dwell hold) and the strict
49
+ legacy/no-op guards.
50
+ - `tests/unit/ws3-one-voice-wiring.test.ts` โ€” 11 tests: creation stamp defaults,
51
+ gate seams in both sentinels, one shared election wired server-side, local-only
52
+ ownership reads on the hot path, migration idempotency + the
53
+ no-identity-skips-without-marking retry path.
54
+ - All affected suites green (PromiseBeacon ร—3, PresenceProxy ร—5, CommitmentTracker ร—2,
55
+ beacon integration ร—3); `tsc --noEmit` clean; full build green.
56
+ - Side-effects artifact: `upgrades/side-effects/multi-machine-seamlessness-ws3.md`
57
+ (second-pass review: required and recorded in the artifact).
58
+ - Spec: `docs/specs/MULTI-MACHINE-SEAMLESSNESS-SPEC.md` (converged + approved, on main).
@@ -0,0 +1,76 @@
1
+ # Side-Effects Review โ€” Multi-machine posture review dimension (Cross-Machine Coherence widening)
2
+
3
+ **Trigger:** operator question, 2026-06-12 topic 13481 ("we have developed so many
4
+ features that have gaps โ€ฆ is it the constitution or our enforcement?"). The audit
5
+ answer: both, but chiefly enforcement โ€” NO review surface ever asked the multi-machine
6
+ question, so ~20 features shipped machine-blind.
7
+ **Change:** (1) side-effects template gains mandatory ยง7 "Multi-machine posture"
8
+ (replicated / proxied-on-read / machine-local-by-design-with-reason; single-machine
9
+ assumption is a finding, not a posture); (2) the matching Phase-4 question in the
10
+ instar-dev SKILL; (3) a mandatory posture check in spec-converge's
11
+ integration/deployment reviewer charter; (4) the Cross-Machine Coherence article's
12
+ "Applied through" widened to name these gates (so the conformance audit can verify
13
+ them); (5) PostUpdateMigrator migration delivering the updated skill content to
14
+ deployed agents (Migration Parity, "updating existing skill content" โ€” pattern copy of
15
+ migrateSpecConvergeFoundationAudit: marker-sniffed, fingerprint-guarded, idempotent,
16
+ customized files untouched).
17
+
18
+ ## 1. Over-block
19
+ The new review question blocks nothing programmatically โ€” it is a required SECTION in
20
+ a review document and a reviewer charter line. The cost it adds is one honest
21
+ paragraph per change. A genuinely machine-agnostic change answers "machine-local by
22
+ design: <reason>" in one line. No legitimate change becomes unshippable.
23
+
24
+ ## 2. Under-block
25
+ The question is willpower-adjacent until the conformance audit verifies the section's
26
+ presence (the registry now names the gates, which is what the Standards Enforcement
27
+ Coverage audit reads). A reviewer can still write a lazy posture answer โ€” the
28
+ spec-converge integration reviewer's charter makes a silent single-machine assumption
29
+ a MATERIAL finding, which is the structural counterweight. Programmatic
30
+ section-presence checking in the pre-commit gate is a natural ratchet if lazy answers
31
+ show up (the registry text gives it a home).
32
+
33
+ ## 3. Level-of-abstraction fit
34
+ Right layer: the defect was a missing QUESTION at review time, so the fix adds the
35
+ question to both review surfaces (side-effects + spec-converge) and anchors it in the
36
+ constitution article those surfaces trace to. Not duplicated logic โ€” one rule, named
37
+ in the registry, asked at the two existing chokepoints.
38
+
39
+ ## 4. Signal vs authority compliance
40
+ No new blocking authority. The template/charter additions are review-content
41
+ requirements; the only "block" is the existing spec-converge materiality bar, which is
42
+ already the convergence mechanism. The migration is a content patch with no decision
43
+ logic.
44
+
45
+ ## 5. Interactions
46
+ - The migration follows the established skill-content-update pattern and runs once
47
+ (marker check). It cannot fight installBuiltinSkills (which never overwrites).
48
+ - Renumbering Rollback cost ยง7โ†’ยง8 in the template: existing committed artifacts are
49
+ unaffected (the pre-commit gate checks artifact existence/coverage, not section
50
+ numbering).
51
+ - The spec-converge charter addition extends the integration reviewer's prompt
52
+ composition; no phase logic changes.
53
+
54
+ ## 6. External surfaces
55
+ None outside the install base. Deployed agents receive updated skill files on their
56
+ next update via the migration; operator-customized files are left untouched and
57
+ reported in the migration result.
58
+
59
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
60
+ **machine-local BY DESIGN, with reason** โ€” these are agent-installed skill/template
61
+ files: each machine's install legitimately carries its own copy at its own installed
62
+ version, kept current per machine by PostUpdateMigrator on that machine's update
63
+ cycle. Version skew between machines is bounded by the existing update train (drift
64
+ promoter). No user-facing notices, no durable runtime state, no generated URLs.
65
+ (This section is itself the new question, answered โ€” dogfooded on its own PR.)
66
+
67
+ ## 8. Rollback cost
68
+ Revert the template/SKILL/registry edits and ship; the migration is marker-gated so a
69
+ reverted bundle simply stops patching (already-patched agents keep the question โ€” a
70
+ benign residue: it asks reviewers for one honest paragraph). No data, no config, no
71
+ runtime behavior.
72
+
73
+ ## Second-pass review
74
+ Not required โ€” no block/allow decisions, no session lifecycle, no
75
+ gate/sentinel/watchdog runtime surface (docs/skills content + a content-patch
76
+ migration following an established pattern).
@@ -0,0 +1,125 @@
1
+ # Side-Effects Review โ€” WS3.1/WS3.2 one-voice gates (SpeakerElection)
2
+
3
+ **Spec:** docs/specs/MULTI-MACHINE-SEAMLESSNESS-SPEC.md (converged 2026-06-12, approved; merged to main in PR #1083)
4
+ **Change:** new `src/monitoring/SpeakerElection.ts` (deterministic speaker election with
5
+ lease-stability dwell); PresenceProxy gate at its single send chokepoint (closes F18 โ€”
6
+ it had NO machine gate); PromiseBeacon gate upgraded to live owner re-resolution with
7
+ the commitment stamp as fallback (closes F19); CommitmentTracker stamps
8
+ `ownerMachineId` at creation (defaulting to originMachineId); PostUpdateMigrator
9
+ backfills existing open commitments; dark config flag
10
+ `multiMachine.seamlessness.ws3OneVoice` (default false) + `ws3DwellMs`.
11
+
12
+ ## 1. Over-block โ€” what legitimate emissions could this wrongly silence?
13
+
14
+ The known hazard (round-1 adversarial finding, addressed in the converged spec): a
15
+ gate keyed on "am I the owner?" can conclude "no" on EVERY machine and silence the
16
+ pool. The election is built against that direction of error:
17
+ - Unknown ownership NEVER yields pool-wide silence: lease-holder speaks; no/offline
18
+ lease-holder โ†’ deterministic lowest-online-id tiebreak; flapping lease โ†’ bounded
19
+ defer then tiebreak. The exactly-one-speaks invariant test asserts โ‰ฅ1 as well as โ‰ค1,
20
+ including under a flapping lease.
21
+ - Every guard failure (flag off, no machine id, pool < 2, deps unbound during the
22
+ boot window) returns "speak" โ€” fail toward speech.
23
+ - PromiseBeacon's silent verdicts RE-ARM the commitment (schedule), never drop it; the
24
+ owning machine's beacon carries the heartbeat.
25
+ Residual over-block: a STALE local placement view could say "owner is the other
26
+ machine" while that machine is dead โ€” its election (not running) can't speak, ours
27
+ stays silent for that topic until placement converges. Mitigation: ownership records
28
+ key the election only when status is 'active'; a dead machine's records converge via
29
+ the existing failover path, and PresenceProxy's cadence re-evaluates each tick.
30
+ Accepted as bounded-by-failover; noted for the WS1.3 reconcile build which shortens
31
+ exactly this window. Second residual (independent reviewer finding): a held silent
32
+ verdict can outlive the chosen speaker's departure for up to one dwell window
33
+ (default 60s) plus one cadence tick on a doubly-degraded path (speaker offline
34
+ mid-dwell + unknown ownership + no online lease-holder) โ€” bounded, self-healing
35
+ (re-election after dwell expiry), and no emission is dropped (both consumers re-arm).
36
+
37
+ ## 2. Under-block โ€” what double-voice does this still miss?
38
+
39
+ - Both gates only cover PresenceProxy + PromiseBeacon (the F18/F19 surfaces named in
40
+ the spec). Other notice paths (rate-limit sentinel, recovery notices โ€” F23/WS3.3)
41
+ are explicitly a separate spec item with episode-key dedup; not silently claimed.
42
+ - The โ‰ฅ1/โ‰ค1 invariant holds when machines agree on the replicated inputs. During a
43
+ partition with divergent placement views, two machines can transiently both believe
44
+ they own a topic and both speak โ€” the same exposure as today (no regression), fully
45
+ closed only by the WS1 epoch-fenced reconcile family.
46
+
47
+ ## 3. Level-of-abstraction fit
48
+
49
+ One shared deterministic module consumed by both sentinels at their existing
50
+ chokepoints โ€” not two bespoke gates (the F18 finding showed exactly how per-sentinel
51
+ divergence happens). The election reads only local replicated state (ownership
52
+ registry, capacity registry, lease status) โ€” never a mesh call on the emission path
53
+ (spec hot-path rule). The dwell lives in the module so every consumer inherits it.
54
+
55
+ ## 4. Signal vs authority compliance
56
+
57
+ The election holds NARROW authority: it gates WHO SPEAKS a duplicate-prone sentinel
58
+ notice โ€” never recovery actions, never message content, never anything user-initiated
59
+ (spec "Decision points touched": WS3 gates sentinel SPEECH only). Its logic is
60
+ deterministic over replicated state (not brittle pattern-matching), it fails open to
61
+ "speak" on every uncertainty, ships dark, and every verdict is observable (onVerdict
62
+ hook โ†’ server log; P7). This is the blocking-authority shape the converged spec
63
+ explicitly reviewed and approved across three rounds.
64
+
65
+ ## 5. Interactions
66
+
67
+ - The per-topic proxy mutex (PromiseBeacon/PresenceProxy double-post guard, spec A10)
68
+ is untouched and still applies AFTER the election โ€” the two compose (election picks
69
+ the machine, mutex serializes emitters within it).
70
+ - PromiseBeacon's legacy static gate remains for election-absent construction
71
+ (back-compat for tests/embedders); the election branch supersedes it when wired.
72
+ - The CommitmentTracker stamp default cannot collide with cross-machine commitment
73
+ sync (P1.5): originMachineId was already recorded; ownerMachineId now mirrors it at
74
+ creation and remains mutable via the existing CAS mutate path.
75
+ - The migration touches state/commitments.json directly at update time (server down/
76
+ pre-boot โ€” same pattern as every other PostUpdateMigrator state migration); it
77
+ stamps only missing fields on 'pending' records.
78
+
79
+ ## 6. External surfaces
80
+
81
+ None new. No routes, no message formats, no cross-machine wire changes. The flag is
82
+ config-local. Single-machine agents: structurally inert (no SpeakerElection
83
+ engagement below 2 online machines; CommitmentTracker stamp is absent without an
84
+ originMachineId โ€” both tested).
85
+
86
+ ## 7. Rollback cost
87
+
88
+ Flag-flip: `multiMachine.seamlessness.ws3OneVoice: false` (the default) restores
89
+ legacy verdicts instantly at the next decision โ€” no restart-ordering hazard since the
90
+ flag is read live per decision. The backfilled ownerMachineId stamps are inert data
91
+ when the flag is off (the legacy gate only acts when currentMachineId is ALSO set,
92
+ i.e. multi-machine pools) and are correct-by-construction on single-machine agents;
93
+ no data rollback needed.
94
+
95
+ ## Second-pass review
96
+
97
+ REQUIRED (sentinel surface). Reviewer response appended below.
98
+
99
+ <!-- second-pass reviewer response appended below by the independent reviewer -->
100
+
101
+ ---
102
+
103
+ ## Second-pass reviewer response (independent subagent, 2026-06-12)
104
+
105
+ Concur with the review
106
+
107
+ 1. **Fail-toward-speech verified on every uncertain path.** Both consumer gates skip the send ONLY when `!verdict.speak` (PromiseBeacon.ts:360, PresenceProxy.ts:1847), and `SpeakerElection.decideInner` returns `speak:true` on every legacy/no-op/ambiguous branch: flag-off (`legacy-disabled`), no machine id, pool<2 (`single-machine`), owner-self, stamp-self, lease-holder-self, and the deterministic `tiebreak-lowest-id` whenever the lease holder is null or offline. The guards are ordered legacy/no-op FIRST, so none of the election machinery runs when disabled. The boot-window no-op is real: `poolMachineIds()` returns `[]` until `ws3PoolDeps` binds, yielding `single-machine`/speak. The flag is read live (`() => ws3Cfg().ws3OneVoice === true`) so the rollback flip takes effect at the next decision with no restart โ€” artifact ยง7 is accurate.
108
+
109
+ 2. **Silent verdicts re-arm, never drop.** PromiseBeacon calls `this.schedule(c)` on a non-speak verdict, which re-arms at the commitment cadence (default 10min, 1s floor โ€” no busy-loop); PresenceProxy logs and returns, and its own cadence re-evaluates. No emission is lost on a silent/defer verdict. The legacy static gate is preserved for election-absent construction as the artifact ยง5 claims.
110
+
111
+ 3. **Migration direction-of-error is safe and idempotent.** `migrateCommitmentOwnerBackfill` stamps ONLY `status==='pending'` records lacking `ownerMachineId`, with THIS machine's own identity.json machineId โ€” and crucially, when no identity exists it skips WITHOUT writing the marker, so it retries on a later update rather than permanently no-op'ing. The stamp is only a fallback; live placement re-resolution at speak time means a stale backfilled stamp racing a transfer cannot wedge the gate (live owner beats stamp; tested). The single-machine no-op is genuinely byte-identical: no `originMachineId` โ†’ `ownerMachineId` stays absent (tested).
112
+
113
+ 4. **No authority beyond sentinel speech; cannot gate recovery/delivery/user flows.** The election is consumed at exactly two chokepoints โ€” `PresenceProxy.sendProxyMessage` and `PromiseBeacon.fire` โ€” both of which emit duplicate-prone ๐Ÿ”ญ/heartbeat notices. It is injected as an optional dep; it touches no recovery action, no message content, no user-initiated path, and `resolveTopicOwner` reads only local replicated state with no mesh/fetch call on the hot path (asserted by test). The `onVerdict` hook is try/catch-swallowed so observability can never gate. I agree with the artifact's "no issue identified" answers in ยง3โ€“ยง6.
114
+
115
+ 5. **Bounded memory; โ‰ค1 invariant holds.** The `held` map is opportunistically swept past the dwell window at size>512 and `unstableSince` entries are deleted on every decisive path โ€” no unbounded growth. Two machines cannot BOTH speak for a topic under agreed replicated inputs: live owner is single-valued, the stamp comparison is to one id, and the tiebreak picks one `sort()[0]`. (Divergent-placement double-voice during a partition is correctly scoped to WS1 and disclosed as no-regression in ยง2.)
116
+
117
+ **One verification note for the record (bounded, not a blocker):** the speaker-identity dwell can HOLD a silent verdict, and the held branch returns it irrespective of current pool state. If the chosen speaker machine drops offline mid-dwell while ownership is unknown AND there is no online lease holder AND the remaining pool is still โ‰ฅ2, the surviving machines can each return a fresh held-silent verdict, producing a transient ALL-silent window for that topic. This does NOT violate the spec's โ‰ฅ1 framing ("never pool-wide / never unbounded silence"): it is bounded by `dwellMs` (default 60s) plus one cadence tick, it self-heals (after dwell expiry the new lowest-id re-elects to `tiebreak-lowest-id` and speaks), no emission is dropped (both consumers re-arm), and the `single-machine` guard correctly rescues the pool-of-1 case by firing before the dwell check. It is strictly no worse in outcome than the partition exposure already disclosed in ยง2, and the gate holds no authority beyond speech โ€” an acceptable residual on a doubly-degraded path, not a defect. Recorded in ยง1's residual list.
118
+
119
+ ## Addendum โ€” no-silent-fallbacks ratchet (CI round 1)
120
+
121
+ The three new fail-open guards in server.ts (identity read, pool-ids read, ownership
122
+ read) tripped the repo-wide silent-fallback ratchet (466 > 463). Each now carries the
123
+ sanctioned `@silent-fallback-ok` exemption with its reason inline: every one degrades
124
+ to the election's designed fail-toward-speech/no-op verdict โ€” the documented safe
125
+ direction, not a silent loss. Ratchet count restored to baseline.