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.
@@ -31,7 +31,7 @@ var init_types = __esm({
31
31
  });
32
32
 
33
33
  // src/drift/cli.ts
34
- import path11 from "path";
34
+ import path9 from "path";
35
35
 
36
36
  // src/utils/git-read.ts
37
37
  import { execFile } from "child_process";
@@ -84,10 +84,7 @@ function execGit(args, options) {
84
84
 
85
85
  // src/drift/drift.ts
86
86
  import fs4 from "fs/promises";
87
- import path6 from "path";
88
-
89
- // src/snapshot/snapshot.ts
90
- import path5 from "path";
87
+ import path4 from "path";
91
88
 
92
89
  // src/utils/validation.ts
93
90
  function isRecord(value) {
@@ -387,11 +384,13 @@ async function storePath(root, relative, createParents = false) {
387
384
  }
388
385
  return current;
389
386
  }
390
- async function readStoreJson(root, relative) {
387
+ var STORE_MAX_BYTES = 10 * 1024 * 1024;
388
+ async function readStoreJson(root, relative, maxBytes = STORE_MAX_BYTES) {
391
389
  try {
392
390
  const file = await storePath(root, relative);
393
- const raw = await readBoundedFile(file, 10 * 1024 * 1024);
394
- if (raw === null) throw new Error("file is not regular or exceeds 10 MiB");
391
+ const raw = await readBoundedFile(file, maxBytes);
392
+ if (raw === null)
393
+ throw new Error(`file is not regular or exceeds ${maxBytes / 1024 / 1024} MiB`);
395
394
  const parsed = JSON.parse(raw);
396
395
  if (parsed === null) throw new Error("expected a JSON object, received null");
397
396
  return parsed;
@@ -413,9 +412,6 @@ import fs3 from "fs/promises";
413
412
  import os from "os";
414
413
  var lockOwnerSchema = z.object({ host: z.string(), pid: z.number().int().positive() });
415
414
 
416
- // src/test-map.ts
417
- import path4 from "path";
418
-
419
415
  // src/snapshot/snapshot.ts
420
416
  var repoPath = z2.string().refine((value) => normalizeRepoPath(value) !== null, "Expected a relative repository path");
421
417
  var verificationFields = {
@@ -550,7 +546,7 @@ async function findGhostFiles(resolvedRoot, mappedFiles) {
550
546
  const ghosts = [];
551
547
  for (const file of mappedFiles) {
552
548
  try {
553
- await fs4.access(path6.join(resolvedRoot, file));
549
+ await fs4.access(path4.join(resolvedRoot, file));
554
550
  } catch {
555
551
  ghosts.push(file);
556
552
  }
@@ -558,7 +554,7 @@ async function findGhostFiles(resolvedRoot, mappedFiles) {
558
554
  return ghosts.sort();
559
555
  }
560
556
  async function computeDrift(rootDir) {
561
- const root = path6.resolve(rootDir);
557
+ const root = path4.resolve(rootDir);
562
558
  const snapshot = await loadSnapshot(root);
563
559
  if (!snapshot) return null;
564
560
  const [headHash, workingTree] = await Promise.all([
@@ -673,11 +669,11 @@ import fg3 from "fast-glob";
673
669
  // src/audit/inspection.ts
674
670
  import fg2 from "fast-glob";
675
671
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
676
- import path7 from "path";
672
+ import path5 from "path";
677
673
  var inspections = new AsyncLocalStorage2();
678
674
  function inspectionRead(root, key, read) {
679
675
  const inspection = inspections.getStore();
680
- if (!inspection || inspection.root !== path7.resolve(root)) return read();
676
+ if (!inspection || inspection.root !== path5.resolve(root)) return read();
681
677
  if (!inspection.reads.has(key)) {
682
678
  const pending = read();
683
679
  inspection.reads.set(key, pending);
@@ -718,7 +714,12 @@ async function captureAnchorScopes(root, scopes) {
718
714
  );
719
715
  const inventory = access.inventory();
720
716
  if (!inventory) throw new Error("Git anchor inventory unavailable");
721
- const ignored = await inspectionGit(
717
+ const visible = indexMatchingPaths(
718
+ union.map((anchor) => [anchor]),
719
+ inventory
720
+ );
721
+ const hidden = new Set(union.filter((_, index) => !visible[index].length));
722
+ const ignored = hidden.size ? (await inspectionGit(
722
723
  [
723
724
  "ls-files",
724
725
  "-z",
@@ -726,13 +727,11 @@ async function captureAnchorScopes(root, scopes) {
726
727
  "--ignored",
727
728
  "--exclude-standard",
728
729
  "--",
729
- ...union.map((anchor) => `:(literal)${anchor}`)
730
+ ...[...hidden].map((anchor) => `:(literal)${anchor}`)
730
731
  ],
731
732
  { cwd: root, maxBuffer: 8 * 1024 * 1024 }
732
- );
733
- const files = [
734
- .../* @__PURE__ */ new Set([...inventory, ...ignored.stdout.split("\0").filter(Boolean)])
735
- ].sort();
733
+ )).stdout.split("\0").filter(Boolean) : [];
734
+ const files = [.../* @__PURE__ */ new Set([...inventory, ...ignored])].sort();
736
735
  const patterns = await Promise.all(
737
736
  union.map(async (anchor) => {
738
737
  const literal = fg3.escapePath(anchor);
@@ -748,7 +747,7 @@ async function captureAnchorScopes(root, scopes) {
748
747
  const filesByScope = new Map(
749
748
  scopes.map((scope, index) => [JSON.stringify([...new Set(scope)].sort()), indexed[index]])
750
749
  );
751
- return { filesByScope, eligible };
750
+ return { filesByScope, eligible, hidden, ignored: new Set(ignored) };
752
751
  };
753
752
  return Promise.all(
754
753
  scopes.map((scope) => {
@@ -760,8 +759,10 @@ async function captureAnchorScopes(root, scopes) {
760
759
  const capture = { anchors, files: {}, complete: true };
761
760
  if (!anchors.length) return capture;
762
761
  try {
763
- const { eligible, filesByScope } = await (context ??= prepareContext());
764
- const files = filesByScope.get(JSON.stringify(anchors));
762
+ const { eligible, filesByScope, hidden, ignored } = await (context ??= prepareContext());
763
+ const files = filesByScope.get(JSON.stringify(anchors)).filter(
764
+ (file) => !ignored.has(file) || anchors.some((anchor) => hidden.has(anchor) && anchorMatches(anchor, file))
765
+ );
765
766
  if (files.length > 2e3) return { ...capture, complete: false };
766
767
  for (const file of files) {
767
768
  const evidence = await inspectionRead(
@@ -817,16 +818,16 @@ function changedCaptureFiles(before, after) {
817
818
  }
818
819
 
819
820
  // src/decisions/drift.ts
820
- import path10 from "path";
821
+ import path8 from "path";
821
822
 
822
823
  // src/decisions/decisions.ts
823
824
  import fs6 from "fs/promises";
824
- import path9 from "path";
825
+ import path7 from "path";
825
826
  import { createHash as createHash3 } from "crypto";
826
827
  import { z as z5 } from "zod";
827
828
 
828
829
  // src/context/lexical.ts
829
- import path8 from "path";
830
+ import path6 from "path";
830
831
 
831
832
  // src/decisions/provenance.ts
832
833
  import { z as z4 } from "zod";
@@ -908,6 +909,7 @@ var currentSchema = legacySchema.extend({
908
909
  inspector: text(200),
909
910
  note: text(1500),
910
911
  outcome: z4.literal("no_contradiction_found"),
912
+ scope: z4.literal("operative").optional(),
911
913
  decisionDigest: z4.string().regex(/^[a-f0-9]{64}$/),
912
914
  headHash: z4.string(),
913
915
  capture: anchorCaptureSchema
@@ -991,8 +993,19 @@ function effectiveDecision(record) {
991
993
  history: record.history.slice(0, index + 1)
992
994
  };
993
995
  }
994
- function decisionInspectionDigest(record) {
996
+ function decisionInspectionDigest(record, scope = "all") {
995
997
  const effective = effectiveDecision(record);
998
+ if (scope === "operative")
999
+ return createHash2("sha256").update(
1000
+ JSON.stringify([
1001
+ "operative",
1002
+ record.status,
1003
+ decisionApproval(effective),
1004
+ effective.version === 2 ? effective.revision : 0,
1005
+ decisionContent(effective),
1006
+ effective.refreshedHash
1007
+ ])
1008
+ ).digest("hex");
996
1009
  return createHash2("sha256").update(
997
1010
  JSON.stringify([
998
1011
  record.status,
@@ -1004,8 +1017,10 @@ function decisionInspectionDigest(record) {
1004
1017
  ])
1005
1018
  ).digest("hex");
1006
1019
  }
1007
- function latestDecisionInspection(record) {
1008
- return record.version === 2 ? record.inspections?.filter((i) => i.decisionDigest === decisionInspectionDigest(record)).at(-1) : void 0;
1020
+ function latestDecisionInspection(record, scope = "all") {
1021
+ return record.version === 2 ? record.inspections?.filter(
1022
+ (i) => (scope === "operative" || i.scope !== "operative") && i.decisionDigest === decisionInspectionDigest(record, i.scope ?? "all")
1023
+ ).at(-1) : void 0;
1009
1024
  }
1010
1025
 
1011
1026
  // src/decisions/decisions.ts
@@ -1084,9 +1099,29 @@ async function loadDecisionStore(rootDir) {
1084
1099
  );
1085
1100
  }
1086
1101
 
1102
+ // src/decisions/excerpts.ts
1103
+ import { createHash as createHash4 } from "crypto";
1104
+ import { execFile as execFile2 } from "child_process";
1105
+ import { promisify as promisify2 } from "util";
1106
+ var exec = promisify2(execFile2);
1107
+ function driftBaseline(record) {
1108
+ const effective = effectiveDecision(record);
1109
+ const inspection = latestDecisionInspection(
1110
+ record,
1111
+ decisionApproval(effective) === "accepted" ? "operative" : "all"
1112
+ );
1113
+ return {
1114
+ record: effective,
1115
+ capture: inspection?.capture ?? (effective.version === 2 ? effective.capture : void 0),
1116
+ commits: [inspection?.headHash, effective.refreshedHash].filter(
1117
+ (hash) => !!hash && /^[a-f0-9]{40,64}$/.test(hash)
1118
+ )
1119
+ };
1120
+ }
1121
+
1087
1122
  // src/decisions/drift.ts
1088
1123
  async function computeDecisionDrift(rootDir, decisions) {
1089
- const resolvedRoot = path10.resolve(rootDir);
1124
+ const resolvedRoot = path8.resolve(rootDir);
1090
1125
  const store = decisions ? { records: decisions, diagnostics: [] } : await loadDecisionStore(resolvedRoot);
1091
1126
  const report = {
1092
1127
  historyAvailable: true,
@@ -1101,11 +1136,15 @@ async function computeDecisionDrift(rootDir, decisions) {
1101
1136
  ]);
1102
1137
  const changesByHash = /* @__PURE__ */ new Map();
1103
1138
  const baselines = store.records.filter((record) => record.status === "active").flatMap((record) => {
1104
- const inspection = latestDecisionInspection(record);
1105
- if (inspection) return [inspection.capture];
1106
- return [.../* @__PURE__ */ new Set([effectiveDecision(record), record])].flatMap(
1107
- (candidate) => candidate.version === 2 && candidate.capture ? [candidate.capture] : []
1108
- );
1139
+ const effective = effectiveDecision(record);
1140
+ return [.../* @__PURE__ */ new Set([effective, record])].flatMap((candidate) => {
1141
+ const inspection = latestDecisionInspection(
1142
+ record,
1143
+ candidate === effective && decisionApproval(effective) === "accepted" ? "operative" : "all"
1144
+ );
1145
+ const capture = inspection?.capture ?? (candidate.version === 2 ? candidate.capture : void 0);
1146
+ return capture ? [capture] : [];
1147
+ });
1109
1148
  });
1110
1149
  const captures = await captureAnchorScopes(
1111
1150
  resolvedRoot,
@@ -1143,13 +1182,15 @@ async function computeDecisionDrift(rootDir, decisions) {
1143
1182
  };
1144
1183
  for (const record of store.records) {
1145
1184
  if (record.status !== "active") continue;
1146
- const effective = effectiveDecision(record);
1147
- const inspection = latestDecisionInspection(record);
1148
- const state = await inspect(effective, inspection?.capture);
1185
+ const baseline = driftBaseline(record);
1186
+ const state = await inspect(baseline.record, baseline.capture);
1149
1187
  report.freshness[record.id] = state.freshness;
1150
1188
  if (state.changedFiles.length) report.staleDecisions[record.id] = state.changedFiles;
1151
- if (effective !== record)
1152
- (report.pendingProposals ??= {})[record.id] = await inspect(record, inspection?.capture);
1189
+ if (baseline.record !== record)
1190
+ (report.pendingProposals ??= {})[record.id] = await inspect(
1191
+ record,
1192
+ latestDecisionInspection(record)?.capture
1193
+ );
1153
1194
  }
1154
1195
  return report;
1155
1196
  }
@@ -1270,18 +1311,14 @@ function formatRefreshPrompt(report, decisionDrift) {
1270
1311
  )
1271
1312
  );
1272
1313
  lines.push("");
1273
- const scopedFiles = [...report.changedFiles, ...report.unmappedFiles];
1274
- if (!report.historyAvailable || report.recommendation === "full-rebuild") {
1275
- lines.push(
1276
- "PROCEDURE (full rebuild): run the complete Map-Reduce build. Call generate_snapshot_batch repeatedly (follow nextOffset until null), calling save_partial_snapshot after each batch, then reduce_snapshot, then save_snapshot once with the unified map. Derive features ONLY from files shown in each batch prompt \u2014 never invent paths."
1277
- );
1278
- } else {
1314
+ lines.push(
1315
+ "PROCEDURE (scoped refresh): inspect the changed and unmapped files with your own source tools. Use get_snapshot to read the existing map, then save_snapshot with only evidence-supported corrected features/flows. Remove deleted entries with removeFeatures/removeFlows. Managed batch creation and reduction have been removed."
1316
+ );
1317
+ if (!report.historyAvailable || report.recommendation === "full-rebuild")
1279
1318
  lines.push(
1280
- "PROCEDURE (scoped refresh): call generate_snapshot_batch with the files list below \u2014 the SAME list on every call \u2014 following nextOffset until null, calling save_partial_snapshot after each batch. Then call reduce_snapshot (it merges into the existing map, preserving untouched entries) and save_snapshot once. Use save_snapshot's removeFeatures/removeFlows for features that no longer exist (see ghostFiles/renames). Derive features ONLY from files shown in each batch prompt \u2014 never invent paths."
1319
+ "History or coverage is insufficient for a bounded refresh. Inspect all affected entries directly; do not claim freshness for entries you have not checked."
1281
1320
  );
1282
- lines.push("");
1283
- lines.push(`files: ${JSON.stringify(scopedFiles)}`);
1284
- }
1321
+ lines.push(`files: ${JSON.stringify([...report.changedFiles, ...report.unmappedFiles])}`);
1285
1322
  const staleDecisionIds = Object.keys(decisionDrift.staleDecisions);
1286
1323
  if (staleDecisionIds.length > 0) {
1287
1324
  lines.push("");
@@ -1319,7 +1356,7 @@ async function runDriftCli(argv, io = {
1319
1356
  io.out(USAGE);
1320
1357
  return 0;
1321
1358
  }
1322
- const rootDir = path11.resolve(args.dir);
1359
+ const rootDir = path9.resolve(args.dir);
1323
1360
  let report;
1324
1361
  try {
1325
1362
  report = await computeDrift(rootDir);
@@ -1329,7 +1366,7 @@ async function runDriftCli(argv, io = {
1329
1366
  }
1330
1367
  if (!report) {
1331
1368
  io.err(
1332
- `No Mason snapshot found at ${path11.join(rootDir, ".mason", "snapshot.json")}. Build one via the Mason MCP server first.`
1369
+ `No Mason snapshot found at ${path9.join(rootDir, ".mason", "snapshot.json")}. Managed map creation has been removed; decisions and context work without a map.`
1333
1370
  );
1334
1371
  return 2;
1335
1372
  }