instar 1.3.328 → 1.3.330

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,98 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ The Instar CLI now rejects unknown top-level commands before the bare setup flow can run. A typo such as `instar dev:claim-checkk` prints a clear unknown-command error and exits with status 1 instead of falling into the interactive setup wizard.
9
+
10
+ Bare `instar` remains unchanged: it still opens the setup flow for new or interrupted setup sessions. `instar help` now prints CLI help instead of entering setup.
11
+
12
+ Audit fix #2 under "No Unbounded Loops" (P19): the lease transport — the
13
+ machine-to-machine "who is awake" wire — gains its missing brakes. (1) Per-peer
14
+ failure logging is now state-change-based via the new pure `PeerFailureLogGate`
15
+ (first failure / every-360th reminder / recovery-once): a down peer at the
16
+ deliberate ~5s anti-blinding pull cadence now produces ~49 log lines per day
17
+ instead of ~17,000, and previously-SILENT non-ok responses (a peer actively
18
+ rejecting us) are now visibly logged under the same bound. (2) Both transport
19
+ fetches carry `AbortSignal.timeout` — the pull loop's re-entrancy guard meant a
20
+ single hung socket would have wedged every future pull forever. The timeout
21
+ defaults to 30s and is derived from `leaseTtlMs` at the server construction
22
+ site (`min(ttl/2, 30s)`) — sized ABOVE the fleet's documented 5–40s
23
+ receiver-stall envelope after the adversarial second-pass reviewer showed a
24
+ 10s default could convert a slow-but-alive peer into "no medium" and falsely
25
+ self-suspend a healthy lease holder via the renewal path. Cadence, lease, and
26
+ anti-blinding semantics unchanged.
27
+
28
+ The ServerSupervisor's slow-retry mode (respawn a dead server every 2h, forever)
29
+ is a sanctioned Eternal Sentinel under the new "No Unbounded Loops" standard —
30
+ but it violated condition 4: it never told anyone. A multi-day crash-revive loop
31
+ was indistinguishable from silence. Now a pure one-shot latch
32
+ (`SlowRetrySentinelEscalation`, the AgeKillBackoff suppressor shape) fires
33
+ exactly once per outage episode after a sustained-failure threshold (default
34
+ 12h ≈ 6 failed cycles): the supervisor emits `sentinelStalled` and the lifeline
35
+ sends ONE operator message with the two useful levers (`/lifeline doctor`,
36
+ `/lifeline reset`), then the sentinel keeps quietly retrying. The message goes
37
+ directly to the Telegram Bot API from the lifeline process — no dependency on
38
+ the (down) agent server. Recovery re-arms the latch via the single
39
+ `resetCircuitBreaker()` funnel; retry/kill/spawn decisions are unchanged.
40
+
41
+ ## What to Tell Your User
42
+
43
+ - **Clearer command typos**: "When I mistype an Instar command, I now fail fast with a clear message instead of opening setup and looking stuck."
44
+
45
+ If you run me on more than one machine: my logs no longer flood with thousands
46
+ of identical "can't reach the other machine" lines when a machine is down (you
47
+ get one line when it goes, a brief reminder every half hour, one when it's
48
+ back), and a single stuck network connection can no longer silently break the
49
+ machines' ability to notice each other — without any risk of a healthy machine
50
+ wrongly stepping down.
51
+
52
+ If your agent's server ever gets stuck in a crash loop, you'll now get one
53
+ clear heads-up after ~12 hours — "still down, here's how to diagnose or force a
54
+ retry, I'll keep trying" — instead of discovering the outage by noticing your
55
+ agent went quiet for days. One message per outage, never a stream.
56
+
57
+ ## Summary of New Capabilities
58
+
59
+ | Capability | How to Use |
60
+ |-----------|-----------|
61
+ | Unknown command rejection | Automatic for mistyped top-level Instar commands |
62
+ | Bare setup preservation | Automatic when running Instar without a command |
63
+ | Help command preservation | Automatic when asking Instar for help |
64
+
65
+ - Bounded mesh failure logging (state-change + capped reminders, recovery
66
+ notices, rejecting-peer visibility) via `PeerFailureLogGate` — reusable for
67
+ other fixed-cadence transports.
68
+ - Hung-socket protection on all lease-transport HTTP calls (config-derived
69
+ abort timeout).
70
+
71
+ - Slow-retry escalation: a sustained server outage (default 12h in slow-retry)
72
+ produces exactly one operator notification per episode; the never-give-up
73
+ revival behavior is unchanged. In-code default; no config required.
74
+
75
+ ## Evidence
76
+
77
+ Reproduced the typo path locally with the built CLI. Before this change, a typoed command could enter the interactive setup path and hang an agent shell. After the change, `node dist/cli.js dev:claim-checkk` exits 1 with `error: unknown command 'dev:claim-checkk'` and the help hint. A separate `node dist/cli.js help` probe exits 0 and prints CLI help. A separate bare invocation probe, `timeout 3 node dist/cli.js`, still enters setup and times out at status 124, confirming the default setup behavior remains intact.
78
+
79
+ Loop-safety audit (CMT-1109) verified at source: `pullPeer` per-attempt logging
80
+ (~17k lines/day per down peer at 5s cadence — the reaper-flood signature) and
81
+ no abort signal on either fetch while `leasePulling` blocks re-entry. Reviewer
82
+ finding applied pre-commit (timeout 10s→30s + config derivation; the
83
+ false-self-suspend trace is documented in `docs/specs/mesh-pull-brakes.md`).
84
+ Tests: 22 green across `PeerFailureLogGate.test.ts` (incl. the P19
85
+ sustained-failure bound: 17,280 failures → 49 lines) and
86
+ `HttpLeaseTransport.test.ts` (AbortSignal presence pin, gated pull/broadcast
87
+ logging, recovery-once, server.ts derivation wiring pin); all pre-existing
88
+ lease suites green; tsc clean.
89
+
90
+ Loop-safety audit finding (CMT-1109), verified at source: the slow-retry block
91
+ in `src/lifeline/ServerSupervisor.ts` was annotated "never truly give up" with
92
+ no operator signal. Fix is the first PR under constitution P19 ("No Unbounded
93
+ Loops", incl. Justin's Eternal Sentinel caveat — this loop is its namesake
94
+ case). Tests: 10 green in `tests/unit/SlowRetrySentinelEscalation.test.ts`
95
+ including the P19 sustained-failure bound (week-long never-recovering episode →
96
+ exactly 1 escalation) and source-shape wiring pins. Independent adversarial
97
+ second-pass: CONCUR (all probes safe — per-tick reachability, no double-fire,
98
+ server-independent delivery, emit-before-spawn ordering). tsc clean.
@@ -0,0 +1,31 @@
1
+ # Side-Effects Review - CLI unknown-command trap
2
+
3
+ **Version / slug:** `cli-unknown-command-trap`
4
+ **Date:** `2026-06-06`
5
+ **Author:** `instar-codey`
6
+
7
+ ## Summary
8
+
9
+ This change adds a top-level command guard before Commander parses the CLI. Unknown first-position command tokens now print a clear error and exit 1. The existing no-argument setup path is preserved, and Commander’s implicit `help` command remains allowed.
10
+
11
+ ## Decision Points
12
+
13
+ - Add `rejectUnknownTopLevelCommand` in `src/cli.ts`.
14
+ - Call it after all commands are registered and before `program.parse()`.
15
+ - Add e2e regression tests for `dist/cli.js dev:claim-checkk` and `dist/cli.js help`.
16
+
17
+ ## Over-Block
18
+
19
+ Risk: rejecting a legitimate command before Commander sees it. Mitigation: the guard builds its allowlist from `program.commands` and command aliases after registration, and explicitly handles Commander’s implicit `help` command because Commander does not list it in `program.commands` and otherwise sends it into the default setup action. Options and the no-argument path bypass the guard.
20
+
21
+ ## Under-Block
22
+
23
+ Nested unknown subcommands remain Commander-owned. This fix targets the incident class: unknown top-level command tokens falling into the default setup action.
24
+
25
+ ## Interactions
26
+
27
+ No server routes, persistent state, messaging, or external APIs change. The behavior changes are limited to process exit behavior for unknown top-level CLI commands and restoring the intended `instar help` help output.
28
+
29
+ ## Rollback
30
+
31
+ Rollback is a normal revert of the helper, call site, test, and notes. No data migration is involved.
@@ -0,0 +1,63 @@
1
+ # Side-Effects Review — Mesh Lease-Transport Brakes
2
+
3
+ **Version / slug:** `mesh-pull-brakes`
4
+ **Date:** `2026-06-05`
5
+ **Author:** `Echo (instar-dev agent)`
6
+ **Second-pass reviewer:** `independent adversarial reviewer subagent — CONCUR with one substantive finding (10s timeout default → false self-suspend risk), APPLIED before commit (30s default + leaseTtlMs-derived wiring)`
7
+
8
+ ## Summary of the change
9
+
10
+ `HttpLeaseTransport` (the fenced-lease wire) gains the two P19 brakes its deliberate fixed-cadence callers were missing: per-peer state-change failure logging via the new pure `PeerFailureLogGate` (first / every-Nth / recovery; exact bound ⌈F/N⌉+1 lines per streak; default N=360 ≈ 30min at the 5s pull cadence) replacing one-line-per-attempt (≈17k/day per down peer), with previously-silent non-ok responses now gated-logged; and `AbortSignal.timeout` on both fetches (default 30s; `server.ts` derives `min(leaseTtlMs/2, 30_000)`) closing the hung-socket wedge behind the pull loop's `leasePulling` re-entrancy guard. Files: `PeerFailureLogGate.ts` (new), `HttpLeaseTransport.ts`, one wiring line in `server.ts`, two test files.
11
+
12
+ ## Decision-point inventory
13
+
14
+ - `HttpLeaseTransport.pullPeer` / `broadcast` — **modify (bounded)** — same requests, same return semantics; failures now abort at the timeout instead of hanging forever, and logging is gated.
15
+ - `PeerFailureLogGate` — **add** — a pure log shaper; no authority of any kind.
16
+ - `server.ts` lease-transport construction — **modify (one line)** — passes the config-derived timeout.
17
+
18
+ ## 1. Over-block
19
+
20
+ The one real over-block risk was found BY the reviewer and closed BEFORE commit: a 10s timeout default sat inside the fleet's documented 5–40s receiver-stall envelope; because `LeaseCoordinator.renew()` treats an unconfirmed broadcast past `leaseTtlMs` as "no medium" and self-suspends the holder, a slow-but-alive peer would have falsely demoted a healthy awake machine on its FIRST slow renewal (renewal cadence 120s > TTL 60s leaves no retry headroom). Applied fix: 30s default (above the stall envelope's bulk; a truly hung socket never returns, so the wedge bound is intact) + derivation from `leaseTtlMs` at the construction site so operator-widened TTLs keep proportion. Residual: a peer consistently slower than 30s per request still reads as unreachable — at that point it is functionally unreachable (the TTL itself is 60s).
21
+
22
+ ## 2. Under-block
23
+
24
+ (a) Sibling transports (`HttpLiveTailTransport`, `ReplyMarkerTransport`) share the no-timeout + per-attempt-log pattern at lower blast radius (no lease authority; live-tail already has #867's per-topic backoff bounding attempt rate) — next audit targets <!-- tracked: CMT-1109 -->. (b) A peer removed from the registry mid-failure-streak leaves one stale `Map` entry in the gate (never recovers, never deleted) — bounded by historical peer×op count (reviewer: "not worth fixing, worth knowing"). (c) The gate bounds LOG volume, not attempt volume — attempt cadence is the deliberate anti-blinding design and is out of scope by intent.
25
+
26
+ ## 3. Level-of-abstraction fit
27
+
28
+ Yes. The brakes live in the transport that generates the cost; the cadence stays owned by the caller (`MultiMachineCoordinator`), exactly as the file's contract states. `PeerFailureLogGate` is the established pure-suppressor shape (`AgeKillBackoff`, `SlowRetrySentinelEscalation`) — count-based here (no clock) because the bound should be exact per attempt, not wall-time-dependent. Keying broadcast/pull separately mirrors `isReachable()`'s bidirectional reachability model.
29
+
30
+ ## 4. Signal vs authority compliance
31
+
32
+ **Required reference:** `docs/signal-vs-authority.md`
33
+
34
+ - [x] No — the gate shapes LOG output only. The timeout changes failure TIMING (hang → bounded abort), not failure handling: both callers already treated failures as advisisory data. Lease acquire/renew/suspend semantics are byte-identical; the reviewer traced the renewal path end-to-end to confirm the only semantic risk (timeout sizing) and it was resolved.
35
+
36
+ ## 5. Interactions
37
+
38
+ - **Renewal-requires-medium:** the critical interaction, analyzed in §1 (reviewer probe 1) — resolved by the 30s/config-derived sizing.
39
+ - **Anti-blinding pull loop:** a timed-out pull loses one learning tick and retries in ~5s; never feeds self-suspend. The timeout RESTORES anti-blinding in the hung-socket case (previously wedged forever).
40
+ - **Test clocks:** `AbortSignal.timeout` uses real timers; the injected `now()` drives only reachability windows — orthogonal, no interaction (reviewer probe 2; all suites green).
41
+ - **Log-format consumers:** no test or consumer pinned the old per-attempt strings (reviewer probe 5, grepped src+tests).
42
+ - **Node engines:** `AbortSignal.timeout` needs ≥17.3; package.json engines is ≥20.12 (reviewer probe 4).
43
+
44
+ ## 6. External surfaces
45
+
46
+ - **Logs only** — fewer lines in the failure steady-state, new (bounded) visibility for rejecting peers and recoveries. No API, schema, message, or notification surface. No topic creation (Bounded Notification Surface untouched).
47
+ - **Persistent state / config / migration:** none. The derivation reads the existing `leaseTtlMs`; `requestTimeoutMs`/`failureLogEveryN` are dep options with in-code defaults.
48
+
49
+ ## 7. Rollback cost
50
+
51
+ Revert the commit. No state, no config, no schema. Rollback's only observable effect: the log flood and the hung-socket wedge return.
52
+
53
+ ## Conclusion
54
+
55
+ The transport carrying the mesh's most consequential state (the lease) now satisfies P19: bounded per-attempt cost (log gate), a hard bound on a single attempt's duration (abort timeout), with the deliberately-unbounded cadence left intact and explicitly justified (anti-blinding). The review process did its job visibly: the reviewer's false-self-suspend trace changed the shipped default before commit — recorded in the spec so the sizing rationale survives.
56
+
57
+ ---
58
+
59
+ ## Phase 5 — Second-pass review (lease/coherence-critical → required)
60
+
61
+ An independent adversarial reviewer ran five probes at line level: (1) false unreachability via the renewal path — FOUND the 10s-default risk (traced `tickLease` 120s cadence → `renew()` → `broadcast` → self-suspend at TTL 60s against the fleet's 5–40s stall envelope) and proposed the exact fix that was then applied (30s default + `min(leaseTtlMs/2, 30s)` wired at `server.ts`); (2) AbortSignal real-timers vs injected test clocks — orthogonal, clean; (3) gate key independence + memory bounds — intended and bounded (one stale-key note, accepted); (4) Node engines — satisfied; (5) old-format/silence pins — none in src or tests. Ran the six lease/transport suites + tsc — all green. **Verdict: CONCUR** (with the recommended change, which is now in the diff).
62
+
63
+ **Post-CI note:** the original commit was made before this worktree had its husky shims installed (`pnpm install` not yet run → zero hooks → no decision-audit entry staged) — the decision-audit CI gate caught the bypass exactly as designed (#830). This follow-up commit runs through the full gate and carries the entry.
@@ -1,44 +0,0 @@
1
- # Upgrade Guide — vNEXT
2
-
3
- <!-- assembled-by: assemble-next-md -->
4
- <!-- bump: patch -->
5
-
6
- ## What Changed
7
-
8
- The ServerSupervisor's slow-retry mode (respawn a dead server every 2h, forever)
9
- is a sanctioned Eternal Sentinel under the new "No Unbounded Loops" standard —
10
- but it violated condition 4: it never told anyone. A multi-day crash-revive loop
11
- was indistinguishable from silence. Now a pure one-shot latch
12
- (`SlowRetrySentinelEscalation`, the AgeKillBackoff suppressor shape) fires
13
- exactly once per outage episode after a sustained-failure threshold (default
14
- 12h ≈ 6 failed cycles): the supervisor emits `sentinelStalled` and the lifeline
15
- sends ONE operator message with the two useful levers (`/lifeline doctor`,
16
- `/lifeline reset`), then the sentinel keeps quietly retrying. The message goes
17
- directly to the Telegram Bot API from the lifeline process — no dependency on
18
- the (down) agent server. Recovery re-arms the latch via the single
19
- `resetCircuitBreaker()` funnel; retry/kill/spawn decisions are unchanged.
20
-
21
- ## What to Tell Your User
22
-
23
- If your agent's server ever gets stuck in a crash loop, you'll now get one
24
- clear heads-up after ~12 hours — "still down, here's how to diagnose or force a
25
- retry, I'll keep trying" — instead of discovering the outage by noticing your
26
- agent went quiet for days. One message per outage, never a stream.
27
-
28
- ## Summary of New Capabilities
29
-
30
- - Slow-retry escalation: a sustained server outage (default 12h in slow-retry)
31
- produces exactly one operator notification per episode; the never-give-up
32
- revival behavior is unchanged. In-code default; no config required.
33
-
34
- ## Evidence
35
-
36
- Loop-safety audit finding (CMT-1109), verified at source: the slow-retry block
37
- in `src/lifeline/ServerSupervisor.ts` was annotated "never truly give up" with
38
- no operator signal. Fix is the first PR under constitution P19 ("No Unbounded
39
- Loops", incl. Justin's Eternal Sentinel caveat — this loop is its namesake
40
- case). Tests: 10 green in `tests/unit/SlowRetrySentinelEscalation.test.ts`
41
- including the P19 sustained-failure bound (week-long never-recovering episode →
42
- exactly 1 escalation) and source-shape wiring pins. Independent adversarial
43
- second-pass: CONCUR (all probes safe — per-tick reachability, no double-fire,
44
- server-independent delivery, emit-before-spawn ordering). tsc clean.