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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +68 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +8 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +2 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +147 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/monitoring/CommitmentTracker.d.ts.map +1 -1
- package/dist/monitoring/CommitmentTracker.js +7 -1
- package/dist/monitoring/CommitmentTracker.js.map +1 -1
- package/dist/monitoring/PresenceProxy.d.ts +10 -0
- package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
- package/dist/monitoring/PresenceProxy.js +11 -0
- package/dist/monitoring/PresenceProxy.js.map +1 -1
- package/dist/monitoring/PromiseBeacon.d.ts +8 -0
- package/dist/monitoring/PromiseBeacon.d.ts.map +1 -1
- package/dist/monitoring/PromiseBeacon.js +13 -2
- package/dist/monitoring/PromiseBeacon.js.map +1 -1
- package/dist/monitoring/SpeakerElection.d.ts +75 -0
- package/dist/monitoring/SpeakerElection.d.ts.map +1 -0
- package/dist/monitoring/SpeakerElection.js +132 -0
- package/dist/monitoring/SpeakerElection.js.map +1 -0
- package/package.json +1 -1
- package/skills/instar-dev/SKILL.md +2 -1
- package/skills/instar-dev/templates/side-effects-artifact.md +30 -1
- package/skills/spec-converge/SKILL.md +1 -1
- package/src/data/builtin-manifest.json +19 -19
- package/upgrades/1.3.498.md +51 -0
- package/upgrades/1.3.499.md +58 -0
- package/upgrades/side-effects/multi-machine-posture-review-dimension.md +76 -0
- 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.
|