instar 1.3.1319 → 1.3.1321

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 (110) hide show
  1. package/dist/commands/machine.d.ts +20 -0
  2. package/dist/commands/machine.d.ts.map +1 -1
  3. package/dist/commands/machine.js +135 -21
  4. package/dist/commands/machine.js.map +1 -1
  5. package/dist/commands/server.d.ts.map +1 -1
  6. package/dist/commands/server.js +86 -5
  7. package/dist/commands/server.js.map +1 -1
  8. package/dist/commands/setup.d.ts +6 -0
  9. package/dist/commands/setup.d.ts.map +1 -1
  10. package/dist/commands/setup.js +8 -0
  11. package/dist/commands/setup.js.map +1 -1
  12. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  13. package/dist/config/ConfigDefaults.js +6 -0
  14. package/dist/config/ConfigDefaults.js.map +1 -1
  15. package/dist/core/Config.d.ts +12 -0
  16. package/dist/core/Config.d.ts.map +1 -1
  17. package/dist/core/Config.js +38 -1
  18. package/dist/core/Config.js.map +1 -1
  19. package/dist/core/JevReviewFlagShadow.d.ts +161 -0
  20. package/dist/core/JevReviewFlagShadow.d.ts.map +1 -0
  21. package/dist/core/JevReviewFlagShadow.js +456 -0
  22. package/dist/core/JevReviewFlagShadow.js.map +1 -0
  23. package/dist/core/MachineIdentity.d.ts +10 -0
  24. package/dist/core/MachineIdentity.d.ts.map +1 -1
  25. package/dist/core/MachineIdentity.js +15 -4
  26. package/dist/core/MachineIdentity.js.map +1 -1
  27. package/dist/core/PostUpdateMigrator.d.ts +8 -0
  28. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  29. package/dist/core/PostUpdateMigrator.js +138 -3
  30. package/dist/core/PostUpdateMigrator.js.map +1 -1
  31. package/dist/core/SubscriptionPool.d.ts +30 -0
  32. package/dist/core/SubscriptionPool.d.ts.map +1 -1
  33. package/dist/core/SubscriptionPool.js +52 -1
  34. package/dist/core/SubscriptionPool.js.map +1 -1
  35. package/dist/core/SubscriptionPoolAuthority.d.ts +33 -0
  36. package/dist/core/SubscriptionPoolAuthority.d.ts.map +1 -1
  37. package/dist/core/SubscriptionPoolAuthority.js +150 -0
  38. package/dist/core/SubscriptionPoolAuthority.js.map +1 -1
  39. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  40. package/dist/core/devGatedFeatures.js +6 -0
  41. package/dist/core/devGatedFeatures.js.map +1 -1
  42. package/dist/core/shutdownDeadline.d.ts +26 -0
  43. package/dist/core/shutdownDeadline.d.ts.map +1 -0
  44. package/dist/core/shutdownDeadline.js +25 -0
  45. package/dist/core/shutdownDeadline.js.map +1 -0
  46. package/dist/core/types.d.ts +18 -0
  47. package/dist/core/types.d.ts.map +1 -1
  48. package/dist/core/types.js.map +1 -1
  49. package/dist/data/standards-guard-index.json +1 -1
  50. package/dist/data/standards-guard-index.meta.json +2 -2
  51. package/dist/data/standards-registry.meta.json +1 -1
  52. package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
  53. package/dist/messaging/TelegramAdapter.js +12 -1
  54. package/dist/messaging/TelegramAdapter.js.map +1 -1
  55. package/dist/monitoring/CodexRolloutScan.worker.d.ts +16 -0
  56. package/dist/monitoring/CodexRolloutScan.worker.d.ts.map +1 -0
  57. package/dist/monitoring/CodexRolloutScan.worker.js +49 -0
  58. package/dist/monitoring/CodexRolloutScan.worker.js.map +1 -0
  59. package/dist/monitoring/QuotaCollector.d.ts +8 -0
  60. package/dist/monitoring/QuotaCollector.d.ts.map +1 -1
  61. package/dist/monitoring/QuotaCollector.js +73 -32
  62. package/dist/monitoring/QuotaCollector.js.map +1 -1
  63. package/dist/monitoring/TokenLedger.d.ts +11 -1
  64. package/dist/monitoring/TokenLedger.d.ts.map +1 -1
  65. package/dist/monitoring/TokenLedger.js +79 -37
  66. package/dist/monitoring/TokenLedger.js.map +1 -1
  67. package/dist/scaffold/templates.d.ts +4 -0
  68. package/dist/scaffold/templates.d.ts.map +1 -1
  69. package/dist/scaffold/templates.js +17 -4
  70. package/dist/scaffold/templates.js.map +1 -1
  71. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  72. package/dist/server/CapabilityIndex.js +1 -0
  73. package/dist/server/CapabilityIndex.js.map +1 -1
  74. package/dist/server/machineRoutes.d.ts.map +1 -1
  75. package/dist/server/machineRoutes.js +16 -0
  76. package/dist/server/machineRoutes.js.map +1 -1
  77. package/dist/server/routes.d.ts.map +1 -1
  78. package/dist/server/routes.js +16 -3
  79. package/dist/server/routes.js.map +1 -1
  80. package/dist/threadline/ThreadlineBootstrap.d.ts +9 -0
  81. package/dist/threadline/ThreadlineBootstrap.d.ts.map +1 -1
  82. package/dist/threadline/ThreadlineBootstrap.js +5 -1
  83. package/dist/threadline/ThreadlineBootstrap.js.map +1 -1
  84. package/dist/threadline/client/ThreadlineClient.d.ts +14 -0
  85. package/dist/threadline/client/ThreadlineClient.d.ts.map +1 -1
  86. package/dist/threadline/client/ThreadlineClient.js +18 -0
  87. package/dist/threadline/client/ThreadlineClient.js.map +1 -1
  88. package/package.json +1 -1
  89. package/src/data/builtin-manifest.json +64 -64
  90. package/src/data/standards-guard-index.json +1 -1
  91. package/src/data/standards-guard-index.meta.json +2 -2
  92. package/src/data/standards-registry.meta.json +1 -1
  93. package/src/scaffold/templates.ts +18 -4
  94. package/upgrades/1.3.1320.md +47 -0
  95. package/upgrades/1.3.1321.md +153 -0
  96. package/upgrades/luna-stack-ci-ratchets.eli16.md +9 -0
  97. package/upgrades/side-effects/agent-health-reopen-duplicate.md +58 -0
  98. package/upgrades/side-effects/auth-read-after-secret-migration.md +56 -0
  99. package/upgrades/side-effects/codex-rollout-scan-yield.md +58 -0
  100. package/upgrades/side-effects/jev-review-flag-shadow.md +175 -0
  101. package/upgrades/side-effects/join-autostart-lifeline.md +59 -0
  102. package/upgrades/side-effects/join-refuses-copied-identity.md +62 -0
  103. package/upgrades/side-effects/luna-stack-ci-ratchets.md +24 -0
  104. package/upgrades/side-effects/quota-jsonl-off-event-loop.md +54 -0
  105. package/upgrades/side-effects/second-machine-copied-config.md +62 -0
  106. package/upgrades/side-effects/shutdown-hard-deadline.md +62 -0
  107. package/upgrades/side-effects/subscription-pool-legacy-migrate.md +68 -0
  108. package/upgrades/side-effects/threadline-relay-standby.md +54 -0
  109. package/upgrades/side-effects/threadline-wire-thread-id.md +60 -0
  110. package/upgrades/side-effects/wakeup-targets-awake-machine.md +58 -0
