instar 1.3.956 → 1.3.957

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": "1.3.956",
3
+ "version": "1.3.957",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Generate a spec's IMPLEMENTATION CONTRACT — the normative sections only,
4
+ * with the review history stripped out.
5
+ *
6
+ * Why this exists (outbound-gate-advisory-override rounds 22/23/24): a spec that
7
+ * records its own review honestly accumulates change logs describing designs that
8
+ * were REVERSED. Both external reviewers independently named the same risk — an
9
+ * implementer cargo-culting a retired term out of a change log — and the same
10
+ * fix: publish the contract separately from the history.
11
+ *
12
+ * The rule is deliberately dumb and mechanical: everything up to the first
13
+ * history heading is the contract; everything from there on is history. A
14
+ * generator that had to understand the document would drift like the document.
15
+ *
16
+ * Usage:
17
+ * node scripts/generate-spec-contract.mjs --spec docs/specs/<slug>.md
18
+ * node scripts/generate-spec-contract.mjs --spec <path> --check # CI: fail if stale
19
+ */
20
+
21
+ import fs from 'node:fs';
22
+ import path from 'node:path';
23
+
24
+ /** A heading that begins the review-history half. Matched case-insensitively. */
25
+ const HISTORY_HEADING_RE = /^##\s+(?:\d+\.\s+)?(?:Round-\d+\b[^\n]*?(?:change log|hand-check|consistency sweep)|Appendix\b)/i;
26
+
27
+ /** Headings that are contract even though they sort after a history heading. */
28
+ const ALWAYS_CONTRACT_RE = /^##\s+(?:\d+\.\s+)?(?:Decision points touched|Open questions|Frontloaded Decisions|Dependencies|Honest limits|Multi-machine posture|What this does not do)/i;
29
+
30
+ /**
31
+ * Inline review annotations inside NORMATIVE prose — "(round-12, codex — ...)",
32
+ * "*(Round-25, codex: ...)*". They are provenance, not contract. Both external
33
+ * reviewers named them as the reason §§0-11 still read as history (rounds 24/25).
34
+ * Stripped from the generated contract; untouched in the source spec, where they
35
+ * are the record of why a decision is what it is.
36
+ *
37
+ * Deliberately conservative: only fully-delimited groups are removed, so a
38
+ * malformed annotation is left visible rather than silently eating prose.
39
+ */
40
+ const INLINE_ANNOTATION_RES = [
41
+ // *( Round-N ... )* — a complete italic aside
42
+ /\*\((?:round|rounds)[- ]\d+[^()*]*\)\*/gi,
43
+ // ( round-N, reviewer — ... ) — a complete parenthetical with no nesting
44
+ /\s*\((?:round|rounds)[- ]\d+[^()]*\)/gi,
45
+ // — round-N, codex … (an em-dash aside running to the end of a sentence)
46
+ /\s+—\s+(?:round|rounds)[- ]\d+,[^.;]*(?=[.;])/gi,
47
+ ];
48
+
49
+ export function stripInlineAnnotations(text) {
50
+ let out = text;
51
+ for (const re of INLINE_ANNOTATION_RES) out = out.replace(re, '');
52
+ // Collapse the double spaces a removal can leave mid-sentence.
53
+ return out.replace(/[ \t]{2,}/g, ' ').replace(/ +([.,;:])/g, '$1');
54
+ }
55
+
56
+ export function splitContract(markdown) {
57
+ const lines = markdown.split('\n');
58
+ const kept = [];
59
+ let inHistory = false;
60
+ let droppedSections = 0;
61
+ for (const line of lines) {
62
+ if (/^##\s/.test(line)) {
63
+ if (HISTORY_HEADING_RE.test(line) && !ALWAYS_CONTRACT_RE.test(line)) {
64
+ if (!inHistory) inHistory = true;
65
+ droppedSections++;
66
+ continue;
67
+ }
68
+ // A non-history H2 ends a history run (sections are not strictly ordered).
69
+ inHistory = false;
70
+ }
71
+ if (!inHistory) kept.push(line);
72
+ }
73
+ const body = stripInlineAnnotations(kept.join('\n')).replace(/\n{4,}/g, '\n\n\n');
74
+ return { contract: body, droppedSections };
75
+ }
76
+
77
+ /** The banner that makes the generated file unmistakable and un-editable-by-hand. */
78
+ function banner(specRel) {
79
+ return [
80
+ '<!-- GENERATED FILE — DO NOT EDIT.',
81
+ ` Source: ${specRel}`,
82
+ ' Regenerate: node scripts/generate-spec-contract.mjs --spec ' + specRel,
83
+ ' This is the IMPLEMENTATION CONTRACT: the normative design only.',
84
+ ' Review history (change logs, retired designs, reversed decisions) is',
85
+ ' deliberately absent — read the source spec for how the design got here.',
86
+ '-->',
87
+ '',
88
+ ].join('\n');
89
+ }
90
+
91
+ function main() {
92
+ const args = process.argv.slice(2);
93
+ const specIdx = args.indexOf('--spec');
94
+ if (specIdx === -1 || !args[specIdx + 1]) {
95
+ console.error('usage: generate-spec-contract.mjs --spec <path> [--check]');
96
+ process.exit(2);
97
+ }
98
+ const specPath = path.resolve(args[specIdx + 1]);
99
+ const check = args.includes('--check');
100
+ const specRel = path.relative(process.cwd(), specPath);
101
+
102
+ const markdown = fs.readFileSync(specPath, 'utf8');
103
+ const { contract, droppedSections } = splitContract(markdown);
104
+ const output = banner(specRel) + contract;
105
+
106
+ const slug = path.basename(specPath, '.md');
107
+ const outPath = path.join(path.dirname(specPath), 'generated', `${slug}.contract.md`);
108
+
109
+ if (check) {
110
+ if (!fs.existsSync(outPath)) {
111
+ console.error(`STALE: ${path.relative(process.cwd(), outPath)} does not exist. Run without --check.`);
112
+ process.exit(1);
113
+ }
114
+ const existing = fs.readFileSync(outPath, 'utf8');
115
+ if (existing !== output) {
116
+ console.error(
117
+ `STALE: ${path.relative(process.cwd(), outPath)} does not match ${specRel}.\n` +
118
+ 'The contract is generated — regenerate it rather than editing it.',
119
+ );
120
+ process.exit(1);
121
+ }
122
+ console.log(`OK: contract is current (${droppedSections} history sections excluded).`);
123
+ return;
124
+ }
125
+
126
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
127
+ fs.writeFileSync(outPath, output, 'utf8');
128
+ const pct = Math.round((1 - output.length / markdown.length) * 100);
129
+ console.log(
130
+ `wrote ${path.relative(process.cwd(), outPath)} — ${droppedSections} history sections excluded, ${pct}% smaller.`,
131
+ );
132
+ }
133
+
134
+ if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith('generate-spec-contract.mjs')) {
135
+ main();
136
+ }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-07-25T08:37:37.023Z",
5
- "instarVersion": "1.3.956",
4
+ "generatedAt": "2026-07-25T09:37:20.372Z",
5
+ "instarVersion": "1.3.957",
6
6
  "entryCount": 202,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,54 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Added `scripts/generate-spec-contract.mjs` — a build-time generator that emits
