mason-context 0.21.0 → 0.22.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.
@@ -43,7 +43,7 @@ function isStringArray(value) {
43
43
 
44
44
  // src/hook/hook.ts
45
45
  import fs7 from "fs/promises";
46
- import path11 from "path";
46
+ import path9 from "path";
47
47
  import os2 from "os";
48
48
 
49
49
  // src/audit/inspection.ts
@@ -543,12 +543,11 @@ function changedCaptureFiles(before, after) {
543
543
 
544
544
  // src/decisions/decisions.ts
545
545
  import fs5 from "fs/promises";
546
- import path8 from "path";
546
+ import path6 from "path";
547
547
  import { createHash as createHash3 } from "crypto";
548
548
  import { z as z5 } from "zod";
549
549
 
550
550
  // src/snapshot/snapshot.ts
551
- import path6 from "path";
552
551
  import { z as z3 } from "zod";
553
552
 
554
553
  // src/utils/store-lock.ts
@@ -557,9 +556,6 @@ import fs4 from "fs/promises";
557
556
  import os from "os";
558
557
  var lockOwnerSchema = z2.object({ host: z2.string(), pid: z2.number().int().positive() });
559
558
 
560
- // src/test-map.ts
561
- import path5 from "path";
562
-
563
559
  // src/snapshot/snapshot.ts
564
560
  var repoPath2 = z3.string().refine((value) => normalizeRepoPath(value) !== null, "Expected a relative repository path");
565
561
  var verificationFields = {
@@ -598,7 +594,7 @@ async function getCurrentGitHash(rootDir) {
598
594
  }
599
595
 
600
596
  // src/context/lexical.ts
601
- import path7 from "path";
597
+ import path5 from "path";
602
598
 
603
599
  // src/decisions/provenance.ts
604
600
  import { z as z4 } from "zod";
@@ -708,6 +704,7 @@ var currentSchema = legacySchema.extend({
708
704
  inspector: text(200),
709
705
  note: text(1500),
710
706
  outcome: z4.literal("no_contradiction_found"),
707
+ scope: z4.literal("operative").optional(),
711
708
  decisionDigest: z4.string().regex(/^[a-f0-9]{64}$/),
712
709
  headHash: z4.string(),
713
710
  capture: anchorCaptureSchema
@@ -821,8 +818,19 @@ function decisionTrust(record, freshness) {
821
818
  freshness
822
819
  );
823
820
  }
824
- function decisionInspectionDigest(record) {
821
+ function decisionInspectionDigest(record, scope = "all") {
825
822
  const effective = effectiveDecision(record);
823
+ if (scope === "operative")
824
+ return createHash2("sha256").update(
825
+ JSON.stringify([
826
+ "operative",
827
+ record.status,
828
+ decisionApproval(effective),
829
+ effective.version === 2 ? effective.revision : 0,
830
+ decisionContent(effective),
831
+ effective.refreshedHash
832
+ ])
833
+ ).digest("hex");
826
834
  return createHash2("sha256").update(
827
835
  JSON.stringify([
828
836
  record.status,
@@ -834,8 +842,10 @@ function decisionInspectionDigest(record) {
834
842
  ])
835
843
  ).digest("hex");
836
844
  }
837
- function latestDecisionInspection(record) {
838
- return record.version === 2 ? record.inspections?.filter((i) => i.decisionDigest === decisionInspectionDigest(record)).at(-1) : void 0;
845
+ function latestDecisionInspection(record, scope = "all") {
846
+ return record.version === 2 ? record.inspections?.filter(
847
+ (i) => (scope === "operative" || i.scope !== "operative") && i.decisionDigest === decisionInspectionDigest(record, i.scope ?? "all")
848
+ ).at(-1) : void 0;
839
849
  }
840
850
  function revisionKnowledge(record, freshness) {
841
851
  return {
@@ -846,7 +856,10 @@ function revisionKnowledge(record, freshness) {
846
856
  }
847
857
  function decisionKnowledge(record, freshness = "unknown", proposalFreshness = "unknown") {
848
858
  const effective = effectiveDecision(record);
849
- const inspection = latestDecisionInspection(record);
859
+ const inspection = latestDecisionInspection(
860
+ record,
861
+ decisionApproval(effective) === "accepted" ? "operative" : "all"
862
+ );
850
863
  return {
851
864
  ...revisionKnowledge(effective, freshness),
852
865
  ...inspection ? {
@@ -856,10 +869,16 @@ function decisionKnowledge(record, freshness = "unknown", proposalFreshness = "u
856
869
  inspector: inspection.inspector,
857
870
  note: inspection.note,
858
871
  outcome: inspection.outcome,
872
+ scope: inspection.scope ?? "all",
859
873
  headHash: inspection.headHash
860
874
  }
861
875
  } : {},
862
- ...effective !== record ? { pendingProposal: revisionKnowledge(record, proposalFreshness) } : {}
876
+ ...effective !== record ? {
877
+ pendingProposal: {
878
+ ...revisionKnowledge(record, proposalFreshness),
879
+ assessment: latestDecisionInspection(record) ? "no_contradiction_found" : "not_assessed"
880
+ }
881
+ } : {}
863
882
  };
864
883
  }
865
884
  var DECISION_GUIDANCE = "Accepted decisions are recorded team constraints, subject to freshness checks. A pendingProposal is an unaccepted replacement; the accepted revision remains operative until explicit acceptance or retirement. Proposals are suggestions; legacy unreviewed records need confirmation. Use review_decision to prepare evidence and record a separate source inspection without human approval, or an authorized human review; identities and sources are recorded assertions, not authenticated proof.";
@@ -944,11 +963,11 @@ async function loadDecisionStore(rootDir) {
944
963
  import { createHash as createHash4 } from "crypto";
945
964
 
946
965
  // src/decisions/drift.ts
947
- import path10 from "path";
966
+ import path8 from "path";
948
967
 
949
968
  // src/drift/drift.ts
950
969
  import fs6 from "fs/promises";
951
- import path9 from "path";
970
+ import path7 from "path";
952
971
  function parseChanges(output) {
953
972
  const fields = output.split("\0");
954
973
  const changes = [];
@@ -1009,7 +1028,7 @@ async function getWorkingTree(resolvedRoot) {
1009
1028
 
1010
1029
  // src/decisions/drift.ts
1011
1030
  async function computeDecisionDrift(rootDir, decisions) {
1012
- const resolvedRoot = path10.resolve(rootDir);
1031
+ const resolvedRoot = path8.resolve(rootDir);
1013
1032
  const store = decisions ? { records: decisions, diagnostics: [] } : await loadDecisionStore(resolvedRoot);
1014
1033
  const report = {
1015
1034
  historyAvailable: true,
@@ -1024,11 +1043,15 @@ async function computeDecisionDrift(rootDir, decisions) {
1024
1043
  ]);
1025
1044
  const changesByHash = /* @__PURE__ */ new Map();
1026
1045
  const baselines = store.records.filter((record) => record.status === "active").flatMap((record) => {
1027
- const inspection = latestDecisionInspection(record);
1028
- if (inspection) return [inspection.capture];
1029
- return [.../* @__PURE__ */ new Set([effectiveDecision(record), record])].flatMap(
1030
- (candidate) => candidate.version === 2 && candidate.capture ? [candidate.capture] : []
1031
- );
1046
+ const effective = effectiveDecision(record);
1047
+ return [.../* @__PURE__ */ new Set([effective, record])].flatMap((candidate) => {
1048
+ const inspection = latestDecisionInspection(
1049
+ record,
1050
+ candidate === effective && decisionApproval(effective) === "accepted" ? "operative" : "all"
1051
+ );
1052
+ const capture = inspection?.capture ?? (candidate.version === 2 ? candidate.capture : void 0);
1053
+ return capture ? [capture] : [];
1054
+ });
1032
1055
  });
1033
1056
  const captures = await captureAnchorScopes(
1034
1057
  resolvedRoot,
@@ -1067,12 +1090,18 @@ async function computeDecisionDrift(rootDir, decisions) {
1067
1090
  for (const record of store.records) {
1068
1091
  if (record.status !== "active") continue;
1069
1092
  const effective = effectiveDecision(record);
1070
- const inspection = latestDecisionInspection(record);
1093
+ const inspection = latestDecisionInspection(
1094
+ record,
1095
+ decisionApproval(effective) === "accepted" ? "operative" : "all"
1096
+ );
1071
1097
  const state = await inspect(effective, inspection?.capture);
1072
1098
  report.freshness[record.id] = state.freshness;
1073
1099
  if (state.changedFiles.length) report.staleDecisions[record.id] = state.changedFiles;
1074
1100
  if (effective !== record)
1075
- (report.pendingProposals ??= {})[record.id] = await inspect(record, inspection?.capture);
1101
+ (report.pendingProposals ??= {})[record.id] = await inspect(
1102
+ record,
1103
+ latestDecisionInspection(record)?.capture
1104
+ );
1076
1105
  }
1077
1106
  return report;
1078
1107
  }
@@ -1100,9 +1129,9 @@ async function exists(p) {
1100
1129
  async function findMasonRoot(startDir) {
1101
1130
  let dir = startDir;
1102
1131
  for (let i = 0; i < MAX_WALK_UP; i++) {
1103
- if (await exists(path11.join(dir, ".mason", "decisions"))) return dir;
1104
- if (await exists(path11.join(dir, ".git"))) return null;
1105
- const parent = path11.dirname(dir);
1132
+ if (await exists(path9.join(dir, ".mason", "decisions"))) return dir;
1133
+ if (await exists(path9.join(dir, ".git"))) return null;
1134
+ const parent = path9.dirname(dir);
1106
1135
  if (parent === dir) return null;
1107
1136
  dir = parent;
1108
1137
  }
@@ -1170,14 +1199,15 @@ async function runHook(stdinText, env = {}) {
1170
1199
  if (input.tool_name && !SUPPORTED_TOOLS.has(input.tool_name)) return null;
1171
1200
  const filePath = input.tool_input?.file_path;
1172
1201
  if (!filePath || typeof filePath !== "string") return null;
1173
- const absPath = path11.isAbsolute(filePath) ? filePath : path11.resolve(input.cwd ?? process.cwd(), filePath);
1174
- const root = await findMasonRoot(path11.dirname(absPath));
1202
+ const absPath = path9.isAbsolute(filePath) ? filePath : path9.resolve(input.cwd ?? process.cwd(), filePath);
1203
+ const root = await findMasonRoot(path9.dirname(absPath));
1175
1204
  if (!root) return null;
1176
- const relPath = path11.relative(root, absPath).split(path11.sep).join("/");
1205
+ if (env.rootDir && await fs7.realpath(root) !== await fs7.realpath(env.rootDir)) return null;
1206
+ const relPath = path9.relative(root, absPath).split(path9.sep).join("/");
1177
1207
  if (relPath.startsWith("..")) return null;
1178
1208
  const { records } = await loadDecisionStore(root);
1179
1209
  const stateDir = env.stateDir ?? os2.tmpdir();
1180
- const stateFile = path11.join(
1210
+ const stateFile = path9.join(
1181
1211
  stateDir,
1182
1212
  `mason-hook-${createHash4("sha256").update(root).digest("hex").slice(0, 12)}-${stateKey(input)}.json`
1183
1213
  );
@@ -1204,7 +1234,7 @@ async function runHook(stdinText, env = {}) {
1204
1234
  }
1205
1235
  return JSON.stringify({
1206
1236
  hookSpecificOutput: {
1207
- hookEventName: "PostToolUse",
1237
+ hookEventName: env.hookEventName ?? "PostToolUse",
1208
1238
  additionalContext: formatContext(relPath, selected, drift)
1209
1239
  }
1210
1240
  });