truthmark 2.2.0 → 2.2.1

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/dist/main.js CHANGED
@@ -618,14 +618,14 @@ var parseTruthDocumentsFromYaml = (sectionLines, areaName, options) => {
618
618
  const diagnostics = [];
619
619
  const truthDocumentEntries = [];
620
620
  for (const rawEntry of rawEntries) {
621
- const path14 = rawEntry && typeof rawEntry === "object" && "path" in rawEntry ? rawEntry.path : null;
621
+ const path12 = rawEntry && typeof rawEntry === "object" && "path" in rawEntry ? rawEntry.path : null;
622
622
  const kind = rawEntry && typeof rawEntry === "object" && "kind" in rawEntry ? rawEntry.kind : null;
623
623
  const lane = rawEntry && typeof rawEntry === "object" && "lane" in rawEntry ? rawEntry.lane : null;
624
- const inferredKind = typeof path14 === "string" ? inferTruthDocumentKindFromPath(path14, options) : null;
625
- const inferredLane = typeof path14 === "string" ? inferTruthDocumentLaneFromPath(path14, options) : null;
624
+ const inferredKind = typeof path12 === "string" ? inferTruthDocumentKindFromPath(path12, options) : null;
625
+ const inferredLane = typeof path12 === "string" ? inferTruthDocumentLaneFromPath(path12, options) : null;
626
626
  const normalizedKind = isTruthDocumentKind(kind) ? kind : inferredKind;
627
627
  const normalizedLane = lane === "product" || lane === "engineering" ? lane : normalizedKind ? laneForTruthDocumentKind(normalizedKind) : inferredLane;
628
- if (typeof path14 !== "string" || path14.trim().length === 0 || !normalizedKind || !normalizedLane) {
628
+ if (typeof path12 !== "string" || path12.trim().length === 0 || !normalizedKind || !normalizedLane) {
629
629
  diagnostics.push(
630
630
  createAreaDiagnostic(
631
631
  `Area ${areaName} truth_documents entries must include non-empty path plus valid lane and kind fields.`,
@@ -635,7 +635,7 @@ var parseTruthDocumentsFromYaml = (sectionLines, areaName, options) => {
635
635
  continue;
636
636
  }
637
637
  truthDocumentEntries.push({
638
- path: path14.trim(),
638
+ path: path12.trim(),
639
639
  kind: normalizedKind,
640
640
  kindSource: isTruthDocumentKind(kind) ? "explicit" : "inferred",
641
641
  lane: normalizedLane,
@@ -2059,7 +2059,7 @@ var EVIDENCE_AUTHORITY_INSTRUCTIONS = [
2059
2059
  "Implementation code and canonical truth docs are inspected evidence for current behavior; they do not silently override workflow write boundaries."
2060
2060
  ].join("\n");
2061
2061
  var REPOSITORY_INTELLIGENCE_INSTRUCTIONS = [
2062
- "Repository intelligence artifacts are optional derived context: RepoIndex, RouteMap, ImpactSet, and ContextPack may guide routing, context selection, and verification planning when available.",
2062
+ "Repository intelligence artifacts are optional derived context: RepoIndex, RouteMap, ImpactSet, and WorkflowState/action context may guide routing, write boundaries, and verification planning when available.",
2063
2063
  "They do not override checkout evidence, canonical truth docs, route files, or workflow write boundaries.",
2064
2064
  "If unavailable, inspect any present Truthmark config, route files, source files, truth docs, and tests directly, then report that repository-intelligence artifacts were not generated."
2065
2065
  ].join("\n");
@@ -3275,7 +3275,7 @@ ${renderTruthDocOwnershipGateSection(
3275
3275
  ${TRUTH_DOC_DECISION_RATIONALE_PRESERVATION_INSTRUCTIONS}
3276
3276
  ${FEATURE_DOC_TEMPLATE_INSTRUCTIONS}
3277
3277
  ${renderTruthDocRestructureGateSection(
3278
- "Truth Sync may restructure only truth docs impacted by the current functional-code change."
3278
+ "Truth Sync may restructure leased canonical truth docs when the current sync evidence shows repository truth is stale, even when the doc is outside the initially affected route focus."
3279
3279
  )}
3280
3280
  ${ARCHITECTURE_DOC_BOUNDARY_INSTRUCTIONS}
3281
3281
  ${renderRouteFirstEvidenceGateSection(
@@ -3302,7 +3302,7 @@ Parent post-sync verification:
3302
3302
  - block if functional code changed during sync
3303
3303
  - for each write lease, validate the worker report against the actual worker diff, allowedWrites, forbiddenWrites, identity fields, filesChanged, offLeaseChanges, blockers, and required report fields before accepting it
3304
3304
  - validate the final report against the structured Truth Sync report contract, including Claim, indented Evidence, and Result values supported, narrowed, removed, or blocked under Evidence checked
3305
- - verify the updated docs correspond to the reviewed changed-code surface
3305
+ - verify the updated docs correspond to reviewed checkout evidence, changed-code impact, or a recorded stale-truth correction made within the sync write lease
3306
3306
  - verify the final report records ownership review, structure requirement, split, restructure, or blocked reason when the ownership gate fired
3307
3307
  - blocked outcomes must preserve the working tree as-is: no rollback, no post-block cleanup edits, and manual-review reporting of any remaining files`;
3308
3308
  };
@@ -3865,8 +3865,8 @@ var renderTruthmarkSkillPackage = ({
3865
3865
  }
3866
3866
  return files;
3867
3867
  };
3868
- var normalizeOpenCodePermissionPath = (path14) => {
3869
- const normalized = path14.replace(/\\/gu, "/").replace(/^\.\//u, "").replace(/\/+$/u, "");
3868
+ var normalizeOpenCodePermissionPath = (path12) => {
3869
+ const normalized = path12.replace(/\\/gu, "/").replace(/^\.\//u, "").replace(/\/+$/u, "");
3870
3870
  return normalized === "" ? "." : normalized;
3871
3871
  };
3872
3872
  var appendOpenCodePermissionGlob = (root, glob) => {
@@ -4994,8 +4994,8 @@ var geminiFiles = (config, block) => {
4994
4994
  return files;
4995
4995
  };
4996
4996
  var instructionBlockFiles = (paths, block) => {
4997
- return paths.map((path14) => ({
4998
- path: path14,
4997
+ return paths.map((path12) => ({
4998
+ path: path12,
4999
4999
  content: block,
5000
5000
  managedBlock: true
5001
5001
  }));
@@ -5119,16 +5119,16 @@ var upsertManagedBlock = (existingContent, block) => {
5119
5119
 
5120
5120
  ${block}`;
5121
5121
  };
5122
- var writeManagedAgentsFile = async (rootDir, path14 = "AGENTS.md", block) => {
5122
+ var writeManagedAgentsFile = async (rootDir, path12 = "AGENTS.md", block) => {
5123
5123
  let existingContent = null;
5124
5124
  try {
5125
- existingContent = await fs7.readFile(resolveRepoPath(rootDir, path14), "utf8");
5125
+ existingContent = await fs7.readFile(resolveRepoPath(rootDir, path12), "utf8");
5126
5126
  } catch (error) {
5127
5127
  if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
5128
5128
  throw error;
5129
5129
  }
5130
5130
  }
5131
- return writeRepoFile(rootDir, path14, upsertManagedBlock(existingContent, block));
5131
+ return writeRepoFile(rootDir, path12, upsertManagedBlock(existingContent, block));
5132
5132
  };
5133
5133
  var diagnosticCategoryForPath = (filePath, config) => {
5134
5134
  if (filePath === "AGENTS.md") {
@@ -5394,8 +5394,53 @@ var checkAuthority = async (rootDir, config) => {
5394
5394
  // src/checks/frontmatter.ts
5395
5395
  import fs10 from "fs/promises";
5396
5396
 
5397
+ // src/markdown/frontmatter.ts
5398
+ import { parse as parse3 } from "yaml";
5399
+ var openingDelimiterPattern = /^(?:\uFEFF)?---[ \t]*(?:\r?\n|$)/u;
5400
+ var closingDelimiterPattern = /^---[ \t]*(?:\r?\n|$)/mu;
5401
+ var asRecord = (value) => {
5402
+ if (value && typeof value === "object" && !Array.isArray(value)) {
5403
+ return value;
5404
+ }
5405
+ return {};
5406
+ };
5407
+ var parseFrontmatter = (source, options = {}) => {
5408
+ const openingMatch = openingDelimiterPattern.exec(source);
5409
+ if (!openingMatch) {
5410
+ return {
5411
+ data: {},
5412
+ content: source.replace(/^\uFEFF/u, "")
5413
+ };
5414
+ }
5415
+ const bodyStart = openingMatch[0].length;
5416
+ const bodyAndContent = source.slice(bodyStart);
5417
+ const closingMatch = closingDelimiterPattern.exec(bodyAndContent);
5418
+ if (!closingMatch || typeof closingMatch.index !== "number") {
5419
+ return {
5420
+ data: {},
5421
+ content: source.replace(/^\uFEFF/u, "")
5422
+ };
5423
+ }
5424
+ const yamlSource = bodyAndContent.slice(0, closingMatch.index);
5425
+ const content = bodyAndContent.slice(closingMatch.index + closingMatch[0].length);
5426
+ let data = {};
5427
+ if (yamlSource.trim().length > 0) {
5428
+ try {
5429
+ data = asRecord(parse3(yamlSource));
5430
+ } catch (error) {
5431
+ if (options.throwOnInvalid) {
5432
+ throw error;
5433
+ }
5434
+ data = {};
5435
+ }
5436
+ }
5437
+ return {
5438
+ data,
5439
+ content
5440
+ };
5441
+ };
5442
+
5397
5443
  // src/markdown/parse.ts
5398
- import matter from "gray-matter";
5399
5444
  import { unified } from "unified";
5400
5445
  import remarkParse from "remark-parse";
5401
5446
  import { visit } from "unist-util-visit";
@@ -5409,7 +5454,7 @@ var isInternalLink = (url) => {
5409
5454
  return url.startsWith("#") || !url.includes("://") && !url.startsWith("mailto:");
5410
5455
  };
5411
5456
  var parseMarkdownDocument = (source) => {
5412
- const parsed = matter(source);
5457
+ const parsed = parseFrontmatter(source);
5413
5458
  const tree = unified().use(remarkParse).parse(parsed.content);
5414
5459
  const headings = [];
5415
5460
  const internalLinks = [];
@@ -5452,6 +5497,7 @@ var checkFrontmatter = async (rootDir, config, markdownPaths, truthDocumentEntri
5452
5497
  const source = await fs10.readFile(absolutePath, "utf8");
5453
5498
  let document;
5454
5499
  try {
5500
+ parseFrontmatter(source, { throwOnInvalid: true });
5455
5501
  document = parseMarkdownDocument(source);
5456
5502
  } catch (error) {
5457
5503
  diagnostics.push({
@@ -6646,24 +6692,18 @@ var checkGeneratedSurfaces = async (rootDir, config) => {
6646
6692
  };
6647
6693
 
6648
6694
  // src/impact/build.ts
6649
- import path11 from "path";
6695
+ import path10 from "path";
6650
6696
  import micromatch7 from "micromatch";
6651
6697
 
6652
- // src/repo-index/build.ts
6653
- import fs18 from "fs/promises";
6654
- import path9 from "path";
6655
-
6656
6698
  // src/repo-index/file-tree.ts
6657
6699
  import fs16 from "fs/promises";
6658
6700
  import path7 from "path";
6659
6701
  import { execa as execa2 } from "execa";
6660
6702
  import fg5 from "fast-glob";
6661
- import matter3 from "gray-matter";
6662
6703
  import micromatch6 from "micromatch";
6663
6704
 
6664
6705
  // src/truth/source-references.ts
6665
6706
  import path6 from "path";
6666
- import matter2 from "gray-matter";
6667
6707
  var repoRootPrefixes = [
6668
6708
  ".codex/",
6669
6709
  ".github/",
@@ -6724,7 +6764,7 @@ var parseSourceReferencesSection = (content) => {
6724
6764
  return references;
6725
6765
  };
6726
6766
  var parseSourceReferences = (source, truthDocPath) => {
6727
- const parsed = matter2(source);
6767
+ const parsed = parseFrontmatter(source);
6728
6768
  const references = /* @__PURE__ */ new Set();
6729
6769
  const frontmatterSourceOfTruth = Array.isArray(parsed.data.source_of_truth) ? parsed.data.source_of_truth : [];
6730
6770
  for (const entry of frontmatterSourceOfTruth) {
@@ -6752,17 +6792,6 @@ var languageByExtension = /* @__PURE__ */ new Map([
6752
6792
  [".yaml", "yaml"],
6753
6793
  [".toml", "toml"]
6754
6794
  ]);
6755
- var sourceExtensions = /* @__PURE__ */ new Set([
6756
- ".ts",
6757
- ".tsx",
6758
- ".js",
6759
- ".jsx",
6760
- ".mjs",
6761
- ".cjs"
6762
- ]);
6763
- var isJavaScriptLikePath = (filePath) => {
6764
- return sourceExtensions.has(path7.posix.extname(filePath));
6765
- };
6766
6795
  var isTestPath = (filePath) => {
6767
6796
  return filePath.startsWith("tests/") || filePath.includes("/__tests__/") || /(?:^|[./-])(test|spec)\.[cm]?[jt]sx?$/u.test(path7.posix.basename(filePath));
6768
6797
  };
@@ -6868,7 +6897,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
6868
6897
  }
6869
6898
  if (kind === "doc") {
6870
6899
  const source = await fs16.readFile(path7.join(rootDir, filePath), "utf8");
6871
- const parsed = matter3(source);
6900
+ const parsed = parseFrontmatter(source);
6872
6901
  const markdown = parseMarkdownDocument(parsed.content);
6873
6902
  const title = markdown.headings.find((heading) => heading.depth === 1)?.text ?? null;
6874
6903
  const sourceOfTruth = parseSourceReferences(source, filePath);
@@ -6998,96 +7027,6 @@ var buildRouteMap = async (rootDir) => {
6998
7027
  };
6999
7028
  };
7000
7029
 
7001
- // src/repo-index/typescript-symbols.ts
7002
- import ts from "typescript";
7003
- var sortStrings = (values) => [...new Set(values)].sort();
7004
- var hasExportModifier = (node) => {
7005
- return Boolean(
7006
- ts.canHaveModifiers(node) && ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)
7007
- );
7008
- };
7009
- var declarationName = (node) => {
7010
- if (!node.name || !ts.isIdentifier(node.name)) {
7011
- return null;
7012
- }
7013
- return node.name.text;
7014
- };
7015
- var addExport = (exports, publicSymbols, path14, name, kind) => {
7016
- if (!name) {
7017
- return;
7018
- }
7019
- const entry = { path: path14, name, kind };
7020
- exports.push(entry);
7021
- publicSymbols.push(entry);
7022
- };
7023
- var analyzeTypeScriptSource = (path14, source) => {
7024
- const sourceFile = ts.createSourceFile(path14, source, ts.ScriptTarget.Latest, true);
7025
- const imports = [];
7026
- const exports = [];
7027
- const publicSymbols = [];
7028
- for (const statement of sourceFile.statements) {
7029
- if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier)) {
7030
- const imported = [];
7031
- const clause = statement.importClause;
7032
- if (clause?.name) {
7033
- imported.push("default");
7034
- }
7035
- if (clause?.namedBindings && ts.isNamespaceImport(clause.namedBindings)) {
7036
- imported.push("*");
7037
- }
7038
- if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
7039
- for (const element of clause.namedBindings.elements) {
7040
- imported.push(element.propertyName?.text ?? element.name.text);
7041
- }
7042
- }
7043
- imports.push({
7044
- from: path14,
7045
- specifier: statement.moduleSpecifier.text,
7046
- imported: sortStrings(imported)
7047
- });
7048
- continue;
7049
- }
7050
- if (ts.isExportDeclaration(statement)) {
7051
- if (statement.exportClause && ts.isNamedExports(statement.exportClause)) {
7052
- for (const element of statement.exportClause.elements) {
7053
- addExport(exports, publicSymbols, path14, element.name.text, "re-export");
7054
- }
7055
- }
7056
- continue;
7057
- }
7058
- if (ts.isFunctionDeclaration(statement) && hasExportModifier(statement)) {
7059
- addExport(exports, publicSymbols, path14, declarationName(statement), "function");
7060
- continue;
7061
- }
7062
- if (ts.isClassDeclaration(statement) && hasExportModifier(statement)) {
7063
- addExport(exports, publicSymbols, path14, declarationName(statement), "class");
7064
- continue;
7065
- }
7066
- if (ts.isInterfaceDeclaration(statement) && hasExportModifier(statement)) {
7067
- addExport(exports, publicSymbols, path14, declarationName(statement), "interface");
7068
- continue;
7069
- }
7070
- if (ts.isTypeAliasDeclaration(statement) && hasExportModifier(statement)) {
7071
- addExport(exports, publicSymbols, path14, declarationName(statement), "type");
7072
- continue;
7073
- }
7074
- if (ts.isEnumDeclaration(statement) && hasExportModifier(statement)) {
7075
- addExport(exports, publicSymbols, path14, declarationName(statement), "enum");
7076
- continue;
7077
- }
7078
- if (ts.isVariableStatement(statement) && hasExportModifier(statement)) {
7079
- for (const declaration of statement.declarationList.declarations) {
7080
- addExport(exports, publicSymbols, path14, declarationName(declaration), "const");
7081
- }
7082
- }
7083
- }
7084
- return {
7085
- imports: imports.sort((left, right) => left.specifier.localeCompare(right.specifier)),
7086
- exports: exports.sort((left, right) => left.name.localeCompare(right.name)),
7087
- publicSymbols: publicSymbols.sort((left, right) => left.name.localeCompare(right.name))
7088
- };
7089
- };
7090
-
7091
7030
  // src/repo-index/build.ts
7092
7031
  var buildRepoIndex = async (cwd) => {
7093
7032
  const repository = await getGitRepository(cwd);
@@ -7100,20 +7039,7 @@ var buildRepoIndex = async (cwd) => {
7100
7039
  discoverRepoFiles(rootDir, ignore),
7101
7040
  buildRouteMap(rootDir)
7102
7041
  ]);
7103
- const imports = [];
7104
- const exports = [];
7105
- const publicSymbols = [];
7106
7042
  diagnostics.push(...routeMap.diagnostics);
7107
- for (const file of fileTree.files) {
7108
- if (!isJavaScriptLikePath(file.path)) {
7109
- continue;
7110
- }
7111
- const source = await fs18.readFile(path9.join(rootDir, file.path), "utf8");
7112
- const analysis = analyzeTypeScriptSource(file.path, source);
7113
- imports.push(...analysis.imports);
7114
- exports.push(...analysis.exports);
7115
- publicSymbols.push(...analysis.publicSymbols);
7116
- }
7117
7043
  return {
7118
7044
  schemaVersion: "repo-index/v0",
7119
7045
  repository: {
@@ -7125,11 +7051,6 @@ var buildRepoIndex = async (cwd) => {
7125
7051
  files: fileTree.files,
7126
7052
  docs: fileTree.docs,
7127
7053
  tests: fileTree.tests,
7128
- imports: imports.sort((left, right) => `${left.from}:${left.specifier}`.localeCompare(`${right.from}:${right.specifier}`)),
7129
- exports: exports.sort((left, right) => `${left.path}:${left.name}`.localeCompare(`${right.path}:${right.name}`)),
7130
- publicSymbols: publicSymbols.sort(
7131
- (left, right) => `${left.path}:${left.name}`.localeCompare(`${right.path}:${right.name}`)
7132
- ),
7133
7054
  routeMap,
7134
7055
  diagnostics
7135
7056
  };
@@ -7139,8 +7060,8 @@ var buildRepoIndex = async (cwd) => {
7139
7060
  import { execa as execa4 } from "execa";
7140
7061
 
7141
7062
  // src/git/changes.ts
7142
- import fs19 from "fs/promises";
7143
- import path10 from "path";
7063
+ import fs18 from "fs/promises";
7064
+ import path9 from "path";
7144
7065
  import { execa as execa3 } from "execa";
7145
7066
  var normalizePath3 = (filePath) => {
7146
7067
  return filePath.replaceAll("\\", "/").replace(/^\.\//u, "");
@@ -7151,7 +7072,7 @@ var listChangedPaths = async (cwd, args) => {
7151
7072
  };
7152
7073
  var pathExists4 = async (filePath) => {
7153
7074
  try {
7154
- await fs19.access(filePath);
7075
+ await fs18.access(filePath);
7155
7076
  return true;
7156
7077
  } catch {
7157
7078
  return false;
@@ -7195,7 +7116,7 @@ var getUncommittedChanges = async (cwd) => {
7195
7116
  const deletedPathCandidates = /* @__PURE__ */ new Set([...stagedDeletedPaths, ...unstagedDeletedPaths]);
7196
7117
  for (const deletedPath of deletedPathCandidates) {
7197
7118
  const change = getOrCreateChange(changesByPath, deletedPath);
7198
- change.deleted = !await pathExists4(path10.join(rootDir, deletedPath));
7119
+ change.deleted = !await pathExists4(path9.join(rootDir, deletedPath));
7199
7120
  }
7200
7121
  return Array.from(changesByPath.values()).sort((left, right) => {
7201
7122
  return left.path.localeCompare(right.path);
@@ -7279,14 +7200,6 @@ var getChangedFiles = async (cwd, base) => {
7279
7200
  diagnostics
7280
7201
  };
7281
7202
  };
7282
- var readBaseFile = async (cwd, base, filePath) => {
7283
- const repository = await getGitRepository(cwd);
7284
- const result = await execa4("git", ["show", `${base}:${filePath}`], {
7285
- cwd: repository.worktreePath,
7286
- reject: false
7287
- });
7288
- return (result.exitCode ?? 1) === 0 ? result.stdout : null;
7289
- };
7290
7203
 
7291
7204
  // src/impact/build.ts
7292
7205
  var uniqueSorted2 = (values) => [...new Set(values)].sort();
@@ -7304,65 +7217,15 @@ var toImpactRoute = (route) => ({
7304
7217
  truthDocs: [...route.truthDocs].sort(),
7305
7218
  codeSurface: [...route.codeSurface].sort()
7306
7219
  });
7307
- var changedPathSet = (changedFiles) => {
7308
- return new Set(
7309
- changedFiles.flatMap((file) => [file.path, ...file.previousPath ? [file.previousPath] : []])
7310
- );
7311
- };
7312
7220
  var changedFilePaths = (changedFile) => {
7313
7221
  return [changedFile.path, ...changedFile.previousPath ? [changedFile.previousPath] : []];
7314
7222
  };
7315
- var resolveImportPath = (importEdge) => {
7316
- if (!importEdge.specifier.startsWith(".")) {
7317
- return null;
7318
- }
7319
- const basePath = path11.posix.normalize(path11.posix.join(path11.posix.dirname(importEdge.from), importEdge.specifier));
7320
- const withoutExtension = basePath.replace(/\.[cm]?[jt]sx?$/u, "");
7321
- return withoutExtension;
7322
- };
7323
- var importTargetsChangedFile = (importEdge, changedPath) => {
7324
- const resolved = resolveImportPath(importEdge);
7325
- if (!resolved) {
7326
- return false;
7327
- }
7328
- return changedPath.replace(/\.[cm]?[jt]sx?$/u, "") === resolved;
7329
- };
7330
7223
  var pathSegments = (filePath) => filePath.split("/").filter(Boolean);
7331
7224
  var testHintMatchesChangedFile = (hints, changedPath) => {
7332
- const changedBaseName = path11.posix.basename(changedPath);
7225
+ const changedBaseName = path10.posix.basename(changedPath);
7333
7226
  const changedSegments = pathSegments(changedPath);
7334
7227
  return hints.some((hint) => changedBaseName.startsWith(hint) || changedSegments.includes(hint));
7335
7228
  };
7336
- var changedSymbolsFor = async (cwd, base, basePath, currentPath, currentExports) => {
7337
- if (!isJavaScriptLikePath(basePath) && !isJavaScriptLikePath(currentPath)) {
7338
- return [];
7339
- }
7340
- const baseSource = await readBaseFile(cwd, base, basePath);
7341
- const baseExports = baseSource ? analyzeTypeScriptSource(basePath, baseSource).exports : [];
7342
- const currentByName = new Map(currentExports.map((entry) => [entry.name, entry]));
7343
- const baseByName = new Map(baseExports.map((entry) => [entry.name, entry]));
7344
- const changes = [];
7345
- if (basePath !== currentPath) {
7346
- for (const entry of currentExports) {
7347
- changes.push({ path: currentPath, name: entry.name, kind: entry.kind, change: "added" });
7348
- }
7349
- for (const entry of baseExports) {
7350
- changes.push({ path: basePath, name: entry.name, kind: entry.kind, change: "removed" });
7351
- }
7352
- return changes;
7353
- }
7354
- for (const [name, entry] of currentByName) {
7355
- if (!baseByName.has(name)) {
7356
- changes.push({ path: currentPath, name, kind: entry.kind, change: "added" });
7357
- }
7358
- }
7359
- for (const [name, entry] of baseByName) {
7360
- if (!currentByName.has(name)) {
7361
- changes.push({ path: basePath, name, kind: entry.kind, change: "removed" });
7362
- }
7363
- }
7364
- return changes;
7365
- };
7366
7229
  var buildImpactSet = async (cwd, options) => {
7367
7230
  const repository = await getGitRepository(cwd);
7368
7231
  const rootDir = repository.worktreePath;
@@ -7377,7 +7240,6 @@ var buildImpactSet = async (cwd, options) => {
7377
7240
  const affectedRoutes = /* @__PURE__ */ new Map();
7378
7241
  const affectedTruthDocs = [];
7379
7242
  const affectedTests = [];
7380
- const changedPublicSymbols = [];
7381
7243
  const knownTestPaths = new Set(repoIndex.tests.map((test) => test.path));
7382
7244
  for (const changedFile of changedFiles) {
7383
7245
  const routeCandidatePaths = changedFilePaths(changedFile);
@@ -7410,60 +7272,15 @@ var buildImpactSet = async (cwd, options) => {
7410
7272
  file: changedFile.path
7411
7273
  });
7412
7274
  }
7413
- const currentExports = repoIndex.exports.filter((entry) => entry.path === changedFile.path);
7414
- changedPublicSymbols.push(
7415
- ...await changedSymbolsFor(
7416
- rootDir,
7417
- options.base,
7418
- changedFile.previousPath ?? changedFile.path,
7419
- changedFile.path,
7420
- currentExports
7421
- )
7422
- );
7423
7275
  }
7424
7276
  const uniqueAffectedTruthDocs = uniqueSorted2(affectedTruthDocs);
7425
- const changedPaths = changedPathSet(changedFiles);
7426
- for (const symbol of changedPublicSymbols) {
7427
- if (uniqueAffectedTruthDocs.length === 0) {
7428
- diagnostics.push({
7429
- category: "impact",
7430
- severity: "review",
7431
- message: `Changed public symbol ${symbol.name} in ${symbol.path} has no affected truth document.`,
7432
- file: symbol.path,
7433
- data: {
7434
- symbol: symbol.name,
7435
- change: symbol.change
7436
- }
7437
- });
7438
- continue;
7439
- }
7440
- if (!uniqueAffectedTruthDocs.some((truthDoc) => changedPaths.has(truthDoc))) {
7441
- diagnostics.push({
7442
- category: "impact",
7443
- severity: "review",
7444
- message: `Changed public symbol ${symbol.name} in ${symbol.path} has affected truth docs but none were changed in this impact set.`,
7445
- file: symbol.path,
7446
- data: {
7447
- symbol: symbol.name,
7448
- change: symbol.change,
7449
- affectedTruthDocs: uniqueAffectedTruthDocs
7450
- }
7451
- });
7452
- }
7453
- }
7454
7277
  for (const test of repoIndex.tests) {
7455
- const testImports = repoIndex.imports.filter((edge) => edge.from === test.path);
7456
- const importsChangedFile = changedFiles.some(
7457
- (changedFile) => changedFilePaths(changedFile).some(
7458
- (filePath) => testImports.some((importEdge) => importTargetsChangedFile(importEdge, filePath))
7459
- )
7460
- );
7461
7278
  const hintMatchesChangedFile = changedFiles.some(
7462
7279
  (changedFile) => changedFilePaths(changedFile).some(
7463
7280
  (filePath) => testHintMatchesChangedFile(test.targetHints, filePath)
7464
7281
  )
7465
7282
  );
7466
- if (importsChangedFile || hintMatchesChangedFile) {
7283
+ if (hintMatchesChangedFile) {
7467
7284
  affectedTests.push(test.path);
7468
7285
  }
7469
7286
  }
@@ -7477,9 +7294,6 @@ var buildImpactSet = async (cwd, options) => {
7477
7294
  ),
7478
7295
  affectedTruthDocs: uniqueAffectedTruthDocs,
7479
7296
  affectedTests: uniqueSorted2(affectedTests),
7480
- changedPublicSymbols: changedPublicSymbols.sort(
7481
- (left, right) => `${left.path}:${left.name}:${left.change}`.localeCompare(`${right.path}:${right.name}:${right.change}`)
7482
- ),
7483
7297
  diagnostics
7484
7298
  };
7485
7299
  };
@@ -7505,14 +7319,13 @@ var checkFreshness = async (rootDir, _config, _truthDocumentPaths, base) => {
7505
7319
  };
7506
7320
 
7507
7321
  // src/evidence/validate.ts
7508
- import fs21 from "fs/promises";
7322
+ import fs20 from "fs/promises";
7509
7323
  import fg7 from "fast-glob";
7510
7324
 
7511
7325
  // src/evidence/parse.ts
7512
- import fs20 from "fs/promises";
7513
- import path12 from "path";
7514
- import matter4 from "gray-matter";
7515
- import { parse as parse3 } from "yaml";
7326
+ import fs19 from "fs/promises";
7327
+ import path11 from "path";
7328
+ import { parse as parse4 } from "yaml";
7516
7329
  var yamlFencePattern = /```ya?ml\s*\n([\s\S]*?)```/giu;
7517
7330
  var topLevelEvidenceMarkerPattern = /^evidence\s*:/imu;
7518
7331
  var toEvidenceReference = (truthDocPath, raw) => {
@@ -7530,8 +7343,8 @@ var toEvidenceReference = (truthDocPath, raw) => {
7530
7343
  };
7531
7344
  };
7532
7345
  var parseEvidenceReferences = async (rootDir, truthDocPath) => {
7533
- const source = await fs20.readFile(path12.join(rootDir, truthDocPath), "utf8");
7534
- const parsed = matter4(source);
7346
+ const source = await fs19.readFile(path11.join(rootDir, truthDocPath), "utf8");
7347
+ const parsed = parseFrontmatter(source);
7535
7348
  const references = [];
7536
7349
  for (const entry of parseSourceReferences(source, truthDocPath)) {
7537
7350
  references.push({
@@ -7545,7 +7358,7 @@ var parseEvidenceReferences = async (rootDir, truthDocPath) => {
7545
7358
  if (!topLevelEvidenceMarkerPattern.test(yamlBlock)) {
7546
7359
  continue;
7547
7360
  }
7548
- const block = parse3(yamlBlock);
7361
+ const block = parse4(yamlBlock);
7549
7362
  const rawEvidence = block && typeof block === "object" && "evidence" in block ? block.evidence : null;
7550
7363
  if (!Array.isArray(rawEvidence)) {
7551
7364
  continue;
@@ -7563,7 +7376,7 @@ var parseEvidenceReferences = async (rootDir, truthDocPath) => {
7563
7376
  // src/evidence/validate.ts
7564
7377
  var pathExists5 = async (filePath) => {
7565
7378
  try {
7566
- await fs21.access(filePath);
7379
+ await fs20.access(filePath);
7567
7380
  return true;
7568
7381
  } catch {
7569
7382
  return false;
@@ -7601,15 +7414,6 @@ var validateGlob = async (rootDir, reference) => {
7601
7414
  });
7602
7415
  return matches.length > 0 ? null : diagnosticFor(reference, `Referenced file pattern ${reference.path} does not match any file.`);
7603
7416
  };
7604
- var validateSymbol = async (rootDir, reference) => {
7605
- if (!reference.symbol || !isJavaScriptLikePath(reference.path)) {
7606
- return null;
7607
- }
7608
- const source = await fs21.readFile(resolveRepoPath(rootDir, reference.path), "utf8");
7609
- const analysis = analyzeTypeScriptSource(reference.path, source);
7610
- const hasSymbol = analysis.publicSymbols.some((symbol) => symbol.name === reference.symbol);
7611
- return hasSymbol ? null : diagnosticFor(reference, `Evidence symbol ${reference.symbol} was not found in ${reference.path}.`);
7612
- };
7613
7417
  var validateHash = async (rootDir, reference) => {
7614
7418
  if (!reference.contentHash) {
7615
7419
  return null;
@@ -7617,7 +7421,7 @@ var validateHash = async (rootDir, reference) => {
7617
7421
  if (!reference.contentHash.startsWith("sha256:")) {
7618
7422
  return diagnosticFor(reference, `Evidence hash for ${reference.path} must use sha256:.`);
7619
7423
  }
7620
- const source = await fs21.readFile(resolveRepoPath(rootDir, reference.path), "utf8");
7424
+ const source = await fs20.readFile(resolveRepoPath(rootDir, reference.path), "utf8");
7621
7425
  const lines = source.split("\n");
7622
7426
  const startLine = reference.startLine ?? 1;
7623
7427
  const endLine = reference.endLine ?? lines.length;
@@ -7631,7 +7435,7 @@ var validateLineSpan = async (rootDir, reference) => {
7631
7435
  if (reference.startLine === void 0 && reference.endLine === void 0) {
7632
7436
  return null;
7633
7437
  }
7634
- const source = await fs21.readFile(resolveRepoPath(rootDir, reference.path), "utf8");
7438
+ const source = await fs20.readFile(resolveRepoPath(rootDir, reference.path), "utf8");
7635
7439
  const lines = source.split("\n");
7636
7440
  const startLine = reference.startLine ?? 1;
7637
7441
  const endLine = reference.endLine ?? lines.length;
@@ -7650,10 +7454,6 @@ var validateReference = async (rootDir, reference) => {
7650
7454
  diagnostics.push(diagnosticFor(reference, `Referenced file ${reference.path} does not exist.`));
7651
7455
  return diagnostics;
7652
7456
  }
7653
- const symbolDiagnostic = await validateSymbol(rootDir, reference);
7654
- if (symbolDiagnostic) {
7655
- diagnostics.push(symbolDiagnostic);
7656
- }
7657
7457
  const lineSpanDiagnostic = await validateLineSpan(rootDir, reference);
7658
7458
  if (lineSpanDiagnostic) {
7659
7459
  diagnostics.push(lineSpanDiagnostic);
@@ -7910,195 +7710,11 @@ var runCheck = async (cwd, options = {}) => {
7910
7710
  };
7911
7711
  };
7912
7712
 
7913
- // src/context-pack/build.ts
7914
- import fs22 from "fs/promises";
7915
- import path13 from "path";
7916
- import fg8 from "fast-glob";
7917
- var uniqueSorted3 = (values) => [...new Set(values)].sort();
7918
- var repoRootPrefixes2 = [".codex/", ".github/", ".truthmark/", "docs/", "src/", "tests/"];
7919
- var isGlobReference2 = (referencePath) => /[*?[\]{}()]/u.test(referencePath);
7920
- var normalizeDocReferencePath = (docPath, referencePath) => {
7921
- const strippedPath = referencePath.split("#")[0]?.trim() ?? "";
7922
- if (strippedPath.length === 0 || strippedPath.startsWith("/")) {
7923
- return null;
7924
- }
7925
- const isRepoRelative = repoRootPrefixes2.some((prefix) => strippedPath.startsWith(prefix));
7926
- const normalized = isRepoRelative ? path13.posix.normalize(strippedPath) : path13.posix.normalize(path13.posix.join(path13.posix.dirname(docPath), strippedPath));
7927
- return normalized === ".." || normalized.startsWith("../") ? null : normalized;
7928
- };
7929
- var readIfExists = async (rootDir, filePath) => {
7930
- try {
7931
- return await fs22.readFile(path13.join(rootDir, filePath), "utf8");
7932
- } catch {
7933
- return null;
7934
- }
7935
- };
7936
- var boundContent = (content) => {
7937
- const lines = content.split("\n");
7938
- if (lines.length <= 200) {
7939
- return { content, truncated: false };
7940
- }
7941
- return {
7942
- content: [...lines.slice(0, 80), "...", ...lines.slice(-40)].join("\n"),
7943
- truncated: true
7944
- };
7945
- };
7946
- var boundedContent = (filePath, content, warnings) => {
7947
- const bounded = boundContent(content);
7948
- if (!bounded.truncated) {
7949
- return { path: filePath, content, truncated: false };
7950
- }
7951
- warnings.push({
7952
- category: "context-pack",
7953
- severity: "review",
7954
- message: `Context source file ${filePath} was truncated to fit ContextPack v0 bounds.`,
7955
- file: filePath
7956
- });
7957
- return {
7958
- path: filePath,
7959
- content: bounded.content,
7960
- truncated: true
7961
- };
7962
- };
7963
- var documentsFor = async (rootDir, paths, warnings) => {
7964
- const documents = [];
7965
- for (const filePath of uniqueSorted3(paths)) {
7966
- const content = await readIfExists(rootDir, filePath);
7967
- if (content !== null) {
7968
- const bounded = boundContent(content);
7969
- if (bounded.truncated) {
7970
- warnings.push({
7971
- category: "context-pack",
7972
- severity: "review",
7973
- message: `Context truth doc ${filePath} was truncated to fit ContextPack v0 bounds.`,
7974
- file: filePath
7975
- });
7976
- }
7977
- documents.push({ path: filePath, content: bounded.content, truncated: bounded.truncated });
7978
- }
7979
- }
7980
- return documents;
7981
- };
7982
- var sourceFilesFor = async (rootDir, paths, warnings) => {
7983
- const sourceFiles = [];
7984
- for (const filePath of uniqueSorted3(paths)) {
7985
- const content = await readIfExists(rootDir, filePath);
7986
- if (content !== null) {
7987
- sourceFiles.push(boundedContent(filePath, content, warnings));
7988
- }
7989
- }
7990
- return sourceFiles;
7991
- };
7992
- var sourceOfTruthPathsFor = async (rootDir, docs, truthDocPaths) => {
7993
- const selectedTruthDocs = new Set(truthDocPaths);
7994
- const sourcePaths = [];
7995
- for (const doc of docs) {
7996
- if (!selectedTruthDocs.has(doc.path)) {
7997
- continue;
7998
- }
7999
- for (const referencePath of doc.sourceOfTruth) {
8000
- const normalizedPath = normalizeDocReferencePath(doc.path, referencePath);
8001
- if (!normalizedPath) {
8002
- continue;
8003
- }
8004
- if (isGlobReference2(normalizedPath)) {
8005
- sourcePaths.push(
8006
- ...await fg8(normalizedPath, {
8007
- cwd: rootDir,
8008
- dot: true,
8009
- onlyFiles: true,
8010
- followSymbolicLinks: false
8011
- })
8012
- );
8013
- } else {
8014
- sourcePaths.push(normalizedPath);
8015
- }
8016
- }
8017
- }
8018
- return uniqueSorted3(sourcePaths);
8019
- };
8020
- var writePathsFor = (workflow, routeIndexPath, truthDocs, routes) => {
8021
- if (workflow === "truth-sync") {
8022
- return uniqueSorted3([routeIndexPath, ...truthDocs]);
8023
- }
8024
- if (workflow === "truth-document") {
8025
- return uniqueSorted3([routeIndexPath, ...truthDocs]);
8026
- }
8027
- return uniqueSorted3(routes.flatMap((route) => route.codeSurface));
8028
- };
8029
- var testCommandsFor = (affectedTests) => {
8030
- return affectedTests.length === 0 ? ["npm test"] : [`npm test -- ${affectedTests.join(" ")}`];
8031
- };
8032
- var buildContextPack = async (cwd, options) => {
8033
- const repoIndex = await buildRepoIndex(cwd);
8034
- const rootDir = repoIndex.repository.root;
8035
- const loadResult = await loadConfig(rootDir);
8036
- const config = loadResult.config ?? (loadResult.status === "missing" ? createDefaultConfig() : null);
8037
- const impactSet = options.base ? await buildImpactSet(rootDir, { base: options.base }) : null;
8038
- const routeMap = impactSet ? repoIndex.routeMap : repoIndex.routeMap;
8039
- const warnings = loadResult.status === "invalid" ? [...loadResult.diagnostics] : [];
8040
- const truthDocPaths = impactSet?.affectedTruthDocs ?? (options.workflow === "truth-realize" ? [] : routeMap.routes.flatMap((route) => route.truthDocs));
8041
- const contextRoutes = impactSet?.affectedRoutes ?? (options.workflow === "truth-realize" ? [] : routeMap.routes);
8042
- if (options.workflow === "truth-realize" && !impactSet) {
8043
- warnings.push({
8044
- category: "context-pack",
8045
- severity: "review",
8046
- message: "truth-realize requires --base to derive bounded allowed write paths."
8047
- });
8048
- }
8049
- const sourceOfTruthPaths = await sourceOfTruthPathsFor(rootDir, repoIndex.docs, truthDocPaths);
8050
- const sourceFilePaths = uniqueSorted3([
8051
- ...impactSet?.changedFiles.filter((file) => !file.deleted).map((file) => file.path) ?? [],
8052
- ...sourceOfTruthPaths
8053
- ]);
8054
- return {
8055
- schemaVersion: "context-pack/v0",
8056
- workflow: options.workflow,
8057
- base: options.base ?? null,
8058
- impactSet,
8059
- routeMap,
8060
- allowedWritePaths: config === null ? [] : writePathsFor(
8061
- options.workflow,
8062
- config.truthmark.paths.routesIndex,
8063
- truthDocPaths,
8064
- contextRoutes
8065
- ),
8066
- truthDocs: await documentsFor(rootDir, truthDocPaths, warnings),
8067
- sourceFiles: await sourceFilesFor(rootDir, sourceFilePaths, warnings),
8068
- testCommands: testCommandsFor(impactSet?.affectedTests ?? []),
8069
- warnings
8070
- };
8071
- };
8072
-
8073
- // src/context-pack/render.ts
8074
- var renderContextPackMarkdown = (pack) => {
8075
- const lines = [
8076
- `# Truthmark ContextPack (${pack.workflow})`,
8077
- "",
8078
- `Schema: ${pack.schemaVersion}`,
8079
- `Base: ${pack.base ?? "none"}`,
8080
- "",
8081
- "## Allowed Write Paths",
8082
- ...pack.allowedWritePaths.map((filePath) => `- ${filePath}`),
8083
- "",
8084
- "## Truth Docs",
8085
- ...pack.truthDocs.map((doc) => `- ${doc.path}${doc.truncated ? " (truncated)" : ""}`),
8086
- "",
8087
- "## Source Files",
8088
- ...pack.sourceFiles.map((file) => `- ${file.path}${file.truncated ? " (truncated)" : ""}`),
8089
- "",
8090
- "## Test Commands",
8091
- ...pack.testCommands.map((command) => `- ${command}`)
8092
- ];
8093
- return `${lines.join("\n")}
8094
- `;
8095
- };
8096
-
8097
7713
  // src/workflow-state/build.ts
8098
7714
  import { execa as execa5 } from "execa";
8099
7715
 
8100
7716
  // src/workflow-state/action-context.ts
8101
- var uniqueSorted4 = (values) => [...new Set(values.filter((value) => value.length > 0))].sort();
7717
+ var uniqueSorted3 = (values) => [...new Set(values.filter((value) => value.length > 0))].sort();
8102
7718
  var helperCommandsFor = (manifestEntry) => (manifestEntry.helpers ?? []).map((helper) => ({
8103
7719
  id: helper.id,
8104
7720
  runner: helper.runner,
@@ -8108,8 +7724,8 @@ var helperCommandsFor = (manifestEntry) => (manifestEntry.helpers ?? []).map((he
8108
7724
  var evidenceFor = (manifestEntry) => manifestEntry.requiredGates.filter((gate) => /evidence|ownership|containment/iu.test(gate));
8109
7725
  var baseContext = (manifestEntry, mode, allowedWritePaths, forbiddenWritePaths, writeLeaseRequired) => ({
8110
7726
  mode,
8111
- allowedWritePaths: uniqueSorted4(allowedWritePaths),
8112
- forbiddenWritePaths: uniqueSorted4(forbiddenWritePaths),
7727
+ allowedWritePaths: uniqueSorted3(allowedWritePaths),
7728
+ forbiddenWritePaths: uniqueSorted3(forbiddenWritePaths),
8113
7729
  stopConditions: [...manifestEntry.negativeTriggers, ...manifestEntry.forbiddenAdjacency],
8114
7730
  requiredEvidence: evidenceFor(manifestEntry),
8115
7731
  helperValidationCommands: helperCommandsFor(manifestEntry),
@@ -8171,7 +7787,7 @@ var helperCommandsFor2 = (workflow) => (TRUTHMARK_WORKFLOW_MANIFEST[workflow].he
8171
7787
  argv: [...helper.command.argv],
8172
7788
  optional: helper.optional
8173
7789
  }));
8174
- var uniqueSorted5 = (values) => [...new Set(values.filter((value) => value.length > 0))].sort();
7790
+ var uniqueSorted4 = (values) => [...new Set(values.filter((value) => value.length > 0))].sort();
8175
7791
  var isWriteCapable = (workflow) => !["truthmark-preview", "truthmark-check"].includes(workflow);
8176
7792
  var DEFAULT_BASE_CANDIDATES = ["@{upstream}", "origin/main", "main", "origin/master", "master"];
8177
7793
  var selectComparisonBase = async (rootDir, suppliedBase) => {
@@ -8189,7 +7805,7 @@ var selectComparisonBase = async (rootDir, suppliedBase) => {
8189
7805
  }
8190
7806
  return null;
8191
7807
  };
8192
- var routeFilesFor = (repoIndex) => uniqueSorted5(repoIndex.routeMap.routes.map((route) => route.sourcePath));
7808
+ var routeFilesFor = (repoIndex) => uniqueSorted4(repoIndex.routeMap.routes.map((route) => route.sourcePath));
8193
7809
  var hasUnmappedFunctionalChange = (impactSet) => impactSet?.diagnostics.some(
8194
7810
  (diagnostic) => diagnostic.category === "impact" && /not mapped to a Truthmark route|no affected truth document/u.test(diagnostic.message)
8195
7811
  ) ?? false;
@@ -8200,7 +7816,7 @@ var applicabilityFor = (workflow, diagnostics, impactSet) => {
8200
7816
  return { state: isWriteCapable(workflow) ? "blocked" : "not_applicable", reasons };
8201
7817
  }
8202
7818
  if (workflow === "truthmark-sync" && !impactSet) {
8203
- reasons.push("truthmark-sync requires --base to derive bounded truth-doc write paths.");
7819
+ reasons.push("truthmark-sync requires --base to derive changed-file impact before exposing sync write paths.");
8204
7820
  return { state: "blocked", reasons };
8205
7821
  }
8206
7822
  if (workflow === "truthmark-realize" && !impactSet) {
@@ -8222,16 +7838,15 @@ var contextDataFor = (workflow, repoIndex, config, impactSet) => {
8222
7838
  return {};
8223
7839
  }
8224
7840
  const routeFiles = routeFilesFor(repoIndex);
8225
- const truthDocs = uniqueSorted5(
8226
- impactSet?.affectedTruthDocs ?? repoIndex.routeMap.routes.flatMap((route) => route.truthDocs)
8227
- );
7841
+ const indexedTruthDocs = uniqueSorted4(repoIndex.routeMap.routes.flatMap((route) => route.truthDocs));
7842
+ const truthDocs = workflow === "truthmark-sync" ? indexedTruthDocs : uniqueSorted4(impactSet?.affectedTruthDocs ?? indexedTruthDocs);
8228
7843
  return {
8229
7844
  routeIndexPath: config.truthmark.paths.routesIndex,
8230
7845
  routeFiles,
8231
7846
  truthRoot: config.truthmark.paths.truthRoot,
8232
7847
  truthDocs,
8233
7848
  starterTruthDocs: workflow === "truthmark-structure" ? truthDocs : [],
8234
- codeWritePaths: workflow === "truthmark-realize" ? uniqueSorted5(impactSet?.affectedRoutes.flatMap((route) => route.codeSurface) ?? []) : [],
7849
+ codeWritePaths: workflow === "truthmark-realize" ? uniqueSorted4(impactSet?.affectedRoutes.flatMap((route) => route.codeSurface) ?? []) : [],
8235
7850
  portalEnabled: config.truthmark.generated.portal.enabled,
8236
7851
  portalOutputPath: config.truthmark.paths.portalOutput,
8237
7852
  routes: repoIndex.routeMap.routes
@@ -8243,7 +7858,7 @@ var nextStepsFor = (workflow, applicability, comparisonBase) => {
8243
7858
  }
8244
7859
  if ((workflow === "truthmark-sync" || workflow === "truthmark-realize") && applicability.state === "blocked" && !comparisonBase) {
8245
7860
  return [
8246
- workflow === "truthmark-sync" ? "Rerun with --base <ref> so Truthmark can derive bounded truth-doc write paths." : "Rerun with --base <ref> so Truthmark can derive bounded allowed code-write paths."
7861
+ workflow === "truthmark-sync" ? "Rerun with --base <ref> so Truthmark can derive changed-file impact before exposing sync write paths." : "Rerun with --base <ref> so Truthmark can derive bounded allowed code-write paths."
8247
7862
  ];
8248
7863
  }
8249
7864
  return [];
@@ -8279,7 +7894,8 @@ var buildWorkflowState = async (cwd, options) => {
8279
7894
  checks: {
8280
7895
  required: [...manifestEntry.requiredGates],
8281
7896
  recommended: [...manifestEntry.positiveTriggers],
8282
- helpers: helperCommandsFor2(options.workflow)
7897
+ helpers: helperCommandsFor2(options.workflow),
7898
+ affectedTests: impactSet?.affectedTests ?? []
8283
7899
  },
8284
7900
  nextSteps: nextStepsFor(options.workflow, applicability, comparisonBase),
8285
7901
  reportSections: [...manifestEntry.reportSections]
@@ -8287,7 +7903,7 @@ var buildWorkflowState = async (cwd, options) => {
8287
7903
  };
8288
7904
 
8289
7905
  // src/cli/handlers.ts
8290
- import fs23 from "fs/promises";
7906
+ import fs21 from "fs/promises";
8291
7907
 
8292
7908
  // src/agents/workflow-helper-validation.ts
8293
7909
  import { parse as parseYaml2 } from "yaml";
@@ -8649,12 +8265,6 @@ var runImpact = async (options) => {
8649
8265
  }
8650
8266
  };
8651
8267
  };
8652
- var isContextPackWorkflow = (value) => {
8653
- return value === "truth-sync" || value === "truth-document" || value === "truth-realize";
8654
- };
8655
- var isContextMarkdownFormat = (value) => {
8656
- return value === void 0 || value === "markdown";
8657
- };
8658
8268
  var isTruthmarkWorkflowId = (value) => {
8659
8269
  return typeof value === "string" && TRUTHMARK_WORKFLOW_IDS.includes(value);
8660
8270
  };
@@ -8674,7 +8284,7 @@ var invalidWorkflowResult = (command, workflow) => ({
8674
8284
  });
8675
8285
  var readHelperFile = async (filePath, helper) => {
8676
8286
  try {
8677
- return await fs23.readFile(filePath, "utf8");
8287
+ return await fs21.readFile(filePath, "utf8");
8678
8288
  } catch (error) {
8679
8289
  const message = error instanceof Error ? error.message : String(error);
8680
8290
  return { ok: false, helper, errors: [`could not read file: ${message}`] };
@@ -8699,63 +8309,6 @@ var runValidateWriteLease = async (leaseFile, changedFilesFile) => {
8699
8309
  }
8700
8310
  return validateWriteLeaseText(leaseText, changedText);
8701
8311
  };
8702
- var runContext = async (options) => {
8703
- if (!isContextPackWorkflow(options.workflow)) {
8704
- return {
8705
- command: "context",
8706
- summary: "Truthmark context requires a supported --workflow value.",
8707
- diagnostics: [
8708
- {
8709
- category: "context-pack",
8710
- severity: "error",
8711
- message: "truthmark context requires --workflow truth-sync, truth-document, or truth-realize."
8712
- }
8713
- ]
8714
- };
8715
- }
8716
- if (options.format === "json") {
8717
- return {
8718
- command: "context",
8719
- summary: "Truthmark context no longer supports JSON ContextPack output.",
8720
- diagnostics: [
8721
- {
8722
- category: "context-pack",
8723
- severity: "error",
8724
- message: "JSON ContextPack output was removed in v2; use --format markdown."
8725
- }
8726
- ]
8727
- };
8728
- }
8729
- if (!isContextMarkdownFormat(options.format)) {
8730
- return {
8731
- command: "context",
8732
- summary: "Truthmark context requires a supported --format value.",
8733
- diagnostics: [
8734
- {
8735
- category: "context-pack",
8736
- severity: "error",
8737
- message: "truthmark context supports only --format markdown; JSON ContextPack output was removed in v2."
8738
- }
8739
- ]
8740
- };
8741
- }
8742
- const contextPack = await buildContextPack(process.cwd(), {
8743
- workflow: options.workflow,
8744
- base: options.base
8745
- });
8746
- const diagnostics = contextPack.warnings;
8747
- const summary = `Truthmark context generated ${contextPack.workflow} ContextPack with ${diagnostics.length} warnings.`;
8748
- const markdown = renderContextPackMarkdown(contextPack);
8749
- return {
8750
- command: "context",
8751
- summary,
8752
- diagnostics,
8753
- data: {
8754
- markdown,
8755
- summary
8756
- }
8757
- };
8758
- };
8759
8312
  var runWorkflowStatus = async (options) => {
8760
8313
  if (!isTruthmarkWorkflowId(options.workflow)) {
8761
8314
  return invalidWorkflowResult("workflow status", options.workflow);
@@ -8790,14 +8343,6 @@ var writeResult = (result, options) => {
8790
8343
  `);
8791
8344
  markFailedWhenErrorDiagnosticsExist(result);
8792
8345
  };
8793
- var writeContextResult = (result, options) => {
8794
- if (!options.json && typeof result.data?.markdown === "string") {
8795
- process.stdout.write(result.data.markdown);
8796
- markFailedWhenErrorDiagnosticsExist(result);
8797
- return;
8798
- }
8799
- writeResult(result, options);
8800
- };
8801
8346
  var renderValidationHuman = (result) => {
8802
8347
  if (result.ok === true) {
8803
8348
  return [`${result.helper}: ok`, ...result.checks.map((check) => `- ${check}`)].join("\n");
@@ -8842,7 +8387,7 @@ var buildProgram = () => {
8842
8387
  writeResult(await runCheck2({ base: options.base }), options);
8843
8388
  });
8844
8389
  addJsonOption(
8845
- program.command("index").description("Build the deterministic Truthmark repository index.")
8390
+ program.command("index").description("Inspect derived Truthmark workflow routing metadata for the current checkout.")
8846
8391
  ).action(async (options) => {
8847
8392
  writeResult(await runIndex(), options);
8848
8393
  });
@@ -8851,18 +8396,6 @@ var buildProgram = () => {
8851
8396
  ).action(async (options) => {
8852
8397
  writeResult(await runImpact({ base: options.base }), options);
8853
8398
  });
8854
- addJsonOption(
8855
- program.command("context").description("Generate a bounded workflow context pack.").requiredOption("--workflow <workflow>", "Workflow name: truth-sync, truth-document, or truth-realize").option("--base <ref>", "Base Git ref for impact-backed packs").option("--format <format>", "Output format: markdown", "markdown")
8856
- ).action(async (options) => {
8857
- writeContextResult(
8858
- await runContext({
8859
- workflow: options.workflow,
8860
- base: options.base,
8861
- format: options.format
8862
- }),
8863
- options
8864
- );
8865
- });
8866
8399
  const workflow = program.command("workflow").description("Inspect agent-facing Truthmark workflow state.");
8867
8400
  addJsonOption(
8868
8401
  workflow.command("status").description("Return schema-versioned workflow state for a canonical workflow ID.").option("--workflow <workflow>", "Canonical workflow ID, such as truthmark-sync").option("--base <ref>", "Base Git ref for impact-backed workflow state")