truthmark 1.6.1 → 2.1.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/README.de.md +118 -106
- package/README.es.md +135 -123
- package/README.md +57 -45
- package/README.ru.md +107 -95
- package/README.zh.md +106 -96
- package/dist/main.js +906 -528
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -266,11 +266,11 @@ var DEFAULT_PLATFORMS = [
|
|
|
266
266
|
var truthmarkConfigSchema = {
|
|
267
267
|
type: "object",
|
|
268
268
|
additionalProperties: false,
|
|
269
|
-
required: ["version", "
|
|
269
|
+
required: ["version", "truthmark"],
|
|
270
270
|
properties: {
|
|
271
271
|
version: {
|
|
272
272
|
type: "integer",
|
|
273
|
-
const:
|
|
273
|
+
const: 2
|
|
274
274
|
},
|
|
275
275
|
platforms: {
|
|
276
276
|
type: "array",
|
|
@@ -281,52 +281,58 @@ var truthmarkConfigSchema = {
|
|
|
281
281
|
},
|
|
282
282
|
minItems: 1
|
|
283
283
|
},
|
|
284
|
-
|
|
284
|
+
truthmark: {
|
|
285
285
|
type: "object",
|
|
286
|
-
nullable: true,
|
|
287
286
|
additionalProperties: false,
|
|
288
|
-
required: ["
|
|
287
|
+
required: ["workspace", "routes", "truth", "templates", "generated"],
|
|
289
288
|
properties: {
|
|
290
|
-
|
|
291
|
-
type: "string"
|
|
292
|
-
const: "hierarchical"
|
|
289
|
+
workspace: {
|
|
290
|
+
type: "string"
|
|
293
291
|
},
|
|
294
|
-
|
|
292
|
+
routes: {
|
|
295
293
|
type: "object",
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
294
|
+
additionalProperties: false,
|
|
295
|
+
required: ["index", "areas", "default_area", "max_delegation_depth"],
|
|
296
|
+
properties: {
|
|
297
|
+
index: { type: "string" },
|
|
298
|
+
areas: { type: "string" },
|
|
299
|
+
default_area: { type: "string" },
|
|
300
|
+
max_delegation_depth: { type: "integer", const: 1 }
|
|
299
301
|
}
|
|
300
302
|
},
|
|
301
|
-
|
|
303
|
+
truth: {
|
|
302
304
|
type: "object",
|
|
303
305
|
additionalProperties: false,
|
|
304
|
-
required: ["
|
|
306
|
+
required: ["root"],
|
|
305
307
|
properties: {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
308
|
+
root: { type: "string" }
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
templates: {
|
|
312
|
+
type: "object",
|
|
313
|
+
additionalProperties: false,
|
|
314
|
+
required: ["root"],
|
|
315
|
+
properties: {
|
|
316
|
+
root: { type: "string" }
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
generated: {
|
|
320
|
+
type: "object",
|
|
321
|
+
additionalProperties: false,
|
|
322
|
+
required: ["portal"],
|
|
323
|
+
properties: {
|
|
324
|
+
portal: {
|
|
325
|
+
type: "object",
|
|
326
|
+
additionalProperties: false,
|
|
327
|
+
required: ["enabled"],
|
|
328
|
+
properties: {
|
|
329
|
+
enabled: { type: "boolean" }
|
|
330
|
+
}
|
|
318
331
|
}
|
|
319
332
|
}
|
|
320
333
|
}
|
|
321
334
|
}
|
|
322
335
|
},
|
|
323
|
-
authority: {
|
|
324
|
-
type: "array",
|
|
325
|
-
items: {
|
|
326
|
-
type: "string"
|
|
327
|
-
},
|
|
328
|
-
minItems: 1
|
|
329
|
-
},
|
|
330
336
|
instruction_targets: {
|
|
331
337
|
type: "array",
|
|
332
338
|
nullable: true,
|
|
@@ -334,22 +340,6 @@ var truthmarkConfigSchema = {
|
|
|
334
340
|
type: "string"
|
|
335
341
|
}
|
|
336
342
|
},
|
|
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
|
-
},
|
|
353
343
|
frontmatter: {
|
|
354
344
|
type: "object",
|
|
355
345
|
nullable: true,
|
|
@@ -383,44 +373,39 @@ var truthmarkConfigSchema = {
|
|
|
383
373
|
};
|
|
384
374
|
|
|
385
375
|
// src/config/defaults.ts
|
|
386
|
-
var
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
architecture: "docs/architecture",
|
|
392
|
-
truth: "docs/truth"
|
|
393
|
-
},
|
|
394
|
-
routing: {
|
|
395
|
-
root_index: "docs/truthmark/areas.md",
|
|
396
|
-
area_files_root: "docs/truthmark/areas",
|
|
376
|
+
var DEFAULT_TRUTHMARK_WORKSPACE = {
|
|
377
|
+
workspace: "docs/truthmark",
|
|
378
|
+
routes: {
|
|
379
|
+
index: "routes/areas.md",
|
|
380
|
+
areas: "routes/areas",
|
|
397
381
|
default_area: "repository",
|
|
398
382
|
max_delegation_depth: 1
|
|
383
|
+
},
|
|
384
|
+
truth: {
|
|
385
|
+
root: "truth"
|
|
386
|
+
},
|
|
387
|
+
templates: {
|
|
388
|
+
root: "templates"
|
|
389
|
+
},
|
|
390
|
+
generated: {
|
|
391
|
+
portal: {
|
|
392
|
+
enabled: false
|
|
393
|
+
}
|
|
399
394
|
}
|
|
400
395
|
};
|
|
401
|
-
var DEFAULT_AUTHORITY = [
|
|
402
|
-
DEFAULT_DOCS_HIERARCHY.routing.root_index,
|
|
403
|
-
`${DEFAULT_DOCS_HIERARCHY.routing.area_files_root}/**/*.md`,
|
|
404
|
-
`${DEFAULT_DOCS_HIERARCHY.roots.ai}/**/*.md`,
|
|
405
|
-
`${DEFAULT_DOCS_HIERARCHY.roots.standards}/**/*.md`,
|
|
406
|
-
`${DEFAULT_DOCS_HIERARCHY.roots.architecture}/**/*.md`,
|
|
407
|
-
`${DEFAULT_DOCS_HIERARCHY.roots.truth}/**/*.md`
|
|
408
|
-
];
|
|
409
396
|
var DEFAULT_INSTRUCTION_TARGETS = ["AGENTS.md"];
|
|
410
|
-
var DEFAULT_TRUTHMARK_PORTAL = {
|
|
411
|
-
enabled: false,
|
|
412
|
-
output: "docs/truthmark-portal",
|
|
413
|
-
template: "default"
|
|
414
|
-
};
|
|
415
397
|
var createDefaultRawConfig = () => ({
|
|
416
|
-
version:
|
|
398
|
+
version: 2,
|
|
417
399
|
platforms: [...DEFAULT_PLATFORMS],
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
400
|
+
truthmark: {
|
|
401
|
+
workspace: DEFAULT_TRUTHMARK_WORKSPACE.workspace,
|
|
402
|
+
routes: { ...DEFAULT_TRUTHMARK_WORKSPACE.routes },
|
|
403
|
+
truth: { ...DEFAULT_TRUTHMARK_WORKSPACE.truth },
|
|
404
|
+
templates: { ...DEFAULT_TRUTHMARK_WORKSPACE.templates },
|
|
405
|
+
generated: {
|
|
406
|
+
portal: { ...DEFAULT_TRUTHMARK_WORKSPACE.generated.portal }
|
|
407
|
+
}
|
|
422
408
|
},
|
|
423
|
-
authority: [...DEFAULT_AUTHORITY],
|
|
424
409
|
instruction_targets: [...DEFAULT_INSTRUCTION_TARGETS],
|
|
425
410
|
frontmatter: {
|
|
426
411
|
required: [],
|
|
@@ -429,21 +414,37 @@ var createDefaultRawConfig = () => ({
|
|
|
429
414
|
ignore: ["node_modules/**", "vendor/**", "dist/**", "build/**"]
|
|
430
415
|
});
|
|
431
416
|
var createDefaultConfig = () => ({
|
|
432
|
-
version:
|
|
417
|
+
version: 2,
|
|
433
418
|
platforms: [...DEFAULT_PLATFORMS],
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
419
|
+
truthmark: {
|
|
420
|
+
workspace: DEFAULT_TRUTHMARK_WORKSPACE.workspace,
|
|
421
|
+
routes: {
|
|
422
|
+
index: DEFAULT_TRUTHMARK_WORKSPACE.routes.index,
|
|
423
|
+
areas: DEFAULT_TRUTHMARK_WORKSPACE.routes.areas,
|
|
424
|
+
defaultArea: DEFAULT_TRUTHMARK_WORKSPACE.routes.default_area,
|
|
425
|
+
maxDelegationDepth: DEFAULT_TRUTHMARK_WORKSPACE.routes.max_delegation_depth
|
|
426
|
+
},
|
|
427
|
+
truth: { root: DEFAULT_TRUTHMARK_WORKSPACE.truth.root },
|
|
428
|
+
templates: { root: DEFAULT_TRUTHMARK_WORKSPACE.templates.root },
|
|
429
|
+
generated: {
|
|
430
|
+
portal: { ...DEFAULT_TRUTHMARK_WORKSPACE.generated.portal }
|
|
431
|
+
},
|
|
432
|
+
paths: {
|
|
433
|
+
routesIndex: "docs/truthmark/routes/areas.md",
|
|
434
|
+
routeAreasRoot: "docs/truthmark/routes/areas",
|
|
435
|
+
truthRoot: "docs/truthmark/truth",
|
|
436
|
+
templatesRoot: "docs/truthmark/templates",
|
|
437
|
+
portalOutput: "docs/truthmark/generated/portal",
|
|
438
|
+
portalTemplate: "docs/truthmark/templates/portal.html"
|
|
439
|
+
},
|
|
440
|
+
controlledPaths: [
|
|
441
|
+
"docs/truthmark/routes/areas.md",
|
|
442
|
+
"docs/truthmark/routes/areas/**/*.md",
|
|
443
|
+
"docs/truthmark/truth/**/*.md",
|
|
444
|
+
"docs/truthmark/templates/*.md"
|
|
445
|
+
]
|
|
443
446
|
},
|
|
444
|
-
authority: [...DEFAULT_AUTHORITY],
|
|
445
447
|
instructionTargets: [...DEFAULT_INSTRUCTION_TARGETS],
|
|
446
|
-
truthmarkPortal: { ...DEFAULT_TRUTHMARK_PORTAL },
|
|
447
448
|
frontmatter: {
|
|
448
449
|
required: [],
|
|
449
450
|
recommended: ["status", "doc_type", "last_reviewed", "source_of_truth"]
|
|
@@ -461,6 +462,7 @@ var TRUTH_DOCUMENT_KINDS = [
|
|
|
461
462
|
"operations",
|
|
462
463
|
"test-behavior"
|
|
463
464
|
];
|
|
465
|
+
var DEFAULT_WORKSPACE_TRUTH_DOCS_ROOT = "docs/truthmark/truth";
|
|
464
466
|
var slugify = (value) => {
|
|
465
467
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
466
468
|
};
|
|
@@ -480,25 +482,10 @@ var isTruthDocumentKind = (value) => {
|
|
|
480
482
|
};
|
|
481
483
|
var inferTruthDocumentKindFromPath = (documentPath, options = {}) => {
|
|
482
484
|
const normalizedPath = documentPath.replaceAll("\\", "/");
|
|
483
|
-
const truthDocsRoot = options.truthDocsRoot?.replaceAll("\\", "/").replace(/\/+$/u, "");
|
|
484
|
-
if (truthDocsRoot && normalizedPath.startsWith(`${truthDocsRoot}/`)
|
|
485
|
+
const truthDocsRoot = (options.truthDocsRoot ?? DEFAULT_WORKSPACE_TRUTH_DOCS_ROOT)?.replaceAll("\\", "/").replace(/\/+$/u, "");
|
|
486
|
+
if (truthDocsRoot && normalizedPath.startsWith(`${truthDocsRoot}/`)) {
|
|
485
487
|
return "behavior";
|
|
486
488
|
}
|
|
487
|
-
if (normalizedPath.startsWith("docs/contracts/") || normalizedPath.startsWith("docs/contract/") || normalizedPath.startsWith("docs/api/")) {
|
|
488
|
-
return "contract";
|
|
489
|
-
}
|
|
490
|
-
if (normalizedPath.startsWith("docs/architecture/")) {
|
|
491
|
-
return "architecture";
|
|
492
|
-
}
|
|
493
|
-
if (normalizedPath.startsWith("docs/workflows/") || normalizedPath.startsWith("docs/workflow/")) {
|
|
494
|
-
return "workflow";
|
|
495
|
-
}
|
|
496
|
-
if (normalizedPath.startsWith("docs/operations/") || normalizedPath.startsWith("docs/platform/")) {
|
|
497
|
-
return "operations";
|
|
498
|
-
}
|
|
499
|
-
if (normalizedPath.startsWith("docs/testing/") || normalizedPath.startsWith("docs/tests/")) {
|
|
500
|
-
return "test-behavior";
|
|
501
|
-
}
|
|
502
489
|
return null;
|
|
503
490
|
};
|
|
504
491
|
var findTruthDocumentsYamlFenceRange = (sectionLines) => {
|
|
@@ -738,9 +725,8 @@ var parseAreasMarkdown = (source, options = {}) => {
|
|
|
738
725
|
};
|
|
739
726
|
|
|
740
727
|
// src/truth/docs.ts
|
|
741
|
-
var DEFAULT_TRUTH_DOCS_ROOT = DEFAULT_DOCS_HIERARCHY.roots.truth;
|
|
742
728
|
var resolveTruthDocsRoot = (config) => {
|
|
743
|
-
return config.
|
|
729
|
+
return config.truthmark.paths.truthRoot;
|
|
744
730
|
};
|
|
745
731
|
|
|
746
732
|
// src/templates/init-files.ts
|
|
@@ -759,11 +745,11 @@ var titleCase = (value) => {
|
|
|
759
745
|
return value.split(/[-_\s]+/u).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
|
|
760
746
|
};
|
|
761
747
|
var renderHierarchicalAreasIndexTemplate = (config) => {
|
|
762
|
-
const defaultArea = config.
|
|
763
|
-
const childPath = `${config.
|
|
748
|
+
const defaultArea = config.truthmark.routes.defaultArea;
|
|
749
|
+
const childPath = `${config.truthmark.paths.routeAreasRoot}/${defaultArea}.md`;
|
|
764
750
|
const title = titleCase(defaultArea);
|
|
765
751
|
const sourceOfTruth = resolveRelativePath(
|
|
766
|
-
config.
|
|
752
|
+
config.truthmark.paths.routesIndex,
|
|
767
753
|
".truthmark/config.yml"
|
|
768
754
|
);
|
|
769
755
|
return [
|
|
@@ -792,11 +778,11 @@ var renderHierarchicalAreasIndexTemplate = (config) => {
|
|
|
792
778
|
].join("\n");
|
|
793
779
|
};
|
|
794
780
|
var renderChildAreaTemplate = (config) => {
|
|
795
|
-
const defaultArea = config.
|
|
781
|
+
const defaultArea = config.truthmark.routes.defaultArea;
|
|
796
782
|
const title = titleCase(defaultArea);
|
|
797
783
|
const truthDocsRoot = truthRoot(config);
|
|
798
784
|
const leafTruthDoc = `${truthDocsRoot}/${defaultArea}/overview.md`;
|
|
799
|
-
const templatePath = `${config.
|
|
785
|
+
const templatePath = `${config.truthmark.paths.routeAreasRoot}/${defaultArea}.md`;
|
|
800
786
|
const sourceOfTruth = resolveRelativePath(templatePath, ".truthmark/config.yml");
|
|
801
787
|
return [
|
|
802
788
|
"---",
|
|
@@ -830,7 +816,7 @@ var renderTruthRootReadmeTemplate = (config = createDefaultConfig()) => {
|
|
|
830
816
|
const templatePath = `${truthRoot(config)}/README.md`;
|
|
831
817
|
const sourceOfTruth = resolveRelativePath(
|
|
832
818
|
templatePath,
|
|
833
|
-
config.
|
|
819
|
+
config.truthmark.paths.routesIndex
|
|
834
820
|
);
|
|
835
821
|
return [
|
|
836
822
|
"---",
|
|
@@ -850,12 +836,12 @@ var renderTruthRootReadmeTemplate = (config = createDefaultConfig()) => {
|
|
|
850
836
|
].join("\n");
|
|
851
837
|
};
|
|
852
838
|
var renderTruthDomainReadmeTemplate = (config) => {
|
|
853
|
-
const defaultArea = config.
|
|
839
|
+
const defaultArea = config.truthmark.routes.defaultArea;
|
|
854
840
|
const title = titleCase(defaultArea);
|
|
855
841
|
const templatePath = `${truthRoot(config)}/${defaultArea}/README.md`;
|
|
856
842
|
const sourceOfTruth = resolveRelativePath(
|
|
857
843
|
templatePath,
|
|
858
|
-
`${config.
|
|
844
|
+
`${config.truthmark.paths.routeAreasRoot}/${defaultArea}.md`
|
|
859
845
|
);
|
|
860
846
|
return [
|
|
861
847
|
"---",
|
|
@@ -878,12 +864,7 @@ var renderTruthDomainReadmeTemplate = (config) => {
|
|
|
878
864
|
""
|
|
879
865
|
].join("\n");
|
|
880
866
|
};
|
|
881
|
-
var BEHAVIOR_DOC_TEMPLATE_PATH = "docs/templates/behavior-doc.md";
|
|
882
|
-
var CONTRACT_DOC_TEMPLATE_PATH = "docs/templates/contract-doc.md";
|
|
883
|
-
var ARCHITECTURE_DOC_TEMPLATE_PATH = "docs/templates/architecture-doc.md";
|
|
884
|
-
var WORKFLOW_DOC_TEMPLATE_PATH = "docs/templates/workflow-doc.md";
|
|
885
|
-
var OPERATIONS_DOC_TEMPLATE_PATH = "docs/templates/operations-doc.md";
|
|
886
|
-
var TEST_BEHAVIOR_DOC_TEMPLATE_PATH = "docs/templates/test-behavior-doc.md";
|
|
867
|
+
var BEHAVIOR_DOC_TEMPLATE_PATH = "docs/truthmark/templates/behavior-doc.md";
|
|
887
868
|
var renderTemplateSection = (section) => {
|
|
888
869
|
return [
|
|
889
870
|
section.heading,
|
|
@@ -1286,12 +1267,12 @@ var renderTemplate = (template, values) => {
|
|
|
1286
1267
|
}, template);
|
|
1287
1268
|
};
|
|
1288
1269
|
var renderBehaviorLeafDocTemplate = (config, template = renderBehaviorDocTemplateFile()) => {
|
|
1289
|
-
const defaultArea = config.
|
|
1270
|
+
const defaultArea = config.truthmark.routes.defaultArea;
|
|
1290
1271
|
const title = titleCase(defaultArea);
|
|
1291
1272
|
const templatePath = `${truthRoot(config)}/${defaultArea}/overview.md`;
|
|
1292
1273
|
const sourceOfTruth = resolveRelativePath(
|
|
1293
1274
|
templatePath,
|
|
1294
|
-
`${config.
|
|
1275
|
+
`${config.truthmark.paths.routeAreasRoot}/${defaultArea}.md`
|
|
1295
1276
|
);
|
|
1296
1277
|
const today = currentDate();
|
|
1297
1278
|
return renderTemplate(template, {
|
|
@@ -1399,18 +1380,15 @@ import { Ajv } from "ajv";
|
|
|
1399
1380
|
import { parse as parse2 } from "yaml";
|
|
1400
1381
|
var ajv = new Ajv({ allErrors: true });
|
|
1401
1382
|
var validateTruthmarkConfig = ajv.compile(truthmarkConfigSchema);
|
|
1402
|
-
var toConfigDiagnostic = (message, file) => {
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
};
|
|
1409
|
-
};
|
|
1383
|
+
var toConfigDiagnostic = (message, file) => ({
|
|
1384
|
+
category: "config",
|
|
1385
|
+
severity: "error",
|
|
1386
|
+
message,
|
|
1387
|
+
file
|
|
1388
|
+
});
|
|
1410
1389
|
var normalizeRepoRelativePath = (value) => {
|
|
1411
1390
|
const slashNormalized = value.replace(/\\/gu, "/");
|
|
1412
|
-
|
|
1413
|
-
return pathNormalized;
|
|
1391
|
+
return path4.posix.normalize(slashNormalized).replace(/\/+$/u, "");
|
|
1414
1392
|
};
|
|
1415
1393
|
var isUnsafeRepoRelativePath = (value) => {
|
|
1416
1394
|
const slashNormalized = value.replace(/\\/gu, "/");
|
|
@@ -1418,85 +1396,153 @@ var isUnsafeRepoRelativePath = (value) => {
|
|
|
1418
1396
|
const parts = slashNormalized.split("/");
|
|
1419
1397
|
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("..");
|
|
1420
1398
|
};
|
|
1399
|
+
var joinWorkspacePath = (workspace, childPath) => {
|
|
1400
|
+
return normalizeRepoRelativePath(`${workspace}/${childPath}`);
|
|
1401
|
+
};
|
|
1402
|
+
var portalOutputFor = (workspace) => joinWorkspacePath(workspace, "generated/portal");
|
|
1403
|
+
var portalTemplateFor = (templatesRoot) => joinWorkspacePath(templatesRoot, "portal.html");
|
|
1421
1404
|
var pathsOverlap = (left, right) => {
|
|
1422
1405
|
const normalizedLeft = normalizeRepoRelativePath(left);
|
|
1423
1406
|
const normalizedRight = normalizeRepoRelativePath(right);
|
|
1424
1407
|
return normalizedLeft === normalizedRight || normalizedLeft.startsWith(`${normalizedRight}/`) || normalizedRight.startsWith(`${normalizedLeft}/`);
|
|
1425
1408
|
};
|
|
1426
|
-
var
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
".truthmark/config.yml",
|
|
1440
|
-
"AGENTS.md",
|
|
1441
|
-
"CLAUDE.md",
|
|
1442
|
-
"GEMINI.md",
|
|
1443
|
-
".github/copilot-instructions.md",
|
|
1444
|
-
...rawConfig.instruction_targets ?? DEFAULT_INSTRUCTION_TARGETS
|
|
1445
|
-
];
|
|
1446
|
-
};
|
|
1447
|
-
var validatePortalConfig = (rawConfig, configPath) => {
|
|
1448
|
-
const portal = rawConfig["truthmark-portal"];
|
|
1449
|
-
if (portal === void 0) {
|
|
1409
|
+
var CONFIG_PATH2 = ".truthmark/config.yml";
|
|
1410
|
+
var FORBIDDEN_WORKSPACE_OVERLAPS = [
|
|
1411
|
+
".git",
|
|
1412
|
+
".truthmark",
|
|
1413
|
+
"package.json",
|
|
1414
|
+
"package-lock.json",
|
|
1415
|
+
"pnpm-lock.yaml",
|
|
1416
|
+
"yarn.lock",
|
|
1417
|
+
"src",
|
|
1418
|
+
"tests"
|
|
1419
|
+
];
|
|
1420
|
+
var unsupportedShapeDiagnostics = (parsedConfig, configPath) => {
|
|
1421
|
+
if (!parsedConfig || typeof parsedConfig !== "object" || Array.isArray(parsedConfig)) {
|
|
1450
1422
|
return [];
|
|
1451
1423
|
}
|
|
1424
|
+
const record = parsedConfig;
|
|
1452
1425
|
const diagnostics = [];
|
|
1453
|
-
|
|
1454
|
-
const template = portal.template ?? DEFAULT_TRUTHMARK_PORTAL.template;
|
|
1455
|
-
if (isUnsafeRepoRelativePath(output) || portalForbiddenOutputRoots(rawConfig).some((forbidden) => pathsOverlap(output, forbidden))) {
|
|
1426
|
+
if (record.version !== 2) {
|
|
1456
1427
|
diagnostics.push(
|
|
1457
1428
|
toConfigDiagnostic(
|
|
1458
|
-
"
|
|
1429
|
+
"Unsupported Truthmark config shape. This release requires version: 2 with a truthmark workspace block.",
|
|
1459
1430
|
configPath
|
|
1460
1431
|
)
|
|
1461
1432
|
);
|
|
1462
1433
|
}
|
|
1463
|
-
if (
|
|
1434
|
+
if ("docs" in record || "authority" in record) {
|
|
1464
1435
|
diagnostics.push(
|
|
1465
1436
|
toConfigDiagnostic(
|
|
1466
|
-
"
|
|
1437
|
+
"Unsupported Truthmark config shape. Remove old docs.roots and legacy authority settings; use version: 2 truthmark.workspace paths.",
|
|
1467
1438
|
configPath
|
|
1468
1439
|
)
|
|
1469
1440
|
);
|
|
1470
1441
|
}
|
|
1471
1442
|
return diagnostics;
|
|
1472
1443
|
};
|
|
1444
|
+
var validateWorkspacePaths = (rawConfig, configPath) => {
|
|
1445
|
+
const diagnostics = [];
|
|
1446
|
+
const workspace = normalizeRepoRelativePath(rawConfig.truthmark.workspace);
|
|
1447
|
+
const childPaths = [
|
|
1448
|
+
["truthmark.routes.index", rawConfig.truthmark.routes.index],
|
|
1449
|
+
["truthmark.routes.areas", rawConfig.truthmark.routes.areas],
|
|
1450
|
+
["truthmark.truth.root", rawConfig.truthmark.truth.root],
|
|
1451
|
+
["truthmark.templates.root", rawConfig.truthmark.templates.root]
|
|
1452
|
+
];
|
|
1453
|
+
if (isUnsafeRepoRelativePath(rawConfig.truthmark.workspace) || FORBIDDEN_WORKSPACE_OVERLAPS.some((forbidden) => pathsOverlap(workspace, forbidden))) {
|
|
1454
|
+
diagnostics.push(
|
|
1455
|
+
toConfigDiagnostic(
|
|
1456
|
+
"truthmark.workspace must be a non-empty repo-relative directory that does not overlap repository control, package, source, test, or instruction paths.",
|
|
1457
|
+
configPath
|
|
1458
|
+
)
|
|
1459
|
+
);
|
|
1460
|
+
}
|
|
1461
|
+
for (const [name, value] of childPaths) {
|
|
1462
|
+
if (isUnsafeRepoRelativePath(value)) {
|
|
1463
|
+
diagnostics.push(
|
|
1464
|
+
toConfigDiagnostic(
|
|
1465
|
+
`${name} must be a non-empty path relative to truthmark.workspace without absolute or parent traversal segments.`,
|
|
1466
|
+
configPath
|
|
1467
|
+
)
|
|
1468
|
+
);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
const portalOutput = normalizeRepoRelativePath("generated/portal");
|
|
1472
|
+
const controlledWorkspaceChildren = [
|
|
1473
|
+
["truthmark.routes.index", rawConfig.truthmark.routes.index],
|
|
1474
|
+
["truthmark.routes.areas", rawConfig.truthmark.routes.areas],
|
|
1475
|
+
["truthmark.truth.root", rawConfig.truthmark.truth.root],
|
|
1476
|
+
["truthmark.templates.root", rawConfig.truthmark.templates.root]
|
|
1477
|
+
];
|
|
1478
|
+
for (const [name, value] of controlledWorkspaceChildren) {
|
|
1479
|
+
if (pathsOverlap(portalOutput, value)) {
|
|
1480
|
+
diagnostics.push(
|
|
1481
|
+
toConfigDiagnostic(
|
|
1482
|
+
`Truthmark Portal output ${portalOutputFor(workspace)} must not overlap ${name}; Portal output is generated and must stay outside controlled truth, routing, and template paths.`,
|
|
1483
|
+
configPath
|
|
1484
|
+
)
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
for (const target of rawConfig.instruction_targets ?? DEFAULT_INSTRUCTION_TARGETS) {
|
|
1489
|
+
if (isUnsafeRepoRelativePath(target) || pathsOverlap(workspace, target)) {
|
|
1490
|
+
diagnostics.push(
|
|
1491
|
+
toConfigDiagnostic(
|
|
1492
|
+
"instruction_targets must be repo-relative files outside truthmark.workspace.",
|
|
1493
|
+
configPath
|
|
1494
|
+
)
|
|
1495
|
+
);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
return diagnostics;
|
|
1499
|
+
};
|
|
1473
1500
|
var normalizeConfig = (rawConfig) => {
|
|
1474
|
-
const
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
const
|
|
1501
|
+
const workspace = normalizeRepoRelativePath(rawConfig.truthmark.workspace);
|
|
1502
|
+
const routesIndex = joinWorkspacePath(workspace, rawConfig.truthmark.routes.index);
|
|
1503
|
+
const routeAreasRoot = joinWorkspacePath(workspace, rawConfig.truthmark.routes.areas);
|
|
1504
|
+
const truthRoot3 = joinWorkspacePath(workspace, rawConfig.truthmark.truth.root);
|
|
1505
|
+
const templatesRoot = joinWorkspacePath(workspace, rawConfig.truthmark.templates.root);
|
|
1506
|
+
const portalOutput = portalOutputFor(workspace);
|
|
1507
|
+
const portalTemplate = portalTemplateFor(templatesRoot);
|
|
1480
1508
|
return {
|
|
1481
1509
|
version: rawConfig.version,
|
|
1482
1510
|
platforms: rawConfig.platforms ?? [...DEFAULT_PLATFORMS],
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1511
|
+
truthmark: {
|
|
1512
|
+
workspace,
|
|
1513
|
+
routes: {
|
|
1514
|
+
index: normalizeRepoRelativePath(rawConfig.truthmark.routes.index),
|
|
1515
|
+
areas: normalizeRepoRelativePath(rawConfig.truthmark.routes.areas),
|
|
1516
|
+
defaultArea: rawConfig.truthmark.routes.default_area,
|
|
1517
|
+
maxDelegationDepth: rawConfig.truthmark.routes.max_delegation_depth
|
|
1518
|
+
},
|
|
1519
|
+
truth: {
|
|
1520
|
+
root: normalizeRepoRelativePath(rawConfig.truthmark.truth.root)
|
|
1521
|
+
},
|
|
1522
|
+
templates: {
|
|
1523
|
+
root: normalizeRepoRelativePath(rawConfig.truthmark.templates.root)
|
|
1524
|
+
},
|
|
1525
|
+
generated: {
|
|
1526
|
+
portal: {
|
|
1527
|
+
enabled: rawConfig.truthmark.generated.portal.enabled
|
|
1528
|
+
}
|
|
1529
|
+
},
|
|
1530
|
+
paths: {
|
|
1531
|
+
routesIndex,
|
|
1532
|
+
routeAreasRoot,
|
|
1533
|
+
truthRoot: truthRoot3,
|
|
1534
|
+
templatesRoot,
|
|
1535
|
+
portalOutput,
|
|
1536
|
+
portalTemplate
|
|
1537
|
+
},
|
|
1538
|
+
controlledPaths: [
|
|
1539
|
+
routesIndex,
|
|
1540
|
+
`${routeAreasRoot}/**/*.md`,
|
|
1541
|
+
`${truthRoot3}/**/*.md`,
|
|
1542
|
+
`${templatesRoot}/*.md`
|
|
1543
|
+
]
|
|
1492
1544
|
},
|
|
1493
|
-
authority: rawConfig.authority,
|
|
1494
1545
|
instructionTargets: rawConfig.instruction_targets ?? [...DEFAULT_INSTRUCTION_TARGETS],
|
|
1495
|
-
truthmarkPortal: {
|
|
1496
|
-
enabled: rawConfig["truthmark-portal"]?.enabled ?? DEFAULT_TRUTHMARK_PORTAL.enabled,
|
|
1497
|
-
output: rawConfig["truthmark-portal"]?.output ?? DEFAULT_TRUTHMARK_PORTAL.output,
|
|
1498
|
-
template: rawConfig["truthmark-portal"]?.template ?? DEFAULT_TRUTHMARK_PORTAL.template
|
|
1499
|
-
},
|
|
1500
1546
|
frontmatter: {
|
|
1501
1547
|
required: rawConfig.frontmatter?.required ?? [],
|
|
1502
1548
|
recommended: rawConfig.frontmatter?.recommended ?? []
|
|
@@ -1505,8 +1551,7 @@ var normalizeConfig = (rawConfig) => {
|
|
|
1505
1551
|
};
|
|
1506
1552
|
};
|
|
1507
1553
|
var loadConfig = async (rootDir) => {
|
|
1508
|
-
const
|
|
1509
|
-
const absolutePath = resolveRepoPath(rootDir, configPath);
|
|
1554
|
+
const absolutePath = resolveRepoPath(rootDir, CONFIG_PATH2);
|
|
1510
1555
|
let source;
|
|
1511
1556
|
try {
|
|
1512
1557
|
source = await fs4.readFile(absolutePath, "utf8");
|
|
@@ -1515,8 +1560,8 @@ var loadConfig = async (rootDir) => {
|
|
|
1515
1560
|
return {
|
|
1516
1561
|
status: "missing",
|
|
1517
1562
|
config: null,
|
|
1518
|
-
diagnostics: [toConfigDiagnostic("Missing .truthmark/config.yml.",
|
|
1519
|
-
configPath
|
|
1563
|
+
diagnostics: [toConfigDiagnostic("Missing .truthmark/config.yml.", CONFIG_PATH2)],
|
|
1564
|
+
configPath: CONFIG_PATH2
|
|
1520
1565
|
};
|
|
1521
1566
|
}
|
|
1522
1567
|
throw error;
|
|
@@ -1531,10 +1576,19 @@ var loadConfig = async (rootDir) => {
|
|
|
1531
1576
|
diagnostics: [
|
|
1532
1577
|
toConfigDiagnostic(
|
|
1533
1578
|
`Invalid YAML: ${error instanceof Error ? error.message : String(error)}`,
|
|
1534
|
-
|
|
1579
|
+
CONFIG_PATH2
|
|
1535
1580
|
)
|
|
1536
1581
|
],
|
|
1537
|
-
configPath
|
|
1582
|
+
configPath: CONFIG_PATH2
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
const unsupportedDiagnostics = unsupportedShapeDiagnostics(parsedConfig, CONFIG_PATH2);
|
|
1586
|
+
if (unsupportedDiagnostics.length > 0) {
|
|
1587
|
+
return {
|
|
1588
|
+
status: "invalid",
|
|
1589
|
+
config: null,
|
|
1590
|
+
diagnostics: unsupportedDiagnostics,
|
|
1591
|
+
configPath: CONFIG_PATH2
|
|
1538
1592
|
};
|
|
1539
1593
|
}
|
|
1540
1594
|
if (!validateTruthmarkConfig(parsedConfig)) {
|
|
@@ -1545,49 +1599,30 @@ var loadConfig = async (rootDir) => {
|
|
|
1545
1599
|
const propertyPath = error.instancePath || "/";
|
|
1546
1600
|
const additionalProperty = error.keyword === "additionalProperties" && error.params && "additionalProperty" in error.params ? String(error.params.additionalProperty) : null;
|
|
1547
1601
|
const message = additionalProperty ? `${propertyPath} additional property ${additionalProperty} is not allowed` : `${propertyPath} ${error.message ?? "is invalid"}`.trim();
|
|
1548
|
-
return toConfigDiagnostic(message,
|
|
1602
|
+
return toConfigDiagnostic(message, CONFIG_PATH2);
|
|
1549
1603
|
}),
|
|
1550
|
-
configPath
|
|
1604
|
+
configPath: CONFIG_PATH2
|
|
1551
1605
|
};
|
|
1552
1606
|
}
|
|
1553
|
-
const
|
|
1554
|
-
|
|
1555
|
-
configPath
|
|
1556
|
-
);
|
|
1557
|
-
if (portalDiagnostics.length > 0) {
|
|
1607
|
+
const pathDiagnostics = validateWorkspacePaths(parsedConfig, CONFIG_PATH2);
|
|
1608
|
+
if (pathDiagnostics.length > 0) {
|
|
1558
1609
|
return {
|
|
1559
1610
|
status: "invalid",
|
|
1560
1611
|
config: null,
|
|
1561
|
-
diagnostics:
|
|
1562
|
-
configPath
|
|
1612
|
+
diagnostics: pathDiagnostics,
|
|
1613
|
+
configPath: CONFIG_PATH2
|
|
1563
1614
|
};
|
|
1564
1615
|
}
|
|
1565
1616
|
return {
|
|
1566
1617
|
status: "loaded",
|
|
1567
1618
|
config: normalizeConfig(parsedConfig),
|
|
1568
1619
|
diagnostics: [],
|
|
1569
|
-
configPath
|
|
1620
|
+
configPath: CONFIG_PATH2
|
|
1570
1621
|
};
|
|
1571
1622
|
};
|
|
1572
1623
|
|
|
1573
1624
|
// src/init/hierarchy.ts
|
|
1574
1625
|
import fs5 from "fs/promises";
|
|
1575
|
-
import fg from "fast-glob";
|
|
1576
|
-
var KNOWN_DEFAULT_ROOTS = [
|
|
1577
|
-
DEFAULT_DOCS_HIERARCHY.roots.truth,
|
|
1578
|
-
"docs/api",
|
|
1579
|
-
DEFAULT_DOCS_HIERARCHY.roots.architecture,
|
|
1580
|
-
DEFAULT_DOCS_HIERARCHY.roots.standards,
|
|
1581
|
-
"docs/guides"
|
|
1582
|
-
];
|
|
1583
|
-
var hasMarkdownFiles = async (rootDir, root) => {
|
|
1584
|
-
const matches = await fg([`${root}/**/*.md`], {
|
|
1585
|
-
cwd: rootDir,
|
|
1586
|
-
onlyFiles: true,
|
|
1587
|
-
followSymbolicLinks: false
|
|
1588
|
-
});
|
|
1589
|
-
return matches.length > 0;
|
|
1590
|
-
};
|
|
1591
1626
|
var truthRoot2 = resolveTruthDocsRoot;
|
|
1592
1627
|
var rootIndexReferencesChildRoute = async (rootDir, rootIndexPath, childRoutePath) => {
|
|
1593
1628
|
const rootIndexSource = await fs5.readFile(resolveRepoPath(rootDir, rootIndexPath), "utf8");
|
|
@@ -1596,9 +1631,15 @@ var rootIndexReferencesChildRoute = async (rootDir, rootIndexPath, childRoutePat
|
|
|
1596
1631
|
(areaReference) => areaReference.areaFiles.includes(childRoutePath)
|
|
1597
1632
|
);
|
|
1598
1633
|
};
|
|
1599
|
-
var
|
|
1634
|
+
var truthTemplatePath = (config, fileName) => {
|
|
1635
|
+
return `${config.truthmark.paths.templatesRoot}/${fileName}`;
|
|
1636
|
+
};
|
|
1637
|
+
var readBehaviorDocTemplate = async (rootDir, config) => {
|
|
1600
1638
|
try {
|
|
1601
|
-
return await fs5.readFile(
|
|
1639
|
+
return await fs5.readFile(
|
|
1640
|
+
resolveRepoPath(rootDir, truthTemplatePath(config, "behavior-doc.md")),
|
|
1641
|
+
"utf8"
|
|
1642
|
+
);
|
|
1602
1643
|
} catch (error) {
|
|
1603
1644
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
1604
1645
|
return renderBehaviorDocTemplateFile();
|
|
@@ -1618,16 +1659,16 @@ var ensureOrUpdateTruthDocTemplate = async (rootDir, templatePath, defaultTempla
|
|
|
1618
1659
|
var scaffoldHierarchy = async (rootDir, config) => {
|
|
1619
1660
|
const results = [];
|
|
1620
1661
|
const truthDocsRoot = truthRoot2(config);
|
|
1621
|
-
const truthDomainRoot = `${truthDocsRoot}/${config.
|
|
1622
|
-
const childRoutePath = `${config.
|
|
1662
|
+
const truthDomainRoot = `${truthDocsRoot}/${config.truthmark.routes.defaultArea}`;
|
|
1663
|
+
const childRoutePath = `${config.truthmark.paths.routeAreasRoot}/${config.truthmark.routes.defaultArea}.md`;
|
|
1623
1664
|
results.push(
|
|
1624
1665
|
await ensureRepoFile(
|
|
1625
1666
|
rootDir,
|
|
1626
|
-
config.
|
|
1667
|
+
config.truthmark.paths.routesIndex,
|
|
1627
1668
|
renderHierarchicalAreasIndexTemplate(config)
|
|
1628
1669
|
)
|
|
1629
1670
|
);
|
|
1630
|
-
if (await rootIndexReferencesChildRoute(rootDir, config.
|
|
1671
|
+
if (await rootIndexReferencesChildRoute(rootDir, config.truthmark.paths.routesIndex, childRoutePath)) {
|
|
1631
1672
|
results.push(await ensureRepoFile(rootDir, childRoutePath, renderChildAreaTemplate(config)));
|
|
1632
1673
|
}
|
|
1633
1674
|
results.push(
|
|
@@ -1647,46 +1688,46 @@ var scaffoldHierarchy = async (rootDir, config) => {
|
|
|
1647
1688
|
results.push(
|
|
1648
1689
|
await ensureOrUpdateTruthDocTemplate(
|
|
1649
1690
|
rootDir,
|
|
1650
|
-
|
|
1691
|
+
truthTemplatePath(config, "behavior-doc.md"),
|
|
1651
1692
|
renderBehaviorDocTemplateFile()
|
|
1652
1693
|
)
|
|
1653
1694
|
);
|
|
1654
1695
|
results.push(
|
|
1655
1696
|
await ensureOrUpdateTruthDocTemplate(
|
|
1656
1697
|
rootDir,
|
|
1657
|
-
|
|
1698
|
+
truthTemplatePath(config, "contract-doc.md"),
|
|
1658
1699
|
renderContractDocTemplateFile()
|
|
1659
1700
|
)
|
|
1660
1701
|
);
|
|
1661
1702
|
results.push(
|
|
1662
1703
|
await ensureOrUpdateTruthDocTemplate(
|
|
1663
1704
|
rootDir,
|
|
1664
|
-
|
|
1705
|
+
truthTemplatePath(config, "architecture-doc.md"),
|
|
1665
1706
|
renderArchitectureDocTemplateFile()
|
|
1666
1707
|
)
|
|
1667
1708
|
);
|
|
1668
1709
|
results.push(
|
|
1669
1710
|
await ensureOrUpdateTruthDocTemplate(
|
|
1670
1711
|
rootDir,
|
|
1671
|
-
|
|
1712
|
+
truthTemplatePath(config, "workflow-doc.md"),
|
|
1672
1713
|
renderWorkflowDocTemplateFile()
|
|
1673
1714
|
)
|
|
1674
1715
|
);
|
|
1675
1716
|
results.push(
|
|
1676
1717
|
await ensureOrUpdateTruthDocTemplate(
|
|
1677
1718
|
rootDir,
|
|
1678
|
-
|
|
1719
|
+
truthTemplatePath(config, "operations-doc.md"),
|
|
1679
1720
|
renderOperationsDocTemplateFile()
|
|
1680
1721
|
)
|
|
1681
1722
|
);
|
|
1682
1723
|
results.push(
|
|
1683
1724
|
await ensureOrUpdateTruthDocTemplate(
|
|
1684
1725
|
rootDir,
|
|
1685
|
-
|
|
1726
|
+
truthTemplatePath(config, "test-behavior-doc.md"),
|
|
1686
1727
|
renderTestBehaviorDocTemplateFile()
|
|
1687
1728
|
)
|
|
1688
1729
|
);
|
|
1689
|
-
const behaviorDocTemplate = await readBehaviorDocTemplate(rootDir);
|
|
1730
|
+
const behaviorDocTemplate = await readBehaviorDocTemplate(rootDir, config);
|
|
1690
1731
|
results.push(
|
|
1691
1732
|
await ensureRepoFile(
|
|
1692
1733
|
rootDir,
|
|
@@ -1696,24 +1737,6 @@ var scaffoldHierarchy = async (rootDir, config) => {
|
|
|
1696
1737
|
);
|
|
1697
1738
|
return results;
|
|
1698
1739
|
};
|
|
1699
|
-
var detectHierarchyMigrationDiagnostics = async (rootDir, config) => {
|
|
1700
|
-
const configuredRoots = new Set(Object.values(config.docs.roots));
|
|
1701
|
-
const diagnostics = [];
|
|
1702
|
-
for (const defaultRoot of KNOWN_DEFAULT_ROOTS) {
|
|
1703
|
-
if (configuredRoots.has(defaultRoot)) {
|
|
1704
|
-
continue;
|
|
1705
|
-
}
|
|
1706
|
-
if (await hasMarkdownFiles(rootDir, defaultRoot)) {
|
|
1707
|
-
diagnostics.push({
|
|
1708
|
-
category: "config",
|
|
1709
|
-
severity: "review",
|
|
1710
|
-
message: `Configured hierarchy no longer includes ${defaultRoot}, but markdown still exists there. Perform manual migration before relying on the new hierarchy.`,
|
|
1711
|
-
file: ".truthmark/config.yml"
|
|
1712
|
-
});
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
|
-
return diagnostics;
|
|
1716
|
-
};
|
|
1717
1740
|
|
|
1718
1741
|
// src/truth/evidence.ts
|
|
1719
1742
|
var renderClaimEvidenceCheckedSection = (items) => {
|
|
@@ -1758,12 +1781,12 @@ var REPOSITORY_INTELLIGENCE_INSTRUCTIONS = [
|
|
|
1758
1781
|
"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."
|
|
1759
1782
|
].join("\n");
|
|
1760
1783
|
var FEATURE_DOC_TEMPLATE_INSTRUCTIONS = [
|
|
1761
|
-
"When creating or updating a truth doc, inspect the routed truth kind and use the matching
|
|
1784
|
+
"When creating or updating a truth doc, inspect the routed truth kind and use the matching template under the configured Truthmark templates root.",
|
|
1762
1785
|
"Supported kinds: behavior, contract, architecture, workflow, operations, and test-behavior.",
|
|
1763
1786
|
"Treat the HTML comments under each template section as normative authoring guidance for that section.",
|
|
1764
1787
|
"Align existing docs to that template and write or repair section content so it satisfies the comment guidance while preserving accurate authored content.",
|
|
1765
1788
|
"If the template is missing, use Scope, Product Decisions, Rationale, and the kind-specific current-truth section.",
|
|
1766
|
-
"Teams may edit
|
|
1789
|
+
"Teams may edit template files under the configured Truthmark templates root to define their local truth-doc standards."
|
|
1767
1790
|
].join("\n");
|
|
1768
1791
|
var renderTruthDocOwnershipGateSection = (subject, outcome) => {
|
|
1769
1792
|
return [
|
|
@@ -1937,8 +1960,8 @@ var renderHierarchySummary = (config) => {
|
|
|
1937
1960
|
return [
|
|
1938
1961
|
"Truthmark hierarchy hints:",
|
|
1939
1962
|
"- Config, when present: .truthmark/config.yml",
|
|
1940
|
-
`- Root route index, when present: ${config.
|
|
1941
|
-
`- Area route files, when present: ${config.
|
|
1963
|
+
`- Root route index, when present: ${config.truthmark.paths.routesIndex}`,
|
|
1964
|
+
`- Area route files, when present: ${config.truthmark.paths.routeAreasRoot}/**/*.md`,
|
|
1942
1965
|
`- Truth docs, when present: ${truthRoot3}/**/*.md`
|
|
1943
1966
|
].join("\n");
|
|
1944
1967
|
};
|
|
@@ -1955,10 +1978,10 @@ var TRUTHMARK_BLOCK_START = "<!-- truthmark:start -->";
|
|
|
1955
1978
|
var TRUTHMARK_BLOCK_END = "<!-- truthmark:end -->";
|
|
1956
1979
|
var renderCompactHierarchySummary = (config) => {
|
|
1957
1980
|
const truthRoot3 = resolveTruthDocsRoot(config);
|
|
1958
|
-
return `Hierarchy hints: config .truthmark/config.yml when present; routes ${config.
|
|
1981
|
+
return `Hierarchy hints: config .truthmark/config.yml when present; routes ${config.truthmark.paths.routesIndex} and ${config.truthmark.paths.routeAreasRoot}/**/*.md when present; Truth docs: ${truthRoot3}/**/*.md when present.`;
|
|
1959
1982
|
};
|
|
1960
1983
|
var renderAgentsBlock = (config = defaultAgentConfig()) => {
|
|
1961
|
-
const portalLine = config.
|
|
1984
|
+
const portalLine = config.truthmark.generated.portal.enabled ? `Truthmark Portal is a separate manual-only presentation workflow. Run it only when explicitly requested; it writes generated non-canonical static files under ${config.truthmark.paths.portalOutput}/. Markdown remains canonical.` : null;
|
|
1962
1985
|
return [
|
|
1963
1986
|
TRUTHMARK_BLOCK_START,
|
|
1964
1987
|
"## Truthmark Workflow",
|
|
@@ -1978,70 +2001,6 @@ var renderAgentsBlock = (config = defaultAgentConfig()) => {
|
|
|
1978
2001
|
].join("\n");
|
|
1979
2002
|
};
|
|
1980
2003
|
|
|
1981
|
-
// src/templates/default-standards.ts
|
|
1982
|
-
var DEFAULT_STANDARDS = [
|
|
1983
|
-
{
|
|
1984
|
-
path: "docs/standards/default-principles.md",
|
|
1985
|
-
content: `---
|
|
1986
|
-
status: active
|
|
1987
|
-
doc_type: standard
|
|
1988
|
-
last_reviewed: 2026-05-03
|
|
1989
|
-
source_of_truth:
|
|
1990
|
-
- README.md
|
|
1991
|
-
---
|
|
1992
|
-
|
|
1993
|
-
# Default Principles
|
|
1994
|
-
|
|
1995
|
-
## Scope
|
|
1996
|
-
|
|
1997
|
-
This is a bootstrap standards baseline for repositories that adopt Truthmark.
|
|
1998
|
-
|
|
1999
|
-
## Reusable Defaults
|
|
2000
|
-
|
|
2001
|
-
- Authority order should be explicit.
|
|
2002
|
-
- Committed repository artifacts are the durable source of truth.
|
|
2003
|
-
- Each document should have one primary responsibility.
|
|
2004
|
-
- Each class of fact should have one canonical source.
|
|
2005
|
-
- Architecture docs describe system structure, module boundaries, runtime topology, persistence boundaries, cross-cutting contracts, and generated-surface ownership.
|
|
2006
|
-
- Do not put ordinary feature behavior in architecture docs.
|
|
2007
|
-
- Verification should be explicit, and skipped checks should state why.
|
|
2008
|
-
- Missing, stale, broad, overloaded, or unrouteable documentation topology should be repaired through AI-native structure workflow before agents create more generic truth docs.
|
|
2009
|
-
- Installed repository workflows should remain usable from committed files even when the Truthmark CLI is unavailable.
|
|
2010
|
-
`
|
|
2011
|
-
},
|
|
2012
|
-
{
|
|
2013
|
-
path: "docs/standards/documentation-governance.md",
|
|
2014
|
-
content: `---
|
|
2015
|
-
status: active
|
|
2016
|
-
doc_type: standard
|
|
2017
|
-
last_reviewed: 2026-05-03
|
|
2018
|
-
source_of_truth:
|
|
2019
|
-
- README.md
|
|
2020
|
-
---
|
|
2021
|
-
|
|
2022
|
-
# Documentation Governance
|
|
2023
|
-
|
|
2024
|
-
## Core Rules
|
|
2025
|
-
|
|
2026
|
-
- Each document should have one primary responsibility.
|
|
2027
|
-
- Each class of fact should have one canonical source.
|
|
2028
|
-
- Current implementation, reusable standards, and future proposals should be stored separately.
|
|
2029
|
-
- Generated helper output is never canonical truth.
|
|
2030
|
-
- Architecture docs describe structure and ownership; truth docs describe current product behavior.
|
|
2031
|
-
|
|
2032
|
-
## Truthmark Implications
|
|
2033
|
-
|
|
2034
|
-
- Truth Sync should extend mapped docs first, create an area-local doc second, and create a new area only as a last resort.
|
|
2035
|
-
- Weak routing produces weak truth maintenance.
|
|
2036
|
-
- Missing, stale, broad, overloaded, or unrouteable routing should trigger Truth Structure before more generic truth docs are created.
|
|
2037
|
-
`
|
|
2038
|
-
}
|
|
2039
|
-
];
|
|
2040
|
-
var renderDefaultStandards = (documents) => {
|
|
2041
|
-
const existingPaths = new Set(documents.map((document) => document.path));
|
|
2042
|
-
return DEFAULT_STANDARDS.filter((template) => !existingPaths.has(template.path));
|
|
2043
|
-
};
|
|
2044
|
-
|
|
2045
2004
|
// src/templates/workflow-surfaces.ts
|
|
2046
2005
|
import { stringify as stringify2 } from "yaml";
|
|
2047
2006
|
|
|
@@ -2325,7 +2284,7 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2325
2284
|
"generate the Truthmark Portal",
|
|
2326
2285
|
"refresh the committed HTML docs site",
|
|
2327
2286
|
"create a browsable project map from Truthmark docs",
|
|
2328
|
-
"update
|
|
2287
|
+
"update the Truthmark Portal output",
|
|
2329
2288
|
"make a human-readable static site from the truth docs"
|
|
2330
2289
|
],
|
|
2331
2290
|
negativeTriggers: [
|
|
@@ -2339,7 +2298,7 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2339
2298
|
forbiddenAdjacency: [
|
|
2340
2299
|
"must not run as a completion gate",
|
|
2341
2300
|
"must not replace Truth Sync, Truth Check, Truth Document, Truth Realize, or Truth Structure",
|
|
2342
|
-
"must not write outside the
|
|
2301
|
+
"must not write outside the fixed Portal output directory"
|
|
2343
2302
|
],
|
|
2344
2303
|
requiredGates: [
|
|
2345
2304
|
"manual-only invocation",
|
|
@@ -2347,7 +2306,7 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2347
2306
|
"Markdown canonical statement",
|
|
2348
2307
|
"source provenance"
|
|
2349
2308
|
],
|
|
2350
|
-
allowedWrites: ["
|
|
2309
|
+
allowedWrites: ["fixed Portal output directory only"],
|
|
2351
2310
|
reportSections: [
|
|
2352
2311
|
"Output path",
|
|
2353
2312
|
"Page count",
|
|
@@ -2372,7 +2331,7 @@ var renderMarkdownExample = (content) => {
|
|
|
2372
2331
|
};
|
|
2373
2332
|
var TRUTH_CHECK_EXPLICIT_INVOCATIONS = "OpenCode /skill truthmark-check; Codex /truthmark-check or $truthmark-check; Claude Code /truthmark-check; GitHub Copilot /truthmark-check; Gemini CLI /truthmark:check.";
|
|
2374
2333
|
var renderTruthCheckReportExample = (config = defaultAgentConfig()) => {
|
|
2375
|
-
const rootRouteIndex = config.
|
|
2334
|
+
const rootRouteIndex = config.truthmark.paths.routesIndex;
|
|
2376
2335
|
return `Truth Check: completed
|
|
2377
2336
|
|
|
2378
2337
|
Files reviewed:
|
|
@@ -2441,7 +2400,7 @@ Truth Check is agent-led:
|
|
|
2441
2400
|
|
|
2442
2401
|
- inspect .truthmark/config.yml and configured route files only when they exist; then inspect canonical docs and relevant implementation directly
|
|
2443
2402
|
- ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
|
|
2444
|
-
- inspect the configured root route index at ${config.
|
|
2403
|
+
- inspect the configured root route index at ${config.truthmark.paths.routesIndex} and relevant child route files under ${config.truthmark.paths.routeAreasRoot}/ when they exist
|
|
2445
2404
|
- check that current docs describe current code rather than historical plans
|
|
2446
2405
|
- check that route files map code surfaces to canonical truth docs when route files exist
|
|
2447
2406
|
- check for broad, catch-all, index-like, or mixed-owner truth docs and report them as topology issues requiring Truth Structure
|
|
@@ -2474,7 +2433,7 @@ Implementation reviewed:
|
|
|
2474
2433
|
- src/routing/area-resolver.ts
|
|
2475
2434
|
|
|
2476
2435
|
Ownership reviewed:
|
|
2477
|
-
- ${config.
|
|
2436
|
+
- ${config.truthmark.paths.routesIndex}
|
|
2478
2437
|
|
|
2479
2438
|
Truth docs created:
|
|
2480
2439
|
- ${truthDocsRoot}/contracts.md
|
|
@@ -2486,14 +2445,14 @@ Truth docs restructured:
|
|
|
2486
2445
|
- ${truthDocsRoot}/check-diagnostics.md
|
|
2487
2446
|
|
|
2488
2447
|
Routing updated:
|
|
2489
|
-
- ${config.
|
|
2448
|
+
- ${config.truthmark.paths.routesIndex}
|
|
2490
2449
|
|
|
2491
2450
|
${renderClaimEvidenceCheckedSection([
|
|
2492
2451
|
{
|
|
2493
2452
|
claim: "Route resolution behavior is documented in the contracts truth doc.",
|
|
2494
2453
|
evidence: [
|
|
2495
2454
|
"src/routing/area-resolver.ts:14",
|
|
2496
|
-
`${config.
|
|
2455
|
+
`${config.truthmark.paths.routesIndex}:9`
|
|
2497
2456
|
],
|
|
2498
2457
|
result: "supported"
|
|
2499
2458
|
}
|
|
@@ -2551,7 +2510,7 @@ Truth Document is manual and implementation-first:
|
|
|
2551
2510
|
- inspect .truthmark/config.yml and configured route files only when they exist; then inspect existing canonical docs, implementation code, and tests directly
|
|
2552
2511
|
- ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
|
|
2553
2512
|
- document current implemented behavior; do not invent future behavior or planned endpoints
|
|
2554
|
-
- may write canonical truth docs and ${config.
|
|
2513
|
+
- may write canonical truth docs and ${config.truthmark.paths.routesIndex} or relevant child route files only
|
|
2555
2514
|
- must not write functional code
|
|
2556
2515
|
- when routing is missing, stale, broad, overloaded, catch-all, or cannot map the behavior to a bounded truth owner, run Truth Structure first when routing repair is safe and in scope
|
|
2557
2516
|
- block and recommend Truth Structure when routing repair is unsafe, ambiguous, or outside the task boundary
|
|
@@ -2614,7 +2573,7 @@ Why this workflow:
|
|
|
2614
2573
|
- forbidden adjacency considered: must not edit functional code
|
|
2615
2574
|
|
|
2616
2575
|
Likely route owner:
|
|
2617
|
-
- route file: ${config.
|
|
2576
|
+
- route file: ${config.truthmark.paths.routesIndex}
|
|
2618
2577
|
- truth doc: ${truthDocsRoot}/example.md
|
|
2619
2578
|
- confidence: medium
|
|
2620
2579
|
|
|
@@ -2658,8 +2617,8 @@ Purpose:
|
|
|
2658
2617
|
|
|
2659
2618
|
Read:
|
|
2660
2619
|
- .truthmark/config.yml, only when present
|
|
2661
|
-
- ${config.
|
|
2662
|
-
- relevant child route files under ${config.
|
|
2620
|
+
- ${config.truthmark.paths.routesIndex}, only when present
|
|
2621
|
+
- relevant child route files under ${config.truthmark.paths.routeAreasRoot}/, only when present
|
|
2663
2622
|
- relevant truth docs and implementation files needed to preview ownership
|
|
2664
2623
|
- ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
|
|
2665
2624
|
|
|
@@ -2688,12 +2647,12 @@ ${renderMarkdownExample3(renderTruthPreviewReportExample(config))}`;
|
|
|
2688
2647
|
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.";
|
|
2689
2648
|
var renderTruthmarkPortalSkillBody = (config = defaultAgentConfig()) => {
|
|
2690
2649
|
const workflow = getTruthmarkWorkflow("truthmark-portal");
|
|
2691
|
-
const output = config.
|
|
2692
|
-
const template = config.
|
|
2650
|
+
const output = config.truthmark.paths.portalOutput;
|
|
2651
|
+
const template = config.truthmark.paths.portalTemplate;
|
|
2693
2652
|
return `---
|
|
2694
2653
|
name: truthmark-portal
|
|
2695
2654
|
description: ${workflow.description}
|
|
2696
|
-
argument-hint: Optional
|
|
2655
|
+
argument-hint: Optional portal generation focus
|
|
2697
2656
|
user-invocable: true
|
|
2698
2657
|
truthmark-version: ${TRUTHMARK_VERSION}
|
|
2699
2658
|
---
|
|
@@ -2709,9 +2668,9 @@ Core rules:
|
|
|
2709
2668
|
- Markdown remains canonical; generated HTML is presentation only.
|
|
2710
2669
|
- Read Markdown directly from the checkout; the workflow does not require the truthmark CLI or package.
|
|
2711
2670
|
- truthmark check/index may be used only as optional supporting evidence when available.
|
|
2712
|
-
-
|
|
2713
|
-
-
|
|
2714
|
-
- The workflow may replace the entire output directory, but writes are limited to the
|
|
2671
|
+
- Determined Portal output is ${output}.
|
|
2672
|
+
- Determined Portal template path is ${template}; use built-in template instructions if that file is absent.
|
|
2673
|
+
- The workflow may replace the entire output directory, but writes are limited to the fixed Portal output directory only.
|
|
2715
2674
|
- Portal writes are generated non-canonical static files for human browsing.
|
|
2716
2675
|
- Generate a committed multi-page static HTML site with local CSS, JavaScript, assets, and search metadata under the output directory.
|
|
2717
2676
|
- Use no remote dependencies by default: no remote scripts, analytics, fonts, CSS, or CDN assets.
|
|
@@ -2723,8 +2682,8 @@ Core rules:
|
|
|
2723
2682
|
Workflow:
|
|
2724
2683
|
|
|
2725
2684
|
1. Confirm the user explicitly requested Portal generation or refresh.
|
|
2726
|
-
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
|
|
2727
|
-
3. Validate the
|
|
2685
|
+
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 determined Portal template when present.
|
|
2686
|
+
3. Validate the determined output path is repo-relative, non-empty, inside the repository, and does not overlap canonical docs, source roots, routing files, or instruction targets.
|
|
2728
2687
|
4. Plan the generated page inventory, diagrams/assets, source docs reviewed, and skipped or ambiguous docs.
|
|
2729
2688
|
5. Replace or write only under ${output}; do not edit canonical Markdown, routing, source code, or instruction files unless the user explicitly changes scope.
|
|
2730
2689
|
6. Generate the multi-page static site with local assets/search metadata and visible source provenance.
|
|
@@ -2771,11 +2730,11 @@ var renderTruthStructureReportExample = (config = defaultAgentConfig()) => {
|
|
|
2771
2730
|
Topology reviewed:
|
|
2772
2731
|
- controllers: src/auth/**
|
|
2773
2732
|
- docs root: ${truthDocsRoot}
|
|
2774
|
-
- route files: ${config.
|
|
2733
|
+
- route files: ${config.truthmark.paths.routesIndex}
|
|
2775
2734
|
Areas reviewed:
|
|
2776
2735
|
- src/auth/**
|
|
2777
2736
|
Routing updated:
|
|
2778
|
-
- ${config.
|
|
2737
|
+
- ${config.truthmark.paths.routesIndex}
|
|
2779
2738
|
Initial truth boundary:
|
|
2780
2739
|
- Area: Authentication
|
|
2781
2740
|
- Code: src/auth/**
|
|
@@ -2790,7 +2749,7 @@ Truth docs restructured:
|
|
|
2790
2749
|
${renderClaimEvidenceCheckedSection([
|
|
2791
2750
|
{
|
|
2792
2751
|
claim: "Session behavior belongs to a dedicated Authentication truth owner.",
|
|
2793
|
-
evidence: ["src/auth/**", `${config.
|
|
2752
|
+
evidence: ["src/auth/**", `${config.truthmark.paths.routesIndex}:7`],
|
|
2794
2753
|
result: "supported"
|
|
2795
2754
|
}
|
|
2796
2755
|
])}
|
|
@@ -2826,15 +2785,15 @@ Invocations: ${TRUTH_STRUCTURE_EXPLICIT_INVOCATIONS}
|
|
|
2826
2785
|
Truth Structure is agent-native:
|
|
2827
2786
|
- inspect repository layout, current docs, Truthmark config and route files when present, and relevant code directly
|
|
2828
2787
|
- ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
|
|
2829
|
-
- inspect the configured root route index at ${config.
|
|
2788
|
+
- inspect the configured root route index at ${config.truthmark.paths.routesIndex} and relevant child route files under ${config.truthmark.paths.routeAreasRoot}/ when they exist
|
|
2830
2789
|
- define areas by product or behavior ownership, not by mechanical directory mirroring
|
|
2831
|
-
- create or repair ${config.
|
|
2790
|
+
- create or repair ${config.truthmark.paths.routesIndex}
|
|
2832
2791
|
- create starter truth docs when useful and when they belong in the canonical current-truth surface
|
|
2833
2792
|
- Starter truth docs must use closed YAML frontmatter bounded by opening and closing --- lines; include status, doc_type, last_reviewed, and source_of_truth inside that frontmatter.
|
|
2834
2793
|
- Starter truth docs must include ## Product Decisions and ## Rationale sections.
|
|
2835
2794
|
${subagentMode}
|
|
2836
2795
|
${FEATURE_DOC_TEMPLATE_INSTRUCTIONS}
|
|
2837
|
-
- use ${truthDocsRoot}
|
|
2796
|
+
- use ${truthDocsRoot}/** for current truth destinations
|
|
2838
2797
|
- use only canonical current-truth destinations for starter truth docs
|
|
2839
2798
|
- keep active Product Decisions and Rationale in the canonical doc that owns the behavior
|
|
2840
2799
|
- preserve unrelated authored content
|
|
@@ -2869,7 +2828,7 @@ Topology pressure signals:
|
|
|
2869
2828
|
- the configured truth root has many direct non-index docs
|
|
2870
2829
|
- a changed controller, route, or service cannot map to a specific behavior doc
|
|
2871
2830
|
- Truth Sync would need to create a new generic truth doc because routing is too broad
|
|
2872
|
-
- endpoint or controller names reveal domains missing from ${config.
|
|
2831
|
+
- endpoint or controller names reveal domains missing from ${config.truthmark.paths.routeAreasRoot}/**
|
|
2873
2832
|
Use these review thresholds as guidance:
|
|
2874
2833
|
- more than 10 direct truth docs in one folder
|
|
2875
2834
|
- more than 15 leaf areas in one child route file
|
|
@@ -2878,7 +2837,7 @@ Use these review thresholds as guidance:
|
|
|
2878
2837
|
Repair rules:
|
|
2879
2838
|
- split broad, overloaded, or catch-all areas into behavior-owned child route files
|
|
2880
2839
|
- split mixed-owner truth docs into bounded owner docs before adding new behavior claims
|
|
2881
|
-
- create route files under ${config.
|
|
2840
|
+
- create route files under ${config.truthmark.paths.routeAreasRoot}/ when a product/domain boundary is clear
|
|
2882
2841
|
- create behavior truth docs under the configured truth root only when behavior lacks a current doc
|
|
2883
2842
|
- README.md files are indexes, not Truth Sync targets
|
|
2884
2843
|
- prefer bounded leaf truth docs at <truth-root>/<domain>/<behavior>.md
|
|
@@ -3117,12 +3076,12 @@ Report completion in this shape:
|
|
|
3117
3076
|
${renderMarkdownExample5(
|
|
3118
3077
|
renderTruthSyncCompletedReport({
|
|
3119
3078
|
changedCode: ["src/auth/session.ts"],
|
|
3120
|
-
ownershipReviewed: [config.
|
|
3079
|
+
ownershipReviewed: [config.truthmark.paths.routesIndex],
|
|
3121
3080
|
truthDocsUpdated: [`${truthDocsRoot}/repository/overview.md`],
|
|
3122
3081
|
evidenceChecked: [
|
|
3123
3082
|
{
|
|
3124
3083
|
claim: "Session timeout behavior is documented in the mapped repository truth doc.",
|
|
3125
|
-
evidence: ["src/auth/session.ts:12", `${config.
|
|
3084
|
+
evidence: ["src/auth/session.ts:12", `${config.truthmark.paths.routesIndex}:11`],
|
|
3126
3085
|
result: "supported"
|
|
3127
3086
|
}
|
|
3128
3087
|
],
|
|
@@ -3134,7 +3093,7 @@ Blocked report example:
|
|
|
3134
3093
|
${renderMarkdownExample5(
|
|
3135
3094
|
renderTruthSyncBlockedReport({
|
|
3136
3095
|
reason: "routing repair is not allowed",
|
|
3137
|
-
manualReviewFiles: [config.
|
|
3096
|
+
manualReviewFiles: [config.truthmark.paths.routesIndex],
|
|
3138
3097
|
nextAction: "update routing metadata and rerun Truth Sync"
|
|
3139
3098
|
})
|
|
3140
3099
|
)}`;
|
|
@@ -3157,20 +3116,20 @@ var TRUTHMARK_WRITE_WORKER_REPORT_FIELDS = [
|
|
|
3157
3116
|
];
|
|
3158
3117
|
|
|
3159
3118
|
// src/templates/workflow-surfaces.ts
|
|
3160
|
-
var TRUTHMARK_STRUCTURE_SKILL_PATH = ".
|
|
3161
|
-
var TRUTHMARK_STRUCTURE_SKILL_METADATA_PATH = ".
|
|
3162
|
-
var TRUTHMARK_DOCUMENT_SKILL_PATH = ".
|
|
3163
|
-
var TRUTHMARK_DOCUMENT_SKILL_METADATA_PATH = ".
|
|
3164
|
-
var TRUTHMARK_SYNC_SKILL_PATH = ".
|
|
3165
|
-
var TRUTHMARK_SYNC_SKILL_METADATA_PATH = ".
|
|
3166
|
-
var TRUTHMARK_REALIZE_SKILL_PATH = ".
|
|
3167
|
-
var TRUTHMARK_REALIZE_SKILL_METADATA_PATH = ".
|
|
3168
|
-
var TRUTHMARK_CHECK_SKILL_PATH = ".
|
|
3169
|
-
var TRUTHMARK_CHECK_SKILL_METADATA_PATH = ".
|
|
3170
|
-
var TRUTHMARK_PREVIEW_SKILL_PATH = ".
|
|
3171
|
-
var TRUTHMARK_PREVIEW_SKILL_METADATA_PATH = ".
|
|
3172
|
-
var TRUTHMARK_PORTAL_SKILL_PATH = ".
|
|
3173
|
-
var TRUTHMARK_PORTAL_SKILL_METADATA_PATH = ".
|
|
3119
|
+
var TRUTHMARK_STRUCTURE_SKILL_PATH = ".agents/skills/truthmark-structure/SKILL.md";
|
|
3120
|
+
var TRUTHMARK_STRUCTURE_SKILL_METADATA_PATH = ".agents/skills/truthmark-structure/agents/openai.yaml";
|
|
3121
|
+
var TRUTHMARK_DOCUMENT_SKILL_PATH = ".agents/skills/truthmark-document/SKILL.md";
|
|
3122
|
+
var TRUTHMARK_DOCUMENT_SKILL_METADATA_PATH = ".agents/skills/truthmark-document/agents/openai.yaml";
|
|
3123
|
+
var TRUTHMARK_SYNC_SKILL_PATH = ".agents/skills/truthmark-sync/SKILL.md";
|
|
3124
|
+
var TRUTHMARK_SYNC_SKILL_METADATA_PATH = ".agents/skills/truthmark-sync/agents/openai.yaml";
|
|
3125
|
+
var TRUTHMARK_REALIZE_SKILL_PATH = ".agents/skills/truthmark-realize/SKILL.md";
|
|
3126
|
+
var TRUTHMARK_REALIZE_SKILL_METADATA_PATH = ".agents/skills/truthmark-realize/agents/openai.yaml";
|
|
3127
|
+
var TRUTHMARK_CHECK_SKILL_PATH = ".agents/skills/truthmark-check/SKILL.md";
|
|
3128
|
+
var TRUTHMARK_CHECK_SKILL_METADATA_PATH = ".agents/skills/truthmark-check/agents/openai.yaml";
|
|
3129
|
+
var TRUTHMARK_PREVIEW_SKILL_PATH = ".agents/skills/truthmark-preview/SKILL.md";
|
|
3130
|
+
var TRUTHMARK_PREVIEW_SKILL_METADATA_PATH = ".agents/skills/truthmark-preview/agents/openai.yaml";
|
|
3131
|
+
var TRUTHMARK_PORTAL_SKILL_PATH = ".agents/skills/truthmark-portal/SKILL.md";
|
|
3132
|
+
var TRUTHMARK_PORTAL_SKILL_METADATA_PATH = ".agents/skills/truthmark-portal/agents/openai.yaml";
|
|
3174
3133
|
var TRUTHMARK_ROUTE_AUDITOR_AGENT_PATH = ".codex/agents/truth-route-auditor.toml";
|
|
3175
3134
|
var TRUTHMARK_CLAIM_VERIFIER_AGENT_PATH = ".codex/agents/truth-claim-verifier.toml";
|
|
3176
3135
|
var TRUTHMARK_DOC_REVIEWER_AGENT_PATH = ".codex/agents/truth-doc-reviewer.toml";
|
|
@@ -3201,10 +3160,10 @@ var TRUTHMARK_COPILOT_REALIZE_PROMPT_PATH = ".github/prompts/truthmark-realize.p
|
|
|
3201
3160
|
var TRUTHMARK_COPILOT_CHECK_PROMPT_PATH = ".github/prompts/truthmark-check.prompt.md";
|
|
3202
3161
|
var TRUTHMARK_COPILOT_PREVIEW_PROMPT_PATH = ".github/prompts/truthmark-preview.prompt.md";
|
|
3203
3162
|
var TRUTHMARK_COPILOT_PORTAL_PROMPT_PATH = ".github/prompts/truthmark-portal.prompt.md";
|
|
3204
|
-
var TRUTHMARK_COPILOT_ROUTE_AUDITOR_AGENT_PATH = ".github/agents/truth-route-auditor.
|
|
3205
|
-
var TRUTHMARK_COPILOT_CLAIM_VERIFIER_AGENT_PATH = ".github/agents/truth-claim-verifier.
|
|
3206
|
-
var TRUTHMARK_COPILOT_DOC_REVIEWER_AGENT_PATH = ".github/agents/truth-doc-reviewer.
|
|
3207
|
-
var TRUTHMARK_COPILOT_DOC_WRITER_AGENT_PATH = ".github/agents/truth-doc-writer.
|
|
3163
|
+
var TRUTHMARK_COPILOT_ROUTE_AUDITOR_AGENT_PATH = ".github/agents/truth-route-auditor.md";
|
|
3164
|
+
var TRUTHMARK_COPILOT_CLAIM_VERIFIER_AGENT_PATH = ".github/agents/truth-claim-verifier.md";
|
|
3165
|
+
var TRUTHMARK_COPILOT_DOC_REVIEWER_AGENT_PATH = ".github/agents/truth-doc-reviewer.md";
|
|
3166
|
+
var TRUTHMARK_COPILOT_DOC_WRITER_AGENT_PATH = ".github/agents/truth-doc-writer.md";
|
|
3208
3167
|
var renderGeminiCommand = (description, prompt) => {
|
|
3209
3168
|
const promptWithArgs = `${prompt.trimEnd()}
|
|
3210
3169
|
User focus or arguments: {{args}}`;
|
|
@@ -3230,7 +3189,7 @@ var renderTomlStringArray = (values) => {
|
|
|
3230
3189
|
return `[${values.map(renderTomlString).join(", ")}]`;
|
|
3231
3190
|
};
|
|
3232
3191
|
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.";
|
|
3233
|
-
var routeFilesHint = (config) => `${config.
|
|
3192
|
+
var routeFilesHint = (config) => `${config.truthmark.paths.routesIndex}; ${config.truthmark.paths.routeAreasRoot}/`;
|
|
3234
3193
|
var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
3235
3194
|
"truthmark-structure": {
|
|
3236
3195
|
title: "Truthmark Structure",
|
|
@@ -3258,7 +3217,7 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
3258
3217
|
"Document current implemented behavior; do not invent future behavior.",
|
|
3259
3218
|
"May write canonical truth docs and truth routing files only; must not write functional code.",
|
|
3260
3219
|
"Read support/procedure.md before editing truth docs.",
|
|
3261
|
-
"Read support/subagents-and-leases.md
|
|
3220
|
+
"Read support/subagents-and-leases.md only when dispatching or accepting worker output.",
|
|
3262
3221
|
"Read support/report-template.md before the final report."
|
|
3263
3222
|
],
|
|
3264
3223
|
parentRule: "Parent agent owns Truth Document acceptance, lease validation, and final report"
|
|
@@ -3275,7 +3234,7 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
3275
3234
|
"direct checkout inspection is the canonical path; do not require the truthmark binary.",
|
|
3276
3235
|
"May write canonical truth docs and truth routing files only; must not rewrite functional code.",
|
|
3277
3236
|
"Read support/procedure.md before editing truth docs.",
|
|
3278
|
-
"Read support/subagents-and-leases.md
|
|
3237
|
+
"Read support/subagents-and-leases.md only when dispatching or accepting worker output.",
|
|
3279
3238
|
"Read support/report-template.md before the final report."
|
|
3280
3239
|
],
|
|
3281
3240
|
parentRule: "Parent agent owns Truth Sync acceptance, lease validation, and final report"
|
|
@@ -3318,16 +3277,15 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
3318
3277
|
"Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
|
|
3319
3278
|
`Inspect .truthmark/config.yml and configured route files (${routeFilesHint(config)}) only when they exist; then inspect canonical docs and relevant implementation directly.`,
|
|
3320
3279
|
"Report issues and suggested fixes; do not silently rewrite unrelated files.",
|
|
3321
|
-
"Direct checkout inspection is valid even when local tooling is unavailable.",
|
|
3322
3280
|
"Read support/procedure.md before auditing details.",
|
|
3323
|
-
"Read support/subagents-and-leases.md
|
|
3281
|
+
"Read support/subagents-and-leases.md only when dispatching verifier subagents.",
|
|
3324
3282
|
"Read support/report-template.md before the final report."
|
|
3325
3283
|
],
|
|
3326
3284
|
parentRule: "Parent agent owns the final Truth Check report"
|
|
3327
3285
|
},
|
|
3328
3286
|
"truthmark-portal": {
|
|
3329
3287
|
title: "Truthmark Portal",
|
|
3330
|
-
argumentHint: "Optional
|
|
3288
|
+
argumentHint: "Optional portal generation focus",
|
|
3331
3289
|
invocations: TRUTHMARK_PORTAL_EXPLICIT_INVOCATIONS,
|
|
3332
3290
|
use: () => "Use this skill only when the user explicitly asks to generate or refresh the committed static HTML Truthmark Portal.",
|
|
3333
3291
|
quickRules: (config) => [
|
|
@@ -3336,8 +3294,8 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
3336
3294
|
"Markdown remains canonical; generated HTML is non-canonical presentation only.",
|
|
3337
3295
|
"Read Markdown directly; the workflow does not require the truthmark CLI or package.",
|
|
3338
3296
|
"Generate committed, generated non-canonical static files for humans.",
|
|
3339
|
-
`Write only under
|
|
3340
|
-
`Use
|
|
3297
|
+
`Write only under fixed Portal output ${config.truthmark.paths.portalOutput}.`,
|
|
3298
|
+
`Use determined Portal template ${config.truthmark.paths.portalTemplate} when present; no .truthmark/index.json dependency.`,
|
|
3341
3299
|
"Use no remote dependencies by default and include source provenance on every page.",
|
|
3342
3300
|
"Read support/procedure.md before generating Portal output.",
|
|
3343
3301
|
"Read support/report-template.md before the final report."
|
|
@@ -3441,7 +3399,25 @@ var renderStandaloneWorkflowSkillBody = (workflowId, config) => {
|
|
|
3441
3399
|
var renderWorkflowEntrypoint = (workflowId, config, supportFiles, host) => {
|
|
3442
3400
|
const workflow = getTruthmarkWorkflow(workflowId);
|
|
3443
3401
|
const definition = WORKFLOW_PACKAGE_DEFINITIONS[workflowId];
|
|
3444
|
-
const
|
|
3402
|
+
const supportFileUsage = (supportFile) => {
|
|
3403
|
+
if (supportFile === "support/procedure.md") {
|
|
3404
|
+
return "read before edits or detailed auditing; contains core quality gates";
|
|
3405
|
+
}
|
|
3406
|
+
if (supportFile === "support/report-template.md") {
|
|
3407
|
+
return "read before the final report";
|
|
3408
|
+
}
|
|
3409
|
+
if (supportFile === "support/subagents-and-leases.md") {
|
|
3410
|
+
return "read only when using subagents, leases, or accepting worker output";
|
|
3411
|
+
}
|
|
3412
|
+
if (supportFile === "support/helper-policy.md") {
|
|
3413
|
+
return "read only when invoking helper validators or reporting helper status";
|
|
3414
|
+
}
|
|
3415
|
+
if (supportFile === "helper-manifest.yml") {
|
|
3416
|
+
return "read only when invoking helper validators or validating helper registration";
|
|
3417
|
+
}
|
|
3418
|
+
return "available when relevant to the current step";
|
|
3419
|
+
};
|
|
3420
|
+
const supportFileList = supportFiles.map((supportFile) => `- ${supportFile} \u2014 ${supportFileUsage(supportFile)}`).join("\n");
|
|
3445
3421
|
const hostUsage = host === "github-copilot" ? "Use as a Copilot agent skill. Prompt files remain available under `.github/prompts/` for command-style invocation in supported Copilot IDEs." : host === "gemini-cli" ? "Use as a Gemini CLI Agent Skill; commands remain available under `/truthmark:*` for command-first invocation." : void 0;
|
|
3446
3422
|
return `---
|
|
3447
3423
|
name: ${workflowId}
|
|
@@ -3453,10 +3429,9 @@ truthmark-version: ${TRUTHMARK_VERSION}
|
|
|
3453
3429
|
|
|
3454
3430
|
# ${definition.title}
|
|
3455
3431
|
|
|
3456
|
-
${definition.use(config)}
|
|
3457
|
-
|
|
3458
|
-
${hostUsage}
|
|
3459
|
-
`}
|
|
3432
|
+
${definition.use(config)}${hostUsage === void 0 ? "" : `
|
|
3433
|
+
|
|
3434
|
+
${hostUsage}`}
|
|
3460
3435
|
|
|
3461
3436
|
Invocations: ${definition.invocations}
|
|
3462
3437
|
|
|
@@ -3586,10 +3561,10 @@ var renderOpenCodeWriterEditAllowRules = (config) => {
|
|
|
3586
3561
|
resolveTruthDocsRoot(config)
|
|
3587
3562
|
);
|
|
3588
3563
|
const rootRouteIndex = normalizeOpenCodePermissionPath(
|
|
3589
|
-
config.
|
|
3564
|
+
config.truthmark.paths.routesIndex
|
|
3590
3565
|
);
|
|
3591
3566
|
const areaFilesRoot = normalizeOpenCodePermissionPath(
|
|
3592
|
-
config.
|
|
3567
|
+
config.truthmark.paths.routeAreasRoot
|
|
3593
3568
|
);
|
|
3594
3569
|
const allowedPatterns = [
|
|
3595
3570
|
appendOpenCodePermissionGlob(truthDocsRoot, "/**"),
|
|
@@ -4359,7 +4334,7 @@ var codexFiles = (config) => {
|
|
|
4359
4334
|
content: renderTruthmarkDocWriterAgent()
|
|
4360
4335
|
}
|
|
4361
4336
|
];
|
|
4362
|
-
if (config.
|
|
4337
|
+
if (config.truthmark.generated.portal.enabled) {
|
|
4363
4338
|
files.push(
|
|
4364
4339
|
...renderTruthmarkSkillPackage({
|
|
4365
4340
|
skillPath: TRUTHMARK_PORTAL_SKILL_PATH,
|
|
@@ -4430,7 +4405,7 @@ var opencodeFiles = (config) => {
|
|
|
4430
4405
|
content: renderTruthmarkOpenCodeDocWriterAgent(config)
|
|
4431
4406
|
}
|
|
4432
4407
|
];
|
|
4433
|
-
if (config.
|
|
4408
|
+
if (config.truthmark.generated.portal.enabled) {
|
|
4434
4409
|
files.push(
|
|
4435
4410
|
...renderTruthmarkSkillPackage({
|
|
4436
4411
|
skillPath: ".opencode/skills/truthmark-portal/SKILL.md",
|
|
@@ -4498,7 +4473,7 @@ var claudeFiles = (config, block) => {
|
|
|
4498
4473
|
content: renderTruthmarkClaudeDocWriterAgent()
|
|
4499
4474
|
}
|
|
4500
4475
|
];
|
|
4501
|
-
if (config.
|
|
4476
|
+
if (config.truthmark.generated.portal.enabled) {
|
|
4502
4477
|
files.push(
|
|
4503
4478
|
...renderTruthmarkSkillPackage({
|
|
4504
4479
|
skillPath: ".claude/skills/truthmark-portal/SKILL.md",
|
|
@@ -4590,7 +4565,7 @@ var copilotFiles = (config, block) => {
|
|
|
4590
4565
|
content: renderTruthmarkCopilotDocWriterAgent()
|
|
4591
4566
|
}
|
|
4592
4567
|
];
|
|
4593
|
-
if (config.
|
|
4568
|
+
if (config.truthmark.generated.portal.enabled) {
|
|
4594
4569
|
files.push(
|
|
4595
4570
|
...renderTruthmarkSkillPackage({
|
|
4596
4571
|
skillPath: ".github/skills/truthmark-portal/SKILL.md",
|
|
@@ -4686,7 +4661,7 @@ var geminiFiles = (config, block) => {
|
|
|
4686
4661
|
content: renderTruthmarkGeminiDocWriterAgent()
|
|
4687
4662
|
}
|
|
4688
4663
|
];
|
|
4689
|
-
if (config.
|
|
4664
|
+
if (config.truthmark.generated.portal.enabled) {
|
|
4690
4665
|
files.push(
|
|
4691
4666
|
...renderTruthmarkSkillPackage({
|
|
4692
4667
|
skillPath: ".gemini/skills/truthmark-portal/SKILL.md",
|
|
@@ -4740,19 +4715,10 @@ var escapeRegExp = (value) => {
|
|
|
4740
4715
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4741
4716
|
};
|
|
4742
4717
|
var MANAGED_WORKFLOW_HEADING = "## Truthmark Workflow";
|
|
4743
|
-
var
|
|
4744
|
-
"
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
"- must not rewrite functional code"
|
|
4748
|
-
];
|
|
4749
|
-
var CANONICAL_MANAGED_LINES = /* @__PURE__ */ new Set(
|
|
4750
|
-
[
|
|
4751
|
-
...renderAgentsBlock().split("\n").map((line) => line.trim()).filter(
|
|
4752
|
-
(line) => line.length > 0 && line !== TRUTHMARK_BLOCK_START && line !== TRUTHMARK_BLOCK_END
|
|
4753
|
-
),
|
|
4754
|
-
...LEGACY_MANAGED_LINES
|
|
4755
|
-
]
|
|
4718
|
+
var CANONICAL_MANAGED_LINES = new Set(
|
|
4719
|
+
renderAgentsBlock().split("\n").map((line) => line.trim()).filter(
|
|
4720
|
+
(line) => line.length > 0 && line !== TRUTHMARK_BLOCK_START && line !== TRUTHMARK_BLOCK_END
|
|
4721
|
+
)
|
|
4756
4722
|
);
|
|
4757
4723
|
var countCanonicalManagedLineMatches = (lines) => {
|
|
4758
4724
|
return lines.reduce((matchCount, line) => {
|
|
@@ -4779,46 +4745,11 @@ var removeTrailingManagedChunk = (preservedLines) => {
|
|
|
4779
4745
|
preservedLines.splice(startIndex);
|
|
4780
4746
|
}
|
|
4781
4747
|
};
|
|
4782
|
-
var LEGACY_REPO_RULES_PATH = ["docs", "ai", `repo-${"rules.md"}`].join("/");
|
|
4783
|
-
var LEGACY_AGENT_ONBOARDING_PATH = ["docs", "ai", `agent-${"onboarding.md"}`].join(
|
|
4784
|
-
"/"
|
|
4785
|
-
);
|
|
4786
|
-
var LEGACY_PRIMARY_REPO_INSTRUCTION_PHRASE = [
|
|
4787
|
-
"primary repository",
|
|
4788
|
-
"instruction source"
|
|
4789
|
-
].join(" ");
|
|
4790
|
-
var normalizeLegacyInstructionPreamble = (content) => {
|
|
4791
|
-
return content.replaceAll(
|
|
4792
|
-
`Follow \`${LEGACY_REPO_RULES_PATH}\`.`,
|
|
4793
|
-
"Follow repository instruction files that are present in this checkout; do not assume optional policy docs exist."
|
|
4794
|
-
).replaceAll(
|
|
4795
|
-
`Follow \`${LEGACY_REPO_RULES_PATH}\` as the ${LEGACY_PRIMARY_REPO_INSTRUCTION_PHRASE}.`,
|
|
4796
|
-
"Follow repository instruction files that are present in this checkout; do not assume optional policy docs exist."
|
|
4797
|
-
).replaceAll(
|
|
4798
|
-
`Use that file as the ${LEGACY_PRIMARY_REPO_INSTRUCTION_PHRASE} for Codex.`,
|
|
4799
|
-
"Use explicitly configured repository policy docs only when they exist in this checkout."
|
|
4800
|
-
).replaceAll(
|
|
4801
|
-
`Use that file as the ${LEGACY_PRIMARY_REPO_INSTRUCTION_PHRASE} for this agent.`,
|
|
4802
|
-
"Use explicitly configured repository policy docs only when they exist in this checkout."
|
|
4803
|
-
).replaceAll("Codex-specific:", "Agent-specific:").replaceAll(
|
|
4804
|
-
"- Read `docs/README.md` for the canonical docs map.",
|
|
4805
|
-
"- Read the configured Truthmark routing files when choosing or updating canonical docs."
|
|
4806
|
-
).replaceAll(
|
|
4807
|
-
"- Read `docs/README.md` only when choosing or updating canonical docs.",
|
|
4808
|
-
"- Read the configured Truthmark routing files when choosing or updating canonical docs."
|
|
4809
|
-
).replaceAll(
|
|
4810
|
-
`- Use \`${LEGACY_AGENT_ONBOARDING_PATH}\` for quick task routing.`,
|
|
4811
|
-
"- Use repository onboarding or docs-map files only when present and needed for unclear or cross-area routing."
|
|
4812
|
-
).replaceAll(
|
|
4813
|
-
`- Use \`${LEGACY_AGENT_ONBOARDING_PATH}\` only when task routing is unclear or cross-area.`,
|
|
4814
|
-
"- Use repository onboarding or docs-map files only when present and needed for unclear or cross-area routing."
|
|
4815
|
-
);
|
|
4816
|
-
};
|
|
4817
4748
|
var upsertManagedBlock = (existingContent, block) => {
|
|
4818
4749
|
if (!existingContent || existingContent.trim().length === 0) {
|
|
4819
4750
|
return block;
|
|
4820
4751
|
}
|
|
4821
|
-
const normalizedExistingContent =
|
|
4752
|
+
const normalizedExistingContent = existingContent;
|
|
4822
4753
|
const startMarkerPattern = new RegExp(escapeRegExp(TRUTHMARK_BLOCK_START), "g");
|
|
4823
4754
|
const endMarkerPattern = new RegExp(escapeRegExp(TRUTHMARK_BLOCK_END), "g");
|
|
4824
4755
|
const managedBlockPattern = new RegExp(
|
|
@@ -4887,13 +4818,13 @@ var diagnosticCategoryForPath = (filePath, config) => {
|
|
|
4887
4818
|
if (filePath === "AGENTS.md") {
|
|
4888
4819
|
return "truth-sync";
|
|
4889
4820
|
}
|
|
4890
|
-
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(".
|
|
4821
|
+
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(".agents/skills/truthmark-realize/") || filePath.startsWith(".gemini/skills/truthmark-realize/")) {
|
|
4891
4822
|
return "realization";
|
|
4892
4823
|
}
|
|
4893
4824
|
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-")) {
|
|
4894
4825
|
return "truth-sync";
|
|
4895
4826
|
}
|
|
4896
|
-
if (filePath.startsWith(".
|
|
4827
|
+
if (filePath.startsWith(".agents/skills/truthmark-")) {
|
|
4897
4828
|
return "truth-sync";
|
|
4898
4829
|
}
|
|
4899
4830
|
if (filePath.startsWith(".gemini/commands/truthmark/realize")) {
|
|
@@ -4902,8 +4833,8 @@ var diagnosticCategoryForPath = (filePath, config) => {
|
|
|
4902
4833
|
if (filePath.startsWith(".gemini/commands/truthmark/")) {
|
|
4903
4834
|
return "truth-sync";
|
|
4904
4835
|
}
|
|
4905
|
-
if (filePath === config.
|
|
4906
|
-
return "
|
|
4836
|
+
if (filePath === config.truthmark.paths.routesIndex) {
|
|
4837
|
+
return "area-index";
|
|
4907
4838
|
}
|
|
4908
4839
|
return "config";
|
|
4909
4840
|
};
|
|
@@ -4938,7 +4869,7 @@ var runInit = async (cwd) => {
|
|
|
4938
4869
|
if (!loadedConfig.config) {
|
|
4939
4870
|
return {
|
|
4940
4871
|
command: "init",
|
|
4941
|
-
summary: "Truthmark init requires .truthmark/config.yml. Run truthmark config first, review the
|
|
4872
|
+
summary: "Truthmark init requires .truthmark/config.yml. Run truthmark config first, review the workspace paths, then run truthmark init.",
|
|
4942
4873
|
diagnostics: loadedConfig.diagnostics,
|
|
4943
4874
|
data: {
|
|
4944
4875
|
repositoryRoot: repository.repositoryRoot,
|
|
@@ -4949,14 +4880,9 @@ var runInit = async (cwd) => {
|
|
|
4949
4880
|
}
|
|
4950
4881
|
};
|
|
4951
4882
|
}
|
|
4952
|
-
const defaultStandards = renderDefaultStandards([]);
|
|
4953
4883
|
const results = [];
|
|
4954
|
-
for (const template of defaultStandards) {
|
|
4955
|
-
results.push(await ensureRepoFile(rootDir, template.path, template.content));
|
|
4956
|
-
}
|
|
4957
4884
|
const config = loadedConfig.config;
|
|
4958
4885
|
results.push(...await scaffoldHierarchy(rootDir, config));
|
|
4959
|
-
const migrationDiagnostics = await detectHierarchyMigrationDiagnostics(rootDir, config);
|
|
4960
4886
|
const block = renderAgentsBlock(config);
|
|
4961
4887
|
const platformFiles = renderGeneratedSurfaces(config, block);
|
|
4962
4888
|
for (const file of platformFiles) {
|
|
@@ -4966,7 +4892,7 @@ var runInit = async (cwd) => {
|
|
|
4966
4892
|
return {
|
|
4967
4893
|
command: "init",
|
|
4968
4894
|
summary: changedResults.length > 0 ? "Initialized or updated the Truthmark repository scaffold." : "Truthmark repository scaffold is already up to date.",
|
|
4969
|
-
diagnostics:
|
|
4895
|
+
diagnostics: writeDiagnostics(results, config),
|
|
4970
4896
|
data: {
|
|
4971
4897
|
repositoryRoot: repository.repositoryRoot,
|
|
4972
4898
|
worktreePath: repository.worktreePath,
|
|
@@ -4979,7 +4905,7 @@ var runInit = async (cwd) => {
|
|
|
4979
4905
|
|
|
4980
4906
|
// src/checks/branch-scope.ts
|
|
4981
4907
|
import fs8 from "fs/promises";
|
|
4982
|
-
import
|
|
4908
|
+
import fg from "fast-glob";
|
|
4983
4909
|
|
|
4984
4910
|
// src/markdown/hash.ts
|
|
4985
4911
|
import { createHash } from "crypto";
|
|
@@ -5020,10 +4946,11 @@ var getBranchScopeData = async (cwd) => {
|
|
|
5020
4946
|
const repository = await getGitRepository(cwd);
|
|
5021
4947
|
const relevantFileHashes = {};
|
|
5022
4948
|
const loadResult = await loadConfig(repository.worktreePath);
|
|
5023
|
-
const
|
|
5024
|
-
const
|
|
4949
|
+
const defaultConfig = createDefaultConfig();
|
|
4950
|
+
const rootIndex = loadResult.config?.truthmark.paths.routesIndex ?? defaultConfig.truthmark.paths.routesIndex;
|
|
4951
|
+
const areaFilesRoot = loadResult.config?.truthmark.paths.routeAreasRoot ?? defaultConfig.truthmark.paths.routeAreasRoot;
|
|
5025
4952
|
const relevantFiles = /* @__PURE__ */ new Set([...RELEVANT_BRANCH_SCOPE_FILES, rootIndex]);
|
|
5026
|
-
const routeFiles = await
|
|
4953
|
+
const routeFiles = await fg([`${areaFilesRoot}/**/*.md`], {
|
|
5027
4954
|
cwd: repository.worktreePath,
|
|
5028
4955
|
onlyFiles: true,
|
|
5029
4956
|
followSymbolicLinks: false
|
|
@@ -5051,7 +4978,7 @@ var getBranchScopeData = async (cwd) => {
|
|
|
5051
4978
|
|
|
5052
4979
|
// src/checks/authority.ts
|
|
5053
4980
|
import fs9 from "fs/promises";
|
|
5054
|
-
import
|
|
4981
|
+
import fg2 from "fast-glob";
|
|
5055
4982
|
var looksLikeGlob = (pattern) => {
|
|
5056
4983
|
return /[*?[\]{}()!+@]/u.test(pattern);
|
|
5057
4984
|
};
|
|
@@ -5066,11 +4993,11 @@ var pathExists = async (absolutePath) => {
|
|
|
5066
4993
|
throw error;
|
|
5067
4994
|
}
|
|
5068
4995
|
};
|
|
5069
|
-
var
|
|
4996
|
+
var checkControlledPaths = async (rootDir, controlledPaths) => {
|
|
5070
4997
|
const diagnostics = [];
|
|
5071
4998
|
const orderedPaths = [];
|
|
5072
4999
|
const seenPaths = /* @__PURE__ */ new Set();
|
|
5073
|
-
for (const entry of
|
|
5000
|
+
for (const entry of controlledPaths) {
|
|
5074
5001
|
if (looksLikeGlob(entry)) {
|
|
5075
5002
|
try {
|
|
5076
5003
|
resolveRepoPath(rootDir, entry);
|
|
@@ -5078,17 +5005,17 @@ var checkAuthority = async (rootDir, config) => {
|
|
|
5078
5005
|
diagnostics.push({
|
|
5079
5006
|
category: "authority",
|
|
5080
5007
|
severity: "error",
|
|
5081
|
-
message: `
|
|
5008
|
+
message: `Truthmark-controlled path ${entry} must stay inside the repository root.`,
|
|
5082
5009
|
file: entry
|
|
5083
5010
|
});
|
|
5084
5011
|
continue;
|
|
5085
5012
|
}
|
|
5086
|
-
const matches = (await
|
|
5013
|
+
const matches = (await fg2([entry], { cwd: rootDir, onlyFiles: true })).sort();
|
|
5087
5014
|
if (matches.length === 0) {
|
|
5088
5015
|
diagnostics.push({
|
|
5089
5016
|
category: "authority",
|
|
5090
5017
|
severity: "review",
|
|
5091
|
-
message: `
|
|
5018
|
+
message: `Truthmark-controlled glob ${entry} did not match any files.`,
|
|
5092
5019
|
file: entry
|
|
5093
5020
|
});
|
|
5094
5021
|
}
|
|
@@ -5100,7 +5027,7 @@ var checkAuthority = async (rootDir, config) => {
|
|
|
5100
5027
|
diagnostics.push({
|
|
5101
5028
|
category: "authority",
|
|
5102
5029
|
severity: "error",
|
|
5103
|
-
message: `
|
|
5030
|
+
message: `Truthmark-controlled path ${match} must stay inside the repository root.`,
|
|
5104
5031
|
file: match
|
|
5105
5032
|
});
|
|
5106
5033
|
continue;
|
|
@@ -5120,7 +5047,7 @@ var checkAuthority = async (rootDir, config) => {
|
|
|
5120
5047
|
diagnostics.push({
|
|
5121
5048
|
category: "authority",
|
|
5122
5049
|
severity: "error",
|
|
5123
|
-
message: `
|
|
5050
|
+
message: `Truthmark-controlled path ${entry} must stay inside the repository root.`,
|
|
5124
5051
|
file: entry
|
|
5125
5052
|
});
|
|
5126
5053
|
continue;
|
|
@@ -5129,7 +5056,7 @@ var checkAuthority = async (rootDir, config) => {
|
|
|
5129
5056
|
diagnostics.push({
|
|
5130
5057
|
category: "authority",
|
|
5131
5058
|
severity: "error",
|
|
5132
|
-
message: `Missing
|
|
5059
|
+
message: `Missing Truthmark-controlled file ${entry}.`,
|
|
5133
5060
|
file: entry
|
|
5134
5061
|
});
|
|
5135
5062
|
continue;
|
|
@@ -5144,6 +5071,9 @@ var checkAuthority = async (rootDir, config) => {
|
|
|
5144
5071
|
diagnostics
|
|
5145
5072
|
};
|
|
5146
5073
|
};
|
|
5074
|
+
var checkAuthority = async (rootDir, config) => {
|
|
5075
|
+
return checkControlledPaths(rootDir, config.truthmark.controlledPaths);
|
|
5076
|
+
};
|
|
5147
5077
|
|
|
5148
5078
|
// src/checks/frontmatter.ts
|
|
5149
5079
|
import fs10 from "fs/promises";
|
|
@@ -5320,12 +5250,12 @@ var checkLinks = async (rootDir, markdownPaths) => {
|
|
|
5320
5250
|
|
|
5321
5251
|
// src/checks/areas.ts
|
|
5322
5252
|
import fs13 from "fs/promises";
|
|
5323
|
-
import
|
|
5253
|
+
import fg4 from "fast-glob";
|
|
5324
5254
|
import micromatch4 from "micromatch";
|
|
5325
5255
|
|
|
5326
5256
|
// src/routing/area-resolver.ts
|
|
5327
5257
|
import fs12 from "fs/promises";
|
|
5328
|
-
import
|
|
5258
|
+
import fg3 from "fast-glob";
|
|
5329
5259
|
import micromatch2 from "micromatch";
|
|
5330
5260
|
var unique = (values) => {
|
|
5331
5261
|
return [...new Set(values)];
|
|
@@ -5490,7 +5420,7 @@ var resolveAreaRouting = async (rootDir, config) => {
|
|
|
5490
5420
|
);
|
|
5491
5421
|
}
|
|
5492
5422
|
}
|
|
5493
|
-
const routeFilesUnderRoot = await
|
|
5423
|
+
const routeFilesUnderRoot = await fg3([`${config.areaFilesRoot}/**/*.md`], {
|
|
5494
5424
|
cwd: rootDir,
|
|
5495
5425
|
onlyFiles: true,
|
|
5496
5426
|
followSymbolicLinks: false
|
|
@@ -5652,7 +5582,7 @@ var classifyPath = (filePath, ignorePatterns) => {
|
|
|
5652
5582
|
if (normalizedPath.startsWith(".truthmark/")) {
|
|
5653
5583
|
return "derived";
|
|
5654
5584
|
}
|
|
5655
|
-
if (normalizedPath.startsWith(".claude/") || normalizedPath.startsWith(".codex/") || normalizedPath.startsWith(".gemini/commands/") || normalizedPath.startsWith(".opencode/") || normalizedPath === ".github/copilot-instructions.md" || normalizedPath.startsWith(".github/agents/truth-") || normalizedPath.startsWith(".github/prompts/truthmark-") || normalizedPath === "AGENTS.md" || normalizedPath === "CLAUDE.md" || normalizedPath === "GEMINI.md" || normalizedPath.startsWith(".gemini/commands/truthmark/")) {
|
|
5585
|
+
if (normalizedPath.startsWith(".agents/skills/truthmark-") || normalizedPath.startsWith(".claude/") || normalizedPath.startsWith(".codex/") || normalizedPath.startsWith(".gemini/agents/truth-") || normalizedPath.startsWith(".gemini/commands/") || normalizedPath.startsWith(".gemini/skills/truthmark-") || normalizedPath.startsWith(".opencode/") || normalizedPath === ".github/copilot-instructions.md" || normalizedPath.startsWith(".github/agents/truth-") || normalizedPath.startsWith(".github/prompts/truthmark-") || normalizedPath.startsWith(".github/skills/truthmark-") || normalizedPath === "AGENTS.md" || normalizedPath === "CLAUDE.md" || normalizedPath === "GEMINI.md" || normalizedPath.startsWith(".gemini/commands/truthmark/")) {
|
|
5656
5586
|
return "derived";
|
|
5657
5587
|
}
|
|
5658
5588
|
if (ignorePatterns.length > 0 && micromatch3.isMatch(normalizedPath, ignorePatterns)) {
|
|
@@ -5728,11 +5658,11 @@ var isBroadCodeSurface = (pattern) => {
|
|
|
5728
5658
|
};
|
|
5729
5659
|
var checkAreas = async (rootDir, config) => {
|
|
5730
5660
|
const routing = await resolveAreaRouting(rootDir, {
|
|
5731
|
-
rootIndex: config.
|
|
5732
|
-
areaFilesRoot: config.
|
|
5661
|
+
rootIndex: config.truthmark.paths.routesIndex,
|
|
5662
|
+
areaFilesRoot: config.truthmark.paths.routeAreasRoot,
|
|
5733
5663
|
truthDocsRoot: resolveTruthDocsRoot(config)
|
|
5734
5664
|
});
|
|
5735
|
-
const discoveredCodeFiles = await
|
|
5665
|
+
const discoveredCodeFiles = await fg4([...COVERAGE_SCAN_PATTERNS], {
|
|
5736
5666
|
cwd: rootDir,
|
|
5737
5667
|
onlyFiles: true,
|
|
5738
5668
|
ignore: config.ignore,
|
|
@@ -5785,7 +5715,7 @@ var checkAreas = async (rootDir, config) => {
|
|
|
5785
5715
|
const routedGlobEntry = area.truthDocumentEntries.find(
|
|
5786
5716
|
(entry) => entry.path === truthDocument
|
|
5787
5717
|
);
|
|
5788
|
-
const matches = (await
|
|
5718
|
+
const matches = (await fg4([truthDocument], { cwd: rootDir, onlyFiles: true })).sort();
|
|
5789
5719
|
if (matches.length === 0) {
|
|
5790
5720
|
diagnostics.push({
|
|
5791
5721
|
category: "area-index",
|
|
@@ -5888,7 +5818,7 @@ var checkAreas = async (rootDir, config) => {
|
|
|
5888
5818
|
entry.valid = false;
|
|
5889
5819
|
continue;
|
|
5890
5820
|
}
|
|
5891
|
-
const matches = await
|
|
5821
|
+
const matches = await fg4([codeSurfaceEntry], {
|
|
5892
5822
|
cwd: rootDir,
|
|
5893
5823
|
onlyFiles: true,
|
|
5894
5824
|
followSymbolicLinks: false
|
|
@@ -6039,11 +5969,7 @@ var kindSpecificHeadingMessages = (source, kind) => {
|
|
|
6039
5969
|
return [];
|
|
6040
5970
|
};
|
|
6041
5971
|
var decisionTruthGlobs = (config) => {
|
|
6042
|
-
return [
|
|
6043
|
-
config.docs.roots.architecture,
|
|
6044
|
-
resolveTruthDocsRoot(config),
|
|
6045
|
-
config.docs.roots.api
|
|
6046
|
-
].filter((root) => Boolean(root)).map((root) => `${root}/**/*.md`);
|
|
5972
|
+
return [`${resolveTruthDocsRoot(config)}/**/*.md`];
|
|
6047
5973
|
};
|
|
6048
5974
|
var isDecisionTruthCandidate = (config, filePath) => {
|
|
6049
5975
|
return !filePath.endsWith("/README.md") && micromatch5.isMatch(filePath, decisionTruthGlobs(config));
|
|
@@ -6175,7 +6101,7 @@ import path8 from "path";
|
|
|
6175
6101
|
import fs16 from "fs/promises";
|
|
6176
6102
|
import path6 from "path";
|
|
6177
6103
|
import { execa as execa2 } from "execa";
|
|
6178
|
-
import
|
|
6104
|
+
import fg5 from "fast-glob";
|
|
6179
6105
|
import matter2 from "gray-matter";
|
|
6180
6106
|
import micromatch6 from "micromatch";
|
|
6181
6107
|
var languageByExtension = /* @__PURE__ */ new Map([
|
|
@@ -6257,7 +6183,7 @@ var isIgnoredPath = (filePath, ignore) => {
|
|
|
6257
6183
|
return micromatch6.isMatch(filePath, [...defaultIgnore, ...ignore]);
|
|
6258
6184
|
};
|
|
6259
6185
|
var discoverRepoFiles = async (rootDir, ignore) => {
|
|
6260
|
-
const discoveredFiles = await gitDiscoverableFiles(rootDir) ?? await
|
|
6186
|
+
const discoveredFiles = await gitDiscoverableFiles(rootDir) ?? await fg5(["**/*"], {
|
|
6261
6187
|
cwd: rootDir,
|
|
6262
6188
|
onlyFiles: true,
|
|
6263
6189
|
dot: true,
|
|
@@ -6318,7 +6244,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
|
|
|
6318
6244
|
// src/repo-index/package-metadata.ts
|
|
6319
6245
|
import fs17 from "fs/promises";
|
|
6320
6246
|
import path7 from "path";
|
|
6321
|
-
import
|
|
6247
|
+
import fg6 from "fast-glob";
|
|
6322
6248
|
var packageManagerFor = async (rootDir, packageDir) => {
|
|
6323
6249
|
const lockfiles = [
|
|
6324
6250
|
["package-lock.json", "npm"],
|
|
@@ -6338,7 +6264,7 @@ var packageManagerFor = async (rootDir, packageDir) => {
|
|
|
6338
6264
|
return "npm";
|
|
6339
6265
|
};
|
|
6340
6266
|
var discoverPackageMetadata = async (rootDir) => {
|
|
6341
|
-
const packageFiles = await
|
|
6267
|
+
const packageFiles = await fg6(["package.json", "*/package.json", "packages/*/package.json"], {
|
|
6342
6268
|
cwd: rootDir,
|
|
6343
6269
|
onlyFiles: true,
|
|
6344
6270
|
ignore: ["node_modules/**", "dist/**", "build/**"],
|
|
@@ -6372,8 +6298,8 @@ var buildRouteMap = async (rootDir) => {
|
|
|
6372
6298
|
};
|
|
6373
6299
|
}
|
|
6374
6300
|
const routing = await resolveAreaRouting(rootDir, {
|
|
6375
|
-
rootIndex: loadResult.config.
|
|
6376
|
-
areaFilesRoot: loadResult.config.
|
|
6301
|
+
rootIndex: loadResult.config.truthmark.paths.routesIndex,
|
|
6302
|
+
areaFilesRoot: loadResult.config.truthmark.paths.routeAreasRoot,
|
|
6377
6303
|
truthDocsRoot: resolveTruthDocsRoot(loadResult.config)
|
|
6378
6304
|
});
|
|
6379
6305
|
return {
|
|
@@ -6878,16 +6804,37 @@ var buildImpactSet = async (cwd, options) => {
|
|
|
6878
6804
|
};
|
|
6879
6805
|
};
|
|
6880
6806
|
|
|
6807
|
+
// src/freshness/check.ts
|
|
6808
|
+
var checkFreshness = async (rootDir, _config, _truthDocumentPaths, base) => {
|
|
6809
|
+
const impactSet = await buildImpactSet(rootDir, { base });
|
|
6810
|
+
const diagnostics = [];
|
|
6811
|
+
for (const diagnostic of impactSet.diagnostics) {
|
|
6812
|
+
if (diagnostic.category !== "impact") {
|
|
6813
|
+
continue;
|
|
6814
|
+
}
|
|
6815
|
+
diagnostics.push({
|
|
6816
|
+
...diagnostic,
|
|
6817
|
+
category: "freshness",
|
|
6818
|
+
message: diagnostic.message.replace("not mapped to a Truthmark route", "not routed to truth ownership")
|
|
6819
|
+
});
|
|
6820
|
+
}
|
|
6821
|
+
return {
|
|
6822
|
+
diagnostics,
|
|
6823
|
+
impactSet
|
|
6824
|
+
};
|
|
6825
|
+
};
|
|
6826
|
+
|
|
6881
6827
|
// src/evidence/validate.ts
|
|
6882
6828
|
import fs21 from "fs/promises";
|
|
6883
|
-
import
|
|
6829
|
+
import fg7 from "fast-glob";
|
|
6884
6830
|
|
|
6885
6831
|
// src/evidence/parse.ts
|
|
6886
6832
|
import fs20 from "fs/promises";
|
|
6887
6833
|
import path11 from "path";
|
|
6888
6834
|
import matter3 from "gray-matter";
|
|
6889
6835
|
import { parse as parse3 } from "yaml";
|
|
6890
|
-
var
|
|
6836
|
+
var yamlFencePattern = /```ya?ml\s*\n([\s\S]*?)```/giu;
|
|
6837
|
+
var topLevelEvidenceMarkerPattern = /^evidence\s*:/imu;
|
|
6891
6838
|
var repoRootPrefixes = [".codex/", ".github/", ".truthmark/", "docs/", "src/", "tests/"];
|
|
6892
6839
|
var normalizeReferencePath = (truthDocPath, referencePath) => {
|
|
6893
6840
|
const strippedPath = referencePath.split("#")[0]?.trim() ?? "";
|
|
@@ -6926,8 +6873,12 @@ var parseEvidenceReferences = async (rootDir, truthDocPath) => {
|
|
|
6926
6873
|
source: "frontmatter"
|
|
6927
6874
|
});
|
|
6928
6875
|
}
|
|
6929
|
-
for (const match of parsed.content.matchAll(
|
|
6930
|
-
const
|
|
6876
|
+
for (const match of parsed.content.matchAll(yamlFencePattern)) {
|
|
6877
|
+
const yamlBlock = match[1] ?? "";
|
|
6878
|
+
if (!topLevelEvidenceMarkerPattern.test(yamlBlock)) {
|
|
6879
|
+
continue;
|
|
6880
|
+
}
|
|
6881
|
+
const block = parse3(yamlBlock);
|
|
6931
6882
|
const rawEvidence = block && typeof block === "object" && "evidence" in block ? block.evidence : null;
|
|
6932
6883
|
if (!Array.isArray(rawEvidence)) {
|
|
6933
6884
|
continue;
|
|
@@ -6952,7 +6903,7 @@ var pathExists5 = async (filePath) => {
|
|
|
6952
6903
|
}
|
|
6953
6904
|
};
|
|
6954
6905
|
var diagnosticFor = (reference, message) => ({
|
|
6955
|
-
category: "
|
|
6906
|
+
category: "source-traceability",
|
|
6956
6907
|
severity: "error",
|
|
6957
6908
|
message,
|
|
6958
6909
|
file: reference.truthDocPath,
|
|
@@ -6961,12 +6912,21 @@ var diagnosticFor = (reference, message) => ({
|
|
|
6961
6912
|
source: reference.source
|
|
6962
6913
|
}
|
|
6963
6914
|
});
|
|
6915
|
+
var parseDiagnosticFor = (truthDocPath, error) => ({
|
|
6916
|
+
category: "source-traceability",
|
|
6917
|
+
severity: "error",
|
|
6918
|
+
message: `Malformed evidence YAML block in ${truthDocPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
6919
|
+
file: truthDocPath,
|
|
6920
|
+
data: {
|
|
6921
|
+
source: "evidence-block"
|
|
6922
|
+
}
|
|
6923
|
+
});
|
|
6964
6924
|
var isGlobReference = (referencePath) => /[*?[\]{}()]/u.test(referencePath);
|
|
6965
6925
|
var validateGlob = async (rootDir, reference) => {
|
|
6966
6926
|
if (reference.path.startsWith("../") || reference.path.startsWith("/")) {
|
|
6967
6927
|
return diagnosticFor(reference, `Referenced file pattern ${reference.path} must stay inside the repository root.`);
|
|
6968
6928
|
}
|
|
6969
|
-
const matches = await
|
|
6929
|
+
const matches = await fg7(reference.path, {
|
|
6970
6930
|
cwd: rootDir,
|
|
6971
6931
|
dot: true,
|
|
6972
6932
|
onlyFiles: true,
|
|
@@ -7044,7 +7004,13 @@ var validateReference = async (rootDir, reference) => {
|
|
|
7044
7004
|
var validateEvidenceReferences = async (rootDir, truthDocPaths) => {
|
|
7045
7005
|
const diagnostics = [];
|
|
7046
7006
|
for (const truthDocPath of [...truthDocPaths].sort()) {
|
|
7047
|
-
|
|
7007
|
+
let references;
|
|
7008
|
+
try {
|
|
7009
|
+
references = await parseEvidenceReferences(rootDir, truthDocPath);
|
|
7010
|
+
} catch (error) {
|
|
7011
|
+
diagnostics.push(parseDiagnosticFor(truthDocPath, error));
|
|
7012
|
+
continue;
|
|
7013
|
+
}
|
|
7048
7014
|
for (const reference of references) {
|
|
7049
7015
|
diagnostics.push(...await validateReference(rootDir, reference));
|
|
7050
7016
|
}
|
|
@@ -7052,25 +7018,135 @@ var validateEvidenceReferences = async (rootDir, truthDocPaths) => {
|
|
|
7052
7018
|
return diagnostics;
|
|
7053
7019
|
};
|
|
7054
7020
|
|
|
7055
|
-
// src/
|
|
7056
|
-
var
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7021
|
+
// src/checks/scorecard.ts
|
|
7022
|
+
var TRUTH_HEALTH_SCORECARD_SCHEMA_VERSION = "truthmark-scorecard/v0";
|
|
7023
|
+
var TRUTH_HEALTH_DIMENSION_IDS = [
|
|
7024
|
+
"routing-coverage",
|
|
7025
|
+
"ownership-clarity",
|
|
7026
|
+
"source-traceability",
|
|
7027
|
+
"branch-freshness",
|
|
7028
|
+
"generated-surface-freshness",
|
|
7029
|
+
"truth-doc-structure",
|
|
7030
|
+
"decision-rationale-preservation"
|
|
7031
|
+
];
|
|
7032
|
+
var EVIDENCE_LIMIT = 2;
|
|
7033
|
+
var textIncludesAny = (text, needles) => {
|
|
7034
|
+
const normalized = text.toLowerCase();
|
|
7035
|
+
return needles.some((needle) => normalized.includes(needle));
|
|
7036
|
+
};
|
|
7037
|
+
var diagnosticText = (diagnostic) => {
|
|
7038
|
+
const dataText = diagnostic.data ? JSON.stringify(diagnostic.data) : "";
|
|
7039
|
+
return `${diagnostic.message} ${diagnostic.file ?? ""} ${diagnostic.area ?? ""} ${dataText}`;
|
|
7040
|
+
};
|
|
7041
|
+
var isRouteAmbiguityDiagnostic = (diagnostic) => diagnostic.category === "context-pack" && textIncludesAny(diagnosticText(diagnostic), [
|
|
7042
|
+
"route",
|
|
7043
|
+
"routing",
|
|
7044
|
+
"ownership",
|
|
7045
|
+
"write boundary",
|
|
7046
|
+
"allowed write"
|
|
7047
|
+
]);
|
|
7048
|
+
var isSourceTraceabilityDiagnostic = (diagnostic) => diagnostic.category === "source-traceability" || diagnostic.category === "links" && textIncludesAny(diagnosticText(diagnostic), [
|
|
7049
|
+
"source_of_truth",
|
|
7050
|
+
"source of truth",
|
|
7051
|
+
"source evidence",
|
|
7052
|
+
"evidence"
|
|
7053
|
+
]);
|
|
7054
|
+
var isMarkdownShapeDiagnostic = (diagnostic) => diagnostic.category === "authority" && textIncludesAny(diagnosticText(diagnostic), ["markdown", "heading", "section"]);
|
|
7055
|
+
var isOwnershipFreshnessDiagnostic = (diagnostic) => diagnostic.category === "freshness" && textIncludesAny(diagnosticText(diagnostic), [
|
|
7056
|
+
"route",
|
|
7057
|
+
"routing",
|
|
7058
|
+
"ownership",
|
|
7059
|
+
"truth owner",
|
|
7060
|
+
"truth ownership",
|
|
7061
|
+
"affected truth document"
|
|
7062
|
+
]);
|
|
7063
|
+
var isDecisionRationaleDiagnostic = (diagnostic) => diagnostic.category === "doc-structure" && textIncludesAny(diagnosticText(diagnostic), ["product decisions", "rationale"]);
|
|
7064
|
+
var mapsToDimension = (diagnostic, dimensionId) => {
|
|
7065
|
+
switch (dimensionId) {
|
|
7066
|
+
case "routing-coverage":
|
|
7067
|
+
return ["config", "authority", "area-index", "coverage", "repo-index"].includes(
|
|
7068
|
+
diagnostic.category
|
|
7069
|
+
);
|
|
7070
|
+
case "ownership-clarity":
|
|
7071
|
+
return ["config", "area-index", "coverage", "impact"].includes(diagnostic.category) || isRouteAmbiguityDiagnostic(diagnostic) || isOwnershipFreshnessDiagnostic(diagnostic);
|
|
7072
|
+
case "source-traceability":
|
|
7073
|
+
return isSourceTraceabilityDiagnostic(diagnostic);
|
|
7074
|
+
case "branch-freshness":
|
|
7075
|
+
return diagnostic.category === "freshness";
|
|
7076
|
+
case "generated-surface-freshness":
|
|
7077
|
+
return diagnostic.category === "config" || diagnostic.category === "generated-surface";
|
|
7078
|
+
case "truth-doc-structure":
|
|
7079
|
+
return diagnostic.category === "config" || diagnostic.category === "frontmatter" || diagnostic.category === "links" || diagnostic.category === "doc-structure" || isMarkdownShapeDiagnostic(diagnostic);
|
|
7080
|
+
case "decision-rationale-preservation":
|
|
7081
|
+
return isDecisionRationaleDiagnostic(diagnostic);
|
|
7082
|
+
}
|
|
7083
|
+
};
|
|
7084
|
+
var checkerRanForDimension = (dimensionId, context) => {
|
|
7085
|
+
switch (dimensionId) {
|
|
7086
|
+
case "routing-coverage":
|
|
7087
|
+
return context.routingChecksRan ?? true;
|
|
7088
|
+
case "ownership-clarity":
|
|
7089
|
+
return context.ownershipChecksRan ?? true;
|
|
7090
|
+
case "source-traceability":
|
|
7091
|
+
return context.evidenceChecksRan ?? true;
|
|
7092
|
+
case "branch-freshness":
|
|
7093
|
+
return context.branchFreshnessRan;
|
|
7094
|
+
case "generated-surface-freshness":
|
|
7095
|
+
return context.generatedSurfaceChecksRan ?? true;
|
|
7096
|
+
case "truth-doc-structure":
|
|
7097
|
+
return context.truthDocStructureChecksRan ?? true;
|
|
7098
|
+
case "decision-rationale-preservation":
|
|
7099
|
+
return context.decisionRationaleChecksRan ?? true;
|
|
7100
|
+
}
|
|
7101
|
+
};
|
|
7102
|
+
var statusForDiagnostics = (diagnostics, diagnosticIndexes, checkerRan) => {
|
|
7103
|
+
if (diagnosticIndexes.length > 0) {
|
|
7104
|
+
return diagnosticIndexes.some((index) => diagnostics[index]?.severity === "error") ? "fail" : "warn";
|
|
7105
|
+
}
|
|
7106
|
+
return checkerRan ? "pass" : "not-run";
|
|
7107
|
+
};
|
|
7108
|
+
var evidenceForDiagnostics = (diagnostics, diagnosticIndexes, status, dimensionId, context) => {
|
|
7109
|
+
if (status === "pass") {
|
|
7110
|
+
return void 0;
|
|
7111
|
+
}
|
|
7112
|
+
if (diagnosticIndexes.length === 0) {
|
|
7113
|
+
if (dimensionId === "branch-freshness") {
|
|
7114
|
+
return [context.branchFreshnessNotRunReason ?? "base not supplied"];
|
|
7062
7115
|
}
|
|
7063
|
-
|
|
7064
|
-
...diagnostic,
|
|
7065
|
-
category: "freshness",
|
|
7066
|
-
message: diagnostic.message.replace("not mapped to a Truthmark route", "not routed to truth ownership")
|
|
7067
|
-
});
|
|
7116
|
+
return ["checker not run"];
|
|
7068
7117
|
}
|
|
7069
|
-
return {
|
|
7070
|
-
diagnostics
|
|
7071
|
-
|
|
7072
|
-
|
|
7118
|
+
return diagnosticIndexes.slice(0, EVIDENCE_LIMIT).map((index) => {
|
|
7119
|
+
const diagnostic = diagnostics[index];
|
|
7120
|
+
const subject = diagnostic.file ?? diagnostic.area;
|
|
7121
|
+
return subject ? `${diagnostic.category}:${subject}` : diagnostic.category;
|
|
7122
|
+
});
|
|
7073
7123
|
};
|
|
7124
|
+
var buildTruthHealthScorecard = (diagnostics, context) => ({
|
|
7125
|
+
schemaVersion: TRUTH_HEALTH_SCORECARD_SCHEMA_VERSION,
|
|
7126
|
+
dimensions: TRUTH_HEALTH_DIMENSION_IDS.map((dimensionId) => {
|
|
7127
|
+
const diagnosticIndexes = diagnostics.flatMap(
|
|
7128
|
+
(diagnostic, index) => mapsToDimension(diagnostic, dimensionId) ? [index] : []
|
|
7129
|
+
);
|
|
7130
|
+
const status = statusForDiagnostics(
|
|
7131
|
+
diagnostics,
|
|
7132
|
+
diagnosticIndexes,
|
|
7133
|
+
checkerRanForDimension(dimensionId, context)
|
|
7134
|
+
);
|
|
7135
|
+
const evidence = evidenceForDiagnostics(
|
|
7136
|
+
diagnostics,
|
|
7137
|
+
diagnosticIndexes,
|
|
7138
|
+
status,
|
|
7139
|
+
dimensionId,
|
|
7140
|
+
context
|
|
7141
|
+
);
|
|
7142
|
+
return {
|
|
7143
|
+
id: dimensionId,
|
|
7144
|
+
status,
|
|
7145
|
+
diagnosticIndexes,
|
|
7146
|
+
...evidence ? { evidence } : {}
|
|
7147
|
+
};
|
|
7148
|
+
})
|
|
7149
|
+
});
|
|
7074
7150
|
|
|
7075
7151
|
// src/checks/check.ts
|
|
7076
7152
|
var summarizeDiagnostics = (diagnostics) => {
|
|
@@ -7087,12 +7163,23 @@ var runCheck = async (cwd, options = {}) => {
|
|
|
7087
7163
|
const branchScope = await getBranchScopeData(rootDir);
|
|
7088
7164
|
const loadResult = await loadConfig(rootDir);
|
|
7089
7165
|
if (!loadResult.config) {
|
|
7166
|
+
const scorecard2 = buildTruthHealthScorecard(loadResult.diagnostics, {
|
|
7167
|
+
branchFreshnessRan: false,
|
|
7168
|
+
branchFreshnessNotRunReason: options.base ? "config unavailable" : "base not supplied",
|
|
7169
|
+
routingChecksRan: false,
|
|
7170
|
+
ownershipChecksRan: false,
|
|
7171
|
+
evidenceChecksRan: false,
|
|
7172
|
+
generatedSurfaceChecksRan: false,
|
|
7173
|
+
truthDocStructureChecksRan: false,
|
|
7174
|
+
decisionRationaleChecksRan: false
|
|
7175
|
+
});
|
|
7090
7176
|
return {
|
|
7091
7177
|
command: "check",
|
|
7092
7178
|
summary: summarizeDiagnostics(loadResult.diagnostics),
|
|
7093
7179
|
diagnostics: loadResult.diagnostics,
|
|
7094
7180
|
data: {
|
|
7095
|
-
branchScope
|
|
7181
|
+
branchScope,
|
|
7182
|
+
scorecard: scorecard2
|
|
7096
7183
|
}
|
|
7097
7184
|
};
|
|
7098
7185
|
}
|
|
@@ -7113,6 +7200,7 @@ var runCheck = async (cwd, options = {}) => {
|
|
|
7113
7200
|
areas.truthDocumentEntries
|
|
7114
7201
|
);
|
|
7115
7202
|
const generatedSurfaces = await checkGeneratedSurfaces(rootDir, loadResult.config);
|
|
7203
|
+
const sourceTraceability = await validateEvidenceReferences(rootDir, areas.truthDocumentPaths);
|
|
7116
7204
|
const freshness = options.base ? await checkFreshness(rootDir, loadResult.config, areas.truthDocumentPaths, options.base) : null;
|
|
7117
7205
|
const diagnostics = [
|
|
7118
7206
|
...loadResult.diagnostics,
|
|
@@ -7122,6 +7210,7 @@ var runCheck = async (cwd, options = {}) => {
|
|
|
7122
7210
|
...areas.diagnostics,
|
|
7123
7211
|
...decisionSections,
|
|
7124
7212
|
...generatedSurfaces,
|
|
7213
|
+
...sourceTraceability,
|
|
7125
7214
|
...freshness?.diagnostics ?? []
|
|
7126
7215
|
];
|
|
7127
7216
|
const truthVisibility = {
|
|
@@ -7136,6 +7225,11 @@ var runCheck = async (cwd, options = {}) => {
|
|
|
7136
7225
|
topologyPressureCount: areas.topologyPressureCount,
|
|
7137
7226
|
freshnessDiagnosticCount: freshness?.diagnostics.length ?? 0
|
|
7138
7227
|
};
|
|
7228
|
+
const scorecard = buildTruthHealthScorecard(diagnostics, {
|
|
7229
|
+
branchFreshnessRan: Boolean(freshness),
|
|
7230
|
+
branchFreshnessNotRunReason: options.base ? "freshness checker skipped" : "base not supplied",
|
|
7231
|
+
evidenceChecksRan: true
|
|
7232
|
+
});
|
|
7139
7233
|
return {
|
|
7140
7234
|
command: "check",
|
|
7141
7235
|
summary: summarizeDiagnostics(diagnostics),
|
|
@@ -7143,6 +7237,7 @@ var runCheck = async (cwd, options = {}) => {
|
|
|
7143
7237
|
data: {
|
|
7144
7238
|
branchScope,
|
|
7145
7239
|
truthVisibility,
|
|
7240
|
+
scorecard,
|
|
7146
7241
|
...freshness ? { impactSet: freshness.impactSet } : {}
|
|
7147
7242
|
}
|
|
7148
7243
|
};
|
|
@@ -7151,7 +7246,7 @@ var runCheck = async (cwd, options = {}) => {
|
|
|
7151
7246
|
// src/context-pack/build.ts
|
|
7152
7247
|
import fs22 from "fs/promises";
|
|
7153
7248
|
import path12 from "path";
|
|
7154
|
-
import
|
|
7249
|
+
import fg8 from "fast-glob";
|
|
7155
7250
|
var uniqueSorted2 = (values) => [...new Set(values)].sort();
|
|
7156
7251
|
var repoRootPrefixes2 = [".codex/", ".github/", ".truthmark/", "docs/", "src/", "tests/"];
|
|
7157
7252
|
var isGlobReference2 = (referencePath) => /[*?[\]{}()]/u.test(referencePath);
|
|
@@ -7171,9 +7266,19 @@ var readIfExists = async (rootDir, filePath) => {
|
|
|
7171
7266
|
return null;
|
|
7172
7267
|
}
|
|
7173
7268
|
};
|
|
7174
|
-
var
|
|
7269
|
+
var boundContent = (content) => {
|
|
7175
7270
|
const lines = content.split("\n");
|
|
7176
7271
|
if (lines.length <= 200) {
|
|
7272
|
+
return { content, truncated: false };
|
|
7273
|
+
}
|
|
7274
|
+
return {
|
|
7275
|
+
content: [...lines.slice(0, 80), "...", ...lines.slice(-40)].join("\n"),
|
|
7276
|
+
truncated: true
|
|
7277
|
+
};
|
|
7278
|
+
};
|
|
7279
|
+
var boundedContent = (filePath, content, warnings) => {
|
|
7280
|
+
const bounded = boundContent(content);
|
|
7281
|
+
if (!bounded.truncated) {
|
|
7177
7282
|
return { path: filePath, content, truncated: false };
|
|
7178
7283
|
}
|
|
7179
7284
|
warnings.push({
|
|
@@ -7184,16 +7289,25 @@ var boundedContent = (filePath, content, warnings) => {
|
|
|
7184
7289
|
});
|
|
7185
7290
|
return {
|
|
7186
7291
|
path: filePath,
|
|
7187
|
-
content:
|
|
7292
|
+
content: bounded.content,
|
|
7188
7293
|
truncated: true
|
|
7189
7294
|
};
|
|
7190
7295
|
};
|
|
7191
|
-
var documentsFor = async (rootDir, paths) => {
|
|
7296
|
+
var documentsFor = async (rootDir, paths, warnings) => {
|
|
7192
7297
|
const documents = [];
|
|
7193
7298
|
for (const filePath of uniqueSorted2(paths)) {
|
|
7194
7299
|
const content = await readIfExists(rootDir, filePath);
|
|
7195
7300
|
if (content !== null) {
|
|
7196
|
-
|
|
7301
|
+
const bounded = boundContent(content);
|
|
7302
|
+
if (bounded.truncated) {
|
|
7303
|
+
warnings.push({
|
|
7304
|
+
category: "context-pack",
|
|
7305
|
+
severity: "review",
|
|
7306
|
+
message: `Context truth doc ${filePath} was truncated to fit ContextPack v0 bounds.`,
|
|
7307
|
+
file: filePath
|
|
7308
|
+
});
|
|
7309
|
+
}
|
|
7310
|
+
documents.push({ path: filePath, content: bounded.content, truncated: bounded.truncated });
|
|
7197
7311
|
}
|
|
7198
7312
|
}
|
|
7199
7313
|
return documents;
|
|
@@ -7222,7 +7336,7 @@ var sourceOfTruthPathsFor = async (rootDir, docs, truthDocPaths) => {
|
|
|
7222
7336
|
}
|
|
7223
7337
|
if (isGlobReference2(normalizedPath)) {
|
|
7224
7338
|
sourcePaths.push(
|
|
7225
|
-
...await
|
|
7339
|
+
...await fg8(normalizedPath, {
|
|
7226
7340
|
cwd: rootDir,
|
|
7227
7341
|
dot: true,
|
|
7228
7342
|
onlyFiles: true,
|
|
@@ -7236,12 +7350,12 @@ var sourceOfTruthPathsFor = async (rootDir, docs, truthDocPaths) => {
|
|
|
7236
7350
|
}
|
|
7237
7351
|
return uniqueSorted2(sourcePaths);
|
|
7238
7352
|
};
|
|
7239
|
-
var writePathsFor = (workflow, truthDocs, routes) => {
|
|
7353
|
+
var writePathsFor = (workflow, routeIndexPath, truthDocs, routes) => {
|
|
7240
7354
|
if (workflow === "truth-sync") {
|
|
7241
|
-
return uniqueSorted2([
|
|
7355
|
+
return uniqueSorted2([routeIndexPath, ...truthDocs]);
|
|
7242
7356
|
}
|
|
7243
7357
|
if (workflow === "truth-document") {
|
|
7244
|
-
return uniqueSorted2([
|
|
7358
|
+
return uniqueSorted2([routeIndexPath, ...truthDocs]);
|
|
7245
7359
|
}
|
|
7246
7360
|
return uniqueSorted2(routes.flatMap((route) => route.codeSurface));
|
|
7247
7361
|
};
|
|
@@ -7251,9 +7365,11 @@ var testCommandsFor = (affectedTests) => {
|
|
|
7251
7365
|
var buildContextPack = async (cwd, options) => {
|
|
7252
7366
|
const repoIndex = await buildRepoIndex(cwd);
|
|
7253
7367
|
const rootDir = repoIndex.repository.root;
|
|
7368
|
+
const loadResult = await loadConfig(rootDir);
|
|
7369
|
+
const config = loadResult.config ?? (loadResult.status === "missing" ? createDefaultConfig() : null);
|
|
7254
7370
|
const impactSet = options.base ? await buildImpactSet(rootDir, { base: options.base }) : null;
|
|
7255
7371
|
const routeMap = impactSet ? repoIndex.routeMap : repoIndex.routeMap;
|
|
7256
|
-
const warnings = [];
|
|
7372
|
+
const warnings = loadResult.status === "invalid" ? [...loadResult.diagnostics] : [];
|
|
7257
7373
|
const truthDocPaths = impactSet?.affectedTruthDocs ?? (options.workflow === "truth-realize" ? [] : routeMap.routes.flatMap((route) => route.truthDocs));
|
|
7258
7374
|
const contextRoutes = impactSet?.affectedRoutes ?? (options.workflow === "truth-realize" ? [] : routeMap.routes);
|
|
7259
7375
|
if (options.workflow === "truth-realize" && !impactSet) {
|
|
@@ -7274,8 +7390,13 @@ var buildContextPack = async (cwd, options) => {
|
|
|
7274
7390
|
base: options.base ?? null,
|
|
7275
7391
|
impactSet,
|
|
7276
7392
|
routeMap,
|
|
7277
|
-
allowedWritePaths: writePathsFor(
|
|
7278
|
-
|
|
7393
|
+
allowedWritePaths: config === null ? [] : writePathsFor(
|
|
7394
|
+
options.workflow,
|
|
7395
|
+
config.truthmark.paths.routesIndex,
|
|
7396
|
+
truthDocPaths,
|
|
7397
|
+
contextRoutes
|
|
7398
|
+
),
|
|
7399
|
+
truthDocs: await documentsFor(rootDir, truthDocPaths, warnings),
|
|
7279
7400
|
sourceFiles: await sourceFilesFor(rootDir, sourceFilePaths, warnings),
|
|
7280
7401
|
testCommands: testCommandsFor(impactSet?.affectedTests ?? []),
|
|
7281
7402
|
warnings
|
|
@@ -7294,7 +7415,7 @@ var renderContextPackMarkdown = (pack) => {
|
|
|
7294
7415
|
...pack.allowedWritePaths.map((filePath) => `- ${filePath}`),
|
|
7295
7416
|
"",
|
|
7296
7417
|
"## Truth Docs",
|
|
7297
|
-
...pack.truthDocs.map((doc) => `- ${doc.path}`),
|
|
7418
|
+
...pack.truthDocs.map((doc) => `- ${doc.path}${doc.truncated ? " (truncated)" : ""}`),
|
|
7298
7419
|
"",
|
|
7299
7420
|
"## Source Files",
|
|
7300
7421
|
...pack.sourceFiles.map((file) => `- ${file.path}${file.truncated ? " (truncated)" : ""}`),
|
|
@@ -7306,6 +7427,198 @@ var renderContextPackMarkdown = (pack) => {
|
|
|
7306
7427
|
`;
|
|
7307
7428
|
};
|
|
7308
7429
|
|
|
7430
|
+
// src/workflow-state/build.ts
|
|
7431
|
+
import { execa as execa5 } from "execa";
|
|
7432
|
+
|
|
7433
|
+
// src/workflow-state/action-context.ts
|
|
7434
|
+
var uniqueSorted3 = (values) => [...new Set(values.filter((value) => value.length > 0))].sort();
|
|
7435
|
+
var helperCommandsFor = (manifestEntry) => (manifestEntry.helpers ?? []).map((helper) => ({
|
|
7436
|
+
id: helper.id,
|
|
7437
|
+
runner: helper.runner,
|
|
7438
|
+
argv: [...helper.command.argv],
|
|
7439
|
+
optional: helper.optional
|
|
7440
|
+
}));
|
|
7441
|
+
var evidenceFor = (manifestEntry) => manifestEntry.requiredGates.filter((gate) => /evidence|ownership|containment/iu.test(gate));
|
|
7442
|
+
var baseContext = (manifestEntry, mode, allowedWritePaths, forbiddenWritePaths, writeLeaseRequired) => ({
|
|
7443
|
+
mode,
|
|
7444
|
+
allowedWritePaths: uniqueSorted3(allowedWritePaths),
|
|
7445
|
+
forbiddenWritePaths: uniqueSorted3(forbiddenWritePaths),
|
|
7446
|
+
stopConditions: [...manifestEntry.negativeTriggers, ...manifestEntry.forbiddenAdjacency],
|
|
7447
|
+
requiredEvidence: evidenceFor(manifestEntry),
|
|
7448
|
+
helperValidationCommands: helperCommandsFor(manifestEntry),
|
|
7449
|
+
writeLeaseRequired
|
|
7450
|
+
});
|
|
7451
|
+
var buildWorkflowActionContext = (manifestEntry, data = {}) => {
|
|
7452
|
+
if (manifestEntry.id === "truthmark-preview" || manifestEntry.id === "truthmark-check") {
|
|
7453
|
+
return baseContext(manifestEntry, "read-only", [], [], false);
|
|
7454
|
+
}
|
|
7455
|
+
if (manifestEntry.id === "truthmark-sync" || manifestEntry.id === "truthmark-document") {
|
|
7456
|
+
return baseContext(
|
|
7457
|
+
manifestEntry,
|
|
7458
|
+
"truth-doc-write",
|
|
7459
|
+
[...data.routeIndexPath ? [data.routeIndexPath] : [], ...data.routeFiles ?? [], ...data.truthDocs ?? []],
|
|
7460
|
+
[],
|
|
7461
|
+
true
|
|
7462
|
+
);
|
|
7463
|
+
}
|
|
7464
|
+
if (manifestEntry.id === "truthmark-structure") {
|
|
7465
|
+
return baseContext(
|
|
7466
|
+
manifestEntry,
|
|
7467
|
+
"route-write",
|
|
7468
|
+
[
|
|
7469
|
+
...data.routeIndexPath ? [data.routeIndexPath] : [],
|
|
7470
|
+
...data.routeFiles ?? [],
|
|
7471
|
+
...data.starterTruthDocs ?? []
|
|
7472
|
+
],
|
|
7473
|
+
[],
|
|
7474
|
+
true
|
|
7475
|
+
);
|
|
7476
|
+
}
|
|
7477
|
+
if (manifestEntry.id === "truthmark-realize") {
|
|
7478
|
+
return baseContext(
|
|
7479
|
+
manifestEntry,
|
|
7480
|
+
"code-write",
|
|
7481
|
+
data.codeWritePaths ?? [],
|
|
7482
|
+
[
|
|
7483
|
+
...data.routeIndexPath ? [data.routeIndexPath] : [],
|
|
7484
|
+
...data.routeFiles ?? [],
|
|
7485
|
+
...data.truthRoot ? [`${data.truthRoot}/**/*.md`] : [],
|
|
7486
|
+
...data.truthDocs ?? []
|
|
7487
|
+
],
|
|
7488
|
+
false
|
|
7489
|
+
);
|
|
7490
|
+
}
|
|
7491
|
+
return baseContext(
|
|
7492
|
+
manifestEntry,
|
|
7493
|
+
"portal-write",
|
|
7494
|
+
data.portalEnabled && data.portalOutputPath ? [`${data.portalOutputPath}/**`] : [],
|
|
7495
|
+
[],
|
|
7496
|
+
false
|
|
7497
|
+
);
|
|
7498
|
+
};
|
|
7499
|
+
|
|
7500
|
+
// src/workflow-state/build.ts
|
|
7501
|
+
var helperCommandsFor2 = (workflow) => (TRUTHMARK_WORKFLOW_MANIFEST[workflow].helpers ?? []).map((helper) => ({
|
|
7502
|
+
id: helper.id,
|
|
7503
|
+
runner: helper.runner,
|
|
7504
|
+
argv: [...helper.command.argv],
|
|
7505
|
+
optional: helper.optional
|
|
7506
|
+
}));
|
|
7507
|
+
var uniqueSorted4 = (values) => [...new Set(values.filter((value) => value.length > 0))].sort();
|
|
7508
|
+
var isWriteCapable = (workflow) => !["truthmark-preview", "truthmark-check"].includes(workflow);
|
|
7509
|
+
var DEFAULT_BASE_CANDIDATES = ["@{upstream}", "origin/main", "main", "origin/master", "master"];
|
|
7510
|
+
var selectComparisonBase = async (rootDir, suppliedBase) => {
|
|
7511
|
+
if (suppliedBase) {
|
|
7512
|
+
return suppliedBase;
|
|
7513
|
+
}
|
|
7514
|
+
for (const candidate of DEFAULT_BASE_CANDIDATES) {
|
|
7515
|
+
const result = await execa5("git", ["rev-parse", "--verify", `${candidate}^{commit}`], {
|
|
7516
|
+
cwd: rootDir,
|
|
7517
|
+
reject: false
|
|
7518
|
+
});
|
|
7519
|
+
if ((result.exitCode ?? 1) === 0) {
|
|
7520
|
+
return candidate;
|
|
7521
|
+
}
|
|
7522
|
+
}
|
|
7523
|
+
return null;
|
|
7524
|
+
};
|
|
7525
|
+
var routeFilesFor = (repoIndex) => uniqueSorted4(repoIndex.routeMap.routes.map((route) => route.sourcePath));
|
|
7526
|
+
var hasUnmappedFunctionalChange = (impactSet) => impactSet?.diagnostics.some(
|
|
7527
|
+
(diagnostic) => diagnostic.category === "impact" && /not mapped to a Truthmark route|no affected truth document/u.test(diagnostic.message)
|
|
7528
|
+
) ?? false;
|
|
7529
|
+
var applicabilityFor = (workflow, diagnostics, impactSet) => {
|
|
7530
|
+
const reasons = [];
|
|
7531
|
+
if (diagnostics.some((diagnostic) => diagnostic.message.includes("Missing .truthmark/config.yml"))) {
|
|
7532
|
+
reasons.push("Missing .truthmark/config.yml.");
|
|
7533
|
+
return { state: isWriteCapable(workflow) ? "blocked" : "not_applicable", reasons };
|
|
7534
|
+
}
|
|
7535
|
+
if (workflow === "truthmark-sync" && !impactSet) {
|
|
7536
|
+
reasons.push("truthmark-sync requires --base to derive bounded truth-doc write paths.");
|
|
7537
|
+
return { state: "blocked", reasons };
|
|
7538
|
+
}
|
|
7539
|
+
if (workflow === "truthmark-realize" && !impactSet) {
|
|
7540
|
+
reasons.push("truthmark-realize requires --base to derive bounded allowed write paths.");
|
|
7541
|
+
return { state: "blocked", reasons };
|
|
7542
|
+
}
|
|
7543
|
+
if (hasUnmappedFunctionalChange(impactSet)) {
|
|
7544
|
+
reasons.push("Changed functional files have ambiguous or missing Truthmark route ownership.");
|
|
7545
|
+
return { state: "ambiguous", reasons };
|
|
7546
|
+
}
|
|
7547
|
+
if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
7548
|
+
reasons.push("Existing diagnostics contain errors that block safe workflow execution.");
|
|
7549
|
+
return { state: "blocked", reasons };
|
|
7550
|
+
}
|
|
7551
|
+
return { state: "applicable", reasons };
|
|
7552
|
+
};
|
|
7553
|
+
var contextDataFor = (workflow, repoIndex, config, impactSet) => {
|
|
7554
|
+
if (!config) {
|
|
7555
|
+
return {};
|
|
7556
|
+
}
|
|
7557
|
+
const routeFiles = routeFilesFor(repoIndex);
|
|
7558
|
+
const truthDocs = uniqueSorted4(
|
|
7559
|
+
impactSet?.affectedTruthDocs ?? repoIndex.routeMap.routes.flatMap((route) => route.truthDocs)
|
|
7560
|
+
);
|
|
7561
|
+
return {
|
|
7562
|
+
routeIndexPath: config.truthmark.paths.routesIndex,
|
|
7563
|
+
routeFiles,
|
|
7564
|
+
truthRoot: config.truthmark.paths.truthRoot,
|
|
7565
|
+
truthDocs,
|
|
7566
|
+
starterTruthDocs: workflow === "truthmark-structure" ? truthDocs : [],
|
|
7567
|
+
codeWritePaths: workflow === "truthmark-realize" ? uniqueSorted4(impactSet?.affectedRoutes.flatMap((route) => route.codeSurface) ?? []) : [],
|
|
7568
|
+
portalEnabled: config.truthmark.generated.portal.enabled,
|
|
7569
|
+
portalOutputPath: config.truthmark.paths.portalOutput,
|
|
7570
|
+
routes: repoIndex.routeMap.routes
|
|
7571
|
+
};
|
|
7572
|
+
};
|
|
7573
|
+
var nextStepsFor = (workflow, applicability, comparisonBase) => {
|
|
7574
|
+
if (applicability.state === "ambiguous") {
|
|
7575
|
+
return ["Run Truth Structure or repair route ownership before writing truth docs."];
|
|
7576
|
+
}
|
|
7577
|
+
if ((workflow === "truthmark-sync" || workflow === "truthmark-realize") && applicability.state === "blocked" && !comparisonBase) {
|
|
7578
|
+
return [
|
|
7579
|
+
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."
|
|
7580
|
+
];
|
|
7581
|
+
}
|
|
7582
|
+
return [];
|
|
7583
|
+
};
|
|
7584
|
+
var buildWorkflowState = async (cwd, options) => {
|
|
7585
|
+
const manifestEntry = TRUTHMARK_WORKFLOW_MANIFEST[options.workflow];
|
|
7586
|
+
if (!manifestEntry) {
|
|
7587
|
+
throw new Error(`Unknown Truthmark workflow: ${String(options.workflow)}`);
|
|
7588
|
+
}
|
|
7589
|
+
const repoIndex = await buildRepoIndex(cwd);
|
|
7590
|
+
const rootDir = repoIndex.repository.root;
|
|
7591
|
+
const loadResult = await loadConfig(rootDir);
|
|
7592
|
+
const comparisonBase = options.base ? options.base : options.workflow === "truthmark-sync" ? await selectComparisonBase(rootDir) : null;
|
|
7593
|
+
const impactSet = comparisonBase ? await buildImpactSet(rootDir, { base: comparisonBase }) : null;
|
|
7594
|
+
const checkResult = await runCheck(cwd, comparisonBase ? { base: comparisonBase } : {});
|
|
7595
|
+
const diagnostics = [
|
|
7596
|
+
...loadResult.diagnostics,
|
|
7597
|
+
...repoIndex.diagnostics,
|
|
7598
|
+
...impactSet?.diagnostics ?? [],
|
|
7599
|
+
...checkResult.diagnostics
|
|
7600
|
+
];
|
|
7601
|
+
const applicability = applicabilityFor(options.workflow, diagnostics, impactSet);
|
|
7602
|
+
const actionData = applicability.state === "blocked" || applicability.state === "ambiguous" ? {} : contextDataFor(options.workflow, repoIndex, loadResult.config, impactSet);
|
|
7603
|
+
return {
|
|
7604
|
+
schemaVersion: "truthmark-workflow/v0",
|
|
7605
|
+
workflow: options.workflow,
|
|
7606
|
+
applicability,
|
|
7607
|
+
actionContext: buildWorkflowActionContext(manifestEntry, actionData),
|
|
7608
|
+
changedFiles: impactSet?.changedFiles ?? [],
|
|
7609
|
+
affectedRoutes: impactSet?.affectedRoutes ?? [],
|
|
7610
|
+
targetTruthDocs: applicability.state === "ambiguous" ? [] : impactSet?.affectedTruthDocs ?? [],
|
|
7611
|
+
diagnostics,
|
|
7612
|
+
checks: {
|
|
7613
|
+
required: [...manifestEntry.requiredGates],
|
|
7614
|
+
recommended: [...manifestEntry.positiveTriggers],
|
|
7615
|
+
helpers: helperCommandsFor2(options.workflow)
|
|
7616
|
+
},
|
|
7617
|
+
nextSteps: nextStepsFor(options.workflow, applicability, comparisonBase),
|
|
7618
|
+
reportSections: [...manifestEntry.reportSections]
|
|
7619
|
+
};
|
|
7620
|
+
};
|
|
7621
|
+
|
|
7309
7622
|
// src/cli/handlers.ts
|
|
7310
7623
|
import fs23 from "fs/promises";
|
|
7311
7624
|
|
|
@@ -7672,9 +7985,26 @@ var runImpact = async (options) => {
|
|
|
7672
7985
|
var isContextPackWorkflow = (value) => {
|
|
7673
7986
|
return value === "truth-sync" || value === "truth-document" || value === "truth-realize";
|
|
7674
7987
|
};
|
|
7675
|
-
var
|
|
7676
|
-
return value === void 0 || value === "
|
|
7988
|
+
var isContextMarkdownFormat = (value) => {
|
|
7989
|
+
return value === void 0 || value === "markdown";
|
|
7990
|
+
};
|
|
7991
|
+
var isTruthmarkWorkflowId = (value) => {
|
|
7992
|
+
return typeof value === "string" && TRUTHMARK_WORKFLOW_IDS.includes(value);
|
|
7677
7993
|
};
|
|
7994
|
+
var invalidWorkflowResult = (command, workflow) => ({
|
|
7995
|
+
command,
|
|
7996
|
+
summary: workflow ? `Truthmark workflow requires a supported full workflow ID; received ${workflow}.` : "Truthmark workflow requires --workflow.",
|
|
7997
|
+
diagnostics: [
|
|
7998
|
+
{
|
|
7999
|
+
category: "workflow-state",
|
|
8000
|
+
severity: "error",
|
|
8001
|
+
message: workflow ? `Unknown Truthmark workflow: ${workflow}. Use a canonical full manifest ID such as truthmark-sync or truthmark-check.` : `truthmark ${command} requires --workflow <workflow>.`
|
|
8002
|
+
}
|
|
8003
|
+
],
|
|
8004
|
+
data: {
|
|
8005
|
+
request: workflow ? { workflow } : {}
|
|
8006
|
+
}
|
|
8007
|
+
});
|
|
7678
8008
|
var readHelperFile = async (filePath, helper) => {
|
|
7679
8009
|
try {
|
|
7680
8010
|
return await fs23.readFile(filePath, "utf8");
|
|
@@ -7716,7 +8046,20 @@ var runContext = async (options) => {
|
|
|
7716
8046
|
]
|
|
7717
8047
|
};
|
|
7718
8048
|
}
|
|
7719
|
-
if (
|
|
8049
|
+
if (options.format === "json") {
|
|
8050
|
+
return {
|
|
8051
|
+
command: "context",
|
|
8052
|
+
summary: "Truthmark context no longer supports JSON ContextPack output.",
|
|
8053
|
+
diagnostics: [
|
|
8054
|
+
{
|
|
8055
|
+
category: "context-pack",
|
|
8056
|
+
severity: "error",
|
|
8057
|
+
message: "JSON ContextPack output was removed in v2; use --format markdown."
|
|
8058
|
+
}
|
|
8059
|
+
]
|
|
8060
|
+
};
|
|
8061
|
+
}
|
|
8062
|
+
if (!isContextMarkdownFormat(options.format)) {
|
|
7720
8063
|
return {
|
|
7721
8064
|
command: "context",
|
|
7722
8065
|
summary: "Truthmark context requires a supported --format value.",
|
|
@@ -7724,7 +8067,7 @@ var runContext = async (options) => {
|
|
|
7724
8067
|
{
|
|
7725
8068
|
category: "context-pack",
|
|
7726
8069
|
severity: "error",
|
|
7727
|
-
message: "truthmark context
|
|
8070
|
+
message: "truthmark context supports only --format markdown; JSON ContextPack output was removed in v2."
|
|
7728
8071
|
}
|
|
7729
8072
|
]
|
|
7730
8073
|
};
|
|
@@ -7734,13 +8077,36 @@ var runContext = async (options) => {
|
|
|
7734
8077
|
base: options.base
|
|
7735
8078
|
});
|
|
7736
8079
|
const diagnostics = contextPack.warnings;
|
|
8080
|
+
const summary = `Truthmark context generated ${contextPack.workflow} ContextPack with ${diagnostics.length} warnings.`;
|
|
8081
|
+
const markdown = renderContextPackMarkdown(contextPack);
|
|
7737
8082
|
return {
|
|
7738
8083
|
command: "context",
|
|
7739
|
-
summary
|
|
8084
|
+
summary,
|
|
7740
8085
|
diagnostics,
|
|
7741
8086
|
data: {
|
|
7742
|
-
|
|
7743
|
-
|
|
8087
|
+
markdown,
|
|
8088
|
+
summary
|
|
8089
|
+
}
|
|
8090
|
+
};
|
|
8091
|
+
};
|
|
8092
|
+
var runWorkflowStatus = async (options) => {
|
|
8093
|
+
if (!isTruthmarkWorkflowId(options.workflow)) {
|
|
8094
|
+
return invalidWorkflowResult("workflow status", options.workflow);
|
|
8095
|
+
}
|
|
8096
|
+
const workflowState = await buildWorkflowState(process.cwd(), {
|
|
8097
|
+
workflow: options.workflow,
|
|
8098
|
+
...options.base ? { base: options.base } : {}
|
|
8099
|
+
});
|
|
8100
|
+
return {
|
|
8101
|
+
command: "workflow status",
|
|
8102
|
+
summary: `Truthmark workflow status completed for ${options.workflow}.`,
|
|
8103
|
+
diagnostics: workflowState.diagnostics,
|
|
8104
|
+
data: {
|
|
8105
|
+
request: {
|
|
8106
|
+
workflow: options.workflow,
|
|
8107
|
+
...options.base ? { base: options.base } : {}
|
|
8108
|
+
},
|
|
8109
|
+
workflowState
|
|
7744
8110
|
}
|
|
7745
8111
|
};
|
|
7746
8112
|
};
|
|
@@ -7758,7 +8124,7 @@ var writeResult = (result, options) => {
|
|
|
7758
8124
|
markFailedWhenErrorDiagnosticsExist(result);
|
|
7759
8125
|
};
|
|
7760
8126
|
var writeContextResult = (result, options) => {
|
|
7761
|
-
if (!options.json &&
|
|
8127
|
+
if (!options.json && typeof result.data?.markdown === "string") {
|
|
7762
8128
|
process.stdout.write(result.data.markdown);
|
|
7763
8129
|
markFailedWhenErrorDiagnosticsExist(result);
|
|
7764
8130
|
return;
|
|
@@ -7819,7 +8185,7 @@ var buildProgram = () => {
|
|
|
7819
8185
|
writeResult(await runImpact({ base: options.base }), options);
|
|
7820
8186
|
});
|
|
7821
8187
|
addJsonOption(
|
|
7822
|
-
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:
|
|
8188
|
+
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")
|
|
7823
8189
|
).action(async (options) => {
|
|
7824
8190
|
writeContextResult(
|
|
7825
8191
|
await runContext({
|
|
@@ -7830,6 +8196,18 @@ var buildProgram = () => {
|
|
|
7830
8196
|
options
|
|
7831
8197
|
);
|
|
7832
8198
|
});
|
|
8199
|
+
const workflow = program.command("workflow").description("Inspect agent-facing Truthmark workflow state.");
|
|
8200
|
+
addJsonOption(
|
|
8201
|
+
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")
|
|
8202
|
+
).action(async (options) => {
|
|
8203
|
+
writeResult(
|
|
8204
|
+
await runWorkflowStatus({
|
|
8205
|
+
workflow: options.workflow,
|
|
8206
|
+
base: options.base
|
|
8207
|
+
}),
|
|
8208
|
+
options
|
|
8209
|
+
);
|
|
8210
|
+
});
|
|
7833
8211
|
const validate = program.command("validate").description("Run optional Truthmark workflow helper validators from the installed CLI.");
|
|
7834
8212
|
addJsonOption(
|
|
7835
8213
|
validate.command("sync-report").description("Validate a Truth Sync report file.").argument("<report-file>", "Truth Sync report file")
|