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.
- package/dist/cli.js +23 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +5 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/core/HttpLeaseTransport.d.ts +24 -0
- package/dist/core/HttpLeaseTransport.d.ts.map +1 -1
- package/dist/core/HttpLeaseTransport.js +34 -4
- package/dist/core/HttpLeaseTransport.js.map +1 -1
- package/dist/core/PeerFailureLogGate.d.ts +44 -0
- package/dist/core/PeerFailureLogGate.d.ts.map +1 -0
- package/dist/core/PeerFailureLogGate.js +60 -0
- package/dist/core/PeerFailureLogGate.js.map +1 -0
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +34 -34
- package/upgrades/1.3.330.md +98 -0
- package/upgrades/side-effects/cli-unknown-command-trap.md +31 -0
- package/upgrades/side-effects/mesh-pull-brakes.md +63 -0
- package/upgrades/1.3.328.md +0 -44
|
@@ -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.
|
package/upgrades/1.3.328.md
DELETED
|
@@ -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.
|