instar 0.28.104 → 0.28.105

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.104",
3
+ "version": "0.28.105",
4
4
  "description": "Persistent autonomy infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -13,6 +13,7 @@
13
13
  import fs from 'node:fs';
14
14
  import path from 'node:path';
15
15
  import { fileURLToPath } from 'node:url';
16
+ import { validateGuideContent } from './upgrade-guide-validator.mjs';
16
17
 
17
18
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
19
  const ROOT = path.resolve(__dirname, '..');
@@ -21,14 +22,15 @@ const nextPath = path.join(ROOT, 'upgrades', 'NEXT.md');
21
22
  const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf-8'));
22
23
  const version = pkg.version;
23
24
 
25
+ // Section list kept here only for the "no upgrade guide found" error
26
+ // message. The full per-section validation now flows through
27
+ // validateGuideContent (which has its own canonical REQUIRED_SECTIONS).
24
28
  const REQUIRED_SECTIONS = [
25
29
  '## What Changed',
26
30
  '## What to Tell Your User',
27
31
  '## Summary of New Capabilities',
28
32
  ];
29
33
 
30
- const MIN_LENGTH = 200;
31
-
32
34
  let errors = [];
33
35
  let warnings = [];
34
36
 
@@ -38,39 +40,28 @@ const versionedGuidePath = path.join(upgradesDir, `${version}.md`);
38
40
  const versionedGuideExists = fs.existsSync(versionedGuidePath);
39
41
  const nextExists = fs.existsSync(nextPath);
40
42
 
