instar 1.3.152 → 1.3.154

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.152",
3
+ "version": "1.3.154",
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,311 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * assemble-next-md — fold per-PR release-note FRAGMENTS into a single NEXT.md.
4
+ *
5
+ * ── Why this exists ───────────────────────────────────────────────────
6
+ * The release pipeline consumes a single shared `upgrades/NEXT.md` per merge.
7
+ * With many agents merging concurrently, EVERY PR rewrote that one file, so
8
+ * every PR collided on it within minutes — the release notes became an
9
+ * un-landable hot file. The fix is per-PR FRAGMENTS: each PR ships its note as
10
+ * `upgrades/next/<slug>.md`, touching a distinct file, so two PRs never
11
+ * collide. This script concatenates the fragments into `upgrades/NEXT.md`
12
+ * BEFORE the existing publish pipeline runs, leaving everything downstream
13
+ * unchanged.
14
+ *
15
+ * ── What it does ──────────────────────────────────────────────────────
16
+ * 1. Reads every `upgrades/next/*.md` fragment (deterministic filename sort).
17
+ * 2. Folds in a legacy `upgrades/NEXT.md` too, if one exists (backward compat).
18
+ * 3. Merges by section: the result has ONE "## What Changed", ONE
19
+ * "## Summary of New Capabilities", ONE "## What to Tell Your User", ONE
20
+ * "## Evidence" — each section is the concatenation of that section across
21
+ * all inputs (in source order). Any non-canonical H2 section is preserved
22
+ * and appended after the canonical ones (also concatenated by title).
23
+ * 4. The bump directive is the HIGHEST tier among the inputs
24
+ * (major > minor > patch), emitted as `<!-- bump: X -->`. This is a
25
+ * documentation hint only — the REAL release tier is
26
+ * `.instar/release-tier.json`, which this script never touches.
27
+ * 5. Writes the merged result to `upgrades/NEXT.md`.
28
+ *
29
+ * ── No-op / failure semantics ─────────────────────────────────────────
30
+ * - NO fragments AND NO legacy NEXT.md → does nothing, exits 0 quietly. The
31
+ * existing publish "skip if no NEXT.md" logic then fires unchanged.
32
+ * - A real malformation (a fragment with content but no parseable H2 section
33
+ * at all) → exits non-zero with a clear message, so the workflow fails
34
+ * loudly rather than shipping a broken guide.
35
+ * - Idempotent: running twice produces the same NEXT.md. (When a generated
36
+ * NEXT.md is the ONLY input — no fragments — it is re-emitted byte-stable.)
37
+ *
38
+ * The core `assembleNextMd()` function is pure (string in / string out) and is
39
+ * exported for unit testing. The CLI wrapper at the bottom handles I/O.
40
+ *
41
+ * Usage:
42
+ * node scripts/assemble-next-md.mjs # assemble + write upgrades/NEXT.md
43
+ * node scripts/assemble-next-md.mjs --upgrades-dir <path> # override (tests)
44
+ */
45
+
46
+ import fs from 'node:fs';
47
+ import path from 'node:path';
48
+ import { fileURLToPath, pathToFileURL } from 'node:url';
49
+
50
+ // ── Canonical section order ───────────────────────────────────────────
51
+ // These four are the sections check-upgrade-guide.js / upgrade-guide-validator
52
+ // know about. We emit them in this order; any other H2 is appended after.
53
+ export const CANONICAL_SECTIONS = [
54
+ 'What Changed',
55
+ 'What to Tell Your User',
56
+ 'Summary of New Capabilities',
57
+ 'Evidence',
58
+ ];
59
+
60
+ const BUMP_RANK = { patch: 0, minor: 1, major: 2 };
61
+ const RANK_BUMP = ['patch', 'minor', 'major'];
62
+
63
+ const H1_HEADER = '# Upgrade Guide — vNEXT';
64
+
65
+ // Stable marker stamped into every machine-assembled NEXT.md. It lets us
66
+ // recognize (and skip re-folding) a NEXT.md that this script generated, which
67
+ // is what keeps the on-disk assemble idempotent: a second run shouldn't fold
68
+ // the previous run's output back in on top of the still-present fragments. A
69
+ // hand-authored legacy NEXT.md (no marker) is still folded for backward compat.
70
+ export const GENERATED_MARKER = '<!-- assembled-by: assemble-next-md -->';
71
+
72
+ /** True when content was produced by this assembler (carries the marker). */
73
+ export function isAssembledOutput(content) {
74
+ return String(content).includes(GENERATED_MARKER);
75
+ }
76
+
77
+ /**
78
+ * Parse the declared bump type from a fragment's `<!-- bump: TYPE -->` comment.
79
+ * Returns 'patch' | 'minor' | 'major' | null.
80
+ */
81
+ export function parseBumpType(content) {
82
+ const match = /<!--\s*bump:\s*(patch|minor|major)\s*-->/i.exec(content);
83
+ return match ? match[1].toLowerCase() : null;
84
+ }
85
+
86
+ /**
87
+ * Split a fragment body into H2 sections: [{ title, body }] in source order.
88
+ * `body` excludes the heading line itself and is right-trimmed.
89
+ * Content before the first H2 (H1 header, bump comment, stray prose) is dropped
90
+ * from the section map — the assembler synthesizes a fresh H1 + bump comment.
91
+ */
92
+ export function parseSections(content) {
93
+ const out = [];
94
+ const lines = String(content).replace(/\r\n/g, '\n').split('\n');
95
+ let current = null;
96
+ for (const line of lines) {
97
+ const m = /^##\s+(.+?)\s*$/.exec(line);
98
+ if (m) {
99
+ if (current) out.push(current);
100
+ current = { title: m[1].trim(), lines: [] };
101
+ } else if (current) {
102
+ current.lines.push(line);
103
+ }
104
+ }
105
+ if (current) out.push(current);
106
+ return out.map((s) => ({ title: s.title, body: s.lines.join('\n').replace(/\s+$/, '') }));
107
+ }
108
+
109
+ /**
110
+ * Is this fragment content "empty" for assembly purposes? True when stripping
111
+ * HTML comments and whitespace leaves nothing.
112
+ */
113
+ function isEffectivelyEmpty(content) {
114
+ return String(content).replace(/<!--[\s\S]*?-->/g, '').trim().length === 0;
115
+ }
116
+
117
+ /**
118
+ * Core assembler. Pure: takes named fragment inputs, returns the merged NEXT.md
119
+ * string (or throws on a real malformation).
120
+ *
121
+ * @param {Array<{ name: string, content: string }>} fragments
122
+ * Each input fragment, in the order they should be concatenated. `name` is
123
+ * used only for error messages.
124
+ * @returns {string} the assembled NEXT.md content (with trailing newline).
125
+ * @throws {Error} when a fragment has content but no parseable H2 section.
126
+ */
127
+ export function assembleNextMd(fragments) {
128
+ // Idempotency guard: a previously-assembled NEXT.md (carrying GENERATED_MARKER)
129
+ // is the FOLD of the current fragments — folding it again on top of the same
130
+ // fragments would duplicate every section. So when at least one non-generated
131
+ // input is present, drop the generated one(s). When a generated output is the
132
+ // SOLE input (no fragments), keep it: parse→emit is stable, so it re-emits
133
+ // identically.
134
+ const nonGenerated = fragments.filter((f) => !isAssembledOutput(f.content ?? ''));
135
+ const effective = nonGenerated.length > 0 ? nonGenerated : fragments;
136
+
137
+ // Map title -> array of section bodies (in source order).
138
+ const merged = new Map();
139
+ // Preserve first-seen order of non-canonical titles.
140
+ const extraOrder = [];
141
+ let maxBumpRank = 0;
142
+ let sawAnyBump = false;
143
+
144
+ for (const frag of effective) {
145
+ const content = frag.content ?? '';
146
+ const bump = parseBumpType(content);
147
+ if (bump) {
148
+ sawAnyBump = true;
149
+ maxBumpRank = Math.max(maxBumpRank, BUMP_RANK[bump]);
150
+ }
151
+
152
+ const sections = parseSections(content);
153
+ if (sections.length === 0) {
154
+ // No H2 sections. An effectively-empty fragment (only comments/whitespace)
155
+ // is malformed — a fragment file should always carry real notes.
156
+ // A fragment with real prose but no headings is also malformed: we can't
157
+ // place its content under a known section.
158
+ throw new Error(
159
+ `release-note fragment "${frag.name}" has no recognizable "## " section. ` +
160
+ `Every fragment must contain at least one section heading ` +
161
+ `(e.g. "## What Changed"). Fix or remove this fragment.`,
162
+ );
163
+ }
164
+
165
+ for (const sec of sections) {
166
+ const body = sec.body.trim();
167
+ // Skip a section whose body is only whitespace/comments — nothing to fold.
168
+ if (isEffectivelyEmpty(body)) continue;
169
+ if (!merged.has(sec.title)) {
170
+ merged.set(sec.title, []);
171
+ if (!CANONICAL_SECTIONS.includes(sec.title)) extraOrder.push(sec.title);
172
+ }
173
+ merged.get(sec.title).push(body);
174
+ }
175
+ }
176
+
177
+ // If after folding there is genuinely nothing to say, that's a malformation
178
+ // for a non-empty input set (every fragment was comments-only).
179
+ if (merged.size === 0) {
180
+ throw new Error(
181
+ 'release-note fragments produced no content — every section was empty. ' +
182
+ 'Fill in at least "## What Changed" in one fragment.',
183
+ );
184
+ }
185
+
186
+ // Emit canonical sections first (only when present), then extras in
187
+ // first-seen order.
188
+ const titleOrder = [
189
+ ...CANONICAL_SECTIONS.filter((t) => merged.has(t)),
190
+ ...extraOrder,
191
+ ];
192
+
193
+ const bump = sawAnyBump ? RANK_BUMP[maxBumpRank] : 'patch';
194
+
195
+ const parts = [];
196
+ parts.push(H1_HEADER);
197
+ parts.push('');
198
+ parts.push(GENERATED_MARKER);
199
+ parts.push(`<!-- bump: ${bump} -->`);
200
+ parts.push('');
201
+ for (const title of titleOrder) {
202
+ const bodies = merged.get(title);
203
+ parts.push(`## ${title}`);
204
+ parts.push('');
205
+ // Concatenate each fragment's body for this section, separated by a blank
206
+ // line. Bodies were already trimmed.
207
+ parts.push(bodies.join('\n\n'));
208
+ parts.push('');
209
+ }
210
+
211
+ // Single trailing newline, no duplicate blank lines at EOF.
212
+ return parts.join('\n').replace(/\n+$/, '\n');
213
+ }
214
+
215
+ // ── CLI entry point ────────────────────────────────────────────────────
216
+
217
+ /**
218
+ * Gather fragment inputs from disk in deterministic order: legacy NEXT.md first
219
+ * (so its content leads), then `next/*.md` sorted by filename.
220
+ */
221
+ export function gatherFragmentInputs(upgradesDir) {
222
+ const inputs = [];
223
+
224
+ const legacyPath = path.join(upgradesDir, 'NEXT.md');
225
+ const fragmentsDir = path.join(upgradesDir, 'next');
226
+
227
+ let legacyContent = null;
228
+ if (fs.existsSync(legacyPath)) {
229
+ legacyContent = fs.readFileSync(legacyPath, 'utf-8');
230
+ }
231
+
232
+ let fragmentFiles = [];
233
+ if (fs.existsSync(fragmentsDir)) {
234
+ fragmentFiles = fs
235
+ .readdirSync(fragmentsDir)
236
+ .filter((f) => f.endsWith('.md'))
237
+ .sort((a, b) => a.localeCompare(b, 'en'));
238
+ }
239
+
240
+ // Fold the legacy NEXT.md in first — only when it carries real content
241
+ // (skip a bare template/empty file so re-runs are idempotent and a fresh
242
+ // template doesn't masquerade as content).
243
+ if (legacyContent !== null && !isEffectivelyEmpty(legacyContent)) {
244
+ inputs.push({ name: 'NEXT.md', content: legacyContent });
245
+ }
246
+
247
+ for (const file of fragmentFiles) {
248
+ const content = fs.readFileSync(path.join(fragmentsDir, file), 'utf-8');
249
+ inputs.push({ name: `next/${file}`, content });
250
+ }
251
+
252
+ return { inputs, hadLegacy: legacyContent !== null, fragmentCount: fragmentFiles.length };
253
+ }
254
+
255
+ function main() {
256
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
257
+ const ROOT = path.resolve(__dirname, '..');
258
+
259
+ // Allow tests / callers to point at an alternate upgrades dir.
260
+ const argIdx = process.argv.indexOf('--upgrades-dir');
261
+ const upgradesDir =
262
+ argIdx !== -1 && process.argv[argIdx + 1]
263
+ ? path.resolve(process.argv[argIdx + 1])
264
+ : path.join(ROOT, 'upgrades');
265
+
266
+ const { inputs, hadLegacy, fragmentCount } = gatherFragmentInputs(upgradesDir);
267
+
268
+ if (inputs.length === 0) {
269
+ // No fragments and no (content-bearing) legacy NEXT.md. Do nothing — the
270
+ // existing publish skip logic handles the "nothing to publish" case.
271
+ console.log(
272
+ `[assemble-next-md] no fragments in ${path.join(upgradesDir, 'next')} ` +
273
+ `and no content-bearing NEXT.md — nothing to assemble.`,
274
+ );
275
+ process.exit(0);
276
+ }
277
+
278
+ let assembled;
279
+ try {
280
+ assembled = assembleNextMd(inputs);
281
+ } catch (err) {
282
+ console.error(`[assemble-next-md] ERROR: ${err instanceof Error ? err.message : String(err)}`);
283
+ process.exit(1);
284
+ }
285
+
286
+ const nextPath = path.join(upgradesDir, 'NEXT.md');
287
+ fs.mkdirSync(upgradesDir, { recursive: true });
288
+ fs.writeFileSync(nextPath, assembled);
289
+
290
+ console.log(
291
+ `[assemble-next-md] assembled ${fragmentCount} fragment(s)` +
292
+ `${hadLegacy ? ' + legacy NEXT.md' : ''} → ${path.relative(ROOT, nextPath)}`,
293
+ );
294
+ process.exit(0);
295
+ }
296
+
297
+ // Only run main() when invoked directly (not when imported by tests).
298
+ // Compare resolved file URLs rather than string-concatenating argv[1] — that
299
+ // naive form breaks on relative paths and paths containing spaces.
300
+ function invokedDirectly() {
301
+ if (!process.argv[1]) return false;
302
+ try {
303
+ return import.meta.url === pathToFileURL(fs.realpathSync(process.argv[1])).href;
304
+ } catch {
305
+ return import.meta.url === pathToFileURL(process.argv[1]).href;
306
+ }
307
+ }
308
+
309
+ if (invokedDirectly()) {
310
+ main();
311
+ }
@@ -14,11 +14,11 @@ import fs from 'node:fs';
14
14
  import path from 'node:path';
