instar 1.3.520 → 1.3.521

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.
@@ -0,0 +1,69 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: minor -->
5
+
6
+ ## What Changed
7
+
8
+ The generic machinery that future cross-machine memory stores (preferences, relationships, learnings, …) will plug into. This step builds the reusable substrate ONLY — it adds no concrete store kind (that lands with the first store, WS2.1).
9
+
10
+ - **Replicated-record envelope** (`src/core/ReplicatedRecordEnvelope.ts`) — the fields every replicated change carries on top of its store-specific data: `recordKey`, the `HybridLogicalClock` stamp (`hlc`), a `put`/`delete` `op`, the author machine (`origin`), and the single prior stamp the author had already merged for that key (`observed` — the last-writer-witness; absent ⇒ "no prior witness" ⇒ flag-on-conflict, the safe direction). A strict, parameterizable validator mirrors the coherence-journal typed-schema discipline (rejects free text, drops + counts unknown fields, jails path-shaped fields, validates the HLC fields). A `ReplicatedKindRegistry` (ships EMPTY) is the registration mechanism each store will use.
11
+ - **Flag-gated emission (dark per store)** — a store emits its kind only when `multiMachine.stateSync.<store>.enabled` is on (default false). When off, no journal traffic — a strict no-op.
12
+ - **Flag-coherence-gated emission** — a kind is forwarded to a peer ONLY when that peer advertises (`seamlessnessFlags.stateSyncReceive`) it can receive it. Emitting a new kind to an older peer would be silently dropped by the applier — the named data-loss skew mode this gate prevents. The per-peer decision is correct for N peers; a boot-time pool-flag-coherence check surfaces a mixed-flag pool ONCE, coalesced.
13
+ - **Config + invariants** (`src/core/stateSyncConfig.ts`, `ConfigDefaults.ts`, `types.ts`) — the foundation-level `multiMachine.stateSync` knobs (journal budget, the §3.4 HLC drift ceiling, snapshot-cache bounds), validated at startup by `validateStateSyncInvariants()` (an out-of-range value is REJECTED, not silently coerced), backfilled to existing agents via the add-missing migration path.
14
+
15
+ Pure MECHANISM, dark by default. The only two refusal surfaces are at the receive door (the validator rejects malformed data) and the emission door (don't forward to a non-advertising peer); neither blocks a user-initiated action. A single-machine install is a strict no-op.
16
+
17
+ Two cohesive changes (topic 22367, CMT-1434):
18
+
19
+ 1. **Mandates dashboard tab redesign.** The Mandates card now leads with the Grant
20
+ form as its primary, always-open block; Revoke is demoted to a quiet, collapsed
21
+ control beneath it. Raw JSON bounds, agent fingerprints, and scope slugs are
22
+ replaced with a plain-language summary sentence; identifiers survive only on a
23
+ muted "For support" line. Existing grants read as plain English ("Adam Admin can
24
+ deploy to production until 9:37 PM — authorized by you"), and the decision-audit
25
+ table stacks into labelled rows at phone width so the reason column is never
26
+ truncated. Renderer + markup + CSS only — the mandate API, payloads, and
27
+ PIN-never-retained discipline are unchanged. Ships via `dashboard/` static
28
+ serving, so it reaches every agent through the normal update path.
29
+
30
+ 2. **New constitutional standard: Operator-Surface Quality** (sibling to
31
+ Mobile-Complete Operator Actions) in `docs/STANDARDS-REGISTRY.md`. Where
32
+ Mobile-Complete asks "can the operator do this from a phone?", this asks "is it
33
+ actually good when they do?" — lead with the primary action, zero raw internals,
34
+ de-emphasized destructive actions, plain language, phone-width layout. It lands
35
+ with teeth: a new operator-surface-quality question in the instar-dev
36
+ side-effects review template, and a pre-commit gate
37
+ (`scripts/instar-dev-precommit.js`) that blocks any commit touching an operator
38
+ surface unless the review answers it. The standard names that gate, so the
39
+ Standards-Enforcement-Coverage audit classifies it as an enforced gate.
40
+
41
+ ## What to Tell Your User
42
+
43
+ None — internal substrate (no user-facing surface). The replicated cross-machine memory stores that this foundation enables ship later, store by store, each with its own announcement when its user-facing surface (conflict viewing, rollback) lands.
44
+
45
+ - **A cleaner Mandates screen on your phone**: "I gave the Mandates tab a real
46
+ overhaul. The action you actually came to do — granting someone permission — is
47
+ now front and centre, the risky Revoke button is tucked quietly out of the way,
48
+ and the screen reads in plain English instead of raw data. The history list also
49
+ finally fits your phone without cutting off the reason column."
50
+ - **A new quality bar for everything you touch**: "I also turned this into a
51
+ standing rule for myself: any screen you use to approve or decide something has to
52
+ be genuinely good to use on your phone, not just technically reachable. I added an
53
+ automatic check that holds me to it whenever I build one of those screens."
54
+
55
+ ## Summary of New Capabilities
56
+
57
+ None — internal change. This is the reusable substrate the first concrete replicated store (the cross-machine preferences pool, WS2.1) will register a journal kind onto; it exposes no new endpoint, config a user would set, or behavior a user would notice until a store turns its flag on.
58
+
59
+ | Capability | How to Use |
60
+ |-----------|-----------|
61
+ | Redesigned Mandates tab (humanized, mobile-first) | Open the Mandates tab in the dashboard |
62
+ | Operator-Surface Quality standard | docs/STANDARDS-REGISTRY.md (constitution) |
63
+ | Operator-surface-quality pre-commit gate | automatic during instar-dev work |
64
+
65
+ ## Evidence
66
+
67
+ Tier-1 unit tests in `tests/unit/ReplicatedRecordEnvelope.test.ts` (46 tests, all green) cover both sides of every boundary: the validator (valid put/delete; missing recordKey/hlc; malformed hlc; observed present-valid / present-malformed-rejected / absent-legal; unknown-field-dropped-and-counted; path-shaped field jailed; non-object/free-text rejected; store-schema rejection), the registry (empty default, unregistered-kind absent, conflict-throws, idempotent re-register), flag-gated emission (enabled=false ⇒ no emission; enabled=true ⇒ emits), flag-coherence (advertising ⇒ emit; non-advertising ⇒ withhold + surface; 3+-peer mix ⇒ per-peer correctness + ONE coalesced surface), `validateStateSyncInvariants` (maxDriftMs floor/ceiling/in-range), and a wiring-integrity case for the advert self-report (driven by registered+enabled stores, never a hardcoded true).
68
+
69
+ Gate-parity (run in the worktree): `tsc --noEmit` clean; `no-silent-fallbacks` = 471 (= BASELINE, no bump — the new files carry no un-tagged silent catches); `docs-coverage --check` PASS (class floor 55% held — `ReplicatedRecordEnvelope` documented in `multi-machine.md` + `under-the-hood.md`); `feature-delivery-completeness` PASS; `lint-dev-agent-dark-gate` PASS (the config line-map recomputed for the +17 cartographer shift); focused suites `CoherenceJournal*` / `JournalSyncApplier*` / `CoherenceJournalReader` + the new test all green.
@@ -0,0 +1,104 @@
1
+ # Side-Effects Review — Replicated-store foundation Step 2 (journal-kind tagging + flag-gated emission)
2
+
3
+ **Version / slug:** `multi-machine-replicated-store-foundation-step2-journal-kind`
4
+ **Date:** `2026-06-13`
5
+ **Author:** `Echo (instar-dev subagent)`
6
+ **Second-pass reviewer:** `not required`
7
+
8
+ ## Summary of the change
9
+
10
+ Step 2 of the multi-machine replicated-store foundation builds the GENERIC machinery that the concrete replicated stores (preferences, relationships, learnings, …) will layer a journal kind onto — it adds NO concrete store kind itself. New module `src/core/ReplicatedRecordEnvelope.ts` defines (A) the replicated-record envelope type (`recordKey`, `hlc`, `op`, `origin`, `observed`) plus a strict, parameterizable validator that mirrors the CoherenceJournal typed-schema discipline (reject free text, drop+count unknown fields, jail path-shaped fields, validate HLC fields); (B) a `ReplicatedKindRegistry` (ships empty — the registration mechanism, no concrete kind); (C) flag-gated emission (`isStoreEmissionEnabled` — a store emits only when `multiMachine.stateSync.<store>.enabled`, default false); and (D) flag-coherence-gated emission (`shouldEmitToPeer` + `checkPoolFlagCoherence` — never forward a kind to a peer that does not advertise it can receive it, N-peer-correct, one coalesced surface). New module `src/core/stateSyncConfig.ts` resolves + validates the foundation-level `stateSync` knobs (`validateStateSyncInvariants`, the §3.4 maxDriftMs clamp). Config defaults (`ConfigDefaults.ts` `multiMachine.stateSync`), a `StateSyncConfig` type + the `stateSyncReceive` advert flag (`types.ts`), and the server wiring (startup invariant assertion, the empty registry, the advert self-report, the boot-time coherence check) complete it. The change is pure MECHANISM, dark by default; a single-machine install is a strict no-op.
11
+
12
+ ## Decision-point inventory
13
+
14
+ - `validateReplicatedEnvelope` (RECEIVE door) — **add** — rejects malformed replicated-record data before it can enter a stream. The only "block" surface, and it blocks DATA, never a user action.
15
+ - `isStoreEmissionEnabled` / `shouldEmitToPeer` (EMISSION door) — **add** — withholds emitting a store's kind when the store is dark or a peer can't receive it. Withholds JOURNAL TRAFFIC, never a user action.
16
+ - `validateStateSyncInvariants` (startup config gate) — **add** — rejects an out-of-range foundation knob at boot (loud, not silent), mirroring `validateSeamlessnessInvariants`.
17
+ - `seamlessnessFlags.stateSyncReceive` (capability advert) — **add** — self-reported per-store receive capability in the capacity heartbeat.
18
+
19
+ ---
20
+
21
+ ## 1. Over-block
22
+
23
+ The validator rejects: a non-object/array; a missing/empty/oversized/path-shaped `recordKey`; a malformed/missing `hlc`; an `op` outside `{put,delete}`; a missing/empty/path-shaped `origin`; a PRESENT-but-malformed `observed`. These are all genuinely-malformed records — there is no legitimate replicated record that fails them. The one deliberate design choice that could look like over-block is the path-shape jail on `recordKey`/`origin`: a store whose primary key legitimately contains a `/` would be rejected. This is intentional (§4 "jail any path-shaped field") — the envelope carries identifiers, not paths, and a concrete store (WS2.1) must choose a non-path key. ABSENT `observed` is explicitly NOT rejected (legal ⇒ "no prior witness" ⇒ flag-on-conflict, the safe direction). No user-message surface exists, so no user input is over-blocked.
24
+
25
+ ---
26
+
27
+ ## 2. Under-block
28
+
29
+ The validator does NOT semantically validate store-specific fields beyond delegating to the supplied `StoreFieldSchema` — a store that supplies a permissive schema could let through store data the envelope can't reason about. This is by design (parameterizable substrate; the store owns its field discipline) — BUT the envelope's own LOAD-BEARING fields are now protected against a buggy/hostile store by construction (post-adversarial-review hardening, findings #1–#3):
30
+ - **A store can NEVER override a reserved envelope field on `data`.** `validateReplicatedEnvelope` strips every `RESERVED_ENVELOPE_FIELDS` key (`op`/`recordKey`/`hlc`/`origin`/`observed`) from the store's returned object (counting each as a dropped field) and spreads the store fields FIRST, the VALIDATED envelope fields LAST — so `data.op/recordKey/hlc/origin/observed` always equal the validated `envelope.*`. The earlier `...storeFields`-last ordering let a store's un-validated, un-jailed copy win on a key collision; that divergence is closed (finding #1).
31
+ - **A schema cannot claim a reserved field name.** `ReplicatedKindRegistry.register()` throws (a wiring-time programmer error, like the conflict throws) if `knownFields` intersects `RESERVED_ENVELOPE_FIELDS` — the reserved constant is now ENFORCED, not merely documented for self-check (finding #2).
32
+ - **Reusable store-field path-jail.** The §4 path-jail is now reusable machinery for store fields too: a store declares `pathSensitiveFields` (auto-jailed by the validator before its `validate()`, rejecting the whole record with `store-field-path-shaped` + a jail-counter bump) and/or calls the exported `jailStoreStringField(value, ctx)` helper, which feeds the SAME `jailRejects` counter via the new `StoreValidateContext.countJailReject` (finding #3). Structure > Willpower instead of every store re-implementing (and possibly forgetting) the jail.
33
+
34
+ The emission gate's flag-coherence check trusts the peer's advert: a peer that advertises `stateSyncReceive[store]=true` but is actually unable to apply the kind would still be sent to. This is the same trust model as the existing `ws11DeliverReceive`/`ws12DrainReceive` adverts — the advert is self-reported from machinery presence, and a lying peer is out of scope for this foundation (the applier's per-record validation at the RECEIVE door is the backstop). The validator never throws on bad data (it counts + rejects); a thrown error is reserved for a programmer error (a registration conflict OR a reserved-field knownFields collision), which is the correct loud-fail.
35
+
36
+ ---
37
+
38
+ ## 3. Level-of-abstraction fit
39
+
40
+ Correct layer. This is a low-level, deterministic PRIMITIVE (a validator + a registry + pure decision functions) with no reasoning and no context — exactly right for a substrate that runs on every replicated record. It does NOT hold smart-gate authority; it produces typed verdicts (`EnvelopeValidationResult`, `shouldEmitToPeer` decision, `PoolFlagCoherenceResult`) that the eventual consumers (the concrete stores, the apply path) act on. It USES the existing lower-level primitive (`HybridLogicalClock.coerceHlc` for HLC validation) rather than re-implementing HLC parsing, and it MIRRORS — does not duplicate — the CoherenceJournal typed-schema discipline (the journal's own `validate()`/jail stays the authority for the existing kinds; this is the parallel discipline for the generic replicated envelope a concrete store will register).
41
+
42
+ ---
43
+
44
+ ## 4. Signal vs authority compliance
45
+
46
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
47
+
48
+ - [x] No — this change has no block/allow surface **over a user action**.
49
+
50
+ The two refusal surfaces (validator at the RECEIVE door, emission gate at the EMISSION door) are mechanism: they protect DATA integrity and prevent the named silent-drop skew mode. Neither blocks, delays, or rewrites a user-initiated action — there is no user-message path through this code at all. The validator's logic is deterministic-but-NOT-brittle-with-authority-over-a-user: it is a structural schema check (the same class as the existing journal typed-schema validation, which is the accepted pattern), and its "authority" is only over whether a malformed REPLICATED RECORD enters a stream. The emission gate is a conservative "don't forward what the peer can't receive" — withholding traffic, the safe direction. No brittle detector owns block authority over anything a user does.
51
+
52
+ ---
53
+
54
+ ## 5. Interactions
55
+
56
+ - **Shadowing:** none. The new validator runs on the replicated-record envelope path, which does not yet exist on disk (the registry is empty); no concrete kind is emitted, so it shadows no existing check. The startup `assertStateSyncInvariants` runs alongside `assertSeamlessnessInvariants` (independent config sub-trees) — neither shadows the other.
57
+ - **PULL transport + dual-registry coupling (adversarial finding #4).** The real journal-sync transport is RECEIVER-DRIVEN PULL (`PeerPresencePuller.driveJournalDelta` iterates the SENDER's advert from `CoherenceJournal.getOwnAdvert()`, which enumerates the static `JOURNAL_KINDS`; serve + apply both gate on `JOURNAL_KINDS`). There is NO push-forward step, so `shouldEmitToPeer` is intentionally UNWIRED in Step 2 (no concrete kind to serve; it is the pure per-peer decision the WS2.1 serve/pull chokepoint will consult). The named "emit a new kind to an OLD peer → silently dropped" mode manifests on this PULL transport as "an old peer never PULLS a kind absent from its own JOURNAL_KINDS." CRITICAL COUPLING for the consumer PRs: a replicated kind MUST be added to BOTH `ReplicatedKindRegistry` (read by the gate + the `stateSyncReceive` advert) AND the static `JOURNAL_KINDS` (gates serve + apply, enumerated by `getOwnAdvert`) — registering into only the former yields a store that advertises receive=true yet serves/applies/pulls nothing (a silent no-replication). A wiring-integrity ratchet (`tests/unit/ReplicatedRecordEnvelope.test.ts`) asserts every registered replicated kind is present in `JOURNAL_KINDS`. Documented in spec §4 + the `ReplicatedRecordEnvelope.ts` module header.
58
+ - **Double-fire:** the boot-time coherence check is guarded (`stateSyncCoherenceSurfaced` one-shot) so a mixed-flag pool surfaces ONCE, not per-tick — explicitly the anti-double-fire design (§4 "surfaces ONCE, coalesced"). With an empty registry it never fires at all.
59
+ - **Races:** the registry is constructed once at boot and (in Step 2) never mutated after; the advert self-report reads config + the registry's store list (immutable in this step). No shared mutable state with concurrent code. The validator + decision functions are pure (no shared state).
60
+ - **Feedback loops:** none — the coherence check is observe-only (logs a line); it never auto-enables/disables a store or changes the advert.
61
+
62
+ ---
63
+
64
+ ## 6. External surfaces
65
+
66
+ - **Other agents on the same machine:** none — no new shared files, no new routes.
67
+ - **Other users of the install base:** the capacity heartbeat now carries an additional `seamlessnessFlags.stateSyncReceive` field (`{}` until a store is registered+enabled). Older peers that don't know the field ignore it (forward-compat); newer peers read it. Additive, bounded, no breakage.
68
+ - **External systems:** none.
69
+ - **Persistent state:** config gains a `multiMachine.stateSync` block (foundation knobs only, no `enabled` key) via the existing `applyDefaults`/`migrateConfig` add-missing path — no new on-disk stream, no DB, no ledger. The replicated-record streams themselves land with WS2.1.
70
+ - **Timing/runtime:** the boot coherence check timer (60s, unref'd) is inert while the registry is empty.
71
+ - **Operator surface (Mobile-Complete Operator Actions):** no operator-facing actions — this is internal substrate with no PIN-gated or approval-class route. N/A.
72
+
73
+ ---
74
+
75
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
76
+
77
+ **Posture: replicated (journal-kind emission + flag-coherence-gated).** This IS the replication machinery — its entire purpose is to let a store's state follow the agent across machines via flag-gated, flag-coherence-gated journal-kind emission on top of the coherence-journal replication path (the same first-hop replication the existing kinds use). The named replication path is: a concrete store (WS2.1) registers a kind onto `ReplicatedKindRegistry`, the per-record envelope rides the coherence journal's existing serve/apply transport, and emission is gated per-peer on the `seamlessnessFlags.stateSyncReceive` advert so a kind is NEVER forwarded to a peer that would silently drop it (the named data-loss skew mode this step exists to prevent).
78
+
79
+ - **User-facing notices:** the boot coherence check logs ONE coalesced line to the server log on a mixed-flag pool; it emits no user-facing Telegram notice (a richer surface is the store PR's to add). No one-voice gating needed.
80
+ - **Durable state on topic transfer:** Step 2 holds no per-topic durable state (the registry is in-memory, config is per-machine). No strand-on-transfer risk.
81
+ - **URLs across machine boundaries:** none generated.
82
+ - **Single-machine:** strict no-op — emission is gated on a peer advertising the matching flag, so with no peers nothing is ever emitted; the registry is empty regardless.
83
+
84
+ ---
85
+
86
+ ## 8. Rollback cost
87
+
88
+ - **Hot-fix release:** pure code change — revert the two new modules + the types/config/server additions and ship as the next patch. No persistent state to undo.
89
+ - **Data migration:** none. The `multiMachine.stateSync` config block is add-missing and inert (no per-store `enabled` key shipped); leaving it on an agent after a code revert is harmless (the consuming code is gone). No new on-disk stream is created in this step.
90
+ - **Agent state repair:** none — no agent needs notifying or resetting; the advert field simply stops being emitted after a revert (peers tolerate its absence).
91
+ - **User visibility:** none — the feature is dark with no user-facing surface; a rollback is invisible to users.
92
+
93
+ ---
94
+
95
+ ## Conclusion
96
+
97
+ This review produced no design changes — the change is, by construction, pure dark-by-default mechanism with its only two refusal surfaces at the receive door (malformed-data rejection) and the emission door (don't forward to a non-advertising peer), neither of which touches a user action. The N-peer correctness of the flag-coherence check and the coalesced single-surface design were verified by unit tests (3+-peer mixed cases). The line-map golden test was recomputed by hand for the +17 cartographer shift the `stateSync` config block introduced. The change is clear to ship as the substrate WS2.1 will register a concrete kind onto.
98
+
99
+ ---
100
+
101
+ ## Evidence pointers
102
+
103
+ - `tests/unit/ReplicatedRecordEnvelope.test.ts` (46 tests) — validator both-sides, registry, flag-gated + flag-coherence (incl. 3+-peer mix), config invariants, wiring-integrity advert.
104
+ - Gate-parity (run in the worktree): `npx tsc --noEmit` clean; `no-silent-fallbacks` = 471 (= BASELINE, no bump, my files absent from the flagged list); `docs-coverage --check` PASS (class 55%); `feature-delivery-completeness` PASS (no new CLAUDE.md section); `lint-dev-agent-dark-gate` PASS (line-map updated); focused suites `CoherenceJournal*` / `JournalSyncApplier*` / new test all green.
@@ -1,50 +0,0 @@
1
- # Upgrade Guide — vNEXT
2
-
3
- <!-- assembled-by: assemble-next-md -->
4
- <!-- bump: minor -->
5
-
6
- ## What Changed
7
-
8
- Two cohesive changes (topic 22367, CMT-1434):
9
-
10
- 1. **Mandates dashboard tab redesign.** The Mandates card now leads with the Grant
11
- form as its primary, always-open block; Revoke is demoted to a quiet, collapsed
12
- control beneath it. Raw JSON bounds, agent fingerprints, and scope slugs are
13
- replaced with a plain-language summary sentence; identifiers survive only on a
14
- muted "For support" line. Existing grants read as plain English ("Adam Admin can
15
- deploy to production until 9:37 PM — authorized by you"), and the decision-audit
16
- table stacks into labelled rows at phone width so the reason column is never
17
- truncated. Renderer + markup + CSS only — the mandate API, payloads, and
18
- PIN-never-retained discipline are unchanged. Ships via `dashboard/` static
19
- serving, so it reaches every agent through the normal update path.
20
-
21
- 2. **New constitutional standard: Operator-Surface Quality** (sibling to
22
- Mobile-Complete Operator Actions) in `docs/STANDARDS-REGISTRY.md`. Where
23
- Mobile-Complete asks "can the operator do this from a phone?", this asks "is it
24
- actually good when they do?" — lead with the primary action, zero raw internals,
25
- de-emphasized destructive actions, plain language, phone-width layout. It lands
26
- with teeth: a new operator-surface-quality question in the instar-dev
27
- side-effects review template, and a pre-commit gate
28
- (`scripts/instar-dev-precommit.js`) that blocks any commit touching an operator
29
- surface unless the review answers it. The standard names that gate, so the
30
- Standards-Enforcement-Coverage audit classifies it as an enforced gate.
31
-
32
- ## What to Tell Your User
33
-
34
- - **A cleaner Mandates screen on your phone**: "I gave the Mandates tab a real
35
- overhaul. The action you actually came to do — granting someone permission — is
36
- now front and centre, the risky Revoke button is tucked quietly out of the way,
37
- and the screen reads in plain English instead of raw data. The history list also
38
- finally fits your phone without cutting off the reason column."
39
- - **A new quality bar for everything you touch**: "I also turned this into a
40
- standing rule for myself: any screen you use to approve or decide something has to
41
- be genuinely good to use on your phone, not just technically reachable. I added an
42
- automatic check that holds me to it whenever I build one of those screens."
43
-
44
- ## Summary of New Capabilities
45
-
46
- | Capability | How to Use |
47
- |-----------|-----------|
48
- | Redesigned Mandates tab (humanized, mobile-first) | Open the Mandates tab in the dashboard |
49
- | Operator-Surface Quality standard | docs/STANDARDS-REGISTRY.md (constitution) |
50
- | Operator-surface-quality pre-commit gate | automatic during instar-dev work |