@@ -0,0 +1,47 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ A new dark research instrument measures whether Jev (TypeSafe, `jev-1.13.0`)
9
+ can tell which agent replies need the operator's review, without ever showing
10
+ a flag. On a development agent, once a minute the server reads the newest
11
+ agent replies in its own Telegram history (conversational sends only, at most
12
+ 30 minutes old), strips secrets from each reply and the operator message it
13
+ answers before cutting anything, and asks Jev: does this reply need review
14
+ (it claims done without evidence, asks the operator to decide or act, reports
15
+ a costly step, or misses the ask), is it fine, or can't it tell? It writes one
16
+ content-free row per reply to `logs/jev-review-flag-shadow.jsonl` (the
17
+ Telegram message ids, the label, P(needs review) in both option orders, and
18
+ whether a flag would have been shown). `GET /jev-review-flag/summary` reports
19
+ the counts. No hook or settings change: it is a server-side timer, so it
20
+ covers every framework that relays through the server.
21
+
22
+ Dev-gated: live on a development agent, dark on the fleet
23
+ (`intelligence.jevReviewFlagShadow`, `enabled` omitted). Needs the vault
24
+ `typesafe_api_key`. Bounded by a daily call cap (default 3000), a 1.5 s
25
+ timeout and one call at a time; the kill switch is read live at every tick.
26
+
27
+ ## What to Tell Your User
28
+
29
+ Nothing changes for you. On a development agent, Instar now quietly records
30
+ which of its Telegram replies a fast model thinks you should look at, so we
31
+ can check those guesses against the replies you actually corrected before
32
+ deciding whether to show you a "worth a look" marker.
33
+
34
+ ## Summary of New Capabilities
35
+
36
+ - `GET /jev-review-flag/summary`: checks, would-flags and per-topic counts from the log-only review-flag shadow (development agents).
37
+
38
+ ## Evidence
39
+
40
+ - Live through the real module against this agent's real Telegram history and
41
+ the real vendor: four recent replies judged in 89–169 ms each, all `fine`
42
+ (P(needs review) 0.07–0.49, both option orders agreeing); the log held no
43
+ message text.
44
+ - Unit, integration and e2e tests; the e2e builds the shadow with the server's
45
+ factory reading config.json live, starts the real timer, and shows the
46
+ feature alive on a development agent, dark on the fleet, and stopped by an
47
+ explicit `false`.
@@ -0,0 +1,153 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ `TelegramAdapter.createOrReopenAgentHealthAttentionItem` rethrew a `duplicate-content` hold on its "returned after recovery" post, so callers (the Threadline spawn drain's give-up handler) treated an alert the operator already had as a failed Attention write and retried every 30 s for the whole dedup window (ACT-1304 fault 2, Luna/sagemind log 2026-10-04 12:05-12:18Z). That one hold reason now counts as delivered; every other hold still throws.
9
+
10
+ After `instar pair` externalizes secrets, `.instar/config.json` holds `{ "secret": true }` for `authToken`. Instar's hooks and scripts already read `$INSTAR_AUTH_TOKEN` first, but the CLAUDE.md "API Authentication" instruction still read the config directly and turned the placeholder into a rejected Bearer value (ACT-1303, reported by Luna/sagemind).
11
+
12
+ - `generateClaudeMd` (both auth sections): `$INSTAR_AUTH_TOKEN` → `node .instar/scripts/secret-get.mjs authToken` → config value only when it is a string.
13
+ - `PostUpdateMigrator.migrateClaudeMd` rewrites the old line in existing agents' CLAUDE.md (content-sniffed, idempotent).
14
+
15
+ Codex rollout discovery, file reads, and full parsing now run in a worker thread. The worker relocates the prior scan without changing its selection or results: `listAllRollouts` still selects 500 files by default in descending modification-time order, every selected file is read on every poll, the optional age and project-directory filters are unchanged, and duplicate session IDs retain final-upsert-wins behavior in discovery order. The poller keeps its existing 30-day age window.
16
+
17
+ Worker timeout, error, unexpected-exit, shutdown termination, and overlapping-poll guards prevent a stuck or duplicate scan. The main thread applies compact parsed results with the legacy per-file SQLite upserts.
18
+
19
+ `instar join` installs a standby's LaunchAgent with `server start` (its scaffolded config has no Telegram, and the lifeline needs one), and nothing revisited that once Telegram was configured; join's closing text also told the operator to `instar server start` a second, unsupervised server (instar#2122, Luna/sagemind).
20
+
21
+ - Server boot's auto-start self-heal (macOS) now regenerates the LaunchAgent with the lifeline when Telegram is configured but the plist starts the bare server (`autoStartNeedsLifeline` in `src/commands/setup.ts`).
22
+ - `instar join` no longer suggests starting a server when auto-start was installed.
23
+
24
+ `instar join` used any existing `.instar/machine/identity.json`, so a second machine set up from a copied agent home claimed the first machine's id (ACT-1302, Luna/sagemind's Studio booted as the laptop).
25
+
26
+ - `POST /api/pair` refuses `409 joiner-identity-is-inviter` when the joiner presents the inviter's own machine id, before the pairing code is checked (no attempt spent, no state change).
27
+ - `instar join`: an identity whose private signing key is not on this machine is first offered to boot identity recovery (keychain-authenticated key rebuild keeps the id). If recovery does not restore it, the identity and key files (current and legacy names) are renamed `*.copied-<timestamp>` and a fresh standby identity is minted. On the inviter's 409 the joiner does the same and retries once.
28
+
29
+ Fixes instar#2120. When the quota read from Anthropic fails, `QuotaCollector` estimates usage from the last 7 days of Claude JSONL transcripts. It read each file whole and parsed every line synchronously on the event loop; on ~1.8 GB of transcripts the server stopped answering for 3-20 s every 1-2.5 minutes.
30
+
31
+ - `JsonlParser.parseFileAsync` streams each file line by line; `collectFromJsonl` is async and yields between files.
32
+ - `JsonlParser.accumulateLine` holds the per-line rule for both the sync and streamed paths, so they count identically.
33
+ - Lines without `assistant` are skipped before `JSON.parse`; every counted entry contains it, so totals are unchanged.
34
+
35
+ Fixes the blocker in instar#2122, found while bringing Luna up on a second machine.
36
+
37
+ - **Copied Claude path** (`src/core/Config.ts` → `resolveConfiguredClaudePath`). `sessions.claudePath` that provably does not exist on this machine is replaced by the detected Claude binary, with a warning naming both paths; with none detected the configured value is kept. Previously it was used verbatim, every Claude session died at spawn, and each death revoked the session's Telegram sending credential (`invalid-origin-token`). An existing path, a bare command name, or an unprobeable path is kept as configured — the rule `sessions.frameworkBinaryPaths` already follows.
38
+ - **Half-revoked machine entries** (`src/core/MachineIdentity.ts` → `isRegistryEntryActive`). A registry entry is active only with `status: 'active'` and no `revokedAt`. Used by the active-machine list, awake lookup, nickname lookup, `instar machine` views, the passkey cell-state check and the origin pool audit. A removed identity whose endpoints were this machine's own no longer receives this machine's cross-machine calls (`wrong-recipient`).
39
+
40
+ The server's SIGTERM/SIGINT teardown awaited ~10 subsystem stops with no overall bound, so one hang kept the process alive until SIGKILL (instar#2122: Luna's Studio standby ignored SIGTERM).
41
+
42
+ - `src/core/shutdownDeadline.ts`: `armShutdownDeadline` arms an unref'd timer at teardown start (default 20 s; `INSTAR_SHUTDOWN_DEADLINE_MS` override, floor 2 s); on expiry it logs the step in flight, closes SQLite handles, releases the single-instance lock and exits 1.
43
+ - A second signal during teardown exits at once with the same cleanup.
44
+ - The awaited steps are labelled in the log (`telegram-origin`, `notification-flush`, `tunnel`, `threadline`, `telegram`, `http-server`, `sqlite`).
45
+
46
+ Builds the legacy single-file migration named in the approved spec `subscription-pool-authority-foundation.md` (operation `legacy-migrate`), prompted by instar#2122 (a joined machine inherited the first machine's login locations through the git-shared `.instar/subscription-pool.json`).
47
+
48
+ - `SubscriptionPoolAuthorityStore.migrateLegacy` + `recoverLegacyMigrate`: witness bound to the untouched source's SHA-256+size; staging, finalize, rebuild-from-matching-source, fail-closed recovery per the spec table.
49
+ - `SubscriptionPool.migrateLegacyToMachineLocal`: drops rows whose login home is not on this machine; `persist()` writes to the authority once one is published, even with a legacy file still present.
50
+ - `PostUpdateMigrator.migrateSubscriptionPoolToMachineLocal`: `.gitignore` entry, one-time publication, legacy file removed only when untracked (a tracked copy stays — deleting it would propagate to peers). `GITIGNORE_ENTRIES` gains `.instar/subscription-pool.json`.
51
+
52
+ The relay admits one connection per agent identity, so two machines of one agent displaced each other (instar#2122). `bootstrapThreadline` gains `relayStandby`; `server.ts` passes `!shouldOwnTelegramPoll(config)` — the per-machine `multiMachine.telegramPolling: false` flag the lifeline already uses. A standby logs `relay connection SUPPRESSED (standby: …)` and keeps local Threadline. CLAUDE.md template + migration gain one awareness bullet.
53
+
54
+ `POST /threadline/relay-send` (relay path) recorded `threadId ?? messageId` when the caller passed no thread, while `ThreadlineClient` put its own affinity/minted `thread-…` id on the wire. The outbox, thread log, bridge mirror, delivery tracker, reply waiter and the origin-topic binding were all keyed on the `msg-…` id, so the peer's reply on `thread-…` matched nothing and cold-spawned — refused at a full session cap and held (ACT-1304 fault 3, reported by Luna/sagemind).
55
+
56
+ - New `ThreadlineClient.sendAutoWithThread()` returns `{ messageId, threadId }` with the id that went on the wire.
57
+ - The relay-send route uses it for every record and for its response; `threadline_send` now returns the real conversation id.
58
+
59
+ `wakeup()` named the awake machine from the registry's lagging role field and posted the handoff challenge to its OWN server, which verified the signature with itself as receiver and answered `Invalid challenge signature` (instar#2122).
60
+
61
+ - `resolveAwakeMachine`: live lease holder from the local server's `/health` (`multiMachine.syncStatus.leaseHolder`, active non-revoked entry), else the registry role.
62
+ - `resolveAwakeServerUrl`: probes the awake machine's advertised endpoints + last-known URL with `/health`; never localhost.
63
+
64
+ ## What to Tell Your User
65
+
66
+ If something I warned you about in the Agent Health topic comes back, I no longer keep retrying the same heads-up in the background every 30 seconds.
67
+
68
+ If you've connected a second machine, I no longer get locked out of my own controls when I follow my setup notes. The notes now find my access key wherever it's kept.
69
+
70
+ If you use Codex a lot, I no longer freeze for several seconds every minute or so while counting its usage. The numbers I report are the same.
71
+
72
+ If you add a second machine, once Telegram is set up there it starts at login the same way as the first machine, with my watchdog keeping the server running. The join steps no longer tell you to start a second copy of the server.
73
+
74
+ If you set up a second machine by copying my folder from the first, it now creates its own identity instead of pretending to be the first machine. A machine that has only lost its keys still recovers its own identity.
75
+
76
+ When I can't get my usage numbers straight from Anthropic and have to estimate them from my own logs, I no longer freeze for several seconds while doing it. The estimate itself is the same.
77
+
78
+ If you add a second machine by copying settings from the first, I no longer go silent on the new machine when my settings name a Claude program that only exists on the old one — I find the right one and note the mismatch. A machine you removed also stays removed, even if an old copy of the machine list still calls it active.
79
+
80
+ When my server is asked to stop, it now always stops within about 20 seconds, even if one part of it is stuck, and the log says which part. Updates and restarts no longer have to force-kill it.
81
+
82
+ My list of which Claude or Codex logins live on which machine is now kept separately on each machine. A second machine no longer inherits the first one's login locations, and a login that only exists on the other machine is left off this machine's list. Nothing is deleted on any other machine.
83
+
84
+ When I run on two machines, only the one in charge connects to the agent network; the other stays quiet instead of knocking it off the relay every few seconds.
85
+
86
+ When I message another agent and it answers, the answer now comes back to the conversation where I asked, instead of sometimes getting stuck waiting for a free session.
87
+
88
+ Moving me to another machine with "instar wakeup" now talks to the machine that is really in charge, so the hand-over no longer fails with a signature error.
89
+
90
+ ## Summary of New Capabilities
91
+
92
+ None — a retry loop is removed.
93
+
94
+ None — a broken instruction is corrected for new and existing agents.
95
+
96
+ None — the same token totals, gathered without freezing the server.
97
+
98
+ None — an auto-start repair and corrected join instructions.
99
+
100
+ - `/api/pair` names a copied identity (`joiner-identity-is-inviter`) instead of accepting it.
101
+
102
+ None — the same estimate, without the freeze.
103
+
104
+ - A copied `sessions.claudePath` that is missing on this machine falls back to the detected Claude program, with a boot warning.
105
+ - A machine-list entry carrying a removal date is always treated as removed.
106
+
107
+ - `INSTAR_SHUTDOWN_DEADLINE_MS` sets the teardown bound.
108
+
109
+ None user-visible — the per-machine store the spec already promised is now populated automatically on update.
110
+
111
+ None — a connection gate on an existing flag.
112
+
113
+ - `threadline_send` returns the real conversation id on relay sends, so `threadline_history` on it works.
114
+
115
+ None — a CLI fix.
116
+
117
+ ## Evidence
118
+
119
+ `tests/unit/attention-single-topic-routing.test.ts`: a `duplicate-content` hold on reopen resolves with the item OPEN; an `origin-display-authority-unavailable` hold still rejects. The new test fails on main and passes with the fix; all 21 tests pass.
120
+
121
+ - `tests/unit/auth-read-after-secret-migration.test.ts`: the generated instruction resolves a plain string token, never emits the `{ secret: true }` placeholder (the old line does), and the migration rewrites an existing CLAUDE.md once.
122
+ - Live on this machine with no session env: a vault-externalized agent and a plain-config agent both get `200` from `/jobs` using the new lines; the old read gave `400`.
123
+ - 40 template/migration test files pass; the secret-externalization lint stays green.
124
+
125
+ `TokenLedgerPoller-codex.test.ts` covers responsiveness, repeated full reads, preserved-mtime rewrites, legacy duplicate-session ordering, the 500-file default, worker timeout/error/exit/close, and overlapping polls. The focused Vitest suites and TypeScript build validate the change.
126
+
127
+ - `tests/unit/autostart-needs-lifeline.test.ts`: server plist + Telegram configured → switch; lifeline plist → leave; server plist without Telegram → leave.
128
+ - The predicate matches the real LaunchAgent format on this machine (`<string>lifeline</string>` in the ProgramArguments of Echo's and Luna's plists).
129
+
130
+ - `tests/e2e/multi-machine-http.test.ts`: on the real server path, the inviter's own id is refused with 409 and the code is not spent.
131
+ - `tests/integration/pool-noninteractive-pairing.test.ts`: the inviter's own id is refused with 409, the code is not spent, the registry is unchanged; all 8 pass.
132
+ - `tests/unit/join-copied-identity.test.ts`: a copied identity is set aside (renamed, kept) including legacy key names, and a fresh identity with a different id is minted.
133
+ - Independent second-pass review: two concerns raised and fixed (see `upgrades/side-effects/join-refuses-copied-identity.md`).
134
+
135
+ - `tests/unit/quota-collector.test.ts`: streamed and sync parsing return identical totals on mixed input (CRLF, malformed, out-of-window, non-assistant, missing timestamp); a 20 MB file lets a 1 ms timer fire during the read; all 56 tests pass.
136
+ - Original CPU profiles (in instar#2120) attribute the stalls to `collectFromJsonl` → `parseFile` → `readFileSync`.
137
+
138
+ - Luna's Mac Studio log: 34 sessions "died during startup" while `sessions.claudePath` was `/opt/homebrew/bin/claude` (absent on the Studio); none after it was corrected by hand. Continuous `[mesh-rpc] rejected telegram-origin from <self>: wrong-recipient` from a registry entry with `revokedAt` set and `status: 'active'`.
139
+ - `tests/unit/second-machine-copied-config.test.ts` (8 tests), `tests/unit/Config.test.ts`, `tests/unit/machine-identity.test.ts` and 165 related unit files pass.
140
+
141
+ `tests/unit/shutdown-deadline.test.ts`: the deadline fires with the in-flight step name at exactly the bound, a cancelled timer never fires and is unref'd, and the env override/floor/default resolve as documented.
142
+
143
+ - `tests/unit/subscription-pool-legacy-migrate.test.ts` (7): publish + witness binding + legacy removal; tracked copy kept; refusals; recovery (finalize, rebuild from matching source, fail closed on a mutated source); pool-level drop/keep and persist-to-authority.
144
+ - `tests/unit/PostUpdateMigrator-subscription-pool-machine-local.test.ts` (3): tracked / untracked / no-repo; idempotent.
145
+ - `tests/e2e/subscription-pool-lifecycle.test.ts`: production update path + server boot serve only this machine's logins.
146
+ - 166 related unit files pass.
147
+
148
+ `tests/unit/threadline/ThreadlineBootstrap.test.ts`: with the relay enabled and `relayStandby: true`, no relay client is created, the suppression line is logged, and local components still bootstrap. 148 template/migrator test files pass.
149
+
150
+ - `tests/unit/ThreadlineClient-wire-thread.test.ts`: plaintext and encrypted paths return the wire thread (not the message id); affinity reuse and explicit ids preserved.
151
+ - Relay-send integration suites (nickname, priority, canonical remote agent, negotiator gate, local roundtrip) pass; 141 Threadline unit files (2,335 tests) pass.
152
+
153
+ `tests/unit/wakeup-targets-awake-machine.test.ts` (5): live lease wins over the registry; registry fallback; a revoked/unknown lease holder is ignored; endpoint probing picks the first rope that answers and never localhost; null when none answer.
@@ -0,0 +1,9 @@
1
+ # Combined Luna fixes: two CI checks made green (ELI16)
2
+
3
+ The combined submission of Luna's multi-machine fixes failed two automatic checks on GitHub.
4
+
5
+ 1. **The "no silent fallbacks" counter.** The repository counts every place where an error is caught and quietly ignored, and refuses any change that raises the count. Three new error handlers in the login-list migration ignore errors on purpose: a machine that has no identity yet, a migration record that does not exist, and a legacy file that cannot be read. Each of these is a normal, expected state, and each already falls back in the safe direction. They now carry the required explanatory note inside the handler, so the counter is back at its baseline. No behavior changes.
6
+
7
+ 2. **Documentation coverage.** The repository requires a minimum share of its code classes to be mentioned in the public docs. A new background worker that scans Codex usage files had no mention, which pushed the share just below the floor. The token-ledger section of the observability docs page now names the Codex rollout parser and the worker and says what they do.
8
+
9
+ Nothing about how the agent runs changes; these are comments and documentation only.
@@ -0,0 +1,58 @@
1
+ # Side-Effects Review — a reopened agent-health notice held as a duplicate counts as delivered
2
+
3
+ **Version / slug:** `agent-health-reopen-duplicate`
4
+ **Date:** `2026-10-05`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required (narrow error classification on one notice path)`
7
+
8
+ ## Summary of the change
9
+
10
+ ACT-1304 fault 2 (reported by Luna/sagemind). Luna's laptop log, 2026-10-04 12:05-12:18Z: `Threadline.SpawnDrainAttention: Attention write failed: Telegram message held: duplicate-content`, every 30 s for 13+ minutes. The spawn drain's give-up handler calls `TelegramAdapter.createOrReopenAgentHealthAttentionItem`; once the item exists, each call reposts a fixed "returned after recovery" line to the Agent Health topic. The outbound dedup held each repost as a duplicate of the copy already accepted in that topic, the adapter rethrew the hold, and the spawn manager treated it as a failed Attention write and retried. Now a `TelegramOriginHoldError` with reason `duplicate-content` on that reopen post is treated as delivered; every other hold still throws so the caller retries as before.
11
+
12
+ ## Decision-point inventory
13
+
14
+ None added. The dedup gate's decision is unchanged; this changes how one caller interprets "already delivered".
15
+
16
+ ## 1. Over-block
17
+
18
+ Nothing new is refused.
19
+
20
+ ## 2. Under-block
21
+
22
+ `duplicate-content` is returned only when the same text to the same topic was platform-accepted within the dedup window (`OriginContentDedup` returns `content-reservation-pending` for an in-flight copy, which still throws), so the operator has the notice. Other holds (for example `origin-display-authority-unavailable`) still fail and retry.
23
+
24
+ ## 3. Level-of-abstraction fit
25
+
26
+ The adapter owns this send and knows the hold reason; the spawn manager stays generic.
27
+
28
+ ## 4. Signal vs authority compliance
29
+
30
+ No authority added; the dedup gate keeps its authority.
31
+
32
+ ## 4b. Judgment-point check
33
+
34
+ Not a decision point.
35
+
36
+ ## 5. Interactions
37
+
38
+ Ends the 30 s retry loop and its degradation-log noise. The first-creation path already swallowed send errors. Other `sendToTopic` callers are untouched.
39
+
40
+ ## 6. External surfaces
41
+
42
+ No new message is sent; a duplicate stays suppressed.
43
+
44
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
45
+
46
+ Machine-local; the Agent Health lane and the dedup are per machine.
47
+
48
+ ## 8. Rollback cost
49
+
50
+ Revert; no state change.
51
+
52
+ ## Conclusion
53
+
54
+ A one-branch error classification that stops a retry loop on a notice the operator already has.
55
+
56
+ ## Class-Closure Declaration (display-only mirror)
57
+
58
+ `{defectClass: "unbounded-self-action", closure: "n/a", reason: "Adds no self-triggered action: it classifies a duplicate-content hold on one notice as already delivered, which REMOVES the trigger for the callers 30s retry loop. Other holds still throw, bounded by the spawn drain existing give-up latch."}`
@@ -0,0 +1,56 @@
1
+ # Side-Effects Review — the CLAUDE.md API-auth instruction survives secret externalization
2
+
3
+ **Version / slug:** `auth-read-after-secret-migration`
4
+ **Date:** `2026-10-05`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required (instruction text + idempotent CLAUDE.md migration)`
7
+
8
+ ## Summary of the change
9
+
10
+ ACT-1303 (reported by Luna/sagemind on Threadline, 2026-10-03): "instar pair moving secrets to {secret:true} breaks scripts that read them." After `SecretMigrator` externalizes `authToken`, `.instar/config.json` holds `{ "secret": true }`. Instar's shipped hooks and scripts were already migrated to read `$INSTAR_AUTH_TOKEN` first with a string-type guard, but the CLAUDE.md template's "API Authentication" section still told every agent to run `AUTH=$(python3 -c "...get('authToken',''))")`, which prints the placeholder object. Every curl an agent built from it was rejected (reproduced in this session: `400` with the config read, `200` with the env token).
11
+
12
+ The template (both occurrences in `generateClaudeMd`) now reads `$INSTAR_AUTH_TOKEN`, then `node .instar/scripts/secret-get.mjs authToken`, then the config only when the value is a string. `PostUpdateMigrator.migrateClaudeMd` rewrites the old line in existing agents' CLAUDE.md (content-sniffed, idempotent).
13
+
14
+ ## Decision-point inventory
15
+
16
+ None. Instruction text and a text migration.
17
+
18
+ ## 1. Over-block
19
+
20
+ Nothing is refused.
21
+
22
+ ## 2. Under-block
23
+
24
+ Agent-authored custom scripts that copied the old one-liner are not rewritten (custom files are never touched); the corrected CLAUDE.md instruction is what agents copy from going forward.
25
+
26
+ ## 3. Level-of-abstraction fit
27
+
28
+ CLAUDE.md is where agents learn this; the migration path is the documented one for CLAUDE.md sections.
29
+
30
+ ## 4. Signal vs authority compliance
31
+
32
+ No authority involved.
33
+
34
+ ## 4b. Judgment-point check
35
+
36
+ Not a decision point.
37
+
38
+ ## 5. Interactions
39
+
40
+ Matches the env-first, string-guarded pattern Instar's hooks already use. The secret-externalization lint over `PostUpdateMigrator.ts` stays green: the old line is assembled from pieces because it is the text being removed.
41
+
42
+ ## 6. External surfaces
43
+
44
+ CLAUDE.md text for new and updated agents. Verified live: on a vault-externalized agent and on a plain-config agent, with no session env, the new lines produce a token the server accepts (`200`).
45
+
46
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
47
+
48
+ Each machine's CLAUDE.md is migrated on its own update; the instruction resolves per machine (session env / local secret store / local config).
49
+
50
+ ## 8. Rollback cost
51
+
52
+ Revert; the migration only rewrites one known line.
53
+
54
+ ## Conclusion
55
+
56
+ Closes the last place Instar itself told agents to read a secret that pairing had moved.
@@ -0,0 +1,58 @@
1
+ # Side-Effects Review — Codex rollout scan runs on a worker thread
2
+
3
+ **Version / slug:** `codex-rollout-scan-yield`
4
+ **Date:** `2026-10-05`
5
+ **Author:** `Luna (sagemind) drafted; echo completed`
6
+ **Second-pass reviewer:** `independent subagent (see below)`
7
+
8
+ ## Summary of the change
9
+
10
+ instar#2121. `TokenLedger.scanCodexRolloutsAsync` read and parsed up to 500 Codex rollout files synchronously on the server event loop; on a 14 GB / ~54k-file store that blocked the server 8-12 s about once a minute (a CPU profile put ~84% of frozen time here). Discovery, reads and parsing now run in `src/monitoring/CodexRolloutScan.worker.ts`; the main thread only applies the legacy per-file SQLite upserts in the worker's discovery order. Same `listAllRollouts` selection (500, descending mtime), same age cutoff and cwd attribution, same latest-upsert-wins behavior, no cache.
11
+
12
+ ## Decision-point inventory
13
+
14
+ None. Observability-only scan (read-only over Codex files, writes only the token ledger).
15
+
16
+ ## 1. Over-block
17
+
18
+ Nothing is refused. An overlapping poll while a scan runs reports zero and starts no second worker; the next poll scans normally.
19
+
20
+ ## 2. Under-block
21
+
22
+ SQLite upserts stay synchronous on the main thread and can pause behind a competing writer; the rows are small. Selected files are still reread every poll, as before.
23
+
24
+ ## 3. Level-of-abstraction fit
25
+
26
+ The fix sits at the one place that did the blocking work. The poller and its cadence are unchanged.
27
+
28
+ ## 4. Signal vs authority compliance
29
+
30
+ No authority. Token observability never gates anything.
31
+
32
+ ## 4b. Judgment-point check
33
+
34
+ Not a decision point.
35
+
36
+ ## 5. Interactions
37
+
38
+ A worker error, unexpected exit or 120 s timeout now rejects the scan (logged by the poller's `onError`) where an in-process failure returned zeros; a later poll retries. `close()` terminates an active worker; `finally` resets the running flag. No heap cap on the worker, so one large rollout cannot fail the whole scan (the in-process scan had the main heap). Starting a worker per poll costs roughly 30-50 ms and ~10 MB briefly.
39
+
40
+ ## 6. External surfaces
41
+
42
+ None visible. `/tokens/*` returns the same totals.
43
+
44
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
45
+
46
+ Machine-local by design: each machine scans its own Codex store into its own ledger.
47
+
48
+ ## 8. Rollback cost
49
+
50
+ Revert the commit; no schema or data change.
51
+
52
+ ## Conclusion
53
+
54
+ A relocation of existing work off the event loop with parity preserved, covered by ordering, re-read, timeout, overlap, failure, shutdown and responsiveness tests.
55
+
56
+ ## Second-pass review (if required)
57
+
58
+ Two passes on Luna's draft (astra) found three parity breaks (a size/mtime skip cache, changed processing order, batching/limit changes); all were removed. Independent subagent review on the final diff, 2026-10-05: promise settling, close-during-scan, data copy (`ParsedCodexSession` is strings/numbers/nulls) and parity all check out. **Concern raised:** the 512 MB worker heap cap let one oversized rollout fail every file in the scan. Resolved by removing the cap. A second note — the `src`-run fallback to `dist/` could test a stale worker — is covered by `tests/setup/build-dist.globalSetup.ts`, which rebuilds `dist` before every vitest run. Concur after the fix.
@@ -0,0 +1,175 @@
1
+ # Side-Effects Review — Jev review-flag shadow (log-only "does this reply need review?" measurement)
2
+
3
+ **Version / slug:** `jev-review-flag-shadow`
4
+ **Date:** `2026-10-05`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `independent review subagent + codex-cli gpt-5.5 (one 80/20 round on the spec)`
7
+
8
+ ## Summary of the change
9
+
10
+ A dark, log-only research instrument (spec `docs/specs/jev-review-flag-shadow.md`).
11
+ A 60-second unref'd server timer reads the last 512 KB of the agent's own
12
+ Telegram history, selects each agent conversational reply (provenance
13
+ `agent`, at most 30 minutes old, newer than its topic's watermark), pairs it
14
+ with the operator message it answers (same topic, earlier, within 6 hours),
15
+ secret-scrubs both before cutting them (reply 2,000, request 600 characters,
16
+ cuts disclosed in band), asks Jev (`jev-1.13.0`) one three-label question
17
+ (`needs_review` / `fine` / `cannot_tell`) in both option orders, and appends a
18
+ content-free row to `logs/jev-review-flag-shadow.jsonl`.
19
+ `GET /jev-review-flag/summary` reads the log. Files:
20
+ `src/core/JevReviewFlagShadow.ts` (new), `src/server/routes.ts` (one read
21
+ route), `src/commands/server.ts` (construction + start),
22
+ `src/config/ConfigDefaults.ts`, `src/core/types.ts`,
23
+ `src/core/devGatedFeatures.ts`, `src/scaffold/templates.ts` +
24
+ `src/core/PostUpdateMigrator.ts` (awareness card, both paths),
25
+ `src/server/CapabilityIndex.ts` (internal prefix).
26
+
27
+ ## Decision-point inventory
28
+
29
+ No decision-point surface. Nothing is blocked, allowed, filtered, delivered or
30
+ routed. No code path reads the log except the summary route, which a person
31
+ reads.
32
+
33
+ ---
34
+
35
+ ## 1. Over-block
36
+
37
+ No block/allow surface — over-block not applicable.
38
+
39
+ ---
40
+
41
+ ## 2. Under-block
42
+
43
+ No block/allow surface — under-block not applicable. Measurement blind spots,
44
+ named in the spec: Slack replies (not in this history); desk messages sent
45
+ through the operator's account can be picked as the request; replies logged
46
+ more than 30 minutes before the timer sees them (only possible if the server
47
+ was down) are not judged.
48
+
49
+ ---
50
+
51
+ ## 3. Level-of-abstraction fit
52
+
53
+ Right layer. The operator corrects what reaches Telegram, and the server
54
+ already keeps that history; reading it server-side needs no hook, no settings
55
+ migration and no per-framework work. It reuses `readJsonlTailLines`,
56
+ `scrubForStore`, `resolveDevAgentGate` and the feature-metrics recorder, and
57
+ follows the jev-circles-shadow shape (vault key, closed reason enums, daily
58
+ cap seeded from the log). It does not feed a gate: the point is to measure
59
+ before anything is shown.
60
+
61
+ ---
62
+
63
+ ## 4. Signal vs authority compliance
64
+
65
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
66
+
67
+ - [ ] No — this change produces a signal consumed by an existing smart gate.
68
+ - [x] No — this change has no block/allow surface.
69
+ - [ ] Yes — but the logic is a smart gate with full conversational context.
70
+ - [ ] ⚠️ Yes, with brittle logic — STOP.
71
+
72
+ It produces a logged signal that nothing consumes. It holds no authority.
73
+
74
+ ---
75
+
76
+ ## 4b. Judgment-point check (Judgment Within Floors standard)
77
+
78
+ No new static heuristic at a competing-signals decision point. The 0.5
79
+ threshold only labels rows in a research log; both orders' probabilities are
80
+ kept so the threshold can be moved after the fact. No action follows.
81
+
82
+ ---
83
+
84
+ ## 5. Interactions
85
+
86
+ - **Shadowing / double-fire:** none. It reads the Telegram history (append-only,
87
+ written by TelegramAdapter) and writes only its own log. The other Jev
88
+ shadows have separate logs, feature names and daily bounds.
89
+ - **Event loop:** one synchronous bounded read of at most 512 KB per minute
90
+ (the same helper the 2026-06-22 event-loop fixes introduced), off every
91
+ request path; nothing when disabled. Calls are sequential, so at most one is
92
+ in flight.
93
+ - **Races:** a reply logged after a tick is seen at the next tick. Ticks never
94
+ overlap (a `running` latch). Watermarks advance only past replies that were
95
+ judged or failed at the vendor/scrub, so the daily cap never drops an
96
+ eligible reply silently; seeding from the log at start means a restart
97
+ judges nothing twice.
98
+ - **Feedback loops:** none. It never sends, so it never adds to the history it
99
+ reads.
100
+
101
+ ---
102
+
103
+ ## 6. External surfaces
104
+
105
+ - **Other agents:** fleet agents get the code and the config block; the
106
+ feature stays dark (dev gate) and they hold no TypeSafe key.
107
+ - **External systems:** on the development agent, a scrubbed reply (≤2,000
108
+ characters) and request (≤600) go to TypeSafe (`api.typesafe.ai`), the
109
+ vendor the other Jev shadows already use. Bounded by the daily cap (default
110
+ 3000 calls ≈ $0.45/day worst case; ~$0.05/day expected), one call at a time,
111
+ and a 1.5 s abort.
112
+ - **Persistent state:** one append-only JSONL log (~300 bytes/row, a few
113
+ hundred rows/day); the summary reads only the last 5 MB.
114
+ - **Operator surface:** none. The read route is informational (Bearer).
115
+
116
+ ---
117
+
118
+ ## 6b. Operator-surface quality (Operator-Surface Quality standard)
119
+
120
+ No operator surface — not applicable.
121
+
122
+ ---
123
+
124
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
125
+
126
+ Machine-local BY DESIGN: each machine judges the replies its own Telegram
127
+ history holds (the sends relayed through it), and the measurement is a rate
128
+ that per-machine logs answer; scoring joins each machine's log to the same
129
+ machine's history by message id. No notices, no topic-following state, no
130
+ generated URLs.
131
+
132
+ ---
133
+
134
+ ## 8. Rollback cost
135
+
136
+ Pure code change. Kill switch: `intelligence.jevReviewFlagShadow.enabled:
137
+ false` (read live at the next tick, no restart). Full back-out: revert and
138
+ ship a patch; the log file can be left or deleted. No user-visible effect in
139
+ either direction.
140
+
141
+ ---
142
+
143
+ ## Conclusion
144
+
145
+ Clear to ship behind the dev gate. The review round moved the trigger from
146
+ the Stop hook to a server-side tail of the Telegram history, which removed a
147
+ hook change, a reply-logging race, a session-name mismatch and the
148
+ Claude-only limit; the remaining findings were local and are tested.
149
+
150
+ ---
151
+
152
+ ## Second-pass review (if required)
153
+
154
+ **Reviewer:** independent review subagent + codex-cli gpt-5.5 (one 80/20 round, 2026-10-05)
155
+ **Independent read: concur, after the trigger change and local fixes above**
156
+
157
+ Full record: `docs/specs/reports/jev-review-flag-shadow-convergence.md`.
158
+
159
+ ---
160
+
161
+ ## Evidence pointers
162
+
163
+ - `tests/unit/jev-review-flag-shadow.test.ts`, `tests/integration/jev-review-flag-shadow-routes.test.ts`, `tests/e2e/jev-review-flag-shadow-lifecycle.test.ts`.
164
+ - Live smoke through the real module, real history and real vendor
165
+ (2026-10-05): 4 replies judged, 89–169 ms, all `fine`, both orders agreeing;
166
+ no message text in the log.
167
+
168
+ ---
169
+
170
+ ## Class-Closure Declaration (display-only mirror)
171
+
172
+ No agent-authored-artifact defect — not applicable. Not a self-triggered
173
+ controller in the `unbounded-self-action` sense: it never fires a restart,
174
+ retry, respawn, spawn, notify or kill; its only action is one bounded, capped
175
+ vendor call and a log row per agent reply.
@@ -0,0 +1,59 @@
1
+ # Side-Effects Review — a joined machine ends up supervised by the lifeline, like the first
2
+
3
+ **Version / slug:** `join-autostart-lifeline`
4
+ **Date:** `2026-10-05`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required (auto-start self-heal extends an existing check; join output text)`
7
+
8
+ ## Summary of the change
9
+
10
+ instar#2122 (Luna/sagemind second-machine bring-up): "`instar join` installed a `server start` LaunchAgent instead of `lifeline start` (unlike the first machine and Echo's Studio), and it started a stray server outside the supervisor."
11
+
12
+ `instar join` scaffolds a machine-local config with `messaging: []` (the Telegram token is machine-local), so `installAutoStart` correctly picks `server start` at join time — the lifeline refuses to run without Telegram config. Nothing ever revisited that once Telegram was configured on the standby. And join's closing text told the operator to run `instar server start`, though auto-start had already launched one, producing a second, unsupervised server.
13
+
14
+ 1. Server boot's existing auto-start self-heal (which already regenerates stale plist formats) now also regenerates the LaunchAgent with the lifeline when Telegram is configured but the plist starts the bare server (`autoStartNeedsLifeline` in `setup.ts`).
15
+ 2. `instar join` no longer tells the operator to start a server when auto-start was installed.
16
+
17
+ ## Decision-point inventory
18
+
19
+ One added condition in the boot self-heal: "Telegram configured AND plist lacks `lifeline`" → reinstall.
20
+
21
+ ## 1. Over-block
22
+
23
+ Nothing is refused. An agent with Telegram configured that deliberately runs a server-only LaunchAgent is switched to the lifeline — the same outcome the self-heal already produces whenever it reinstalls for any other reason (it passes the Telegram flag).
24
+
25
+ ## 2. Under-block
26
+
27
+ macOS only (the plist check lives in the darwin branch, like the existing format checks). Linux systemd units are unchanged. The standby lifeline's own Telegram-polling behaviour is unchanged (it already suppresses polling under `multiMachine.telegramPolling: false`).
28
+
29
+ ## 3. Level-of-abstraction fit
30
+
31
+ Extends the one place that already repairs auto-start drift at boot.
32
+
33
+ ## 4. Signal vs authority compliance
34
+
35
+ No new authority over agent behaviour; an install-time repair.
36
+
37
+ ## 4b. Judgment-point check
38
+
39
+ Not a competing-signals decision.
40
+
41
+ ## 5. Interactions
42
+
43
+ Reinstalling reloads the LaunchAgent, which restarts the server once under the lifeline — the same one-time restart the existing format self-heals cause. On the next boot the plist matches and nothing further happens.
44
+
45
+ ## 6. External surfaces
46
+
47
+ One boot log line when it switches; `instar join` closing text.
48
+
49
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
50
+
51
+ Machine-local by design: each machine's LaunchAgent is its own.
52
+
53
+ ## 8. Rollback cost
54
+
55
+ Revert; a switched plist keeps running the lifeline, which is the intended state.
56
+
57
+ ## Conclusion
58
+
59
+ A joined standby converges on the same supervision as the first machine once it can, and join stops inviting a duplicate server.