instar 1.3.312 → 1.3.313

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.312",
3
+ "version": "1.3.313",
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",
@@ -278,6 +278,41 @@ try {
278
278
  const slug = (freshestTrace && (freshestTrace.slug || freshestTrace.name)) || 'unknown';
279
279
  const decision = decideRequirementSet(declaredTier);
280
280
 
281
+ // ─── Step 4.55: causal autopsy (directive 2026-06-05) ───────────────────
282
+ // Low-ceremony lanes (Tier-1) ship without an independent reviewer, so the
283
+ // compensating control is a durable causal record per issue: every fix-class
284
+ // commit SHOULD declare what caused the issue it fixes — a prior PR, an
285
+ // environment shift that invalidated old assumptions, plain new code, a
286
+ // latent bug, or honestly unknown. The field rides the decision audit so
287
+ // meta-analysis ("are we converging or playing whack-a-mole?") is a query
288
+ // over .instar/instar-dev-decisions/, not archaeology. ADVISORY in this
289
+ // slice: absence warns on fix-class signals, never blocks. A PRESENT but
290
+ // malformed autopsy blocks — a corrupt record is worse than none.
291
+ const AUTOPSY_ORIGINS = ['prior-pr', 'environment-shift', 'new-code', 'latent', 'unknown'];
292
+ let causalAutopsy = null;
293
+ let autopsyError = null;
294
+ if (freshestTrace && freshestTrace.causalAutopsy !== undefined) {
295
+ const ca = freshestTrace.causalAutopsy;
296
+ const validPrs = (a) => Array.isArray(a) && a.length > 0 && a.every((n) => Number.isInteger(n) && n > 0);
297
+ if (!ca || typeof ca !== 'object' || Array.isArray(ca)) {
298
+ autopsyError = 'causalAutopsy must be an object: { origin, relatedPrs?, notes? }';
299
+ } else if (!AUTOPSY_ORIGINS.includes(ca.origin)) {
300
+ autopsyError = `causalAutopsy.origin must be one of ${AUTOPSY_ORIGINS.join(' | ')} (got ${JSON.stringify(ca.origin)})`;
301
+ } else if (ca.origin === 'prior-pr' && !validPrs(ca.relatedPrs)) {
302
+ autopsyError = 'causalAutopsy.origin "prior-pr" requires relatedPrs: a non-empty array of positive PR numbers';
303
+ } else if (ca.relatedPrs !== undefined && !validPrs(ca.relatedPrs)) {
304
+ autopsyError = 'causalAutopsy.relatedPrs must be a non-empty array of positive integers when present';
305
+ } else if (ca.notes !== undefined && typeof ca.notes !== 'string') {
306
+ autopsyError = 'causalAutopsy.notes must be a string when present';
307
+ } else {
308
+ causalAutopsy = {
309
+ origin: ca.origin,
310
+ ...(ca.relatedPrs !== undefined ? { relatedPrs: ca.relatedPrs } : {}),
311
+ ...(ca.notes !== undefined ? { notes: ca.notes } : {}),
312
+ };
313
+ }
314
+ }
315
+
281
316
  // AUDIT (all in-scope cases): one JSON line, written regardless of branch.
282
317
  // belowFloor = the agent declared UNDER the risk-signaled floor. We never
283
318
  // block on it (the mind holds authority) — the record is the backstop.
@@ -291,7 +326,54 @@ const decisionEntryPath = writeDecisionAudit({
291
326
  belowFloor,
292
327
  files: inScopeFiles.length,
293
328
  loc: totalChangedLoc,
329
+ causalAutopsy,
294
330
  });
331
+ // Malformed autopsy blocks AFTER the audit write — the blocked attempt is
332
+ // recorded (verdict 'blocked' via the exit handler), same as every gate
333
+ // refusal. Validated-when-present: absence never reaches this.
334
+ if (autopsyError) {
335
+ blockCommit(
336
+ inScopeFiles,
337
+ `Invalid causalAutopsy in trace: ${autopsyError}\n` +
338
+ ` Shape: { "origin": "prior-pr|environment-shift|new-code|latent|unknown", "relatedPrs": [123], "notes": "..." }\n` +
339
+ ` (origin "prior-pr" requires relatedPrs; the field is otherwise optional-but-validated.)`,
340
+ );
341
+ }
342
+ // Advisory (never blocks): a fix-class commit with NO autopsy gets a loud
343
+ // nudge. Fix-class signal = branch name says fix, or a staged release-note
344
+ // fragment declares change_type: fix.
345
+ if (!causalAutopsy) {
346
+ let fixClassSignal = false;
347
+ try {
348
+ const branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd: ROOT, encoding: 'utf8' }).trim();
349
+ if (/(^|[\/-])fix([\/-]|$)/i.test(branch)) fixClassSignal = true;
350
+ } catch { /* detached HEAD / no commits — stay quiet, advisory only */ }
351
+ if (!fixClassSignal) {
352
+ try {
353
+ const staged = execSync('git diff --cached --name-only', { cwd: ROOT, encoding: 'utf8' })
354
+ .split('\n').map((s) => s.trim()).filter(Boolean);
355
+ for (const f of staged) {
356
+ if (!/^upgrades\/next\/.+\.md$/.test(f)) continue;
357
+ const fp = path.join(ROOT, f);
358
+ if (fs.existsSync(fp) && /change_type:\s*fix/.test(fs.readFileSync(fp, 'utf8'))) {
359
+ fixClassSignal = true;
360
+ break;
361
+ }
362
+ }
363
+ } catch { /* advisory only */ }
364
+ }
365
+ if (fixClassSignal) {
366
+ console.error('');
367
+ console.error('┌──────────────────────────────────────────────────────────────────┐');
368
+ console.error('│ ⚠ ADVISORY — fix-class commit with no causalAutopsy in trace. │');
369
+ console.error('│ What caused the issue this fixes? Add to your trace JSON: │');
370
+ console.error('│ "causalAutopsy": { "origin": "prior-pr|environment-shift| │');
371
+ console.error('│ new-code|latent|unknown", "relatedPrs": [N], "notes": "…" } │');
372
+ console.error('│ NOT blocked — but the meta-analysis record stays blind here. │');
373
+ console.error('└──────────────────────────────────────────────────────────────────┘');
374
+ console.error('');
375
+ }
376
+ }
295
377
  if (belowFloor) {
296
378
  console.error('');
297
379
  console.error('┌──────────────────────────────────────────────────────────────────┐');
@@ -814,7 +896,7 @@ function blockCommit(files, reason) {
814
896
  // fire, the line just evaporated with the worktree). If the commit is later
815
897
  // blocked by the gate, the staged line simply rides the retry commit — both
816
898
  // lines describe real gate evaluations.
817
- function writeDecisionAudit({ slug, suggestedTier, declaredTier, riskFloor, riskFloorReasons, belowFloor, files, loc }) {
899
+ function writeDecisionAudit({ slug, suggestedTier, declaredTier, riskFloor, riskFloorReasons, belowFloor, files, loc, causalAutopsy = null }) {
818
900
  try {
819
901
  fs.mkdirSync(DECISIONS_DIR, { recursive: true });
820
902
  const ts = new Date().toISOString();
@@ -840,6 +922,12 @@ function writeDecisionAudit({ slug, suggestedTier, declaredTier, riskFloor, risk
840
922
  belowFloor,
841
923
  files,
842
924
  loc,
925
+ // Causal autopsy (directive 2026-06-05): what caused the issue this
926
+ // commit fixes — prior-pr / environment-shift / new-code / latent /
927
+ // unknown, with linked PRs. null = not declared (advisory in slice 1).
928
+ // This is THE meta-analysis substrate: convergence vs whack-a-mole is
929
+ // a query over these entries, not archaeology.
930
+ causalAutopsy,
843
931
  // Finalized by the process exit handler below: 'pass' when the gate
844
932
  // allowed the commit, 'blocked' otherwise. The riding-the-retry design
845
933
  // (a blocked evaluation's entry rides the next successful commit, see
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-06-05T18:04:49.958Z",
5
- "instarVersion": "1.3.312",
4
+ "generatedAt": "2026-06-05T18:12:41.393Z",
5
+ "instarVersion": "1.3.313",
6
6
  "entryCount": 199,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,21 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ The instar-dev pre-commit gate accepts an optional `causalAutopsy` field in the trace JSON — { origin: prior-pr | environment-shift | new-code | latent | unknown, relatedPrs, notes } — validates it when present (malformed blocks; absence never does), copies it verbatim into the per-commit decision-audit entry, and prints an advisory nudge on fix-class commits that omit it.
9
+
10
+ ## What to Tell Your User
11
+
12
+ Every fix can now carry a durable record of what actually caused the issue it fixes. Over time that record shows whether the system is genuinely converging toward stability or just trading one bug for another — analysis that used to require digging through old conversations.
13
+
14
+ ## Summary of New Capabilities
15
+
16
+ - Causal autopsy per fix, riding the existing decision-audit files — meta-analysis over causes becomes a one-line query.
17
+ - Advisory-first rollout: fix-class commits without an autopsy get a loud nudge, never a block.
18
+
19
+ ## Evidence
20
+
21
+ Directive from Justin (2026-06-05): with the Tier-1 lane shipping fixes without an independent reviewer, causal autopsies of every issue are the compensating control for convergence. Day-one retro-autopsy of 6 issues found 5 were prior-PR assumptions invalidated by the release-cadence shift — a pattern that currently lives only in session memory. 5 new gate tests pin the contract (valid recorded verbatim / malformed blocks with attempt recorded / prior-pr requires linked PRs / absent never blocks + advisory on fix signal / no noise on non-fix commits); 9/9 in the gate audit suite.