instar 1.3.548 → 1.3.550
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 +122 -5
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +29 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/CoherenceJournal.d.ts +1 -1
- package/dist/core/CoherenceJournal.d.ts.map +1 -1
- package/dist/core/CoherenceJournal.js +23 -2
- package/dist/core/CoherenceJournal.js.map +1 -1
- package/dist/core/CredentialRebalancerSnapshot.d.ts +17 -0
- package/dist/core/CredentialRebalancerSnapshot.d.ts.map +1 -1
- package/dist/core/CredentialRebalancerSnapshot.js +24 -0
- package/dist/core/CredentialRebalancerSnapshot.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +27 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/TopicOperatorReplicatedStore.d.ts +250 -0
- package/dist/core/TopicOperatorReplicatedStore.d.ts.map +1 -0
- package/dist/core/TopicOperatorReplicatedStore.js +413 -0
- package/dist/core/TopicOperatorReplicatedStore.js.map +1 -0
- package/dist/core/UserRegistryReplicatedStore.d.ts +287 -0
- package/dist/core/UserRegistryReplicatedStore.d.ts.map +1 -0
- package/dist/core/UserRegistryReplicatedStore.js +527 -0
- package/dist/core/UserRegistryReplicatedStore.js.map +1 -0
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +10 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +3 -1
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/users/TopicOperatorStore.d.ts +24 -0
- package/dist/users/TopicOperatorStore.d.ts.map +1 -1
- package/dist/users/TopicOperatorStore.js +28 -0
- package/dist/users/TopicOperatorStore.js.map +1 -1
- package/dist/users/UserManager.d.ts +27 -0
- package/dist/users/UserManager.d.ts.map +1 -1
- package/dist/users/UserManager.js +44 -0
- package/dist/users/UserManager.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +19 -19
- package/src/scaffold/templates.ts +3 -1
- package/upgrades/1.3.549.md +36 -0
- package/upgrades/1.3.550.md +23 -0
- package/upgrades/side-effects/multi-machine-replicated-store-ws26-user-topicop.md +123 -0
- package/upgrades/side-effects/ws52-busyness.md +38 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Makes the autonomous balancer's dry-run dogfooding produce meaningful drain decisions: the drain objective now targets the actually-busiest slot (real session-count signal) instead of an arbitrary one.
|
|
9
|
+
|
|
10
|
+
- **`computeBusynessBySlot`** (src/core/CredentialRebalancerSnapshot.ts) — a pure helper: per-slot busyness = count of RUNNING claude-code sessions on each slot. A session's slot is its account's current slot (`ledger.slotOf`); an untagged session counts toward the default slot; non-running and explicit-non-claude sessions don't count.
|
|
11
|
+
- **Wired into the balancer** — the server's `listSlots` provider now feeds this real busyness from the live session list, so the §2.4 drain objective deals an expiring weekly window to the slot where the work actually is. Read-only; dry-run on a dev agent (zero writes), dark on the fleet.
|
|
12
|
+
|
|
13
|
+
## What to Tell Your User
|
|
14
|
+
|
|
15
|
+
A small sharpening of the (still off-by-default, dry-run) account-balancer so what you watch is actually meaningful: when it considers draining an account whose weekly allowance is about to expire, it now sends that work to whichever of your config slots is genuinely busiest right now, instead of picking one arbitrarily. Nothing moves — it's still dry-run, still observation only — but the decisions you'd watch at the rebalancer status endpoint now reflect real activity, which is exactly the signal that builds confidence before you decide whether to turn on real moves.
|
|
16
|
+
|
|
17
|
+
## Summary of New Capabilities
|
|
18
|
+
|
|
19
|
+
No new runtime capability — a read-only refinement to the Increment-B balancer's dry-run decisions. New internal helper `computeBusynessBySlot` feeds real per-slot busyness (count of running claude-code sessions) into the balancer's drain-target selection. No route, no config flag, no credential write path; dry-run on dev, dark on fleet.
|
|
20
|
+
|
|
21
|
+
## Evidence
|
|
22
|
+
|
|
23
|
+
- `tests/unit/credential-rebalancer-snapshot.test.ts` (+5 = 15) — counts running claude-code sessions per slot via the account→slot map; untagged session → default slot; non-running excluded; explicit-non-claude excluded but legacy (undefined framework) counted; a tagged session whose account has no current slot is dropped. tsc + full lint clean.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Side-Effects Review — WS2.6 user-registry + topic-operator replicated stores (the last two memory-family PII kinds, dark)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `multi-machine-replicated-store-ws26-user-topicop`
|
|
4
|
+
**Date:** `2026-06-13`
|
|
5
|
+
**Author:** `Instar Agent (echo)`
|
|
6
|
+
**Second-pass reviewer:** `5-lens adversarial review (untrusted-replicated-operator / recordKey-identity / disclosure-min-PII-leak / tombstone-erasure / dark-ship-inertness) — verdict + named tests appended below`
|
|
7
|
+
**Parent principle:** Cross-Machine Coherence — One Agent, Robust Under Degraded Conditions
|
|
8
|
+
|
|
9
|
+
## Summary of the change
|
|
10
|
+
|
|
11
|
+
WS2.6 adds the final two PII kinds to the WS2 memory family on the proven HLC
|
|
12
|
+
replicated-store foundation: `user-record` (the multi-user registry the `UserManager`
|
|
13
|
+
resolves an inbound message to) and `topic-operator-record` (which VERIFIED operator a
|
|
14
|
+
topic was bound to). Both are thin instantiations of the shipped WS2.3 relationships PII
|
|
15
|
+
template: discriminated-union-on-`op` schema (value + tombstone), strict type-clamp on
|
|
16
|
+
receive, disclosure-minimized projection, content/identity-surface `recordKey`, 64KB
|
|
17
|
+
per-entry cap with a named over-cap error, op:delete tombstone for offline-peer erasure,
|
|
18
|
+
HIGH-impact-at-replication / advisory-at-read. With these landed, the WS2 memory family
|
|
19
|
+
is COMPLETE (7 kinds: preferences, relationships, learnings, knowledge, evolution-actions,
|
|
20
|
+
user-registry, topic-operator; playbook deferred CMT-1416).
|
|
21
|
+
|
|
22
|
+
Both ship **DARK** behind `multiMachine.stateSync.userRegistry` and
|
|
23
|
+
`.topicOperator` (`enabled:false`, `dryRun:true` — the graduated dark→dryRun→live ladder).
|
|
24
|
+
Flag-off / single-machine = strict no-op; NO PII ever crosses a machine boundary while dark.
|
|
25
|
+
|
|
26
|
+
## Decision-point inventory (the decided forks, baked in verbatim)
|
|
27
|
+
|
|
28
|
+
- **user-record recordKey** = `sha256(sorted channel-set "type:identifier" pairs)` — a user IS
|
|
29
|
+
their channel identifiers (same model as relationships, mirroring `UserManager.channelIndex`).
|
|
30
|
+
NEVER the local `userId` (the cross-machine-unstable id).
|
|
31
|
+
- **topic-operator recordKey** = `sha256(topicId + ":" + verified-uid)`. NEVER a content-name
|
|
32
|
+
(Know-Your-Principal: the binding is auth-sender-derived).
|
|
33
|
+
- **Impact tier**: BOTH HIGH@replication (append-both-and-flag on concurrent divergence) /
|
|
34
|
+
advisory@read (a replicated PII record is a hint, never the authoritative answer).
|
|
35
|
+
- **disclosure-min**: user-record strips the local `userId`; topic-operator projects exactly
|
|
36
|
+
`{platform, uid, names, boundAt}` (no internal `boundFrom`).
|
|
37
|
+
- **THE LOAD-BEARING INVARIANT (topic-operator)**: a replicated topic-operator record is
|
|
38
|
+
UNTRUSTED peer data — NEVER this machine's authoritative answer to "who is my verified
|
|
39
|
+
operator?". Only the LOCAL authenticated `TopicOperatorStore.setOperator` binds the principal.
|
|
40
|
+
The store exposes NO apply/set/establish path back into `TopicOperatorStore` by construction.
|
|
41
|
+
|
|
42
|
+
## 1. Over-block
|
|
43
|
+
None. Both kinds ADD reach (replicated PII as advisory context) and never block a message, a
|
|
44
|
+
route, or a session. Flag-off / single-machine = strict no-op (named E2E + unit tests).
|
|
45
|
+
|
|
46
|
+
## 2. Under-block (the real risk surface)
|
|
47
|
+
The inverse risk: replicated PII is materialized into a union read. Mitigations: (a) strict
|
|
48
|
+
type-clamp on receive (ISO-8601 dates, finite numbers, jailed slugs) makes markup
|
|
49
|
+
un-smuggleable through a render slot; (b) every foreign record renders inside a
|
|
50
|
+
`<replicated-untrusted-data origin="…">` envelope; (c) the topic-operator foreign render
|
|
51
|
+
EXPLICITLY states the record is NOT the verified operator and cannot establish/override one;
|
|
52
|
+
(d) identity RESOLUTION of an inbound principal stays LOCAL-ONLY for user-record (the local
|
|
53
|
+
channel index always wins); (e) the topic-operator store has no apply path, so a replicated
|
|
54
|
+
record can never reach `getOperator()` authority. Accepted residual: a compromised
|
|
55
|
+
same-operator peer could inject advisory context text, but it cannot escalate to principal
|
|
56
|
+
authority and the blast radius is one operator's own pool — recorded as a known bound.
|
|
57
|
+
|
|
58
|
+
## 3. Level-of-abstraction fit
|
|
59
|
+
The two `*ReplicatedStore.ts` modules are PURE logic (no fs, no Date, no network) — schema +
|
|
60
|
+
projection + recordKey + merge + render, unit-testable in isolation. The wiring sits in
|
|
61
|
+
server.ts (kind registration + union reader) and the manager seams (`UserManager.persistUsers`/
|
|
62
|
+
`removeUser`, `TopicOperatorStore.setOperator`) exactly where the WS2.2–2.5 siblings sit. No
|
|
63
|
+
new transport, no new auth, no new tick.
|
|
64
|
+
|
|
65
|
+
## 4. Signal vs authority compliance
|
|
66
|
+
Fully signal-only. Neither kind gates or rewrites anything. The user-registry merged read is a
|
|
67
|
+
HINT (inbound resolution stays local-authoritative); the topic-operator merged read is advisory
|
|
68
|
+
context (the principal stays local-authoritative). The mesh path is the existing read/observe
|
|
69
|
+
replicated-store substrate — adds reach, never authority; the receiver type-clamps + envelopes.
|
|
70
|
+
|
|
71
|
+
## 5. Interactions
|
|
72
|
+
- Rides the existing `ReplicatedKindRegistry` + dual-registry (`JOURNAL_KINDS`) + union reader +
|
|
73
|
+
conflict/rollback substrate (no new machinery).
|
|
74
|
+
- Two new `enabled:false` ConfigDefaults paths shift the dark-gate EXPECTED line-map by +29;
|
|
75
|
+
hand-edited (regeneration forbidden) + two new attributed entries added.
|
|
76
|
+
- Coexists with the WS2.1–2.5 siblings (same `multiMachine.stateSync.*` block, independent flags).
|
|
77
|
+
- The maintainer's WS5.2 credential-repointing track was NOT touched (separate lane).
|
|
78
|
+
|
|
79
|
+
## 6. External surfaces
|
|
80
|
+
No new HTTP routes. The kinds register onto the shared registry and ride the existing
|
|
81
|
+
`/state/*` conflict/quarantine routes (already alive). Emission/serve/pull stay dark behind the
|
|
82
|
+
two new flags. The journal-backed emitter is wired at the manager seams but only attached when
|
|
83
|
+
the flag is on (a later rollout stage, mirroring the siblings).
|
|
84
|
+
|
|
85
|
+
## 7. Multi-machine posture (Cross-Machine Coherence) + Phase C
|
|
86
|
+
- **Per-machine emission; N-machine pool = N independent emitters.** Identity keyed on the
|
|
87
|
+
channel-set (user) / topic+uid (operator), NOT a per-machine id — so the SAME user/operator
|
|
88
|
+
across N machines is ONE record, never N duplicates.
|
|
89
|
+
- **The topic-operator untrusted-replicated invariant scales:** on N machines only the LOCAL
|
|
90
|
+
authenticated binding is authoritative for the principal; replicated records from N-1 peers
|
|
91
|
+
are advisory. A re-bind to a NEW operator (different uid) is a DIFFERENT record (different
|
|
92
|
+
key), so a replica can never overwrite the local binding.
|
|
93
|
+
- **No LAN/broadcast assumption.** Rides the existing per-peer replicated-store substrate.
|
|
94
|
+
- **Offline-peer erasure:** a removed user / unbound operator propagates an op:delete tombstone
|
|
95
|
+
keyed on the SAME identity surface, so an erased record stays erased on a peer that was offline
|
|
96
|
+
at delete time.
|
|
97
|
+
|
|
98
|
+
## Migration parity
|
|
99
|
+
- ConfigDefaults: two new dark blocks (no migrateConfig needed — an absent per-store key resolves
|
|
100
|
+
to dark, the safe default; identical to the WS2.2–2.5 siblings).
|
|
101
|
+
- CLAUDE.md template (`templates.ts`) + a chained migrator else-if in `PostUpdateMigrator` splice
|
|
102
|
+
the two new One Memory bullets — ESPECIALLY the topic-operator UNTRUSTED-REPLICATED-OPERATOR
|
|
103
|
+
invariant — into already-deployed agents before any operator enables the replication. Idempotent
|
|
104
|
+
(guarded by the unique 'User registry is the SECOND PII store' marker), with a focused test.
|
|
105
|
+
- devGatedFeatures: two new DARK_GATE_EXCLUSIONS entries (optional-integration).
|
|
106
|
+
- Docs: two new `under-the-hood.md` class blocks (≥2 mentions each — docs-coverage passes).
|
|
107
|
+
|
|
108
|
+
## 5-lens adversarial verdict (folded as named tests)
|
|
109
|
+
1. **Untrusted-replicated-operator (THE blocker)** — `untrusted-replicated-operator-never-authoritative`:
|
|
110
|
+
the foreign render ALWAYS says NOT the verified operator; the module exposes NO apply/set/establish
|
|
111
|
+
export; the wiring test proves `getOperator()` authority is UNCHANGED by a replicated record. PASS.
|
|
112
|
+
2. **recordKey identity** — `recordKey-identity-collapses-cross-machine`: same channel-set/topic+uid →
|
|
113
|
+
one key regardless of local id; a different uid is a different record. PASS.
|
|
114
|
+
3. **disclosure-min / PII leak** — `disclosure-min-strips-local-id` (userId never on wire),
|
|
115
|
+
topic-operator projection exactly `{platform,uid,names,boundAt}`, `64KB-named-error`, `type-clamp`
|
|
116
|
+
(markup injection blocked, path-shaped slug jailed). PASS.
|
|
117
|
+
4. **tombstone erasure** — `op:delete-tombstone-erasure`: tombstone keyed on the SAME identity surface;
|
|
118
|
+
schema op:delete branch accepts it. PASS.
|
|
119
|
+
5. **dark-ship inertness** — `dark-ship-strict-noop`: no emitter ⇒ manager behaves byte-identically;
|
|
120
|
+
E2E DISABLED → 503. PASS.
|
|
121
|
+
|
|
122
|
+
No residual blockers. Known accepted bound: a compromised same-operator peer can inject advisory
|
|
123
|
+
context (cannot escalate to authority; one-operator blast radius).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Side-Effects Review — WS5.2 balancer busyness signal (drain-target refinement)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `ws52-busyness`
|
|
4
|
+
**Date:** 2026-06-13
|
|
5
|
+
**Author:** echo
|
|
6
|
+
**Second-pass reviewer:** not required (a pure read-only data-signal helper + thin wiring; no gate/sentinel/lifecycle/write surface; the balancer it feeds is already dry-run-gated + B3a-reviewed)
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Resolves the tracked busyness follow-up for the autonomous balancer (Increment B). Adds a pure `computeBusynessBySlot(sessions, slotOf, defaultSlot)` to `CredentialRebalancerSnapshot.ts` — per-slot busyness = count of RUNNING claude-code sessions on each slot (a session's slot is its account's current slot via `ledger.slotOf`; an untagged session counts toward the default slot; non-running / explicit-non-claude sessions don't count). Wires it into the server's `listSlots` provider so the balancer's drain objective targets the actually-busiest slot (real signal) instead of uniform-0. Read-only; affects only the §2.4 drain TARGET selection, and only in dry-run on a dev agent (zero credential writes).
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `CredentialRebalancerPolicy` drain-target selection (objective 2) — now fed a real busyness signal instead of uniform 0. No new decision point; it sharpens an existing one (which busy slot a drain deals to). No authority change; the balancer remains dry-run-gated.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Over-block / ## 2. Under-block
|
|
19
|
+
No block/allow surface. The signal only orders drain TARGET candidates; a wrong count would at worst pick a sub-optimal busy slot to drain to (still a valid eligible slot), and only in dry-run.
|
|
20
|
+
|
|
21
|
+
## 3. Level-of-abstraction fit
|
|
22
|
+
Correct: the busyness computation is a pure mapper in the snapshot module (alongside mapSlot/mapAccount), kept out of server.ts so a counting bug is unit-testable. The server wiring is a one-line provider change passing `state.listSessions()` + the ledger's `slotOf`.
|
|
23
|
+
|
|
24
|
+
## 4. Signal vs authority compliance
|
|
25
|
+
- [x] No — a read-only data-signal helper; no block/allow surface, no authority. (Ref: docs/signal-vs-authority.md.)
|
|
26
|
+
|
|
27
|
+
## 5. Interactions
|
|
28
|
+
- Feeds only the policy's drain `busyness` field (already consumed; previously always 0). No shadowing/race — pure, computed fresh each pass from the live session list.
|
|
29
|
+
- The untagged-session-→-default-slot attribution is a deliberate choice (the default interactive session runs on the default account); documented.
|
|
30
|
+
|
|
31
|
+
## 6. External surfaces
|
|
32
|
+
- None directly. The balancer's `GET /credentials/rebalancer` status (and its dry-run audit) will now reflect drain decisions that target the busiest slot — observably better dogfooding signal, still zero writes (dry-run on dev; dark on fleet).
|
|
33
|
+
|
|
34
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
35
|
+
- **Machine-local BY DESIGN.** Busyness is computed from THIS machine's live session list + THIS machine's ledger; per-machine. No cross-machine input.
|
|
36
|
+
|
|
37
|
+
## 8. Rollback cost
|
|
38
|
+
Trivial. Revert → the balancer falls back to uniform busyness (drain picks the first eligible busy slot). No state, no migration, no credential touch.
|