instar 1.3.736 → 1.3.737

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 (58) hide show
  1. package/dist/commands/devPreflight.d.ts +21 -0
  2. package/dist/commands/devPreflight.d.ts.map +1 -1
  3. package/dist/commands/devPreflight.js +36 -2
  4. package/dist/commands/devPreflight.js.map +1 -1
  5. package/dist/commands/server.d.ts.map +1 -1
  6. package/dist/commands/server.js +30 -0
  7. package/dist/commands/server.js.map +1 -1
  8. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  9. package/dist/config/ConfigDefaults.js +13 -0
  10. package/dist/config/ConfigDefaults.js.map +1 -1
  11. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  12. package/dist/core/PostUpdateMigrator.js +12 -0
  13. package/dist/core/PostUpdateMigrator.js.map +1 -1
  14. package/dist/core/SessionManager.d.ts.map +1 -1
  15. package/dist/core/SessionManager.js +12 -0
  16. package/dist/core/SessionManager.js.map +1 -1
  17. package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
  18. package/dist/core/WriteDomainRegistry.js +22 -0
  19. package/dist/core/WriteDomainRegistry.js.map +1 -1
  20. package/dist/core/hostSemaphoreCore.d.ts +115 -0
  21. package/dist/core/hostSemaphoreCore.d.ts.map +1 -0
  22. package/dist/core/hostSemaphoreCore.js +215 -0
  23. package/dist/core/hostSemaphoreCore.js.map +1 -0
  24. package/dist/core/hostSpawnSemaphore.d.ts +8 -9
  25. package/dist/core/hostSpawnSemaphore.d.ts.map +1 -1
  26. package/dist/core/hostSpawnSemaphore.js +48 -126
  27. package/dist/core/hostSpawnSemaphore.js.map +1 -1
  28. package/dist/core/hostTestRunnerSemaphore.d.ts +492 -0
  29. package/dist/core/hostTestRunnerSemaphore.d.ts.map +1 -0
  30. package/dist/core/hostTestRunnerSemaphore.js +1648 -0
  31. package/dist/core/hostTestRunnerSemaphore.js.map +1 -0
  32. package/dist/core/testRunnerRunClassifier.d.ts +128 -0
  33. package/dist/core/testRunnerRunClassifier.d.ts.map +1 -0
  34. package/dist/core/testRunnerRunClassifier.js +382 -0
  35. package/dist/core/testRunnerRunClassifier.js.map +1 -0
  36. package/dist/core/types.d.ts +21 -0
  37. package/dist/core/types.d.ts.map +1 -1
  38. package/dist/core/types.js.map +1 -1
  39. package/dist/monitoring/guardManifest.d.ts.map +1 -1
  40. package/dist/monitoring/guardManifest.js +26 -0
  41. package/dist/monitoring/guardManifest.js.map +1 -1
  42. package/dist/scaffold/templates.d.ts.map +1 -1
  43. package/dist/scaffold/templates.js +7 -0
  44. package/dist/scaffold/templates.js.map +1 -1
  45. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  46. package/dist/server/CapabilityIndex.js +9 -0
  47. package/dist/server/CapabilityIndex.js.map +1 -1
  48. package/dist/server/routes.d.ts.map +1 -1
  49. package/dist/server/routes.js +150 -0
  50. package/dist/server/routes.js.map +1 -1
  51. package/package.json +1 -1
  52. package/scripts/lib/test-runner-selfdisable-patterns.mjs +326 -0
  53. package/scripts/pre-push-test-runner-selfdisable.mjs +81 -0
  54. package/src/data/builtin-manifest.json +64 -64
  55. package/src/scaffold/templates.ts +7 -0
  56. package/upgrades/1.3.737.md +140 -0
  57. package/upgrades/side-effects/test-runner-concurrency-bound.md +173 -0
  58. package/upgrades/1.3.736.md +0 -57
