instar 1.3.234 → 1.3.236
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/dashboard/index.html +140 -0
- package/dist/commands/reflect.d.ts.map +1 -1
- package/dist/commands/reflect.js +5 -0
- package/dist/commands/reflect.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +41 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +14 -4
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/CircuitBreakingIntelligenceProvider.d.ts +1 -1
- package/dist/core/CircuitBreakingIntelligenceProvider.d.ts.map +1 -1
- package/dist/core/CircuitBreakingIntelligenceProvider.js +5 -2
- package/dist/core/CircuitBreakingIntelligenceProvider.js.map +1 -1
- package/dist/core/IntelligenceRouter.d.ts +82 -0
- package/dist/core/IntelligenceRouter.d.ts.map +1 -0
- package/dist/core/IntelligenceRouter.js +108 -0
- package/dist/core/IntelligenceRouter.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +40 -2
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts +12 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +30 -0
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/componentCategories.d.ts +37 -0
- package/dist/core/componentCategories.d.ts.map +1 -0
- package/dist/core/componentCategories.js +84 -0
- package/dist/core/componentCategories.js.map +1 -0
- package/dist/core/intelligenceProviderFactory.d.ts +8 -0
- package/dist/core/intelligenceProviderFactory.d.ts.map +1 -1
- package/dist/core/intelligenceProviderFactory.js +3 -3
- package/dist/core/intelligenceProviderFactory.js.map +1 -1
- package/dist/core/types.d.ts +48 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/ResourceLedger.d.ts +62 -0
- package/dist/monitoring/ResourceLedger.d.ts.map +1 -1
- package/dist/monitoring/ResourceLedger.js +152 -2
- package/dist/monitoring/ResourceLedger.js.map +1 -1
- package/dist/monitoring/ResourceSampler.d.ts +63 -0
- package/dist/monitoring/ResourceSampler.d.ts.map +1 -0
- package/dist/monitoring/ResourceSampler.js +277 -0
- package/dist/monitoring/ResourceSampler.js.map +1 -0
- package/dist/monitoring/SessionWatchdog.d.ts.map +1 -1
- package/dist/monitoring/SessionWatchdog.js +5 -0
- package/dist/monitoring/SessionWatchdog.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +10 -4
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +51 -5
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +5 -2
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +2 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +70 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +65 -65
- package/src/scaffold/templates.ts +10 -4
- package/upgrades/1.3.235.md +110 -0
- package/upgrades/1.3.236.md +53 -0
- package/upgrades/side-effects/esm-require-fixes-gate-rearm.md +110 -0
- package/upgrades/side-effects/per-component-framework-routing.md +88 -0
- package/upgrades/side-effects/resource-ledger.md +110 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
**Fixed three latent ESM `require()` bugs and re-armed the two standards-gates that
|
|
9
|
+
should have caught them.** Instar ships as ESM (`"type":"module"`, tsconfig Node16),
|
|
10
|
+
where a bare CommonJS `require(...)` is undefined and throws `ReferenceError` the
|
|
11
|
+
moment that line runs. Three runtime files had one on a live path:
|
|
12
|
+
`reflect.ts` (hit by `instar reflect run`), `SessionWatchdog.ts` (the stuck-session
|
|
13
|
+
watchdog's child-detection), and `PostUpdateMigrator.ts` (Codex-path detection — it
|
|
14
|
+
silently returned null instead of throwing).
|
|
15
|
+
|
|
16
|
+
The fix uses the repo's existing `createRequire(import.meta.url)` idiom for the first
|
|
17
|
+
two (same lazy load, now ESM-legal), and for `PostUpdateMigrator.ts` simply calls the
|
|
18
|
+
`detectCodexPath` it was already statically importing (the lazy require was redundant).
|
|
19
|
+
|
|
20
|
+
These bugs reached `main` because the `esm-compliance` and `no-silent-fallbacks`
|
|
21
|
+
gates — whose entire job is catching this class of issue — had been parked in
|
|
22
|
+
`vitest.push.config.ts`'s flaky-exclude list, so CI stopped running them. This change
|
|
23
|
+
re-arms both (taught `esm-compliance` to recognize the one legitimate ESM use of
|
|
24
|
+
`require` — `createRequire`, needed to re-load a native module after a rebuild) and
|
|
25
|
+
corrects the `no-silent-fallbacks` baseline to the true count.
|
|
26
|
+
|
|
27
|
+
The per-agent **ResourceLedger** gains **Phase B: continuous CPU + memory
|
|
28
|
+
sampling**, closing the gap where Instar tracked token usage well (the
|
|
29
|
+
`TokenLedger`) but tracked *zero* CPU and *zero* memory per agent.
|
|
30
|
+
|
|
31
|
+
A new `ResourceSampler` (mirroring `TokenLedgerPoller`) runs on a cadence and
|
|
32
|
+
samples:
|
|
33
|
+
- the agent's **own server process** — CPU% from a `process.cpuUsage()` delta over
|
|
34
|
+
the tick interval, plus RSS and V8 `heapUsed` from `process.memoryUsage()`;
|
|
35
|
+
- each **spawned session** — by its tmux pane PID, via a single batched
|
|
36
|
+
`ps -o pid=,%cpu=,rss= -p …` call (one child process per tick, dead-PID tolerant);
|
|
37
|
+
- a computed **aggregate** (server + all sessions).
|
|
38
|
+
|
|
39
|
+
Each reading is written to the existing `ResourceLedger` SQLite store (a new
|
|
40
|
+
`resource_samples` table, bounded by a retention prune). Two read-only routes
|
|
41
|
+
expose it — `GET /resources/summary` (current + windowed avg/peak CPU%/RSS per
|
|
42
|
+
source) and `GET /resources/samples` (recent raw samples) — alongside a new
|
|
43
|
+
dashboard **Resource Usage** tab that renders it in plain language.
|
|
44
|
+
|
|
45
|
+
The sampler rides the `developmentAgent` dark-feature gate: it is **live on
|
|
46
|
+
development agents and dark on the fleet** until promoted. Configuration lives
|
|
47
|
+
under `monitoring.resourceLedger` (`sampleIntervalMs`, `idleSampleIntervalMs`,
|
|
48
|
+
`retentionDays`). It is read-only observability — it never gates, throttles, or
|
|
49
|
+
mutates anything, and it fails open (a sampling error never crashes the agent).
|
|
50
|
+
It is also cheap by design: a free CPU-usage delta, one batched `ps` per tick,
|
|
51
|
+
bounded retention, and an idle-cadence backoff when no sessions are running.
|
|
52
|
+
|
|
53
|
+
## What to Tell Your User
|
|
54
|
+
|
|
55
|
+
A few features that could have thrown a runtime error on a rarely-hit path — job
|
|
56
|
+
reflection, the stuck-session watchdog, and Codex detection during updates — are now
|
|
57
|
+
fixed. Two safety checks that had been accidentally switched off in CI are switched
|
|
58
|
+
back on, so this class of bug can't quietly return. Nothing for you to configure.
|
|
59
|
+
|
|
60
|
+
I can now show you how much CPU and memory I'm actually using — my own server
|
|
61
|
+
process and each session I have running — sampled continuously. Just ask "how
|
|
62
|
+
much CPU or memory am I using right now?" and I'll give you the current numbers
|
|
63
|
+
plus the recent peaks, or open the new Resource Usage tab on the dashboard to see
|
|
64
|
+
it laid out: a right-now headline, a breakdown by process, and a recent trend.
|
|
65
|
+
|
|
66
|
+
This is purely a window into resource use — it watches and reports, it never
|
|
67
|
+
changes how I work or slows anything down. On most agents it ships turned off for
|
|
68
|
+
now while it proves itself; if you'd like it switched on, just say so.
|
|
69
|
+
|
|
70
|
+
## Summary of New Capabilities
|
|
71
|
+
|
|
72
|
+
- Three latent ESM require() ReferenceErrors repaired (reflect, SessionWatchdog,
|
|
73
|
+
PostUpdateMigrator) using the established createRequire idiom or an existing static import.
|
|
74
|
+
- The esm-compliance and no-silent-fallbacks CI gates are re-armed (removed from
|
|
75
|
+
FLAKY_TESTS), restoring enforcement of two standards that had silently stopped gating.
|
|
76
|
+
|
|
77
|
+
- See current and recent CPU and memory use per agent, broken down by the server
|
|
78
|
+
and each running session, plus a combined total.
|
|
79
|
+
- New read-only endpoints: GET /resources/summary and GET /resources/samples.
|
|
80
|
+
- New dashboard Resource Usage tab.
|
|
81
|
+
- Tunable cadence and retention; ships live on development agents, dark on the fleet.
|
|
82
|
+
|
|
83
|
+
## Evidence
|
|
84
|
+
|
|
85
|
+
- `tsc --noEmit` clean across the three source edits.
|
|
86
|
+
- Re-armed gates verified green under the REAL CI config:
|
|
87
|
+
`vitest run --config vitest.push.config.ts tests/unit/esm-compliance.test.ts tests/unit/no-silent-fallbacks.test.ts`
|
|
88
|
+
→ 2 files / 7 tests passed.
|
|
89
|
+
- Baseline correction is evidence-backed: the heuristic count at d0fe838 (the
|
|
90
|
+
`[skip ci]` release that set 186) was already 431; at HEAD it is 437; a set-diff of
|
|
91
|
+
the two match-lists shows the +6 are the same catch blocks at shifted line numbers,
|
|
92
|
+
not new silent fallbacks.
|
|
93
|
+
|
|
94
|
+
- New unit tests (`tests/unit/ResourceLedger.test.ts` Phase B block,
|
|
95
|
+
`tests/unit/ResourceSampler.test.ts`): record/summary/recentSamples/prune/count,
|
|
96
|
+
CPU% from a `cpuUsage` delta, batched-PID sampling, dead-PID tolerance, aggregate,
|
|
97
|
+
idle backoff, and fail-open-never-throws — 22 passing.
|
|
98
|
+
- Wiring-integrity (`tests/unit/resource-sampler-wiring.test.ts`): the sampler is
|
|
99
|
+
constructed behind the `developmentAgent` gate, stopped on shutdown, delegates to
|
|
100
|
+
the real ledger (not a no-op), and is read-only / off the hot path — 8 passing.
|
|
101
|
+
- Integration (`tests/integration/resources-summary-routes.test.ts`):
|
|
102
|
+
`/resources/summary` + `/resources/samples` return 401 without a bearer token,
|
|
103
|
+
503 when disabled, and 200 with per-source data; no raw event bodies leak —
|
|
104
|
+
5 passing.
|
|
105
|
+
- E2E (`tests/e2e/resources-summary-lifecycle.test.ts`): on the production
|
|
106
|
+
`AgentServer` boot path the route is alive (200 under `developmentAgent`) and
|
|
107
|
+
503-stubs when the ledger is disabled — 5 passing.
|
|
108
|
+
- `npm run build`, `npm run lint`, `node scripts/docs-coverage.mjs --check`, and the
|
|
109
|
+
parity guards (feature-delivery-completeness, capabilities-discoverability,
|
|
110
|
+
builtin-manifest, ConfigDefaults) all green.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
**You can now run different internal components on different agentic frameworks** —
|
|
9
|
+
for example, keep the agent's conversation on Claude Code but route ALL of its
|
|
10
|
+
sentinels and gates to Codex, so that background LLM chatter stops spending your Claude
|
|
11
|
+
rate-limit budget. A new `IntelligenceRouter` sits at the single LLM funnel and resolves
|
|
12
|
+
each call's framework at call time from the calling component's category (with a
|
|
13
|
+
per-component override), reading config live so changes take effect without a restart.
|
|
14
|
+
|
|
15
|
+
Each framework gets its **own** circuit breaker, so a Claude rate-limit trip no longer
|
|
16
|
+
pauses Codex (the previous global-singleton breaker did). Model "size" is preserved
|
|
17
|
+
automatically — a `fast` check becomes Haiku on Claude or a small GPT model on Codex —
|
|
18
|
+
so nothing at the call sites changes. Fallback is circuit-aware: a missing CLI degrades
|
|
19
|
+
to the default framework and reports it, while a merely rate-limited framework lets the
|
|
20
|
+
component fall back to its own heuristic instead of stampeding the default.
|
|
21
|
+
|
|
22
|
+
This is **opt-in and absent by default** — with no `componentFrameworks` config, every
|
|
23
|
+
component stays on your default framework, byte-identical to before.
|
|
24
|
+
|
|
25
|
+
## What to Tell Your User
|
|
26
|
+
|
|
27
|
+
If you keep hitting rate limits on one provider, you can now spread the load: route your
|
|
28
|
+
background checks (sentinels and gates) to a second framework like Codex while your main
|
|
29
|
+
conversation stays where it is. Turn it on under sessions.componentFrameworks in your
|
|
30
|
+
config; check what's routed where at the intelligence/routing endpoint. With no config,
|
|
31
|
+
nothing changes.
|
|
32
|
+
|
|
33
|
+
## Summary of New Capabilities
|
|
34
|
+
|
|
35
|
+
- `sessions.componentFrameworks` config — route internal components by category
|
|
36
|
+
(sentinel/gate/job/reflector/other) or per-component name to any enabled framework.
|
|
37
|
+
- `GET /intelligence/routing` — read-only: the resolved framework per known component,
|
|
38
|
+
per-framework availability, and how many are routed off the default.
|
|
39
|
+
- Per-framework circuit breakers (true rate-limit isolation between frameworks).
|
|
40
|
+
- Framework-aware model-size passthrough (a routed component keeps its fast/balanced/
|
|
41
|
+
capable size, mapped to the right concrete model for its framework).
|
|
42
|
+
|
|
43
|
+
## Evidence
|
|
44
|
+
|
|
45
|
+
- Converged spec (2-reviewer adversarial + integration pass that corrected the
|
|
46
|
+
breaker-isolation and resolution-point design before any code was written):
|
|
47
|
+
docs/specs/per-component-framework-routing.md.
|
|
48
|
+
- All three test tiers green: 11 unit (tests/unit/intelligence-router.test.ts — resolution
|
|
49
|
+
precedence, per-framework provider-instance isolation, both fallback modes, live-config
|
|
50
|
+
hot-reload, the read surface), 3 integration (200/503), 3 e2e (feature-alive on the real
|
|
51
|
+
AgentServer init path + Bearer-auth + read-only). tsc clean.
|
|
52
|
+
- Unconfigured zero-change is proven by a unit test (no config ⇒ the default provider
|
|
53
|
+
answers every call and no other provider is ever built).
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Side-Effects Review — ESM require() bug fixes + standards-gate re-arm
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `esm-require-fixes-gate-rearm`
|
|
4
|
+
**Date:** `2026-06-03`
|
|
5
|
+
**Author:** `Echo (instar-dev agent)`
|
|
6
|
+
**Tier:** `1` (small / low-risk — bugfix + test/config; no new capability, no block/allow surface)
|
|
7
|
+
**Second-pass reviewer:** `not required`
|
|
8
|
+
|
|
9
|
+
## Summary of the change
|
|
10
|
+
|
|
11
|
+
Fixes three genuine latent ESM bugs and re-arms the two standards-gates that should
|
|
12
|
+
have caught them but were parked in the CI flaky-exclude list.
|
|
13
|
+
|
|
14
|
+
The package is ESM (`"type":"module"`, tsconfig `Node16`), where a bare CommonJS
|
|
15
|
+
`require(...)` is undefined and throws `ReferenceError` at runtime. Three runtime
|
|
16
|
+
files used a bare `require()`:
|
|
17
|
+
- `src/commands/reflect.ts:355` — lazy `require('../core/intelligenceProviderFactory.js')`
|
|
18
|
+
inside `resolveIntelligence()`; `instar reflect run` would throw when it hits this.
|
|
19
|
+
- `src/monitoring/SessionWatchdog.ts:713` — lazy `require('./frameworkProcessSignals.js')`
|
|
20
|
+
during child-process detection; throws on the watchdog's polling path.
|
|
21
|
+
- `src/core/PostUpdateMigrator.ts:1137` — lazy `require('./Config.js')` for
|
|
22
|
+
`detectCodexPath` inside a try/catch (so it silently returned null → codex never
|
|
23
|
+
detected).
|
|
24
|
+
|
|
25
|
+
Fixes:
|
|
26
|
+
- `reflect.ts`, `SessionWatchdog.ts` — bind a real `require` via
|
|
27
|
+
`createRequire(import.meta.url)` (the repo's existing idiom, see
|
|
28
|
+
`NativeModuleHealer.ts:44`). Preserves the deliberate lazy-load semantics exactly,
|
|
29
|
+
but now ESM-legal.
|
|
30
|
+
- `PostUpdateMigrator.ts` — `detectCodexPath` is ALREADY statically imported at the
|
|
31
|
+
top of the module (line 56), so the lazy require was redundant; replaced with a
|
|
32
|
+
direct call to the static import. No shim needed.
|
|
33
|
+
|
|
34
|
+
Gate re-arm + test corrections:
|
|
35
|
+
- `tests/unit/esm-compliance.test.ts` — teach the guard to recognize the legitimate
|
|
36
|
+
`require = createRequire(import.meta.url)` ESM binding (otherwise the naive
|
|
37
|
+
`require(` regex false-positives on `node-abi-mismatch.ts`, which already uses the
|
|
38
|
+
pattern correctly to re-load a native module after an ABI rebuild — which static
|
|
39
|
+
`import` cannot express).
|
|
40
|
+
- `tests/unit/no-silent-fallbacks.test.ts` — baseline corrected 186→437 with
|
|
41
|
+
evidence (the 186 was set by a `[skip ci]` release while the true count was already
|
|
42
|
+
431; the +6 to 437 is line-shift re-counts, zero new fallbacks).
|
|
43
|
+
- `tests/unit/feature-delivery-completeness.test.ts` — track the previously-untracked
|
|
44
|
+
`Token-Burn Alerts` migrator section.
|
|
45
|
+
- `src/data/builtin-manifest.json` — regenerated against current source.
|
|
46
|
+
- `vitest.push.config.ts` — RE-ARM `esm-compliance` + `no-silent-fallbacks` (removed
|
|
47
|
+
from `FLAKY_TESTS`) so these standards-gates run in CI again. They were quarantined,
|
|
48
|
+
which is exactly how the three bare-require bugs reached main unnoticed.
|
|
49
|
+
|
|
50
|
+
## Decision-point inventory
|
|
51
|
+
- `reflect.ts / SessionWatchdog.ts` require binding — **modify (mechanics only)** —
|
|
52
|
+
same lazy load, now via a real ESM `require`. No behavior change beyond "no longer
|
|
53
|
+
throws."
|
|
54
|
+
- `PostUpdateMigrator.ts` — **modify** — use the existing static import; drops a
|
|
55
|
+
silent-null path (codex detection now actually works).
|
|
56
|
+
- Test guards + push config + manifest — **modify/add** — no runtime surface.
|
|
57
|
+
|
|
58
|
+
## 1. Over-block
|
|
59
|
+
No block/allow surface. The two re-armed gates are CI test gates, not runtime gates.
|
|
60
|
+
They cannot block a running agent; they can only fail a PR's CI (their intended job).
|
|
61
|
+
|
|
62
|
+
## 2. Under-block
|
|
63
|
+
The esm-compliance guard previously UNDER-blocked (it was excluded, so it caught
|
|
64
|
+
nothing — three real require() bugs slipped through). Re-arming restores the intended
|
|
65
|
+
coverage. The createRequire recognition is precise (`require\s*=\s*createRequire\s*\(`)
|
|
66
|
+
so it does not newly under-block: a file only earns the skip by establishing a real
|
|
67
|
+
ESM `require` binding.
|
|
68
|
+
|
|
69
|
+
## 3. Level-of-abstraction fit
|
|
70
|
+
Correct. The runtime fixes operate at the module-loading layer (the bug's layer). The
|
|
71
|
+
gate changes operate at the CI-config layer. Neither reaches into business logic.
|
|
72
|
+
|
|
73
|
+
## 4. Signal vs authority compliance
|
|
74
|
+
**Required reference:** docs/signal-vs-authority.md
|
|
75
|
+
- [x] No block/allow runtime surface of its own. The re-armed tests are SIGNALS to CI
|
|
76
|
+
(a PR fails), not runtime authorities. No agent decision is gated by this change.
|
|
77
|
+
|
|
78
|
+
## 5. Interactions
|
|
79
|
+
- **Double-fire / shadowing:** none. The require fixes are byte-equivalent loads.
|
|
80
|
+
- **PostUpdateMigrator:** the static `detectCodexPath` import already existed and is
|
|
81
|
+
used elsewhere; calling it directly cannot introduce a cycle that the static import
|
|
82
|
+
didn't already have.
|
|
83
|
+
- **no-silent-fallbacks baseline:** exact-count ratchet on a hyperactive main may need
|
|
84
|
+
a future bump if a large mechanical sweep lands (as happened 3× before) — that is the
|
|
85
|
+
ratchet working, not a regression. Re-measured fresh before merge.
|
|
86
|
+
- **esm-compliance:** a future dependency-introduced `require()` will now (correctly)
|
|
87
|
+
fail CI — that is the gate doing its job.
|
|
88
|
+
|
|
89
|
+
## 6. External surfaces
|
|
90
|
+
None. No new route, no new persistent state, no config default change. `vitest.push.config.ts`
|
|
91
|
+
is build/test tooling, not shipped agent config.
|
|
92
|
+
|
|
93
|
+
## 7. Rollback cost
|
|
94
|
+
Trivial. Revert the commit: the require fixes go back to throwing (status quo ante),
|
|
95
|
+
and the two gates return to FLAKY_TESTS. No state, no migration, no user-visible change.
|
|
96
|
+
|
|
97
|
+
## Conclusion
|
|
98
|
+
Low-risk Tier-1 bugfix. Three real latent ESM `require()` ReferenceErrors fixed using
|
|
99
|
+
the repo's established `createRequire` idiom (or, for PostUpdateMigrator, the existing
|
|
100
|
+
static import). The standards-gates that exist to catch this class of bug are re-armed
|
|
101
|
+
and verified green under the real CI push config. Clear to ship.
|
|
102
|
+
|
|
103
|
+
## Evidence pointers
|
|
104
|
+
- `tsc --noEmit` clean on the three src edits.
|
|
105
|
+
- Re-armed gates pass under the real CI config:
|
|
106
|
+
`npx vitest run --config vitest.push.config.ts tests/unit/esm-compliance.test.ts tests/unit/no-silent-fallbacks.test.ts`
|
|
107
|
+
→ 2 files / 7 tests passed.
|
|
108
|
+
- Root-cause evidence for the baseline correction: heuristic count at d0fe838
|
|
109
|
+
(the `[skip ci]` release that set 186) = 431; at HEAD = 437; set-diff shows the +6
|
|
110
|
+
are line-shifted re-counts of the same catch blocks, not new fallbacks.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Side-Effects Review — Per-component framework routing (B1)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `per-component-framework-routing`
|
|
4
|
+
**Date:** `2026-06-03`
|
|
5
|
+
**Author:** `Echo (instar-dev agent)`
|
|
6
|
+
**Tier:** `2` (new capability; converged + approved spec: docs/specs/per-component-framework-routing.md)
|
|
7
|
+
**Second-pass reviewer:** `not required (the converged spec already carries a 2-reviewer adversarial+integration pass)`
|
|
8
|
+
|
|
9
|
+
## Summary of the change
|
|
10
|
+
Adds an `IntelligenceRouter` at the single LLM funnel so different internal components
|
|
11
|
+
(sentinels, gates, …) can be routed to different agentic frameworks (e.g. all sentinels on
|
|
12
|
+
Codex while the agent stays on Claude), to move that LLM chatter off the Claude rate-limit
|
|
13
|
+
budget. ABSENT-by-default config ⇒ byte-identical to today.
|
|
14
|
+
|
|
15
|
+
New / changed:
|
|
16
|
+
- `src/core/componentCategories.ts` (NEW) — component-name → category registry + resolver.
|
|
17
|
+
- `src/core/IntelligenceRouter.ts` (NEW) — funnel-level, per-call, per-framework routing with
|
|
18
|
+
per-framework circuit breakers and circuit-aware fallback.
|
|
19
|
+
- `src/core/intelligenceProviderFactory.ts` — additive `breaker?` option threaded to the wrap
|
|
20
|
+
calls (lets the router give each framework its OWN breaker).
|
|
21
|
+
- `src/core/CircuitBreakingIntelligenceProvider.ts` — `wrapIntelligenceWithCircuitBreaker`
|
|
22
|
+
gains an optional `breaker` param (default unchanged = global singleton).
|
|
23
|
+
- `src/core/types.ts` — additive `attribution.category?` on IntelligenceOptions; additive
|
|
24
|
+
`sessions.componentFrameworks?` config (TYPE-ONLY — NOT added to ConfigDefaults).
|
|
25
|
+
- `src/commands/server.ts` — wrap the shared provider in the router in its OWN try/catch.
|
|
26
|
+
- `tests/unit/intelligence-router.test.ts` (NEW) — 11 tests.
|
|
27
|
+
|
|
28
|
+
## Decision-point inventory
|
|
29
|
+
- `IntelligenceRouter.evaluate` routing — **add** — chooses WHICH framework answers; no
|
|
30
|
+
block/allow authority (providers are signal). The one safety branch is fallback (D4).
|
|
31
|
+
- `wrapIntelligenceWithCircuitBreaker` / factory `breaker?` — **modify (additive)** — default
|
|
32
|
+
path unchanged; only the router passes a distinct breaker.
|
|
33
|
+
|
|
34
|
+
## 1. Over-block / 2. Under-block
|
|
35
|
+
No block/allow surface — providers are SIGNAL. The router cannot block a running agent; the
|
|
36
|
+
worst it does is degrade-and-report (D4). Unconfigured, it is a transparent passthrough.
|
|
37
|
+
|
|
38
|
+
## 3. Level-of-abstraction fit
|
|
39
|
+
Correct — the router sits at the existing `CircuitBreakingIntelligenceProvider` funnel (the
|
|
40
|
+
one chokepoint every `.evaluate()` already passes), so it covers ALL callers (including the
|
|
41
|
+
config-path and inline-closure ones) without editing 38 call sites — Structure > Willpower.
|
|
42
|
+
|
|
43
|
+
## 4. Signal vs authority compliance
|
|
44
|
+
**Reference:** docs/signal-vs-authority.md. [x] No block/allow surface of its own. Routing
|
|
45
|
+
changes which model answers; it adds no gate. The per-framework breakers are the existing
|
|
46
|
+
breaker authority, now correctly ISOLATED per framework (a Claude trip no longer pauses Codex)
|
|
47
|
+
— a correctness improvement, not a new authority.
|
|
48
|
+
|
|
49
|
+
## 5. Interactions
|
|
50
|
+
- **Breaker isolation:** each non-default framework gets a distinct `new LlmCircuitBreaker()`
|
|
51
|
+
(proven by unit test — a routed call reaches a different provider instance than the default).
|
|
52
|
+
The default framework keeps the global singleton, so status/health surfaces are unchanged for
|
|
53
|
+
it. (Per-framework breaker states are additionally surfaced via GET /intelligence/routing —
|
|
54
|
+
part 2 of B1, see "Remaining".)
|
|
55
|
+
- **Fallback (D4):** binary-missing → degrade to default + DegradationReporter (no silent
|
|
56
|
+
fallback — composes with Task-1's re-armed gate); rate-limited → LlmCircuitOpenError
|
|
57
|
+
propagates and the caller swallows it into its heuristic (NO herd onto Claude).
|
|
58
|
+
- **Cascade-503:** the router is built in its OWN try/catch in server boot; a build failure
|
|
59
|
+
leaves the raw provider, never 503s the server.
|
|
60
|
+
- **Config:** type-only; NOT in ConfigDefaults (applyDefaults deep-merge would otherwise inject
|
|
61
|
+
it everywhere and break absent-equals-unchanged).
|
|
62
|
+
|
|
63
|
+
## 6. External surfaces
|
|
64
|
+
- (Remaining in this PR) `GET /intelligence/routing` read-only surface + its CapabilityIndex
|
|
65
|
+
classification (the #727 lesson). No persistent state. Config: `sessions.componentFrameworks`
|
|
66
|
+
(opt-in, absent by default).
|
|
67
|
+
|
|
68
|
+
## 7. Rollback cost
|
|
69
|
+
Pure additive + opt-in. Remove `componentFrameworks` (or revert) → every call routes to the
|
|
70
|
+
single shared provider exactly as today. No state, no migration.
|
|
71
|
+
|
|
72
|
+
## Conclusion
|
|
73
|
+
B1 complete across all three test tiers (tsc clean): per-call funnel resolution,
|
|
74
|
+
per-framework breaker isolation, circuit-aware fallback, live-config (hot), the category
|
|
75
|
+
registry, factory breaker-threading, config type, server wiring, the `GET /intelligence/routing`
|
|
76
|
+
read surface (+ CapabilityIndex INTERNAL_PREFIXES classification — the #727 lesson),
|
|
77
|
+
CLAUDE.md agent-awareness (template + migrateClaudeMd for existing agents). Tests: 11 unit
|
|
78
|
+
(`intelligence-router.test.ts`) + 3 integration (`intelligence-routing-routes.test.ts`,
|
|
79
|
+
200/503) + 3 e2e (`intelligence-routing-lifecycle.test.ts`, feature-alive on the real
|
|
80
|
+
AgentServer init path + Bearer-auth + read-only). The design was converged (2-reviewer
|
|
81
|
+
adversarial+integration) which corrected the breaker-isolation and resolution-point design
|
|
82
|
+
before any code — see the spec's Convergence Report. B2 (gates/reflectors + per-framework
|
|
83
|
+
spend accounting + dashboard view) is a deliberate follow-up.
|
|
84
|
+
|
|
85
|
+
## Evidence pointers
|
|
86
|
+
- `tsc --noEmit` clean.
|
|
87
|
+
- `vitest run tests/unit/intelligence-router.test.ts` → 11/11 pass (resolution precedence,
|
|
88
|
+
dispatch, per-framework instance isolation, fallback both modes, live-config hot, for() surface).
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Side-Effects Review — ResourceLedger Phase B (CPU + memory sampling)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `resource-ledger`
|
|
4
|
+
**Date:** `2026-06-03`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `not required`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Add continuous CPU + memory sampling to the per-agent `ResourceLedger`. A new
|
|
11
|
+
`ResourceSampler` (mirroring `TokenLedgerPoller`) samples the agent's own server
|
|
12
|
+
process (CPU% via a `process.cpuUsage()` delta, RSS + heapUsed via
|
|
13
|
+
`process.memoryUsage()`) and each spawned session by its pane PID (one batched `ps`
|
|
14
|
+
call, dead-PID tolerant), plus an aggregate, into a new `resource_samples` table.
|
|
15
|
+
Two read-only routes (`/resources/summary`, `/resources/samples`) and a dashboard
|
|
16
|
+
"Resource Usage" tab expose it. Constructed at boot in `AgentServer` behind the
|
|
17
|
+
`developmentAgent` dark-feature gate; bounded by a retention prune.
|
|
18
|
+
|
|
19
|
+
## Decision-point inventory
|
|
20
|
+
|
|
21
|
+
One gate: whether to construct + start the sampler. It resolves
|
|
22
|
+
`monitoring.resourceLedger.enabled ?? !!config.developmentAgent` — explicit config
|
|
23
|
+
wins; otherwise live on dev agents, dark on the fleet. No other decision logic; the
|
|
24
|
+
sampler is pure observation (no kill/throttle/route-block verdicts).
|
|
25
|
+
|
|
26
|
+
## 1. Over-block
|
|
27
|
+
|
|
28
|
+
**What legitimate inputs does this change reject?** Nothing is rejected. The sampler
|
|
29
|
+
gates nothing — it only reads `ps`/`process.*` and writes the ledger. The routes
|
|
30
|
+
return data or a `503` stub when the ledger is null; they never block a caller.
|
|
31
|
+
|
|
32
|
+
## 2. Under-block
|
|
33
|
+
|
|
34
|
+
**What does this still miss?** CPU% for sessions comes from the pane PID's `ps
|
|
35
|
+
%cpu`, which on macOS/Linux is a lifetime-average for the process, not a precise
|
|
36
|
+
instantaneous interval rate (the own-process reading IS a true interval delta). It
|
|
37
|
+
samples the pane shell PID, not a full descendant-tree roll-up, so a session's
|
|
38
|
+
total tree CPU is approximated by the pane process line `ps` returns. Rate-limit
|
|
39
|
+
aggregation across the fleet and mitigation actions are explicitly later slices —
|
|
40
|
+
this slice only measures.
|
|
41
|
+
|
|
42
|
+
## 3. Level-of-abstraction fit
|
|
43
|
+
|
|
44
|
+
**Right layer?** Yes. The sampler lives in `monitoring/` beside `TokenLedgerPoller`
|
|
45
|
+
and writes the existing `ResourceLedger`; boot wiring lives in `AgentServer`
|
|
46
|
+
alongside the Phase-A ledger construction; the routes are inline in `routes.ts` next
|
|
47
|
+
to `/resources/rate-limits` and `/tokens/*`. The pane-PID resolver is a read-only
|
|
48
|
+
method on `SessionManager` (the owner of session/tmux truth), reusing the same
|
|
49
|
+
`tmux list-panes #{pane_pid}` pattern already there.
|
|
50
|
+
|
|
51
|
+
## 4. Signal vs authority compliance
|
|
52
|
+
|
|
53
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
54
|
+
|
|
55
|
+
No blocking authority added. The ledger and sampler are pure signal: every write
|
|
56
|
+
swallows its own error, every OS call is guarded, and a sampling failure records
|
|
57
|
+
nothing and retries next tick (`@silent-fallback-ok`). The sampler never reaches
|
|
58
|
+
back into any runtime flow — it cannot affect the `LlmCircuitBreaker`, the reaper,
|
|
59
|
+
sessions, or messaging.
|
|
60
|
+
|
|
61
|
+
## 5. Interactions
|
|
62
|
+
|
|
63
|
+
Reads `SessionManager.getRunningSessionPanePids()` (new, read-only) once per tick;
|
|
64
|
+
no mutation of session state. Shares the `ResourceLedger` SQLite handle with the
|
|
65
|
+
Phase-A rate-limit path (same DB, separate `resource_samples` table — no schema
|
|
66
|
+
collision; the rate-limit methods are untouched). The `ps` call is a child process,
|
|
67
|
+
isolated from the event loop. No interaction with the SessionReaper, sentinels,
|
|
68
|
+
compaction recovery, or the multi-machine lease. Idempotent boot (additive table
|
|
69
|
+
DDL), clean shutdown (sampler stopped before the ledger closes).
|
|
70
|
+
|
|
71
|
+
## 6. External surfaces
|
|
72
|
+
|
|
73
|
+
Two new HTTP routes (`GET /resources/summary`, `GET /resources/samples`,
|
|
74
|
+
Bearer-authed, read-only), registered in `CapabilityIndex`. One new dashboard tab
|
|
75
|
+
(static HTML/JS, XSS-safe via escaping, friendly "not turned on" state on 503). New
|
|
76
|
+
config block `monitoring.resourceLedger.{sampleIntervalMs,idleSampleIntervalMs,
|
|
77
|
+
retentionDays}` with `applyDefaults` backfill. CLAUDE.md template + idempotent
|
|
78
|
+
`migrateClaudeMd` backfill so existing agents learn the capability. No new
|
|
79
|
+
notifications, no Telegram, no external network calls. One new SQLite table inside
|
|
80
|
+
the already-present `resource-ledger.db`.
|
|
81
|
+
|
|
82
|
+
## 7. Performance (a resource tracker must itself be cheap)
|
|
83
|
+
|
|
84
|
+
This is the dimension that matters most here — the tracker must not become a
|
|
85
|
+
resource hog.
|
|
86
|
+
|
|
87
|
+
- **Sampling cost per tick is bounded and small.** The own-process reading is a free
|
|
88
|
+
`process.cpuUsage()` + `process.memoryUsage()` pair (no syscall fork). All session
|
|
89
|
+
PIDs are measured in **one** batched `ps` child process per tick — never one fork
|
|
90
|
+
per PID — with a 5s timeout and a 1 MB output cap. Default cadence is 60s
|
|
91
|
+
(active), backing off to 5 minutes when no sessions are running, so the idle CPU
|
|
92
|
+
floor is barely moved.
|
|
93
|
+
- **Write cost is bounded.** Each tick writes a handful of rows (server + N sessions
|
|
94
|
+
+ 1 aggregate) in a single transaction. No JSONL scan, no file walk (unlike the
|
|
95
|
+
TokenLedger), so there is no large-history boot hazard.
|
|
96
|
+
- **Storage is bounded.** The `resource_samples` table is pruned every tick to a
|
|
97
|
+
retention window (default 7 days). At a 60s cadence with a few sessions that is on
|
|
98
|
+
the order of tens of thousands of small rows — kilobytes-to-low-megabytes, indexed
|
|
99
|
+
on `(ts)` and `(source, ts)` for cheap windowed queries.
|
|
100
|
+
- **Fail-open never amplifies cost.** A slow/failed `ps` is caught (no stacking — a
|
|
101
|
+
re-entrant tick is skipped), and the timer is `unref()`'d so it can't keep the
|
|
102
|
+
process alive. The feature ships dark on the fleet (developmentAgent gate), so the
|
|
103
|
+
fleet-wide cost is zero until each agent is deliberately promoted.
|
|
104
|
+
|
|
105
|
+
## Rollback
|
|
106
|
+
|
|
107
|
+
Set `monitoring.resourceLedger.enabled` to `false` (disables the whole ledger; the
|
|
108
|
+
routes 503) — or, to keep Phase A but stop sampling, the sampler simply is not
|
|
109
|
+
constructed when the `developmentAgent` gate resolves off. No data migration to
|
|
110
|
+
reverse; the `resource_samples` table is additive and inert when unused.
|