41
- if (versionedGuideExists) {
42
- // Already finalized — validate the versioned guide instead
43
- const content = fs.readFileSync(versionedGuidePath, 'utf-8');
44
- for (const section of REQUIRED_SECTIONS) {
45
- if (!content.includes(section)) {
46
- errors.push(`${version}.md missing "${section}" section`);
47
- }
48
- }
49
- if (content.length < MIN_LENGTH) {
50
- errors.push(`${version}.md is too short (${content.length} chars, need ${MIN_LENGTH}+)`);
51
- }
52
- } else if (nextExists) {
53
- const content = fs.readFileSync(nextPath, 'utf-8');
54
-
55
- for (const section of REQUIRED_SECTIONS) {
56
- if (!content.includes(section)) {
57
- errors.push(`NEXT.md missing "${section}" section`);
58
- }
59
- }
60
-
61
- if (content.length < MIN_LENGTH) {
62
- errors.push(`NEXT.md is too short (${content.length} chars, need ${MIN_LENGTH}+)`);
63
- }
64
-
65
- // Check for unfilled template placeholders
66
- if (content.includes('<!-- Describe what changed')) {
67
- errors.push(`NEXT.md "What Changed" still has template placeholder`);
68
- }
69
- if (content.includes('[Feature name]') || content.includes('[Brief, friendly description')) {
70
- errors.push(`NEXT.md "What to Tell Your User" still has template placeholder`);
71
- }
72
- if (content.includes('[Capability]') && content.includes('[Endpoint, command')) {
73
- errors.push(`NEXT.md "Summary of New Capabilities" still has template placeholder`);
43
+ // Pick the active guide for this push. NEXT.md wins if present (in-flight
44
+ // release notes); fall back to the versioned guide once NEXT.md has been
45
+ // renamed during a publish.
46
+ const activeGuidePath = nextExists
47
+ ? nextPath
48
+ : (versionedGuideExists ? versionedGuidePath : null);
49
+ const activeGuideLabel = nextExists
50
+ ? 'NEXT.md'
51
+ : (versionedGuideExists ? `${version}.md` : null);
52
+
53
+ if (activeGuidePath && activeGuideLabel) {
54
+ const content = fs.readFileSync(activeGuidePath, 'utf-8');
55
+
56
+ // Run the shared validator (same checks as check-upgrade-guide.js at publish
57
+ // time). This catches the publish-blocker bugs that previously slipped past
58
+ // pre-push: inline code / fenced blocks / camelCase config keys in "What to
59
+ // Tell Your User", and missing "## Evidence" when "What Changed" claims a fix.
60
+ // Before this gate, those defects only surfaced as silently-dropped publish
61
+ // runs on main — agents never received the merged code.
62
+ const validatorIssues = validateGuideContent(content);
63
+ for (const issue of validatorIssues) {
64
+ errors.push(`${activeGuideLabel}: ${issue}`);
74
65
  }
75
66
  } else {
76
67
  errors.push(
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-05-15T19:51:52.508Z",
5
- "instarVersion": "0.28.104",
4
+ "generatedAt": "2026-05-15T20:09:47.987Z",
5
+ "instarVersion": "0.28.105",
6
6
  "entryCount": 188,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,70 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ ### fix(release): pre-push gate now validates upgrade-guide well-formedness
8
+
9
+ `scripts/pre-push-gate.js` now imports `validateGuideContent` from
10
+ `scripts/upgrade-guide-validator.mjs` and runs it on the active upgrade
11
+ guide (NEXT.md or the versioned fallback). Same authority as the
12
+ publish-time `check-upgrade-guide.js`, just enforced one step earlier.
13
+
14
+ Before this fix, malformed release notes (inline code or camelCase
15
+ config keys in "What to Tell Your User", missing "## Evidence" when
16
+ fixes are claimed) passed pre-push, merged on main, and only failed at
17
+ publish-time — silently. Between 2026-05-13 and 2026-05-15 that pattern
18
+ stranded four PRs on main without reaching npm: the token-ledger Phase
19
+ 1 (#112), the PromptGate token-burn fix (#226), and the entire
20
+ remediation track for over a day.
21
+
22
+ Test coverage: 4 new integration tests in `tests/unit/pre-push-gate.test.ts`
23
+ spawn the actual gate script against handcrafted malformed-NEXT.md fixtures
24
+ in a scratch directory and assert the right exit code + error text for
25
+ each shape, plus a well-formed control case that the gate must accept.
26
+
27
+ Spec: `docs/specs/pre-push-upgrade-guide-validation.md`. Side-effects
28
+ review: `upgrades/side-effects/pre-push-upgrade-guide-validation.md`.
29
+
30
+ ## What to Tell Your User
31
+
32
+ **Releases stop failing silently.** Before today, a small wording issue
33
+ in a release notes file could quietly stop your agent from getting any
34
+ new code at all — the release just wouldn't ship to the package registry,
35
+ and nothing would alert anyone. This release moves the same check the
36
+ release pipeline already runs into the developer's push step, so the
37
+ issue is caught the moment the change is made instead of two days later
38
+ when someone notices the silence. Nothing changes from your side; this
39
+ is about how the developer running instar catches their own mistakes
40
+ before they reach you.
41
+
42
+ ## Summary of New Capabilities
43
+
44
+ | Capability | How to Use |
45
+ |-----------|-----------|
46
+ | Pre-push upgrade-guide validation | automatic at git push time |
47
+
48
+ ## Evidence
49
+
50
+ Four consecutive publish workflow runs failed silently between
51
+ 2026-05-14 05:00 and 2026-05-15 16:00 against the following commits on
52
+ main: 529e0726, 97f90e4e, 79a2c0e8, 656360b5. All four were merged
53
+ through pre-push with malformed NEXT.md, then dropped by the publish
54
+ workflow's check-upgrade-guide.js step. No alert; the agent that needed
55
+ to react never did.
56
+
57
+ After this fix, the same malformed shapes are rejected at push time
58
+ with the same error message the publish workflow would have produced.
59
+ Verified by running the new integration tests:
60
+
61
+ ```
62
+ npx vitest run tests/unit/pre-push-gate.test.ts
63
+ ✓ tests/unit/pre-push-gate.test.ts (10 tests) 2798ms
64
+ Test Files 1 passed (1)
65
+ Tests 10 passed (10)
66
+ ```
67
+
68
+ The three known publish-blocker shapes (inline code in WTTYU, camelCase
69
+ config in WTTYU, missing Evidence with fix-claim) are pinned by
70
+ dedicated integration tests that spawn the actual gate script.
@@ -0,0 +1,126 @@
1
+ # Side-Effects Review — Pre-Push Upgrade-Guide Validation
2
+
3
+ **Version / slug:** `pre-push-upgrade-guide-validation`
4
+ **Date:** `2026-05-15`
5
+ **Author:** Echo (instar developer agent)
6
+ **Second-pass reviewer:** required (touches a release-gate decision point)
7
+
8
+ ## Summary of the change
9
+
10
+ Imports `validateGuideContent` from `scripts/upgrade-guide-validator.mjs` (the canonical validator already used by the publish-time `check-upgrade-guide.js`) and runs it inside `scripts/pre-push-gate.js`. Pre-push now rejects the same malformed upgrade-guide shapes that publish rejects, instead of letting them pass push and silently fail publish.
11
+
12
+ Files touched:
13
+ - `scripts/pre-push-gate.js` — adds import, runs `validateGuideContent` on the active guide, surfaces returned issues as errors. Removes the local section-presence + template-placeholder checks (now covered by the validator).
14
+ - `tests/unit/pre-push-gate.test.ts` — adjusts the existing scaffolding tests (one shallow content-pattern check replaced with a check that the validator is imported). Adds 4 new integration tests that spawn the actual gate script against fixture NEXT.md files in a scratch directory and assert the right exit code + error message for each of the three publish-blocker shapes plus a well-formed control.
15
+
16
+ Decision points the change interacts with: only one — the pre-push upgrade-guide gate. The validator is unchanged; this PR just makes pre-push consume it.
17
+
18
+ ## Decision-point inventory
19
+
20
+ - `scripts/pre-push-gate.js` (push-time deterministic gate over upgrade-guide well-formedness) — **modify** — replaces narrow local rules with a call into the canonical shared validator. Same rules, same authority, now consistently enforced.
21
+
22
+ ---
23
+
24
+ ## 1. Over-block
25
+
26
+ **What legitimate inputs does this change reject that it shouldn't?**
27
+
28
+ None that wouldn't ALSO be rejected at publish time. The validator's rules are the same ones publishing already enforces — a release notes file that passes pre-push today but is well-formed enough for publish would NOT be newly rejected. A release notes file that is currently passing pre-push but would fail publish IS now rejected at push time. That's the intended over-block fix.
29
+
30
+ Concrete check: I ran the gate against the live `upgrades/0.28.103.md` (which we cleaned and re-validated earlier today). Local gate reports zero validator-related errors. Other errors surface (e.g., missing side-effects artifact for v0.28.103, a pre-existing issue not introduced by this PR), but those are unrelated to the validator change.
31
+
32
+ ---
33
+
34
+ ## 2. Under-block
35
+
36
+ **What failure modes does this still miss?**
37
+
38
+ - **Validator coverage gaps.** Only the rules currently in `validateGuideContent` are enforced — if new publish-blocker patterns emerge that aren't yet in the validator, neither gate catches them. Out of scope for this PR; surfaced as a follow-up to extend the validator rule set together with the next publish-blocker class.
39
+ - **Manual side-effects artifact rename at release-cut time.** The existing `## 5 — Side-effects review artifact` block of the gate looks for an artifact filename matching `${version}.md` when the versioned guide is what's being validated. If NEXT.md is renamed to `${version}.md` but the artifact is not renamed to match, the gate rejects. This pre-existing behavior is unchanged by my PR; surfacing it here so a future PR can address.
40
+
41
+ Neither is a regression introduced by this PR.
42
+
43
+ ---
44
+
45
+ ## 3. Level-of-abstraction fit
46
+
47
+ **Is this at the right layer?**
48
+
49
+ Yes. `scripts/pre-push-gate.js` is the canonical home for push-time deterministic gates. `validateGuideContent` is the canonical authority over upgrade-guide well-formedness, shared with `check-upgrade-guide.js` at publish time. This PR is the smallest possible edge that makes the two gates consistent. No new layer; no relocation of existing logic.
50
+
51
+ ---
52
+
53
+ ## 4. Signal vs authority compliance
54
+
55
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
56
+
57
+ **Does this change hold blocking authority with brittle logic?**
58
+
59
+ - [ ] No — this change has no block/allow surface.
60
+ - [x] No — this change produces a signal consumed by an existing smart gate.
61
+ - [ ] Yes — but the logic is a smart gate with full conversational context.
62
+ - [ ] ⚠️ Yes, with brittle logic.
63
+
64
+ The validator IS the authority — a deterministic ruleset over a constrained domain (markdown release notes). The pre-push gate is a consumer of that authority. The PR is a refactor that points the consumer at the canonical authority instead of a stale local copy. No new authority introduced; no brittle logic gains blocking power.
65
+
66
+ ---
67
+
68
+ ## 5. Interactions
69
+
70
+ **Does this interact with existing checks, recovery paths, or infrastructure?**
71
+
72
+ - **Shadowing**: the pre-push gate runs validate after section-presence (now collapsed into the same call) and before version-increment, side-effects-artifact, lint-no-direct-destructive, URL.pathname. All other checks unchanged.
73
+ - **Double-fire**: the validator runs once at pre-push and once at publish. Pre-push catches malformed guides earlier; publish remains the authoritative final check. Double-fire is the intended design — push-time is a strict subset enforcer of publish-time.
74
+ - **Races**: pre-push is single-process synchronous. No race surface.
75
+ - **Adjacent cleanups**: existing scaffold tests in `pre-push-gate.test.ts` are adjusted, not removed. The integration tests use temp dirs cleaned up via `SafeFsExecutor.safeRmSync` per the lint-no-direct-destructive rule.
76
+
77
+ ---
78
+
79
+ ## 6. External surfaces
80
+
81
+ **Does this change anything visible outside the immediate code path?**
82
+
83
+ - **Other agents on the same machine** — no, this is a pre-push gate for the instar source repo. Agents don't push to instar; they only consume releases.
84
+ - **Other users of the install base** — no, see above.
85
+ - **External systems** — none.
86
+ - **Persistent state** — none.
87
+ - **Timing / runtime conditions** — the gate now runs validator code at push time. Validator runs synchronously in ~10ms; adds no perceptible latency.
88
+
89
+ ---
90
+
91
+ ## 7. Rollback cost
92
+
93
+ **If this turns out wrong in production, what's the back-out?**
94
+
95
+ - **Hot-fix release**: revert `scripts/pre-push-gate.js` and `tests/unit/pre-push-gate.test.ts`, ship a patch. ~5 minutes.
96
+ - **Data migration**: none.
97
+ - **Agent state repair**: none.
98
+ - **User visibility**: zero. The gate is a developer-facing pre-push check; users see nothing.
99
+
100
+ ---
101
+
102
+ ## Conclusion
103
+
104
+ Pure scope-tightening of a pre-existing push-time gate to consume a pre-existing publish-time authority. No new decision logic, no new blocking surface, no new persistent state. The motivating incident (yesterday and this morning, multiple PRs stranded on main without reaching npm) is the explicit driver. Worst-case rollback is a two-file revert at ~5 minutes. Clear to ship pending second-pass concurrence.
105
+
106
+ ---
107
+
108
+ ## Second-pass review
109
+
110
+ **Reviewer:** general-purpose subagent (Echo's review subagent)
111
+ **Independent read of the artifact: concur**
112
+
113
+ The validator-based check is structurally clean: both `pre-push-gate.js` and `check-upgrade-guide.js` import `validateGuideContent` from the same module and feed it `fs.readFileSync(activeGuide, 'utf-8')` — identical input shape, so push-time cannot over-reject relative to publish-time. The validator's REQUIRED_SECTIONS / template-placeholder rules subsume the removed local checks one-for-one; the only other consumer of the old "[Feature name]" / "[Capability]" / section-presence patterns in the gate source was this test file itself, and `tests/unit/upgrade-guide-finalization.test.ts` exercises those strings against the template (unrelated to the gate). Symlink-vs-copy analysis is correct: `lint-no-direct-destructive.js` resolves `ROOT` via `path.resolve(fileURLToPath(import.meta.url), '..', '..')` and Node's ESM loader collapses symlinks before module resolution, so a symlinked copy would scan the production worktree; `fs.copyFileSync` produces a sibling at `scratch/scripts/...` and ROOT correctly resolves to the scratch dir. `SafeFsExecutor.safeRmSync` is called with the correct `{recursive, force, operation}` shape (operation is the required label); scratch lives under `os.tmpdir()` so SourceTreeGuard won't intervene. Integration with the unmodified "Side-effects review artifact" step (gate §5) is consistent — both checks accumulate into the same `errors[]` and fire together. Signal-vs-authority: the validator remains the deterministic authority over a constrained domain (release-notes well-formedness, which falls under the "structural validators at the boundary" carve-out in `docs/signal-vs-authority.md`); the gate is a pure consumer surfacing existing decisions earlier in the workflow.
114
+
115
+ Minor non-blocking notes (none block the ship):
116
+ - `execSync` is imported at line 16 of the test file but no longer used (it was replaced by `spawnSync` on line 33). Dead import; cleanup-only.
117
+ - The integration test for the third malformed case ("Fix the misbehavior...") triggers BOTH the missing-Evidence error AND the missing-side-effects-artifact error simultaneously; the assertion on "has no \"## Evidence\" section" still passes because both errors are appended to stdout, but if a future regression flipped the order or the artifact check moved earlier and short-circuited, the assertion might survive without exercising the validator path. Consider an additional fixture that has the side-effects artifact present but the Evidence section missing, to lock in the validator path independently.
118
+ - The "ACCEPTS a well-formed NEXT.md" test uses "A small improvement" which doesn't trigger any FIX_PATTERNS — but the test still creates a `test-artifact.md` defensively. Comment in the test claims the gate requires the artifact for "add/new/feature/fix words", but "improvement" matches none of those. The artifact creation is unnecessary belt-and-suspenders, not load-bearing. Not a bug.
119
+
120
+ ---
121
+
122
+ ## Evidence pointers
123
+
124
+ - Original failed publish runs visible in `gh run list -L 5 --workflow=publish.yml --status=failure` — four consecutive `failure` runs between 2026-05-14 05:00 and 2026-05-15 16:00 before the cleanup PR #228 unblocked publish.
125
+ - Test verification: `npx vitest run tests/unit/pre-push-gate.test.ts` → 10/10 passed (4 new integration tests; one scaffold test re-shaped).
126
+ - Manual verification: against three handcrafted malformed NEXT.md fixtures, the gate exits non-zero with the right error message; against a well-formed fixture, the gate exits zero.