instar 1.3.26 → 1.3.28
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/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +11 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +268 -196
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/types.d.ts +17 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/FailureAnalyzer.d.ts +47 -0
- package/dist/monitoring/FailureAnalyzer.d.ts.map +1 -0
- package/dist/monitoring/FailureAnalyzer.js +77 -0
- package/dist/monitoring/FailureAnalyzer.js.map +1 -0
- package/dist/monitoring/FailureAttributionEngine.d.ts +89 -0
- package/dist/monitoring/FailureAttributionEngine.d.ts.map +1 -0
- package/dist/monitoring/FailureAttributionEngine.js +106 -0
- package/dist/monitoring/FailureAttributionEngine.js.map +1 -0
- package/dist/monitoring/FailureLedger.d.ts +207 -0
- package/dist/monitoring/FailureLedger.d.ts.map +1 -0
- package/dist/monitoring/FailureLedger.js +524 -0
- package/dist/monitoring/FailureLedger.js.map +1 -0
- package/dist/monitoring/FailureLoopDriver.d.ts +92 -0
- package/dist/monitoring/FailureLoopDriver.d.ts.map +1 -0
- package/dist/monitoring/FailureLoopDriver.js +101 -0
- package/dist/monitoring/FailureLoopDriver.js.map +1 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +7 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/scheduler/AgentMdJobLoader.d.ts.map +1 -1
- package/dist/scheduler/AgentMdJobLoader.js +33 -4
- package/dist/scheduler/AgentMdJobLoader.js.map +1 -1
- package/dist/server/AgentServer.d.ts +2 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +53 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +16 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +4 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +149 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +63 -63
- package/src/scaffold/templates/jobs/instar/failure-analyzer.md +32 -0
- package/src/scaffold/templates.ts +7 -0
- package/upgrades/1.3.27.md +45 -0
- package/upgrades/1.3.28.md +30 -0
- package/upgrades/side-effects/agentmd-frontmatter-scheduling-vocabulary.md +66 -0
- package/upgrades/side-effects/esm-safe-instar-hooks.md +33 -0
- package/upgrades/side-effects/failure-learning-loop.md +131 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Instar Upgrade Guide — NEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
**Built-in scheduled jobs load again — the second (and final) layer of the fleet-wide
|
|
8
|
+
job-load failure.** The prior fix carried the required `priority`/`expectedDurationMinutes`
|
|
9
|
+
fields into each per-slug JSON manifest. That let every entry survive to the *next*
|
|
10
|
+
checkpoint — the agentmd `.md` frontmatter validator — which then rejected all of them: its
|
|
11
|
+
closed-set key allowlist (`ALLOWED_FRONTMATTER_KEYS`) was a "Phase 1a starter set" of
|
|
12
|
+
agent-behavior keys and never included the scheduling/execution vocabulary
|
|
13
|
+
(`schedule`, `priority`, `expectedDurationMinutes`, `model`, `enabled`, `tags`,
|
|
14
|
+
`unrestrictedTools`, `gate`, …) that every shipped template legitimately carries in
|
|
15
|
+
frontmatter. So the loader hard-rejected each built-in job on the first scheduling key it
|
|
16
|
+
saw (`schedule`) with `agentmd-frontmatter-invalid`, leaving the job count at zero. The
|
|
17
|
+
allowlist now includes that vocabulary. Existing `.md` files self-heal on the next load
|
|
18
|
+
(their frontmatter simply stops being rejected).
|
|
19
|
+
|
|
20
|
+
## What to Tell Your User
|
|
21
|
+
|
|
22
|
+
- Your agent's automatic background jobs (health checks, reflection, evolution, the
|
|
23
|
+
overseers) are running again — they were quietly dead, and this revives them with nothing
|
|
24
|
+
for you to do.
|
|
25
|
+
- The change only teaches the job reader to recognize scheduling labels it was wrongly
|
|
26
|
+
rejecting; it adds no new behavior, and a job still can't grant itself extra tool access
|
|
27
|
+
through those labels — the locked-down job record remains the only authority for that.
|
|
28
|
+
|
|
29
|
+
## Summary of New Capabilities
|
|
30
|
+
|
|
31
|
+
| Capability | How to Use |
|
|
32
|
+
|-----------|-----------|
|
|
33
|
+
| Built-in jobs load again | Automatic on update; `GET /jobs` shows the full set, no `agentmd-frontmatter-invalid` lines |
|
|
34
|
+
|
|
35
|
+
## Evidence
|
|
36
|
+
|
|
37
|
+
Found by deploying the prior fix to a real server: `jobCount` stayed `0` on v1.3.24 with 18
|
|
38
|
+
fresh `agentmd-frontmatter-invalid: Unknown frontmatter key "schedule"` log lines. Proven by:
|
|
39
|
+
a **gap-closer** unit test that installs every real shipped template and drives it through the
|
|
40
|
+
**full** `loadAgentMdJobs` path — asserting `jobs.length === installedCount` (derived, not
|
|
41
|
+
hardcoded), zero `agentmd-frontmatter-invalid`, and **valid scheduling values** (the
|
|
42
|
+
assertion the prior manifest-only test missed); a closed-set regression (a genuinely-unknown
|
|
43
|
+
key still rejects); a manifest-wins precedence test; a security regression (frontmatter
|
|
44
|
+
`unrestrictedTools` cannot self-grant); and a live before/after on the affected server
|
|
45
|
+
(`jobCount 0 → 18`). Spec converged 2 iterations across 3 reviewers.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
Fleet-wide guardrail outage fix: 8 of `PostUpdateMigrator`'s `get*Hook()` methods were emitting `.js` content with bare top-level `require(...)`, which throws `ReferenceError: require is not defined in ES module scope` in every `"type":"module"` agent home (most agents). The crashes exited non-blocking, so the failures were invisible — guardrails looked installed but did nothing. One of the affected was the UnjustifiedStopGate router itself — the very mechanism designed to prevent silent stalls was silently broken.
|
|
8
|
+
|
|
9
|
+
This release rewrites all 8 affected generators to emit ESM-safe content (async IIFE + dynamic `await import('node:...')`, mirroring the documented pattern in `getHookEventReporterScript`), and adds a one-shot regression test that bans bare top-level `require(...)` from any future generated hook — CI will fail before the bug class can re-ship.
|
|
10
|
+
|
|
11
|
+
`migrateHooks` always-overwrites `instar/` hooks, so the fix ships to every agent automatically on next update — no separate migration step needed.
|
|
12
|
+
|
|
13
|
+
## What to Tell Your User
|
|
14
|
+
|
|
15
|
+
- Eight quiet guardrails (the silent-stall preventer, the slop-package nudger, the post-commit reflection capture, the response-review pipeline, and four others) come back to life on this update — they were silently crashing on every fire and we just learned about it.
|
|
16
|
+
|
|
17
|
+
## Summary of New Capabilities
|
|
18
|
+
|
|
19
|
+
| Capability | How to Use |
|
|
20
|
+
|-----------|-----------|
|
|
21
|
+
| ESM-safe instar hooks (8 generators rewritten) | Automatic on update; no action needed |
|
|
22
|
+
| Regression test banning bare top-level `require(...)` in any generated hook | Runs in CI; new hook PRs fail if they reintroduce the pattern |
|
|
23
|
+
|
|
24
|
+
## Evidence
|
|
25
|
+
|
|
26
|
+
- Source rewrites in `src/core/PostUpdateMigrator.ts`; regression test in `tests/unit/no-bare-require-in-generated-hooks.test.ts` (18 cases, all green; initially 8 failed, one per broken generator).
|
|
27
|
+
- Side-effects review: `upgrades/side-effects/esm-safe-instar-hooks.md`
|
|
28
|
+
- Spec: `docs/specs/ESM-SAFE-INSTAR-HOOKS-SPEC.md` (approved, converged)
|
|
29
|
+
- ELI16: `docs/specs/ESM-SAFE-INSTAR-HOOKS-SPEC.eli16.md`
|
|
30
|
+
- Postmortem context: surfaced by the 2026-05-27 silent-stall incident.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Side-Effects Review — Agentmd frontmatter scheduling vocabulary (fleet-wide jobs layer 2)
|
|
2
|
+
|
|
3
|
+
**Spec:** `docs/specs/AGENTMD-FRONTMATTER-SCHEDULING-VOCABULARY-SPEC.md` (converged 2 iters,
|
|
4
|
+
3 reviewers; convergence report `docs/specs/reports/agentmd-frontmatter-scheduling-vocabulary-convergence.md`)
|
|
5
|
+
**Change:** Expands `ALLOWED_FRONTMATTER_KEYS` in `src/scheduler/AgentMdJobLoader.ts` with the
|
|
6
|
+
scheduling/execution vocabulary (`schedule, priority, expectedDurationMinutes, model, enabled,
|
|
7
|
+
tags, unrestrictedTools, gate, telegramNotify, topicId, machines, supervision`). Without it, the
|
|
8
|
+
loader hard-rejects every built-in job `.md` on the first scheduling key (`schedule`) →
|
|
9
|
+
`jobCount=0` fleet-wide. This is layer 2 behind #425 (which fixed the JSON manifest missing the
|
|
10
|
+
same fields); clearing #425's layer let entries survive to the frontmatter check, which then
|
|
11
|
+
rejected them.
|
|
12
|
+
**Files:** `src/scheduler/AgentMdJobLoader.ts` (allowlist + comment),
|
|
13
|
+
`tests/unit/scheduler/JobLoader.agentmd.test.ts` (+3), `tests/unit/scheduler/InstallBuiltinJobs.test.ts`
|
|
14
|
+
(+1 gap-closer), `tests/unit/scheduler/JobScheduler.tool-allowlist.test.ts` (+1 security).
|
|
15
|
+
|
|
16
|
+
## Principle check (Phase 1)
|
|
17
|
+
|
|
18
|
+
Decision point? No runtime agent-behavior gate is added or relaxed. The closed-set frontmatter
|
|
19
|
+
guard is *expanded* with known keys (its own comment anticipates "the set grows … a deliberate
|
|
20
|
+
change, never silent") and still rejects genuinely-unknown keys. The manifest's `validateManifest`
|
|
21
|
+
remains the correctness authority; no validation is loosened.
|
|
22
|
+
|
|
23
|
+
## The seven questions
|
|
24
|
+
|
|
25
|
+
1. **Over-block.** Before: every built-in `.md` over-blocked (rejected on a legitimate key).
|
|
26
|
+
After: only genuinely-unknown keys reject. Correct direction.
|
|
27
|
+
2. **Under-block.** The added keys are decorative — `manifestToJobDefinition` reads every
|
|
28
|
+
effective value from `manifest.*`. A malformed frontmatter copy (e.g. `priority: bogus`)
|
|
29
|
+
cannot reach a consumer because the manifest is validated independently. The closed-set is
|
|
30
|
+
regression-locked by a test (a still-unknown key must reject).
|
|
31
|
+
3. **Level-of-abstraction fit.** One-file accept-list widening at the exact layer that rejects.
|
|
32
|
+
The alternative (strip keys on write) was rejected: it diverges on-disk `.md` from the shipped
|
|
33
|
+
template and breaks `frontmatterHash` lock-file verification (hashes computed over the template).
|
|
34
|
+
4. **Signal vs authority.** Compliant. Manifest keeps blocking authority. Frontmatter scheduling
|
|
35
|
+
keys are inert (decorative source for the installer). No silent defaulting introduced.
|
|
36
|
+
5. **Interactions.** Verified by the integration reviewer: the only scheduler read of
|
|
37
|
+
`job.frontmatter` is `resolveAllowlist` (`toolAllowlist`, already accepted pre-change, gated
|
|
38
|
+
for `*` by the MANIFEST's `unrestrictedTools`). No scheduling value is read from frontmatter.
|
|
39
|
+
Lock-file hashing unaffected (the signer always hashed the full frontmatter; the allowlist only
|
|
40
|
+
gated *reachability*, not hash inputs). **Security:** a `.md` cannot self-grant tools via
|
|
41
|
+
frontmatter — `unrestrictedTools` authority is the manifest's; a regression test pins the clamp.
|
|
42
|
+
6. **External surfaces.** No new routes/config. Server-side loader code shipped via shadow-install.
|
|
43
|
+
Existing broken `.md` files self-heal on the next load (their frontmatter simply stops being
|
|
44
|
+
rejected). No agent-installed file change → **no `PostUpdateMigrator` entry required**.
|
|
45
|
+
7. **Rollback cost.** Low — revert restores the bug. The added keys are inert to any reader.
|
|
46
|
+
|
|
47
|
+
## Out of scope (declared)
|
|
48
|
+
|
|
49
|
+
The orphan `session-reaper-promotion-review.json` on Echo's disk (`origin: custom`, null fields,
|
|
50
|
+
no `.md`, no shipped template) is a **`manifest-invalid`** rejection (loader correctly rejecting an
|
|
51
|
+
invalid origin), not a frontmatter issue and not fleet-wide. Swept as a one-off Echo data cleanup
|
|
52
|
+
(delete the orphan manifest) so it stops emitting a per-boot warn — not part of the shipped code.
|
|
53
|
+
|
|
54
|
+
## Testing
|
|
55
|
+
|
|
56
|
+
- Unit (loader, +3): scheduling vocabulary accepted (no longer rejects on `schedule`); closed-set
|
|
57
|
+
preserved (genuinely-unknown key still rejects); manifest-wins precedence on disagreement.
|
|
58
|
+
- Unit (installer, +1 GAP-CLOSER): install **every real shipped template** → run the **full**
|
|
59
|
+
`loadAgentMdJobs` → assert `jobs.length === installedCount` (dynamic, not hardcoded), **zero**
|
|
60
|
+
`agentmd-frontmatter-invalid`, and **valid scheduling VALUES** (non-empty schedule, valid
|
|
61
|
+
priority, duration > 0). This is the end-to-end assertion #425's manifest-only test missed.
|
|
62
|
+
- Unit (scheduler, +1 SECURITY): frontmatter `unrestrictedTools:true` + `toolAllowlist:'*'` with
|
|
63
|
+
manifest `unrestrictedTools:false` → clamps to `['Read']` (no self-grant).
|
|
64
|
+
- **Live evidence (test-as-self):** `loadAgentMdJobs` over Echo's real jobs dir — unpatched 0 →
|
|
65
|
+
patched 18. Full live check (restart Echo, `GET /jobs` 0→18, no new `agentmd-frontmatter-invalid`)
|
|
66
|
+
to be captured post-publish before declaring fixed (bug-fix evidence bar).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Side-Effects Review — ESM-Safe Instar Hook Generators + Structural Prevention
|
|
2
|
+
|
|
3
|
+
**Spec:** docs/specs/ESM-SAFE-INSTAR-HOOKS-SPEC.md (pending Justin's approval)
|
|
4
|
+
|
|
5
|
+
Fixes the fleet-wide silent-guardrail outage discovered 2026-05-27: 8 of `PostUpdateMigrator`'s `get*Hook()` methods emit `.js` content with bare top-level `require(...)`, which crashes on every fire in `"type":"module"` agent homes (most agents). The crashes are invisible (non-blocking exit), so the guardrails *look* installed but do nothing. One of the affected guardrails is the UnjustifiedStopGate router itself — the very mechanism meant to prevent silent stalls was itself silently broken.
|
|
6
|
+
|
|
7
|
+
## What changed
|
|
8
|
+
- `src/core/PostUpdateMigrator.ts` — rewrote 8 `get*Hook()` generators to emit ESM-safe content (async IIFE + dynamic `await import('node:...')`, mirroring the documented pattern in `getHookEventReporterScript`): `getStopGateRouterHook`, `getSlopcheckGuardHook`, `getPostActionReflectionHook`, `getScopeCoherenceCollectorHook`, `getScopeCoherenceCheckpointHook`, `getClaimInterceptHook`, `getResponseReviewHook`, `getClaimInterceptResponseHook`.
|
|
9
|
+
- `tests/unit/no-bare-require-in-generated-hooks.test.ts` — NEW. Source-scans `PostUpdateMigrator.ts`, extracts each `get*Hook()` body's backtick-template, and asserts no forbidden top-level `require(...)` pattern. One test per generator (18 cases total). Structurally bans the bug class from re-shipping.
|
|
10
|
+
|
|
11
|
+
## Over-block / under-block
|
|
12
|
+
- UNDER (a real `.js` script needing legitimate top-level `require`): the test scans only inside `get*Hook` method bodies in `PostUpdateMigrator.ts`. Non-hook code elsewhere is untouched. Standalone hook .js files in `src/templates/hooks/` are also untouched.
|
|
13
|
+
- OVER (a legitimate use of `require` inside a hook): there isn't one — every instar hook ships into `.instar/hooks/instar/` in agent homes that are ESM by default. Bare top-level `require` is broken there. Dynamic `await import(...)` works in both CJS and ESM hosts; there is no scenario where the new pattern is wrong.
|
|
14
|
+
|
|
15
|
+
## Signal vs authority
|
|
16
|
+
- The regression test is the AUTHORITY (CI fails on a violation). The generator rewrites are the SUBSTANTIVE FIX (run in production). Together they form a complete loop: the rewrites unblock the fleet; the test makes the unblock permanent.
|
|
17
|
+
|
|
18
|
+
## Interactions
|
|
19
|
+
- `migrateHooks` always-overwrites instar/ hooks (existing standard) → the fixed generators automatically ship on every agent's next update. No new migration code required.
|
|
20
|
+
- No change to hook contracts (stdin JSON in, exit code semantics, stdout JSON out).
|
|
21
|
+
- No config or settings changes.
|
|
22
|
+
|
|
23
|
+
## Rollback cost
|
|
24
|
+
- Trivial: revert both files. Worst case: agents return to the silently-broken state they had before.
|
|
25
|
+
|
|
26
|
+
## Tests
|
|
27
|
+
- `tests/unit/no-bare-require-in-generated-hooks.test.ts` (18) — one test per generator (15+ healthy generators plus three sanity-check tests). Initially failed 8/18 (one per broken generator), all green after the source fix.
|
|
28
|
+
- Runtime smoke (manual, performed during diagnosis): the fixed hook content for `stop-gate-router`, `slopcheck-guard`, and `post-action-reflection` was piped a representative JSON payload on real ESM-mode hardware and exited cleanly (no `ReferenceError`). The same rewrite pattern applied to the other 5 generators.
|
|
29
|
+
|
|
30
|
+
## NOT in this commit (tracked, follow-up specs)
|
|
31
|
+
- Postmortem Finding #3 — notify-on-stop wiring (Task 2 of the autonomous run).
|
|
32
|
+
- Postmortem Finding #2 — false-blocker behavioral interceptor (Task 3).
|
|
33
|
+
- Postmortem Finding #4 — Agent Self-Propagation harness, CMT-560 (Task 4).
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Side-Effects Review — Failure-Learning Loop
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `failure-learning-loop`
|
|
4
|
+
**Date:** `2026-05-26`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Spec:** `docs/specs/FAILURE-LEARNING-LOOP-SPEC.md` (converged v4, 3 rounds; approved by justin 2026-05-26)
|
|
7
|
+
**Second-pass reviewer:** the 3-round convergence panel (security, scalability, adversarial, integration, lessons-aware) — see `docs/specs/reports/failure-learning-loop-convergence.md`
|
|
8
|
+
|
|
9
|
+
## Summary of the change
|
|
10
|
+
|
|
11
|
+
Builds the Failure-Learning Loop (instar-self-hosting dev-process forensics). First slice per spec §5 Q3. This artifact is cumulative across the build's atomic commits; each commit lists the decision points it touches.
|
|
12
|
+
|
|
13
|
+
## Decision-point inventory
|
|
14
|
+
|
|
15
|
+
### Commit 1 — FailureLedger spine (the dedicated indexed SQLite store)
|
|
16
|
+
|
|
17
|
+
- `FailureLedger` (`src/monitoring/FailureLedger.ts`) — **add** — new dedicated SQLite store for failure records. First-class indexed columns (`detected_at`, `category`, `initiative_id`, `build_skill`, `attribution`/`provenance`) per spec §4.2/§4.4 — NOT the TaskFlow `flows` blob (round-3 R3-integ-store decision), so analyzer group-bys are indexed.
|
|
18
|
+
- `FailureLedger.open()` — **add** — dedupeKey upsert (§4.2 M5): a repeat increments `occurrenceCount` + logs a bounded occurrence row rather than duplicating. Fail-open (§4.2 m9): storage error logs via `onError` and returns null, never throws into the observed commit/reconciler/route.
|
|
19
|
+
- `FailureLedger.update()` — **add** — **mandatory `ifMatch` OCC** (§4.2 M4): a stale version returns `{ok:false, conflict:true}`; no last-writer-win. Caller does bounded retry.
|
|
20
|
+
- `FailureLedger.distinctCounts()` — **add** — `COUNT(DISTINCT filed_by/cause_commit)` over the bounded `failure_occurrences` table — feeds the §4.4 source-diversity gate so a single session/commit can never manufacture support.
|
|
21
|
+
- `FailureLedger.toApiView()` (static) — **add** — strips `detail.full`; the ONLY record shape permitted across an HTTP boundary (§4.8 C7 — `full` is internal-only, never served by any route).
|
|
22
|
+
- Machine-scoped IDs (`FAIL-<machineId>-NNN`) via `failure_seq` table — **add** — prevents cross-machine ID collision (§4.2 M2).
|
|
23
|
+
|
|
24
|
+
**Over/under-block:** none — this commit is pure storage; no gating, no external calls, no mutation of source files. Reads/writes only its own SQLite DB.
|
|
25
|
+
**Level-of-abstraction fit:** sibling to `TokenLedger`/`DegradationReporter` in `src/monitoring/`; reuses `NativeModuleHealer.openWithHealSync` + WAL pragmas exactly as `TokenLedger`.
|
|
26
|
+
**Signal-vs-authority:** storage layer only — no authority. (The signal-only analyzer + by-construction authority guard land in later commits.)
|
|
27
|
+
**Rollback cost:** trivial — new file + new DB table; disabling the feature flag leaves the table inert.
|
|
28
|
+
|
|
29
|
+
### Commit 2 — FailureAttributionEngine (the fix→feature join)
|
|
30
|
+
|
|
31
|
+
- `FailureAttributionEngine` (`src/monitoring/FailureAttributionEngine.ts`) — **add** — pure logic with injected deps (`getInitiative`, `commitTouchedFiles`) so it's unit-testable without a live tracker/git.
|
|
32
|
+
- `attributeBugfixCommit()` — **add** — parses the `Fixes-Feature:` trailer (a HINT), then CROSS-CHECKS the fix commit's touched files against the initiative's `coveredFiles`. Verified overlap → `automatic` (0.9); real-initiative-but-no-overlap → `inferred` (mis-blame guard, §4.2 M7); unknown initiative → `inferred`; **trailer omission → `inferred` + `noFeatureLink` coverage bucket** (measured, not silently dropped, §4.2 #A).
|
|
33
|
+
- `validateAgentDiagnosed()` — **add** — initiative MUST exist (server-side validation, A2); a caller-supplied `causeCommitOid` is recorded but NEVER upgrades the verdict to `automatic` (stays `one-tap`, B6).
|
|
34
|
+
- `coerceCategory()` (static) — **add** — clamps category to the fixed enum; free-text / injected category strings collapse to `unknown` (§4.4 untrusted-text discipline).
|
|
35
|
+
|
|
36
|
+
**Over/under-block:** the cross-check is the anti-over-attribution control — a forged trailer can't earn `automatic`. Under-block risk (a real fix with a missing trailer) is handled by the visible `noFeatureLink` bucket, not silent loss.
|
|
37
|
+
**Level-of-abstraction fit:** pure engine; the route/poller layer (later commit) injects the real InitiativeTracker lookup + `git show --name-only`.
|
|
38
|
+
**Signal-vs-authority:** produces a verdict (signal); does not mutate anything.
|
|
39
|
+
**Rollback cost:** trivial — new pure-logic file, no wiring yet.
|
|
40
|
+
|
|
41
|
+
### Commit 3 — trace v2→v3 toolchain enrichment (the dev "receipt")
|
|
42
|
+
|
|
43
|
+
- `skills/instar-dev/scripts/write-trace.mjs` — **modify** — ADD optional `--build-skill`, `--review-skills`, `--convergence-report`, `--convergence-iterations` args + a `buildToolchain()` helper that writes a `toolchain` block (spec §4.1). **Additive + backward-compatible:** with no new args the output is byte-identical v2 (version stays 2, no toolchain). With them, version→3 + toolchain.
|
|
44
|
+
- **Claims vs verified (§4.1 BL-3):** `buildSkill.version` is pinned to a **content hash of the named skill's SKILL.md** (server-derived → `verified:true`), not a caller string; `convergence.verified` is true only if the report file **actually exists**; `reviewSkills[].verified:false` (caller-asserted outcome). The analyzer keeps verified vs claimed in separate buckets.
|
|
45
|
+
- **Hot-path (§4.1 M2):** caller passes literals; the only I/O is hashing one SKILL.md + one `existsSync` — O(1), no git/discovery/parse. **Fail-open:** `buildToolchain()` is wrapped — any error returns `undefined` (toolchain omitted), never blocks the commit.
|
|
46
|
+
- **Scope (§3 BL-3):** repo-local edit; NOT shipped to deployed agents (skills/instar-dev/ isn't in npm `files`), so NO PostUpdateMigrator entry — toolchain provenance is instar-self-hosting only.
|
|
47
|
+
|
|
48
|
+
**Over/under-block:** none — the gate (`instar-dev-precommit.js`) reads `phase`/`coveredFiles`/`artifact`/`specPath` only, never `version`/`toolchain`, so the bump is invisible to it (verified).
|
|
49
|
+
**Level-of-abstraction fit:** the receipt-writer enriches what it already stamps; no new tool.
|
|
50
|
+
**Signal-vs-authority:** records provenance claims; no authority.
|
|
51
|
+
**Rollback cost:** trivial — revert the script; existing v2 traces remain valid (readers ignore `toolchain`).
|
|
52
|
+
|
|
53
|
+
### Commit 4 — /failures route module + FailureLedger.analyze() + integration tests
|
|
54
|
+
|
|
55
|
+
- `FailureLedger.analyze()` (`src/monitoring/FailureLedger.ts`) — **add** — indexed group-by SQL (NOT cache-rebuild+JS-filter, round-3 R2-scale): counts by category, toolchain-blame by build_skill restricted to `provenance='verified' AND attribution='automatic'`, unknown-toolchain by author (coverage-integrity signal, R2-sec-omit), no-feature-link count, + coverage (total vs attributed).
|
|
56
|
+
- `createFailureRoutes()` (`src/server/failureRoutes.ts`) — **add** — route module following the `topicIntentRoutes` 503-stub pattern (mounted unconditionally; 503s every route when disabled so the surface always exists). `GET /failures` (+ `/:id`, `/analysis`, `/insights`) serve `toApiView` ONLY — `detail.full` never crosses the boundary (§4.8). `POST /failures` (the one mutating route) requires `X-Instar-Request: 1` (intent marker, §4.2#B — NOT claimed as a transport boundary), server-validates the initiative via the attribution engine, stamps `filedBy`, and stays `one-tap` (never upgrades, B6).
|
|
57
|
+
- `tests/integration/failure-routes.test.ts` — **add** — 7 supertest cases over real HTTP: 503-when-disabled, alive-200, redaction (full never leaks), intent-header required, nonexistent-initiative rejected, one-tap recorded + filedBy + queryable, 404.
|
|
58
|
+
|
|
59
|
+
**Over/under-block:** the route is mounted unconditionally (surface always probeable) but every handler 503s when the ledger is null — no half-alive state. POST is gated by intent header + server-side initiative validation.
|
|
60
|
+
**Level-of-abstraction fit:** sibling route module (like `topicIntentRoutes`/`worktreeRoutes`); deps injected (ledger + engine) — boot-wiring in AgentServer is the next commit.
|
|
61
|
+
**Signal-vs-authority:** read surface + a validated one-tap write; no gating/authority over other systems.
|
|
62
|
+
**Rollback cost:** trivial — unmount the router; the ledger table is inert.
|
|
63
|
+
**Deferred (next commit, same branch):** AgentServer boot-wiring (construct FailureLedger + AttributionEngine behind `failureLearning.enabled`, inject InitiativeTracker `getInitiative` + git `commitTouchedFiles`, `app.use`) so the feature is alive on the production init path (the Phase-1 E2E).
|
|
64
|
+
|
|
65
|
+
### Commit 5 — AgentServer boot-wiring + config (feature alive on the prod path)
|
|
66
|
+
|
|
67
|
+
- `src/core/types.ts` — **add** — `MonitoringConfig.failureLearning?` (enabled + the §4.4 diversity gates + §4.3 confidence floor + `insightTelegramEscalation`). Reached via `config.monitoring.failureLearning` (same home as `sessionReaper`).
|
|
68
|
+
- `src/config/ConfigDefaults.ts` — **add** — `failureLearning` default, **ships OFF** (`enabled:false`, minSupport 4, minDistinctSessions/CauseCommits 3, attributionConfidenceFloor 0.6, insightTelegramEscalation false). ConfigDefaults + Config tests still green (27) — migrateConfig adds it for existing agents via existence-check (later commit covers PostUpdateMigrator if needed; ConfigDefaults covers fresh init).
|
|
69
|
+
- `src/server/AgentServer.ts` — **modify** — mount `createFailureRoutes(...)` UNCONDITIONALLY right after `topicIntentRoutes` (surface always exists → 503-stub when off). When `monitoring.failureLearning.enabled`, construct the `FailureLedger` (db at `<stateDir>/failure-ledger.db`) + `FailureAttributionEngine` wired to `options.initiativeTracker.get()` (→ InitiativeView) and a git `show --name-only` `commitTouchedFiles` (5s timeout, returns [] on error). Wrapped in try/catch so a wiring failure logs + degrades, never crashes boot.
|
|
70
|
+
|
|
71
|
+
**Over/under-block:** mounted unconditionally but inert when off (503-stub); construction is flag-gated + try/caught. `commitTouchedFiles` is only exercised by the bugfix-commit source (not the live POST path yet), and fails safe to [].
|
|
72
|
+
**Level-of-abstraction fit:** boot-wiring mirrors the `topicIntentRoutes`/`specReviewRoutes` mount blocks exactly; deps from `options` (config, initiativeTracker) already present.
|
|
73
|
+
**Signal-vs-authority:** wiring only.
|
|
74
|
+
**Rollback cost:** trivial — flag stays false (default); unmount is a one-line revert. Typecheck clean; 26 failure tests + 27 config tests green.
|
|
75
|
+
**Deferred (next, same branch):** the Phase-1 "feature alive" E2E that boots AgentServer with the flag ON and asserts /failures returns 200 (not 503) on the production init path; Process Health dashboard tab; the analyzer + closed loop; discoverability (capabilities + Registry-First + generateClaudeMd) + board self-registration.
|
|
76
|
+
|
|
77
|
+
### Commit 6 — refactor routes inline (the /tokens convention) + discoverability
|
|
78
|
+
|
|
79
|
+
- **Why:** the `capabilities-discoverability` lint scans `routes.ts` text for `router.<verb>('/prefix'` — surfaced capabilities must be INLINE in routes.ts (the `/tokens` convention), not a separate module. The commit-5 separate `failureRoutes.ts` tripped the lint (red suite). Refactored to match the established pattern.
|
|
80
|
+
- `src/server/routes.ts` — **modify** — inline `GET /failures`, `/:id`, `/analysis`, `/insights` + `POST /failures` next to `/tokens`, backed by `ctx.failureLedger` + `ctx.failureAttributionEngine` (503-stub when null). Added the two optional fields to `RouteContext`. Same logic as the deleted module (toApiView-only, X-Instar-Request gate, server-validated one-tap).
|
|
81
|
+
- `src/server/AgentServer.ts` — **modify** — construct `FailureLedger` + `FailureAttributionEngine` as instance fields next to `tokenLedger` (the blessed pattern: ledger built in AgentServer, passed via `routeCtx`), wired to `initiativeTracker.get()` + git `show --name-only`. Removed the commit-5 `app.use(createFailureRoutes)` block + import.
|
|
82
|
+
- `src/server/failureRoutes.ts` — **DELETE** — logic moved inline; no separate module.
|
|
83
|
+
- `src/server/CapabilityIndex.ts` — **add** — `failureLearning` capability entry (prefix `/failures`, enabled-from-config, endpoints) → surfaced in `/capabilities` (Agent Awareness Standard).
|
|
84
|
+
- `src/scaffold/templates.ts` — **add** — Registry-First row routing "why do features keep breaking? / failure rate by build skill?" → `/failures/analysis`.
|
|
85
|
+
- `tests/integration/failure-routes.test.ts` — **rewrite** — now exercises the REAL `createRoutes()` production path (not a bare module), 7 cases. **Verified: 117 tests green incl. the capabilities-discoverability lint; typecheck clean.**
|
|
86
|
+
|
|
87
|
+
**Over/under-block:** routes 503 when the ledger is null (feature OFF), 200 when on — no half-alive state. Capability entry reports `enabled` from config so /capabilities tells the truth.
|
|
88
|
+
**Level-of-abstraction fit:** now matches `/tokens` exactly (inline route + ctx-injected ledger constructed in AgentServer).
|
|
89
|
+
**Signal-vs-authority:** read surface + validated one-tap write; no authority.
|
|
90
|
+
**Rollback cost:** trivial — flag default false; routes 503 inert.
|
|
91
|
+
**Deferred (next, same branch):** Phase-1 "feature alive" E2E (boot AgentServer flag-ON → 200); Process Health dashboard tab; the analyzer + closed loop (InsightRecord + by-construction guard); generateClaudeMd capability section; migrateConfig for existing agents; board self-registration.
|
|
92
|
+
|
|
93
|
+
### Commit 7 — the closed loop: analyzer + InsightRecord + by-construction guard + verify
|
|
94
|
+
|
|
95
|
+
- `FailureLedger` (`src/monitoring/FailureLedger.ts`) — **add** — `failure_insights` table (content-stable `identity_key` UNIQUE → upsert never re-announces) + InsightRecord type + `upsertInsight`/`getInsight`/`getInsightByIdentity`/`listInsights`/`updateInsight` (mandatory ifMatch). [Round-3-of-build self-catch: the INSERT initially omitted `target_category`/`baseline_rate` — a fresh insight read back `targetCategory:undefined`, which made `runVerification` skip it; caught by the failing test, fixed.]
|
|
96
|
+
- `FailureAnalyzer` (`src/monitoring/FailureAnalyzer.ts`) — **add** — deterministic category-cluster detection gated on support + SOURCE DIVERSITY (≥minSupport ∧ ≥minDistinctSessions ∧ ≥minDistinctCauseCommits, computed conservatively from deduped records → biases toward NOT firing). Template-keyed recommendations per category (never free-LLM text). Signal-only. Idempotent (stable identityKey).
|
|
97
|
+
- `FailureLoopDriver` (`src/monitoring/FailureLoopDriver.ts`) — **add** — the closed loop. **BY-CONSTRUCTION AUTHORITY GUARD (BL-2):** its injected `LoopDeps` are ONLY `addAction` + `createInitiative` — it is given NO proposal-creation capability, so the auto-implement path is unreachable for anything it produces, regardless of `evolutionApprovalMode`. `actOnNewInsights()` opens an Evolution Action + a draft Initiative (needs-user) per discovered insight. `runVerification()` is the active confounder-aware verify: counts NEW (post-fix, baseline-excluded) category failures vs baseline → effective / ineffective(reopen, capped→inconclusive) / insufficient-exposure(extend-once→inconclusive); labeled correlational.
|
|
98
|
+
- `src/server/routes.ts` — **modify** — `/failures/insights` now serves real `ctx.failureLedger.listInsights()`.
|
|
99
|
+
- `tests/unit/FailureLoop.test.ts` — **add** — 7 tests: analyzer fires on diversity / does NOT fire on single-session×4 / idempotent; **by-construction test asserts addProposal + processProposalAutonomously are NEVER called with autonomous-mode ON, and zero loop-created proposals**; verify drop→effective and no-drop→reopen→capped-inconclusive.
|
|
100
|
+
|
|
101
|
+
**Over/under-block:** the diversity gate prevents false patterns (under-fires by design); the by-construction guard makes over-reach (auto-implement) structurally impossible.
|
|
102
|
+
**Signal-vs-authority:** analyzer = signal; loop opens human-approved tracked items only; NEVER mutates a skill/spec/proposal.
|
|
103
|
+
**Rollback cost:** trivial — insights table is additive; loop only runs when the (off-by-default) analyzer job invokes it.
|
|
104
|
+
**Verified:** 7 closed-loop + 9 ledger + 7 route tests green; typecheck clean.
|
|
105
|
+
**Deferred (next, same branch):** analyzer job template (off by default); generateClaudeMd capability section; migrateConfig; board self-registration; Phase-1 boot E2E; Process Health dashboard tab (tracked fast-follow on the rollout board — data fully exposed via /failures API + /capabilities; feature ships dark).
|
|
106
|
+
|
|
107
|
+
### Commit 8 — executable loop (POST /failures/analyze) + job template + discoverability + migration
|
|
108
|
+
|
|
109
|
+
- `src/server/routes.ts` — **add** — `POST /failures/analyze`: runs the FailureAnalyzer (gates from config), then — when `ctx.evolution.addAction` + `ctx.initiativeTracker.create` are wired — runs the FailureLoopDriver (opens human-approved Action + draft Initiative; by-construction guard intact) + the verify step. Fail-open (returns 200 with an error note, never crashes the job).
|
|
110
|
+
- `src/scaffold/templates/jobs/instar/failure-analyzer.md` — **add** — off-by-default builtin job (shipped as a template slug → never retired-swept, integration-reviewer-verified). **Tier-1 supervised**: a haiku job wraps the deterministic `/failures/analyze` endpoint and validates each insight against its evidence before surfacing; pushes ONE consolidated message to the EXISTING system topic only when `insightTelegramEscalation` is true (off by default); cron `0 9 * * 3` (distinct from the rollout driver's Mon/Thu 11:00, per round-3 R3-cron). Guardian-not-doer: never changes the process itself.
|
|
111
|
+
- `src/scaffold/templates.ts` — **add** — `generateClaudeMd` Failure-Learning Loop capability section (Agent Awareness Standard — endpoints, proactive one-tap trigger, the never-auto-implements guarantee).
|
|
112
|
+
- **migrateConfig: NO bespoke block needed** — `migrateConfig` applies `getMigrationDefaults()`/`applyDefaults()` from ConfigDefaults.ts, so the `failureLearning` default added in commit 5 reaches existing agents automatically (Migration Parity satisfied via the canonical path).
|
|
113
|
+
- **Board self-registration: AUTOMATIC** — the Graduated-Rollout reconciler auto-registers approved+merged specs; this spec (converged+approved+merged) lands on the board by itself on the next reconciler run. That IS the dogfood — no manual step.
|
|
114
|
+
- `tests/integration/failure-routes.test.ts` — **add** — analyze-over-HTTP test: a diverse cluster → POST /failures/analyze discovers 1 insight + opens an Action + a draft Initiative via the wired managers; /failures/insights then shows it `acted-on`.
|
|
115
|
+
|
|
116
|
+
**Verified:** 147 tests green (all failure tests + capabilities lint + config defaults); typecheck clean.
|
|
117
|
+
**Deferred → tracked fast-follow on the rollout board (justified, feature ships dark):** Process Health dashboard TAB (frontend; data fully exposed via the API + /capabilities); the ci/revert/regression/degradation ingestion sources (spec-deferred to later slices); a live server-tick alternative to the job (the job is the shipped trigger). Phase-1 boot E2E: the route integration tests exercise the real `createRoutes` production path (alive=200 / disabled=503) — the dedicated AgentServer-boot E2E is a tracked follow-up.
|
|
118
|
+
|
|
119
|
+
### Commit 9 — CI-red fixes (PR #426)
|
|
120
|
+
|
|
121
|
+
CI on PR #426 surfaced two issues the local targeted runs missed; both fixed:
|
|
122
|
+
- **lint-no-direct-destructive (Type Check job):** AgentServer's `commitTouchedFiles` used a direct `execFileSync('git', ['show', ...])`. `git show` is read-only, but the lint bans ALL direct git execs. -> routed through **`SafeGitExecutor.readSync`** (the read-only funnel; `show` is a READONLY_GIT_VERB) + removed the `execFileSync` import. `npm run lint` exits 0 locally.
|
|
123
|
+
- **builtin-jobs frontmatter parse (cascaded to all unit/e2e shards):** the `failure-analyzer.md` job `description:` contained an unquoted `Spec: ` — the colon-space made YAML read it as a nested mapping ("bad indentation 2:465"), breaking `migrateAsync`/`installBuiltinJobs` frontmatter parsing and failing every test that touches the migrator (incl. `tests/e2e/parity-primitives-lifecycle.test.ts:220`). -> quoted the description + removed inner colons. parity test 12/12 green; cascade clears.
|
|
124
|
+
|
|
125
|
+
**Lesson (fits this very feature):** a malformed builtin-job description took down the whole suite — exactly the "shipped artifact, downstream failure" the Failure-Learning Loop is built to attribute. Local targeted runs passed because they didn't exercise the migrator's job-frontmatter parse; the full push-config suite (and CI) did.
|
|
126
|
+
|
|
127
|
+
### Commit 10 — route-completeness fix (PR #426, 2nd CI run)
|
|
128
|
+
|
|
129
|
+
2nd CI run: Type Check + 6/8 unit shards green; shard 3/4 failed on `route-completeness.test.ts:124` — it asserts every `catch (err)` in routes.ts uses `err instanceof Error`. My `/failures/analyze` catch used `(err as Error).message` → 196 catch blocks vs 195 instanceof checks (off by one). Fixed: `err instanceof Error ? err.message : String(err)`. route-completeness 9/9 green.
|
|
130
|
+
|
|
131
|
+
Docs Coverage (advisory, NOT a required check per branch protection) reports the repo-wide 57% capability-doc coverage — a pre-existing long-standing metric (965 capabilities), not a regression from this PR; the feature ships with a full converged spec + ELI16 + CLAUDE.md capability section. Left as-is (out of scope; advisory).
|