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
|
@@ -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:
|
|
5
|
-
"instarVersion": "1.3.
|
|
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.
|