instar 0.28.111 → 0.28.112

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "0.28.111",
3
+ "version": "0.28.112",
4
4
  "description": "Persistent autonomy infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,6 +29,7 @@ import crypto from 'node:crypto';
29
29
  import { execSync } from 'node:child_process';
30
30
  import { fileURLToPath } from 'node:url';
31
31
  import { checkEli16Overview } from './eli16-overview-check.mjs';
32
+ import { verifyProposalDerivedRunbooks } from '../skills/instar-dev/scripts/verify-proposal-derived-runbook.mjs';
32
33
 
33
34
  const __filename = fileURLToPath(import.meta.url);
34
35
  const __dirname = path.dirname(__filename);
@@ -329,10 +330,39 @@ if (staged.includes(spec) && !staged.includes(eli16Rel)) {
329
330
  );
330
331
  }
331
332
 
333
+ // ─── Step 8: proposal-derived runbook gate (S-3) ─────────────────────────
334
+ // Per SELF-HEALING-REMEDIATOR-V2-SPEC §A11/§A22/§A32, runbook source files
335
+ // emitted by the SystemReviewer proposal pipeline must:
336
+ // 1. Reference a proposal that actually exists in
337
+ // .instar/remediation/proposals-*/<id>.json on this checkout, AND
338
+ // 2. Carry a __producingAgentId const matching the proposal's
339
+ // producingAgentId field.
340
+ // The CI gate (C-1) re-verifies at PR-merge time with full fleet visibility.
341
+ // This commit-time gate catches author mistakes before the PR is pushed.
342
+
343
+ const promotionGateResult = verifyProposalDerivedRunbooks({
344
+ repoRoot: ROOT,
345
+ files: staged,
346
+ });
347
+ if (!promotionGateResult.ok) {
348
+ blockCommit(
349
+ inScopeFiles,
350
+ [
351
+ 'Proposal-derived runbook gate refused this commit:',
352
+ ` ${promotionGateResult.reason}`,
353
+ '',
354
+ 'See SELF-HEALING-REMEDIATOR-V2-SPEC.md §A11, §A22, §A32 for the rationale.',
355
+ 'The proposal pipeline (Tier-3 S-1) must emit __proposalDerivedFrom +',
356
+ '__producingAgentId together, and the matching proposal JSON must be',
357
+ 'present at .instar/remediation/proposals-<machineId>/<id>.json.',
358
+ ].join('\n'),
359
+ );
360
+ }
361
+
332
362
  // ─── Pass ────────────────────────────────────────────────────────────────
333
363
 
334
364
  console.error(
335
- `[instar-dev-precommit] OK — trace ${path.basename(validTrace.entry.file)} covers ${inScopeFiles.length} in-scope file(s), artifact ${validTrace.trace.artifactPath} verified, spec ${spec} is converged + approved, ELI16 overview ${eli16Rel} present (${eli16Result.charCount} chars).`,
365
+ `[instar-dev-precommit] OK — trace ${path.basename(validTrace.entry.file)} covers ${inScopeFiles.length} in-scope file(s), artifact ${validTrace.trace.artifactPath} verified, spec ${spec} is converged + approved, ELI16 overview ${eli16Rel} present (${eli16Result.charCount} chars), promotion-gate: ${promotionGateResult.reason}.`,
336
366
  );
337
367
  process.exit(0);