15
15
  import { fileURLToPath } from 'node:url';
16
16
  import { validateGuideContent } from './upgrade-guide-validator.mjs';
17
+ import { assembleNextMd, gatherFragmentInputs } from './assemble-next-md.mjs';
17
18
 
18
19
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
20
  const ROOT = path.resolve(__dirname, '..');
20
21
  const upgradesDir = path.join(ROOT, 'upgrades');
21
- const nextPath = path.join(ROOT, 'upgrades', 'NEXT.md');
22
22
  const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf-8'));
23
23
  const version = pkg.version;
24
24
 
@@ -34,38 +34,66 @@ const REQUIRED_SECTIONS = [
34
34
  let errors = [];
35
35
  let warnings = [];
36
36
 
37
- // ── 1. NEXT.md validation ─────────────────────────────────────────────
37
+ // ── 1. Release-note validation (fragment-aware) ───────────────────────
38
+ //
39
+ // Release notes are authored as per-PR FRAGMENTS in upgrades/next/<slug>.md
40
+ // (so concurrent PRs never collide on a single shared NEXT.md). The pre-push
41
+ // gate validates the ASSEMBLED result — fragments folded together with any
42
+ // legacy upgrades/NEXT.md — so a PR that ships only a fragment passes the same
43
+ // section/content checks the publish gate enforces. We assemble in-memory and
44
+ // NEVER write NEXT.md to disk here: the PR keeps its fragment, not a generated
45
+ // guide. publish.yml runs the real assemble step before publishing.
38
46
 
39
47
  const versionedGuidePath = path.join(upgradesDir, `${version}.md`);
40
48
  const versionedGuideExists = fs.existsSync(versionedGuidePath);
41
- const nextExists = fs.existsSync(nextPath);
42
-
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
49
 
53
- if (activeGuidePath && activeGuideLabel) {
54
- const content = fs.readFileSync(activeGuidePath, 'utf-8');
50
+ // Assemble fragments + legacy NEXT.md in-memory.
51
+ let assembledContent = null;
52
+ let assembleError = null;
53
+ {
54
+ const { inputs } = gatherFragmentInputs(upgradesDir);
55
+ if (inputs.length > 0) {
56
+ try {
57
+ assembledContent = assembleNextMd(inputs);
58
+ } catch (err) {
59
+ assembleError = err instanceof Error ? err.message : String(err);
60
+ }
61
+ }
62
+ }
55
63
 
64
+ // The active guide for validation: the assembled fragments/NEXT.md (in-flight
65
+ // release notes) win when present; otherwise fall back to the versioned guide
66
+ // (post-release-cut state, when NEXT.md has been renamed and no new fragment is
67
+ // staged yet).
68
+ const activeGuideLabel = assembledContent !== null
69
+ ? 'assembled release notes (upgrades/next/*.md + NEXT.md)'
70
+ : (versionedGuideExists ? `${version}.md` : null);
71
+
72
+ if (assembleError) {
73
+ // A malformed fragment must fail the push loudly — the same loud failure the
74
+ // publish workflow would hit.
75
+ errors.push(`Release-note fragments are malformed: ${assembleError}`);
76
+ } else if (assembledContent !== null) {
56
77
  // Run the shared validator (same checks as check-upgrade-guide.js at publish
57
78
  // time). This catches the publish-blocker bugs that previously slipped past
58
79
  // pre-push: inline code / fenced blocks / camelCase config keys in "What to
59
80
  // Tell Your User", and missing "## Evidence" when "What Changed" claims a fix.
60
81
  // Before this gate, those defects only surfaced as silently-dropped publish
61
82
  // runs on main — agents never received the merged code.
62
- const validatorIssues = validateGuideContent(content);
83
+ const validatorIssues = validateGuideContent(assembledContent);
63
84
  for (const issue of validatorIssues) {
64
85
  errors.push(`${activeGuideLabel}: ${issue}`);
65
86
  }
87
+ } else if (versionedGuideExists) {
88
+ const content = fs.readFileSync(versionedGuidePath, 'utf-8');
89
+ const validatorIssues = validateGuideContent(content);
90
+ for (const issue of validatorIssues) {
91
+ errors.push(`${version}.md: ${issue}`);
92
+ }
66
93
  } else {
67
94
  errors.push(
68
- `No upgrade guide found. Create upgrades/NEXT.md with sections: ${REQUIRED_SECTIONS.join(', ')}`
95
+ `No upgrade guide found. Create a release-note fragment ` +
96
+ `upgrades/next/<slug>.md (or legacy upgrades/NEXT.md) with sections: ${REQUIRED_SECTIONS.join(', ')}`
69
97
  );
70
98
  }
71
99
 
@@ -221,14 +249,16 @@ if (!process.env.CI) {
221
249
  /\bnew\b/i,
222
250
  ];
223
251
 
224
- // When NEXT.md exists, it represents the *next* shipment being prepared in
225
- // this push — prefer it over the (frozen) versioned guide, which describes
226
- // the already-released version and isn't what this PR is changing. This is
227
- // the post-release-cut + new-PR state: both files coexist, but only NEXT.md
228
- // is in flight. Falls back to the versioned guide when no NEXT.md is staged.
229
- const guidePath = nextExists ? nextPath : (versionedGuideExists ? versionedGuidePath : null);
230
- if (guidePath && fs.existsSync(guidePath)) {
231
- const guideContent = fs.readFileSync(guidePath, 'utf-8');
252
+ // The in-flight release notes (assembled fragments + legacy NEXT.md) represent
253
+ // the *next* shipment being prepared in this push — prefer them over the
254
+ // (frozen) versioned guide, which describes the already-released version and
255
+ // isn't what this PR is changing. Falls back to the versioned guide when no
256
+ // fragment / NEXT.md is staged (post-release-cut state).
257
+ const inFlight = assembledContent !== null;
258
+ const guideContent = inFlight
259
+ ? assembledContent
260
+ : (versionedGuideExists ? fs.readFileSync(versionedGuidePath, 'utf-8') : null);
261
+ if (guideContent !== null) {
232
262
  // Extract "## What Changed" section
233
263
  const whatChangedMatch = guideContent.match(/## What Changed\s*([\s\S]*?)(?=\n##\s|$)/);
234
264
  const whatChanged = whatChangedMatch ? whatChangedMatch[1] : '';
@@ -237,10 +267,10 @@ if (!process.env.CI) {
237
267
 
238
268
  if (qualifies) {
239
269
  const sideEffectsDir = path.join(ROOT, 'upgrades', 'side-effects');
240
- // If NEXT.md is the active guide, any fresh artifact (last 24h) counts —
241
- // the versioned-filename requirement only applies when a versioned guide
242
- // is being validated without an in-flight NEXT.md.
243
- const artifactName = (!nextExists && versionedGuideExists) ? `${version}.md` : null;
270
+ // When in-flight notes (fragments/NEXT.md) drive the push, any fresh
271
+ // artifact (last 24h) counts — the versioned-filename requirement only
272
+ // applies when a versioned guide is being validated without in-flight notes.
273
+ const artifactName = (!inFlight && versionedGuideExists) ? `${version}.md` : null;
244
274
  let artifactFound = false;
245
275
 
246
276
  if (fs.existsSync(sideEffectsDir)) {
@@ -248,9 +278,9 @@ if (!process.env.CI) {
248
278
  if (artifactName) {
249
279
  artifactFound = files.includes(artifactName);
250
280
  } else {
251
- // For NEXT.md, any fresh artifact from the last 24h counts.
252
- // The expectation is that during release cut, NEXT.md -> <version>.md
253
- // rename will pair with the artifact rename as well.
281
+ // For in-flight notes (fragments/NEXT.md), any fresh artifact from the
282
+ // last 24h counts. The expectation is that during release cut, the
283
+ // fragment/NEXT.md -> <version>.md rename pairs with an artifact rename.
254
284
  const recent = files.filter((f) => {
255
285
  const stat = fs.statSync(path.join(sideEffectsDir, f));
256
286
  return Date.now() - stat.mtimeMs < 24 * 60 * 60 * 1000;
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-05-31T09:03:11.651Z",
5
- "instarVersion": "1.3.152",
4
+ "generatedAt": "2026-05-31T09:22:25.465Z",
5
+ "instarVersion": "1.3.154",
6
6
  "entryCount": 193,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -11,7 +11,7 @@
11
11
  "domain": "identity",
12
12
  "sourcePath": "src/core/PostUpdateMigrator.ts",
13
13
  "installedPath": ".instar/hooks/instar/session-start.sh",
14
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
14
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
15
15
  "since": "2025-01-01"
16
16
  },
17
17
  "hook:dangerous-command-guard": {
@@ -20,7 +20,7 @@
20
20
  "domain": "safety",
21
21
  "sourcePath": "src/core/PostUpdateMigrator.ts",
22
22
  "installedPath": ".instar/hooks/instar/dangerous-command-guard.sh",
23
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
23
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
24
24
  "since": "2025-01-01"
25
25
  },
26
26
  "hook:grounding-before-messaging": {
@@ -29,7 +29,7 @@
29
29
  "domain": "safety",
30
30
  "sourcePath": "src/core/PostUpdateMigrator.ts",
31
31
  "installedPath": ".instar/hooks/instar/grounding-before-messaging.sh",
32
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
32
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
33
33
  "since": "2025-01-01"
34
34
  },
35
35
  "hook:compaction-recovery": {
@@ -38,7 +38,7 @@
38
38
  "domain": "identity",
39
39
  "sourcePath": "src/core/PostUpdateMigrator.ts",
40
40
  "installedPath": ".instar/hooks/instar/compaction-recovery.sh",
41
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
41
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
42
42
  "since": "2025-01-01"
43
43
  },
44
44
  "hook:external-operation-gate": {
@@ -47,7 +47,7 @@
47
47
  "domain": "safety",
48
48
  "sourcePath": "src/core/PostUpdateMigrator.ts",
49
49
  "installedPath": ".instar/hooks/instar/external-operation-gate.js",
50
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
50
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
51
51
  "since": "2025-01-01"
52
52
  },
53
53
  "hook:deferral-detector": {
@@ -56,7 +56,7 @@
56
56
  "domain": "safety",
57
57
  "sourcePath": "src/core/PostUpdateMigrator.ts",
58
58
  "installedPath": ".instar/hooks/instar/deferral-detector.js",
59
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
59
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
60
60
  "since": "2025-01-01"
61
61
  },
62
62
  "hook:post-action-reflection": {
@@ -65,7 +65,7 @@
65
65
  "domain": "evolution",
66
66
  "sourcePath": "src/core/PostUpdateMigrator.ts",
67
67
  "installedPath": ".instar/hooks/instar/post-action-reflection.js",
68
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
68
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
69
69
  "since": "2025-01-01"
70
70
  },
71
71
  "hook:external-communication-guard": {
@@ -74,7 +74,7 @@
74
74
  "domain": "safety",
75
75
  "sourcePath": "src/core/PostUpdateMigrator.ts",
76
76
  "installedPath": ".instar/hooks/instar/external-communication-guard.js",
77
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
77
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
78
78
  "since": "2025-01-01"
79
79
  },
80
80
  "hook:scope-coherence-collector": {
@@ -83,7 +83,7 @@
83
83
  "domain": "coherence",
84
84
  "sourcePath": "src/core/PostUpdateMigrator.ts",
85
85
  "installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
86
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
86
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
87
87
  "since": "2025-01-01"
88
88
  },
89
89
  "hook:scope-coherence-checkpoint": {
@@ -92,7 +92,7 @@
92
92
  "domain": "coherence",
93
93
  "sourcePath": "src/core/PostUpdateMigrator.ts",
94
94
  "installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
95
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
95
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
96
96
  "since": "2025-01-01"
97
97
  },
98
98
  "hook:free-text-guard": {
@@ -101,7 +101,7 @@
101
101
  "domain": "safety",
102
102
  "sourcePath": "src/core/PostUpdateMigrator.ts",
103
103
  "installedPath": ".instar/hooks/instar/free-text-guard.sh",
104
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
104
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
105
105
  "since": "2025-01-01"
106
106
  },
107
107
  "hook:claim-intercept": {
@@ -110,7 +110,7 @@
110
110
  "domain": "coherence",
111
111
  "sourcePath": "src/core/PostUpdateMigrator.ts",
112
112
  "installedPath": ".instar/hooks/instar/claim-intercept.js",
113
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
113
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
114
114
  "since": "2025-01-01"
115
115
  },
116
116
  "hook:claim-intercept-response": {
@@ -119,7 +119,7 @@
119
119
  "domain": "coherence",
120
120
  "sourcePath": "src/core/PostUpdateMigrator.ts",
121
121
  "installedPath": ".instar/hooks/instar/claim-intercept-response.js",
122
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
122
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
123
123
  "since": "2025-01-01"
124
124
  },
125
125
  "hook:stop-gate-router": {
@@ -128,7 +128,7 @@
128
128
  "domain": "safety",
129
129
  "sourcePath": "src/core/PostUpdateMigrator.ts",
130
130
  "installedPath": ".instar/hooks/instar/stop-gate-router.js",
131
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
131
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
132
132
  "since": "2025-01-01"
133
133
  },
134
134
  "hook:auto-approve-permissions": {
@@ -137,7 +137,7 @@
137
137
  "domain": "safety",
138
138
  "sourcePath": "src/core/PostUpdateMigrator.ts",
139
139
  "installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
140
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
140
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
141
141
  "since": "2025-01-01"
142
142
  },
143
143
  "job:health-check": {
@@ -1489,7 +1489,7 @@
1489
1489
  "type": "subsystem",
1490
1490
  "domain": "updates",
1491
1491
  "sourcePath": "src/core/PostUpdateMigrator.ts",
1492
- "contentHash": "3418f7109a38172a28a3a202c8a878c1064f55911a82155c53a0b1ccde44e804",
1492
+ "contentHash": "9eed6792d9f566c568f499824c267d35f1c98d484ef3f853919fa9d42ca5049c",
1493
1493
  "since": "2025-01-01"
1494
1494
  },
1495
1495
  "subsystem:scheduler": {