instar 1.3.1012 → 1.3.1013

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.1012",
3
+ "version": "1.3.1013",
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",
@@ -345,44 +345,57 @@ if (!process.env.CI) {
345
345
  // isn't what this PR is changing. Falls back to the versioned guide when no
346
346
  // fragment / NEXT.md is staged (post-release-cut state).
347
347
  const inFlight = assembledContent !== null;
348
- const guideContent = inFlight
349
- ? assembledContent
350
- : (versionedGuideExists ? fs.readFileSync(versionedGuidePath, 'utf-8') : null);
351
- if (guideContent !== null) {
348
+
349
+ // This check is a RELEASE-LEVEL re-check on the notes THIS PUSH is shipping —
350
+ // which means it only has a subject when in-flight notes exist. It deliberately
351
+ // does NOT fall back to the versioned guide.
352
+ //
353
+ // Why (three recurrences, 2026: v1.3.492, v1.3.802, v1.3.1009): the release cut
354
+ // renames upgrades/next/*.md into upgrades/<version>.md and bumps package.json,
355
+ // but side-effects artifacts are named per CHANGE SLUG, never per version — so
356
+ // upgrades/side-effects/<version>.md is a file the release flow never creates.
357
+ // Falling back to the frozen guide therefore demanded a filename that cannot
358
+ // exist, on EVERY push from a clean post-release tree, for a release that had
359
+ // already shipped and already been reviewed.
360
+ //
361
+ // The damage was not the refusal, it was the REMEDY the message named: three
362
+ // separate times someone hand-wrote a placeholder upgrades/side-effects/<version>.md
363
+ // to get past it (see 1.3.492.md and 1.3.802.md, both of which say so in their
364
+ // own text). A gate whose advice is unfollowable teaches people to write junk
365
+ // that satisfies it.
366
+ //
367
+ // Nothing is weakened. Per-change enforcement lives in the pre-COMMIT gate
368
+ // (scripts/instar-dev-precommit.js — refuses in-scope staged files without an
369
+ // ELI16 + side-effects artifact), and check 3b above already refuses a
370
+ // release-relevant push that ships no fragment, with an actionable remedy.
371
+ if (inFlight) {
352
372
  // Extract "## What Changed" section
353
- const whatChangedMatch = guideContent.match(/## What Changed\s*([\s\S]*?)(?=\n##\s|$)/);
373
+ const whatChangedMatch = assembledContent.match(/## What Changed\s*([\s\S]*?)(?=\n##\s|$)/);
354
374
  const whatChanged = whatChangedMatch ? whatChangedMatch[1] : '';
355
375
 
356
376
  const qualifies = FIX_PATTERNS.some((p) => p.test(whatChanged));
357
377
 
358
378
  if (qualifies) {
359
379
  const sideEffectsDir = path.join(ROOT, 'upgrades', 'side-effects');
360
- // When in-flight notes (fragments/NEXT.md) drive the push, any fresh
361
- // artifact (last 24h) counts — the versioned-filename requirement only
362
- // applies when a versioned guide is being validated without in-flight notes.
363
- const artifactName = (!inFlight && versionedGuideExists) ? `${version}.md` : null;
364
380
  let artifactFound = false;
365
381
 
366
382
  if (fs.existsSync(sideEffectsDir)) {
367
383
  const files = fs.readdirSync(sideEffectsDir).filter((f) => f.endsWith('.md'));
368
- if (artifactName) {
369
- artifactFound = files.includes(artifactName);
370
- } else {
371
- // For in-flight notes (fragments/NEXT.md), any fresh artifact from the
372
- // last 24h counts. The expectation is that during release cut, the
373
- // fragment/NEXT.md -> <version>.md rename pairs with an artifact rename.
374
- const recent = files.filter((f) => {
375
- const stat = fs.statSync(path.join(sideEffectsDir, f));
376
- return Date.now() - stat.mtimeMs < 24 * 60 * 60 * 1000;
377
- });
378
- artifactFound = recent.length > 0;
379
- }
384
+ // Any fresh artifact from the last 24h counts — the in-flight notes name
385
+ // the change, the artifact reviews it, and the two are paired by the PR
386
+ // rather than by filename.
387
+ const recent = files.filter((f) => {
388
+ const stat = fs.statSync(path.join(sideEffectsDir, f));
389
+ return Date.now() - stat.mtimeMs < 24 * 60 * 60 * 1000;
390
+ });
391
+ artifactFound = recent.length > 0;
380
392
  }
381
393
 
382
394
  if (!artifactFound) {
383
395
  errors.push(
384
- `Upgrade notes claim a fix/feature but no matching side-effects review artifact found in upgrades/side-effects/. ` +
385
- `Every change qualifying for review must ship with an artifact produced via the /instar-dev skill. ` +
396
+ `Your release-note fragment claims a fix/feature but no side-effects review artifact was written in the last 24h. ` +
397
+ `Add upgrades/side-effects/<slug>.md for this change (produced via the /instar-dev skill) ` +
398
+ `do NOT create a version-named file to satisfy this check. ` +
386
399
  `See skills/instar-dev/SKILL.md and docs/signal-vs-authority.md.`
387
400
  );
388
401
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-07-27T19:56:42.745Z",
5
- "instarVersion": "1.3.1012",
4
+ "generatedAt": "2026-07-27T20:30:23.365Z",
5
+ "instarVersion": "1.3.1013",
6
6
  "entryCount": 202,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,55 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Fixes the pre-push gate's side-effects-artifact check, which refused **every push from a clean
9
+ post-release tree** and named an impossible remedy when it did.
10
+
11
+ After a release cut there are no in-flight release-note fragments, so the check fell back to
12
+ `upgrades/<version>.md` — the guide for the version that already shipped — and demanded
13
+ `upgrades/side-effects/<version>.md`. Side-effects artifacts are named per change slug; the release
14
+ flow has never produced a version-named one, so the demand could not be satisfied.
15
+
16
+ The check is now scoped to the notes the push is actually shipping. With no notes in flight there is
17
+ nothing to check and it stays silent. With notes in flight it fires exactly as before.
18
+
19
+ ## What to Tell Your User
20
+
21
+ None — internal change (no user-facing surface).
22
+
23
+ ## Summary of New Capabilities
24
+
25
+ None — internal change (no user-facing surface).
26
+
27
+ ## Evidence
28
+
29
+ **Three observed recurrences, self-documented in the repo.** `upgrades/side-effects/1.3.492.md` and
30
+ `1.3.802.md` are hand-written placeholders whose own text says they exist only to satisfy this
31
+ check; the second calls itself "its second observed recurrence". Today (v1.3.1009) was the third,
32
+ and it refused a sibling branch before diagnosis.
33
+
34
+ Both placeholders claim the gap "remains logged in the framework-issues ledger under dedupKey
35
+ `pre-push-gate-versioned-artifact-fallback`". It is not. Queried four ways — unfiltered,
36
+ `?status=fixed`, `?bucket=instar-integration-gap`, `?framework=instar` — the ledger holds 163 issues
37
+ and zero matches, with no dedupKey containing `release`, `artifact`, or `push`. Declared tracked,
38
+ never tracked, recurred. It is registered for real with this change.
39
+
40
+ **Verified by reverting, because a passing test proves nothing:**
41
+
42
+ | gate | result |
43
+ |---|---|
44
+ | OLD code, new tests | **3 failed** \| 16 passed (19) |
45
+ | NEW code | **19 passed** (19) |
46
+
47
+ Plus a live run against the real repo in its actual post-cut state: errors before, warnings only
48
+ after.
49
+
50
+ **Nothing is weakened, and this was the load-bearing question.** Per-change enforcement still lives
51
+ in the pre-COMMIT gate (which refuses in-scope staged files lacking an ELI16 + side-effects artifact,
52
+ and blocked this very change twice while it was being written); check 3b still refuses a
53
+ release-relevant push with no fragment; and check 5 still fires when in-flight notes claim a fix with
54
+ no fresh artifact — asserted by a dedicated negative-control test, since the easy mistake here is
55
+ trading a false positive for a false negative.
@@ -0,0 +1,78 @@
1
+ # Side-effects review — pre-push gate stops validating the frozen versioned guide
2
+
3
+ **Change:** `scripts/pre-push-gate.js` check 5 (side-effects-artifact requirement) is scoped to
4
+ in-flight release notes. It no longer falls back to `upgrades/<version>.md` — the guide for the
5
+ version that already shipped.
6
+
7
+ **Tier:** 1. One local developer gate. No `src/` surface, no route, no config, no persisted state,
8
+ no migration. Not run in CI (`if (!process.env.CI)`), so it cannot change what merges — only what a
9
+ developer can push from their own machine.
10
+
11
+ ## The defect, with its recurrence record
12
+
13
+ Post-release-cut the tree has `upgrades/<version>.md` (frozen), no fragments, and no
14
+ `upgrades/side-effects/<version>.md`. That last file is one the release flow never creates: artifacts
15
+ are named per change slug. The fallback demanded it anyway, so **every push from a clean
16
+ post-release tree was refused**.
17
+
18
+ Three observed recurrences, all self-documented in the repo:
19
+
20
+ | version | evidence |
21
+ |---|---|
22
+ | v1.3.492 | `upgrades/side-effects/1.3.492.md` — a hand-written placeholder that says so |
23
+ | v1.3.802 | `upgrades/side-effects/1.3.802.md` — same, and calls itself "its second observed recurrence" |
24
+ | v1.3.1009 | today; refused this branch's sibling PR before diagnosis |
25
+
26
+ Both placeholders state the gap "remains logged in the framework-issues ledger under dedupKey
27
+ `pre-push-gate-versioned-artifact-fallback`". **It is not.** Queried four ways
28
+ (`GET /framework-issues` unfiltered, `?status=fixed`, `?bucket=instar-integration-gap`,
29
+ `?framework=instar`): 163 issues, zero matches, and no dedupKey in the store containing `release`,
30
+ `artifact`, or `push`. The tracking claim was hollow, which is why it recurred a third time. The
31
+ issue is registered for real as part of this change.
32
+
33
+ ## What is NOT weakened — the load-bearing question
34
+
35
+ Removing a check is the risky half of this. Three separate enforcement points remain:
36
+
37
+ 1. **`scripts/instar-dev-precommit.js` — untouched.** This is the real per-change enforcement: it
38
+ refuses a COMMIT whose in-scope staged files lack an ELI16 doc and a side-effects artifact, and
39
+ it verifies the artifact's sha against the decision trace. It blocked this very change twice
40
+ while it was being written.
41
+ 2. **Check 3b — untouched.** A push with release-relevant files and no fragment is still refused,
42
+ with an actionable remedy (`add upgrades/next/<slug>.md`).
43
+ 3. **Check 5 itself — still fires, scoped.** With in-flight notes claiming a fix and no fresh
44
+ artifact, it refuses exactly as before. Test:
45
+ `STILL refuses an in-flight fix-claiming fragment with no fresh side-effects artifact`.
46
+
47
+ The narrowed case — "no in-flight notes, frozen guide claims a fix, no version-named artifact" — was
48
+ never a real signal. It fired on a release that had already shipped and already been reviewed.
49
+
50
+ ## Blast radius
51
+
52
+ A developer can now push from a clean post-release tree without hand-writing a placeholder. That is
53
+ the entire behavioural change. Nothing in CI, publishing, or runtime reads this script.
54
+
55
+ **Residual risk:** if the release process ever DID start producing version-named artifacts and
56
+ someone relied on this check to enforce that, this would silently stop enforcing it. Judged
57
+ acceptable: no such producer exists, and the 37 version-named artifacts already in the repo are
58
+ release rollups and placeholders, not a maintained convention.
59
+
60
+ ## Verification
61
+
62
+ Verified by REVERTING, because a passing test proves nothing:
63
+
64
+ ```
65
+ against the OLD gate: 3 failed | 16 passed (19)
66
+ × ACCEPTS a post-release-cut tree with no fragment and no version-named artifact
67
+ × does not demand a version-named side-effects artifact even when one has never existed
68
+ × STILL refuses an in-flight fix-claiming fragment with no fresh side-effects artifact
69
+ against the NEW gate: 19 passed (19)
70
+ ```
71
+
72
+ Plus a live run against the real repo in its actual post-cut state (v1.3.1009, no fragments): errors
73
+ before, warnings only after.
74
+
75
+ ## Rollback
76
+
77
+ `git revert`. The gate returns to refusing every clean post-release push, and the next person writes
78
+ `upgrades/side-effects/<version>.md` by hand for the fourth time.