mason-context 0.21.0 → 0.23.0

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.
@@ -405,11 +405,12 @@ async function storePath(root, relative, createParents = false) {
405
405
  }
406
406
  return current;
407
407
  }
408
- async function readStoreJson(root, relative) {
408
+ async function readStoreJson(root, relative, maxBytes = STORE_MAX_BYTES) {
409
409
  try {
410
410
  const file = await storePath(root, relative);
411
- const raw = await readBoundedFile(file, 10 * 1024 * 1024);
412
- if (raw === null) throw new Error("file is not regular or exceeds 10 MiB");
411
+ const raw = await readBoundedFile(file, maxBytes);
412
+ if (raw === null)
413
+ throw new Error(`file is not regular or exceeds ${maxBytes / 1024 / 1024} MiB`);
413
414
  const parsed = JSON.parse(raw);
414
415
  if (parsed === null) throw new Error("expected a JSON object, received null");
415
416
  return parsed;
@@ -421,9 +422,9 @@ async function readStoreJson(root, relative) {
421
422
  );
422
423
  }
423
424
  }
424
- async function writeStoreJson(root, relative, value) {
425
- const payload = JSON.stringify(value, null, 2) + "\n";
426
- if (Buffer.byteLength(payload) > 10 * 1024 * 1024) {
425
+ async function writeStoreJson(root, relative, value, options = {}) {
426
+ const payload = JSON.stringify(value, null, options.compact ? void 0 : 2) + "\n";
427
+ if (Buffer.byteLength(payload) > STORE_MAX_BYTES) {
427
428
  throw new Error(`Mason store ${relative} exceeds 10 MiB`);
428
429
  }
429
430
  const file = await storePath(root, relative, true);
@@ -441,11 +442,13 @@ async function writeStoreJson(root, relative, value) {
441
442
  await fs2.rm(temporary, { force: true });
442
443
  }
443
444
  }
445
+ var STORE_MAX_BYTES;
444
446
  var init_storage = __esm({
445
447
  "src/utils/storage.ts"() {
446
448
  "use strict";
447
449
  init_paths();
448
450
  init_files();
451
+ STORE_MAX_BYTES = 10 * 1024 * 1024;
449
452
  }
450
453
  });
451
454
 
@@ -775,17 +778,7 @@ var init_lock = __esm({
775
778
  }
776
779
  });
777
780
 
778
- // src/test-map.ts
779
- import path6 from "path";
780
- var init_test_map = __esm({
781
- "src/test-map.ts"() {
782
- "use strict";
783
- init_files();
784
- }
785
- });
786
-
787
781
  // src/snapshot/snapshot.ts
788
- import path7 from "path";
789
782
  import { z as z2 } from "zod";
790
783
  async function getCurrentGitHash(rootDir) {
791
784
  try {
@@ -803,11 +796,9 @@ var init_snapshot = __esm({
803
796
  "use strict";
804
797
  init_git_read();
805
798
  init_files();
806
- init_files();
807
799
  init_storage();
808
800
  init_lock();
809
801
  init_paths();
810
- init_test_map();
811
802
  repoPath = z2.string().refine((value) => normalizeRepoPath(value) !== null, "Expected a relative repository path");
812
803
  verificationFields = {
813
804
  refreshedHash: z2.string().optional(),
@@ -838,7 +829,7 @@ var init_snapshot = __esm({
838
829
 
839
830
  // src/drift/drift.ts
840
831
  import fs5 from "fs/promises";
841
- import path8 from "path";
832
+ import path6 from "path";
842
833
  function parseChanges(output) {
843
834
  const fields = output.split("\0");
844
835
  const changes = [];
@@ -906,7 +897,7 @@ var init_drift = __esm({
906
897
  });
907
898
 
908
899
  // src/audit/commands.ts
909
- import path9 from "path";
900
+ import path7 from "path";
910
901
  function splitCommands(line) {
911
902
  const parts = [];
912
903
  let quote = "", start = 0;
@@ -942,7 +933,7 @@ function commandClaims(line, lineNo, cwd, shell = false) {
942
933
  const cd = text2.match(/^cd\s+(.+)$/);
943
934
  if (cd) {
944
935
  const directory = literal(cd[1]);
945
- cwd = directory === null || cwd === null || /\s/.test(directory) && !/^["']/.test(cd[1]) ? null : path9.posix.join(cwd ?? ".", directory);
936
+ cwd = directory === null || cwd === null || /\s/.test(directory) && !/^["']/.test(cd[1]) ? null : path7.posix.join(cwd ?? ".", directory);
946
937
  continue;
947
938
  }
948
939
  for (const match of text2.matchAll(COMMAND)) {
@@ -954,7 +945,7 @@ function commandClaims(line, lineNo, cwd, shell = false) {
954
945
  scopeUnknown = "Unsupported shell wrapper or options before the script.";
955
946
  if (selector) {
956
947
  const selected = literal(selector[1]);
957
- directory = selected === null || cwd === null ? null : path9.posix.join(cwd ?? ".", selected);
948
+ directory = selected === null || cwd === null ? null : path7.posix.join(cwd ?? ".", selected);
958
949
  } else if (flags) scopeUnknown = "Unsupported package-manager options before run: " + flags;
959
950
  const tail = text2.slice(match.index + match[0].length).split(/\s--(?:\s|$)/)[0];
960
951
  if (/(?:^|\s)(?:--workspace(?:s)?|-w|--filter|-F|--prefix|--dir|-C|--if-present)(?:\s|=|$)/.test(
@@ -989,7 +980,7 @@ var init_commands = __esm({
989
980
  );
990
981
  literal = (value) => {
991
982
  const unquoted = value.replace(/^(["'])(.*)\1$/, "$2");
992
- return /[$`*?[\]{}~\\\x00-\x1f]/.test(unquoted) || path9.posix.isAbsolute(unquoted) ? null : unquoted;
983
+ return /[$`*?[\]{}~\\\x00-\x1f]/.test(unquoted) || path7.posix.isAbsolute(unquoted) ? null : unquoted;
993
984
  };
994
985
  }
995
986
  });
@@ -1372,7 +1363,7 @@ var init_git = __esm({
1372
1363
 
1373
1364
  // src/audit/docs.ts
1374
1365
  import fs6 from "fs/promises";
1375
- import path10 from "path";
1366
+ import path8 from "path";
1376
1367
  import fg5 from "fast-glob";
1377
1368
  async function dirtyDocs(resolvedRoot, files) {
1378
1369
  if (!files.length) return /* @__PURE__ */ new Set();
@@ -1468,15 +1459,15 @@ async function localDocPaths(root) {
1468
1459
  }
1469
1460
  for (const entry of entries)
1470
1461
  if (/^(?:agents|claude|readme)\.md$/i.test(entry.name)) {
1471
- paths.push(path10.posix.join(dir, entry.name));
1462
+ paths.push(path8.posix.join(dir, entry.name));
1472
1463
  }
1473
1464
  }
1474
1465
  return paths;
1475
1466
  }
1476
1467
  function documentScope(file) {
1477
- if (!/^(readme|agents|claude)\.md$/i.test(path10.posix.basename(file))) return ".";
1478
- const parent = path10.posix.dirname(file);
1479
- return path10.posix.basename(parent).toLowerCase() === ".claude" ? path10.posix.dirname(parent) : parent;
1468
+ if (!/^(readme|agents|claude)\.md$/i.test(path8.posix.basename(file))) return ".";
1469
+ const parent = path8.posix.dirname(file);
1470
+ return path8.posix.basename(parent).toLowerCase() === ".claude" ? path8.posix.dirname(parent) : parent;
1480
1471
  }
1481
1472
  async function discoverDocs(resolvedRoot) {
1482
1473
  return profilePhase("audit.documents", () => readDocs(resolvedRoot));
@@ -1499,7 +1490,7 @@ async function readDocs(resolvedRoot) {
1499
1490
  path: candidate,
1500
1491
  content,
1501
1492
  scope: documentScope(candidate),
1502
- kind: /^readme\.md$/i.test(path10.posix.basename(candidate)) ? "readme" : /^(agents|claude)\.md$/i.test(path10.posix.basename(candidate)) ? "instructions" : "guide",
1493
+ kind: /^readme\.md$/i.test(path8.posix.basename(candidate)) ? "readme" : /^(agents|claude)\.md$/i.test(path8.posix.basename(candidate)) ? "instructions" : "guide",
1503
1494
  lineCount: content.split("\n").length,
1504
1495
  lastCommit,
1505
1496
  dirty: dirty.has(candidate),
@@ -1528,15 +1519,15 @@ var init_docs = __esm({
1528
1519
 
1529
1520
  // src/audit/scope.ts
1530
1521
  import fs7 from "fs/promises";
1531
- import path11 from "path";
1522
+ import path9 from "path";
1532
1523
  function scopedPath(directory, value) {
1533
- if (path11.posix.isAbsolute(value) || value.includes("\\") || /[\x00-\x1f]/.test(value))
1524
+ if (path9.posix.isAbsolute(value) || value.includes("\\") || /[\x00-\x1f]/.test(value))
1534
1525
  return null;
1535
- const resolved = path11.posix.normalize(path11.posix.join(directory, value));
1526
+ const resolved = path9.posix.normalize(path9.posix.join(directory, value));
1536
1527
  return resolved === ".." || resolved.startsWith("../") ? null : resolved;
1537
1528
  }
1538
1529
  function pathClaimScope(doc, claim) {
1539
- const directory = claim.relativeTo === "document" ? path11.posix.dirname(doc) : documentScope(doc);
1530
+ const directory = claim.relativeTo === "document" ? path9.posix.dirname(doc) : documentScope(doc);
1540
1531
  const local = scopedPath(directory, claim.path);
1541
1532
  if (!local) return null;
1542
1533
  const candidates = claim.relativeTo === "document" || directory === "." ? [local] : [...new Set([local, scopedPath(".", claim.path)].filter((p) => p !== null))];
@@ -1570,7 +1561,7 @@ var init_scope = __esm({
1570
1561
  });
1571
1562
 
1572
1563
  // src/audit/checks/deleted-reference.ts
1573
- import path12 from "path";
1564
+ import path10 from "path";
1574
1565
  async function checkDeletedReferences(ctx) {
1575
1566
  const result = emptyResult();
1576
1567
  for (const doc of ctx.docs) {
@@ -1605,7 +1596,7 @@ async function checkDeletedReferences(ctx) {
1605
1596
  scope.candidates.map(async (file) => ({
1606
1597
  file,
1607
1598
  tracked: await lastCommitOf(ctx.root, file),
1608
- parent: await pathExists(ctx.root, path12.posix.dirname(file))
1599
+ parent: await pathExists(ctx.root, path10.posix.dirname(file))
1609
1600
  }))
1610
1601
  );
1611
1602
  const candidate = candidates.find((c) => c.tracked || renames.has(c.file)) ?? candidates.find((c) => c.parent);
@@ -1658,7 +1649,7 @@ var init_deleted_reference = __esm({
1658
1649
  // src/audit/checks/new-module.ts
1659
1650
  import fg6 from "fast-glob";
1660
1651
  import fs8 from "fs/promises";
1661
- import path13 from "path";
1652
+ import path11 from "path";
1662
1653
  function escapeRegExp(text2) {
1663
1654
  return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1664
1655
  }
@@ -1694,7 +1685,7 @@ async function checkNewModules(ctx) {
1694
1685
  }
1695
1686
  function moduleDocumentation(docs) {
1696
1687
  return docs.map(
1697
- (doc) => doc.content + "\n" + (path13.posix.dirname(doc.path) === "." ? "" : path13.posix.dirname(doc.path) + "/")
1688
+ (doc) => doc.content + "\n" + (path11.posix.dirname(doc.path) === "." ? "" : path11.posix.dirname(doc.path) + "/")
1698
1689
  ).join("\n");
1699
1690
  }
1700
1691
  function moduleCandidates(root, combinedDocs) {
@@ -1720,9 +1711,9 @@ async function collectModuleCandidates(root, combinedDocs) {
1720
1711
  });
1721
1712
  const dirs = entries.filter(
1722
1713
  (entry) => (entry.isDirectory() || entry.isSymbolicLink() && sources.some(
1723
- (file) => file.startsWith(path13.posix.join(dir, entry.name) + "/")
1714
+ (file) => file.startsWith(path11.posix.join(dir, entry.name) + "/")
1724
1715
  )) && !entry.name.startsWith(".") && !DIR_DENYLIST.has(entry.name)
1725
- ).map((entry) => path13.posix.join(dir, entry.name));
1716
+ ).map((entry) => path11.posix.join(dir, entry.name));
1726
1717
  byParent.set(dir, dirs);
1727
1718
  })
1728
1719
  );
@@ -1742,7 +1733,7 @@ async function collectModuleCandidates(root, combinedDocs) {
1742
1733
  onlyDirectories: true,
1743
1734
  label: "Module directory discovery",
1744
1735
  select: (file) => visible.has(file)
1745
- })).map((file) => path13.posix.basename(file))
1736
+ })).map((file) => path11.posix.basename(file))
1746
1737
  );
1747
1738
  }
1748
1739
  return result;
@@ -1824,7 +1815,7 @@ var init_new_module = __esm({
1824
1815
  });
1825
1816
 
1826
1817
  // src/audit/checks/stale-count.ts
1827
- import path14 from "path";
1818
+ import path12 from "path";
1828
1819
  async function countGradleModules(root) {
1829
1820
  for (const name of ["settings.gradle.kts", "settings.gradle"]) {
1830
1821
  const content = await readAuditInput(root, name);
@@ -1861,7 +1852,7 @@ async function countNpmWorkspaces(root) {
1861
1852
  return {
1862
1853
  actual: matched.length,
1863
1854
  countedFrom: "package.json workspaces",
1864
- members: matched.map((m) => path14.dirname(m)).sort()
1855
+ members: matched.map((m) => path12.dirname(m)).sort()
1865
1856
  };
1866
1857
  }
1867
1858
  }
@@ -1892,7 +1883,7 @@ async function countNpmWorkspaces(root) {
1892
1883
  return {
1893
1884
  actual: matched.length,
1894
1885
  countedFrom: "pnpm-workspace.yaml",
1895
- members: matched.map((m) => path14.dirname(m)).sort()
1886
+ members: matched.map((m) => path12.dirname(m)).sort()
1896
1887
  };
1897
1888
  }
1898
1889
  }
@@ -1912,8 +1903,8 @@ async function countCargoCrates(root) {
1912
1903
  ignore: ["**/target/**"],
1913
1904
  label: "Cargo workspace discovery"
1914
1905
  });
1915
- for (const m of matched) members.add(path14.dirname(m));
1916
- } else if (await readAuditInput(root, path14.posix.join(entry, "Cargo.toml")) !== null) {
1906
+ for (const m of matched) members.add(path12.dirname(m));
1907
+ } else if (await readAuditInput(root, path12.posix.join(entry, "Cargo.toml")) !== null) {
1917
1908
  members.add(entry);
1918
1909
  }
1919
1910
  }
@@ -1933,7 +1924,7 @@ async function resolveCountSource(root, claim) {
1933
1924
  }
1934
1925
  async function resolveDocCountSource(root, doc, claim) {
1935
1926
  const scope = documentScope(doc);
1936
- const source = await resolveCountSource(path14.join(root, scope), claim);
1927
+ const source = await resolveCountSource(path12.join(root, scope), claim);
1937
1928
  if (!source || scope === ".") return source;
1938
1929
  return {
1939
1930
  ...source,
@@ -1986,7 +1977,7 @@ var init_stale_count = __esm({
1986
1977
  });
1987
1978
 
1988
1979
  // src/audit/checks/dead-command.ts
1989
- import path15 from "path";
1980
+ import path13 from "path";
1990
1981
  function scriptsOf(raw) {
1991
1982
  try {
1992
1983
  const pkg = JSON.parse(raw);
@@ -2030,7 +2021,7 @@ async function commandInputs(root, docs) {
2030
2021
  }
2031
2022
  let cursor = directory;
2032
2023
  while (true) {
2033
- const manifest = path15.posix.join(cursor, "package.json");
2024
+ const manifest = path13.posix.join(cursor, "package.json");
2034
2025
  scope.candidates.push(manifest);
2035
2026
  const raw = await read(manifest);
2036
2027
  if (raw !== null) {
@@ -2041,7 +2032,7 @@ async function commandInputs(root, docs) {
2041
2032
  break;
2042
2033
  }
2043
2034
  if (claim.directory !== void 0 || cursor === ".") break;
2044
- cursor = path15.posix.dirname(cursor);
2035
+ cursor = path13.posix.dirname(cursor);
2045
2036
  }
2046
2037
  if (!resolution.manifest && !resolution.problem) {
2047
2038
  resolution.problem = `No package.json for the command's scope (${directory}).`;
@@ -2127,7 +2118,7 @@ var init_dead_command = __esm({
2127
2118
  });
2128
2119
 
2129
2120
  // src/audit/dependency-claims.ts
2130
- import path16 from "path";
2121
+ import path14 from "path";
2131
2122
  import { parse as parseToml } from "smol-toml";
2132
2123
  function dependencyText(content) {
2133
2124
  return content.replace(
@@ -2138,7 +2129,7 @@ function dependencyText(content) {
2138
2129
  function declarations(file, source) {
2139
2130
  const result = /* @__PURE__ */ new Map();
2140
2131
  if (!source) return result;
2141
- if (path16.posix.basename(file) === "package.json") {
2132
+ if (path14.posix.basename(file) === "package.json") {
2142
2133
  const json = JSON.parse(source);
2143
2134
  if (!isRecord(json)) throw new Error("Invalid package manifest");
2144
2135
  for (const section of [
@@ -2470,7 +2461,12 @@ async function captureAnchorScopes(root, scopes2) {
2470
2461
  );
2471
2462
  const inventory = access.inventory();
2472
2463
  if (!inventory) throw new Error("Git anchor inventory unavailable");
2473
- const ignored = await inspectionGit(
2464
+ const visible = indexMatchingPaths(
2465
+ union.map((anchor) => [anchor]),
2466
+ inventory
2467
+ );
2468
+ const hidden = new Set(union.filter((_, index) => !visible[index].length));
2469
+ const ignored = hidden.size ? (await inspectionGit(
2474
2470
  [
2475
2471
  "ls-files",
2476
2472
  "-z",
@@ -2478,13 +2474,11 @@ async function captureAnchorScopes(root, scopes2) {
2478
2474
  "--ignored",
2479
2475
  "--exclude-standard",
2480
2476
  "--",
2481
- ...union.map((anchor) => `:(literal)${anchor}`)
2477
+ ...[...hidden].map((anchor) => `:(literal)${anchor}`)
2482
2478
  ],
2483
2479
  { cwd: root, maxBuffer: 8 * 1024 * 1024 }
2484
- );
2485
- const files = [
2486
- .../* @__PURE__ */ new Set([...inventory, ...ignored.stdout.split("\0").filter(Boolean)])
2487
- ].sort();
2480
+ )).stdout.split("\0").filter(Boolean) : [];
2481
+ const files = [.../* @__PURE__ */ new Set([...inventory, ...ignored])].sort();
2488
2482
  const patterns = await Promise.all(
2489
2483
  union.map(async (anchor) => {
2490
2484
  const literal2 = fg7.escapePath(anchor);
@@ -2500,7 +2494,7 @@ async function captureAnchorScopes(root, scopes2) {
2500
2494
  const filesByScope = new Map(
2501
2495
  scopes2.map((scope, index) => [JSON.stringify([...new Set(scope)].sort()), indexed[index]])
2502
2496
  );
2503
- return { filesByScope, eligible };
2497
+ return { filesByScope, eligible, hidden, ignored: new Set(ignored) };
2504
2498
  };
2505
2499
  return Promise.all(
2506
2500
  scopes2.map((scope) => {
@@ -2512,8 +2506,10 @@ async function captureAnchorScopes(root, scopes2) {
2512
2506
  const capture = { anchors, files: {}, complete: true };
2513
2507
  if (!anchors.length) return capture;
2514
2508
  try {
2515
- const { eligible, filesByScope } = await (context ??= prepareContext());
2516
- const files = filesByScope.get(JSON.stringify(anchors));
2509
+ const { eligible, filesByScope, hidden, ignored } = await (context ??= prepareContext());
2510
+ const files = filesByScope.get(JSON.stringify(anchors)).filter(
2511
+ (file) => !ignored.has(file) || anchors.some((anchor) => hidden.has(anchor) && anchorMatches(anchor, file))
2512
+ );
2517
2513
  if (files.length > 2e3) return { ...capture, complete: false };
2518
2514
  for (const file of files) {
2519
2515
  const evidence = await inspectionRead(
@@ -2591,7 +2587,7 @@ var init_anchors = __esm({
2591
2587
  });
2592
2588
 
2593
2589
  // src/context/lexical.ts
2594
- import path17 from "path";
2590
+ import path15 from "path";
2595
2591
  var init_lexical = __esm({
2596
2592
  "src/context/lexical.ts"() {
2597
2593
  "use strict";
@@ -2660,8 +2656,19 @@ function decisionProvenance(record, freshness = "unknown") {
2660
2656
  } : null
2661
2657
  };
2662
2658
  }
2663
- function decisionInspectionDigest(record) {
2659
+ function decisionInspectionDigest(record, scope = "all") {
2664
2660
  const effective = effectiveDecision(record);
2661
+ if (scope === "operative")
2662
+ return createHash2("sha256").update(
2663
+ JSON.stringify([
2664
+ "operative",
2665
+ record.status,
2666
+ decisionApproval(effective),
2667
+ effective.version === 2 ? effective.revision : 0,
2668
+ decisionContent(effective),
2669
+ effective.refreshedHash
2670
+ ])
2671
+ ).digest("hex");
2665
2672
  return createHash2("sha256").update(
2666
2673
  JSON.stringify([
2667
2674
  record.status,
@@ -2673,8 +2680,10 @@ function decisionInspectionDigest(record) {
2673
2680
  ])
2674
2681
  ).digest("hex");
2675
2682
  }
2676
- function latestDecisionInspection(record) {
2677
- return record.version === 2 ? record.inspections?.filter((i) => i.decisionDigest === decisionInspectionDigest(record)).at(-1) : void 0;
2683
+ function latestDecisionInspection(record, scope = "all") {
2684
+ return record.version === 2 ? record.inspections?.filter(
2685
+ (i) => (scope === "operative" || i.scope !== "operative") && i.decisionDigest === decisionInspectionDigest(record, i.scope ?? "all")
2686
+ ).at(-1) : void 0;
2678
2687
  }
2679
2688
  var text, decisionSourceSchema, attributionSchema, contentSchema, approvalSchema, statusSchema, reviewEvidenceSchema, eventSchema, legacySchema, currentSchema, decisionSchema;
2680
2689
  var init_provenance = __esm({
@@ -2760,6 +2769,7 @@ var init_provenance = __esm({
2760
2769
  inspector: text(200),
2761
2770
  note: text(1500),
2762
2771
  outcome: z4.literal("no_contradiction_found"),
2772
+ scope: z4.literal("operative").optional(),
2763
2773
  decisionDigest: z4.string().regex(/^[a-f0-9]{64}$/),
2764
2774
  headHash: z4.string(),
2765
2775
  capture: anchorCaptureSchema
@@ -2816,7 +2826,7 @@ var init_provenance = __esm({
2816
2826
 
2817
2827
  // src/decisions/decisions.ts
2818
2828
  import fs10 from "fs/promises";
2819
- import path18 from "path";
2829
+ import path16 from "path";
2820
2830
  import { createHash as createHash3 } from "crypto";
2821
2831
  import { z as z5 } from "zod";
2822
2832
  async function readDecisionInputs(rootDir) {
@@ -2908,10 +2918,39 @@ var init_decisions = __esm({
2908
2918
  }
2909
2919
  });
2910
2920
 
2921
+ // src/decisions/excerpts.ts
2922
+ import { createHash as createHash4 } from "crypto";
2923
+ import { execFile as execFile2 } from "child_process";
2924
+ import { promisify as promisify2 } from "util";
2925
+ function driftBaseline(record) {
2926
+ const effective = effectiveDecision(record);
2927
+ const inspection = latestDecisionInspection(
2928
+ record,
2929
+ decisionApproval(effective) === "accepted" ? "operative" : "all"
2930
+ );
2931
+ return {
2932
+ record: effective,
2933
+ capture: inspection?.capture ?? (effective.version === 2 ? effective.capture : void 0),
2934
+ commits: [inspection?.headHash, effective.refreshedHash].filter(
2935
+ (hash) => !!hash && /^[a-f0-9]{40,64}$/.test(hash)
2936
+ )
2937
+ };
2938
+ }
2939
+ var exec;
2940
+ var init_excerpts = __esm({
2941
+ "src/decisions/excerpts.ts"() {
2942
+ "use strict";
2943
+ init_files();
2944
+ init_paths();
2945
+ init_provenance();
2946
+ exec = promisify2(execFile2);
2947
+ }
2948
+ });
2949
+
2911
2950
  // src/decisions/drift.ts
2912
- import path19 from "path";
2951
+ import path17 from "path";
2913
2952
  async function computeDecisionDrift(rootDir, decisions) {
2914
- const resolvedRoot = path19.resolve(rootDir);
2953
+ const resolvedRoot = path17.resolve(rootDir);
2915
2954
  const store = decisions ? { records: decisions, diagnostics: [] } : await loadDecisionStore(resolvedRoot);
2916
2955
  const report = {
2917
2956
  historyAvailable: true,
@@ -2926,11 +2965,15 @@ async function computeDecisionDrift(rootDir, decisions) {
2926
2965
  ]);
2927
2966
  const changesByHash = /* @__PURE__ */ new Map();
2928
2967
  const baselines = store.records.filter((record) => record.status === "active").flatMap((record) => {
2929
- const inspection = latestDecisionInspection(record);
2930
- if (inspection) return [inspection.capture];
2931
- return [.../* @__PURE__ */ new Set([effectiveDecision(record), record])].flatMap(
2932
- (candidate) => candidate.version === 2 && candidate.capture ? [candidate.capture] : []
2933
- );
2968
+ const effective = effectiveDecision(record);
2969
+ return [.../* @__PURE__ */ new Set([effective, record])].flatMap((candidate) => {
2970
+ const inspection = latestDecisionInspection(
2971
+ record,
2972
+ candidate === effective && decisionApproval(effective) === "accepted" ? "operative" : "all"
2973
+ );
2974
+ const capture = inspection?.capture ?? (candidate.version === 2 ? candidate.capture : void 0);
2975
+ return capture ? [capture] : [];
2976
+ });
2934
2977
  });
2935
2978
  const captures = await captureAnchorScopes(
2936
2979
  resolvedRoot,
@@ -2968,13 +3011,15 @@ async function computeDecisionDrift(rootDir, decisions) {
2968
3011
  };
2969
3012
  for (const record of store.records) {
2970
3013
  if (record.status !== "active") continue;
2971
- const effective = effectiveDecision(record);
2972
- const inspection = latestDecisionInspection(record);
2973
- const state = await inspect(effective, inspection?.capture);
3014
+ const baseline = driftBaseline(record);
3015
+ const state = await inspect(baseline.record, baseline.capture);
2974
3016
  report.freshness[record.id] = state.freshness;
2975
3017
  if (state.changedFiles.length) report.staleDecisions[record.id] = state.changedFiles;
2976
- if (effective !== record)
2977
- (report.pendingProposals ??= {})[record.id] = await inspect(record, inspection?.capture);
3018
+ if (baseline.record !== record)
3019
+ (report.pendingProposals ??= {})[record.id] = await inspect(
3020
+ record,
3021
+ latestDecisionInspection(record)?.capture
3022
+ );
2978
3023
  }
2979
3024
  return report;
2980
3025
  }
@@ -2986,13 +3031,14 @@ var init_drift2 = __esm({
2986
3031
  init_paths();
2987
3032
  init_snapshot();
2988
3033
  init_decisions();
3034
+ init_excerpts();
2989
3035
  init_provenance();
2990
3036
  }
2991
3037
  });
2992
3038
 
2993
3039
  // src/audit/checks/decision-anchor.ts
2994
3040
  async function checkDecisionAnchors(ctx) {
2995
- const result = emptyResult();
3041
+ const result = { ...emptyResult(), incompleteDecisions: [] };
2996
3042
  if (!ctx.decisionsPresent) return result;
2997
3043
  const store = await loadDecisionStore(ctx.root);
2998
3044
  const records = store.records;
@@ -3024,6 +3070,7 @@ async function checkDecisionAnchors(ctx) {
3024
3070
  check: "decision-anchor-drift",
3025
3071
  reason: `Anchor evidence for decision ${record.id} is incomplete or unreadable`
3026
3072
  });
3073
+ result.incompleteDecisions.push(record.id);
3027
3074
  continue;
3028
3075
  }
3029
3076
  if (!changedFiles.length) continue;
@@ -3085,7 +3132,7 @@ var init_checks = __esm({
3085
3132
  });
3086
3133
 
3087
3134
  // src/audit/findings.ts
3088
- import { createHash as createHash4 } from "crypto";
3135
+ import { createHash as createHash5 } from "crypto";
3089
3136
  import { z as z6 } from "zod";
3090
3137
  function findingId(finding) {
3091
3138
  const e = finding.evidence;
@@ -3116,7 +3163,7 @@ var digest, checkSchema, commitSchema, anchorSchema, count, scopeSchema, evidenc
3116
3163
  var init_findings = __esm({
3117
3164
  "src/audit/findings.ts"() {
3118
3165
  "use strict";
3119
- digest = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
3166
+ digest = (value) => createHash5("sha256").update(JSON.stringify(value)).digest("hex");
3120
3167
  checkSchema = z6.enum([
3121
3168
  "deleted-reference",
3122
3169
  "new-module",
@@ -3223,6 +3270,13 @@ var init_findings = __esm({
3223
3270
  }
3224
3271
  });
3225
3272
 
3273
+ // src/automation/pending-tools.ts
3274
+ var init_pending_tools = __esm({
3275
+ "src/automation/pending-tools.ts"() {
3276
+ "use strict";
3277
+ }
3278
+ });
3279
+
3226
3280
  // src/automation/stop-review.ts
3227
3281
  import { z as z7 } from "zod";
3228
3282
  var evidenceSchema2, stopReviewSchema;
@@ -3231,6 +3285,7 @@ var init_stop_review = __esm({
3231
3285
  "use strict";
3232
3286
  init_anchors();
3233
3287
  init_findings();
3288
+ init_pending_tools();
3234
3289
  evidenceSchema2 = z7.record(z7.object({ digest: z7.string(), capture: anchorCaptureSchema }));
3235
3290
  stopReviewSchema = z7.object({
3236
3291
  pending: z7.record(z7.object({ head: z7.string(), evidence: evidenceSchema2 })),
@@ -3267,13 +3322,14 @@ import { z as z9 } from "zod";
3267
3322
  function withLock(root, directory, run, waitMs = 5e3) {
3268
3323
  return withStoreLock(root, directory, run, waitMs, "Automation");
3269
3324
  }
3270
- var hostSchema, stateSchema;
3325
+ var hostSchema, stateSchema, IDLE_PENDING_MS, STATE_READ_MAX_BYTES;
3271
3326
  var init_store = __esm({
3272
3327
  "src/automation/store.ts"() {
3273
3328
  "use strict";
3274
3329
  init_store_lock();
3275
3330
  init_stop_review();
3276
3331
  init_notifications();
3332
+ init_storage();
3277
3333
  hostSchema = z9.enum(["claude", "codex"]);
3278
3334
  stateSchema = z9.object({
3279
3335
  version: z9.literal(1),
@@ -3288,6 +3344,8 @@ var init_store = __esm({
3288
3344
  host: hostSchema,
3289
3345
  lastUsed: z9.string(),
3290
3346
  notifiedDiagnostics: z9.array(z9.string()).default([]),
3347
+ /** Digest of the upstream drift notice already shown to this session. */
3348
+ upstreamNotice: z9.string().optional(),
3291
3349
  pending: z9.record(z9.string()),
3292
3350
  stopReview: stopReviewSchema.optional(),
3293
3351
  coverageGaps: z9.array(z9.string()),
@@ -3297,10 +3355,13 @@ var init_store = __esm({
3297
3355
  updatedAt: z9.string(),
3298
3356
  fingerprint: z9.string().nullable(),
3299
3357
  latest: z9.string().nullable(),
3358
+ guidance: z9.object({ fingerprint: z9.string(), path: z9.string() }).optional(),
3300
3359
  notifications: notificationStateSchema.optional(),
3301
3360
  agentNotifications: notificationStateSchema.optional(),
3302
3361
  analysis: z9.object({ id: z9.string(), fingerprint: z9.string(), completedAt: z9.number(), path: z9.string() }).optional()
3303
3362
  });
3363
+ IDLE_PENDING_MS = 24 * 60 * 60 * 1e3;
3364
+ STATE_READ_MAX_BYTES = 64 * 1024 * 1024;
3304
3365
  }
3305
3366
  });
3306
3367
 
@@ -3319,7 +3380,7 @@ __export(repair_exports, {
3319
3380
  withRepairSession: () => withRepairSession
3320
3381
  });
3321
3382
  import fs11 from "fs/promises";
3322
- import path20 from "path";
3383
+ import path18 from "path";
3323
3384
  import { randomUUID as randomUUID2 } from "crypto";
3324
3385
  import { z as z10 } from "zod";
3325
3386
  function recheckable(finding) {
@@ -3417,8 +3478,8 @@ async function prepareOriginal(rootDir, checks, options, session) {
3417
3478
  }
3418
3479
  async function loadRepairBaseline(rootDir, baselinePath) {
3419
3480
  const root = await fs11.realpath(rootDir);
3420
- const declaredRoot = path20.resolve(rootDir);
3421
- const relative = path20.isAbsolute(baselinePath) ? path20.relative(isWithinRoot(declaredRoot, baselinePath) ? declaredRoot : root, baselinePath) : baselinePath;
3481
+ const declaredRoot = path18.resolve(rootDir);
3482
+ const relative = path18.isAbsolute(baselinePath) ? path18.relative(isWithinRoot(declaredRoot, baselinePath) ? declaredRoot : root, baselinePath) : baselinePath;
3422
3483
  const stored = baselineSchema.parse(await readStoreJson(root, relative));
3423
3484
  const { digest: savedDigest, ...payload } = stored;
3424
3485
  if (digest(payload) !== savedDigest)
@@ -3779,7 +3840,10 @@ async function decisionAssessment(root, finding, baselineHead) {
3779
3840
  );
3780
3841
  const record = store.records.find((record2) => record2.id === evidence.decisionId);
3781
3842
  if (!record || record.version !== 2) return null;
3782
- const inspection = latestDecisionInspection(record);
3843
+ const inspection = latestDecisionInspection(
3844
+ record,
3845
+ evidence.provenance?.approval === "accepted" ? "operative" : "all"
3846
+ );
3783
3847
  if (inspection && record.status === "active") {
3784
3848
  const capture = await captureAnchors(root, inspection.capture.anchors);
3785
3849
  let ancestor = true;
@@ -4074,12 +4138,12 @@ var init_advisory_review = __esm({
4074
4138
 
4075
4139
  // src/audit/audit.ts
4076
4140
  import fs13 from "fs/promises";
4077
- import path21 from "path";
4141
+ import path19 from "path";
4078
4142
  async function computeAudit(rootDir, options = {}) {
4079
4143
  return profilePhase("audit.current", () => auditCurrent(rootDir, options));
4080
4144
  }
4081
4145
  async function auditCurrent(rootDir, options) {
4082
- const resolvedRoot = path21.resolve(rootDir);
4146
+ const resolvedRoot = path19.resolve(rootDir);
4083
4147
  const [docs, headHash] = await Promise.all([
4084
4148
  discoverDocs(resolvedRoot),
4085
4149
  getCurrentGitHash(resolvedRoot)
@@ -4117,7 +4181,7 @@ async function auditCurrent(rootDir, options) {
4117
4181
  }
4118
4182
  let decisionsPresent = false;
4119
4183
  try {
4120
- await fs13.access(path21.join(resolvedRoot, ".mason", "decisions"));
4184
+ await fs13.access(path19.join(resolvedRoot, ".mason", "decisions"));
4121
4185
  decisionsPresent = true;
4122
4186
  } catch {
4123
4187
  }
@@ -4186,11 +4250,11 @@ init_audit();
4186
4250
  init_types();
4187
4251
  init_repair();
4188
4252
  init_findings();
4189
- import path23 from "path";
4253
+ import path21 from "path";
4190
4254
 
4191
4255
  // src/audit/advisory-cli.ts
4192
4256
  init_advisory_review();
4193
- import path22 from "path";
4257
+ import path20 from "path";
4194
4258
  import { stripVTControlCharacters } from "util";
4195
4259
  var USAGE = `Usage: mason audit review --baseline <path> --finding <id> [options]
4196
4260
 
@@ -4250,7 +4314,7 @@ async function runAdvisoryCli(args, io = { out: (text2) => console.log(text2), e
4250
4314
  reviewToken: flags["--token"],
4251
4315
  reasonCode: flags["--reason"]
4252
4316
  };
4253
- const result = await reviewAdvisory(path22.resolve(flags["--dir"] ?? process.cwd()), input);
4317
+ const result = await reviewAdvisory(path20.resolve(flags["--dir"] ?? process.cwd()), input);
4254
4318
  if (json) io.out(JSON.stringify(result, null, 2));
4255
4319
  else {
4256
4320
  const lines = [`Advisory review: ${result.status}.`];
@@ -4438,7 +4502,7 @@ function formatFixPrompt(report, baselinePath) {
4438
4502
  lines.push("");
4439
4503
  lines.push("RULES:");
4440
4504
  lines.push(
4441
- baselinePath ? `- Preserve the original repair baseline: ${JSON.stringify(baselinePath)}. Do not replace it after editing.` : "- Before the first edit, call mason_repair with action: prepare, or run mason-audit --prepare-repair --json with the same --dir and --checks. Keep the returned baselinePath through verification."
4505
+ baselinePath ? `- Preserve the original repair baseline: ${JSON.stringify(baselinePath)}. Do not replace it after editing.` : "- Before the first edit, run mason audit --prepare-repair --json with the same --dir and --checks. Keep the returned baselinePath through verification."
4442
4506
  );
4443
4507
  lines.push(
4444
4508
  `- Edit ONLY these files: ${flaggedDocs.join(", ") || "none (advisory review only)"}. Bring the docs into agreement with verified source evidence; do not change source code or configs to silence findings.`
@@ -4461,7 +4525,7 @@ function formatFixPrompt(report, baselinePath) {
4461
4525
  "- new-module: review whether the directory needs documenting; an omission alone does not establish a defect. Preserve scope and describe only what you verified."
4462
4526
  );
4463
4527
  lines.push(
4464
- "- Specific historical ADVISORIES require a separate authorized assessment using review_advisory with the baselinePath and findingId from verification. Prepare and inspect evidence first; record addressed, inapplicable or deferred with the actual reviewer, note and reviewToken. Dependency notices marked resolution: informational require no review. Dependency advisories may be dismissed with action: dismiss, reasonCode (no-dependency-claims or unrelated-manifest-change), and note, without a reviewer or token. Decision findings use review_decision. Candidates marked resolution: recheck can be rerun to see whether the condition remains; this does not establish semantic approval. Report what remains unknown."
4528
+ "- Specific historical ADVISORIES require a separate authorized assessment using mason audit review with --baseline and --finding from verification. Prepare and inspect evidence first; record addressed, inapplicable or deferred with the actual reviewer, note and reviewToken. Dependency notices marked resolution: informational require no review. Dependency advisories may be dismissed with --outcome dismiss, --reason (no-dependency-claims or unrelated-manifest-change), and --note, without a reviewer or token. Decision findings use review_decision. Candidates marked resolution: recheck can be rerun to see whether the condition remains; this does not establish semantic approval. Report what remains unknown."
4465
4529
  );
4466
4530
  lines.push("");
4467
4531
  lines.push(
@@ -4483,7 +4547,7 @@ function formatFixPrompt(report, baselinePath) {
4483
4547
  );
4484
4548
  lines.push("");
4485
4549
  lines.push(
4486
- "After edits, call mason_repair with action: verify and the original baselinePath, or mason-audit --verify-repair <baselinePath> --dir <project>. Repeat against the same baseline after any final documentation commit. Summarize resolved, unresolved, review-required, unverified, and new findings with their evidence. Do not report a suppressed or unavailable check as fixed. This audit covers the listed documents and supported claims; application behavior and code examples need their own validation."
4550
+ "After edits, run mason audit --verify-repair <baselinePath> --dir <project>. Repeat against the same baseline after any final documentation commit. Summarize resolved, unresolved, review-required, unverified, and new findings with their evidence. Do not report a suppressed or unavailable check as fixed. This audit covers the listed documents and supported claims; application behavior and code examples need their own validation."
4487
4551
  );
4488
4552
  return lines.join("\n");
4489
4553
  }
@@ -4514,7 +4578,7 @@ async function runAuditCli(argv, io = {
4514
4578
  io.out(USAGE2);
4515
4579
  return 0;
4516
4580
  }
4517
- const rootDir = path23.resolve(args.dir);
4581
+ const rootDir = path21.resolve(args.dir);
4518
4582
  if (args.baseline || args.prepareRepair) {
4519
4583
  try {
4520
4584
  if (args.baseline) {