instar 1.3.479 → 1.3.481

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 (49) hide show
  1. package/dashboard/index.html +103 -0
  2. package/dist/commands/server.d.ts.map +1 -1
  3. package/dist/commands/server.js +10 -7
  4. package/dist/commands/server.js.map +1 -1
  5. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  6. package/dist/config/ConfigDefaults.js +20 -8
  7. package/dist/config/ConfigDefaults.js.map +1 -1
  8. package/dist/core/PostUpdateMigrator.d.ts +1 -0
  9. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  10. package/dist/core/PostUpdateMigrator.js +139 -0
  11. package/dist/core/PostUpdateMigrator.js.map +1 -1
  12. package/dist/core/devGatedFeatures.d.ts +31 -0
  13. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  14. package/dist/core/devGatedFeatures.js +121 -0
  15. package/dist/core/devGatedFeatures.js.map +1 -1
  16. package/dist/core/types.d.ts +27 -0
  17. package/dist/core/types.d.ts.map +1 -1
  18. package/dist/core/types.js.map +1 -1
  19. package/dist/monitoring/BlockerLedger.d.ts +294 -0
  20. package/dist/monitoring/BlockerLedger.d.ts.map +1 -0
  21. package/dist/monitoring/BlockerLedger.js +597 -0
  22. package/dist/monitoring/BlockerLedger.js.map +1 -0
  23. package/dist/monitoring/blockerSettleAuthority.d.ts +28 -0
  24. package/dist/monitoring/blockerSettleAuthority.d.ts.map +1 -0
  25. package/dist/monitoring/blockerSettleAuthority.js +0 -0
  26. package/dist/monitoring/blockerSettleAuthority.js.map +1 -0
  27. package/dist/scaffold/templates.d.ts.map +1 -1
  28. package/dist/scaffold/templates.js +9 -0
  29. package/dist/scaffold/templates.js.map +1 -1
  30. package/dist/server/AgentServer.d.ts +1 -0
  31. package/dist/server/AgentServer.d.ts.map +1 -1
  32. package/dist/server/AgentServer.js +32 -0
  33. package/dist/server/AgentServer.js.map +1 -1
  34. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  35. package/dist/server/CapabilityIndex.js +15 -0
  36. package/dist/server/CapabilityIndex.js.map +1 -1
  37. package/dist/server/routes.d.ts +6 -0
  38. package/dist/server/routes.d.ts.map +1 -1
  39. package/dist/server/routes.js +108 -1
  40. package/dist/server/routes.js.map +1 -1
  41. package/package.json +1 -1
  42. package/scripts/lib/dark-gate-attribution.js +148 -0
  43. package/scripts/lint-dev-agent-dark-gate.js +159 -14
  44. package/src/data/builtin-manifest.json +64 -64
  45. package/src/scaffold/templates.ts +9 -0
  46. package/upgrades/1.3.480.md +35 -0
  47. package/upgrades/1.3.481.md +38 -0
  48. package/upgrades/side-effects/blocker-ledger.md +106 -0
  49. package/upgrades/side-effects/dev-agent-dark-gate-enforcement.md +95 -0