@@ -0,0 +1,140 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: minor -->
5
+
6
+ ## What Changed
7
+
8
+ The structural fix for the 29-concurrent-test-suites meltdown class (2026-06-19,
9
+ 2026-07-02): every vitest run on a machine now passes a host-wide two-lane
10
+ semaphore before its worker pool fans out. Full suites take a **suite-lane**
11
+ slot (cap 1 — the operator-ratified "never run builders' full suites at once"
12
+ rule); small inner-loop runs (≤5 matched test files, pool ≤4 workers, no
13
+ pool-shaping flags) take a roomier **targeted-lane** slot (cap 6), so
14
+ day-to-day iteration never queues behind a big suite. Per
15
+ `docs/specs/test-runner-concurrency-bound.md` (converged round 10, approved):
16
+
17
+ - **Ships watch-only (dry-run).** Full bookkeeping, ZERO enforcement: a run
18
+ that would block logs `would-block` to a durable ledger and admits. Real
19
+ blocking exists only behind a deliberate host tuning-file flip
20
+ (`~/.instar/host-test-runner-tuning.json`) gated on a 14-day soak review.
21
+ Nothing changes about test execution at ship.
22
+ - **Fail-open everywhere it matters** (the deliberate inversion of the spawn
23
+ cap's fail-closed default): corrupt state, unknown df, wedged lock — the
24
+ run ADMITS, loudly, with a witness record. A false BLOCK would wedge every
25
+ push and build on the machine; a false admit is one extra suite for a few
26
+ minutes. The single exception: a provable fail-open STORM (8 live O_EXCL
27
+ witness slots) refuses further admits — at that point over-admission IS the
28
+ meltdown.
29
+ - **Capacity-reclaim only by default.** A hung suite past its TTL loses its
30
+ SLOT (capacity restored, `stale-holder-reclaimed` ledgered) — no process is
31
+ ever signaled. Process-killing is a separate opt-in arm
32
+ (`ttlSignal`, tuning-file-armable ONLY — env can only disarm), off through
33
+ the whole soak, quadruple-gated (pid sanity, identity corroboration via
34
+ process start-time, group-leadership, durable tombstone escalation) plus a
35
+ sleep-wake re-arm so a laptop resume is never mistaken for a hang.
36
+ - **The chokepoint rides the vitest configs** (globalSetup + config-eval seam
37
+ in all five configs), so every invocation path — package scripts, husky
38
+ pre-push, editor integrations, ad-hoc `npx vitest` — is bounded with zero
39
+ script rewiring. Nested test runs never deadlock (lane-scoped
40
+ ancestry+holders skip) and are unconditionally clamped ≤4 workers,
41
+ CLI-proof (pool-shaping argv neutralized at config-eval).
42
+ - **Observability**: `GET /test-runner-limiter` (pure read — per-lane
43
+ saturation, live holders, recent ledger events, skip histogram) +
44
+ `POST /test-runner-limiter/prune` (the recovery lever that replaces
45
+ hand-editing JSON — single-flight, rate-limited, enumerates what it
46
+ reclaimed). CapabilityIndex entry, `/guards` grading (a sustained
47
+ self-disable pattern grades `diverged`), CLAUDE.md awareness for new AND
48
+ existing agents (templates + PostUpdateMigrator). Serverless builder hosts
49
+ get loud stderr WARNs at the chokepoint plus WARN-only ledger-pattern
50
+ checks in `dev:preflight` and pre-push (structurally incapable of blocking
51
+ a push).
52
+ - **Kill switch**: env-only `INSTAR_HOST_TEST_SEMAPHORE=off` — immediate,
53
+ ledger-visible, never silent. Config `intelligence.testRunnerCap` tunes the
54
+ route report only; it is deliberately NOT a chokepoint lever.
55
+
56
+ Enforcement machinery for three operator-ratified constitutional standards, per
57
+ `docs/specs/three-standards-enforcement.md` (converged). This ships the STRUCTURE that makes
58
+ each standard stick — the standard TEXTS ship separately under the already-granted ratification.
59
+
60
+ - **Standard A — reject an undefended machine-local.** The `/spec-converge` integration reviewer
61
+ is upgraded from "a cross-machine posture is *declared*" to "the default posture is `unified`;
62
+ an undefended `machine-local` is a MATERIAL FINDING." A machine-local surface is now allowed
63
+ only with a `machine-local-justification: <key>` marker drawn from a CLOSED taxonomy
64
+ (`physical-credential-locality` / `hardware-bound-resource` / `operator-ratified-exception`),
65
+ and the check is bidirectional (an infeasible `unified` is equally a finding). The marker is the
66
+ cheap deterministic signal; the LLM reviewer holds the semantic authority (Signal vs. Authority).
67
+ - **Standard B — self-heal before notify.** A new `/spec-converge` review-check that flags any
68
+ monitor/watcher/recurring-notice-source whose operator-facing escalation is reachable on first
69
+ detection. A watcher must declare its self-heal step, its `remediation-actions`, its P19 brakes
70
+ (including flapping detection and a `max-notification-latency`), and a contested severity class.
71
+ - **Standard C — alerts-topic routing default.** A table-driven routing CONTRACT test at the
72
+ Telegram adapter boundary proving a topic-less non-critical notice routes to the one hub topic by
73
+ default, HIGH/URGENT keep their own topic, an existing-owning-topic send mints no new topic, and
74
+ an unresolvable hub falls back safely — plus a guard that the hub id is never a baked-in constant.
75
+ - **Migration Parity.** `migrateThreeStandardsReviewChecks` re-copies the two upgraded spec-converge
76
+ skill files to already-installed agents, mirroring the existing multi-machine-posture migration.
77
+
78
+ Honest framing: the purely-deterministic marker/field lints for A and B are hard-sequenced to land
79
+ WITH each standard's registry guard, not in this change; until then A/B enforcement is the per-spec
80
+ conformance-check gate plus the LLM review-lens (a semantic audit), never a no-LLM guarantee.
81
+
82
+ ## What to Tell Your User
83
+
84
+ 🧪 Preview (watch-only): your machine now has a "deli counter" for test runs —
85
+ full test suites take a ticket and run one at a time per machine, while small
86
+ quick test runs get their own express lane. This is the fix for the meltdowns
87
+ where dozens of test suites piled up at once and starved everything else. For
88
+ the first two weeks it only WATCHES and keeps a log of what it would have
89
+ done — it blocks nothing and kills nothing until the log proves it's safe and
90
+ the operator deliberately flips it on. If a test run ever seems to wait, ask
91
+ me "why is my test run waiting?" and I'll read the limiter's live state.
92
+
93
+ Nothing changes in how you talk to me day to day. This is internal development plumbing: when I
94
+ design a new capability, my own spec-review now pushes back harder on two mistakes it used to let
95
+ slide — quietly assuming a feature only ever runs on one of your machines, and building a watchdog
96
+ that pings you the instant it sees a problem instead of trying to fix it first and telling you only
97
+ when it genuinely cannot. It also adds a test proving stray housekeeping notices land in your one
98
+ alerts topic instead of spawning a new topic each time. You do not need to do anything.
99
+
100
+ ## Summary of New Capabilities
101
+
102
+ - Ask "why is my test run waiting?" / "is the test lane saturated?" →
103
+ `GET /test-runner-limiter` (per-lane availability, live holders with ages,
104
+ recent decisions).
105
+ - Ask "clear the stuck test slot" → `POST /test-runner-limiter/prune` — the
106
+ safe recovery lever (frees dead/expired holders; never kills processes by
107
+ default).
108
+ - A rejected `git push` on a busy machine may be lane contention, not red
109
+ tests — the failure message names the holders and the levers, and I check
110
+ the limiter before assuming test failures.
111
+
112
+ No new user-facing capability. Internal enforcement only: stronger spec-review checks for
113
+ cross-machine posture and self-heal-before-notify, an idempotent migration so existing agents
114
+ receive the upgraded checks, and a routing contract test for topic-less notices.
115
+
116
+ ## Evidence
117
+
118
+ - Spec convergence: 10 review rounds, 6 internal reviewers + two external
119
+ model families (codex GPT-tier, gemini) every round; final round zero
120
+ must-fix findings (`docs/specs/reports/test-runner-concurrency-bound-convergence.md`).
121
+ - All three test tiers plus meta-verification ship in this PR: unit
122
+ (semaphore matrix, classifier, core-extraction golden tests pinning the
123
+ spawn lane byte-identical), integration (route through the real pipeline),
124
+ e2e (route alive through real AgentServer plumbing), and meta-verification
125
+ with REAL spawned vitest roots — K=3 simultaneous roots at cap 1 admit
126
+ at-most-one concurrently; acquire-before-fanout proven on the pinned vitest
127
+ version; a nested child with `--maxWorkers=32` measured at ≤4 actual
128
+ workers; the wait-line frame signature validated against the live
129
+ silence/load-stall sentinel predicates so a waiting run never reads as a
130
+ hang.
131
+ - Side-effects review + independent second-pass:
132
+ `upgrades/side-effects/test-runner-concurrency-bound.md`.
133
+
134
+ - `npx vitest run tests/unit/PostUpdateMigrator-threeStandardsReviewChecks.test.ts` — 7 passed
135
+ (content-presence of the A/B review-lenses in the shipped prompts + migration idempotency and
136
+ fingerprint-guard).
137
+ - `npx vitest run tests/integration/notification-flood-burst-invariant.test.ts` — 12 passed
138
+ (5 new Standard C routing-contract cases + 7 existing burst-bound cases).
139
+ - `npx tsc --noEmit` — clean.
140
+ - Side-effects review: `upgrades/side-effects/three-standards-enforcement.md`.
@@ -0,0 +1,173 @@
1
+ # Side-Effects Review — Test-Runner Concurrency Bound (host-wide vitest semaphore)
2
+
3
+ **Version / slug:** `test-runner-concurrency-bound`
4
+ **Date:** `2026-07-03`
5
+ **Author:** `echo (instar-dev agent)`
6
+ **Second-pass reviewer:** `required (concurrency gate touching git-push and /build paths) — see below`
7
+
8
+ ## Summary of the change
9
+
10
+ Adds a host-wide, per-machine concurrency bound on vitest test runs — the structural fix for the 2026-06-19/2026-07-02 meltdowns where ~29 concurrent test suites (agent builds + husky pre-push + build gates) starved the event loop and made health watchdogs kill healthy servers. A parameterized semaphore core is extracted from the proven `hostSpawnSemaphore` (`src/core/hostSemaphoreCore.ts`); a new test lane (`src/core/hostTestRunnerSemaphore.ts`) implements two symmetric slot lanes (suite cap 1 — operator-ratified — and targeted cap 6 for ≤5-file inner-loop runs), a capacity-only ReclaimPolicy (TTL frees slots, never signals by default), a durable event ledger, and a host-uniform tuning file. The chokepoint is a vitest `globalSetup` + config-eval seam (`tests/setup/test-runner-semaphore.globalSetup.ts`, `tests/setup/test-runner-bound.config-eval.ts`) wired into all five vitest configs. **Ship posture is dry-run (watch-only): full bookkeeping, zero enforcement — a run that would block logs `would-block` and admits.** Enforcement exists only behind a deliberate tuning-file flip after a 14-day soak. Server-side: `GET /test-runner-limiter` (pure read) + `POST /test-runner-limiter/prune` (recovery lever), CapabilityIndex entry, `intelligence.testRunnerCap` config knob (route report only — NOT a chokepoint lever), CLAUDE.md awareness via templates + PostUpdateMigrator. Serverless-host surfacing: loud stderr WARNs at the chokepoint + WARN-only ledger-pattern checks in `dev:preflight`/pre-push.
11
+
12
+ Spec: `docs/specs/test-runner-concurrency-bound.md` (review-convergence round 10, operator-approved 2026-07-03). ELI16: `docs/specs/test-runner-concurrency-bound.eli16.md`.
13
+
14
+ ## Decision-point inventory
15
+
16
+ - `tests/setup/test-runner-semaphore.globalSetup.ts` (chokepoint admit/wait/skip) — **add** — decides admit-now / wait / (enforcing-only) typed capacity refusal for every vitest root on the host.
17
+ - `tests/setup/test-runner-bound.config-eval.ts` (lane classification + clamps) — **add** — classifies targeted vs suite; applies worker-pool clamps ONLY when clamp-active/enforcing; dry-run ledgers `would-clamp` and reshapes nothing.
18
+ - `src/core/hostTestRunnerSemaphore.ts` ReclaimPolicy — **add** — frees slots (capacity direction); the opt-in `TTL_SIGNAL` arm (off by default, off through the soak, tuning-file-armable only) is the sole path that can signal a process, behind four mandatory gates + sleep-wake re-arm.
19
+ - `src/core/hostSpawnSemaphore.ts` — **pass-through** — refactored to ride the extracted core; public exports and on-disk byte format pinned unchanged by golden tests.
20
+ - `GET /test-runner-limiter` — **add** — pure read, no blocking authority.
21
+ - `POST /test-runner-limiter/prune` — **add** — forced reclaim pass (same policy as the acquire path; single-flight, rate-limited).
22
+ - `dev:preflight` / pre-push self-disable pattern check — **add** — WARN-only by structure on pre-push (exit 0 unconditionally); advisory on preflight.
23
+
24
+ ---
25
+
26
+ ## 1. Over-block
27
+
28
+ **What legitimate inputs does this change reject that it shouldn't?**
29
+
30
+ At ship posture: **nothing can be blocked** — dry-run admits every run (would-blocks are ledger entries only). The clamps likewise reshape nothing in dry-run (`would-clamp` is logged; the real pool is untouched). The over-block analysis therefore applies to the post-soak enforcing flip, and the design's §1.1 fail-direction inversion exists precisely for it:
31
+
32
+ - A legitimate suite queued behind a hung holder: bounded by TTL capacity-reclaim (default 1h, row-stamped, sanity-ranged [5min, 4h]) + the typed capacity-timeout that names the holder pids/ages and the levers. Worst legitimate wait = wait budget (2 min background / 10 min interactive-class default).
33
+ - A legitimate targeted run mis-classified suite-class (multi-match positional, pool flags): deliberate safe-superset — it queues on the suite lane rather than being rejected; in the worst case it waits, never errors below the budget.
34
+ - A wedged lock: fail-OPEN admit (with witness), never a block — up to the 8-slot storm ceiling where the fail direction deliberately inverts (8+ concurrent open admissions IS the meltdown; refusing the 9th is the lesser harm).
35
+ - Enforcement cannot be armed per-process by a stray env: `INSTAR_HOST_TEST_ENFORCE=1` against a dry-run authority is honored but LOUD (bidirectional posture-divergence WARN), and the kill-arm (`TTL_SIGNAL=1`) is structurally ignorable from env (arm is tuning-file-only).
36
+
37
+ Residual over-block risk accepted and mitigated: pre-push waits at cap=1 behind a genuinely long suite. Mitigation: interactive/push class gets the 5× wait budget, CLAUDE.md guidance ("a rejected push may be contention, not red tests"), and starvation is a named soak metric with a pre-authorized post-soak refinement (waiter tickets / priority admission).
38
+
39
+ ---
40
+
41
+ ## 2. Under-block
42
+
43
+ **What failure modes does this still miss?**
44
+
45
+ - **Old-branch worktrees** predating the chokepoint run unbounded until rebased (spec "coverage honesty": tracked as UNCOVERED in the soak, closed by normal rebase cadence).
46
+ - **Ad-hoc `npx vitest --config <arbitrary>`** at a shell bypasses the guarded configs (documented residual; the repo lint guards `package.json` scripts, and the config-riding chokepoint covers editor/ad-hoc invocations that load the five real configs).
47
+ - **Nested-child aggregate count**: each nested child is clamped ≤4 workers CLI-proof, but the COUNT of nested children is bounded by test authorship, not a hard cap (spec-stated assumption; per-child attribution via `nested-skip` ledger events makes a pathological spawner loud).
48
+ - **A same-user adversary** who forges the tuning file AND the baseline marker AND rewrites the ledger tail defeats the mutation-visibility mechanism — stated threat floor for every same-user-file surface in this design, not a gap this change created.
49
+ - **Watch mode** holds no slot (deliberate — a lingering watcher holding the cap-1 lane would be a standing false-BLOCK); an agent-context watch is loud + soak-metered + `diverged`-graded, never quiet.
50
+
51
+ All are stated in the spec (§2.6, §2.9, §3, §6) — no silent misses identified beyond the spec's own honesty list.
52
+
53
+ ---
54
+
55
+ ## 3. Level-of-abstraction fit
56
+
57
+ **Is this at the right layer?**
58
+
59
+ Yes. The bound must exist where test processes are BORN — inside the vitest config load path — because the incident population is bare worktree checkouts with no agent server running; any server-side gate would be silent exactly there. The primitive reuses the proven `hostSpawnSemaphore` mechanics via a parameterized core rather than re-implementing (lower layer reused, not duplicated) — and deliberately does NOT reuse the spawn lane's semantics where the workload differs (fail-direction inversion, no busy-spin, capacity-only reclaim, age-only lock reclaim: each a reviewed divergence, §2.1/§2.4). The spawn cap's own defects found during extraction (non-atomic lock reclaim, torn lock read, non-df-gated reclaim) are quarantined (test lane doesn't ride them) and tracked as a §4 back-port — not silently inherited, not silently "fixed" in a way that would change spawn behavior in this PR (golden tests pin it).
60
+
61
+ The route feeds existing surfaces (`/guards` grading, CapabilityIndex) instead of inventing parallel observability.
62
+
63
+ ---
64
+
65
+ ## 4. Signal vs authority compliance
66
+
67
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
68
+
69
+ **Does this change hold blocking authority with brittle logic?**
70
+
71
+ - [x] No — at ship posture this change produces signals only (would-block/would-clamp/skip ledger events + loud WARNs). Blocking authority exists but is dormant behind an operator-deliberate tuning-file flip gated on 14 days of soak evidence.
72
+ - [x] Yes-qualified for the post-flip state — and the authority is a **deterministic policy evaluator in a genuinely enumerable domain** (the signal-vs-authority carve-out): process admission by counting slots. This is the same class as the existing fork-bomb spawn cap. Every brittle sub-check inside it is deprived of dangerous authority by construction: classifier uncertainty routes to the STRICTER lane (never fail-open to the roomy lane); reclaim uncertainty fails OPEN (admit); corrupt state fails OPEN + quarantines; only the provable-storm ceiling (8 live O_EXCL witnesses) refuses — the one point where over-admission IS the harm being prevented.
73
+ - The kill path (the only process-harming authority) is quadruple-gated (pid sanity, identity corroboration, group leadership, durable tombstone escalation + sleep-wake re-arm), off by default, un-armable from env, and never armed during the soak.
74
+
75
+ No brittle detector holds block authority. The 2026-04-15 four-layers-of-filters anti-pattern does not recur here: there is exactly ONE admission authority per lane, fed by detectors (classifier, df probe, pid evidence) that individually only produce signals or route toward safety.
76
+
77
+ ---
78
+
79
+ ## 5. Interactions
80
+
81
+ - **Shadowing:** the chokepoint runs BEFORE the integration config's `build-dist.globalSetup.ts` (prepended; teardown reverse order) — the dist build is deliberately inside the held slot (spec-stated, acceptable at cap 1). It does not shadow any existing gate: no other admission control exists on test runs today. The husky pre-push suite ride-through was verified: pre-push invokes the guarded configs, so pushes queue rather than stack.
82
+ - **Double-fire:** the spawn cap and the test cap are separate pools (separate holders/lock files, separate caps) — a `claude -p` spawn and a vitest root never contend for the same slot; no event is double-governed. Within the test lane, the process-global lane-scoped flag prevents one process double-acquiring via multiple globalSetups (workspace/aggregate runs), and the §5 guard-test fails on any `vitest.workspace.*` introduction until reviewed.
83
+ - **Races:** the load-bearing ones are addressed in-design and tested: atomic O_EXCL lock + race-safe age-reclaim with dev+ino verification (two concurrent reclaimers → one winner, no lost holders row); O_EXCL storm slots (no count-then-act); atomic temp+rename on every holders/tuning/marker write; torn tuning read → code-defaults without quarantine until a confirming re-read; `ps` evidence gathered outside the lock. The globalSetup ↔ config-eval seam pair cannot disagree in the dangerous direction because routing verifies live resolved state (pool bound ≤ 4), never a stamp.
84
+ - **Feedback loops:** the wait itself was the found feedback risk — a waiting run reading as a hang would re-trigger the sentinel kills this spec exists to stop (§2.10). Closed by making the once-a-minute wait line carry the sentinels' recognized active-work indicator (braille spinner + "active work, not a hang"), validated against the LIVE sentinel predicate by a dedicated test (ship gate). The ledger feeds `/guards` grading; a `diverged` grade raises an attention item but never feeds back into admission.
85
+
86
+ ---
87
+
88
+ ## 6. External surfaces
89
+
90
+ - **Other agents on the same machine:** yes — that is the point. The bound is host-wide across every checkout that contains the chokepoint (per-user `~/.instar` rendezvous). Cross-actor uniformity is a FILE (tuning), not env exports; divergent-env actors are loud (bidirectional posture WARN, >4× cap WARN) and visible in per-run ledger stamps. Mixed-version hosts are safe by the versioned+tolerant schema rule (unknown fields preserved; unknown states excluded from the count, loud, bounded).
91
+ - **Other users of the install base:** the chokepoint files reach builder/worktree hosts via git (repo source); the route/config/CLAUDE.md awareness ship to deployed agents via the update path (Migration Parity: types + ConfigDefaults + templates.ts + PostUpdateMigrator.migrateClaudeMd — the spec's audience split). Dry-run default means zero behavior change for every population at ship.
92
+ - **External systems:** none (no Telegram/GitHub/Cloudflare surface). No dashboard render added in this PR; route fields are charset/length-clamped at projection since holder rows are same-user-writable (untrusted) data.
93
+ - **Persistent state:** all new state is machine-local under `~/.instar` at frozen paths, `0600`/`0700`, size-bounded (ledger rotation ~5MB + retained segments; keep-newest-5 quarantines; poison ceiling 64 rows), and inert-safe-to-delete after revert (§6 rollback list).
94
+ - **Timing/runtime conditions:** laptop sleep-wake (TTL re-arm gate + named residual in the arm announcement), pid reuse (start-time corroboration), clock skew (bounded early/late slot-free, no signal), vitest version drift (classifier + fanout invariants proven per pinned version by ship-gate tests; drift caught by tests, not production).
95
+ - **Operator surface (Mobile-Complete):** no operator-facing ACTION ships in this change — the enforce flip is deliberately a maintainer action on a dev host (tuning file via CLI/route on the machine), not an end-user operator action; the soak-review decision is likewise maintainer-side. No PIN-gated approval flow is added. (If the flip is ever productized for operators, it needs a dashboard surface then.)
96
+
97
+ ## 6b. Operator-surface quality
98
+
99
+ No operator surface — not applicable (no dashboard renderer/markup, approval page, or grant/revoke/secret form is touched; the route is agent/maintainer-facing JSON).
100
+
101
+ ---
102
+
103
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
104
+
105
+ **Machine-local BY DESIGN, with the reason:** a test-runner concurrency bound governs the CPU/processes of ONE machine — the resource being protected is physically per-host, so the holders/lock/ledger/tuning files must be per-machine truths (a cross-machine bound would be wrong: capacity on the Mini says nothing about the laptop). The design actively DEFENDS machine-locality: `df -P` host-local determination with marker revalidation, foreign-hostname holder drop + loud synced-home signal — because the one way this state could leak across machines (a synced `~/.instar`) is a misconfiguration the spec detects and refuses to act on rather than obeys.
106
+
107
+ - User-facing notices: none (stderr WARNs to the invoking terminal + ledger only; server-side grading rides the existing `/guards` attention path with its own dedup — no new notice channel, no one-voice concern).
108
+ - Durable state: per-machine by design; nothing strands on topic transfer (state is keyed to the host, not to a conversation).
109
+ - URLs: none generated.
110
+ - The pool-wide question ("is any machine's test lane saturated?") is answerable later via the existing `?scope=pool` pattern on the route — additive, not required for correctness, deliberately not in this PR (the bound's correctness never depends on a peer).
111
+
112
+ ---
113
+
114
+ ## 8. Rollback cost
115
+
116
+ - **Chokepoint (incident population):** env kill switch `INSTAR_HOST_TEST_SEMAPHORE=off` — immediate, no release, ledger-visible (never silent). Posture rollback from a bad enforce flip: clear `enforcing` in the tuning file (or `INSTAR_HOST_TEST_ENFORCE=0` per-process) — back to dry-run, not off.
117
+ - **Full rollback:** revert the PR. All on-disk artifacts are inert and enumerated in spec §6 (holders, lock, witness dir, df marker, tuning file, baseline marker, ledger + segments, quarantines) — safe to delete, no migration needed. Stale env exports become no-ops.
118
+ - **Agent state repair:** none — deployed agents received only a read route + a config knob + CLAUDE.md text; removal regresses nothing they depend on.
119
+ - **User visibility during rollback:** none at ship posture (dry-run changes no behavior); post-flip, rollback of a misbehaving enforce is one tuning-file field on the affected host.
120
+
121
+ ---
122
+
123
+ ## Conclusion
124
+
125
+ The review confirms the design holds its two load-bearing inversions everywhere: (1) fail-OPEN on every uncertainty because a false BLOCK (wedged pushes/builds host-wide) is the catastrophic direction — with the single deliberate, provable-storm exception at the 8-slot ceiling; and (2) ship-dark-as-dry-run so the soak itself is the safety net before any blocking exists.
126
+
127
+ **One named deviation from spec §2.9 text:** "an armed ttlSignal and any above-code-default cap as distinct `/guards` rows" is implemented as route-level fields (`ttlSignalArmed` frozen top-level + `capAboveDefault` additive detail) plus ONE load-bearing guard row for the limiter itself (soaking classification, `off-runtime-divergent` on kill-switch, loud gap on lapsed soak) — NOT as separate guard rows per lever. Reason: the GuardRow model is a closed boolean-posture allowlist where a `defaultEnabled:false` code-default row can never grade "on"; a per-lever row would LIE as "off" while armed — the exact dishonest-surface class the guards inventory exists to prevent. The spec's intent (armed ttlSignal and cap inflation are VISIBLE where a server exists) is met on the route + the chokepoint's own loud WARNs + ledger stamps; the letter (distinct rows) is deliberately not, and this is flagged for the second-pass reviewer to affirm or overturn. The kill machinery is structurally disarmed (env cannot arm it; the soak never arms it; four gates + sleep-wake honesty when armed). The riskiest residuals are all named in the spec rather than discovered here: pre-push starvation at cap=1 (soak-metered, pre-authorized refinement), nested-child count (authorship-bounded, loud), ad-hoc `--config` bypass (documented). Second-pass review is REQUIRED (this is a gate touching session-lifecycle-adjacent paths: git push, /build verification) and follows below. Ship gates before commit: the acquire-before-fanout instrumentation test and the §2.10 sentinel-predicate frame validation must pass (or the wrapper fallback / known-blocked registration must be wired per spec).
128
+
129
+ ---
130
+
131
+ ## Second-pass review (if required)
132
+
133
+ **Reviewer:** second-pass-reviewer (independent subagent)
134
+ **Independent read of the artifact: CONCUR**
135
+
136
+ The artifact's load-bearing claims hold true against the spec (§1.1, §2.4, §2.6, §2.9, §2.11) and against the actual code (`hostTestRunnerSemaphore.ts`, `test-runner-semaphore.globalSetup.ts`, `routes.ts`, `guardManifest.ts`); no ship-blocker found — this change is clear to proceed to trace + commit.
137
+
138
+ - **A — Signal vs authority (compliant).** Verified there is exactly ONE admission authority per lane: `live < cap` in `acquire()` (`hostTestRunnerSemaphore.ts` ~L1575). The classifier (targeted-vs-suite) is a router feeding two symmetric throwing lanes, not a blocker; the `df` probe routes only to fail-open-admit; pid/start-time evidence feeds capacity-reclaim, never a block. The one refusal at ship posture (the storm ceiling) is a deterministic count of live `O_EXCL` witnesses — an enumerable-domain policy evaluator, same class as the fork-bomb spawn cap, firing only when 8 concurrent open admissions IS the meltdown. No brittle detector holds block authority. The 2026-04-15 four-filters anti-pattern does not recur.
139
+ - **B — Fail-direction inversion holds in code.** The `globalSetup` catch block (L287-303) admits on ANY internal error (loud WARN + `fail-open-admit` ledger); it re-throws only the two typed capacity errors. Traced that `TestRunnerCapacityTimeoutError` is unreachable in dry-run (the dry-run branch returns an admit before the deadline-throw), so nothing blocks a run at ship posture except the disclosed storm ceiling. Corrupt/unparseable holders → admit+quarantine; `df`-unknown → admit, reclaim disabled; a per-poll lock miss retries (never admits); provable wedge → race-safe age-reclaim then retake. Confirmed no leaked holder on any throw path (throwing process never wrote its own row; `claimStormSlot` only throws when it holds zero slots).
140
+ - **C — Kill machinery disarmed at ship + un-armable from env.** `signalHungHolder` is called ONLY under `ctx.armed && ctx.posture === 'enforcing'` (double-gated); ship posture is dry-run with no tuning arm, so it is unreachable through the soak. The four gates are real and in order: pid-sanity (L1177), identity corroboration (L1183), group-leadership (L1200), durable tombstone + three completers (L1217-1230), with the sleep-wake re-arm short-circuiting before any signal (L1191). `resolveTtlSignal` makes env asymmetric: `=1` against an unarmed authority resolves `armed:false, envArmIgnored:true` + loud WARN — env can only DISARM. The DEFAULT TTL path is capacity-reclaim-only (`stale-holder-reclaimed`, no signal).
141
+ - **D — Over/under-block honesty (complete, with one disclosed nuance).** The §1/§2 lists are complete; the storm-ceiling refusal CAN fire in dry-run (it is posture-independent), a slight tension with the §1 headline "nothing can be blocked at ship posture," but it is explicitly named two bullets down in §1 and in the Conclusion, and is reachable only under a genuine sustained lock wedge (age-reclaim clears normal contention first) — honestly disclosed, not a hidden over-block. Adversarial checks for an unnamed under-block (multi-OS-user host, tuning-cap raise within `[1,ceiling]`, dry-run holders accumulation vs the 64 poison ceiling) all resolve to states the spec's own honesty list (§3/§2.9/§2.11) already names or to intended authority paths surfaced via `tuning-changed` + `capAboveDefault`.
142
+ - **E — §2.9 "distinct /guards rows" deviation: AFFIRM (do not overturn).** Verified `guardManifest.ts` L779-804 registers exactly ONE load-bearing row (`intelligence.testRunnerCap`, `loadBearing:true`, `soakWindowDays:14`, `defaultEnabled:true`) graded by resolved posture — `off-runtime-divergent` on the kill switch, `loadBearingSoaking` during the window, lapsing to a loud gap. A `defaultEnabled:false` per-lever `ttlSignalArmed` row would invert the guards model's semantics (the inventory answers "should this safety system be ON?"; an armed kill-signal being ON is the DANGEROUS state, so it cannot be a guard row without lying as a quiet "off" while armed). The spec's INTENT (armed arm + cap inflation VISIBLE where a server exists) is met on the route — verified `routes.ts` L8509 `ttlSignalArmed` (frozen top-level) + L8519 `capAboveDefault` — plus the chokepoint's loud env-arm-ignored / cap-divergence WARNs and every ledger event's `ttlSignalArmed`/`suiteCap`/`targetedCap`/`tuningHash` stamps. Route fields are the more honest surface than a semantically-inverted guard row; the deviation is sound.
143
+ - **F — Interactions/races.** No dangerous two-seam disagreement: the globalSetup routes to the targeted lane only after independently re-reading the LIVE resolved pool bound (`resolvedPoolBound(ctx.config) <= 4`, L204-207) — a stamp is never trusted, so "unclamped AND targeted-routed" is impossible by construction. Tombstone completion across the three completers is serialized under the holders lock (both `acquire` and `prune` run `applyReclaimPass` inside the lock) and each completer re-corroborates before SIGKILL — no double-kill, no lost obligation (unparseable-corruption salvages/enumerates tombstones). The spawn pool and test pool use separate holders/lock files and caps — no cross-pool double-governance. `GET` route is genuinely pure (verified `status()` does a virtual-prune for display only, no write, no signal); `POST /prune` delegates to `prune()` which gates the kill on the same `armed && enforcing` — the route re-implements no policy.
144
+ - **G — Multi-machine posture: correct.** Machine-local by design is right — the resource protected is one host's CPU, so holders/lock/ledger/tuning must be per-host truths. §7 defends it correctly: `df -P` host-local determination with `statSync().dev` + 24h-TTL marker revalidation (a failed probe is NEVER cached — the §1.2 spawn-lane lesson applied), plus foreign-hostname holder drop on a df-confirmed-local disk (the synced-`~/.instar` misconfiguration is detected and refused, not obeyed). No cross-machine leak missed; the pool-scope question is correctly deferred as additive.
145
+
146
+ ---
147
+
148
+ ## Post-build findings (meta-verification + unit tiers) and their resolution
149
+
150
+ The test build surfaced defects; each was resolved before commit (no deferrals):
151
+
152
+ 1. **`clampConfigPool` min-bound crash (LOAD-BEARING, fixed).** The clamp set `maxWorkers`/`maxForks` to 4 but left `minWorkers`/`minForks` UNSET, so vitest's Tinypool resolved the pool min to `(numCpus − 1)` → on a ≥6-core host min 15 > max 4 → `RangeError`, crashing the root at pool creation whenever the clamp is real (clampActive/enforcing). A §1.1 false-BLOCK. Fixed in `src/core/testRunnerRunClassifier.ts` `clampConfigPool` — min bounds are now set explicitly to `Math.min(existing ?? 1, clampedMax)` (ceiling, never floor). The two ship-gate tests (targeted + nested actual-worker-count ≤4) now pass; verified ≤4 measured actual workers.
153
+ 2. **argv neutralization is a no-op on vitest 2.1.9 (documented; not a hole).** vitest's `cac` parser reads the CLI before the config module loads, so mutating `process.argv` at config-eval cannot reach the already-parsed pool flags. The CLI-proofness is delivered instead by the config `poolOptions.*.{min,max}` hard-set, which outranks a CLI `--maxWorkers` (measured ≤4). The one residual the config clamp can't reach — a CLI `--poolOptions.forks.maxForks=N` same-keying the config — is caught LOUD by the globalSetup `poolOverride` WARN (`nested-skip clamped:false, poolOverride:true`), never silently unbounded, and nested spawns are authorship-bounded per §2.5. The neutralization is kept as a harmless belt (works for wrapper-routed spawns; version-stable). Code comment added.
154
+ 3. **Meta finding "nested-skip `clamped:true` in dry-run" — assessed and DECLINED (no change).** The reviewer read `clamped` as "a reshape physically happened" and flagged it against §2.11. But spec §2.5 DEFINES `clamped` as the guarded-vs-unguarded dimension (`true` = went through the guarded config-eval path; `false` = unguarded config skipped the clamp, + WARN) — NOT the reshape dimension. The dry-run reshape nuance is carried by `clampStash:'dry-run'`, and the §4(e) real-clamp soak count is derived from the separate `would-clamp`/clamp events, not this boolean. Changing it would contradict the converged spec and break the unit test that correctly encodes §2.5; left as spec-specified, with a clarifying code comment.
155
+ 4. **`jitteredPoll` could go negative below a sub-jitter poll seam (fixed, defaults unaffected).** Floored at 0 in `hostTestRunnerSemaphore.ts`. The related enforcing-poll holders-file churn (§2.2 item 3 write-only-on-change) was fixed by the unit tier (`applyReclaimPass` now returns `changed`; callers skip the rewrite when nothing changed; inode-stability tested).
156
+
157
+ Unit-tier module fixes (5, each proven red→green in `hostTestRunnerSemaphore.ts`): poison-ceiling off-by-one (`>` → `>=` at 64 rows); tombstone salvage regex (single-line holders file was un-salvageable); mis-grabbed lock-aside age-sweep (was accumulating forever); write-only-on-change (above); `ps` fingerprint/pgid moved OUTSIDE the holders lock (sub-ms critical section). Four unit-tier spec-vs-code deltas were assessed as acceptable (documented, not defects): ledger appends under the lock are sub-ms best-effort; torn-tuning-read uses the strictly-safer good re-read value; the extraction shares primitives without a single `acquire(admit)` seam (golden test pins spawn behavior unchanged); a stale env marker never CREATES a skip (ancestry+holders is the authority — spec-intended ordering).
158
+
159
+ ## Evidence pointers
160
+
161
+ - Spec + convergence report: `docs/specs/test-runner-concurrency-bound.md`, `docs/specs/reports/test-runner-concurrency-bound-convergence.md` (10 rounds, 6 internal reviewers + codex/gemini externals per round; final round zero must-fix).
162
+ - Test tiers: `tests/unit/host-semaphore-core.test.ts` (golden/extraction), `tests/unit/host-test-runner-semaphore.test.ts` + `tests/unit/test-runner-run-classifier.test.ts` (unit matrix), `tests/integration/test-runner-limiter-route.test.ts`, `tests/e2e/test-runner-limiter-lifecycle.test.ts`, `tests/integration/test-runner-bound-meta.test.ts` (meta-verification incl. both ship gates), `tests/unit/test-runner-config-guard.test.ts`, `tests/unit/test-runner-wait-frame.test.ts`, `tests/unit/test-runner-selfdisable-patterns.test.ts`.
163
+ - _Final test results to be appended at commit time._
164
+
165
+ ## Post-merge CI-fix addendum (2026-07-03)
166
+
167
+ Three CI failures on the feature PR were resolved (no behavior deferred):
168
+
169
+ 1. **`renameAsideVerified` identity check hardened — dev+ino → dev+ino+mtime (LOAD-BEARING, real side effect).** The §2.4 wedged-lock age-reclaim proved "I moved the exact object I lstat'd" using `dev`+`ino` ONLY. A freed inode can be REUSED by the filesystem for a peer's FRESH lock created in the reclaim race (reproduced on CI's filesystem; not local APFS), producing a false identity match — the reclaimer would then treat a live peer's fresh lock as its own reclaimed stale lock (a genuine correctness hole, not just a flaky test). Fixed in `src/core/hostTestRunnerSemaphore.ts` by adding `after.mtimeMs === before.mtimeMs` to the identity conjunction. `rename(2)` preserves mtime, so the happy-path reclaim (same object moved aside) still matches exactly; a swapped-in fresh file — written "now", against a provably-wedged/dead object whose mtime is old — never matches, so the abort-and-leave-aside path fires deterministically on any filesystem regardless of inode reuse. Both callers (`ageReclaimWedgedLock`, `claimStormSlot`) are covered; the storm-slot path is unaffected because a dead claimant's slot mtime still equals the pre-rename lstat on the happy path.
170
+
171
+ 2. **Golden dark-gate attribution map re-generated (test-only).** The new `intelligence.testRunnerCap` ConfigDefaults block (`enabled: true` — NOT an attributed dark-gate `enabled: false` literal) shifted every `monitoring.*` attributed line down by a uniform +13. `tests/unit/lint-dev-agent-dark-gate.test.ts` EXPECTED map updated (24 paths unchanged, all +13); no new attributed dark gate introduced.
172
+
173
+ 3. **CLAUDE.md migrator section tracked (test-only).** `Test-Runner Concurrency Bound` added to `legacyMigratorSections` in `tests/unit/feature-delivery-completeness.test.ts` — same safety-floor, migrator+template, capability-already-in-CapabilityIndex class as its sibling `Fork-Bomb Spawn Cap` (no template-shadow-marker parity required).
@@ -1,57 +0,0 @@
1
- # Upgrade Guide — vNEXT
2
-
3
- <!-- assembled-by: assemble-next-md -->
4
- <!-- bump: patch -->
5
-
6
- ## What Changed
7
-
8
- Enforcement machinery for three operator-ratified constitutional standards, per
9
- `docs/specs/three-standards-enforcement.md` (converged). This ships the STRUCTURE that makes
10
- each standard stick — the standard TEXTS ship separately under the already-granted ratification.
11
-
12
- - **Standard A — reject an undefended machine-local.** The `/spec-converge` integration reviewer
13
- is upgraded from "a cross-machine posture is *declared*" to "the default posture is `unified`;
14
- an undefended `machine-local` is a MATERIAL FINDING." A machine-local surface is now allowed
15
- only with a `machine-local-justification: <key>` marker drawn from a CLOSED taxonomy
16
- (`physical-credential-locality` / `hardware-bound-resource` / `operator-ratified-exception`),
17
- and the check is bidirectional (an infeasible `unified` is equally a finding). The marker is the
18
- cheap deterministic signal; the LLM reviewer holds the semantic authority (Signal vs. Authority).
19
- - **Standard B — self-heal before notify.** A new `/spec-converge` review-check that flags any
20
- monitor/watcher/recurring-notice-source whose operator-facing escalation is reachable on first
21
- detection. A watcher must declare its self-heal step, its `remediation-actions`, its P19 brakes
22
- (including flapping detection and a `max-notification-latency`), and a contested severity class.
23
- - **Standard C — alerts-topic routing default.** A table-driven routing CONTRACT test at the
24
- Telegram adapter boundary proving a topic-less non-critical notice routes to the one hub topic by
25
- default, HIGH/URGENT keep their own topic, an existing-owning-topic send mints no new topic, and
26
- an unresolvable hub falls back safely — plus a guard that the hub id is never a baked-in constant.
27
- - **Migration Parity.** `migrateThreeStandardsReviewChecks` re-copies the two upgraded spec-converge
28
- skill files to already-installed agents, mirroring the existing multi-machine-posture migration.
29
-
30
- Honest framing: the purely-deterministic marker/field lints for A and B are hard-sequenced to land
31
- WITH each standard's registry guard, not in this change; until then A/B enforcement is the per-spec
32
- conformance-check gate plus the LLM review-lens (a semantic audit), never a no-LLM guarantee.
33
-
34
- ## What to Tell Your User
35
-
36
- Nothing changes in how you talk to me day to day. This is internal development plumbing: when I
37
- design a new capability, my own spec-review now pushes back harder on two mistakes it used to let
38
- slide — quietly assuming a feature only ever runs on one of your machines, and building a watchdog
39
- that pings you the instant it sees a problem instead of trying to fix it first and telling you only
40
- when it genuinely cannot. It also adds a test proving stray housekeeping notices land in your one
41
- alerts topic instead of spawning a new topic each time. You do not need to do anything.
42
-
43
- ## Summary of New Capabilities
44
-
45
- No new user-facing capability. Internal enforcement only: stronger spec-review checks for
46
- cross-machine posture and self-heal-before-notify, an idempotent migration so existing agents
47
- receive the upgraded checks, and a routing contract test for topic-less notices.
48
-
49
- ## Evidence
50
-
51
- - `npx vitest run tests/unit/PostUpdateMigrator-threeStandardsReviewChecks.test.ts` — 7 passed
52
- (content-presence of the A/B review-lenses in the shipped prompts + migration idempotency and
53
- fingerprint-guard).
54
- - `npx vitest run tests/integration/notification-flood-burst-invariant.test.ts` — 12 passed
55
- (5 new Standard C routing-contract cases + 7 existing burst-bound cases).
56
- - `npx tsc --noEmit` — clean.
57
- - Side-effects review: `upgrades/side-effects/three-standards-enforcement.md`.