9
+ `docs/specs/generated/<slug>.contract.md` from a spec: the normative sections
10
+ only, with review-history sections and inline round-annotations stripped.
11
+
12
+ **Why.** A spec that honestly records its review accumulates change logs
13
+ describing designs that were later *reversed*. On
14
+ `outbound-gate-advisory-override` (33 review rounds), both external reviewers —
15
+ `codex-cli:gpt-5.5` and `gemini-cli:gemini-3.1-pro-preview` — independently
16
+ identified the same risk and proposed the same fix: an implementer reading the
17
+ document top-to-bottom can follow a retired design, so publish the contract
18
+ separately from the history.
19
+
20
+ `--check` fails the build when the committed contract does not match a fresh
21
+ generation, so the history cannot drift back into the contract over time.
22
+
23
+ The transform is deliberately mechanical — heading shapes mark history,
24
+ everything else is contract. A generator that had to *understand* the document
25
+ would drift out of step with it exactly as the document drifted out of step with
26
+ itself.
27
+
28
+ No runtime surface: no `src/` change, nothing installed to agent homes, no hook,
29
+ job or template references it. Nothing at runtime reads a spec.
30
+
31
+ ## What to Tell Your User
32
+
33
+ None — internal change (no user-facing surface).
34
+
35
+ ## Summary of New Capabilities
36
+
37
+ None — internal change (no user-facing surface).
38
+
39
+ ## Evidence
40
+
41
+ - Generated against the real 2,700-line spec: **36 history sections excluded,
42
+ 37% smaller output**.
43
+ - `--check` verified in both directions: exits 0 on a current contract, exits 1
44
+ with a regenerate message on a stale one.
45
+ - A regex leak was found and fixed during verification — `## 24. Round-11
46
+ external change log` was initially retained, because the heading text between
47
+ `Round-11` and `change log` was not matched. Caught by inspecting the generated
48
+ headings rather than trusting the run.
49
+ - Determinism confirmed by construction: no timestamps, no randomness, no
50
+ environment reads beyond the input path — which is what makes `--check` usable
51
+ in CI at all.
52
+ - Side-effects review: `upgrades/side-effects/spec-contract-generator.md`
53
+ (Tier 1; no decision point, no blocking authority, rollback is deleting two
54
+ files).
@@ -0,0 +1,126 @@
1
+ # Side-effects review — spec contract generator
2
+
3
+ **Change:** adds `scripts/generate-spec-contract.mjs`, a build-time generator that
4
+ produces `docs/specs/generated/<slug>.contract.md` from a spec: the normative
5
+ sections only, with review history and inline round-annotations stripped.
6
+
7
+ **Tier:** 1 (declared). New standalone dev-tooling script; no runtime surface, no
8
+ `src/` change, no decision point. Signal-only in the strongest sense — it emits a
9
+ derived document and can refuse a stale check; it gates no agent behavior.
10
+
11
+ **Why it exists:** on the `outbound-gate-advisory-override` spec, both external
12
+ reviewers (codex-cli, gemini-cli) independently identified the same risk across
13
+ several rounds — a spec that honestly records 33 rounds of review accumulates
14
+ change logs describing designs that were later **reversed**, and an implementer
15
+ reading top-to-bottom can follow a retired design. Both proposed the same fix:
16
+ publish the contract separately from the history.
17
+
18
+ ---
19
+
20
+ ## 1. Over-block — what legitimate inputs does this reject that it shouldn't?
21
+
22
+ The generator's only refusal is `--check` failing when the committed contract does
23
+ not byte-match a fresh generation. That is the intended behavior (CI staleness
24
+ gate), and it is refusable-by-regeneration, not by argument.
25
+
26
+ **Real over-block risk identified:** the history-heading regex could classify a
27
+ *normative* section as history if someone titles one `## Round-trip validation` or
28
+ similar. Mitigated by requiring both a `Round-N` prefix **and** one of
29
+ `change log` / `hand-check` / `consistency sweep`, plus an `ALWAYS_CONTRACT_RE`
30
+ allowlist for the known normative section names. A false positive drops a section
31
+ from the generated contract — visible immediately as a missing section, not
32
+ silent.
33
+
34
+ ## 2. Under-block — what does it still miss?
35
+
36
+ - **Narrative history inside normative prose.** The generator strips history
37
+ *sections* and *inline annotations*; it cannot strip a paragraph of normative
38
+ prose that happens to narrate how a decision evolved. Those remain in the
39
+ contract. Partially addressed by the spec's own §0.2 "current design overview";
40
+ fully addressing it would require judgment the generator deliberately does not
41
+ have.
42
+ - **It does not verify the contract is CORRECT** — only that it is current. A
43
+ spec that is internally contradictory generates a contract that is equally
44
+ contradictory. The separate spec lint (required by the outbound spec) is what
45
+ addresses that; this is not it.
46
+
47
+ ## 3. Level-of-abstraction fit
48
+
49
+ Correct layer: it is a build-time document transform, alongside the other
50
+ `scripts/*.mjs` spec tooling (`write-convergence-tag.mjs`,
51
+ `eli16-overview-check.mjs`, `publish-spec-review.mjs`). It does not belong in
52
+ `src/` — nothing at runtime reads a spec.
53
+
54
+ **Should a higher layer own it?** `/spec-converge` could invoke it automatically
55
+ on convergence. Deliberately not wired that way in this change: the generator
56
+ should prove itself as a standalone tool first, and auto-invocation is a change to
57
+ the skill's contract, not to this script. Not deferred-and-forgotten — it is
58
+ simply not part of this change's scope, and the script is useful without it.
59
+
60
+ ## 4. Signal vs authority compliance
61
+
62
+ **Compliant, trivially.** The script holds no authority over agent behavior. Its
63
+ one enforcement surface (`--check` exiting non-zero) is a build-time staleness
64
+ assertion on a *derived artifact*, with a deterministic remedy (regenerate). It
65
+ makes no judgment, consults no model, and gates no message, session, or action.
66
+
67
+ ## 5. Interactions
68
+
69
+ - **Shadows nothing.** No existing script generates or validates a spec contract.
70
+ - **Shadowed by nothing.** It reads a spec and writes a derived file under
71
+ `docs/specs/generated/`, a path nothing else writes.
72
+ - **No double-fire, no race.** Single-shot CLI; no daemon, no watcher, no
73
+ scheduling.
74
+ - **Adjacent tooling unaffected:** `write-convergence-tag.mjs` and
75
+ `eli16-overview-check.mjs` read the *source* spec; this writes only the
76
+ generated copy. `hashSpecReviewableBody` (used for cross-model delta-gating)
77
+ also reads the source, so generated files never affect review gating.
78
+
79
+ ## 6. External surfaces
80
+
81
+ - **Other agents / users:** none. Dev tooling in the instar repo; not installed to
82
+ agent homes, not referenced by any hook, job, or template.
83
+ - **Timing / runtime conditions:** none — pure file in, file out.
84
+ - **New file paths introduced:** `docs/specs/generated/`. Additive; no existing
85
+ path changes meaning.
86
+
87
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
88
+
89
+ **Machine-local BY DESIGN, and trivially so:** the script runs in a developer's
90
+ checkout and writes a file into that checkout, which is then committed and
91
+ distributed by git like any other source file. There is no runtime state, no
92
+ per-machine store, no notice, and no generated URL. Running it on two machines
93
+ against the same spec produces byte-identical output (the transform is pure and
94
+ deterministic — no timestamps, no randomness, no environment reads beyond the
95
+ input path).
96
+
97
+ That determinism is what makes the `--check` mode usable in CI at all, and it is
98
+ asserted by the mode's existence: if the transform were machine-sensitive, CI
99
+ would fail against a contract generated on a developer's machine.
100
+
101
+ ## 8. Rollback cost
102
+
103
+ **Near zero.** Delete the script and the generated directory; nothing depends on
104
+ either. No release, no migration, no agent-state repair. If the `--check` mode
105
+ turns out to be a nuisance in CI, it can be dropped independently of the
106
+ generator (the generator is useful without the check; the check is not useful
107
+ without the generator).
108
+
109
+ ---
110
+
111
+ ## Verification performed
112
+
113
+ - Generated against the real 2,700-line spec: 36 history sections excluded, 37%
114
+ smaller output.
115
+ - `--check` verified in both directions: passes on a current contract, fails with
116
+ a clear message on a stale one.
117
+ - Regex leak found and fixed during verification (`## 24. Round-11 external
118
+ change log` was initially retained because the heading text between `Round-11`
119
+ and `change log` was not matched) — caught by inspecting the generated
120
+ headings rather than trusting the run.
121
+
122
+ ## Second pass
123
+
124
+ **Not required.** The change touches no block/allow decision, no session
125
+ lifecycle, no coherence gate, and nothing named sentinel/guard/gate/watchdog. It
126
+ is a standalone document transform.