338
368
 
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-05-16T18:51:56.869Z",
5
- "instarVersion": "0.28.111",
4
+ "generatedAt": "2026-05-16T18:53:49.177Z",
5
+ "instarVersion": "0.28.112",
6
6
  "entryCount": 188,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,28 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ Ships S-3 of Tier-3 from the self-healing remediator spec — the **commit-time** half of the promotion gate that pairs with the **PR-merge-time** half shipped in C-1 (PR #204).
8
+
9
+ A "proposal-derived runbook" is a runbook source file that the SystemReviewer's clustering pipeline (Tier-3 S-1, not yet shipped) emits when it spots a repeating failure pattern that warrants a new automated remediation. Those files carry two special markers: `__proposalDerivedFrom = '<proposalId>'` and `__producingAgentId = '<agentId>'`. Before this release, nothing checked those markers locally — the only enforcement happened in CI at PR review.
10
+
11
+ This release adds a second check at commit time, inside the `/instar-dev` skill's pre-commit hook:
12
+
13
+ - If you stage a runbook source under `src/remediation/runbooks/` that carries `__proposalDerivedFrom`, the hook reads the proposal JSON at `.instar/remediation/proposals-<machineId>/<proposalId>.json`, verifies the agent IDs match, and (if a signature and pubkey are present) verifies the Ed25519 signature.
14
+ - If anything is missing or mismatched, the commit is refused with a structured message pointing at the proposal pipeline as the responsible surface.
15
+ - Runbooks without the markers — every runbook on main today — pass through with no work.
16
+
17
+ This catches author mistakes before a PR is pushed. The CI gate (C-1) remains the authoritative check at PR-merge time because only CI can verify signing identity for the different-principal rule.
18
+
19
+ ## What to Tell Your User
20
+
21
+ No behavior change for users. This is a developer-side guardrail that prevents a malformed runbook from being committed in the first place. The user-visible promotion flow (proposal review and approval via the dashboard or Telegram) still works the same way.
22
+
23
+ ## Summary of New Capabilities
24
+
25
+ | Capability | How to Use |
26
+ |-----------|-----------|
27
+ | Commit-time promotion gate for proposal-derived runbooks | Automatic; runs inside `/instar-dev` pre-commit |
28
+ | `verify-proposal-derived-runbook.mjs` CLI | `node skills/instar-dev/scripts/verify-proposal-derived-runbook.mjs --files <csv>` |
@@ -0,0 +1,111 @@
1
+ # Side-Effects Review — S-3: Commit-time promotion gate for proposal-derived runbooks
2
+
3
+ **Version / slug:** `s3-promotion-gate`
4
+ **Date:** `2026-05-16`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required`
7
+
8
+ ## Summary of the change
9
+
10
+ Ships S-3 of Tier-3 from `docs/specs/SELF-HEALING-REMEDIATOR-V2-SPEC.md` — the **commit-time** promotion gate that pairs with C-1's PR-merge-time CI gate.
11
+
12
+ Per A11 / A22 / A32 / A41 / A48 / A57 Tier-3, when the `/instar-dev` skill commits a runbook source file under `src/remediation/runbooks/*.{ts,js}` that declares `__proposalDerivedFrom = '<proposalId>'`, the commit MUST:
13
+
14
+ 1. Carry a matching `__producingAgentId = '<agentId>'` const in the same source file.
15
+ 2. Reference a proposal that exists at `.instar/remediation/proposals-<machineId>/<proposalId>.json` in the current checkout.
16
+ 3. Have a proposal whose `producingAgentId` field matches the runbook's `__producingAgentId` annotation.
17
+ 4. (When pubkey is bundled and proposal carries a signature) verify the proposal's `producingAgentIdSignature` against `.instar/remediation/agent-pubkeys/<agentId>.pem`.
18
+
19
+ Otherwise the pre-commit hook refuses the commit with a structured error.
20
+
21
+ The gate is **additive** — runbook source files without `__proposalDerivedFrom` (i.e., the existing hand-authored runbooks W-1..W-7) pass through with zero work. Files outside `src/remediation/runbooks/` are not inspected.
22
+
23
+ This is the **commit-time** half. C-1 (shipped in PR #204, SHA `780b2659`) is the **PR-merge-time** half — it adds different-principal verification (GPG/sigstore OR Telegram countersignature) at PR review. Both are required per spec: this catches author mistakes before the PR is pushed; C-1 is authoritative because it has fleet-wide visibility.
24
+
25
+ Files touched:
26
+ - `skills/instar-dev/scripts/verify-proposal-derived-runbook.mjs` (new) — pure-Node verifier (no deps beyond `node:crypto`/`node:fs`/`node:path`). Exports `verifyProposalDerivedRunbooks`, `inspectRunbook`, `findProposalJson`, `verifyProposalSignature`. CLI mode at `--files <csv>`.
27
+ - `scripts/instar-dev-precommit.js` — adds Step 8 calling the verifier on the staged file list. Existing Steps 0–7 (merge skip, staged inspect, classify, bootstrap, fresh-trace, trace validation, spec tags, ELI16) unchanged.
28
+ - `skills/instar-dev/SKILL.md` — Phase 0 documentation extended with the new gate's preconditions.
29
+ - `tests/unit/verify-proposal-derived-runbook.test.ts` (new) — 12 cases covering the 6 spec scenarios plus edge cases.
30
+ - `upgrades/NEXT.md` — S-3 entry appended; existing release-notes preserved.
31
+
32
+ ## Decision-point inventory
33
+
34
+ - `inspectRunbook(repoRoot, relPath)` — **add** — reads each touched runbook source, regex-detects `__proposalDerivedFrom` and `__producingAgentId` consts. Pure structural detection (single regex per marker). Not a content classifier; it does not parse arbitrary TypeScript.
35
+ - `findProposalJson(repoRoot, proposalId)` — **add** — scans `.instar/remediation/proposals-*` directories for `<proposalId>.json`. Reads via `fs.readdirSync` + `fs.existsSync` to avoid pulling unrelated history (A48). Malformed JSON treated as not-found.
36
+ - `verifyProposalSignature(repoRoot, proposal)` — **add** — Ed25519 verify over canonical `proposalId=...\nproducingAgentId=...\nemittedAt=...` payload. Returns `'ok' | 'no-signature' | 'no-pubkey' | 'invalid'`. Only `'invalid'` blocks the commit; `'no-signature'` and `'no-pubkey'` defer to C-1 (proposal pipeline and pubkey distribution are downstream infra).
37
+ - `verifyProposalDerivedRunbooks({repoRoot, files})` — **add** — top-level decision tree: filter to runbook source paths → inspect each → presence checks → proposal lookup → agent-id match → signature verify. Short-circuits on first failure.
38
+ - `scripts/instar-dev-precommit.js` Step 8 — **add** — calls the verifier on the full staged file list (not just `inScopeFiles`, because runbooks under `src/remediation/runbooks/` are already in scope; passing the full list is a no-op for non-runbook files).
39
+ - `SKILL.md` Phase 0 — **extend** — documents the new gate's preconditions so the agent knows the contract before producing a runbook.
40
+
41
+ ## Over-block / under-block analysis
42
+
43
+ **Under-block risks (false PASS):**
44
+ - An author hand-writes `__proposalDerivedFrom = 'real-proposal'` + `__producingAgentId = 'self'` matching a legitimate-on-disk proposal that they themselves authored. The S-3 gate passes (the proposal exists, IDs match). The defense-in-depth is C-1: at PR-merge time the different-principal check refuses a PR whose signing identity matches the proposal's `producingAgentId`. This is the spec-intended layering — S-3 catches typos; C-1 catches collusion.
45
+ - A proposal without a `producingAgentIdSignature` field passes the signature step. This is by design — early proposals predate signing infra; the C-1 CI gate will surface this at PR time once the proposal pipeline lands (Tier-3 S-1).
46
+ - A proposal with a signature but no bundled pubkey at `.instar/remediation/agent-pubkeys/<agentId>.pem` passes locally. Pubkey distribution is fleet infrastructure that ships separately; C-1 has the authoritative pubkey set.
47
+
48
+ **Over-block risks (false BLOCK):**
49
+ - A hand-authored runbook (W-1..W-7) that gains a `__proposalDerivedFrom` annotation in the future without a matching proposal would block at commit-time. Mitigation: the annotation is only ever emitted by the SystemReviewer proposal pipeline (S-1, not yet shipped). Hand-authored runbooks never carry the const.
50
+ - Renaming or relocating proposals (e.g., archival to `proposals-archived-<machineId>/` per the spec's rollback line 5) would orphan the runbook reference. Mitigation: archival happens during nuclear uninstall only; runbook source is also archived in the same rollback step.
51
+ - File-encoding edge cases (BOMs, CRLF, unusual quoting) could make the regex miss the const. Mitigation: the regex tolerates single/double/backtick quoting and any of `const|let|var` with optional `export`; tests pin the byte shape.
52
+
53
+ **Bootstrap exception not needed.** The existing `BOOTSTRAP_TRIGGERS` list (`scripts/instar-dev-precommit.js:105`) covers the precommit script's own first-ship and `/spec-converge`. The new gate is additive — staged files with no `__proposalDerivedFrom` (which includes everything in this S-3 PR) pass through harmlessly.
54
+
55
+ ## Level-of-abstraction fit
56
+
57
+ - The verifier sits in `skills/instar-dev/scripts/` because it is part of the `/instar-dev` skill's enforcement surface — it has no caller outside the precommit hook. Co-locating it with `write-trace.mjs` keeps the skill's structural pieces together.
58
+ - The precommit hook adds Step 8 (after spec-tag + ELI16 checks) because the proposal-derivation check is the most expensive (fs scan of `.instar/remediation/proposals-*`). Running it last means cheap structural checks reject first.
59
+ - C-1 lives in CI because PR-merge-time needs different-principal verification (commit signing identity), which only the CI environment can determine authoritatively. S-3 doesn't need that — it only verifies the runbook + proposal shape.
60
+
61
+ ## Signal vs authority compliance
62
+
63
+ - The verifier is a **signal-producer with structural authority** — it makes a deterministic, byte-level structural check (regex match + JSON parse + Ed25519 verify), not a content judgment. Signal-vs-authority's "brittle filters don't get blocking authority" rule is for content classifiers (NLP, heuristics). Structural pre-commit gates are the canonical pattern for authority bound to verifiable invariants — same shape as the existing spec-tag and ELI16 checks.
64
+ - The "intelligent gate" layer above this is the user reviewing the side-effects artifact and PR diff. The structural gate ensures runbook source carries the metadata the intelligent gate needs to make its decision.
65
+
66
+ ## Interactions
67
+
68
+ - **Existing precommit Steps 0–7:** the new Step 8 runs AFTER trace validation, AFTER spec-tag verification, AFTER ELI16. It uses `staged` (not `inScopeFiles`) because runbooks under `src/remediation/runbooks/` are already in `inScopeFiles` per `inScope()`; passing `staged` keeps the API caller-agnostic.
69
+ - **C-1 CI gate:** complementary. S-3 verifies the runbook↔proposal binding locally; C-1 verifies signer-identity + Telegram countersignature at PR-merge time. They share the `__proposalDerivedFrom` const as the trigger marker.
70
+ - **F-5 TrustElevationSource (PR #203):** the trust-elevation logic at the API layer decides whether a high-blast-radius runbook may run. S-3 is at a different layer (source-control), so no interaction.
71
+ - **F-7 PostUpdateMigrator + AnnouncementManager (PR #210):** unaffected. Promotion events aren't yet wired into AnnouncementManager (deferred to a follow-up that ships once S-1's proposal pipeline lands).
72
+ - **Bootstrap exception list:** unchanged. The new gate's own first-ship doesn't add a runbook with `__proposalDerivedFrom`, so it passes through Step 8 naturally.
73
+
74
+ ## External surfaces
75
+
76
+ - New filesystem paths read (none written): `.instar/remediation/proposals-*/<id>.json`, `.instar/remediation/agent-pubkeys/<agentId>.pem`. Both are spec-mandated locations from §A14 / §A20.
77
+ - No new network calls, no new env-var reads, no new IPC.
78
+ - The script is pure-Node ESM — same runtime as the precommit hook. No new runtime dependency.
79
+ - CLI surface: `--files <csv>` + `--repo-root <path>` only. No interactive prompts.
80
+
81
+ ## Rollback cost
82
+
83
+ If S-3 turns out wrong in production:
84
+
85
+ - **Hot-fix path:** revert the import in `scripts/instar-dev-precommit.js` (one line). The verifier script and tests can stay; they're inert without the precommit wire-up.
86
+ - **Full revert:** remove the new script, the test file, the SKILL.md addendum, the precommit import + Step 8 block. `git revert` is sufficient; no data migration.
87
+ - **In-flight runbook PRs:** zero impact. The gate is additive; runbooks without `__proposalDerivedFrom` (every runbook on main today) pass through.
88
+ - **No agent-state repair needed.** The gate writes no state.
89
+
90
+ ## Test coverage
91
+
92
+ `tests/unit/verify-proposal-derived-runbook.test.ts` — 12 cases:
93
+
94
+ 1. Runbook without `__proposalDerivedFrom` → passes through.
95
+ 2. Runbook with `__proposalDerivedFrom` matching existing proposal + agent id → passes.
96
+ 3. Runbook with `__proposalDerivedFrom` but no `__producingAgentId` → fails.
97
+ 4. Runbook with `__proposalDerivedFrom` pointing to non-existent proposal → fails.
98
+ 5. Runbook with `__producingAgentId` whose signature doesn't verify → fails.
99
+ 6. Multiple runbooks, first failure short-circuits.
100
+ 7. Producing-agent-id mismatch between runbook and proposal → fails.
101
+ 8. Proposal exists but lacks `producingAgentId` field → fails.
102
+ 9. No pubkey on disk → passes (defers to CI gate).
103
+ 10. Proposal without signature field → passes (signature optional at commit-time).
104
+ 11. Non-runbook paths in file list are ignored.
105
+ 12. Proposal stored under a different `machineId` directory still resolves.
106
+
107
+ Tests use real Ed25519 keypairs (`crypto.generateKeyPairSync`), real fs scaffolding in `os.tmpdir()`, and `SafeFsExecutor.safeRmSync` for teardown.
108
+
109
+ ## Concur or concern
110
+
111
+ No second-pass required: the change is purely additive, structural, and dual-gated (S-3 here + C-1 in CI). The 12-test suite pins the byte-shape and decision tree.