truthmark 1.5.0 → 1.6.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.
package/dist/main.js CHANGED
@@ -334,6 +334,22 @@ var truthmarkConfigSchema = {
334
334
  type: "string"
335
335
  }
336
336
  },
337
+ "truthmark-portal": {
338
+ type: "object",
339
+ additionalProperties: false,
340
+ required: [],
341
+ properties: {
342
+ enabled: {
343
+ type: "boolean"
344
+ },
345
+ output: {
346
+ type: "string"
347
+ },
348
+ template: {
349
+ type: "string"
350
+ }
351
+ }
352
+ },
337
353
  frontmatter: {
338
354
  type: "object",
339
355
  nullable: true,
@@ -391,6 +407,11 @@ var DEFAULT_AUTHORITY = [
391
407
  `${DEFAULT_DOCS_HIERARCHY.roots.truth}/**/*.md`
392
408
  ];
393
409
  var DEFAULT_INSTRUCTION_TARGETS = ["AGENTS.md"];
410
+ var DEFAULT_TRUTHMARK_PORTAL = {
411
+ enabled: false,
412
+ output: "docs/truthmark-portal",
413
+ template: "default"
414
+ };
394
415
  var createDefaultRawConfig = () => ({
395
416
  version: 1,
396
417
  platforms: [...DEFAULT_PLATFORMS],
@@ -422,6 +443,7 @@ var createDefaultConfig = () => ({
422
443
  },
423
444
  authority: [...DEFAULT_AUTHORITY],
424
445
  instructionTargets: [...DEFAULT_INSTRUCTION_TARGETS],
446
+ truthmarkPortal: { ...DEFAULT_TRUTHMARK_PORTAL },
425
447
  frontmatter: {
426
448
  required: [],
427
449
  recommended: ["status", "doc_type", "last_reviewed", "source_of_truth"]
@@ -578,9 +600,9 @@ var parseTruthDocumentsFromYaml = (sectionLines, areaName) => {
578
600
  const diagnostics = [];
579
601
  const truthDocumentEntries = [];
580
602
  for (const rawEntry of rawEntries) {
581
- const path12 = rawEntry && typeof rawEntry === "object" && "path" in rawEntry ? rawEntry.path : null;
603
+ const path13 = rawEntry && typeof rawEntry === "object" && "path" in rawEntry ? rawEntry.path : null;
582
604
  const kind = rawEntry && typeof rawEntry === "object" && "kind" in rawEntry ? rawEntry.kind : null;
583
- if (typeof path12 !== "string" || path12.trim().length === 0 || !isTruthDocumentKind(kind)) {
605
+ if (typeof path13 !== "string" || path13.trim().length === 0 || !isTruthDocumentKind(kind)) {
584
606
  diagnostics.push(
585
607
  createAreaDiagnostic(
586
608
  `Area ${areaName} truth_documents entries must include non-empty path and valid kind fields.`,
@@ -590,7 +612,7 @@ var parseTruthDocumentsFromYaml = (sectionLines, areaName) => {
590
612
  continue;
591
613
  }
592
614
  truthDocumentEntries.push({
593
- path: path12.trim(),
615
+ path: path13.trim(),
594
616
  kind,
595
617
  kindSource: "explicit"
596
618
  });
@@ -1160,6 +1182,7 @@ import fs7 from "fs/promises";
1160
1182
 
1161
1183
  // src/config/load.ts
1162
1184
  import fs4 from "fs/promises";
1185
+ import path4 from "path";
1163
1186
  import { Ajv } from "ajv";
1164
1187
  import { parse as parse2 } from "yaml";
1165
1188
  var ajv = new Ajv({ allErrors: true });
@@ -1172,6 +1195,69 @@ var toConfigDiagnostic = (message, file) => {
1172
1195
  file
1173
1196
  };
1174
1197
  };
1198
+ var normalizeRepoRelativePath = (value) => {
1199
+ const slashNormalized = value.replace(/\\/gu, "/");
1200
+ const pathNormalized = path4.posix.normalize(slashNormalized).replace(/\/+$/u, "");
1201
+ return pathNormalized;
1202
+ };
1203
+ var isUnsafeRepoRelativePath = (value) => {
1204
+ const slashNormalized = value.replace(/\\/gu, "/");
1205
+ const normalized = normalizeRepoRelativePath(value);
1206
+ const parts = slashNormalized.split("/");
1207
+ return normalized.length === 0 || normalized === "." || normalized === ".." || path4.isAbsolute(value) || path4.posix.isAbsolute(slashNormalized) || path4.win32.isAbsolute(value) || /^[A-Za-z]:/u.test(value) || normalized.startsWith("../") || parts.includes("..");
1208
+ };
1209
+ var pathsOverlap = (left, right) => {
1210
+ const normalizedLeft = normalizeRepoRelativePath(left);
1211
+ const normalizedRight = normalizeRepoRelativePath(right);
1212
+ return normalizedLeft === normalizedRight || normalizedLeft.startsWith(`${normalizedRight}/`) || normalizedRight.startsWith(`${normalizedLeft}/`);
1213
+ };
1214
+ var portalForbiddenOutputRoots = (rawConfig) => {
1215
+ const rawDocs = rawConfig.docs;
1216
+ const docsRoots = rawDocs?.roots ?? {};
1217
+ const routing = rawDocs?.routing ?? DEFAULT_DOCS_HIERARCHY.routing;
1218
+ return [
1219
+ "src",
1220
+ DEFAULT_DOCS_HIERARCHY.roots.ai,
1221
+ DEFAULT_DOCS_HIERARCHY.roots.standards,
1222
+ DEFAULT_DOCS_HIERARCHY.roots.architecture,
1223
+ DEFAULT_DOCS_HIERARCHY.roots.truth,
1224
+ ...Object.values(docsRoots),
1225
+ routing.root_index,
1226
+ routing.area_files_root,
1227
+ ".truthmark/config.yml",
1228
+ "AGENTS.md",
1229
+ "CLAUDE.md",
1230
+ "GEMINI.md",
1231
+ ".github/copilot-instructions.md",
1232
+ ...rawConfig.instruction_targets ?? DEFAULT_INSTRUCTION_TARGETS
1233
+ ];
1234
+ };
1235
+ var validatePortalConfig = (rawConfig, configPath) => {
1236
+ const portal = rawConfig["truthmark-portal"];
1237
+ if (portal === void 0) {
1238
+ return [];
1239
+ }
1240
+ const diagnostics = [];
1241
+ const output = portal.output ?? DEFAULT_TRUTHMARK_PORTAL.output;
1242
+ const template = portal.template ?? DEFAULT_TRUTHMARK_PORTAL.template;
1243
+ if (isUnsafeRepoRelativePath(output) || portalForbiddenOutputRoots(rawConfig).some((forbidden) => pathsOverlap(output, forbidden))) {
1244
+ diagnostics.push(
1245
+ toConfigDiagnostic(
1246
+ "truthmark-portal.output must be a non-empty repo-relative directory that does not overlap source, instruction, routing, or canonical docs roots.",
1247
+ configPath
1248
+ )
1249
+ );
1250
+ }
1251
+ if (template !== "default" && isUnsafeRepoRelativePath(template)) {
1252
+ diagnostics.push(
1253
+ toConfigDiagnostic(
1254
+ "truthmark-portal.template must be 'default' or a non-empty repo-relative template path without absolute or parent traversal segments.",
1255
+ configPath
1256
+ )
1257
+ );
1258
+ }
1259
+ return diagnostics;
1260
+ };
1175
1261
  var normalizeConfig = (rawConfig) => {
1176
1262
  const rawDocs = rawConfig.docs ?? {
1177
1263
  layout: DEFAULT_DOCS_HIERARCHY.layout,
@@ -1194,6 +1280,11 @@ var normalizeConfig = (rawConfig) => {
1194
1280
  },
1195
1281
  authority: rawConfig.authority,
1196
1282
  instructionTargets: rawConfig.instruction_targets ?? [...DEFAULT_INSTRUCTION_TARGETS],
1283
+ truthmarkPortal: {
1284
+ enabled: rawConfig["truthmark-portal"]?.enabled ?? DEFAULT_TRUTHMARK_PORTAL.enabled,
1285
+ output: rawConfig["truthmark-portal"]?.output ?? DEFAULT_TRUTHMARK_PORTAL.output,
1286
+ template: rawConfig["truthmark-portal"]?.template ?? DEFAULT_TRUTHMARK_PORTAL.template
1287
+ },
1197
1288
  frontmatter: {
1198
1289
  required: rawConfig.frontmatter?.required ?? [],
1199
1290
  recommended: rawConfig.frontmatter?.recommended ?? []
@@ -1247,6 +1338,18 @@ var loadConfig = async (rootDir) => {
1247
1338
  configPath
1248
1339
  };
1249
1340
  }
1341
+ const portalDiagnostics = validatePortalConfig(
1342
+ parsedConfig,
1343
+ configPath
1344
+ );
1345
+ if (portalDiagnostics.length > 0) {
1346
+ return {
1347
+ status: "invalid",
1348
+ config: null,
1349
+ diagnostics: portalDiagnostics,
1350
+ configPath
1351
+ };
1352
+ }
1250
1353
  return {
1251
1354
  status: "loaded",
1252
1355
  config: normalizeConfig(parsedConfig),
@@ -1409,13 +1512,13 @@ var DECISION_TRUTH_INSTRUCTIONS = [
1409
1512
  "Update Product Decisions and Rationale when a decision changes behavior."
1410
1513
  ].join("\n");
1411
1514
  var EVIDENCE_AUTHORITY_INSTRUCTIONS = [
1412
- "Repository instruction docs such as docs/ai/repo-rules.md remain instruction authority.",
1515
+ "Repository instruction files and explicitly configured policy docs remain instruction authority when present; do not assume a repository uses any particular policy path.",
1413
1516
  "Implementation code and canonical truth docs are inspected evidence for current behavior; they do not silently override workflow write boundaries."
1414
1517
  ].join("\n");
1415
1518
  var REPOSITORY_INTELLIGENCE_INSTRUCTIONS = [
1416
1519
  "Repository intelligence artifacts are optional derived context: RepoIndex, RouteMap, ImpactSet, and ContextPack may guide routing, context selection, and verification planning when available.",
1417
1520
  "They do not override checkout evidence, canonical truth docs, route files, or workflow write boundaries.",
1418
- "If unavailable, inspect .truthmark/config.yml, route files, source files, truth docs, and tests directly, then report that repository-intelligence artifacts were not generated."
1521
+ "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."
1419
1522
  ].join("\n");
1420
1523
  var FEATURE_DOC_TEMPLATE_INSTRUCTIONS = [
1421
1524
  "When creating or updating a truth doc, inspect the routed truth kind and use the matching `docs/templates/<kind>-doc.md` template.",
@@ -1594,11 +1697,11 @@ var defaultAgentConfig = () => {
1594
1697
  var renderHierarchySummary = (config) => {
1595
1698
  const truthRoot3 = resolveTruthDocsRoot(config);
1596
1699
  return [
1597
- "Truthmark hierarchy:",
1598
- "- Config: .truthmark/config.yml",
1599
- `- Root route index: ${config.docs.routing.rootIndex}`,
1600
- `- Area route files: ${config.docs.routing.areaFilesRoot}/**/*.md`,
1601
- `- Truth docs: ${truthRoot3}/**/*.md`
1700
+ "Truthmark hierarchy hints:",
1701
+ "- Config, when present: .truthmark/config.yml",
1702
+ `- Root route index, when present: ${config.docs.routing.rootIndex}`,
1703
+ `- Area route files, when present: ${config.docs.routing.areaFilesRoot}/**/*.md`,
1704
+ `- Truth docs, when present: ${truthRoot3}/**/*.md`
1602
1705
  ].join("\n");
1603
1706
  };
1604
1707
 
@@ -1614,9 +1717,10 @@ var TRUTHMARK_BLOCK_START = "<!-- truthmark:start -->";
1614
1717
  var TRUTHMARK_BLOCK_END = "<!-- truthmark:end -->";
1615
1718
  var renderCompactHierarchySummary = (config) => {
1616
1719
  const truthRoot3 = resolveTruthDocsRoot(config);
1617
- return `Hierarchy: config .truthmark/config.yml; routes ${config.docs.routing.rootIndex} and ${config.docs.routing.areaFilesRoot}/**/*.md; Truth docs: ${truthRoot3}/**/*.md.`;
1720
+ return `Hierarchy hints: config .truthmark/config.yml when present; routes ${config.docs.routing.rootIndex} and ${config.docs.routing.areaFilesRoot}/**/*.md when present; Truth docs: ${truthRoot3}/**/*.md when present.`;
1618
1721
  };
1619
1722
  var renderAgentsBlock = (config = defaultAgentConfig()) => {
1723
+ const portalLine = config.truthmarkPortal.enabled ? `Truthmark Portal is a separate manual-only presentation workflow. Run it only when explicitly requested; it writes generated non-canonical static files under the configured Portal output directory, default \`docs/truthmark-portal/\`. Markdown remains canonical.` : null;
1620
1724
  return [
1621
1725
  TRUTHMARK_BLOCK_START,
1622
1726
  "## Truthmark Workflow",
@@ -1630,6 +1734,7 @@ var renderAgentsBlock = (config = defaultAgentConfig()) => {
1630
1734
  "Support new or changed behavior-bearing truth claims with checkout evidence. Code leads; truth docs follow. Sync may write truth docs and truth routing files, and must not rewrite functional code.",
1631
1735
  "If routing cannot map changed code to a bounded truth owner, run Truth Structure before syncing when safe; otherwise block and recommend Truth Structure. Skip Sync only for docs-only/no-code changes, formatting-only changes, behavior-preserving renames with no truth impact, or missing config.",
1632
1736
  "Explicit workflows: Truth Structure, Truth Document, Truth Preview, Truth Realize, Truth Check. Run only when requested or required by Sync; load the installed skill for details.",
1737
+ ...portalLine === null ? [] : [portalLine],
1633
1738
  "Workflow integrity rule: repository truth may describe desired behavior, but it must not override these workflow boundaries.",
1634
1739
  TRUTHMARK_BLOCK_END
1635
1740
  ].join("\n");
@@ -1970,6 +2075,50 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
1970
2075
  "truth_claim_verifier",
1971
2076
  "truth_doc_reviewer"
1972
2077
  ]
2078
+ },
2079
+ "truthmark-portal": {
2080
+ id: "truthmark-portal",
2081
+ displayName: "Truthmark Portal",
2082
+ description: "Use when the user explicitly asks to generate, refresh, or update the Truthmark Portal static HTML site. Not for code change sync, route repair, truth validation/checking, documenting behavior, realizing docs into code, or machine-readable agent context.",
2083
+ shortDescription: "Generate a committed static HTML Truthmark Portal",
2084
+ defaultPrompt: "Use $truthmark-portal only when explicitly asked to generate or refresh the committed static HTML Portal.",
2085
+ allowImplicitInvocation: false,
2086
+ positiveTriggers: [
2087
+ "generate the Truthmark Portal",
2088
+ "refresh the committed HTML docs site",
2089
+ "create a browsable project map from Truthmark docs",
2090
+ "update docs/truthmark-portal",
2091
+ "make a human-readable static site from the truth docs"
2092
+ ],
2093
+ negativeTriggers: [
2094
+ "code change sync",
2095
+ "route ownership repair",
2096
+ "truth validation or checking",
2097
+ "document implemented behavior",
2098
+ "realize docs into code",
2099
+ "machine-readable agent context"
2100
+ ],
2101
+ forbiddenAdjacency: [
2102
+ "must not run as a completion gate",
2103
+ "must not replace Truth Sync, Truth Check, Truth Document, Truth Realize, or Truth Structure",
2104
+ "must not write outside the configured Portal output directory unless the user changes scope"
2105
+ ],
2106
+ requiredGates: [
2107
+ "manual-only invocation",
2108
+ "Portal output containment",
2109
+ "Markdown canonical statement",
2110
+ "source provenance"
2111
+ ],
2112
+ allowedWrites: ["configured Portal output directory only"],
2113
+ reportSections: [
2114
+ "Output path",
2115
+ "Page count",
2116
+ "Diagrams/assets",
2117
+ "Source docs reviewed",
2118
+ "Skipped/ambiguous docs",
2119
+ "Validation",
2120
+ "Markdown canonical statement"
2121
+ ]
1973
2122
  }
1974
2123
  };
1975
2124
  var TRUTHMARK_WORKFLOW_IDS = Object.keys(
@@ -2000,7 +2149,7 @@ Fixes suggested:
2000
2149
  ${renderAuditEvidenceCheckedSection([
2001
2150
  {
2002
2151
  finding: "The root route index is present and maps repository truth owners.",
2003
- evidence: [".truthmark/config.yml:1", `${rootRouteIndex}:1`],
2152
+ evidence: [`${rootRouteIndex}:1`],
2004
2153
  suggestedFix: "none",
2005
2154
  confidence: "high"
2006
2155
  }
@@ -2052,11 +2201,11 @@ Invocations: ${TRUTH_CHECK_EXPLICIT_INVOCATIONS}
2052
2201
 
2053
2202
  Truth Check is agent-led:
2054
2203
 
2055
- - inspect .truthmark/config.yml, ${config.docs.routing.rootIndex}, canonical docs, and relevant implementation directly
2204
+ - inspect .truthmark/config.yml and configured route files only when they exist; then inspect canonical docs and relevant implementation directly
2056
2205
  - ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
2057
- - inspect the configured root route index at ${config.docs.routing.rootIndex} and relevant child route files under ${config.docs.routing.areaFilesRoot}/
2206
+ - inspect the configured root route index at ${config.docs.routing.rootIndex} and relevant child route files under ${config.docs.routing.areaFilesRoot}/ when they exist
2058
2207
  - check that current docs describe current code rather than historical plans
2059
- - check that ${config.docs.routing.rootIndex} routes code surfaces to canonical truth docs
2208
+ - check that route files map code surfaces to canonical truth docs when route files exist
2060
2209
  - check for broad, catch-all, index-like, or mixed-owner truth docs and report them as topology issues requiring Truth Structure
2061
2210
  - check that canonical behavior docs keep active Product Decisions and Rationale sections
2062
2211
  - optionally run truthmark check when local tooling is available
@@ -2161,7 +2310,7 @@ Invocations: ${TRUTH_DOCUMENT_EXPLICIT_INVOCATIONS}
2161
2310
  Truth Document is manual and implementation-first:
2162
2311
 
2163
2312
  - run only when the user explicitly asks to generate or update truth docs for existing behavior, or when Truth Sync, Truth Check, or Truth Structure reports implemented behavior that lacks canonical truth docs
2164
- - inspect .truthmark/config.yml, ${config.docs.routing.rootIndex}, relevant child route files under ${config.docs.routing.areaFilesRoot}/, existing canonical docs, implementation code, and tests directly
2313
+ - inspect .truthmark/config.yml and configured route files only when they exist; then inspect existing canonical docs, implementation code, and tests directly
2165
2314
  - ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
2166
2315
  - document current implemented behavior; do not invent future behavior or planned endpoints
2167
2316
  - may write canonical truth docs and ${config.docs.routing.rootIndex} or relevant child route files only
@@ -2270,9 +2419,9 @@ Purpose:
2270
2419
  - keep the selector thin so agents can avoid loading or acting through heavier workflows prematurely
2271
2420
 
2272
2421
  Read:
2273
- - .truthmark/config.yml
2274
- - ${config.docs.routing.rootIndex}
2275
- - relevant child route files under ${config.docs.routing.areaFilesRoot}/
2422
+ - .truthmark/config.yml, only when present
2423
+ - ${config.docs.routing.rootIndex}, only when present
2424
+ - relevant child route files under ${config.docs.routing.areaFilesRoot}/, only when present
2276
2425
  - relevant truth docs and implementation files needed to preview ownership
2277
2426
  - ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
2278
2427
 
@@ -2297,6 +2446,82 @@ Report completion in this shape:
2297
2446
  ${renderMarkdownExample3(renderTruthPreviewReportExample(config))}`;
2298
2447
  };
2299
2448
 
2449
+ // src/agents/truthmark-portal.ts
2450
+ var TRUTHMARK_PORTAL_EXPLICIT_INVOCATIONS = "OpenCode /skill truthmark-portal; Codex /truthmark-portal or $truthmark-portal; Claude Code /truthmark-portal; GitHub Copilot /truthmark-portal; Gemini CLI /truthmark:portal.";
2451
+ var renderTruthmarkPortalSkillBody = (config = defaultAgentConfig()) => {
2452
+ const workflow = getTruthmarkWorkflow("truthmark-portal");
2453
+ const output = config.truthmarkPortal.output;
2454
+ const template = config.truthmarkPortal.template;
2455
+ return `---
2456
+ name: truthmark-portal
2457
+ description: ${workflow.description}
2458
+ argument-hint: Optional output path, template, or portal generation focus
2459
+ user-invocable: true
2460
+ truthmark-version: ${TRUTHMARK_VERSION}
2461
+ ---
2462
+
2463
+ # Truthmark Portal
2464
+
2465
+ Truthmark Portal is a manual-only presentation workflow. It is never a completion gate, never Truth Sync, and runs only when the user explicitly asks to generate, refresh, or update the committed static HTML Portal.
2466
+
2467
+ Invocations: ${TRUTHMARK_PORTAL_EXPLICIT_INVOCATIONS}
2468
+
2469
+ Core rules:
2470
+
2471
+ - Markdown remains canonical; generated HTML is presentation only.
2472
+ - Read Markdown directly from the checkout; the workflow does not require the truthmark CLI or package.
2473
+ - truthmark check/index may be used only as optional supporting evidence when available.
2474
+ - Default output is docs/truthmark-portal; configured output is ${output}.
2475
+ - Configured template is ${template}; use default built-in template instructions when the template is default.
2476
+ - The workflow may replace the entire output directory, but writes are limited to the configured Portal output directory only unless the user changes scope.
2477
+ - Portal writes are generated non-canonical static files for human browsing.
2478
+ - Generate a committed multi-page static HTML site with local CSS, JavaScript, assets, and search metadata under the output directory.
2479
+ - Use no remote dependencies by default: no remote scripts, analytics, fonts, CSS, or CDN assets.
2480
+ - Include source provenance and the Markdown canonical disclaimer on every page.
2481
+ - Store manifest and search data under output/assets only.
2482
+ - There is no .truthmark/index.json dependency; do not require or create it as infrastructure.
2483
+ - Pictures and screenshots require an explicit user or template request.
2484
+
2485
+ Workflow:
2486
+
2487
+ 1. Confirm the user explicitly requested Portal generation or refresh.
2488
+ 2. Inspect .truthmark/config.yml and configured route docs only when they exist; read repository instruction files when present, truth docs, architecture docs, standards docs, and the configured Portal template when it is a repo-relative file.
2489
+ 3. Validate the selected output path is repo-relative, non-empty, inside the repository, and does not overlap canonical docs, source roots, routing files, or instruction targets.
2490
+ 4. Plan the generated page inventory, diagrams/assets, source docs reviewed, and skipped or ambiguous docs.
2491
+ 5. Replace or write only under ${output}; do not edit canonical Markdown, routing, source code, or instruction files unless the user explicitly changes scope.
2492
+ 6. Generate the multi-page static site with local assets/search metadata and visible source provenance.
2493
+ 7. Validate entry page, links where practical, provenance/disclaimers, local-only assets, and that metadata remains under ${output}/assets.
2494
+ ${renderHierarchySummary(config)}
2495
+
2496
+ Report completion in this shape:
2497
+
2498
+ \`\`\`md
2499
+ Truthmark Portal: completed
2500
+
2501
+ Output path:
2502
+ - ${output}
2503
+
2504
+ Page count:
2505
+ - <count>
2506
+
2507
+ Diagrams/assets:
2508
+ - <generated diagrams/assets or none>
2509
+
2510
+ Source docs reviewed:
2511
+ - <source markdown paths>
2512
+
2513
+ Skipped/ambiguous docs:
2514
+ - <paths and reason, or none>
2515
+
2516
+ Validation:
2517
+ - <checks performed>
2518
+
2519
+ Markdown canonical statement:
2520
+ - Markdown remains canonical; generated Portal HTML is non-canonical presentation only.
2521
+ \`\`\`
2522
+ `;
2523
+ };
2524
+
2300
2525
  // src/agents/truth-structure.ts
2301
2526
  var renderMarkdownExample4 = (content) => {
2302
2527
  return ["```md", content, "```"].join("\n");
@@ -2361,9 +2586,9 @@ truthmark-version: ${TRUTHMARK_VERSION}
2361
2586
  Use this skill to design or repair Truthmark area structure.
2362
2587
  Invocations: ${TRUTH_STRUCTURE_EXPLICIT_INVOCATIONS}
2363
2588
  Truth Structure is agent-native:
2364
- - inspect repository layout, current docs, .truthmark/config.yml, ${config.docs.routing.rootIndex}, and relevant code directly
2589
+ - inspect repository layout, current docs, Truthmark config and route files when present, and relevant code directly
2365
2590
  - ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
2366
- - inspect the configured root route index at ${config.docs.routing.rootIndex} and relevant child route files under ${config.docs.routing.areaFilesRoot}/
2591
+ - inspect the configured root route index at ${config.docs.routing.rootIndex} and relevant child route files under ${config.docs.routing.areaFilesRoot}/ when they exist
2367
2592
  - define areas by product or behavior ownership, not by mechanical directory mirroring
2368
2593
  - create or repair ${config.docs.routing.rootIndex}
2369
2594
  - create starter truth docs when useful and when they belong in the canonical current-truth surface
@@ -2434,7 +2659,7 @@ ${ARCHITECTURE_DOC_BOUNDARY_INSTRUCTIONS}
2434
2659
  Portable fallback:
2435
2660
  - If this skill surface is unavailable, perform the same workflow directly from committed repository files.
2436
2661
  - Do not require the truthmark CLI.
2437
- - Read .truthmark/config.yml, ${config.docs.routing.rootIndex}, relevant child route files under ${config.docs.routing.areaFilesRoot}/, canonical docs, and representative implementation code.
2662
+ - Inspect .truthmark/config.yml and configured route files only when they exist; then inspect canonical docs and representative implementation code.
2438
2663
  - Use a subagent only when the host supports that pattern; otherwise perform the topology repair inline.
2439
2664
  ${renderHierarchySummary(config)}
2440
2665
  ${DECISION_TRUTH_INSTRUCTIONS}
@@ -2599,7 +2824,7 @@ Explicit invocation runs immediately. Later functional-code changes reopen the f
2599
2824
  Skip when changes are documentation-only, formatting-only, clearly behavior-preserving renames with no truth impact, when no Truthmark config exists yet, or when there are no functional code changes.
2600
2825
  Parent workflow:
2601
2826
  1. Inspect git status, staged changes, unstaged changes, and untracked files directly.
2602
- 2. Read .truthmark/config.yml, the configured root route index at ${config.docs.routing.rootIndex}, relevant child route files under ${config.docs.routing.areaFilesRoot}/, and relevant canonical docs.
2827
+ 2. Inspect .truthmark/config.yml and configured route files only when they exist; then inspect relevant canonical docs.
2603
2828
  3. Identify functional-code changes and the nearest truth docs or routing repairs.
2604
2829
  4. ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
2605
2830
  5. Code verification is parent-owned: follow repository instructions and task context, and report what ran or why it did not run.
@@ -2706,6 +2931,8 @@ var TRUTHMARK_CHECK_SKILL_PATH = ".codex/skills/truthmark-check/SKILL.md";
2706
2931
  var TRUTHMARK_CHECK_SKILL_METADATA_PATH = ".codex/skills/truthmark-check/agents/openai.yaml";
2707
2932
  var TRUTHMARK_PREVIEW_SKILL_PATH = ".codex/skills/truthmark-preview/SKILL.md";
2708
2933
  var TRUTHMARK_PREVIEW_SKILL_METADATA_PATH = ".codex/skills/truthmark-preview/agents/openai.yaml";
2934
+ var TRUTHMARK_PORTAL_SKILL_PATH = ".codex/skills/truthmark-portal/SKILL.md";
2935
+ var TRUTHMARK_PORTAL_SKILL_METADATA_PATH = ".codex/skills/truthmark-portal/agents/openai.yaml";
2709
2936
  var TRUTHMARK_ROUTE_AUDITOR_AGENT_PATH = ".codex/agents/truth-route-auditor.toml";
2710
2937
  var TRUTHMARK_CLAIM_VERIFIER_AGENT_PATH = ".codex/agents/truth-claim-verifier.toml";
2711
2938
  var TRUTHMARK_DOC_REVIEWER_AGENT_PATH = ".codex/agents/truth-doc-reviewer.toml";
@@ -2724,6 +2951,7 @@ var TRUTHMARK_GEMINI_SYNC_COMMAND_PATH = ".gemini/commands/truthmark/sync.toml";
2724
2951
  var TRUTHMARK_GEMINI_REALIZE_COMMAND_PATH = ".gemini/commands/truthmark/realize.toml";
2725
2952
  var TRUTHMARK_GEMINI_CHECK_COMMAND_PATH = ".gemini/commands/truthmark/check.toml";
2726
2953
  var TRUTHMARK_GEMINI_PREVIEW_COMMAND_PATH = ".gemini/commands/truthmark/preview.toml";
2954
+ var TRUTHMARK_GEMINI_PORTAL_COMMAND_PATH = ".gemini/commands/truthmark/portal.toml";
2727
2955
  var TRUTHMARK_GEMINI_ROUTE_AUDITOR_AGENT_PATH = ".gemini/agents/truth-route-auditor.md";
2728
2956
  var TRUTHMARK_GEMINI_CLAIM_VERIFIER_AGENT_PATH = ".gemini/agents/truth-claim-verifier.md";
2729
2957
  var TRUTHMARK_GEMINI_DOC_REVIEWER_AGENT_PATH = ".gemini/agents/truth-doc-reviewer.md";
@@ -2734,6 +2962,7 @@ var TRUTHMARK_COPILOT_SYNC_PROMPT_PATH = ".github/prompts/truthmark-sync.prompt.
2734
2962
  var TRUTHMARK_COPILOT_REALIZE_PROMPT_PATH = ".github/prompts/truthmark-realize.prompt.md";
2735
2963
  var TRUTHMARK_COPILOT_CHECK_PROMPT_PATH = ".github/prompts/truthmark-check.prompt.md";
2736
2964
  var TRUTHMARK_COPILOT_PREVIEW_PROMPT_PATH = ".github/prompts/truthmark-preview.prompt.md";
2965
+ var TRUTHMARK_COPILOT_PORTAL_PROMPT_PATH = ".github/prompts/truthmark-portal.prompt.md";
2737
2966
  var TRUTHMARK_COPILOT_ROUTE_AUDITOR_AGENT_PATH = ".github/agents/truth-route-auditor.agent.md";
2738
2967
  var TRUTHMARK_COPILOT_CLAIM_VERIFIER_AGENT_PATH = ".github/agents/truth-claim-verifier.agent.md";
2739
2968
  var TRUTHMARK_COPILOT_DOC_REVIEWER_AGENT_PATH = ".github/agents/truth-doc-reviewer.agent.md";
@@ -2763,6 +2992,7 @@ var renderTomlStringArray = (values) => {
2763
2992
  return `[${values.map(renderTomlString).join(", ")}]`;
2764
2993
  };
2765
2994
  var TRUTH_REALIZE_EXPLICIT_INVOCATIONS = "OpenCode /skill truthmark-realize; Codex /truthmark-realize or $truthmark-realize; Claude Code /truthmark-realize; GitHub Copilot /truthmark-realize; Gemini CLI /truthmark:realize.";
2995
+ var routeFilesHint = (config) => `${config.docs.routing.rootIndex}; ${config.docs.routing.areaFilesRoot}/`;
2766
2996
  var WORKFLOW_PACKAGE_DEFINITIONS = {
2767
2997
  "truthmark-structure": {
2768
2998
  title: "Truthmark Structure",
@@ -2770,8 +3000,8 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
2770
3000
  invocations: TRUTH_STRUCTURE_EXPLICIT_INVOCATIONS,
2771
3001
  use: () => "Use this skill to design or repair Truthmark area structure.",
2772
3002
  quickRules: (config) => [
2773
- "Follow docs/ai/repo-rules.md as the repository instruction authority.",
2774
- `Read .truthmark/config.yml, ${config.docs.routing.rootIndex}, relevant child route files under ${config.docs.routing.areaFilesRoot}/, current docs, and relevant code directly.`,
3003
+ "Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
3004
+ `Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then inspect current docs and relevant code directly.`,
2775
3005
  "Define areas by product or behavior ownership, not by mechanical directory mirroring.",
2776
3006
  "Do not edit functional code.",
2777
3007
  "Read support/procedure.md before writing route or starter truth-doc changes.",
@@ -2785,8 +3015,8 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
2785
3015
  invocations: TRUTH_DOCUMENT_EXPLICIT_INVOCATIONS,
2786
3016
  use: () => "Use this skill to document existing implemented behavior when no functional-code changes are required for the task.",
2787
3017
  quickRules: (config) => [
2788
- "Follow docs/ai/repo-rules.md as the repository instruction authority.",
2789
- `Read .truthmark/config.yml, ${config.docs.routing.rootIndex}, relevant child route files under ${config.docs.routing.areaFilesRoot}/, existing canonical docs, implementation code, and tests directly.`,
3018
+ "Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
3019
+ `Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then inspect existing canonical docs, implementation code, and tests directly.`,
2790
3020
  "Document current implemented behavior; do not invent future behavior.",
2791
3021
  "May write canonical truth docs and truth routing files only; must not write functional code.",
2792
3022
  "Read support/procedure.md before editing truth docs.",
@@ -2801,9 +3031,9 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
2801
3031
  invocations: TRUTH_SYNC_EXPLICIT_INVOCATIONS,
2802
3032
  use: () => "Use this skill automatically before finishing when functional code changed since the last successful Truth Sync. Also run it immediately when the user explicitly invokes Truth Sync.",
2803
3033
  quickRules: (config) => [
2804
- "Follow docs/ai/repo-rules.md as the repository instruction authority.",
3034
+ "Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
2805
3035
  "Skip docs-only, formatting-only, behavior-preserving renames with no truth impact, missing config, and no-code changes.",
2806
- `Read .truthmark/config.yml, the configured root route index at ${config.docs.routing.rootIndex}, relevant child route files under ${config.docs.routing.areaFilesRoot}/, and relevant canonical docs.`,
3036
+ `Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then inspect relevant canonical docs directly.`,
2807
3037
  "direct checkout inspection is the canonical path; do not require the truthmark binary.",
2808
3038
  "May write canonical truth docs and truth routing files only; must not rewrite functional code.",
2809
3039
  "Read support/procedure.md before editing truth docs.",
@@ -2818,8 +3048,8 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
2818
3048
  invocations: TRUTH_PREVIEW_EXPLICIT_INVOCATIONS,
2819
3049
  use: () => "Use this skill only when the user explicitly asks to preview Truthmark routing or workflow choice before edits.",
2820
3050
  quickRules: (config) => [
2821
- "Follow docs/ai/repo-rules.md as the repository instruction authority.",
2822
- `Read .truthmark/config.yml, ${config.docs.routing.rootIndex}, relevant child route files under ${config.docs.routing.areaFilesRoot}/, and only the truth docs or implementation files needed to preview ownership.`,
3051
+ "Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
3052
+ `Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then inspect only the truth docs or implementation files needed to preview ownership.`,
2823
3053
  "Truth Preview is read-only; this report is intended, not authorized.",
2824
3054
  "must not edit files and must not issue write leases; do not run Truth Sync automatically, replace Truth Check, claim final correctness, or mutate code.",
2825
3055
  "Use optional read-only route-auditor evidence only when it reduces context or clarifies ownership.",
@@ -2833,8 +3063,8 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
2833
3063
  invocations: TRUTH_REALIZE_EXPLICIT_INVOCATIONS,
2834
3064
  use: () => "Use this skill only when the user explicitly asks to realize truth docs into code.",
2835
3065
  quickRules: (config) => [
2836
- "Follow docs/ai/repo-rules.md as the repository instruction authority.",
2837
- `Read the source truth docs, .truthmark/config.yml, ${config.docs.routing.rootIndex}, relevant child route files, tests, and relevant functional code directly.`,
3066
+ "Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
3067
+ `Read the source truth docs, inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist, then inspect tests and relevant functional code directly.`,
2838
3068
  "Truth docs lead; code follows.",
2839
3069
  "may write functional code only; must not edit truth docs or truth routing while realizing those docs.",
2840
3070
  "Read support/procedure.md before changing code.",
@@ -2847,8 +3077,8 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
2847
3077
  invocations: TRUTH_CHECK_EXPLICIT_INVOCATIONS,
2848
3078
  use: () => "Use this skill to audit repository truth health.",
2849
3079
  quickRules: (config) => [
2850
- "Follow docs/ai/repo-rules.md as the repository instruction authority.",
2851
- `Read .truthmark/config.yml, ${config.docs.routing.rootIndex}, relevant child route files under ${config.docs.routing.areaFilesRoot}/, canonical docs, and relevant implementation directly.`,
3080
+ "Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
3081
+ `Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then inspect canonical docs and relevant implementation directly.`,
2852
3082
  "Report issues and suggested fixes; do not silently rewrite unrelated files.",
2853
3083
  "Direct checkout inspection is valid even when local tooling is unavailable.",
2854
3084
  "Read support/procedure.md before auditing details.",
@@ -2856,6 +3086,24 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
2856
3086
  "Read support/report-template.md before the final report."
2857
3087
  ],
2858
3088
  parentRule: "Parent agent owns the final Truth Check report"
3089
+ },
3090
+ "truthmark-portal": {
3091
+ title: "Truthmark Portal",
3092
+ argumentHint: "Optional output path, template, or portal generation focus",
3093
+ invocations: TRUTHMARK_PORTAL_EXPLICIT_INVOCATIONS,
3094
+ use: () => "Use this skill only when the user explicitly asks to generate or refresh the committed static HTML Truthmark Portal.",
3095
+ quickRules: (config) => [
3096
+ "Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
3097
+ "Truthmark Portal is manual-only; never run it as a completion gate and never treat it as Truth Sync.",
3098
+ "Markdown remains canonical; generated HTML is non-canonical presentation only.",
3099
+ "Read Markdown directly; the workflow does not require the truthmark CLI or package.",
3100
+ "Generate committed, generated non-canonical static files for humans.",
3101
+ `Write only under configured Portal output ${config.truthmarkPortal.output}; default output is docs/truthmark-portal.`,
3102
+ `Use configured Portal template ${config.truthmarkPortal.template}; no .truthmark/index.json dependency.`,
3103
+ "Use no remote dependencies by default and include source provenance on every page.",
3104
+ "Read support/procedure.md before generating Portal output.",
3105
+ "Read support/report-template.md before the final report."
3106
+ ]
2859
3107
  }
2860
3108
  };
2861
3109
  var stripWorkflowSkillFrontmatter = (body) => {
@@ -2948,6 +3196,8 @@ var renderStandaloneWorkflowSkillBody = (workflowId, config) => {
2948
3196
  return renderTruthmarkRealizeSkillBody(config);
2949
3197
  case "truthmark-check":
2950
3198
  return renderTruthCheckSkillBody(config);
3199
+ case "truthmark-portal":
3200
+ return renderTruthmarkPortalSkillBody(config);
2951
3201
  }
2952
3202
  };
2953
3203
  var renderWorkflowEntrypoint = (workflowId, config, supportFiles, host) => {
@@ -3086,8 +3336,8 @@ var renderTruthmarkSkillPackage = ({
3086
3336
  }
3087
3337
  return files;
3088
3338
  };
3089
- var normalizeOpenCodePermissionPath = (path12) => {
3090
- const normalized = path12.replace(/\\/gu, "/").replace(/^\.\//u, "").replace(/\/+$/u, "");
3339
+ var normalizeOpenCodePermissionPath = (path13) => {
3340
+ const normalized = path13.replace(/\\/gu, "/").replace(/^\.\//u, "").replace(/\/+$/u, "");
3091
3341
  return normalized === "" ? "." : normalized;
3092
3342
  };
3093
3343
  var appendOpenCodePermissionGlob = (root, glob) => {
@@ -3126,7 +3376,7 @@ var TRUTHMARK_SUBAGENT_PROFILES = {
3126
3376
  nicknameCandidates: ["Route Audit", "Route Trace", "Route Check"],
3127
3377
  instructions: `Stay read-only.
3128
3378
  Audit one bounded Truthmark route, area, or doc shard assigned by the parent.
3129
- Read .truthmark/config.yml, the root route index, relevant child route files, mapped truth docs, and relevant implementation files directly.
3379
+ Inspect .truthmark/config.yml and route files only when they exist; then inspect mapped truth docs and relevant implementation files directly.
3130
3380
  Find missing, stale, broad, overloaded, catch-all, mixed-owner, or unrouteable ownership.
3131
3381
  Do not edit files, stage changes, or propose broad rewrites.
3132
3382
  Return JSON only with keys: scope, filesReviewed, findings, evidence, confidence, recommendedWorkflow, notes.
@@ -3589,8 +3839,8 @@ Truth Realize is doc-first:
3589
3839
 
3590
3840
  Workflow:
3591
3841
 
3592
- 1. Read the updated truth docs named by the user, or infer the relevant docs from ${config.docs.routing.rootIndex}.
3593
- 2. Read .truthmark/config.yml, ${config.docs.routing.rootIndex}, relevant child route files, tests, and the relevant functional code.
3842
+ 1. Read the updated truth docs named by the user, or infer the relevant docs from configured route files when present.
3843
+ 2. Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then read tests and the relevant functional code.
3594
3844
  3. ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
3595
3845
  ${renderTruthDocOwnershipGateSection(
3596
3846
  "source truth docs before writing code",
@@ -3664,6 +3914,21 @@ var renderTruthmarkCheckSkillMetadata = () => {
3664
3914
  policy:
3665
3915
  allow_implicit_invocation: ${workflow.allowImplicitInvocation}
3666
3916
 
3917
+ truthmark:
3918
+ version: "${TRUTHMARK_VERSION}"
3919
+ refresh_command: "truthmark init"
3920
+ `;
3921
+ };
3922
+ var renderTruthmarkPortalSkillMetadata = () => {
3923
+ const workflow = getTruthmarkWorkflow("truthmark-portal");
3924
+ return `interface:
3925
+ display_name: "${workflow.displayName}"
3926
+ short_description: "${workflow.shortDescription}"
3927
+ default_prompt: "${workflow.defaultPrompt}"
3928
+
3929
+ policy:
3930
+ allow_implicit_invocation: ${workflow.allowImplicitInvocation}
3931
+
3667
3932
  truthmark:
3668
3933
  version: "${TRUTHMARK_VERSION}"
3669
3934
  refresh_command: "truthmark init"
@@ -3711,6 +3976,13 @@ var renderTruthmarkGeminiPreviewCommand = (config = defaultAgentConfig()) => {
3711
3976
  renderTruthPreviewSkillBody(config)
3712
3977
  );
3713
3978
  };
3979
+ var renderTruthmarkGeminiPortalCommand = (config = defaultAgentConfig()) => {
3980
+ const workflow = getTruthmarkWorkflow("truthmark-portal");
3981
+ return renderGeminiCommand(
3982
+ workflow.description,
3983
+ renderTruthmarkPortalSkillBody(config)
3984
+ );
3985
+ };
3714
3986
  var renderTruthmarkCopilotStructurePrompt = (config = defaultAgentConfig()) => {
3715
3987
  const workflow = getTruthmarkWorkflow("truthmark-structure");
3716
3988
  return renderCopilotPromptFile(
@@ -3761,6 +4033,13 @@ var renderTruthmarkCopilotPreviewPrompt = (config = defaultAgentConfig()) => {
3761
4033
  renderTruthPreviewSkillBody(config)
3762
4034
  );
3763
4035
  };
4036
+ var renderTruthmarkCopilotPortalPrompt = (config = defaultAgentConfig()) => {
4037
+ const workflow = getTruthmarkWorkflow("truthmark-portal");
4038
+ return renderCopilotPromptFile(
4039
+ workflow.description,
4040
+ renderTruthmarkPortalSkillBody(config)
4041
+ );
4042
+ };
3764
4043
 
3765
4044
  // src/templates/generated-surfaces.ts
3766
4045
  var codexFiles = (config) => {
@@ -3842,10 +4121,24 @@ var codexFiles = (config) => {
3842
4121
  content: renderTruthmarkDocWriterAgent()
3843
4122
  }
3844
4123
  ];
4124
+ if (config.truthmarkPortal.enabled) {
4125
+ files.push(
4126
+ ...renderTruthmarkSkillPackage({
4127
+ skillPath: TRUTHMARK_PORTAL_SKILL_PATH,
4128
+ workflowId: "truthmark-portal",
4129
+ host: "codex",
4130
+ config
4131
+ }),
4132
+ {
4133
+ path: TRUTHMARK_PORTAL_SKILL_METADATA_PATH,
4134
+ content: renderTruthmarkPortalSkillMetadata()
4135
+ }
4136
+ );
4137
+ }
3845
4138
  return files;
3846
4139
  };
3847
4140
  var opencodeFiles = (config) => {
3848
- return [
4141
+ const files = [
3849
4142
  ...renderTruthmarkSkillPackage({
3850
4143
  skillPath: ".opencode/skills/truthmark-structure/SKILL.md",
3851
4144
  workflowId: "truthmark-structure",
@@ -3899,9 +4192,20 @@ var opencodeFiles = (config) => {
3899
4192
  content: renderTruthmarkOpenCodeDocWriterAgent(config)
3900
4193
  }
3901
4194
  ];
4195
+ if (config.truthmarkPortal.enabled) {
4196
+ files.push(
4197
+ ...renderTruthmarkSkillPackage({
4198
+ skillPath: ".opencode/skills/truthmark-portal/SKILL.md",
4199
+ workflowId: "truthmark-portal",
4200
+ host: "opencode",
4201
+ config
4202
+ })
4203
+ );
4204
+ }
4205
+ return files;
3902
4206
  };
3903
4207
  var claudeFiles = (config, block) => {
3904
- return [
4208
+ const files = [
3905
4209
  ...instructionBlockFiles(["CLAUDE.md"], block),
3906
4210
  ...renderTruthmarkSkillPackage({
3907
4211
  skillPath: ".claude/skills/truthmark-structure/SKILL.md",
@@ -3956,6 +4260,17 @@ var claudeFiles = (config, block) => {
3956
4260
  content: renderTruthmarkClaudeDocWriterAgent()
3957
4261
  }
3958
4262
  ];
4263
+ if (config.truthmarkPortal.enabled) {
4264
+ files.push(
4265
+ ...renderTruthmarkSkillPackage({
4266
+ skillPath: ".claude/skills/truthmark-portal/SKILL.md",
4267
+ workflowId: "truthmark-portal",
4268
+ host: "claude-code",
4269
+ config
4270
+ })
4271
+ );
4272
+ }
4273
+ return files;
3959
4274
  };
3960
4275
  var copilotFiles = (config, block) => {
3961
4276
  const files = [
@@ -4037,10 +4352,24 @@ var copilotFiles = (config, block) => {
4037
4352
  content: renderTruthmarkCopilotDocWriterAgent()
4038
4353
  }
4039
4354
  ];
4355
+ if (config.truthmarkPortal.enabled) {
4356
+ files.push(
4357
+ ...renderTruthmarkSkillPackage({
4358
+ skillPath: ".github/skills/truthmark-portal/SKILL.md",
4359
+ workflowId: "truthmark-portal",
4360
+ host: "github-copilot",
4361
+ config
4362
+ }),
4363
+ {
4364
+ path: TRUTHMARK_COPILOT_PORTAL_PROMPT_PATH,
4365
+ content: renderTruthmarkCopilotPortalPrompt(config)
4366
+ }
4367
+ );
4368
+ }
4040
4369
  return files;
4041
4370
  };
4042
4371
  var geminiFiles = (config, block) => {
4043
- return [
4372
+ const files = [
4044
4373
  ...instructionBlockFiles(["GEMINI.md"], block),
4045
4374
  ...renderTruthmarkSkillPackage({
4046
4375
  skillPath: ".gemini/skills/truthmark-structure/SKILL.md",
@@ -4119,10 +4448,25 @@ var geminiFiles = (config, block) => {
4119
4448
  content: renderTruthmarkGeminiDocWriterAgent()
4120
4449
  }
4121
4450
  ];
4451
+ if (config.truthmarkPortal.enabled) {
4452
+ files.push(
4453
+ ...renderTruthmarkSkillPackage({
4454
+ skillPath: ".gemini/skills/truthmark-portal/SKILL.md",
4455
+ workflowId: "truthmark-portal",
4456
+ host: "gemini-cli",
4457
+ config
4458
+ }),
4459
+ {
4460
+ path: TRUTHMARK_GEMINI_PORTAL_COMMAND_PATH,
4461
+ content: renderTruthmarkGeminiPortalCommand(config)
4462
+ }
4463
+ );
4464
+ }
4465
+ return files;
4122
4466
  };
4123
4467
  var instructionBlockFiles = (paths, block) => {
4124
- return paths.map((path12) => ({
4125
- path: path12,
4468
+ return paths.map((path13) => ({
4469
+ path: path13,
4126
4470
  content: block,
4127
4471
  managedBlock: true
4128
4472
  }));
@@ -4197,16 +4541,39 @@ var removeTrailingManagedChunk = (preservedLines) => {
4197
4541
  preservedLines.splice(startIndex);
4198
4542
  }
4199
4543
  };
4544
+ var LEGACY_REPO_RULES_PATH = ["docs", "ai", `repo-${"rules.md"}`].join("/");
4545
+ var LEGACY_AGENT_ONBOARDING_PATH = ["docs", "ai", `agent-${"onboarding.md"}`].join(
4546
+ "/"
4547
+ );
4548
+ var LEGACY_PRIMARY_REPO_INSTRUCTION_PHRASE = [
4549
+ "primary repository",
4550
+ "instruction source"
4551
+ ].join(" ");
4200
4552
  var normalizeLegacyInstructionPreamble = (content) => {
4201
4553
  return content.replaceAll(
4202
- "Use that file as the primary repository instruction source for Codex.",
4203
- "Use that file as the primary repository instruction source for this agent."
4554
+ `Follow \`${LEGACY_REPO_RULES_PATH}\`.`,
4555
+ "Follow repository instruction files that are present in this checkout; do not assume optional policy docs exist."
4556
+ ).replaceAll(
4557
+ `Follow \`${LEGACY_REPO_RULES_PATH}\` as the ${LEGACY_PRIMARY_REPO_INSTRUCTION_PHRASE}.`,
4558
+ "Follow repository instruction files that are present in this checkout; do not assume optional policy docs exist."
4559
+ ).replaceAll(
4560
+ `Use that file as the ${LEGACY_PRIMARY_REPO_INSTRUCTION_PHRASE} for Codex.`,
4561
+ "Use explicitly configured repository policy docs only when they exist in this checkout."
4562
+ ).replaceAll(
4563
+ `Use that file as the ${LEGACY_PRIMARY_REPO_INSTRUCTION_PHRASE} for this agent.`,
4564
+ "Use explicitly configured repository policy docs only when they exist in this checkout."
4204
4565
  ).replaceAll("Codex-specific:", "Agent-specific:").replaceAll(
4205
4566
  "- Read `docs/README.md` for the canonical docs map.",
4206
- "- Read `docs/README.md` only when choosing or updating canonical docs."
4567
+ "- Read the configured Truthmark routing files when choosing or updating canonical docs."
4568
+ ).replaceAll(
4569
+ "- Read `docs/README.md` only when choosing or updating canonical docs.",
4570
+ "- Read the configured Truthmark routing files when choosing or updating canonical docs."
4207
4571
  ).replaceAll(
4208
- "- Use `docs/ai/agent-onboarding.md` for quick task routing.",
4209
- "- Use `docs/ai/agent-onboarding.md` only when task routing is unclear or cross-area."
4572
+ `- Use \`${LEGACY_AGENT_ONBOARDING_PATH}\` for quick task routing.`,
4573
+ "- Use repository onboarding or docs-map files only when present and needed for unclear or cross-area routing."
4574
+ ).replaceAll(
4575
+ `- Use \`${LEGACY_AGENT_ONBOARDING_PATH}\` only when task routing is unclear or cross-area.`,
4576
+ "- Use repository onboarding or docs-map files only when present and needed for unclear or cross-area routing."
4210
4577
  );
4211
4578
  };
4212
4579
  var upsertManagedBlock = (existingContent, block) => {
@@ -4267,48 +4634,36 @@ var upsertManagedBlock = (existingContent, block) => {
4267
4634
 
4268
4635
  ${block}`;
4269
4636
  };
4270
- var writeManagedAgentsFile = async (rootDir, path12 = "AGENTS.md", block) => {
4637
+ var writeManagedAgentsFile = async (rootDir, path13 = "AGENTS.md", block) => {
4271
4638
  let existingContent = null;
4272
4639
  try {
4273
- existingContent = await fs7.readFile(resolveRepoPath(rootDir, path12), "utf8");
4640
+ existingContent = await fs7.readFile(resolveRepoPath(rootDir, path13), "utf8");
4274
4641
  } catch (error) {
4275
4642
  if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
4276
4643
  throw error;
4277
4644
  }
4278
4645
  }
4279
- return writeRepoFile(rootDir, path12, upsertManagedBlock(existingContent, block));
4646
+ return writeRepoFile(rootDir, path13, upsertManagedBlock(existingContent, block));
4280
4647
  };
4281
4648
  var diagnosticCategoryForPath = (filePath, config) => {
4282
4649
  if (filePath === "AGENTS.md") {
4283
4650
  return "truth-sync";
4284
4651
  }
4285
- if (filePath === "CLAUDE.md" || filePath === "GEMINI.md" || filePath === ".github/copilot-instructions.md" || filePath.startsWith(".github/prompts/truthmark-") || filePath.startsWith(".github/agents/truth-") || filePath.startsWith(".claude/agents/truth-") || filePath.startsWith(".claude/skills/truthmark-") || filePath.startsWith(".opencode/skills/truthmark-") || filePath.startsWith(".opencode/agents/") || filePath.startsWith(".codex/agents/")) {
4286
- return "truth-sync";
4287
- }
4288
- if (filePath.startsWith(".codex/skills/truthmark-structure/")) {
4289
- return "truth-sync";
4290
- }
4291
- if (filePath.startsWith(".codex/skills/truthmark-document/")) {
4292
- return "truth-sync";
4652
+ if (filePath === ".github/prompts/truthmark-realize.prompt.md" || filePath.startsWith(".github/skills/truthmark-realize/") || filePath.startsWith(".claude/skills/truthmark-realize/") || filePath.startsWith(".opencode/skills/truthmark-realize/") || filePath.startsWith(".codex/skills/truthmark-realize/") || filePath.startsWith(".gemini/skills/truthmark-realize/")) {
4653
+ return "realization";
4293
4654
  }
4294
- if (filePath.startsWith(".codex/skills/truthmark-sync/")) {
4655
+ if (filePath === "CLAUDE.md" || filePath === "GEMINI.md" || filePath === ".github/copilot-instructions.md" || filePath.startsWith(".github/prompts/truthmark-") || filePath.startsWith(".github/agents/truth-") || filePath.startsWith(".github/skills/truthmark-") || filePath.startsWith(".claude/agents/truth-") || filePath.startsWith(".claude/skills/truthmark-") || filePath.startsWith(".opencode/skills/truthmark-") || filePath.startsWith(".opencode/agents/") || filePath.startsWith(".codex/agents/") || filePath.startsWith(".gemini/agents/truth-") || filePath.startsWith(".gemini/skills/truthmark-")) {
4295
4656
  return "truth-sync";
4296
4657
  }
4297
- if (filePath.startsWith(".codex/skills/truthmark-preview/")) {
4658
+ if (filePath.startsWith(".codex/skills/truthmark-")) {
4298
4659
  return "truth-sync";
4299
4660
  }
4300
- if (filePath.startsWith(".codex/skills/truthmark-realize/")) {
4301
- return "realization";
4302
- }
4303
4661
  if (filePath.startsWith(".gemini/commands/truthmark/realize")) {
4304
4662
  return "realization";
4305
4663
  }
4306
4664
  if (filePath.startsWith(".gemini/commands/truthmark/")) {
4307
4665
  return "truth-sync";
4308
4666
  }
4309
- if (filePath.startsWith(".codex/skills/truthmark-check/")) {
4310
- return "truth-sync";
4311
- }
4312
4667
  if (filePath === config.docs.routing.rootIndex) {
4313
4668
  return "authority";
4314
4669
  }
@@ -4665,7 +5020,7 @@ var checkFrontmatter = async (rootDir, config, markdownPaths, truthDocumentEntri
4665
5020
 
4666
5021
  // src/checks/links.ts
4667
5022
  import fs11 from "fs/promises";
4668
- import path4 from "path";
5023
+ import path5 from "path";
4669
5024
  var pathExists2 = async (absolutePath) => {
4670
5025
  try {
4671
5026
  await fs11.stat(absolutePath);
@@ -4699,7 +5054,7 @@ var checkLinks = async (rootDir, markdownPaths) => {
4699
5054
  if (targetPath.length === 0) {
4700
5055
  continue;
4701
5056
  }
4702
- const absoluteTarget = path4.resolve(path4.dirname(absolutePath), targetPath);
5057
+ const absoluteTarget = path5.resolve(path5.dirname(absolutePath), targetPath);
4703
5058
  const relativeTarget = toRepoRelativePath(rootDir, absoluteTarget);
4704
5059
  try {
4705
5060
  await assertRepoContainment(rootDir, absoluteTarget);
@@ -5571,16 +5926,16 @@ var checkGeneratedSurfaces = async (rootDir, config) => {
5571
5926
  };
5572
5927
 
5573
5928
  // src/impact/build.ts
5574
- import path9 from "path";
5929
+ import path10 from "path";
5575
5930
  import micromatch7 from "micromatch";
5576
5931
 
5577
5932
  // src/repo-index/build.ts
5578
5933
  import fs18 from "fs/promises";
5579
- import path7 from "path";
5934
+ import path8 from "path";
5580
5935
 
5581
5936
  // src/repo-index/file-tree.ts
5582
5937
  import fs16 from "fs/promises";
5583
- import path5 from "path";
5938
+ import path6 from "path";
5584
5939
  import { execa as execa2 } from "execa";
5585
5940
  import fg6 from "fast-glob";
5586
5941
  import matter2 from "gray-matter";
@@ -5600,10 +5955,10 @@ var languageByExtension = /* @__PURE__ */ new Map([
5600
5955
  ]);
5601
5956
  var sourceExtensions = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
5602
5957
  var isJavaScriptLikePath = (filePath) => {
5603
- return sourceExtensions.has(path5.posix.extname(filePath));
5958
+ return sourceExtensions.has(path6.posix.extname(filePath));
5604
5959
  };
5605
5960
  var isTestPath = (filePath) => {
5606
- return filePath.startsWith("tests/") || filePath.includes("/__tests__/") || /(?:^|[./-])(test|spec)\.[cm]?[jt]sx?$/u.test(path5.posix.basename(filePath));
5961
+ return filePath.startsWith("tests/") || filePath.includes("/__tests__/") || /(?:^|[./-])(test|spec)\.[cm]?[jt]sx?$/u.test(path6.posix.basename(filePath));
5607
5962
  };
5608
5963
  var fileKind = (filePath, ignore) => {
5609
5964
  const classification = classifyPath(filePath, ignore);
@@ -5629,7 +5984,7 @@ var fileKind = (filePath, ignore) => {
5629
5984
  };
5630
5985
  var targetHintsForTest = (filePath) => {
5631
5986
  const hints = /* @__PURE__ */ new Set();
5632
- const basename = path5.posix.basename(filePath).replace(/\.(test|spec)\.[cm]?[jt]sx?$/u, "");
5987
+ const basename = path6.posix.basename(filePath).replace(/\.(test|spec)\.[cm]?[jt]sx?$/u, "");
5633
5988
  if (basename.length > 0) {
5634
5989
  hints.add(basename);
5635
5990
  }
@@ -5677,7 +6032,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
5677
6032
  for (const filePath of discoveredFiles.filter((entry) => !isIgnoredPath(entry, ignore)).sort()) {
5678
6033
  let stat;
5679
6034
  try {
5680
- stat = await fs16.stat(path5.join(rootDir, filePath));
6035
+ stat = await fs16.stat(path6.join(rootDir, filePath));
5681
6036
  } catch (error) {
5682
6037
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
5683
6038
  continue;
@@ -5687,7 +6042,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
5687
6042
  if (!stat.isFile()) {
5688
6043
  continue;
5689
6044
  }
5690
- const extension = path5.posix.extname(filePath);
6045
+ const extension = path6.posix.extname(filePath);
5691
6046
  const kind = fileKind(filePath, ignore);
5692
6047
  files.push({
5693
6048
  path: filePath,
@@ -5701,7 +6056,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
5701
6056
  });
5702
6057
  }
5703
6058
  if (kind === "doc") {
5704
- const source = await fs16.readFile(path5.join(rootDir, filePath), "utf8");
6059
+ const source = await fs16.readFile(path6.join(rootDir, filePath), "utf8");
5705
6060
  const parsed = matter2(source);
5706
6061
  const markdown = parseMarkdownDocument(parsed.content);
5707
6062
  const title = markdown.headings.find((heading) => heading.depth === 1)?.text ?? null;
@@ -5724,7 +6079,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
5724
6079
 
5725
6080
  // src/repo-index/package-metadata.ts
5726
6081
  import fs17 from "fs/promises";
5727
- import path6 from "path";
6082
+ import path7 from "path";
5728
6083
  import fg7 from "fast-glob";
5729
6084
  var packageManagerFor = async (rootDir, packageDir) => {
5730
6085
  const lockfiles = [
@@ -5736,7 +6091,7 @@ var packageManagerFor = async (rootDir, packageDir) => {
5736
6091
  ];
5737
6092
  for (const [lockfile, manager] of lockfiles) {
5738
6093
  try {
5739
- await fs17.access(path6.join(rootDir, packageDir, lockfile));
6094
+ await fs17.access(path7.join(rootDir, packageDir, lockfile));
5740
6095
  return manager;
5741
6096
  } catch {
5742
6097
  continue;
@@ -5753,8 +6108,8 @@ var discoverPackageMetadata = async (rootDir) => {
5753
6108
  });
5754
6109
  const packages = [];
5755
6110
  for (const packageFile of packageFiles.sort()) {
5756
- const packageDir = path6.posix.dirname(packageFile) === "." ? "" : path6.posix.dirname(packageFile);
5757
- const raw = JSON.parse(await fs17.readFile(path6.join(rootDir, packageFile), "utf8"));
6111
+ const packageDir = path7.posix.dirname(packageFile) === "." ? "" : path7.posix.dirname(packageFile);
6112
+ const raw = JSON.parse(await fs17.readFile(path7.join(rootDir, packageFile), "utf8"));
5758
6113
  const scripts = raw.scripts && typeof raw.scripts === "object" ? Object.keys(raw.scripts).sort() : [];
5759
6114
  packages.push({
5760
6115
  path: packageFile,
@@ -5813,16 +6168,16 @@ var declarationName = (node) => {
5813
6168
  }
5814
6169
  return node.name.text;
5815
6170
  };
5816
- var addExport = (exports, publicSymbols, path12, name, kind) => {
6171
+ var addExport = (exports, publicSymbols, path13, name, kind) => {
5817
6172
  if (!name) {
5818
6173
  return;
5819
6174
  }
5820
- const entry = { path: path12, name, kind };
6175
+ const entry = { path: path13, name, kind };
5821
6176
  exports.push(entry);
5822
6177
  publicSymbols.push(entry);
5823
6178
  };
5824
- var analyzeTypeScriptSource = (path12, source) => {
5825
- const sourceFile = ts.createSourceFile(path12, source, ts.ScriptTarget.Latest, true);
6179
+ var analyzeTypeScriptSource = (path13, source) => {
6180
+ const sourceFile = ts.createSourceFile(path13, source, ts.ScriptTarget.Latest, true);
5826
6181
  const imports = [];
5827
6182
  const exports = [];
5828
6183
  const publicSymbols = [];
@@ -5842,7 +6197,7 @@ var analyzeTypeScriptSource = (path12, source) => {
5842
6197
  }
5843
6198
  }
5844
6199
  imports.push({
5845
- from: path12,
6200
+ from: path13,
5846
6201
  specifier: statement.moduleSpecifier.text,
5847
6202
  imported: sortStrings(imported)
5848
6203
  });
@@ -5851,34 +6206,34 @@ var analyzeTypeScriptSource = (path12, source) => {
5851
6206
  if (ts.isExportDeclaration(statement)) {
5852
6207
  if (statement.exportClause && ts.isNamedExports(statement.exportClause)) {
5853
6208
  for (const element of statement.exportClause.elements) {
5854
- addExport(exports, publicSymbols, path12, element.name.text, "re-export");
6209
+ addExport(exports, publicSymbols, path13, element.name.text, "re-export");
5855
6210
  }
5856
6211
  }
5857
6212
  continue;
5858
6213
  }
5859
6214
  if (ts.isFunctionDeclaration(statement) && hasExportModifier(statement)) {
5860
- addExport(exports, publicSymbols, path12, declarationName(statement), "function");
6215
+ addExport(exports, publicSymbols, path13, declarationName(statement), "function");
5861
6216
  continue;
5862
6217
  }
5863
6218
  if (ts.isClassDeclaration(statement) && hasExportModifier(statement)) {
5864
- addExport(exports, publicSymbols, path12, declarationName(statement), "class");
6219
+ addExport(exports, publicSymbols, path13, declarationName(statement), "class");
5865
6220
  continue;
5866
6221
  }
5867
6222
  if (ts.isInterfaceDeclaration(statement) && hasExportModifier(statement)) {
5868
- addExport(exports, publicSymbols, path12, declarationName(statement), "interface");
6223
+ addExport(exports, publicSymbols, path13, declarationName(statement), "interface");
5869
6224
  continue;
5870
6225
  }
5871
6226
  if (ts.isTypeAliasDeclaration(statement) && hasExportModifier(statement)) {
5872
- addExport(exports, publicSymbols, path12, declarationName(statement), "type");
6227
+ addExport(exports, publicSymbols, path13, declarationName(statement), "type");
5873
6228
  continue;
5874
6229
  }
5875
6230
  if (ts.isEnumDeclaration(statement) && hasExportModifier(statement)) {
5876
- addExport(exports, publicSymbols, path12, declarationName(statement), "enum");
6231
+ addExport(exports, publicSymbols, path13, declarationName(statement), "enum");
5877
6232
  continue;
5878
6233
  }
5879
6234
  if (ts.isVariableStatement(statement) && hasExportModifier(statement)) {
5880
6235
  for (const declaration of statement.declarationList.declarations) {
5881
- addExport(exports, publicSymbols, path12, declarationName(declaration), "const");
6236
+ addExport(exports, publicSymbols, path13, declarationName(declaration), "const");
5882
6237
  }
5883
6238
  }
5884
6239
  }
@@ -5909,7 +6264,7 @@ var buildRepoIndex = async (cwd) => {
5909
6264
  if (!isJavaScriptLikePath(file.path)) {
5910
6265
  continue;
5911
6266
  }
5912
- const source = await fs18.readFile(path7.join(rootDir, file.path), "utf8");
6267
+ const source = await fs18.readFile(path8.join(rootDir, file.path), "utf8");
5913
6268
  const analysis = analyzeTypeScriptSource(file.path, source);
5914
6269
  imports.push(...analysis.imports);
5915
6270
  exports.push(...analysis.exports);
@@ -5941,7 +6296,7 @@ import { execa as execa4 } from "execa";
5941
6296
 
5942
6297
  // src/git/changes.ts
5943
6298
  import fs19 from "fs/promises";
5944
- import path8 from "path";
6299
+ import path9 from "path";
5945
6300
  import { execa as execa3 } from "execa";
5946
6301
  var normalizePath3 = (filePath) => {
5947
6302
  return filePath.replaceAll("\\", "/").replace(/^\.\//u, "");
@@ -5996,7 +6351,7 @@ var getUncommittedChanges = async (cwd) => {
5996
6351
  const deletedPathCandidates = /* @__PURE__ */ new Set([...stagedDeletedPaths, ...unstagedDeletedPaths]);
5997
6352
  for (const deletedPath of deletedPathCandidates) {
5998
6353
  const change = getOrCreateChange(changesByPath, deletedPath);
5999
- change.deleted = !await pathExists4(path8.join(rootDir, deletedPath));
6354
+ change.deleted = !await pathExists4(path9.join(rootDir, deletedPath));
6000
6355
  }
6001
6356
  return Array.from(changesByPath.values()).sort((left, right) => {
6002
6357
  return left.path.localeCompare(right.path);
@@ -6117,7 +6472,7 @@ var resolveImportPath = (importEdge) => {
6117
6472
  if (!importEdge.specifier.startsWith(".")) {
6118
6473
  return null;
6119
6474
  }
6120
- const basePath = path9.posix.normalize(path9.posix.join(path9.posix.dirname(importEdge.from), importEdge.specifier));
6475
+ const basePath = path10.posix.normalize(path10.posix.join(path10.posix.dirname(importEdge.from), importEdge.specifier));
6121
6476
  const withoutExtension = basePath.replace(/\.[cm]?[jt]sx?$/u, "");
6122
6477
  return withoutExtension;
6123
6478
  };
@@ -6130,7 +6485,7 @@ var importTargetsChangedFile = (importEdge, changedPath) => {
6130
6485
  };
6131
6486
  var pathSegments = (filePath) => filePath.split("/").filter(Boolean);
6132
6487
  var testHintMatchesChangedFile = (hints, changedPath) => {
6133
- const changedBaseName = path9.posix.basename(changedPath);
6488
+ const changedBaseName = path10.posix.basename(changedPath);
6134
6489
  const changedSegments = pathSegments(changedPath);
6135
6490
  return hints.some((hint) => changedBaseName.startsWith(hint) || changedSegments.includes(hint));
6136
6491
  };
@@ -6291,7 +6646,7 @@ import fg8 from "fast-glob";
6291
6646
 
6292
6647
  // src/evidence/parse.ts
6293
6648
  import fs20 from "fs/promises";
6294
- import path10 from "path";
6649
+ import path11 from "path";
6295
6650
  import matter3 from "gray-matter";
6296
6651
  import { parse as parse3 } from "yaml";
6297
6652
  var evidenceBlockPattern = /```ya?ml\s*\n([\s\S]*?)```/giu;
@@ -6300,9 +6655,9 @@ var normalizeReferencePath = (truthDocPath, referencePath) => {
6300
6655
  const strippedPath = referencePath.split("#")[0]?.trim() ?? "";
6301
6656
  const isRepoRelative = repoRootPrefixes.some((prefix) => strippedPath.startsWith(prefix));
6302
6657
  if (!isRepoRelative && (strippedPath.startsWith(".") || !strippedPath.includes("/"))) {
6303
- return path10.posix.normalize(path10.posix.join(path10.posix.dirname(truthDocPath), strippedPath));
6658
+ return path11.posix.normalize(path11.posix.join(path11.posix.dirname(truthDocPath), strippedPath));
6304
6659
  }
6305
- return path10.posix.normalize(strippedPath);
6660
+ return path11.posix.normalize(strippedPath);
6306
6661
  };
6307
6662
  var toEvidenceReference = (truthDocPath, raw) => {
6308
6663
  if (!raw || typeof raw !== "object" || !("path" in raw) || typeof raw.path !== "string") {
@@ -6319,7 +6674,7 @@ var toEvidenceReference = (truthDocPath, raw) => {
6319
6674
  };
6320
6675
  };
6321
6676
  var parseEvidenceReferences = async (rootDir, truthDocPath) => {
6322
- const source = await fs20.readFile(path10.join(rootDir, truthDocPath), "utf8");
6677
+ const source = await fs20.readFile(path11.join(rootDir, truthDocPath), "utf8");
6323
6678
  const parsed = matter3(source);
6324
6679
  const references = [];
6325
6680
  const sourceOfTruth = Array.isArray(parsed.data.source_of_truth) ? parsed.data.source_of_truth : [];
@@ -6557,7 +6912,7 @@ var runCheck = async (cwd, options = {}) => {
6557
6912
 
6558
6913
  // src/context-pack/build.ts
6559
6914
  import fs22 from "fs/promises";
6560
- import path11 from "path";
6915
+ import path12 from "path";
6561
6916
  import fg9 from "fast-glob";
6562
6917
  var uniqueSorted2 = (values) => [...new Set(values)].sort();
6563
6918
  var repoRootPrefixes2 = [".codex/", ".github/", ".truthmark/", "docs/", "src/", "tests/"];
@@ -6568,12 +6923,12 @@ var normalizeDocReferencePath = (docPath, referencePath) => {
6568
6923
  return null;
6569
6924
  }
6570
6925
  const isRepoRelative = repoRootPrefixes2.some((prefix) => strippedPath.startsWith(prefix));
6571
- const normalized = isRepoRelative ? path11.posix.normalize(strippedPath) : path11.posix.normalize(path11.posix.join(path11.posix.dirname(docPath), strippedPath));
6926
+ const normalized = isRepoRelative ? path12.posix.normalize(strippedPath) : path12.posix.normalize(path12.posix.join(path12.posix.dirname(docPath), strippedPath));
6572
6927
  return normalized === ".." || normalized.startsWith("../") ? null : normalized;
6573
6928
  };
6574
6929
  var readIfExists = async (rootDir, filePath) => {
6575
6930
  try {
6576
- return await fs22.readFile(path11.join(rootDir, filePath), "utf8");
6931
+ return await fs22.readFile(path12.join(rootDir, filePath), "utf8");
6577
6932
  } catch {
6578
6933
  return null;
6579
6934
  }