@@ -0,0 +1,106 @@
1
+ # Side-Effects Review — Blocker Ledger (Autonomy Principles Enforcement, Piece 1)
2
+
3
+ **Version / slug:** `blocker-ledger`
4
+ **Date:** `2026-06-10`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `code-reviewer subagent (see below)`
7
+
8
+ ## Summary of the change
9
+
10
+ Adds the **Blocker Ledger** — the resolution-workflow + memory layer that COMPLETES Principle 1 of `docs/specs/AUTONOMY-PRINCIPLES-ENFORCEMENT-SPEC.md`. The detection half (deferral-detector hook, B16_UNVERIFIED_WALL, B17_FALSE_BLOCKER) already exists; this is the missing resolution half. A detected false-blocker becomes a gated pipeline (`candidate → authority-checked → access-requested → dry-run → live-run → terminal`) with structural evidence-of-work at every terminal, built so the memory can NEVER become a deferral-laundromat. Ships **DARK** behind `monitoring.blockerLedger.enabled` (default false → routes 503).
11
+
12
+ Files: `src/monitoring/BlockerLedger.ts` (new — store + state machine + CAS + archival + audit), `src/monitoring/blockerSettleAuthority.ts` (new — the Tier-1 B17 settle authority), `src/server/routes.ts` (5 `/blockers*` routes + RouteContext field), `src/server/AgentServer.ts` (gated construction + routeCtx thread), `src/core/PostUpdateMigrator.ts` (deferral-detector auto-open trigger in the always-overwritten built-in hook), `src/config/ConfigDefaults.ts` (dark default), `src/core/types.ts` (config type), `src/scaffold/templates.ts` (CLAUDE.md awareness block). Tests: unit (35) + integration (7) + e2e (4).
13
+
14
+ ## Decision-point inventory
15
+
16
+ - `BlockerLedger.advance()` — **add** — gated linear-transition validator (refuses skips). Brittle/structural by design; carries NO message-blocking authority.
17
+ - `BlockerLedger.settle()` (resolved) — **add** — structural evidence validator (confined playbook path, id-reference, successful live-run). Structural, no message authority.
18
+ - `BlockerLedger.settle()` (true-blocker) — **add** — the one JUDGMENT in the feature. The field checks gate the FORM of evidence; the judgment routes to `blockerSettleAuthority` (Tier-1 LLM, B17 pattern). This is the signal-vs-authority crux.
19
+ - `deferral-detector` hook — **modify** — adds a best-effort, non-blocking auto-open POST to `/blockers` on the inability/false-blocker shape. The hook's existing checklist injection is unchanged.
20
+
21
+ ---
22
+
23
+ ## 1. Over-block
24
+
25
+ **What legitimate inputs does this change reject that it shouldn't?**
26
+
27
+ The ledger has **no outbound-message block/allow surface** — it never holds a message. Its only refusals are at its own `/blockers*` API edge (rejecting an advance that skips a state, or a settle whose evidence is incomplete). Those refusals are *intended* and reversible (the caller gathers the missing evidence and retries). A possible over-refusal: the `resolved` confined-path check rejects a legitimate playbook authored outside the configured `confinedPlaybookRoots` (default: `.claude/skills`, `<stateDir>/playbooks`, `.instar/playbooks`). Mitigation: the roots are constructor-configurable; the error message names the constraint. This is a deliberate safety bound (no arbitrary-path write target), not an accidental over-block.
28
+
29
+ ---
30
+
31
+ ## 2. Under-block
32
+
33
+ **What failure modes does this still miss?**
34
+
35
+ The free-text evidence fields (`detectedText`, `rebuttal`, `failedAttempt.detail`) are bounded + enveloped but their *truthfulness* is not verifiable by the ledger — an agent could record a plausible-but-fabricated "vault miss". This is mitigated, not eliminated: the true-blocker settle still routes through the B17 LLM authority, which is prompted to refuse a vague/untried rebuttal, and the self-fetch-first mandate requires the *form* of a failed self-fetch. The ledger's claim is "harder to launder," not "impossible to lie to." The D6 re-walk (re-test on a cadence, require NEW evidence) is the backstop. Noted as an accepted residual.
36
+
37
+ ---
38
+
39
+ ## 3. Level-of-abstraction fit
40
+
41
+ **Is this at the right layer?**
42
+
43
+ Yes. The ledger is a **detector/recorder** (low-level, structural) for everything except the one true-blocker settle judgment, which is correctly delegated UP to an **authority** (the LLM-backed `blockerSettleAuthority`). It does not re-implement the deferral-detector or B16/B17 — it *feeds off* them (the auto-open trigger) and *routes the settle through* B17. The CAS/atomic-write reuses the `CommitmentTracker` pattern; the audit-jsonl reuses the `SessionReaper` pattern; the dark/503 gate reuses the `growthMilestoneAnalyst` pattern. No parallel re-implementation of an existing primitive.
44
+
45
+ ---
46
+
47
+ ## 4. Signal vs authority compliance
48
+
49
+ **Required reference:** docs/signal-vs-authority.md
50
+
51
+ - [x] **No — this change produces a signal consumed by an existing smart gate** (for the message surface: the ledger never blocks a message; B16/B17 keep that authority), **AND** the one judgment it does carry (the true-blocker settle) is a **smart gate with context** (`blockerSettleAuthority`, an LLM authority that fails CLOSED).
52
+
53
+ The brittle field checks in `settle()` gate only the FORM of evidence (taxonomy membership, presence of a failed-attempt record, temporal ordering). They do NOT make the settle decision — that is the LLM authority's. A settle with perfect-form evidence still DENIES if the B17 authority judges it a false blocker. No brittle check owns the judgment. Compliant.
54
+
55
+ ---
56
+
57
+ ## 5. Interactions
58
+
59
+ - **Shadowing:** None. The `/blockers*` routes are new and additive; they do not sit in front of any existing route. The deferral-detector change appends after the existing checklist `process.stdout.write` — it cannot shadow or alter the checklist (verified: checklist is written first, then the fire-and-forget POST).
60
+ - **Double-fire:** The auto-open fires once per inability-match hook invocation. A blocker could be opened multiple times across multiple messages (no dedup in v1) — acceptable; entries are cheap and the list is paginated/archived. Noted as a known v1 characteristic, not a defect.
61
+ - **Races:** All ledger mutations serialize through one in-process chain (`mutate()`), reloading from disk before each apply — a cross-process write is picked up, not clobbered. Concurrent-open test proves 20 parallel opens produce 20 unique ids with no lost increment.
62
+ - **Feedback loops:** The deferral-detector → /blockers auto-open is one-directional; the ledger does not feed back into the hook. The B17 authority consumes ledger free-text only inside the data envelope (no instruction-injection feedback).
63
+ - **Timing:** the deferral-detector hook now holds the process open ~200ms (bounded, global 2000ms safety-net) on inability-matches only, to flush the fire-and-forget POST. Adds bounded latency to that specific hook path; negligible and only on false-blocker framing.
64
+
65
+ ---
66
+
67
+ ## 6. External surfaces
68
+
69
+ - **Other agents / users:** None until `monitoring.blockerLedger.enabled` is set (ships dark → routes 503). Existing agents receive the dark default via `migrateConfig` → `applyDefaults` (idempotent, missing-key-only) and the updated deferral-detector hook via the always-overwrite built-in-hook migration.
70
+ - **External systems:** None. The ledger is purely local file-JSON + the local `/blockers` API + a local-only B17 LLM call through the agent's existing intelligence provider. No network egress beyond the agent's own server.
71
+ - **Persistent state:** Adds `state/blocker-ledger.json`, `state/blocker-ledger-archive.json`, and `logs/blocker-decisions.jsonl`. All created lazily on first write; absent until the feature is enabled AND a blocker is opened.
72
+ - **CLAUDE.md template:** new capability block (agent-awareness). Existing agents get it via the normal template-migration path; it documents the dark default honestly.
73
+ - **Dashboard:** a read-only "Blockers" tab (`dashboard/index.html`) that consumes `GET /blockers`, HTML-escapes all untrusted free-text via the existing `escapeHtml`, renders a true-blocker as a decaying hypothesis ("recheck after <date>", never "settled/stop trying"), and shows a friendly "not turned on yet" message on the 503-dark response. Additive; mirrors the `resources` tab. No mutation controls (advance/settle from the dashboard is a later phase).
74
+
75
+ ---
76
+
77
+ ## 7. Rollback cost
78
+
79
+ Pure additive code + a dark-by-default flag. Back-out = revert the code and ship a patch; nothing is on by default, so no agent is running it unless explicitly enabled. The only persistent state is the three lazily-created files, which are inert when the feature is off and safe to leave on disk (or delete) on rollback. No migration to undo (the config default is missing-key-only and harmless when present-but-unused). No user-visible regression during the rollback window (nothing user-visible shipped). Estimated rollback: one revert commit, zero downtime.
80
+
81
+ ## Conclusion
82
+
83
+ The review produced no signal-vs-authority violation and no message-block surface. The one judgment (true-blocker settle) is correctly an LLM authority that fails closed, with brittle checks confined to evidence-FORM validation. One material build-time discovery (recorded for the user): on current `JKHeadley/main` (v1.3.479), `resolveModelForFramework` **already** handles `gemini-cli`/`pi-cli` with real models, so Piece 3's "broken foundation" premise is already fixed upstream — that is a separate PR's concern and does not affect this one. The design change made *during* this build (vs the converged spec): the true-blocker settle evidence (failedAttempt + accessRequest, each timestamped) is carried in the settle call and validated `accessRequest.at >= failedAttempt.at`, **decoupled** from the linear pipeline's `access-requested` state — because the pipeline orders `access-requested` (state 3) before `dry-run` (state 4), which conflicts with the true-blocker requirement that the ask come AFTER the failed attempt. This is faithful to the spec's *intent* (ask-after-trying) while resolving an ordering contradiction the spec didn't catch. Clear to ship as PR 1.
84
+
85
+ ---
86
+
87
+ ## Second-pass review (if required)
88
+
89
+ **Required** (touches a gate + server lifecycle). An independent reviewer subagent audited the artifact + code adversarially.
90
+
91
+ **Independent read of the artifact: concur.**
92
+
93
+ > Concur with the review. The one judgment (true-blocker settle) routes through the LLM authority (`buildB17SettleAuthority`), which fails closed on no-provider / provider-error / unparseable-verdict; every brittle check gates only the FORM of evidence and holds no settle authority; the message surface is never touched (the deferral-detector hook emits `decision:'approve'` unconditionally before the fire-and-forget auto-open, fully wrapped). Verified: no-skip state machine, `resolved` requires a successful live-run + confined id-referencing playbook, the no-evidence re-settle counter is persisted via `mutate()` BEFORE the throw (survives), single-writer CAS closes the TOCTOU by re-finding inside the final commit after the async authority call, and the feature is ALIVE when enabled / DARK (503) when disabled. `tsc` exit 0; 46 tests pass.
94
+
95
+ Two nice-to-haves the reviewer flagged, both addressed/accepted:
96
+ - The `toLlmSafeEnvelope` close-tag regex was tightened to tolerate whitespace/attributes (`</blocker-ledger-data >`) — **applied** this pass.
97
+ - `escapeHtmlForDashboard` was "exported but unused" at review time — now **wired** by the dashboard "Blockers" tab added this pass (all untrusted text escaped before render).
98
+
99
+ ---
100
+
101
+ ## Evidence pointers
102
+
103
+ - Unit: `tests/unit/BlockerLedger.test.ts` (28), `tests/unit/blockerSettleAuthority.test.ts` (7) — gated state machine, both terminals, self-fetch-first mandate, anti-laundering re-walk + escalate-after-N, injection-envelope, concurrency, archival, fail-closed authority.
104
+ - Integration: `tests/integration/blocker-ledger-routes.test.ts` (7) — 503-dark, 200-alive, X-Instar-Request 403, full create→advance→settle over HTTP, audit line with origin + gate hash.
105
+ - E2E: `tests/e2e/blocker-ledger-lifecycle.test.ts` (4) — production-path "feature is alive" (200 not 503), dark-by-default, persistence across restart.
106
+ - `npx tsc --noEmit` exit 0.
@@ -0,0 +1,95 @@
1
+ # Side-Effects Review — Dev-Agent Dark-Gate Enforcement
2
+
3
+ **Version / slug:** `dev-agent-dark-gate-enforcement`
4
+ **Date:** 2026-06-10
5
+ **Author:** echo (Instar Agent)
6
+ **Spec:** `docs/specs/DEV-AGENT-DARK-GATE-ENFORCEMENT-SPEC.md` (converged 3 rounds, approved)
7
+
8
+ ## Summary of the change
9
+
10
+ Two slices. **Slice A** routes the cartographer *zero-cost read surfaces* through
11
+ the existing `resolveDevAgentGate` so they run LIVE on development agents and DARK
12
+ on the fleet (the doc-tree/navigation read + the deterministic conformance audit),
13
+ and removes the redundant `egressAcknowledged` second gate from the freshness
14
+ sweep (the sweep remains an explicit `enabled:true` opt-in even on dev agents —
15
+ NOT dev-gated — because it is the one ongoing-cost surface). **Slice B** closes the
16
+ lint hole that let cartographer ship dark-for-everyone: every `enabled: false` in
17
+ `ConfigDefaults.ts` must now be DECLARED — either dev-gated (omit `enabled` +
18
+ register) or listed in a new `DARK_GATE_EXCLUSIONS` registry with a closed-enum
19
+ category and a ≥12-char reason. A one-shot, dev-agent-only migration lights up
20
+ existing dev agents on update.
21
+
22
+ **Files changed (source):**
23
+ - `src/config/ConfigDefaults.ts` — OMIT `enabled` from `cartographer` and
24
+ `cartographer.conformanceAudit` (gate decides at runtime); comments updated.
25
+ `freshnessSweep`/`llmEnrichment`/`llmRerank` `enabled:false` left as-is.
26
+ - `src/commands/server.ts` — `cartographerEnabled` now via `resolveDevAgentGate`;
27
+ freshness-sweep start predicate drops the `&& egressAcknowledged` term (now
28
+ `enabled` alone). Off-Claude routing probe + per-pass bounds untouched.
29
+ - `src/server/routes.ts` — conformance route gate `cfg?.enabled !== true` →
30
+ `!resolveDevAgentGate(cfg?.enabled, ctx.config)` (the only cartographer/
31
+ conformance ACCESS gate; the `sweepEnabled` status field at ~L4169 is a response
32
+ field, not a gate, deliberately untouched).
33
+ - `src/core/devGatedFeatures.ts` — required `justification` field on
34
+ `DevGatedFeature` (+ on every existing entry); two new dev-gated entries
35
+ (`cartographer`, `cartographerConformanceAudit`); new `DarkGateCategory`,
36
+ `DarkGateExclusion`, `DARK_GATE_EXCLUSIONS` (all 19 remaining dark defaults
37
+ classified).
38
+ - `src/core/PostUpdateMigrator.ts` — `migrateCartographerDevGate`: one-shot
39
+ (`_instar_migrations` marker), dev-agent-only, strips a default-shaped
40
+ `false` at `cartographer.enabled` + `cartographer.conformanceAudit.enabled`
41
+ ONLY; never touches `freshnessSweep`; records `result.upgraded`.
42
+
43
+ **Files changed (lint/infra):**
44
+ - `scripts/lint-dev-agent-dark-gate.js` — Assertion C (unclassified-dark-default +
45
+ registered-but-hardcoded-false), exclusion quality validation (closed category
46
+ enum + ≥12-char reason), brace-in-string loud-fail guard, literal-only limit
47
+ printed in failure header.
48
+ - `scripts/lib/dark-gate-attribution.js` (NEW) — single path-attribution module
49
+ shared by the lint and the golden-path test (so the canary checks the SAME
50
+ attributor the lint uses).
51
+
52
+ **Files changed (tests):** `tests/unit/lint-dev-agent-dark-gate.test.ts`
53
+ (extended: assertion-C cases, hand-authored golden-path map, brace-in-string,
54
+ destructive-not-gated, exclusion-quality), `tests/unit/PostUpdateMigrator-cartographerDevGate.test.ts`
55
+ (NEW), `tests/integration/conformance-dev-gate-route.test.ts` (NEW),
56
+ `tests/e2e/cartographer-dev-gate-lifecycle.test.ts` (NEW).
57
+
58
+ **Files added (docs):** the spec, its `.eli16.md` companion, and the convergence report.
59
+
60
+ ## Decision-point inventory
61
+
62
+ - **`cartographer.enabled`, `cartographer.conformanceAudit.enabled`** → dev-gate
63
+ (omit + register). Zero egress, zero spend — safe to dogfood live on dev agents.
64
+ - **`cartographer.freshnessSweep.enabled`** → NOT dev-gated; explicit opt-in
65
+ everywhere (DARK_GATE_EXCLUSIONS, category `cost-bearing`). Auto-arming recurring
66
+ third-party spend across the dev fleet is a P19 blast-radius risk.
67
+ - **`egressAcknowledged`** → neutralized as a second gate (privacy framing was
68
+ incoherent: source already egresses to a model every turn). Field retained for
69
+ back-compat; now inert on the sweep. Noted in the upgrade guide.
70
+ - **The other 18 existing `enabled:false` blocks** → classified into
71
+ `DARK_GATE_EXCLUSIONS` (destructive / cost-bearing / structural-stub /
72
+ optional-integration / deliberate-fleet-default). NONE retroactively dev-gated
73
+ in this PR — that is a deliberate, bounded scope. Each observe-only sentinel
74
+ would need its own safety judgment before going live on dev agents; this change
75
+ makes that an explicit, lint-visible decision rather than a silent default.
76
+
77
+ ## 1–7. Analysis
78
+
79
+ - **Behavioral change:** on a `developmentAgent:true` agent, cartographer's
80
+ read-only surfaces resolve LIVE (previously dark for everyone). On the fleet,
81
+ unchanged (still dark). The freshness sweep's activation is UNCHANGED for
82
+ everyone except that it no longer requires the second `egressAcknowledged` flag.
83
+ - **Migration:** existing dev agents have `cartographer.enabled:false` on disk; the
84
+ one-shot migration strips the default-shaped value so the gate decides. Run-once
85
+ marker prevents ever re-stripping an operator's later deliberate `false`. Never
86
+ touches the cost-bearing sweep — no surprise spend on update.
87
+ - **Security/cost:** no new egress path (the sweep was already the only egress, and
88
+ its activation conditions only got simpler, not broader — it still needs explicit
89
+ `enabled:true`). No new spend can be auto-armed. The new lint is deterministic,
90
+ reads one file, no network.
91
+ - **Reversibility:** fully reversible by reverting the commit. The migration is
92
+ additive (deletes a default-shaped key); reverting restores prior behavior on the
93
+ next update for agents that hadn't yet run it.
94
+ - **New failure modes:** none introduced. The lint's brace-in-string guard fails
95
+ LOUD (not silent) if an unhandled case is introduced — a safety improvement.