truthmark 2.2.0 → 2.2.2
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 +16 -16
- package/README.es.md +16 -16
- package/README.md +17 -17
- package/README.ru.md +17 -17
- package/README.zh.md +16 -16
- package/dist/main.js +581 -807
- package/dist/main.js.map +1 -1
- package/package.json +1 -2
package/dist/main.js
CHANGED
|
@@ -618,14 +618,14 @@ var parseTruthDocumentsFromYaml = (sectionLines, areaName, options) => {
|
|
|
618
618
|
const diagnostics = [];
|
|
619
619
|
const truthDocumentEntries = [];
|
|
620
620
|
for (const rawEntry of rawEntries) {
|
|
621
|
-
const
|
|
621
|
+
const path12 = rawEntry && typeof rawEntry === "object" && "path" in rawEntry ? rawEntry.path : null;
|
|
622
622
|
const kind = rawEntry && typeof rawEntry === "object" && "kind" in rawEntry ? rawEntry.kind : null;
|
|
623
623
|
const lane = rawEntry && typeof rawEntry === "object" && "lane" in rawEntry ? rawEntry.lane : null;
|
|
624
|
-
const inferredKind = typeof
|
|
625
|
-
const inferredLane = typeof
|
|
624
|
+
const inferredKind = typeof path12 === "string" ? inferTruthDocumentKindFromPath(path12, options) : null;
|
|
625
|
+
const inferredLane = typeof path12 === "string" ? inferTruthDocumentLaneFromPath(path12, options) : null;
|
|
626
626
|
const normalizedKind = isTruthDocumentKind(kind) ? kind : inferredKind;
|
|
627
627
|
const normalizedLane = lane === "product" || lane === "engineering" ? lane : normalizedKind ? laneForTruthDocumentKind(normalizedKind) : inferredLane;
|
|
628
|
-
if (typeof
|
|
628
|
+
if (typeof path12 !== "string" || path12.trim().length === 0 || !normalizedKind || !normalizedLane) {
|
|
629
629
|
diagnostics.push(
|
|
630
630
|
createAreaDiagnostic(
|
|
631
631
|
`Area ${areaName} truth_documents entries must include non-empty path plus valid lane and kind fields.`,
|
|
@@ -635,7 +635,7 @@ var parseTruthDocumentsFromYaml = (sectionLines, areaName, options) => {
|
|
|
635
635
|
continue;
|
|
636
636
|
}
|
|
637
637
|
truthDocumentEntries.push({
|
|
638
|
-
path:
|
|
638
|
+
path: path12.trim(),
|
|
639
639
|
kind: normalizedKind,
|
|
640
640
|
kindSource: isTruthDocumentKind(kind) ? "explicit" : "inferred",
|
|
641
641
|
lane: normalizedLane,
|
|
@@ -853,7 +853,7 @@ var renderChildAreaTemplate = (config) => {
|
|
|
853
853
|
const defaultArea = config.truthmark.routes.defaultArea;
|
|
854
854
|
const title = titleCase(defaultArea);
|
|
855
855
|
const truthDocsRoot = truthRoot(config);
|
|
856
|
-
const
|
|
856
|
+
const bootstrapTruthDoc = `${truthDocsRoot}/${defaultArea}/bootstrap-routing.md`;
|
|
857
857
|
const templatePath = `${config.truthmark.paths.routeAreasRoot}/${defaultArea}.md`;
|
|
858
858
|
const sourceOfTruth = resolveRelativePath(
|
|
859
859
|
templatePath,
|
|
@@ -873,16 +873,20 @@ var renderChildAreaTemplate = (config) => {
|
|
|
873
873
|
"Truth documents:",
|
|
874
874
|
"```yaml",
|
|
875
875
|
"truth_documents:",
|
|
876
|
-
` - path: ${
|
|
877
|
-
" kind: engineering-
|
|
876
|
+
` - path: ${bootstrapTruthDoc}`,
|
|
877
|
+
" kind: engineering-workflow",
|
|
878
878
|
" lane: engineering",
|
|
879
879
|
"```",
|
|
880
880
|
"",
|
|
881
|
+
"This is a provisional bootstrap route. It exists only to make fresh repositories routeable until real product, service, domain, or ownership areas are created.",
|
|
882
|
+
"",
|
|
881
883
|
"Code surface:",
|
|
882
884
|
"- src/**",
|
|
883
885
|
"",
|
|
884
886
|
"Update truth when:",
|
|
885
|
-
"-
|
|
887
|
+
"- this provisional bootstrap route is the only match for a real code surface",
|
|
888
|
+
"- route ownership is still broad, mixed, or ambiguous",
|
|
889
|
+
"- Run Truth Structure before normal Truth Sync so the touched code gets a bounded owner",
|
|
886
890
|
"",
|
|
887
891
|
"## Source References",
|
|
888
892
|
"",
|
|
@@ -890,6 +894,103 @@ var renderChildAreaTemplate = (config) => {
|
|
|
890
894
|
""
|
|
891
895
|
].join("\n");
|
|
892
896
|
};
|
|
897
|
+
var renderBootstrapRoutingDocTemplate = (config) => {
|
|
898
|
+
const defaultArea = config.truthmark.routes.defaultArea;
|
|
899
|
+
const title = titleCase(defaultArea);
|
|
900
|
+
const templatePath = `${truthRoot(config)}/${defaultArea}/bootstrap-routing.md`;
|
|
901
|
+
const routePath = `${config.truthmark.paths.routeAreasRoot}/${defaultArea}.md`;
|
|
902
|
+
const routeSource = resolveRelativePath(templatePath, routePath);
|
|
903
|
+
const configSource = resolveRelativePath(
|
|
904
|
+
templatePath,
|
|
905
|
+
".truthmark/config.yml"
|
|
906
|
+
);
|
|
907
|
+
const today = currentDate();
|
|
908
|
+
return [
|
|
909
|
+
"---",
|
|
910
|
+
"status: active",
|
|
911
|
+
"truth_kind: engineering-workflow",
|
|
912
|
+
`last_reviewed: ${today}`,
|
|
913
|
+
"---",
|
|
914
|
+
"",
|
|
915
|
+
`# ${title} Bootstrap Routing`,
|
|
916
|
+
"",
|
|
917
|
+
"## Purpose",
|
|
918
|
+
"",
|
|
919
|
+
`This doc records the provisional broad route for ${defaultArea}.`,
|
|
920
|
+
"This doc is a bootstrap handoff, not a behavior truth dumping ground.",
|
|
921
|
+
"It is not a substitute for bounded product and engineering truth docs.",
|
|
922
|
+
"",
|
|
923
|
+
"## Scope",
|
|
924
|
+
"",
|
|
925
|
+
"This doc owns only the initial routing workflow for a fresh Truthmark repository whose default route still maps a broad code surface such as `src/**`.",
|
|
926
|
+
"It does not own implementation behavior under that code surface.",
|
|
927
|
+
"",
|
|
928
|
+
"## Current Implementation Behavior",
|
|
929
|
+
"",
|
|
930
|
+
"The scaffold creates this provisional bootstrap handoff only when a default broad route needs a canonical owner. Agents use it as a signal to run Truth Structure and create bounded routes before normal Truth Sync, not as a place to accumulate implementation claims.",
|
|
931
|
+
"",
|
|
932
|
+
"## Product Truth Links",
|
|
933
|
+
"",
|
|
934
|
+
"- None. This is an engineering bootstrap handoff for routing setup, not a product promise.",
|
|
935
|
+
"",
|
|
936
|
+
"## Triggers",
|
|
937
|
+
"",
|
|
938
|
+
"- A real code change maps only to this provisional broad route.",
|
|
939
|
+
"- Truth Sync cannot identify a specific behavior-owned route and bounded truth owner.",
|
|
940
|
+
"- A maintainer or agent is onboarding the first real product, service, domain, package, or ownership area.",
|
|
941
|
+
"",
|
|
942
|
+
"## Inputs",
|
|
943
|
+
"",
|
|
944
|
+
"- Current route files under the configured Truthmark route root.",
|
|
945
|
+
"- The touched code, tests, configuration, and existing docs needed to infer the smallest real owner.",
|
|
946
|
+
"- Repository instruction files that exist in the checkout.",
|
|
947
|
+
"",
|
|
948
|
+
"## Execution Model",
|
|
949
|
+
"",
|
|
950
|
+
"Run Truth Structure before normal Truth Sync when real code changes touch only this broad route. Truth Structure should create or repair bounded areas first; Truth Sync should then update the bounded owner docs.",
|
|
951
|
+
"",
|
|
952
|
+
"## Steps",
|
|
953
|
+
"",
|
|
954
|
+
"1. Treat this route as provisional and insufficient for normal behavior maintenance.",
|
|
955
|
+
"2. Inspect the touched code/test surface and infer the narrowest durable owner.",
|
|
956
|
+
"3. Create or repair route entries and truth docs for that owner.",
|
|
957
|
+
"4. Leave this bootstrap doc small; do not append behavior details here.",
|
|
958
|
+
"5. Resume Truth Sync only after the touched code resolves to a bounded owner.",
|
|
959
|
+
"",
|
|
960
|
+
"## State, Retry, And Failure Behavior",
|
|
961
|
+
"",
|
|
962
|
+
"If ownership cannot be inferred safely, stop and report manual-review files instead of widening this route or adding generic behavior prose.",
|
|
963
|
+
"",
|
|
964
|
+
"## Outputs",
|
|
965
|
+
"",
|
|
966
|
+
"- Bounded route areas and lane-appropriate truth docs for the touched surface.",
|
|
967
|
+
"- A compact manual handoff report when ownership remains ambiguous.",
|
|
968
|
+
"",
|
|
969
|
+
"## Engineering Decisions",
|
|
970
|
+
"",
|
|
971
|
+
`- Decision (${today}): Default broad routing is provisional bootstrap state. Agents should create bounded areas before normal Truth Sync rather than extending a catch-all overview doc.`,
|
|
972
|
+
"",
|
|
973
|
+
"## Rationale",
|
|
974
|
+
"",
|
|
975
|
+
"Scoped ownership keeps agent context close to affected files and prevents broad default docs from absorbing unrelated behavior. This preserves agent-native truth maintenance without adding a token-heavy discovery layer.",
|
|
976
|
+
"",
|
|
977
|
+
"## Non-Goals",
|
|
978
|
+
"",
|
|
979
|
+
"- This doc is not a repository behavior overview.",
|
|
980
|
+
"- This doc is not a product capability or engineering behavior owner.",
|
|
981
|
+
"- This doc is not a permanent home for claims about files under `src/**`.",
|
|
982
|
+
"",
|
|
983
|
+
"## Maintenance Notes",
|
|
984
|
+
"",
|
|
985
|
+
"Keep this doc short. When a repository has real bounded routes, prefer updating those routes and their truth docs instead of expanding this bootstrap handoff.",
|
|
986
|
+
"",
|
|
987
|
+
"## Source References",
|
|
988
|
+
"",
|
|
989
|
+
`- ${routeSource}`,
|
|
990
|
+
`- ${configSource}`,
|
|
991
|
+
""
|
|
992
|
+
].join("\n");
|
|
993
|
+
};
|
|
893
994
|
var renderTruthRootReadmeTemplate = (config = createDefaultConfig(), lane = "engineering") => {
|
|
894
995
|
const templatePath = `${lane === "product" ? resolveProductTruthRoot(config) : resolveEngineeringTruthRoot(config)}/README.md`;
|
|
895
996
|
const sourceOfTruth = resolveRelativePath(
|
|
@@ -940,7 +1041,7 @@ var renderTruthDomainReadmeTemplate = (config) => {
|
|
|
940
1041
|
"",
|
|
941
1042
|
"Current leaf docs:",
|
|
942
1043
|
"",
|
|
943
|
-
"- [
|
|
1044
|
+
"- [Bootstrap routing](bootstrap-routing.md)",
|
|
944
1045
|
"",
|
|
945
1046
|
"## Source References",
|
|
946
1047
|
"",
|
|
@@ -948,7 +1049,6 @@ var renderTruthDomainReadmeTemplate = (config) => {
|
|
|
948
1049
|
""
|
|
949
1050
|
].join("\n");
|
|
950
1051
|
};
|
|
951
|
-
var BEHAVIOR_DOC_TEMPLATE_PATH = "docs/truthmark/templates/engineering-behavior.md";
|
|
952
1052
|
var renderTemplateSection = (section) => {
|
|
953
1053
|
return [
|
|
954
1054
|
section.heading,
|
|
@@ -1363,7 +1463,7 @@ var renderContractDocTemplateFile = () => {
|
|
|
1363
1463
|
]),
|
|
1364
1464
|
sectionSpec("## Compatibility Rules", [
|
|
1365
1465
|
"State backward/forward compatibility guarantees, tolerated inputs, deprecation rules, and breaking-change triggers.",
|
|
1366
|
-
"Include compatibility tests or review
|
|
1466
|
+
"Include compatibility tests or review questions that protect the contract."
|
|
1367
1467
|
]),
|
|
1368
1468
|
sectionSpec("## Versioning And Migration", [
|
|
1369
1469
|
"Document version negotiation, schema/API version fields, rollout requirements, migration steps, and rollback expectations.",
|
|
@@ -1411,7 +1511,7 @@ var renderWorkflowDocTemplateFile = () => {
|
|
|
1411
1511
|
]),
|
|
1412
1512
|
sectionSpec("## Execution Model", [
|
|
1413
1513
|
"Describe synchronous/asynchronous execution, concurrency, locking, leases, batching, ordering, and idempotency behavior.",
|
|
1414
|
-
"State whether the workflow
|
|
1514
|
+
"State whether the workflow waits for user action, runs in the background, is distributed, or is delegated to another system."
|
|
1415
1515
|
]),
|
|
1416
1516
|
sectionSpec("## Steps", [
|
|
1417
1517
|
"Capture the current ordered steps or phases at a level useful for maintenance and review.",
|
|
@@ -1447,7 +1547,7 @@ var renderOperationsDocTemplateFile = () => {
|
|
|
1447
1547
|
]),
|
|
1448
1548
|
sectionSpec("## Deployment And Rollback", [
|
|
1449
1549
|
"Describe deployment mechanism, migration ordering, compatibility windows, rollback path, and known irreversible operations.",
|
|
1450
|
-
"Call out manual
|
|
1550
|
+
"Call out manual review points, smoke checks, and post-deploy verification responsibilities."
|
|
1451
1551
|
]),
|
|
1452
1552
|
sectionSpec("## Availability And Observability", [
|
|
1453
1553
|
"Capture availability expectations, health checks, metrics, logs, traces, alerts, SLO/error-budget signals, and known blind spots.",
|
|
@@ -1483,39 +1583,6 @@ var renderTestBehaviorDocTemplateFile = () => {
|
|
|
1483
1583
|
])
|
|
1484
1584
|
]);
|
|
1485
1585
|
};
|
|
1486
|
-
var renderTemplate = (template, values) => {
|
|
1487
|
-
return Object.entries(values).reduce((rendered, [key, value]) => {
|
|
1488
|
-
return rendered.split(`{{${key}}}`).join(value);
|
|
1489
|
-
}, template);
|
|
1490
|
-
};
|
|
1491
|
-
var renderBehaviorLeafDocTemplate = (config, template = renderBehaviorDocTemplateFile()) => {
|
|
1492
|
-
const defaultArea = config.truthmark.routes.defaultArea;
|
|
1493
|
-
const title = titleCase(defaultArea);
|
|
1494
|
-
const templatePath = `${truthRoot(config)}/${defaultArea}/overview.md`;
|
|
1495
|
-
const sourceOfTruth = resolveRelativePath(
|
|
1496
|
-
templatePath,
|
|
1497
|
-
`${config.truthmark.paths.routeAreasRoot}/${defaultArea}.md`
|
|
1498
|
-
);
|
|
1499
|
-
const today = currentDate();
|
|
1500
|
-
return renderTemplate(template, {
|
|
1501
|
-
area: defaultArea,
|
|
1502
|
-
contracts: "- External contracts should link to the nearest canonical contract doc when one exists.",
|
|
1503
|
-
core_rules: "- Truth README files are indexes; behavior truth belongs in bounded leaf docs.",
|
|
1504
|
-
current_implementation_behavior: "- Document current behavior here when implementation changes make repository truth incomplete.",
|
|
1505
|
-
engineering_decisions: `- Decision (${today}): Truth README files are indexes; behavior truth belongs in bounded leaf docs.`,
|
|
1506
|
-
flows_and_states: "- None beyond current behavior.",
|
|
1507
|
-
maintenance_notes: "- Update this doc when routed implementation changes alter current behavior, rules, contracts, or decisions.",
|
|
1508
|
-
non_goals: "- This doc is not a catch-all for unrelated repository behavior.",
|
|
1509
|
-
purpose: `Describe why the default ${title.toLowerCase()} behavior surface exists and what outcome it protects.`,
|
|
1510
|
-
rationale: "Bounded leaf docs keep agent context focused and prevent large products from accumulating unreviewable feature manuals.",
|
|
1511
|
-
product_truth_links: "- None.",
|
|
1512
|
-
scope: `This bounded leaf truth doc owns the default ${title.toLowerCase()} behavior surface created by Truthmark.`,
|
|
1513
|
-
source_references: `- ${sourceOfTruth}`,
|
|
1514
|
-
template_path: BEHAVIOR_DOC_TEMPLATE_PATH,
|
|
1515
|
-
title: `${title} Overview`,
|
|
1516
|
-
truth_kind: "engineering-behavior"
|
|
1517
|
-
});
|
|
1518
|
-
};
|
|
1519
1586
|
|
|
1520
1587
|
// src/config/command.ts
|
|
1521
1588
|
var CONFIG_PATH = ".truthmark/config.yml";
|
|
@@ -1594,7 +1661,7 @@ var runConfig = async (cwd, options = {}) => {
|
|
|
1594
1661
|
};
|
|
1595
1662
|
|
|
1596
1663
|
// src/init/init.ts
|
|
1597
|
-
import
|
|
1664
|
+
import fs6 from "fs/promises";
|
|
1598
1665
|
|
|
1599
1666
|
// src/config/load.ts
|
|
1600
1667
|
import fs4 from "fs/promises";
|
|
@@ -1840,22 +1907,6 @@ var rootIndexReferencesChildRoute = async (rootDir, rootIndexPath, childRoutePat
|
|
|
1840
1907
|
var truthTemplatePath = (config, fileName) => {
|
|
1841
1908
|
return `${config.truthmark.paths.templatesRoot}/${fileName}`;
|
|
1842
1909
|
};
|
|
1843
|
-
var readBehaviorDocTemplate = async (rootDir, config) => {
|
|
1844
|
-
try {
|
|
1845
|
-
return await fs5.readFile(
|
|
1846
|
-
resolveRepoPath(
|
|
1847
|
-
rootDir,
|
|
1848
|
-
truthTemplatePath(config, BEHAVIOR_DOC_TEMPLATE_FILE_NAME)
|
|
1849
|
-
),
|
|
1850
|
-
"utf8"
|
|
1851
|
-
);
|
|
1852
|
-
} catch (error) {
|
|
1853
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
1854
|
-
return renderBehaviorDocTemplateFile();
|
|
1855
|
-
}
|
|
1856
|
-
throw error;
|
|
1857
|
-
}
|
|
1858
|
-
};
|
|
1859
1910
|
var ensureOrUpdateTruthDocTemplate = async (rootDir, templatePath, defaultTemplate) => {
|
|
1860
1911
|
const seededResult = await ensureRepoFile(
|
|
1861
1912
|
rootDir,
|
|
@@ -1970,12 +2021,11 @@ var scaffoldHierarchy = async (rootDir, config) => {
|
|
|
1970
2021
|
renderProductCapabilityDocTemplateFile()
|
|
1971
2022
|
)
|
|
1972
2023
|
);
|
|
1973
|
-
const behaviorDocTemplate = await readBehaviorDocTemplate(rootDir, config);
|
|
1974
2024
|
results.push(
|
|
1975
2025
|
await ensureRepoFile(
|
|
1976
2026
|
rootDir,
|
|
1977
|
-
`${truthDomainRoot}/
|
|
1978
|
-
|
|
2027
|
+
`${truthDomainRoot}/bootstrap-routing.md`,
|
|
2028
|
+
renderBootstrapRoutingDocTemplate(config)
|
|
1979
2029
|
)
|
|
1980
2030
|
);
|
|
1981
2031
|
return results;
|
|
@@ -2029,7 +2079,7 @@ var renderReadOnlyLaneClassificationRuleBlock = (config = defaultAgentConfig(),
|
|
|
2029
2079
|
`product-lane ownership belongs under ${productTruthRoot} and describes product promises, boundaries, rationale, decisions, and success criteria`,
|
|
2030
2080
|
`engineering-lane ownership belongs under ${engineeringTruthRoot} and describes source-backed current realization, contracts, architecture, workflows, operations, or tests`,
|
|
2031
2081
|
"both-lane ownership uses separate product and engineering docs cross-linked in route YAML with realized_by and realizes, not in doc frontmatter",
|
|
2032
|
-
"ambiguous lane ownership should be reported
|
|
2082
|
+
"ambiguous lane ownership should be reported for manual handoff or routed to Truth Structure",
|
|
2033
2083
|
LANE_INVARIANT
|
|
2034
2084
|
].join("\n"),
|
|
2035
2085
|
indent
|
|
@@ -2045,12 +2095,12 @@ var renderLaneClassificationInstructions = (config = defaultAgentConfig()) => {
|
|
|
2045
2095
|
const productTruthRoot = resolveProductTruthRoot(config);
|
|
2046
2096
|
const engineeringTruthRoot = resolveEngineeringTruthRoot(config);
|
|
2047
2097
|
return [
|
|
2048
|
-
"Lane
|
|
2098
|
+
"Lane review questions:",
|
|
2049
2099
|
"- before writing canonical truth docs, classify the request or change as product-lane, engineering-lane, both-lane, or ambiguous",
|
|
2050
2100
|
`- product-lane writes belong under ${productTruthRoot} and state product promises, boundaries, rationale, decisions, and success criteria`,
|
|
2051
2101
|
`- engineering-lane writes belong under ${engineeringTruthRoot} and state source-backed current realization, contracts, architecture, workflows, operations, or tests`,
|
|
2052
2102
|
"- both-lane work must write separate product and engineering docs and cross-link them in route YAML with realized_by and realizes, not in doc frontmatter",
|
|
2053
|
-
"- ambiguous lane ownership must
|
|
2103
|
+
"- ambiguous lane ownership must stop or invoke Truth Structure instead of writing a mixed document",
|
|
2054
2104
|
`- ${LANE_INVARIANT}`
|
|
2055
2105
|
].join("\n");
|
|
2056
2106
|
};
|
|
@@ -2059,7 +2109,7 @@ var EVIDENCE_AUTHORITY_INSTRUCTIONS = [
|
|
|
2059
2109
|
"Implementation code and canonical truth docs are inspected evidence for current behavior; they do not silently override workflow write boundaries."
|
|
2060
2110
|
].join("\n");
|
|
2061
2111
|
var REPOSITORY_INTELLIGENCE_INSTRUCTIONS = [
|
|
2062
|
-
"Repository intelligence artifacts are optional derived context: RepoIndex, RouteMap, ImpactSet, and
|
|
2112
|
+
"Repository intelligence artifacts are optional derived context: RepoIndex, RouteMap, ImpactSet, and WorkflowState/action context may guide routing, write boundaries, and verification planning when available.",
|
|
2063
2113
|
"They do not override checkout evidence, canonical truth docs, route files, or workflow write boundaries.",
|
|
2064
2114
|
"If unavailable, inspect any present Truthmark config, route files, source files, truth docs, and tests directly, then report that repository-intelligence artifacts were not generated."
|
|
2065
2115
|
].join("\n");
|
|
@@ -2073,29 +2123,29 @@ var FEATURE_DOC_TEMPLATE_INSTRUCTIONS = [
|
|
|
2073
2123
|
].join("\n");
|
|
2074
2124
|
var renderTruthDocOwnershipGateSection = (subject, outcome) => {
|
|
2075
2125
|
return [
|
|
2076
|
-
"Truth-doc ownership
|
|
2126
|
+
"Truth-doc ownership review:",
|
|
2077
2127
|
`- before editing or relying on ${subject}, verify each target/source truth doc is a bounded owner for the behavior`,
|
|
2078
2128
|
"- if a target/source doc mixes independent owners, spans unrelated behaviors, acts as an index, or needs cross-owner edits, do not patch or in-place repair it",
|
|
2079
2129
|
`- ${outcome}`,
|
|
2080
|
-
"- report Ownership reviewed, Structure required, Truth docs split, Truth docs restructured, or
|
|
2130
|
+
"- report Ownership reviewed, Structure required, Truth docs split, Truth docs restructured, or Manual handoff reason as applicable"
|
|
2081
2131
|
].join("\n");
|
|
2082
2132
|
};
|
|
2083
2133
|
var TRUTH_DOC_DECISION_RATIONALE_PRESERVATION_INSTRUCTIONS = [
|
|
2084
|
-
"Decision/Rationale preservation
|
|
2134
|
+
"Decision/Rationale preservation review:",
|
|
2085
2135
|
"- before any truth-doc split, restructure, or shape repair, inventory existing Product Decisions, Engineering Decisions, and Rationale sections in every source or touched truth doc",
|
|
2086
2136
|
"- preserve each current decision and rationale in the correct product or engineering lane owner; when splitting, move it to the new owner doc rather than deleting it or leaving it in an index",
|
|
2087
2137
|
"- remove or narrow a decision or rationale only when checkout evidence shows it is stale or unsupported, and report the exact claim, evidence, and result",
|
|
2088
|
-
"- if ownership of a decision or rationale is unclear,
|
|
2138
|
+
"- if ownership of a decision or rationale is unclear, stop with manual-review files instead of deleting it or guessing",
|
|
2089
2139
|
"- after the edit, verify every touched truth doc keeps lane-appropriate decision/rationale sections and every pre-existing entry is preserved, moved, narrowed, removed with evidence, or blocked"
|
|
2090
2140
|
].join("\n");
|
|
2091
2141
|
var renderTruthDocRestructureGateSection = (scope) => {
|
|
2092
2142
|
return [
|
|
2093
|
-
"Truth-doc shape repair
|
|
2143
|
+
"Truth-doc shape repair review:",
|
|
2094
2144
|
`- ${scope}`,
|
|
2095
|
-
"- repair shape in place only after the ownership
|
|
2145
|
+
"- repair shape in place only after the ownership review confirms the doc is the right bounded owner",
|
|
2096
2146
|
"- use Truth Structure for ownership splits; do not treat broad or mixed-owner docs as in-place repair work",
|
|
2097
2147
|
"- repair shape when a narrow edit would make truth worse: missing template sections, stale evidence conflicts, cross-section updates within one owner, or wrong frontmatter/source/headings",
|
|
2098
|
-
"- preserve supported claims; remove, narrow, or
|
|
2148
|
+
"- preserve supported claims; remove, narrow, or record unsupported or stale claims for manual handoff",
|
|
2099
2149
|
"- report docs restructured and why a narrow edit was not sufficient"
|
|
2100
2150
|
].join("\n");
|
|
2101
2151
|
};
|
|
@@ -2105,26 +2155,26 @@ var ARCHITECTURE_DOC_BOUNDARY_INSTRUCTIONS = [
|
|
|
2105
2155
|
].join("\n");
|
|
2106
2156
|
var renderRouteFirstEvidenceGateSection = (subject, noImpactedDocOutcome) => {
|
|
2107
2157
|
return [
|
|
2108
|
-
"Evidence
|
|
2158
|
+
"Evidence checklist:",
|
|
2109
2159
|
`- route-first: map ${subject} to bounded route owners and primary canonical docs`,
|
|
2110
2160
|
"- review new or changed behavior-bearing claims only in touched docs, route ownership, lane-specific decisions, and rationale",
|
|
2111
2161
|
"- support claims with primary checkout evidence: implementation, config, routing, generated templates, schemas, or contract definitions",
|
|
2112
2162
|
"- tests/examples/canonical docs corroborate; they are not sole proof when implementation conflicts",
|
|
2113
|
-
"- remove, narrow, or
|
|
2163
|
+
"- remove, narrow, or record unsupported claims for manual handoff",
|
|
2114
2164
|
`- ${noImpactedDocOutcome}`
|
|
2115
2165
|
].join("\n");
|
|
2116
2166
|
};
|
|
2117
2167
|
var renderTopologyEvidenceGateSection = () => {
|
|
2118
2168
|
return [
|
|
2119
|
-
"Evidence
|
|
2120
|
-
"- apply the
|
|
2169
|
+
"Evidence checklist:",
|
|
2170
|
+
"- apply the evidence checklist before finishing when Truth Structure writes routed docs, ownership claims, lane-specific decisions, or rationale",
|
|
2121
2171
|
"- support ownership/behavior claims with topology or primary checkout evidence from layout, implementation boundaries, docs, config, route files, tests, templates, schemas, or contracts",
|
|
2122
|
-
"- tests/examples/canonical docs corroborate; remove, narrow, or
|
|
2172
|
+
"- tests/examples/canonical docs corroborate; remove, narrow, or record unsupported claims for manual handoff"
|
|
2123
2173
|
].join("\n");
|
|
2124
2174
|
};
|
|
2125
2175
|
var renderAuditEvidenceGateSection = () => {
|
|
2126
2176
|
return [
|
|
2127
|
-
"Evidence
|
|
2177
|
+
"Evidence checklist:",
|
|
2128
2178
|
"- support each finding and suggested fix with evidence from config, route files, canonical docs, implementation, templates, or tests",
|
|
2129
2179
|
"- canonical docs are context, not sole proof when implementation conflicts",
|
|
2130
2180
|
"- remove unsupported findings or mark open questions; validate changed claims if you edit docs"
|
|
@@ -2259,22 +2309,15 @@ var renderHierarchySummary = (config) => {
|
|
|
2259
2309
|
].join("\n");
|
|
2260
2310
|
};
|
|
2261
2311
|
|
|
2262
|
-
// src/version.ts
|
|
2263
|
-
import fs6 from "fs";
|
|
2264
|
-
var packageJson = JSON.parse(
|
|
2265
|
-
fs6.readFileSync(new URL("../package.json", import.meta.url), "utf8")
|
|
2266
|
-
);
|
|
2267
|
-
var TRUTHMARK_VERSION = packageJson.version;
|
|
2268
|
-
|
|
2269
2312
|
// src/templates/agents-block.ts
|
|
2270
2313
|
var TRUTHMARK_BLOCK_START = "<!-- truthmark:start -->";
|
|
2271
2314
|
var TRUTHMARK_BLOCK_END = "<!-- truthmark:end -->";
|
|
2272
2315
|
var renderCompactHierarchySummary = (config) => {
|
|
2273
2316
|
const productTruthRoot = resolveProductTruthRoot(config);
|
|
2274
2317
|
const engineeringTruthRoot = resolveEngineeringTruthRoot(config);
|
|
2275
|
-
const truthDocRoots = Array.from(
|
|
2276
|
-
(
|
|
2277
|
-
);
|
|
2318
|
+
const truthDocRoots = Array.from(
|
|
2319
|
+
/* @__PURE__ */ new Set([productTruthRoot, engineeringTruthRoot])
|
|
2320
|
+
).map((truthRoot3) => `${truthRoot3}/**/*.md`);
|
|
2278
2321
|
return `Hierarchy hints: config .truthmark/config.yml when present; routes ${config.truthmark.paths.routesIndex} and ${config.truthmark.paths.routeAreasRoot}/**/*.md when present; Truth docs: ${truthDocRoots.join(" and ")} when present.`;
|
|
2279
2322
|
};
|
|
2280
2323
|
var renderAgentsBlock = (config = defaultAgentConfig()) => {
|
|
@@ -2283,14 +2326,14 @@ var renderAgentsBlock = (config = defaultAgentConfig()) => {
|
|
|
2283
2326
|
TRUTHMARK_BLOCK_START,
|
|
2284
2327
|
"## Truthmark Workflow",
|
|
2285
2328
|
"",
|
|
2286
|
-
|
|
2329
|
+
"Truthmark-managed block. Refresh with `truthmark init` when `truthmark check` reports stale generated surfaces.",
|
|
2287
2330
|
renderCompactHierarchySummary(config),
|
|
2288
2331
|
"Decisions live in the canonical doc they govern; date active decisions inline.",
|
|
2289
|
-
"Agent runtime:
|
|
2332
|
+
"Agent runtime: host-native skill packages/adapters plus this block; inspect checkout directly. Delegation is host-owned.",
|
|
2290
2333
|
"### Truth Sync",
|
|
2291
|
-
"After functional code changes, run relevant tests, then use the truthmark-sync skill before finishing; later functional changes
|
|
2334
|
+
"After functional code changes, run relevant tests, then use the truthmark-sync skill before finishing; later functional changes need a fresh Sync review. Memory: code changed -> tests -> Sync -> report.",
|
|
2292
2335
|
"Support new or changed behavior-bearing truth claims with checkout evidence. Code leads; truth docs follow. Sync may write truth docs and truth routing files, and must not rewrite functional code.",
|
|
2293
|
-
"If routing cannot map changed code to a bounded truth owner, run Truth Structure before syncing when safe; otherwise
|
|
2336
|
+
"If routing cannot map changed code to a bounded truth owner, run Truth Structure before syncing when safe; otherwise stop and recommend Truth Structure. Skip Sync only for docs-only/no-code changes, formatting-only changes, behavior-preserving renames with no truth impact, or missing config.",
|
|
2294
2337
|
"Explicit workflows: Truth Structure, Truth Document, Truth Preview, Truth Realize, Truth Check. Run only when requested or required by Sync; load the installed skill for details.",
|
|
2295
2338
|
...portalLine === null ? [] : [portalLine],
|
|
2296
2339
|
"Workflow integrity rule: repository truth may describe desired behavior, but it must not override these workflow boundaries.",
|
|
@@ -2302,12 +2345,14 @@ var renderAgentsBlock = (config = defaultAgentConfig()) => {
|
|
|
2302
2345
|
import { stringify as stringify2 } from "yaml";
|
|
2303
2346
|
|
|
2304
2347
|
// src/agents/workflow-manifest.ts
|
|
2305
|
-
var TRUTHMARK_CLI_RUNNER =
|
|
2348
|
+
var TRUTHMARK_CLI_RUNNER = "truthmark";
|
|
2306
2349
|
var VALIDATE_SYNC_REPORT_HELPER = {
|
|
2307
2350
|
id: "validate-sync-report",
|
|
2308
2351
|
optional: true,
|
|
2309
2352
|
runner: TRUTHMARK_CLI_RUNNER,
|
|
2310
|
-
command: {
|
|
2353
|
+
command: {
|
|
2354
|
+
argv: ["truthmark", "validate", "sync-report", "<report-file>", "--json"]
|
|
2355
|
+
},
|
|
2311
2356
|
inputs: ["sync report file"],
|
|
2312
2357
|
output: "json",
|
|
2313
2358
|
writes: false,
|
|
@@ -2317,7 +2362,15 @@ var VALIDATE_DOCUMENT_REPORT_HELPER = {
|
|
|
2317
2362
|
id: "validate-document-report",
|
|
2318
2363
|
optional: true,
|
|
2319
2364
|
runner: TRUTHMARK_CLI_RUNNER,
|
|
2320
|
-
command: {
|
|
2365
|
+
command: {
|
|
2366
|
+
argv: [
|
|
2367
|
+
"truthmark",
|
|
2368
|
+
"validate",
|
|
2369
|
+
"document-report",
|
|
2370
|
+
"<report-file>",
|
|
2371
|
+
"--json"
|
|
2372
|
+
]
|
|
2373
|
+
},
|
|
2321
2374
|
inputs: ["document report file"],
|
|
2322
2375
|
output: "json",
|
|
2323
2376
|
writes: false,
|
|
@@ -2365,17 +2418,18 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2365
2418
|
"doc-first implementation belongs to Truth Realize",
|
|
2366
2419
|
"manual topology design belongs to Truth Structure"
|
|
2367
2420
|
],
|
|
2368
|
-
|
|
2369
|
-
"topology
|
|
2421
|
+
reviewQuestions: [
|
|
2422
|
+
"topology review",
|
|
2370
2423
|
"truth-doc ownership",
|
|
2371
2424
|
"lane classification",
|
|
2372
2425
|
"Decision/Rationale preservation",
|
|
2373
2426
|
"truth-doc shape repair when restructuring",
|
|
2374
|
-
"Evidence
|
|
2427
|
+
"Evidence checklist"
|
|
2375
2428
|
],
|
|
2376
2429
|
allowedWrites: ["canonical truth docs", "truth routing files"],
|
|
2377
2430
|
reportSections: [
|
|
2378
2431
|
"Changed code reviewed",
|
|
2432
|
+
"Sync Intent",
|
|
2379
2433
|
"Ownership reviewed",
|
|
2380
2434
|
"Structure required",
|
|
2381
2435
|
"Truth docs updated",
|
|
@@ -2410,13 +2464,13 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2410
2464
|
"must not implement functional code",
|
|
2411
2465
|
"must not patch mixed-owner docs as shape repair"
|
|
2412
2466
|
],
|
|
2413
|
-
|
|
2467
|
+
reviewQuestions: [
|
|
2414
2468
|
"truth-doc ownership",
|
|
2415
2469
|
"lane classification",
|
|
2416
2470
|
"Decision/Rationale preservation",
|
|
2417
2471
|
"lane split and relationship repair",
|
|
2418
2472
|
"truth-doc shape repair when restructuring",
|
|
2419
|
-
"Evidence
|
|
2473
|
+
"Evidence checklist"
|
|
2420
2474
|
],
|
|
2421
2475
|
allowedWrites: ["truth routing files", "starter canonical truth docs"],
|
|
2422
2476
|
reportSections: [
|
|
@@ -2453,11 +2507,11 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2453
2507
|
"must not edit functional code",
|
|
2454
2508
|
"must not repair mixed-owner docs in place"
|
|
2455
2509
|
],
|
|
2456
|
-
|
|
2510
|
+
reviewQuestions: [
|
|
2457
2511
|
"truth-doc ownership",
|
|
2458
2512
|
"lane classification",
|
|
2459
2513
|
"Decision/Rationale preservation",
|
|
2460
|
-
"Evidence
|
|
2514
|
+
"Evidence checklist",
|
|
2461
2515
|
"truth-doc shape repair when restructuring"
|
|
2462
2516
|
],
|
|
2463
2517
|
allowedWrites: ["canonical truth docs", "truth routing files"],
|
|
@@ -2495,7 +2549,7 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2495
2549
|
"must not edit truth docs except through follow-up Truth Sync",
|
|
2496
2550
|
"must not edit truth routing except through follow-up Truth Sync"
|
|
2497
2551
|
],
|
|
2498
|
-
|
|
2552
|
+
reviewQuestions: ["lane classification", "truth-doc ownership"],
|
|
2499
2553
|
allowedWrites: ["functional code"],
|
|
2500
2554
|
reportSections: ["Truth docs used", "Code updated", "Verification"]
|
|
2501
2555
|
},
|
|
@@ -2512,7 +2566,7 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2512
2566
|
],
|
|
2513
2567
|
negativeTriggers: [
|
|
2514
2568
|
"normal validation or final correctness audit",
|
|
2515
|
-
"automatic preflight or finish-time
|
|
2569
|
+
"automatic preflight or finish-time review",
|
|
2516
2570
|
"request to mutate truth docs, routing, or code"
|
|
2517
2571
|
],
|
|
2518
2572
|
forbiddenAdjacency: [
|
|
@@ -2520,10 +2574,10 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2520
2574
|
"must not run Truth Sync automatically",
|
|
2521
2575
|
"must not authorize later edits or issue write leases"
|
|
2522
2576
|
],
|
|
2523
|
-
|
|
2577
|
+
reviewQuestions: [
|
|
2524
2578
|
"read-only boundary",
|
|
2525
2579
|
"intended-not-authorized handoff",
|
|
2526
|
-
"
|
|
2580
|
+
"manual handoff questions"
|
|
2527
2581
|
],
|
|
2528
2582
|
allowedWrites: ["none by default"],
|
|
2529
2583
|
reportSections: [
|
|
@@ -2534,7 +2588,7 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2534
2588
|
"Expected write classes",
|
|
2535
2589
|
"Expected target files",
|
|
2536
2590
|
"Suggested subagent use",
|
|
2537
|
-
"
|
|
2591
|
+
"Manual handoff questions",
|
|
2538
2592
|
"Handoff"
|
|
2539
2593
|
],
|
|
2540
2594
|
subagents: ["truth_route_auditor"]
|
|
@@ -2559,7 +2613,7 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2559
2613
|
"must not replace ordinary verification",
|
|
2560
2614
|
"must not silently rewrite docs"
|
|
2561
2615
|
],
|
|
2562
|
-
|
|
2616
|
+
reviewQuestions: ["audit evidence checklist"],
|
|
2563
2617
|
allowedWrites: ["none by default"],
|
|
2564
2618
|
reportSections: [
|
|
2565
2619
|
"Files reviewed",
|
|
@@ -2597,11 +2651,11 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2597
2651
|
"machine-readable agent context"
|
|
2598
2652
|
],
|
|
2599
2653
|
forbiddenAdjacency: [
|
|
2600
|
-
"must not run
|
|
2654
|
+
"must not run automatically at completion",
|
|
2601
2655
|
"must not replace Truth Sync, Truth Check, Truth Document, Truth Realize, or Truth Structure",
|
|
2602
2656
|
"must not write outside the fixed Portal output directory"
|
|
2603
2657
|
],
|
|
2604
|
-
|
|
2658
|
+
reviewQuestions: [
|
|
2605
2659
|
"manual-only invocation",
|
|
2606
2660
|
"Portal output containment",
|
|
2607
2661
|
"Markdown canonical statement",
|
|
@@ -2804,7 +2858,7 @@ ${renderLaneClassificationRuleBlock(config)}
|
|
|
2804
2858
|
- may write canonical truth docs and ${config.truthmark.paths.routesIndex} or relevant child route files only
|
|
2805
2859
|
- must not write functional code
|
|
2806
2860
|
- 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
|
|
2807
|
-
-
|
|
2861
|
+
- stop and recommend Truth Structure when routing repair is unsafe, ambiguous, or outside the task boundary
|
|
2808
2862
|
- keep feature README.md files as indexes rather than truth-document targets
|
|
2809
2863
|
- create or update bounded leaf truth docs when behavior does not fit an existing leaf doc
|
|
2810
2864
|
- write product capability/boundary truth under ${config.truthmark.paths.productTruthRoot} when documenting product promise, boundary, rationale, or user/stakeholder value
|
|
@@ -2815,7 +2869,7 @@ ${renderLaneClassificationRuleBlock(config)}
|
|
|
2815
2869
|
- preserve unrelated authored content
|
|
2816
2870
|
${renderTruthDocOwnershipGateSection(
|
|
2817
2871
|
"the implemented behavior and candidate truth docs",
|
|
2818
|
-
"if the target doc is broad, mixed-owner, index-like, or the documented behavior spans independent owners, run Truth Structure first when safe and in scope; otherwise
|
|
2872
|
+
"if the target doc is broad, mixed-owner, index-like, or the documented behavior spans independent owners, run Truth Structure first when safe and in scope; otherwise stop and recommend Truth Structure"
|
|
2819
2873
|
)}
|
|
2820
2874
|
${TRUTH_DOC_DECISION_RATIONALE_PRESERVATION_INSTRUCTIONS}
|
|
2821
2875
|
${renderRouteFirstEvidenceGateSection(
|
|
@@ -2838,9 +2892,9 @@ Helper status reporting:
|
|
|
2838
2892
|
- Helper output is derived evidence and never replaces direct checkout inspection, evidence review, or parent acceptance.
|
|
2839
2893
|
Parent post-document verification:
|
|
2840
2894
|
- verify only truth docs and leased truth routing files changed during document work
|
|
2841
|
-
-
|
|
2842
|
-
- for each write lease, validate the worker report against the actual worker diff, allowedWrites, forbiddenWrites, identity fields, filesChanged, offLeaseChanges, blockers, and
|
|
2843
|
-
- verify the final report records ownership review, structure requirement, restructure, routing update, or
|
|
2895
|
+
- stop on functional code, generated host surfaces, or unrelated diffs caused by document work
|
|
2896
|
+
- for each write lease, validate the worker report against the actual worker diff, allowedWrites, forbiddenWrites, identity fields, filesChanged, offLeaseChanges, blockers, and expected report fields before accepting it
|
|
2897
|
+
- verify the final report records ownership review, structure requirement, restructure, routing update, or manual handoff reason when applicable`;
|
|
2844
2898
|
};
|
|
2845
2899
|
|
|
2846
2900
|
// src/agents/truth-preview.ts
|
|
@@ -2877,7 +2931,7 @@ Suggested subagent use:
|
|
|
2877
2931
|
- write workers: none in Preview
|
|
2878
2932
|
- leases needed: none in Preview
|
|
2879
2933
|
|
|
2880
|
-
|
|
2934
|
+
Manual handoff questions:
|
|
2881
2935
|
- none identified in preview
|
|
2882
2936
|
|
|
2883
2937
|
Handoff:
|
|
@@ -2891,7 +2945,7 @@ Invocations: ${TRUTH_PREVIEW_EXPLICIT_INVOCATIONS}
|
|
|
2891
2945
|
Truth Preview is read-only. Its report is intended, not authorized.
|
|
2892
2946
|
|
|
2893
2947
|
Purpose:
|
|
2894
|
-
- preview the likely Truthmark workflow, route owner, target files, expected write classes, suggested subagent use, and
|
|
2948
|
+
- preview the likely Truthmark workflow, route owner, target files, expected write classes, suggested subagent use, and manual handoff questions before edits happen
|
|
2895
2949
|
- report likely product lane impact, engineering lane impact, target docs, and ambiguity before edits
|
|
2896
2950
|
- hand off to the selected workflow after user approval
|
|
2897
2951
|
- keep the selector thin so agents can avoid loading or acting through heavier workflows prematurely
|
|
@@ -2931,7 +2985,7 @@ var renderTruthmarkPortalProcedureBody = (config = defaultAgentConfig()) => {
|
|
|
2931
2985
|
const template = config.truthmark.paths.portalTemplate;
|
|
2932
2986
|
return `# Truthmark Portal
|
|
2933
2987
|
|
|
2934
|
-
Truthmark Portal is a manual-only presentation workflow. It is never
|
|
2988
|
+
Truthmark Portal is a manual-only presentation workflow. It is never an automatic completion workflow, never Truth Sync, and runs only when the user explicitly asks to generate, refresh, or update the committed static HTML Portal.
|
|
2935
2989
|
|
|
2936
2990
|
Invocations: ${TRUTHMARK_PORTAL_EXPLICIT_INVOCATIONS}
|
|
2937
2991
|
|
|
@@ -3044,7 +3098,7 @@ Use when a user asks to onboard a new code area into Truthmark, a new package, c
|
|
|
3044
3098
|
Do:
|
|
3045
3099
|
- inspect the named code area
|
|
3046
3100
|
- infer bounded product or behavior ownership
|
|
3047
|
-
- choose the owning route when ownership is clear; otherwise propose the route and
|
|
3101
|
+
- choose the owning route when ownership is clear; otherwise propose the route and stop for manual review
|
|
3048
3102
|
- create or update the child route entry or file
|
|
3049
3103
|
- create starter truth docs only where current truth is missing
|
|
3050
3104
|
- report the initial truth boundary
|
|
@@ -3060,7 +3114,7 @@ Inspect controllers, routes, handlers, services, packages, tests, existing truth
|
|
|
3060
3114
|
When topology pressure exists, repair structure before creating or extending truth docs.
|
|
3061
3115
|
${renderTruthDocOwnershipGateSection(
|
|
3062
3116
|
"candidate route owners and current truth docs",
|
|
3063
|
-
"if a truth doc mixes independent owners, route ownership is broad, or a split is required for bounded ownership, split and reroute into bounded truth docs when safe; otherwise
|
|
3117
|
+
"if a truth doc mixes independent owners, route ownership is broad, or a split is required for bounded ownership, split and reroute into bounded truth docs when safe; otherwise stop with manual-review files"
|
|
3064
3118
|
)}
|
|
3065
3119
|
${TRUTH_DOC_DECISION_RATIONALE_PRESERVATION_INSTRUCTIONS}
|
|
3066
3120
|
Topology pressure signals:
|
|
@@ -3134,6 +3188,60 @@ var parseOptionalBulletSection = (source, title) => {
|
|
|
3134
3188
|
}
|
|
3135
3189
|
return parseBulletLines(section);
|
|
3136
3190
|
};
|
|
3191
|
+
var parseRequiredBulletSection = (source, title) => {
|
|
3192
|
+
const section = findSection(source, title);
|
|
3193
|
+
if (!section) {
|
|
3194
|
+
throw new Error(`${title} section is required.`);
|
|
3195
|
+
}
|
|
3196
|
+
const entries = parseBulletLines(section);
|
|
3197
|
+
if (entries.length === 0) {
|
|
3198
|
+
throw new Error(`${title} must include at least one bullet.`);
|
|
3199
|
+
}
|
|
3200
|
+
return entries;
|
|
3201
|
+
};
|
|
3202
|
+
var formatIntentValues = (values) => values.join(" / ");
|
|
3203
|
+
var renderSyncIntentSection = (intent) => {
|
|
3204
|
+
return [
|
|
3205
|
+
"Sync Intent:",
|
|
3206
|
+
`- Changed code reviewed: ${formatIntentValues(intent.changedCodeReviewed)}`,
|
|
3207
|
+
`- Affected route/truth owner: ${formatIntentValues(intent.affectedRouteOrTruthOwner)}`,
|
|
3208
|
+
`- Target truth docs: ${formatIntentValues(intent.targetTruthDocs)}`,
|
|
3209
|
+
`- Intended update: ${formatIntentValues(intent.intendedUpdate)}`,
|
|
3210
|
+
`- Evidence to verify: ${formatIntentValues(intent.evidenceToVerify)}`,
|
|
3211
|
+
`- User-provided decisions/rationale: ${formatIntentValues(intent.userProvidedDecisionRationale)}`,
|
|
3212
|
+
`- No-update-needed rationale: ${formatIntentValues(intent.noUpdateNeededRationale)}`,
|
|
3213
|
+
`- Blockers: ${formatIntentValues(intent.blockers)}`
|
|
3214
|
+
].join("\n");
|
|
3215
|
+
};
|
|
3216
|
+
var parseIntentValues = (value) => {
|
|
3217
|
+
return value.split(" / ").map((item) => item.trim()).filter(hasContent);
|
|
3218
|
+
};
|
|
3219
|
+
var parseSyncIntentSection = (source) => {
|
|
3220
|
+
const entries = parseOptionalBulletSection(source, "Sync Intent");
|
|
3221
|
+
if (entries === void 0) {
|
|
3222
|
+
return void 0;
|
|
3223
|
+
}
|
|
3224
|
+
const values = /* @__PURE__ */ new Map();
|
|
3225
|
+
for (const entry of entries) {
|
|
3226
|
+
const separator = entry.indexOf(":");
|
|
3227
|
+
if (separator === -1) {
|
|
3228
|
+
continue;
|
|
3229
|
+
}
|
|
3230
|
+
values.set(entry.slice(0, separator), entry.slice(separator + 1).trim());
|
|
3231
|
+
}
|
|
3232
|
+
return {
|
|
3233
|
+
changedCodeReviewed: parseIntentValues(values.get("Changed code reviewed") ?? ""),
|
|
3234
|
+
affectedRouteOrTruthOwner: parseIntentValues(values.get("Affected route/truth owner") ?? ""),
|
|
3235
|
+
targetTruthDocs: parseIntentValues(values.get("Target truth docs") ?? ""),
|
|
3236
|
+
intendedUpdate: parseIntentValues(values.get("Intended update") ?? ""),
|
|
3237
|
+
evidenceToVerify: parseIntentValues(values.get("Evidence to verify") ?? ""),
|
|
3238
|
+
userProvidedDecisionRationale: parseIntentValues(
|
|
3239
|
+
values.get("User-provided decisions/rationale") ?? ""
|
|
3240
|
+
),
|
|
3241
|
+
noUpdateNeededRationale: parseIntentValues(values.get("No-update-needed rationale") ?? ""),
|
|
3242
|
+
blockers: parseIntentValues(values.get("Blockers") ?? "")
|
|
3243
|
+
};
|
|
3244
|
+
};
|
|
3137
3245
|
var isClaimEvidenceResult = (value) => {
|
|
3138
3246
|
return ["supported", "narrowed", "removed", "blocked"].includes(value);
|
|
3139
3247
|
};
|
|
@@ -3176,8 +3284,13 @@ var renderTruthSyncCompletedReport = (input) => {
|
|
|
3176
3284
|
return [
|
|
3177
3285
|
"Truth Sync: completed",
|
|
3178
3286
|
renderBulletSection("Changed code reviewed", input.changedCode),
|
|
3287
|
+
...input.syncIntent === void 0 ? [] : [renderSyncIntentSection(input.syncIntent)],
|
|
3179
3288
|
renderBulletSection("Ownership reviewed", input.ownershipReviewed),
|
|
3180
3289
|
renderBulletSection("Truth docs updated", input.truthDocsUpdated),
|
|
3290
|
+
renderBulletSection(
|
|
3291
|
+
"Decision/rationale captured",
|
|
3292
|
+
input.decisionRationaleCaptured ?? ["none provided in task conversation"]
|
|
3293
|
+
),
|
|
3181
3294
|
renderClaimEvidenceCheckedSection(input.evidenceChecked),
|
|
3182
3295
|
...input.helperScripts === void 0 ? [] : [renderBulletSection("Helper scripts", input.helperScripts)],
|
|
3183
3296
|
renderBulletSection("Notes", input.notes)
|
|
@@ -3188,11 +3301,17 @@ var parseTruthSyncReport = (source) => {
|
|
|
3188
3301
|
throw new Error("Only completed Truth Sync reports can be parsed.");
|
|
3189
3302
|
}
|
|
3190
3303
|
const helperScripts = parseOptionalBulletSection(source, "Helper scripts");
|
|
3304
|
+
const syncIntent = parseSyncIntentSection(source);
|
|
3191
3305
|
return {
|
|
3192
3306
|
status: "completed",
|
|
3193
3307
|
changedCode: parseBulletSection(source, "Changed code reviewed"),
|
|
3308
|
+
...syncIntent === void 0 ? {} : { syncIntent },
|
|
3194
3309
|
ownershipReviewed: parseBulletSection(source, "Ownership reviewed"),
|
|
3195
3310
|
truthDocsUpdated: parseBulletSection(source, "Truth docs updated"),
|
|
3311
|
+
decisionRationaleCaptured: parseRequiredBulletSection(
|
|
3312
|
+
source,
|
|
3313
|
+
"Decision/rationale captured"
|
|
3314
|
+
),
|
|
3196
3315
|
evidenceChecked: parseEvidenceCheckedSection(source),
|
|
3197
3316
|
...helperScripts === void 0 ? {} : { helperScripts },
|
|
3198
3317
|
notes: parseBulletSection(source, "Notes")
|
|
@@ -3216,6 +3335,17 @@ var renderTruthSyncBlockedReport = (input) => {
|
|
|
3216
3335
|
|
|
3217
3336
|
// src/agents/truth-sync.ts
|
|
3218
3337
|
var TRUTH_SYNC_EXPLICIT_INVOCATIONS = "OpenCode /skill truthmark-sync; Codex /truthmark-sync or $truthmark-sync; Claude Code /truthmark-sync; GitHub Copilot /truthmark-sync; Gemini CLI /truthmark:sync.";
|
|
3338
|
+
var renderTruthSyncProductDecisionRuleBlock = (config) => {
|
|
3339
|
+
return renderBulletBlock(
|
|
3340
|
+
[
|
|
3341
|
+
"ask whether a user-visible promise, capability boundary, API contract, acceptance criterion, or explicit user/product evidence changed",
|
|
3342
|
+
`if yes, update or route product truth under ${config.truthmark.paths.productTruthRoot} as well as engineering truth under ${config.truthmark.paths.engineeringTruthRoot}`,
|
|
3343
|
+
`if no, default to engineering truth under ${config.truthmark.paths.engineeringTruthRoot} for internal implementation changes`,
|
|
3344
|
+
"when both lanes change, keep separate product and engineering docs cross-linked through route YAML with realized_by and realizes",
|
|
3345
|
+
"when ownership is ambiguous, stop or route to Truth Structure instead of writing a mixed document"
|
|
3346
|
+
].join("\n")
|
|
3347
|
+
);
|
|
3348
|
+
};
|
|
3219
3349
|
var renderTruthSyncProcedureBody = (config = defaultAgentConfig(), options = {}) => {
|
|
3220
3350
|
const workflow = getTruthmarkWorkflow("truthmark-sync");
|
|
3221
3351
|
const claudeSubagentMode = options.includeClaudeSubagentMode ? `${renderClaudeSubagentModeSection(
|
|
@@ -3245,7 +3375,7 @@ var renderTruthSyncProcedureBody = (config = defaultAgentConfig(), options = {})
|
|
|
3245
3375
|
const subagentMode = `${claudeSubagentMode}${codexSubagentMode}${copilotCustomAgentMode}${openCodeSubagentMode}`;
|
|
3246
3376
|
return `Use this skill automatically before finishing when functional code changed since the last successful Truth Sync. Also run it immediately when the user explicitly invokes Truth Sync.
|
|
3247
3377
|
Invocations: ${TRUTH_SYNC_EXPLICIT_INVOCATIONS}
|
|
3248
|
-
Explicit invocation runs immediately. Later functional-code changes
|
|
3378
|
+
Explicit invocation runs immediately. Later functional-code changes need a fresh finish-time review, and an earlier explicit run satisfies the finish-time review only if no later functional-code changes occur.
|
|
3249
3379
|
Skip when changes are documentation-only, formatting-only, clearly behavior-preserving renames with no truth impact, when no Truthmark config exists yet, or when there are no functional code changes.
|
|
3250
3380
|
Parent workflow:
|
|
3251
3381
|
1. Inspect git status, staged changes, unstaged changes, and untracked files directly.
|
|
@@ -3253,16 +3383,27 @@ Parent workflow:
|
|
|
3253
3383
|
3. Identify functional-code changes and the nearest truth docs or routing repairs.
|
|
3254
3384
|
4. Evidence authority:
|
|
3255
3385
|
${renderBulletBlock(EVIDENCE_AUTHORITY_INSTRUCTIONS)}
|
|
3256
|
-
5.
|
|
3257
|
-
${
|
|
3258
|
-
6.
|
|
3259
|
-
7.
|
|
3260
|
-
8.
|
|
3261
|
-
|
|
3386
|
+
5. Product truth decision:
|
|
3387
|
+
${renderTruthSyncProductDecisionRuleBlock(config)}
|
|
3388
|
+
6. Capture decision context from the task conversation: ask whether the user provided a product or technical decision, rationale, constraint, tradeoff, rejection reason, or scope boundary. Preserve concise user-provided decision rationale in Sync Intent before truth edits, route it to Product Decisions, Engineering Decisions, Rationale, Capability Scope, Non-Goals, Maintenance Notes, or the relevant workflow/contract section, and report whether it was placed, skipped because none was provided, or needs manual handoff.
|
|
3389
|
+
7. Update engineering truth first after code changes. Product truth is opt-in for externally visible promises, product boundaries, APIs, acceptance criteria, or explicit user/product evidence.
|
|
3390
|
+
8. Code verification is parent-owned: follow repository instructions and task context, and report what ran or why it did not run.
|
|
3391
|
+
9. Dispatch bounded Truth Sync workers only when the host supports subagent dispatch and the acting agent chooses that path; otherwise execute the same sync task inline.
|
|
3392
|
+
10. Fill Sync Intent before editing truth docs or truth routing files:
|
|
3393
|
+
- Changed code reviewed: functional files, tests, configs, generated outputs, or other implementation evidence inspected
|
|
3394
|
+
- Affected route/truth owner: bounded route area or canonical truth owner that maps the change
|
|
3395
|
+
- Target truth docs: docs expected to change, or docs reviewed and left unchanged
|
|
3396
|
+
- Intended update: claim/doc/routing update planned before writing
|
|
3397
|
+
- Evidence to verify: checkout evidence that will support, narrow, remove, or record each claim for manual handoff
|
|
3398
|
+
- User-provided decisions/rationale: decisions, rationale, constraints, tradeoffs, rejection reasons, or scope boundaries from the current task conversation, or "none provided"
|
|
3399
|
+
- No-update-needed rationale: why mapped truth is already current when no truth doc should change
|
|
3400
|
+
- Blockers: missing routing, ambiguous ownership, failed verification, unavailable evidence, or off-boundary write needs
|
|
3401
|
+
11. Only edit allowed truth docs/routes after Sync Intent is clear; if ownership is ambiguous, stop and recommend Truth Structure instead of guessing.
|
|
3402
|
+
${subagentMode}Topology review:
|
|
3262
3403
|
- before updating truth docs, verify the changed code resolves to a specific behavior-owned area and bounded truth owner
|
|
3263
3404
|
- if routing is missing, stale, broad, overloaded, catch-all route only, or cannot map changed code to a bounded truth owner, do not create another generic truth doc
|
|
3264
3405
|
- run Truth Structure before syncing when topology repair is safe and in scope
|
|
3265
|
-
-
|
|
3406
|
+
- stop and recommend Truth Structure when topology repair is unsafe, ambiguous, or outside the current task boundary
|
|
3266
3407
|
- report the route files and changed code paths that require structure repair
|
|
3267
3408
|
- README.md files are indexes, not Truth Sync targets
|
|
3268
3409
|
- must not append behavior details to a README.md index
|
|
@@ -3270,12 +3411,12 @@ ${subagentMode}Topology quality gate:
|
|
|
3270
3411
|
- write engineering truth under ${config.truthmark.paths.engineeringTruthRoot}; product truth updates under ${config.truthmark.paths.productTruthRoot} are allowed only for explicit current product behavior changes
|
|
3271
3412
|
${renderTruthDocOwnershipGateSection(
|
|
3272
3413
|
"changed functional files and impacted truth docs",
|
|
3273
|
-
"if an impacted doc is broad, mixed-owner, index-like, or the update spans independent behavior owners, run Truth Structure before syncing when safe and in scope; otherwise
|
|
3414
|
+
"if an impacted doc is broad, mixed-owner, index-like, or the update spans independent behavior owners, run Truth Structure before syncing when safe and in scope; otherwise stop and recommend Truth Structure"
|
|
3274
3415
|
)}
|
|
3275
3416
|
${TRUTH_DOC_DECISION_RATIONALE_PRESERVATION_INSTRUCTIONS}
|
|
3276
3417
|
${FEATURE_DOC_TEMPLATE_INSTRUCTIONS}
|
|
3277
3418
|
${renderTruthDocRestructureGateSection(
|
|
3278
|
-
"Truth Sync may restructure
|
|
3419
|
+
"Truth Sync may restructure leased canonical truth docs when the current sync evidence shows repository truth is stale, even when the doc is outside the initially affected route focus."
|
|
3279
3420
|
)}
|
|
3280
3421
|
${ARCHITECTURE_DOC_BOUNDARY_INSTRUCTIONS}
|
|
3281
3422
|
${renderRouteFirstEvidenceGateSection(
|
|
@@ -3298,13 +3439,13 @@ ${renderHierarchySummary(config)}
|
|
|
3298
3439
|
${DECISION_TRUTH_INSTRUCTIONS}
|
|
3299
3440
|
Parent post-sync verification:
|
|
3300
3441
|
- verify only truth docs and leased truth routing files changed during sync
|
|
3301
|
-
-
|
|
3302
|
-
-
|
|
3303
|
-
- for each write lease, validate the worker report against the actual worker diff, allowedWrites, forbiddenWrites, identity fields, filesChanged, offLeaseChanges, blockers, and
|
|
3442
|
+
- stop on any unrelated diff caused by the sync step
|
|
3443
|
+
- stop if functional code changed during sync
|
|
3444
|
+
- for each write lease, validate the worker report against the actual worker diff, allowedWrites, forbiddenWrites, identity fields, filesChanged, offLeaseChanges, blockers, and expected report fields before accepting it
|
|
3304
3445
|
- validate the final report against the structured Truth Sync report contract, including Claim, indented Evidence, and Result values supported, narrowed, removed, or blocked under Evidence checked
|
|
3305
|
-
- verify the updated docs correspond to
|
|
3306
|
-
- verify the final report records ownership review, structure requirement, split, restructure, or
|
|
3307
|
-
-
|
|
3446
|
+
- verify the updated docs correspond to reviewed checkout evidence, changed-code impact, or a recorded stale-truth correction made within the sync write lease
|
|
3447
|
+
- verify the final report records ownership review, structure requirement, split, restructure, or manual handoff reason when the ownership review applies
|
|
3448
|
+
- manual handoff outcomes must preserve the working tree as-is: no rollback, no post-block cleanup edits, and manual-review reporting of any remaining files`;
|
|
3308
3449
|
};
|
|
3309
3450
|
|
|
3310
3451
|
// src/agents/write-lease.ts
|
|
@@ -3522,7 +3663,7 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
3522
3663
|
use: () => "Use this skill only when the user explicitly asks to generate or refresh the committed static HTML Truthmark Portal.",
|
|
3523
3664
|
quickRules: (config) => [
|
|
3524
3665
|
"Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
|
|
3525
|
-
"Truthmark Portal is manual-only; never run it
|
|
3666
|
+
"Truthmark Portal is manual-only; never run it automatically at completion and never treat it as Truth Sync.",
|
|
3526
3667
|
"Markdown remains canonical; generated HTML is non-canonical presentation only.",
|
|
3527
3668
|
"Read Markdown directly; the workflow does not require the truthmark CLI or package.",
|
|
3528
3669
|
"Generate committed, generated non-canonical static files for humans.",
|
|
@@ -3556,8 +3697,27 @@ ${renderMarkdownExample(
|
|
|
3556
3697
|
${renderMarkdownExample(
|
|
3557
3698
|
renderTruthSyncCompletedReport({
|
|
3558
3699
|
changedCode: ["src/auth/session.ts"],
|
|
3700
|
+
syncIntent: {
|
|
3701
|
+
changedCodeReviewed: ["src/auth/session.ts"],
|
|
3702
|
+
affectedRouteOrTruthOwner: [config.truthmark.paths.routesIndex],
|
|
3703
|
+
targetTruthDocs: [
|
|
3704
|
+
`${engineeringTruthRoot}/repository/bootstrap-routing.md`
|
|
3705
|
+
],
|
|
3706
|
+
intendedUpdate: ["Update session timeout behavior."],
|
|
3707
|
+
evidenceToVerify: [
|
|
3708
|
+
"src/auth/session.ts:12",
|
|
3709
|
+
`${config.truthmark.paths.routesIndex}:11`
|
|
3710
|
+
],
|
|
3711
|
+
userProvidedDecisionRationale: [
|
|
3712
|
+
"User rationale: session timeout behavior changed for internal implementation consistency"
|
|
3713
|
+
],
|
|
3714
|
+
noUpdateNeededRationale: ["not applicable; mapped truth is stale"],
|
|
3715
|
+
blockers: ["none"]
|
|
3716
|
+
},
|
|
3559
3717
|
ownershipReviewed: [config.truthmark.paths.routesIndex],
|
|
3560
|
-
truthDocsUpdated: [
|
|
3718
|
+
truthDocsUpdated: [
|
|
3719
|
+
`${engineeringTruthRoot}/repository/bootstrap-routing.md`
|
|
3720
|
+
],
|
|
3561
3721
|
evidenceChecked: [
|
|
3562
3722
|
{
|
|
3563
3723
|
claim: "Session timeout behavior is documented in the mapped repository truth doc.",
|
|
@@ -3568,9 +3728,10 @@ ${renderMarkdownExample(
|
|
|
3568
3728
|
result: "supported"
|
|
3569
3729
|
}
|
|
3570
3730
|
],
|
|
3571
|
-
|
|
3572
|
-
"
|
|
3731
|
+
decisionRationaleCaptured: [
|
|
3732
|
+
"Placed user rationale in the mapped engineering truth doc under Engineering Decisions/Rationale."
|
|
3573
3733
|
],
|
|
3734
|
+
helperScripts: ["validate-write-lease: skipped, no write lease used"],
|
|
3574
3735
|
notes: ["Updated session timeout behavior."]
|
|
3575
3736
|
})
|
|
3576
3737
|
)}
|
|
@@ -3643,7 +3804,7 @@ var renderWorkflowSupportParts = (workflowId, config) => {
|
|
|
3643
3804
|
var renderSkillSupportFile = (title, body) => {
|
|
3644
3805
|
return `# ${title}
|
|
3645
3806
|
|
|
3646
|
-
|
|
3807
|
+
Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
3647
3808
|
|
|
3648
3809
|
${body}
|
|
3649
3810
|
`;
|
|
@@ -3667,7 +3828,7 @@ var renderHelperManifest = (helpers) => {
|
|
|
3667
3828
|
)
|
|
3668
3829
|
};
|
|
3669
3830
|
return [
|
|
3670
|
-
`#
|
|
3831
|
+
`# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.`,
|
|
3671
3832
|
stringify2(manifest, { lineWidth: 0 })
|
|
3672
3833
|
].join("\n");
|
|
3673
3834
|
};
|
|
@@ -3678,12 +3839,12 @@ var renderHelperPolicySupport = (helpers) => {
|
|
|
3678
3839
|
).join("\n");
|
|
3679
3840
|
return renderSkillSupportFile(
|
|
3680
3841
|
"Optional Helper CLI Policy",
|
|
3681
|
-
`Optional helper CLI commands may collect deterministic checkout facts or validate artifacts. If the Truthmark CLI is unavailable or
|
|
3842
|
+
`Optional helper CLI commands may collect deterministic checkout facts or validate artifacts. If the Truthmark CLI is unavailable or cannot return the declared helper output, continue manually using this procedure and report which helper was skipped. Helper output is derived evidence; it does not override direct checkout inspection, workflow write boundaries, or parent acceptance.
|
|
3682
3843
|
|
|
3683
3844
|
Runner detection:
|
|
3684
|
-
- Check the declared Truthmark CLI runner before invoking a helper.
|
|
3845
|
+
- Check that the declared Truthmark CLI runner is available before invoking a helper.
|
|
3685
3846
|
- Invoke helpers through the installed \`truthmark validate ... --json\` CLI command using argv-style arguments from helper-manifest.yml.
|
|
3686
|
-
- If unavailable or
|
|
3847
|
+
- If unavailable, failing, or returning incompatible output, treat the helper as skipped and use the manual fallback.
|
|
3687
3848
|
- Do not fail the workflow solely because a helper cannot run.
|
|
3688
3849
|
|
|
3689
3850
|
Available helpers:
|
|
@@ -3721,7 +3882,7 @@ var renderWorkflowEntrypoint = (workflowId, config, supportFiles, host) => {
|
|
|
3721
3882
|
const definition = WORKFLOW_PACKAGE_DEFINITIONS[workflowId];
|
|
3722
3883
|
const supportFileUsage = (supportFile) => {
|
|
3723
3884
|
if (supportFile === "support/procedure.md") {
|
|
3724
|
-
return "read before edits or detailed auditing; contains core
|
|
3885
|
+
return "read before edits or detailed auditing; contains core review questions";
|
|
3725
3886
|
}
|
|
3726
3887
|
if (supportFile === "support/report-template.md") {
|
|
3727
3888
|
return "read before the final report";
|
|
@@ -3744,7 +3905,6 @@ name: ${workflowId}
|
|
|
3744
3905
|
description: ${workflow.description}
|
|
3745
3906
|
argument-hint: ${definition.argumentHint}
|
|
3746
3907
|
user-invocable: true
|
|
3747
|
-
truthmark-version: ${TRUTHMARK_VERSION}
|
|
3748
3908
|
---
|
|
3749
3909
|
|
|
3750
3910
|
# ${definition.title}
|
|
@@ -3865,8 +4025,8 @@ var renderTruthmarkSkillPackage = ({
|
|
|
3865
4025
|
}
|
|
3866
4026
|
return files;
|
|
3867
4027
|
};
|
|
3868
|
-
var normalizeOpenCodePermissionPath = (
|
|
3869
|
-
const normalized =
|
|
4028
|
+
var normalizeOpenCodePermissionPath = (path12) => {
|
|
4029
|
+
const normalized = path12.replace(/\\/gu, "/").replace(/^\.\//u, "").replace(/\/+$/u, "");
|
|
3870
4030
|
return normalized === "" ? "." : normalized;
|
|
3871
4031
|
};
|
|
3872
4032
|
var appendOpenCodePermissionGlob = (root, glob) => {
|
|
@@ -3972,7 +4132,7 @@ var renderCodexReadOnlyAgent = ({
|
|
|
3972
4132
|
nicknameCandidates,
|
|
3973
4133
|
developerInstructions
|
|
3974
4134
|
}) => {
|
|
3975
|
-
return `#
|
|
4135
|
+
return `# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
3976
4136
|
name = ${renderTomlString(name)}
|
|
3977
4137
|
description = ${renderTomlString(description)}
|
|
3978
4138
|
sandbox_mode = "read-only"
|
|
@@ -3988,7 +4148,7 @@ var renderCodexWriteAgent = ({
|
|
|
3988
4148
|
nicknameCandidates,
|
|
3989
4149
|
developerInstructions
|
|
3990
4150
|
}) => {
|
|
3991
|
-
return `#
|
|
4151
|
+
return `# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
3992
4152
|
name = ${renderTomlString(name)}
|
|
3993
4153
|
description = ${renderTomlString(description)}
|
|
3994
4154
|
sandbox_mode = "workspace-write"
|
|
@@ -4010,7 +4170,7 @@ description: ${description}
|
|
|
4010
4170
|
tools: [read, search]
|
|
4011
4171
|
---
|
|
4012
4172
|
|
|
4013
|
-
#
|
|
4173
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4014
4174
|
|
|
4015
4175
|
${agentInstructions}
|
|
4016
4176
|
`;
|
|
@@ -4026,7 +4186,7 @@ description: ${description}
|
|
|
4026
4186
|
tools: [read, search, edit]
|
|
4027
4187
|
---
|
|
4028
4188
|
|
|
4029
|
-
#
|
|
4189
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4030
4190
|
|
|
4031
4191
|
${instructions}
|
|
4032
4192
|
`;
|
|
@@ -4044,7 +4204,7 @@ kind: local
|
|
|
4044
4204
|
tools: [read_file, grep_search]
|
|
4045
4205
|
---
|
|
4046
4206
|
|
|
4047
|
-
#
|
|
4207
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4048
4208
|
|
|
4049
4209
|
Manual invocation: @${copilotName}
|
|
4050
4210
|
|
|
@@ -4063,7 +4223,7 @@ kind: local
|
|
|
4063
4223
|
tools: [read_file, grep_search, write_file]
|
|
4064
4224
|
---
|
|
4065
4225
|
|
|
4066
|
-
#
|
|
4226
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4067
4227
|
|
|
4068
4228
|
Manual invocation: @${copilotName} with an explicit parent write lease.
|
|
4069
4229
|
|
|
@@ -4082,7 +4242,7 @@ description: ${description}
|
|
|
4082
4242
|
tools: Read, Grep, Glob, LS
|
|
4083
4243
|
---
|
|
4084
4244
|
|
|
4085
|
-
#
|
|
4245
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4086
4246
|
|
|
4087
4247
|
Manual invocation: use the ${copilotName} subagent.
|
|
4088
4248
|
|
|
@@ -4100,7 +4260,7 @@ description: ${description}
|
|
|
4100
4260
|
tools: Read, Grep, Glob, LS, Edit, MultiEdit
|
|
4101
4261
|
---
|
|
4102
4262
|
|
|
4103
|
-
#
|
|
4263
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4104
4264
|
|
|
4105
4265
|
Manual invocation: use the ${copilotName} subagent with an explicit parent write lease.
|
|
4106
4266
|
|
|
@@ -4233,7 +4393,7 @@ permission:
|
|
|
4233
4393
|
"grep *": allow
|
|
4234
4394
|
---
|
|
4235
4395
|
|
|
4236
|
-
#
|
|
4396
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4237
4397
|
|
|
4238
4398
|
Manual invocation: @${invocation}
|
|
4239
4399
|
|
|
@@ -4268,7 +4428,7 @@ ${editAllowRules}
|
|
|
4268
4428
|
"git diff*": allow
|
|
4269
4429
|
---
|
|
4270
4430
|
|
|
4271
|
-
#
|
|
4431
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4272
4432
|
|
|
4273
4433
|
Manual invocation: @${invocation}
|
|
4274
4434
|
|
|
@@ -4319,7 +4479,6 @@ policy:
|
|
|
4319
4479
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4320
4480
|
|
|
4321
4481
|
truthmark:
|
|
4322
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4323
4482
|
refresh_command: "truthmark init"
|
|
4324
4483
|
`;
|
|
4325
4484
|
};
|
|
@@ -4334,7 +4493,6 @@ policy:
|
|
|
4334
4493
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4335
4494
|
|
|
4336
4495
|
truthmark:
|
|
4337
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4338
4496
|
refresh_command: "truthmark init"
|
|
4339
4497
|
`;
|
|
4340
4498
|
};
|
|
@@ -4349,7 +4507,6 @@ policy:
|
|
|
4349
4507
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4350
4508
|
|
|
4351
4509
|
truthmark:
|
|
4352
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4353
4510
|
refresh_command: "truthmark init"
|
|
4354
4511
|
`;
|
|
4355
4512
|
};
|
|
@@ -4373,7 +4530,7 @@ Workflow:
|
|
|
4373
4530
|
3. ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
|
|
4374
4531
|
${renderTruthDocOwnershipGateSection(
|
|
4375
4532
|
"source truth docs before writing code",
|
|
4376
|
-
"if a source truth doc is broad, mixed-owner, index-like, unrouteable, stale, or conflicts with implementation evidence,
|
|
4533
|
+
"if a source truth doc is broad, mixed-owner, index-like, unrouteable, stale, or conflicts with implementation evidence, stop before writing code and recommend Truth Structure or Truth Document"
|
|
4377
4534
|
)}
|
|
4378
4535
|
4. Update functional code only so implementation matches bounded, current truth claims from the source docs.
|
|
4379
4536
|
5. Do not edit truth docs or truth routing while realizing those docs.
|
|
@@ -4398,7 +4555,6 @@ policy:
|
|
|
4398
4555
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4399
4556
|
|
|
4400
4557
|
truthmark:
|
|
4401
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4402
4558
|
refresh_command: "truthmark init"
|
|
4403
4559
|
`;
|
|
4404
4560
|
};
|
|
@@ -4413,7 +4569,6 @@ policy:
|
|
|
4413
4569
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4414
4570
|
|
|
4415
4571
|
truthmark:
|
|
4416
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4417
4572
|
refresh_command: "truthmark init"
|
|
4418
4573
|
`;
|
|
4419
4574
|
};
|
|
@@ -4428,7 +4583,6 @@ policy:
|
|
|
4428
4583
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4429
4584
|
|
|
4430
4585
|
truthmark:
|
|
4431
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4432
4586
|
refresh_command: "truthmark init"
|
|
4433
4587
|
`;
|
|
4434
4588
|
};
|
|
@@ -4443,7 +4597,6 @@ policy:
|
|
|
4443
4597
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4444
4598
|
|
|
4445
4599
|
truthmark:
|
|
4446
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4447
4600
|
refresh_command: "truthmark init"
|
|
4448
4601
|
`;
|
|
4449
4602
|
};
|
|
@@ -4994,8 +5147,8 @@ var geminiFiles = (config, block) => {
|
|
|
4994
5147
|
return files;
|
|
4995
5148
|
};
|
|
4996
5149
|
var instructionBlockFiles = (paths, block) => {
|
|
4997
|
-
return paths.map((
|
|
4998
|
-
path:
|
|
5150
|
+
return paths.map((path12) => ({
|
|
5151
|
+
path: path12,
|
|
4999
5152
|
content: block,
|
|
5000
5153
|
managedBlock: true
|
|
5001
5154
|
}));
|
|
@@ -5119,16 +5272,16 @@ var upsertManagedBlock = (existingContent, block) => {
|
|
|
5119
5272
|
|
|
5120
5273
|
${block}`;
|
|
5121
5274
|
};
|
|
5122
|
-
var writeManagedAgentsFile = async (rootDir,
|
|
5275
|
+
var writeManagedAgentsFile = async (rootDir, path12 = "AGENTS.md", block) => {
|
|
5123
5276
|
let existingContent = null;
|
|
5124
5277
|
try {
|
|
5125
|
-
existingContent = await
|
|
5278
|
+
existingContent = await fs6.readFile(resolveRepoPath(rootDir, path12), "utf8");
|
|
5126
5279
|
} catch (error) {
|
|
5127
5280
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
5128
5281
|
throw error;
|
|
5129
5282
|
}
|
|
5130
5283
|
}
|
|
5131
|
-
return writeRepoFile(rootDir,
|
|
5284
|
+
return writeRepoFile(rootDir, path12, upsertManagedBlock(existingContent, block));
|
|
5132
5285
|
};
|
|
5133
5286
|
var diagnosticCategoryForPath = (filePath, config) => {
|
|
5134
5287
|
if (filePath === "AGENTS.md") {
|
|
@@ -5220,7 +5373,7 @@ var runInit = async (cwd) => {
|
|
|
5220
5373
|
};
|
|
5221
5374
|
|
|
5222
5375
|
// src/checks/branch-scope.ts
|
|
5223
|
-
import
|
|
5376
|
+
import fs7 from "fs/promises";
|
|
5224
5377
|
import fg from "fast-glob";
|
|
5225
5378
|
|
|
5226
5379
|
// src/markdown/hash.ts
|
|
@@ -5276,7 +5429,7 @@ var getBranchScopeData = async (cwd) => {
|
|
|
5276
5429
|
}
|
|
5277
5430
|
for (const relativePath of [...relevantFiles].sort()) {
|
|
5278
5431
|
try {
|
|
5279
|
-
const source = await
|
|
5432
|
+
const source = await fs7.readFile(resolveWorktreePath(repository, relativePath), "utf8");
|
|
5280
5433
|
relevantFileHashes[relativePath] = hashText(source);
|
|
5281
5434
|
} catch (error) {
|
|
5282
5435
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
@@ -5293,14 +5446,14 @@ var getBranchScopeData = async (cwd) => {
|
|
|
5293
5446
|
};
|
|
5294
5447
|
|
|
5295
5448
|
// src/checks/authority.ts
|
|
5296
|
-
import
|
|
5449
|
+
import fs8 from "fs/promises";
|
|
5297
5450
|
import fg2 from "fast-glob";
|
|
5298
5451
|
var looksLikeGlob = (pattern) => {
|
|
5299
5452
|
return /[*?[\]{}()!+@]/u.test(pattern);
|
|
5300
5453
|
};
|
|
5301
5454
|
var pathExists = async (absolutePath) => {
|
|
5302
5455
|
try {
|
|
5303
|
-
await
|
|
5456
|
+
await fs8.stat(absolutePath);
|
|
5304
5457
|
return true;
|
|
5305
5458
|
} catch (error) {
|
|
5306
5459
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
@@ -5392,10 +5545,55 @@ var checkAuthority = async (rootDir, config) => {
|
|
|
5392
5545
|
};
|
|
5393
5546
|
|
|
5394
5547
|
// src/checks/frontmatter.ts
|
|
5395
|
-
import
|
|
5548
|
+
import fs9 from "fs/promises";
|
|
5549
|
+
|
|
5550
|
+
// src/markdown/frontmatter.ts
|
|
5551
|
+
import { parse as parse3 } from "yaml";
|
|
5552
|
+
var openingDelimiterPattern = /^(?:\uFEFF)?---[ \t]*(?:\r?\n|$)/u;
|
|
5553
|
+
var closingDelimiterPattern = /^---[ \t]*(?:\r?\n|$)/mu;
|
|
5554
|
+
var asRecord = (value) => {
|
|
5555
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
5556
|
+
return value;
|
|
5557
|
+
}
|
|
5558
|
+
return {};
|
|
5559
|
+
};
|
|
5560
|
+
var parseFrontmatter = (source, options = {}) => {
|
|
5561
|
+
const openingMatch = openingDelimiterPattern.exec(source);
|
|
5562
|
+
if (!openingMatch) {
|
|
5563
|
+
return {
|
|
5564
|
+
data: {},
|
|
5565
|
+
content: source.replace(/^\uFEFF/u, "")
|
|
5566
|
+
};
|
|
5567
|
+
}
|
|
5568
|
+
const bodyStart = openingMatch[0].length;
|
|
5569
|
+
const bodyAndContent = source.slice(bodyStart);
|
|
5570
|
+
const closingMatch = closingDelimiterPattern.exec(bodyAndContent);
|
|
5571
|
+
if (!closingMatch || typeof closingMatch.index !== "number") {
|
|
5572
|
+
return {
|
|
5573
|
+
data: {},
|
|
5574
|
+
content: source.replace(/^\uFEFF/u, "")
|
|
5575
|
+
};
|
|
5576
|
+
}
|
|
5577
|
+
const yamlSource = bodyAndContent.slice(0, closingMatch.index);
|
|
5578
|
+
const content = bodyAndContent.slice(closingMatch.index + closingMatch[0].length);
|
|
5579
|
+
let data = {};
|
|
5580
|
+
if (yamlSource.trim().length > 0) {
|
|
5581
|
+
try {
|
|
5582
|
+
data = asRecord(parse3(yamlSource));
|
|
5583
|
+
} catch (error) {
|
|
5584
|
+
if (options.throwOnInvalid) {
|
|
5585
|
+
throw error;
|
|
5586
|
+
}
|
|
5587
|
+
data = {};
|
|
5588
|
+
}
|
|
5589
|
+
}
|
|
5590
|
+
return {
|
|
5591
|
+
data,
|
|
5592
|
+
content
|
|
5593
|
+
};
|
|
5594
|
+
};
|
|
5396
5595
|
|
|
5397
5596
|
// src/markdown/parse.ts
|
|
5398
|
-
import matter from "gray-matter";
|
|
5399
5597
|
import { unified } from "unified";
|
|
5400
5598
|
import remarkParse from "remark-parse";
|
|
5401
5599
|
import { visit } from "unist-util-visit";
|
|
@@ -5409,7 +5607,7 @@ var isInternalLink = (url) => {
|
|
|
5409
5607
|
return url.startsWith("#") || !url.includes("://") && !url.startsWith("mailto:");
|
|
5410
5608
|
};
|
|
5411
5609
|
var parseMarkdownDocument = (source) => {
|
|
5412
|
-
const parsed =
|
|
5610
|
+
const parsed = parseFrontmatter(source);
|
|
5413
5611
|
const tree = unified().use(remarkParse).parse(parsed.content);
|
|
5414
5612
|
const headings = [];
|
|
5415
5613
|
const internalLinks = [];
|
|
@@ -5449,9 +5647,10 @@ var checkFrontmatter = async (rootDir, config, markdownPaths, truthDocumentEntri
|
|
|
5449
5647
|
}
|
|
5450
5648
|
const absolutePath = resolveRepoPath(rootDir, markdownPath);
|
|
5451
5649
|
await assertRepoContainment(rootDir, absolutePath);
|
|
5452
|
-
const source = await
|
|
5650
|
+
const source = await fs9.readFile(absolutePath, "utf8");
|
|
5453
5651
|
let document;
|
|
5454
5652
|
try {
|
|
5653
|
+
parseFrontmatter(source, { throwOnInvalid: true });
|
|
5455
5654
|
document = parseMarkdownDocument(source);
|
|
5456
5655
|
} catch (error) {
|
|
5457
5656
|
diagnostics.push({
|
|
@@ -5538,11 +5737,11 @@ var checkFrontmatter = async (rootDir, config, markdownPaths, truthDocumentEntri
|
|
|
5538
5737
|
};
|
|
5539
5738
|
|
|
5540
5739
|
// src/checks/links.ts
|
|
5541
|
-
import
|
|
5740
|
+
import fs10 from "fs/promises";
|
|
5542
5741
|
import path5 from "path";
|
|
5543
5742
|
var pathExists2 = async (absolutePath) => {
|
|
5544
5743
|
try {
|
|
5545
|
-
await
|
|
5744
|
+
await fs10.stat(absolutePath);
|
|
5546
5745
|
return true;
|
|
5547
5746
|
} catch (error) {
|
|
5548
5747
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
@@ -5558,7 +5757,7 @@ var checkLinks = async (rootDir, markdownPaths) => {
|
|
|
5558
5757
|
continue;
|
|
5559
5758
|
}
|
|
5560
5759
|
const absolutePath = resolveRepoPath(rootDir, markdownPath);
|
|
5561
|
-
const source = await
|
|
5760
|
+
const source = await fs10.readFile(absolutePath, "utf8");
|
|
5562
5761
|
let document;
|
|
5563
5762
|
try {
|
|
5564
5763
|
document = parseMarkdownDocument(source);
|
|
@@ -5600,12 +5799,12 @@ var checkLinks = async (rootDir, markdownPaths) => {
|
|
|
5600
5799
|
};
|
|
5601
5800
|
|
|
5602
5801
|
// src/checks/areas.ts
|
|
5603
|
-
import
|
|
5802
|
+
import fs12 from "fs/promises";
|
|
5604
5803
|
import fg4 from "fast-glob";
|
|
5605
5804
|
import micromatch4 from "micromatch";
|
|
5606
5805
|
|
|
5607
5806
|
// src/routing/area-resolver.ts
|
|
5608
|
-
import
|
|
5807
|
+
import fs11 from "fs/promises";
|
|
5609
5808
|
import fg3 from "fast-glob";
|
|
5610
5809
|
import micromatch2 from "micromatch";
|
|
5611
5810
|
var unique = (values) => {
|
|
@@ -5656,7 +5855,7 @@ var ensureChildPath = async (rootDir, areaFilesRoot, filePath) => {
|
|
|
5656
5855
|
var readRouteFile = async (rootDir, filePath) => {
|
|
5657
5856
|
try {
|
|
5658
5857
|
return {
|
|
5659
|
-
source: await
|
|
5858
|
+
source: await fs11.readFile(resolveRepoPath(rootDir, filePath), "utf8"),
|
|
5660
5859
|
diagnostic: null
|
|
5661
5860
|
};
|
|
5662
5861
|
} catch (error) {
|
|
@@ -5964,7 +6163,7 @@ var looksLikeGlob2 = (pattern) => {
|
|
|
5964
6163
|
};
|
|
5965
6164
|
var pathExists3 = async (absolutePath) => {
|
|
5966
6165
|
try {
|
|
5967
|
-
await
|
|
6166
|
+
await fs12.stat(absolutePath);
|
|
5968
6167
|
return true;
|
|
5969
6168
|
} catch (error) {
|
|
5970
6169
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
@@ -6405,7 +6604,7 @@ var checkAreas = async (rootDir, config) => {
|
|
|
6405
6604
|
};
|
|
6406
6605
|
|
|
6407
6606
|
// src/checks/decisions.ts
|
|
6408
|
-
import
|
|
6607
|
+
import fs13 from "fs/promises";
|
|
6409
6608
|
import micromatch5 from "micromatch";
|
|
6410
6609
|
var PRODUCT_CAPABILITY_REQUIRED_HEADINGS = [
|
|
6411
6610
|
"Capability Promise",
|
|
@@ -6521,7 +6720,7 @@ var checkDecisionSections = async (rootDir, config, markdownPaths, truthDocument
|
|
|
6521
6720
|
(filePath) => truthDocumentMap.has(filePath) || isDecisionTruthCandidate(config, filePath)
|
|
6522
6721
|
).sort();
|
|
6523
6722
|
for (const filePath of candidatePaths) {
|
|
6524
|
-
const source = await
|
|
6723
|
+
const source = await fs13.readFile(
|
|
6525
6724
|
resolveRepoPath(rootDir, filePath),
|
|
6526
6725
|
"utf8"
|
|
6527
6726
|
);
|
|
@@ -6563,10 +6762,10 @@ var checkDecisionSections = async (rootDir, config, markdownPaths, truthDocument
|
|
|
6563
6762
|
};
|
|
6564
6763
|
|
|
6565
6764
|
// src/checks/generated-surfaces.ts
|
|
6566
|
-
import
|
|
6765
|
+
import fs14 from "fs/promises";
|
|
6567
6766
|
var readOptionalFile = async (rootDir, filePath) => {
|
|
6568
6767
|
try {
|
|
6569
|
-
return await
|
|
6768
|
+
return await fs14.readFile(resolveRepoPath(rootDir, filePath), "utf8");
|
|
6570
6769
|
} catch (error) {
|
|
6571
6770
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
6572
6771
|
return null;
|
|
@@ -6588,22 +6787,6 @@ var normalizeGeneratedSurfaceContent = (content) => {
|
|
|
6588
6787
|
}
|
|
6589
6788
|
return content.replace(/\r\n/g, "\n").replace(/\n$/u, "");
|
|
6590
6789
|
};
|
|
6591
|
-
var versionMarkers = (content) => {
|
|
6592
|
-
const markers = [];
|
|
6593
|
-
const patterns = [
|
|
6594
|
-
/truthmark-version:\s*([^\s]+)/gu,
|
|
6595
|
-
/Generated by Truthmark\s+([^\s.]+(?:\.[^\s.]+){1,2})/gu,
|
|
6596
|
-
/^version:\s*"(\d+\.\d+\.\d+)"\s*$/gmu
|
|
6597
|
-
];
|
|
6598
|
-
for (const pattern of patterns) {
|
|
6599
|
-
for (const match of content.matchAll(pattern)) {
|
|
6600
|
-
if (match[1]) {
|
|
6601
|
-
markers.push(match[1]);
|
|
6602
|
-
}
|
|
6603
|
-
}
|
|
6604
|
-
}
|
|
6605
|
-
return markers;
|
|
6606
|
-
};
|
|
6607
6790
|
var checkGeneratedSurfaces = async (rootDir, config) => {
|
|
6608
6791
|
const diagnostics = [];
|
|
6609
6792
|
for (const surface of renderGeneratedSurfaces(config)) {
|
|
@@ -6629,41 +6812,23 @@ var checkGeneratedSurfaces = async (rootDir, config) => {
|
|
|
6629
6812
|
file: surface.path
|
|
6630
6813
|
});
|
|
6631
6814
|
}
|
|
6632
|
-
const versionContent = surface.managedBlock ? comparableContent ?? "" : content;
|
|
6633
|
-
const mismatchedVersions = versionMarkers(versionContent).filter(
|
|
6634
|
-
(version) => version !== TRUTHMARK_VERSION
|
|
6635
|
-
);
|
|
6636
|
-
if (mismatchedVersions.length > 0) {
|
|
6637
|
-
diagnostics.push({
|
|
6638
|
-
category: "generated-surface",
|
|
6639
|
-
severity: "review",
|
|
6640
|
-
message: `Generated surface ${surface.path} has Truthmark version ${mismatchedVersions[0]} but current version is ${TRUTHMARK_VERSION}; rerun truthmark init.`,
|
|
6641
|
-
file: surface.path
|
|
6642
|
-
});
|
|
6643
|
-
}
|
|
6644
6815
|
}
|
|
6645
6816
|
return diagnostics;
|
|
6646
6817
|
};
|
|
6647
6818
|
|
|
6648
6819
|
// src/impact/build.ts
|
|
6649
|
-
import
|
|
6820
|
+
import path10 from "path";
|
|
6650
6821
|
import micromatch7 from "micromatch";
|
|
6651
6822
|
|
|
6652
|
-
// src/repo-index/build.ts
|
|
6653
|
-
import fs18 from "fs/promises";
|
|
6654
|
-
import path9 from "path";
|
|
6655
|
-
|
|
6656
6823
|
// src/repo-index/file-tree.ts
|
|
6657
|
-
import
|
|
6824
|
+
import fs15 from "fs/promises";
|
|
6658
6825
|
import path7 from "path";
|
|
6659
6826
|
import { execa as execa2 } from "execa";
|
|
6660
6827
|
import fg5 from "fast-glob";
|
|
6661
|
-
import matter3 from "gray-matter";
|
|
6662
6828
|
import micromatch6 from "micromatch";
|
|
6663
6829
|
|
|
6664
6830
|
// src/truth/source-references.ts
|
|
6665
6831
|
import path6 from "path";
|
|
6666
|
-
import matter2 from "gray-matter";
|
|
6667
6832
|
var repoRootPrefixes = [
|
|
6668
6833
|
".codex/",
|
|
6669
6834
|
".github/",
|
|
@@ -6724,7 +6889,7 @@ var parseSourceReferencesSection = (content) => {
|
|
|
6724
6889
|
return references;
|
|
6725
6890
|
};
|
|
6726
6891
|
var parseSourceReferences = (source, truthDocPath) => {
|
|
6727
|
-
const parsed =
|
|
6892
|
+
const parsed = parseFrontmatter(source);
|
|
6728
6893
|
const references = /* @__PURE__ */ new Set();
|
|
6729
6894
|
const frontmatterSourceOfTruth = Array.isArray(parsed.data.source_of_truth) ? parsed.data.source_of_truth : [];
|
|
6730
6895
|
for (const entry of frontmatterSourceOfTruth) {
|
|
@@ -6752,17 +6917,6 @@ var languageByExtension = /* @__PURE__ */ new Map([
|
|
|
6752
6917
|
[".yaml", "yaml"],
|
|
6753
6918
|
[".toml", "toml"]
|
|
6754
6919
|
]);
|
|
6755
|
-
var sourceExtensions = /* @__PURE__ */ new Set([
|
|
6756
|
-
".ts",
|
|
6757
|
-
".tsx",
|
|
6758
|
-
".js",
|
|
6759
|
-
".jsx",
|
|
6760
|
-
".mjs",
|
|
6761
|
-
".cjs"
|
|
6762
|
-
]);
|
|
6763
|
-
var isJavaScriptLikePath = (filePath) => {
|
|
6764
|
-
return sourceExtensions.has(path7.posix.extname(filePath));
|
|
6765
|
-
};
|
|
6766
6920
|
var isTestPath = (filePath) => {
|
|
6767
6921
|
return filePath.startsWith("tests/") || filePath.includes("/__tests__/") || /(?:^|[./-])(test|spec)\.[cm]?[jt]sx?$/u.test(path7.posix.basename(filePath));
|
|
6768
6922
|
};
|
|
@@ -6843,7 +6997,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
|
|
|
6843
6997
|
for (const filePath of discoveredFiles.filter((entry) => !isIgnoredPath(entry, ignore)).sort()) {
|
|
6844
6998
|
let stat;
|
|
6845
6999
|
try {
|
|
6846
|
-
stat = await
|
|
7000
|
+
stat = await fs15.stat(path7.join(rootDir, filePath));
|
|
6847
7001
|
} catch (error) {
|
|
6848
7002
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
6849
7003
|
continue;
|
|
@@ -6867,8 +7021,8 @@ var discoverRepoFiles = async (rootDir, ignore) => {
|
|
|
6867
7021
|
});
|
|
6868
7022
|
}
|
|
6869
7023
|
if (kind === "doc") {
|
|
6870
|
-
const source = await
|
|
6871
|
-
const parsed =
|
|
7024
|
+
const source = await fs15.readFile(path7.join(rootDir, filePath), "utf8");
|
|
7025
|
+
const parsed = parseFrontmatter(source);
|
|
6872
7026
|
const markdown = parseMarkdownDocument(parsed.content);
|
|
6873
7027
|
const title = markdown.headings.find((heading) => heading.depth === 1)?.text ?? null;
|
|
6874
7028
|
const sourceOfTruth = parseSourceReferences(source, filePath);
|
|
@@ -6895,7 +7049,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
|
|
|
6895
7049
|
};
|
|
6896
7050
|
|
|
6897
7051
|
// src/repo-index/package-metadata.ts
|
|
6898
|
-
import
|
|
7052
|
+
import fs16 from "fs/promises";
|
|
6899
7053
|
import path8 from "path";
|
|
6900
7054
|
import fg6 from "fast-glob";
|
|
6901
7055
|
var packageManagerFor = async (rootDir, packageDir) => {
|
|
@@ -6908,7 +7062,7 @@ var packageManagerFor = async (rootDir, packageDir) => {
|
|
|
6908
7062
|
];
|
|
6909
7063
|
for (const [lockfile, manager] of lockfiles) {
|
|
6910
7064
|
try {
|
|
6911
|
-
await
|
|
7065
|
+
await fs16.access(path8.join(rootDir, packageDir, lockfile));
|
|
6912
7066
|
return manager;
|
|
6913
7067
|
} catch {
|
|
6914
7068
|
continue;
|
|
@@ -6926,7 +7080,7 @@ var discoverPackageMetadata = async (rootDir) => {
|
|
|
6926
7080
|
const packages = [];
|
|
6927
7081
|
for (const packageFile of packageFiles.sort()) {
|
|
6928
7082
|
const packageDir = path8.posix.dirname(packageFile) === "." ? "" : path8.posix.dirname(packageFile);
|
|
6929
|
-
const raw = JSON.parse(await
|
|
7083
|
+
const raw = JSON.parse(await fs16.readFile(path8.join(rootDir, packageFile), "utf8"));
|
|
6930
7084
|
const scripts = raw.scripts && typeof raw.scripts === "object" ? Object.keys(raw.scripts).sort() : [];
|
|
6931
7085
|
packages.push({
|
|
6932
7086
|
path: packageFile,
|
|
@@ -6998,96 +7152,6 @@ var buildRouteMap = async (rootDir) => {
|
|
|
6998
7152
|
};
|
|
6999
7153
|
};
|
|
7000
7154
|
|
|
7001
|
-
// src/repo-index/typescript-symbols.ts
|
|
7002
|
-
import ts from "typescript";
|
|
7003
|
-
var sortStrings = (values) => [...new Set(values)].sort();
|
|
7004
|
-
var hasExportModifier = (node) => {
|
|
7005
|
-
return Boolean(
|
|
7006
|
-
ts.canHaveModifiers(node) && ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)
|
|
7007
|
-
);
|
|
7008
|
-
};
|
|
7009
|
-
var declarationName = (node) => {
|
|
7010
|
-
if (!node.name || !ts.isIdentifier(node.name)) {
|
|
7011
|
-
return null;
|
|
7012
|
-
}
|
|
7013
|
-
return node.name.text;
|
|
7014
|
-
};
|
|
7015
|
-
var addExport = (exports, publicSymbols, path14, name, kind) => {
|
|
7016
|
-
if (!name) {
|
|
7017
|
-
return;
|
|
7018
|
-
}
|
|
7019
|
-
const entry = { path: path14, name, kind };
|
|
7020
|
-
exports.push(entry);
|
|
7021
|
-
publicSymbols.push(entry);
|
|
7022
|
-
};
|
|
7023
|
-
var analyzeTypeScriptSource = (path14, source) => {
|
|
7024
|
-
const sourceFile = ts.createSourceFile(path14, source, ts.ScriptTarget.Latest, true);
|
|
7025
|
-
const imports = [];
|
|
7026
|
-
const exports = [];
|
|
7027
|
-
const publicSymbols = [];
|
|
7028
|
-
for (const statement of sourceFile.statements) {
|
|
7029
|
-
if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier)) {
|
|
7030
|
-
const imported = [];
|
|
7031
|
-
const clause = statement.importClause;
|
|
7032
|
-
if (clause?.name) {
|
|
7033
|
-
imported.push("default");
|
|
7034
|
-
}
|
|
7035
|
-
if (clause?.namedBindings && ts.isNamespaceImport(clause.namedBindings)) {
|
|
7036
|
-
imported.push("*");
|
|
7037
|
-
}
|
|
7038
|
-
if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
7039
|
-
for (const element of clause.namedBindings.elements) {
|
|
7040
|
-
imported.push(element.propertyName?.text ?? element.name.text);
|
|
7041
|
-
}
|
|
7042
|
-
}
|
|
7043
|
-
imports.push({
|
|
7044
|
-
from: path14,
|
|
7045
|
-
specifier: statement.moduleSpecifier.text,
|
|
7046
|
-
imported: sortStrings(imported)
|
|
7047
|
-
});
|
|
7048
|
-
continue;
|
|
7049
|
-
}
|
|
7050
|
-
if (ts.isExportDeclaration(statement)) {
|
|
7051
|
-
if (statement.exportClause && ts.isNamedExports(statement.exportClause)) {
|
|
7052
|
-
for (const element of statement.exportClause.elements) {
|
|
7053
|
-
addExport(exports, publicSymbols, path14, element.name.text, "re-export");
|
|
7054
|
-
}
|
|
7055
|
-
}
|
|
7056
|
-
continue;
|
|
7057
|
-
}
|
|
7058
|
-
if (ts.isFunctionDeclaration(statement) && hasExportModifier(statement)) {
|
|
7059
|
-
addExport(exports, publicSymbols, path14, declarationName(statement), "function");
|
|
7060
|
-
continue;
|
|
7061
|
-
}
|
|
7062
|
-
if (ts.isClassDeclaration(statement) && hasExportModifier(statement)) {
|
|
7063
|
-
addExport(exports, publicSymbols, path14, declarationName(statement), "class");
|
|
7064
|
-
continue;
|
|
7065
|
-
}
|
|
7066
|
-
if (ts.isInterfaceDeclaration(statement) && hasExportModifier(statement)) {
|
|
7067
|
-
addExport(exports, publicSymbols, path14, declarationName(statement), "interface");
|
|
7068
|
-
continue;
|
|
7069
|
-
}
|
|
7070
|
-
if (ts.isTypeAliasDeclaration(statement) && hasExportModifier(statement)) {
|
|
7071
|
-
addExport(exports, publicSymbols, path14, declarationName(statement), "type");
|
|
7072
|
-
continue;
|
|
7073
|
-
}
|
|
7074
|
-
if (ts.isEnumDeclaration(statement) && hasExportModifier(statement)) {
|
|
7075
|
-
addExport(exports, publicSymbols, path14, declarationName(statement), "enum");
|
|
7076
|
-
continue;
|
|
7077
|
-
}
|
|
7078
|
-
if (ts.isVariableStatement(statement) && hasExportModifier(statement)) {
|
|
7079
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
7080
|
-
addExport(exports, publicSymbols, path14, declarationName(declaration), "const");
|
|
7081
|
-
}
|
|
7082
|
-
}
|
|
7083
|
-
}
|
|
7084
|
-
return {
|
|
7085
|
-
imports: imports.sort((left, right) => left.specifier.localeCompare(right.specifier)),
|
|
7086
|
-
exports: exports.sort((left, right) => left.name.localeCompare(right.name)),
|
|
7087
|
-
publicSymbols: publicSymbols.sort((left, right) => left.name.localeCompare(right.name))
|
|
7088
|
-
};
|
|
7089
|
-
};
|
|
7090
|
-
|
|
7091
7155
|
// src/repo-index/build.ts
|
|
7092
7156
|
var buildRepoIndex = async (cwd) => {
|
|
7093
7157
|
const repository = await getGitRepository(cwd);
|
|
@@ -7100,20 +7164,7 @@ var buildRepoIndex = async (cwd) => {
|
|
|
7100
7164
|
discoverRepoFiles(rootDir, ignore),
|
|
7101
7165
|
buildRouteMap(rootDir)
|
|
7102
7166
|
]);
|
|
7103
|
-
const imports = [];
|
|
7104
|
-
const exports = [];
|
|
7105
|
-
const publicSymbols = [];
|
|
7106
7167
|
diagnostics.push(...routeMap.diagnostics);
|
|
7107
|
-
for (const file of fileTree.files) {
|
|
7108
|
-
if (!isJavaScriptLikePath(file.path)) {
|
|
7109
|
-
continue;
|
|
7110
|
-
}
|
|
7111
|
-
const source = await fs18.readFile(path9.join(rootDir, file.path), "utf8");
|
|
7112
|
-
const analysis = analyzeTypeScriptSource(file.path, source);
|
|
7113
|
-
imports.push(...analysis.imports);
|
|
7114
|
-
exports.push(...analysis.exports);
|
|
7115
|
-
publicSymbols.push(...analysis.publicSymbols);
|
|
7116
|
-
}
|
|
7117
7168
|
return {
|
|
7118
7169
|
schemaVersion: "repo-index/v0",
|
|
7119
7170
|
repository: {
|
|
@@ -7125,11 +7176,6 @@ var buildRepoIndex = async (cwd) => {
|
|
|
7125
7176
|
files: fileTree.files,
|
|
7126
7177
|
docs: fileTree.docs,
|
|
7127
7178
|
tests: fileTree.tests,
|
|
7128
|
-
imports: imports.sort((left, right) => `${left.from}:${left.specifier}`.localeCompare(`${right.from}:${right.specifier}`)),
|
|
7129
|
-
exports: exports.sort((left, right) => `${left.path}:${left.name}`.localeCompare(`${right.path}:${right.name}`)),
|
|
7130
|
-
publicSymbols: publicSymbols.sort(
|
|
7131
|
-
(left, right) => `${left.path}:${left.name}`.localeCompare(`${right.path}:${right.name}`)
|
|
7132
|
-
),
|
|
7133
7179
|
routeMap,
|
|
7134
7180
|
diagnostics
|
|
7135
7181
|
};
|
|
@@ -7139,8 +7185,8 @@ var buildRepoIndex = async (cwd) => {
|
|
|
7139
7185
|
import { execa as execa4 } from "execa";
|
|
7140
7186
|
|
|
7141
7187
|
// src/git/changes.ts
|
|
7142
|
-
import
|
|
7143
|
-
import
|
|
7188
|
+
import fs17 from "fs/promises";
|
|
7189
|
+
import path9 from "path";
|
|
7144
7190
|
import { execa as execa3 } from "execa";
|
|
7145
7191
|
var normalizePath3 = (filePath) => {
|
|
7146
7192
|
return filePath.replaceAll("\\", "/").replace(/^\.\//u, "");
|
|
@@ -7151,7 +7197,7 @@ var listChangedPaths = async (cwd, args) => {
|
|
|
7151
7197
|
};
|
|
7152
7198
|
var pathExists4 = async (filePath) => {
|
|
7153
7199
|
try {
|
|
7154
|
-
await
|
|
7200
|
+
await fs17.access(filePath);
|
|
7155
7201
|
return true;
|
|
7156
7202
|
} catch {
|
|
7157
7203
|
return false;
|
|
@@ -7195,7 +7241,7 @@ var getUncommittedChanges = async (cwd) => {
|
|
|
7195
7241
|
const deletedPathCandidates = /* @__PURE__ */ new Set([...stagedDeletedPaths, ...unstagedDeletedPaths]);
|
|
7196
7242
|
for (const deletedPath of deletedPathCandidates) {
|
|
7197
7243
|
const change = getOrCreateChange(changesByPath, deletedPath);
|
|
7198
|
-
change.deleted = !await pathExists4(
|
|
7244
|
+
change.deleted = !await pathExists4(path9.join(rootDir, deletedPath));
|
|
7199
7245
|
}
|
|
7200
7246
|
return Array.from(changesByPath.values()).sort((left, right) => {
|
|
7201
7247
|
return left.path.localeCompare(right.path);
|
|
@@ -7279,14 +7325,6 @@ var getChangedFiles = async (cwd, base) => {
|
|
|
7279
7325
|
diagnostics
|
|
7280
7326
|
};
|
|
7281
7327
|
};
|
|
7282
|
-
var readBaseFile = async (cwd, base, filePath) => {
|
|
7283
|
-
const repository = await getGitRepository(cwd);
|
|
7284
|
-
const result = await execa4("git", ["show", `${base}:${filePath}`], {
|
|
7285
|
-
cwd: repository.worktreePath,
|
|
7286
|
-
reject: false
|
|
7287
|
-
});
|
|
7288
|
-
return (result.exitCode ?? 1) === 0 ? result.stdout : null;
|
|
7289
|
-
};
|
|
7290
7328
|
|
|
7291
7329
|
// src/impact/build.ts
|
|
7292
7330
|
var uniqueSorted2 = (values) => [...new Set(values)].sort();
|
|
@@ -7304,65 +7342,15 @@ var toImpactRoute = (route) => ({
|
|
|
7304
7342
|
truthDocs: [...route.truthDocs].sort(),
|
|
7305
7343
|
codeSurface: [...route.codeSurface].sort()
|
|
7306
7344
|
});
|
|
7307
|
-
var changedPathSet = (changedFiles) => {
|
|
7308
|
-
return new Set(
|
|
7309
|
-
changedFiles.flatMap((file) => [file.path, ...file.previousPath ? [file.previousPath] : []])
|
|
7310
|
-
);
|
|
7311
|
-
};
|
|
7312
7345
|
var changedFilePaths = (changedFile) => {
|
|
7313
7346
|
return [changedFile.path, ...changedFile.previousPath ? [changedFile.previousPath] : []];
|
|
7314
7347
|
};
|
|
7315
|
-
var resolveImportPath = (importEdge) => {
|
|
7316
|
-
if (!importEdge.specifier.startsWith(".")) {
|
|
7317
|
-
return null;
|
|
7318
|
-
}
|
|
7319
|
-
const basePath = path11.posix.normalize(path11.posix.join(path11.posix.dirname(importEdge.from), importEdge.specifier));
|
|
7320
|
-
const withoutExtension = basePath.replace(/\.[cm]?[jt]sx?$/u, "");
|
|
7321
|
-
return withoutExtension;
|
|
7322
|
-
};
|
|
7323
|
-
var importTargetsChangedFile = (importEdge, changedPath) => {
|
|
7324
|
-
const resolved = resolveImportPath(importEdge);
|
|
7325
|
-
if (!resolved) {
|
|
7326
|
-
return false;
|
|
7327
|
-
}
|
|
7328
|
-
return changedPath.replace(/\.[cm]?[jt]sx?$/u, "") === resolved;
|
|
7329
|
-
};
|
|
7330
7348
|
var pathSegments = (filePath) => filePath.split("/").filter(Boolean);
|
|
7331
7349
|
var testHintMatchesChangedFile = (hints, changedPath) => {
|
|
7332
|
-
const changedBaseName =
|
|
7350
|
+
const changedBaseName = path10.posix.basename(changedPath);
|
|
7333
7351
|
const changedSegments = pathSegments(changedPath);
|
|
7334
7352
|
return hints.some((hint) => changedBaseName.startsWith(hint) || changedSegments.includes(hint));
|
|
7335
7353
|
};
|
|
7336
|
-
var changedSymbolsFor = async (cwd, base, basePath, currentPath, currentExports) => {
|
|
7337
|
-
if (!isJavaScriptLikePath(basePath) && !isJavaScriptLikePath(currentPath)) {
|
|
7338
|
-
return [];
|
|
7339
|
-
}
|
|
7340
|
-
const baseSource = await readBaseFile(cwd, base, basePath);
|
|
7341
|
-
const baseExports = baseSource ? analyzeTypeScriptSource(basePath, baseSource).exports : [];
|
|
7342
|
-
const currentByName = new Map(currentExports.map((entry) => [entry.name, entry]));
|
|
7343
|
-
const baseByName = new Map(baseExports.map((entry) => [entry.name, entry]));
|
|
7344
|
-
const changes = [];
|
|
7345
|
-
if (basePath !== currentPath) {
|
|
7346
|
-
for (const entry of currentExports) {
|
|
7347
|
-
changes.push({ path: currentPath, name: entry.name, kind: entry.kind, change: "added" });
|
|
7348
|
-
}
|
|
7349
|
-
for (const entry of baseExports) {
|
|
7350
|
-
changes.push({ path: basePath, name: entry.name, kind: entry.kind, change: "removed" });
|
|
7351
|
-
}
|
|
7352
|
-
return changes;
|
|
7353
|
-
}
|
|
7354
|
-
for (const [name, entry] of currentByName) {
|
|
7355
|
-
if (!baseByName.has(name)) {
|
|
7356
|
-
changes.push({ path: currentPath, name, kind: entry.kind, change: "added" });
|
|
7357
|
-
}
|
|
7358
|
-
}
|
|
7359
|
-
for (const [name, entry] of baseByName) {
|
|
7360
|
-
if (!currentByName.has(name)) {
|
|
7361
|
-
changes.push({ path: basePath, name, kind: entry.kind, change: "removed" });
|
|
7362
|
-
}
|
|
7363
|
-
}
|
|
7364
|
-
return changes;
|
|
7365
|
-
};
|
|
7366
7354
|
var buildImpactSet = async (cwd, options) => {
|
|
7367
7355
|
const repository = await getGitRepository(cwd);
|
|
7368
7356
|
const rootDir = repository.worktreePath;
|
|
@@ -7377,7 +7365,6 @@ var buildImpactSet = async (cwd, options) => {
|
|
|
7377
7365
|
const affectedRoutes = /* @__PURE__ */ new Map();
|
|
7378
7366
|
const affectedTruthDocs = [];
|
|
7379
7367
|
const affectedTests = [];
|
|
7380
|
-
const changedPublicSymbols = [];
|
|
7381
7368
|
const knownTestPaths = new Set(repoIndex.tests.map((test) => test.path));
|
|
7382
7369
|
for (const changedFile of changedFiles) {
|
|
7383
7370
|
const routeCandidatePaths = changedFilePaths(changedFile);
|
|
@@ -7410,60 +7397,15 @@ var buildImpactSet = async (cwd, options) => {
|
|
|
7410
7397
|
file: changedFile.path
|
|
7411
7398
|
});
|
|
7412
7399
|
}
|
|
7413
|
-
const currentExports = repoIndex.exports.filter((entry) => entry.path === changedFile.path);
|
|
7414
|
-
changedPublicSymbols.push(
|
|
7415
|
-
...await changedSymbolsFor(
|
|
7416
|
-
rootDir,
|
|
7417
|
-
options.base,
|
|
7418
|
-
changedFile.previousPath ?? changedFile.path,
|
|
7419
|
-
changedFile.path,
|
|
7420
|
-
currentExports
|
|
7421
|
-
)
|
|
7422
|
-
);
|
|
7423
7400
|
}
|
|
7424
7401
|
const uniqueAffectedTruthDocs = uniqueSorted2(affectedTruthDocs);
|
|
7425
|
-
const changedPaths = changedPathSet(changedFiles);
|
|
7426
|
-
for (const symbol of changedPublicSymbols) {
|
|
7427
|
-
if (uniqueAffectedTruthDocs.length === 0) {
|
|
7428
|
-
diagnostics.push({
|
|
7429
|
-
category: "impact",
|
|
7430
|
-
severity: "review",
|
|
7431
|
-
message: `Changed public symbol ${symbol.name} in ${symbol.path} has no affected truth document.`,
|
|
7432
|
-
file: symbol.path,
|
|
7433
|
-
data: {
|
|
7434
|
-
symbol: symbol.name,
|
|
7435
|
-
change: symbol.change
|
|
7436
|
-
}
|
|
7437
|
-
});
|
|
7438
|
-
continue;
|
|
7439
|
-
}
|
|
7440
|
-
if (!uniqueAffectedTruthDocs.some((truthDoc) => changedPaths.has(truthDoc))) {
|
|
7441
|
-
diagnostics.push({
|
|
7442
|
-
category: "impact",
|
|
7443
|
-
severity: "review",
|
|
7444
|
-
message: `Changed public symbol ${symbol.name} in ${symbol.path} has affected truth docs but none were changed in this impact set.`,
|
|
7445
|
-
file: symbol.path,
|
|
7446
|
-
data: {
|
|
7447
|
-
symbol: symbol.name,
|
|
7448
|
-
change: symbol.change,
|
|
7449
|
-
affectedTruthDocs: uniqueAffectedTruthDocs
|
|
7450
|
-
}
|
|
7451
|
-
});
|
|
7452
|
-
}
|
|
7453
|
-
}
|
|
7454
7402
|
for (const test of repoIndex.tests) {
|
|
7455
|
-
const testImports = repoIndex.imports.filter((edge) => edge.from === test.path);
|
|
7456
|
-
const importsChangedFile = changedFiles.some(
|
|
7457
|
-
(changedFile) => changedFilePaths(changedFile).some(
|
|
7458
|
-
(filePath) => testImports.some((importEdge) => importTargetsChangedFile(importEdge, filePath))
|
|
7459
|
-
)
|
|
7460
|
-
);
|
|
7461
7403
|
const hintMatchesChangedFile = changedFiles.some(
|
|
7462
7404
|
(changedFile) => changedFilePaths(changedFile).some(
|
|
7463
7405
|
(filePath) => testHintMatchesChangedFile(test.targetHints, filePath)
|
|
7464
7406
|
)
|
|
7465
7407
|
);
|
|
7466
|
-
if (
|
|
7408
|
+
if (hintMatchesChangedFile) {
|
|
7467
7409
|
affectedTests.push(test.path);
|
|
7468
7410
|
}
|
|
7469
7411
|
}
|
|
@@ -7477,9 +7419,6 @@ var buildImpactSet = async (cwd, options) => {
|
|
|
7477
7419
|
),
|
|
7478
7420
|
affectedTruthDocs: uniqueAffectedTruthDocs,
|
|
7479
7421
|
affectedTests: uniqueSorted2(affectedTests),
|
|
7480
|
-
changedPublicSymbols: changedPublicSymbols.sort(
|
|
7481
|
-
(left, right) => `${left.path}:${left.name}:${left.change}`.localeCompare(`${right.path}:${right.name}:${right.change}`)
|
|
7482
|
-
),
|
|
7483
7422
|
diagnostics
|
|
7484
7423
|
};
|
|
7485
7424
|
};
|
|
@@ -7505,14 +7444,13 @@ var checkFreshness = async (rootDir, _config, _truthDocumentPaths, base) => {
|
|
|
7505
7444
|
};
|
|
7506
7445
|
|
|
7507
7446
|
// src/evidence/validate.ts
|
|
7508
|
-
import
|
|
7447
|
+
import fs19 from "fs/promises";
|
|
7509
7448
|
import fg7 from "fast-glob";
|
|
7510
7449
|
|
|
7511
7450
|
// src/evidence/parse.ts
|
|
7512
|
-
import
|
|
7513
|
-
import
|
|
7514
|
-
import
|
|
7515
|
-
import { parse as parse3 } from "yaml";
|
|
7451
|
+
import fs18 from "fs/promises";
|
|
7452
|
+
import path11 from "path";
|
|
7453
|
+
import { parse as parse4 } from "yaml";
|
|
7516
7454
|
var yamlFencePattern = /```ya?ml\s*\n([\s\S]*?)```/giu;
|
|
7517
7455
|
var topLevelEvidenceMarkerPattern = /^evidence\s*:/imu;
|
|
7518
7456
|
var toEvidenceReference = (truthDocPath, raw) => {
|
|
@@ -7530,8 +7468,8 @@ var toEvidenceReference = (truthDocPath, raw) => {
|
|
|
7530
7468
|
};
|
|
7531
7469
|
};
|
|
7532
7470
|
var parseEvidenceReferences = async (rootDir, truthDocPath) => {
|
|
7533
|
-
const source = await
|
|
7534
|
-
const parsed =
|
|
7471
|
+
const source = await fs18.readFile(path11.join(rootDir, truthDocPath), "utf8");
|
|
7472
|
+
const parsed = parseFrontmatter(source);
|
|
7535
7473
|
const references = [];
|
|
7536
7474
|
for (const entry of parseSourceReferences(source, truthDocPath)) {
|
|
7537
7475
|
references.push({
|
|
@@ -7545,7 +7483,7 @@ var parseEvidenceReferences = async (rootDir, truthDocPath) => {
|
|
|
7545
7483
|
if (!topLevelEvidenceMarkerPattern.test(yamlBlock)) {
|
|
7546
7484
|
continue;
|
|
7547
7485
|
}
|
|
7548
|
-
const block =
|
|
7486
|
+
const block = parse4(yamlBlock);
|
|
7549
7487
|
const rawEvidence = block && typeof block === "object" && "evidence" in block ? block.evidence : null;
|
|
7550
7488
|
if (!Array.isArray(rawEvidence)) {
|
|
7551
7489
|
continue;
|
|
@@ -7563,7 +7501,7 @@ var parseEvidenceReferences = async (rootDir, truthDocPath) => {
|
|
|
7563
7501
|
// src/evidence/validate.ts
|
|
7564
7502
|
var pathExists5 = async (filePath) => {
|
|
7565
7503
|
try {
|
|
7566
|
-
await
|
|
7504
|
+
await fs19.access(filePath);
|
|
7567
7505
|
return true;
|
|
7568
7506
|
} catch {
|
|
7569
7507
|
return false;
|
|
@@ -7601,15 +7539,6 @@ var validateGlob = async (rootDir, reference) => {
|
|
|
7601
7539
|
});
|
|
7602
7540
|
return matches.length > 0 ? null : diagnosticFor(reference, `Referenced file pattern ${reference.path} does not match any file.`);
|
|
7603
7541
|
};
|
|
7604
|
-
var validateSymbol = async (rootDir, reference) => {
|
|
7605
|
-
if (!reference.symbol || !isJavaScriptLikePath(reference.path)) {
|
|
7606
|
-
return null;
|
|
7607
|
-
}
|
|
7608
|
-
const source = await fs21.readFile(resolveRepoPath(rootDir, reference.path), "utf8");
|
|
7609
|
-
const analysis = analyzeTypeScriptSource(reference.path, source);
|
|
7610
|
-
const hasSymbol = analysis.publicSymbols.some((symbol) => symbol.name === reference.symbol);
|
|
7611
|
-
return hasSymbol ? null : diagnosticFor(reference, `Evidence symbol ${reference.symbol} was not found in ${reference.path}.`);
|
|
7612
|
-
};
|
|
7613
7542
|
var validateHash = async (rootDir, reference) => {
|
|
7614
7543
|
if (!reference.contentHash) {
|
|
7615
7544
|
return null;
|
|
@@ -7617,7 +7546,7 @@ var validateHash = async (rootDir, reference) => {
|
|
|
7617
7546
|
if (!reference.contentHash.startsWith("sha256:")) {
|
|
7618
7547
|
return diagnosticFor(reference, `Evidence hash for ${reference.path} must use sha256:.`);
|
|
7619
7548
|
}
|
|
7620
|
-
const source = await
|
|
7549
|
+
const source = await fs19.readFile(resolveRepoPath(rootDir, reference.path), "utf8");
|
|
7621
7550
|
const lines = source.split("\n");
|
|
7622
7551
|
const startLine = reference.startLine ?? 1;
|
|
7623
7552
|
const endLine = reference.endLine ?? lines.length;
|
|
@@ -7631,7 +7560,7 @@ var validateLineSpan = async (rootDir, reference) => {
|
|
|
7631
7560
|
if (reference.startLine === void 0 && reference.endLine === void 0) {
|
|
7632
7561
|
return null;
|
|
7633
7562
|
}
|
|
7634
|
-
const source = await
|
|
7563
|
+
const source = await fs19.readFile(resolveRepoPath(rootDir, reference.path), "utf8");
|
|
7635
7564
|
const lines = source.split("\n");
|
|
7636
7565
|
const startLine = reference.startLine ?? 1;
|
|
7637
7566
|
const endLine = reference.endLine ?? lines.length;
|
|
@@ -7650,10 +7579,6 @@ var validateReference = async (rootDir, reference) => {
|
|
|
7650
7579
|
diagnostics.push(diagnosticFor(reference, `Referenced file ${reference.path} does not exist.`));
|
|
7651
7580
|
return diagnostics;
|
|
7652
7581
|
}
|
|
7653
|
-
const symbolDiagnostic = await validateSymbol(rootDir, reference);
|
|
7654
|
-
if (symbolDiagnostic) {
|
|
7655
|
-
diagnostics.push(symbolDiagnostic);
|
|
7656
|
-
}
|
|
7657
7582
|
const lineSpanDiagnostic = await validateLineSpan(rootDir, reference);
|
|
7658
7583
|
if (lineSpanDiagnostic) {
|
|
7659
7584
|
diagnostics.push(lineSpanDiagnostic);
|
|
@@ -7910,220 +7835,60 @@ var runCheck = async (cwd, options = {}) => {
|
|
|
7910
7835
|
};
|
|
7911
7836
|
};
|
|
7912
7837
|
|
|
7913
|
-
// src/context-pack/build.ts
|
|
7914
|
-
import fs22 from "fs/promises";
|
|
7915
|
-
import path13 from "path";
|
|
7916
|
-
import fg8 from "fast-glob";
|
|
7917
|
-
var uniqueSorted3 = (values) => [...new Set(values)].sort();
|
|
7918
|
-
var repoRootPrefixes2 = [".codex/", ".github/", ".truthmark/", "docs/", "src/", "tests/"];
|
|
7919
|
-
var isGlobReference2 = (referencePath) => /[*?[\]{}()]/u.test(referencePath);
|
|
7920
|
-
var normalizeDocReferencePath = (docPath, referencePath) => {
|
|
7921
|
-
const strippedPath = referencePath.split("#")[0]?.trim() ?? "";
|
|
7922
|
-
if (strippedPath.length === 0 || strippedPath.startsWith("/")) {
|
|
7923
|
-
return null;
|
|
7924
|
-
}
|
|
7925
|
-
const isRepoRelative = repoRootPrefixes2.some((prefix) => strippedPath.startsWith(prefix));
|
|
7926
|
-
const normalized = isRepoRelative ? path13.posix.normalize(strippedPath) : path13.posix.normalize(path13.posix.join(path13.posix.dirname(docPath), strippedPath));
|
|
7927
|
-
return normalized === ".." || normalized.startsWith("../") ? null : normalized;
|
|
7928
|
-
};
|
|
7929
|
-
var readIfExists = async (rootDir, filePath) => {
|
|
7930
|
-
try {
|
|
7931
|
-
return await fs22.readFile(path13.join(rootDir, filePath), "utf8");
|
|
7932
|
-
} catch {
|
|
7933
|
-
return null;
|
|
7934
|
-
}
|
|
7935
|
-
};
|
|
7936
|
-
var boundContent = (content) => {
|
|
7937
|
-
const lines = content.split("\n");
|
|
7938
|
-
if (lines.length <= 200) {
|
|
7939
|
-
return { content, truncated: false };
|
|
7940
|
-
}
|
|
7941
|
-
return {
|
|
7942
|
-
content: [...lines.slice(0, 80), "...", ...lines.slice(-40)].join("\n"),
|
|
7943
|
-
truncated: true
|
|
7944
|
-
};
|
|
7945
|
-
};
|
|
7946
|
-
var boundedContent = (filePath, content, warnings) => {
|
|
7947
|
-
const bounded = boundContent(content);
|
|
7948
|
-
if (!bounded.truncated) {
|
|
7949
|
-
return { path: filePath, content, truncated: false };
|
|
7950
|
-
}
|
|
7951
|
-
warnings.push({
|
|
7952
|
-
category: "context-pack",
|
|
7953
|
-
severity: "review",
|
|
7954
|
-
message: `Context source file ${filePath} was truncated to fit ContextPack v0 bounds.`,
|
|
7955
|
-
file: filePath
|
|
7956
|
-
});
|
|
7957
|
-
return {
|
|
7958
|
-
path: filePath,
|
|
7959
|
-
content: bounded.content,
|
|
7960
|
-
truncated: true
|
|
7961
|
-
};
|
|
7962
|
-
};
|
|
7963
|
-
var documentsFor = async (rootDir, paths, warnings) => {
|
|
7964
|
-
const documents = [];
|
|
7965
|
-
for (const filePath of uniqueSorted3(paths)) {
|
|
7966
|
-
const content = await readIfExists(rootDir, filePath);
|
|
7967
|
-
if (content !== null) {
|
|
7968
|
-
const bounded = boundContent(content);
|
|
7969
|
-
if (bounded.truncated) {
|
|
7970
|
-
warnings.push({
|
|
7971
|
-
category: "context-pack",
|
|
7972
|
-
severity: "review",
|
|
7973
|
-
message: `Context truth doc ${filePath} was truncated to fit ContextPack v0 bounds.`,
|
|
7974
|
-
file: filePath
|
|
7975
|
-
});
|
|
7976
|
-
}
|
|
7977
|
-
documents.push({ path: filePath, content: bounded.content, truncated: bounded.truncated });
|
|
7978
|
-
}
|
|
7979
|
-
}
|
|
7980
|
-
return documents;
|
|
7981
|
-
};
|
|
7982
|
-
var sourceFilesFor = async (rootDir, paths, warnings) => {
|
|
7983
|
-
const sourceFiles = [];
|
|
7984
|
-
for (const filePath of uniqueSorted3(paths)) {
|
|
7985
|
-
const content = await readIfExists(rootDir, filePath);
|
|
7986
|
-
if (content !== null) {
|
|
7987
|
-
sourceFiles.push(boundedContent(filePath, content, warnings));
|
|
7988
|
-
}
|
|
7989
|
-
}
|
|
7990
|
-
return sourceFiles;
|
|
7991
|
-
};
|
|
7992
|
-
var sourceOfTruthPathsFor = async (rootDir, docs, truthDocPaths) => {
|
|
7993
|
-
const selectedTruthDocs = new Set(truthDocPaths);
|
|
7994
|
-
const sourcePaths = [];
|
|
7995
|
-
for (const doc of docs) {
|
|
7996
|
-
if (!selectedTruthDocs.has(doc.path)) {
|
|
7997
|
-
continue;
|
|
7998
|
-
}
|
|
7999
|
-
for (const referencePath of doc.sourceOfTruth) {
|
|
8000
|
-
const normalizedPath = normalizeDocReferencePath(doc.path, referencePath);
|
|
8001
|
-
if (!normalizedPath) {
|
|
8002
|
-
continue;
|
|
8003
|
-
}
|
|
8004
|
-
if (isGlobReference2(normalizedPath)) {
|
|
8005
|
-
sourcePaths.push(
|
|
8006
|
-
...await fg8(normalizedPath, {
|
|
8007
|
-
cwd: rootDir,
|
|
8008
|
-
dot: true,
|
|
8009
|
-
onlyFiles: true,
|
|
8010
|
-
followSymbolicLinks: false
|
|
8011
|
-
})
|
|
8012
|
-
);
|
|
8013
|
-
} else {
|
|
8014
|
-
sourcePaths.push(normalizedPath);
|
|
8015
|
-
}
|
|
8016
|
-
}
|
|
8017
|
-
}
|
|
8018
|
-
return uniqueSorted3(sourcePaths);
|
|
8019
|
-
};
|
|
8020
|
-
var writePathsFor = (workflow, routeIndexPath, truthDocs, routes) => {
|
|
8021
|
-
if (workflow === "truth-sync") {
|
|
8022
|
-
return uniqueSorted3([routeIndexPath, ...truthDocs]);
|
|
8023
|
-
}
|
|
8024
|
-
if (workflow === "truth-document") {
|
|
8025
|
-
return uniqueSorted3([routeIndexPath, ...truthDocs]);
|
|
8026
|
-
}
|
|
8027
|
-
return uniqueSorted3(routes.flatMap((route) => route.codeSurface));
|
|
8028
|
-
};
|
|
8029
|
-
var testCommandsFor = (affectedTests) => {
|
|
8030
|
-
return affectedTests.length === 0 ? ["npm test"] : [`npm test -- ${affectedTests.join(" ")}`];
|
|
8031
|
-
};
|
|
8032
|
-
var buildContextPack = async (cwd, options) => {
|
|
8033
|
-
const repoIndex = await buildRepoIndex(cwd);
|
|
8034
|
-
const rootDir = repoIndex.repository.root;
|
|
8035
|
-
const loadResult = await loadConfig(rootDir);
|
|
8036
|
-
const config = loadResult.config ?? (loadResult.status === "missing" ? createDefaultConfig() : null);
|
|
8037
|
-
const impactSet = options.base ? await buildImpactSet(rootDir, { base: options.base }) : null;
|
|
8038
|
-
const routeMap = impactSet ? repoIndex.routeMap : repoIndex.routeMap;
|
|
8039
|
-
const warnings = loadResult.status === "invalid" ? [...loadResult.diagnostics] : [];
|
|
8040
|
-
const truthDocPaths = impactSet?.affectedTruthDocs ?? (options.workflow === "truth-realize" ? [] : routeMap.routes.flatMap((route) => route.truthDocs));
|
|
8041
|
-
const contextRoutes = impactSet?.affectedRoutes ?? (options.workflow === "truth-realize" ? [] : routeMap.routes);
|
|
8042
|
-
if (options.workflow === "truth-realize" && !impactSet) {
|
|
8043
|
-
warnings.push({
|
|
8044
|
-
category: "context-pack",
|
|
8045
|
-
severity: "review",
|
|
8046
|
-
message: "truth-realize requires --base to derive bounded allowed write paths."
|
|
8047
|
-
});
|
|
8048
|
-
}
|
|
8049
|
-
const sourceOfTruthPaths = await sourceOfTruthPathsFor(rootDir, repoIndex.docs, truthDocPaths);
|
|
8050
|
-
const sourceFilePaths = uniqueSorted3([
|
|
8051
|
-
...impactSet?.changedFiles.filter((file) => !file.deleted).map((file) => file.path) ?? [],
|
|
8052
|
-
...sourceOfTruthPaths
|
|
8053
|
-
]);
|
|
8054
|
-
return {
|
|
8055
|
-
schemaVersion: "context-pack/v0",
|
|
8056
|
-
workflow: options.workflow,
|
|
8057
|
-
base: options.base ?? null,
|
|
8058
|
-
impactSet,
|
|
8059
|
-
routeMap,
|
|
8060
|
-
allowedWritePaths: config === null ? [] : writePathsFor(
|
|
8061
|
-
options.workflow,
|
|
8062
|
-
config.truthmark.paths.routesIndex,
|
|
8063
|
-
truthDocPaths,
|
|
8064
|
-
contextRoutes
|
|
8065
|
-
),
|
|
8066
|
-
truthDocs: await documentsFor(rootDir, truthDocPaths, warnings),
|
|
8067
|
-
sourceFiles: await sourceFilesFor(rootDir, sourceFilePaths, warnings),
|
|
8068
|
-
testCommands: testCommandsFor(impactSet?.affectedTests ?? []),
|
|
8069
|
-
warnings
|
|
8070
|
-
};
|
|
8071
|
-
};
|
|
8072
|
-
|
|
8073
|
-
// src/context-pack/render.ts
|
|
8074
|
-
var renderContextPackMarkdown = (pack) => {
|
|
8075
|
-
const lines = [
|
|
8076
|
-
`# Truthmark ContextPack (${pack.workflow})`,
|
|
8077
|
-
"",
|
|
8078
|
-
`Schema: ${pack.schemaVersion}`,
|
|
8079
|
-
`Base: ${pack.base ?? "none"}`,
|
|
8080
|
-
"",
|
|
8081
|
-
"## Allowed Write Paths",
|
|
8082
|
-
...pack.allowedWritePaths.map((filePath) => `- ${filePath}`),
|
|
8083
|
-
"",
|
|
8084
|
-
"## Truth Docs",
|
|
8085
|
-
...pack.truthDocs.map((doc) => `- ${doc.path}${doc.truncated ? " (truncated)" : ""}`),
|
|
8086
|
-
"",
|
|
8087
|
-
"## Source Files",
|
|
8088
|
-
...pack.sourceFiles.map((file) => `- ${file.path}${file.truncated ? " (truncated)" : ""}`),
|
|
8089
|
-
"",
|
|
8090
|
-
"## Test Commands",
|
|
8091
|
-
...pack.testCommands.map((command) => `- ${command}`)
|
|
8092
|
-
];
|
|
8093
|
-
return `${lines.join("\n")}
|
|
8094
|
-
`;
|
|
8095
|
-
};
|
|
8096
|
-
|
|
8097
7838
|
// src/workflow-state/build.ts
|
|
8098
7839
|
import { execa as execa5 } from "execa";
|
|
8099
7840
|
|
|
8100
7841
|
// src/workflow-state/action-context.ts
|
|
8101
|
-
var
|
|
7842
|
+
var uniqueSorted3 = (values) => [...new Set(values.filter((value) => value.length > 0))].sort();
|
|
8102
7843
|
var helperCommandsFor = (manifestEntry) => (manifestEntry.helpers ?? []).map((helper) => ({
|
|
8103
7844
|
id: helper.id,
|
|
8104
7845
|
runner: helper.runner,
|
|
8105
7846
|
argv: [...helper.command.argv],
|
|
8106
7847
|
optional: helper.optional
|
|
8107
7848
|
}));
|
|
8108
|
-
var evidenceFor = (manifestEntry) => manifestEntry.
|
|
8109
|
-
|
|
7849
|
+
var evidenceFor = (manifestEntry) => manifestEntry.reviewQuestions.filter(
|
|
7850
|
+
(question) => /evidence|ownership|containment/iu.test(question)
|
|
7851
|
+
);
|
|
7852
|
+
var baseContext = (manifestEntry, mode, allowedWritePaths, routeFiles, primaryTruthDocs, candidateStaleTruthDocs, forbiddenWritePaths, writeLeaseRequired) => ({
|
|
8110
7853
|
mode,
|
|
8111
|
-
allowedWritePaths:
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
|
|
7854
|
+
allowedWritePaths: uniqueSorted3(allowedWritePaths),
|
|
7855
|
+
routeFiles: uniqueSorted3(routeFiles),
|
|
7856
|
+
primaryTruthDocs: uniqueSorted3(primaryTruthDocs),
|
|
7857
|
+
candidateStaleTruthDocs: uniqueSorted3(candidateStaleTruthDocs),
|
|
7858
|
+
forbiddenWritePaths: uniqueSorted3(forbiddenWritePaths),
|
|
7859
|
+
stopConditions: [
|
|
7860
|
+
...manifestEntry.negativeTriggers,
|
|
7861
|
+
...manifestEntry.forbiddenAdjacency
|
|
7862
|
+
],
|
|
7863
|
+
evidencePrompts: uniqueSorted3([
|
|
7864
|
+
...evidenceFor(manifestEntry),
|
|
7865
|
+
...candidateStaleTruthDocs.length > 0 ? [
|
|
7866
|
+
"Record checkout evidence and the reason before touching a candidate stale truth doc outside primaryTruthDocs."
|
|
7867
|
+
] : []
|
|
7868
|
+
]),
|
|
8115
7869
|
helperValidationCommands: helperCommandsFor(manifestEntry),
|
|
8116
7870
|
writeLeaseRequired
|
|
8117
7871
|
});
|
|
8118
7872
|
var buildWorkflowActionContext = (manifestEntry, data = {}) => {
|
|
8119
7873
|
if (manifestEntry.id === "truthmark-preview" || manifestEntry.id === "truthmark-check") {
|
|
8120
|
-
return baseContext(manifestEntry, "read-only", [], [], false);
|
|
7874
|
+
return baseContext(manifestEntry, "read-only", [], [], [], [], [], false);
|
|
8121
7875
|
}
|
|
8122
7876
|
if (manifestEntry.id === "truthmark-sync" || manifestEntry.id === "truthmark-document") {
|
|
7877
|
+
const routeFiles = data.routeFiles ?? [];
|
|
7878
|
+
const primaryTruthDocs = data.primaryTruthDocs ?? data.truthDocs ?? [];
|
|
7879
|
+
const candidateStaleTruthDocs = data.candidateStaleTruthDocs ?? [];
|
|
8123
7880
|
return baseContext(
|
|
8124
7881
|
manifestEntry,
|
|
8125
7882
|
"truth-doc-write",
|
|
8126
|
-
[
|
|
7883
|
+
[
|
|
7884
|
+
...data.routeIndexPath ? [data.routeIndexPath] : [],
|
|
7885
|
+
...routeFiles,
|
|
7886
|
+
...primaryTruthDocs,
|
|
7887
|
+
...candidateStaleTruthDocs
|
|
7888
|
+
],
|
|
7889
|
+
routeFiles,
|
|
7890
|
+
primaryTruthDocs,
|
|
7891
|
+
candidateStaleTruthDocs,
|
|
8127
7892
|
[],
|
|
8128
7893
|
true
|
|
8129
7894
|
);
|
|
@@ -8137,6 +7902,9 @@ var buildWorkflowActionContext = (manifestEntry, data = {}) => {
|
|
|
8137
7902
|
...data.routeFiles ?? [],
|
|
8138
7903
|
...data.starterTruthDocs ?? []
|
|
8139
7904
|
],
|
|
7905
|
+
data.routeFiles ?? [],
|
|
7906
|
+
data.starterTruthDocs ?? [],
|
|
7907
|
+
[],
|
|
8140
7908
|
[],
|
|
8141
7909
|
true
|
|
8142
7910
|
);
|
|
@@ -8146,6 +7914,9 @@ var buildWorkflowActionContext = (manifestEntry, data = {}) => {
|
|
|
8146
7914
|
manifestEntry,
|
|
8147
7915
|
"code-write",
|
|
8148
7916
|
data.codeWritePaths ?? [],
|
|
7917
|
+
data.routeFiles ?? [],
|
|
7918
|
+
data.truthDocs ?? [],
|
|
7919
|
+
[],
|
|
8149
7920
|
[
|
|
8150
7921
|
...data.routeIndexPath ? [data.routeIndexPath] : [],
|
|
8151
7922
|
...data.routeFiles ?? [],
|
|
@@ -8160,6 +7931,9 @@ var buildWorkflowActionContext = (manifestEntry, data = {}) => {
|
|
|
8160
7931
|
"portal-write",
|
|
8161
7932
|
data.portalEnabled && data.portalOutputPath ? [`${data.portalOutputPath}/**`] : [],
|
|
8162
7933
|
[],
|
|
7934
|
+
[],
|
|
7935
|
+
[],
|
|
7936
|
+
[],
|
|
8163
7937
|
false
|
|
8164
7938
|
);
|
|
8165
7939
|
};
|
|
@@ -8171,79 +7945,139 @@ var helperCommandsFor2 = (workflow) => (TRUTHMARK_WORKFLOW_MANIFEST[workflow].he
|
|
|
8171
7945
|
argv: [...helper.command.argv],
|
|
8172
7946
|
optional: helper.optional
|
|
8173
7947
|
}));
|
|
8174
|
-
var
|
|
7948
|
+
var uniqueSorted4 = (values) => [...new Set(values.filter((value) => value.length > 0))].sort();
|
|
8175
7949
|
var isWriteCapable = (workflow) => !["truthmark-preview", "truthmark-check"].includes(workflow);
|
|
8176
|
-
var DEFAULT_BASE_CANDIDATES = [
|
|
7950
|
+
var DEFAULT_BASE_CANDIDATES = [
|
|
7951
|
+
"@{upstream}",
|
|
7952
|
+
"origin/main",
|
|
7953
|
+
"main",
|
|
7954
|
+
"origin/master",
|
|
7955
|
+
"master"
|
|
7956
|
+
];
|
|
8177
7957
|
var selectComparisonBase = async (rootDir, suppliedBase) => {
|
|
8178
7958
|
if (suppliedBase) {
|
|
8179
7959
|
return suppliedBase;
|
|
8180
7960
|
}
|
|
8181
7961
|
for (const candidate of DEFAULT_BASE_CANDIDATES) {
|
|
8182
|
-
const result = await execa5(
|
|
8183
|
-
|
|
8184
|
-
|
|
8185
|
-
|
|
7962
|
+
const result = await execa5(
|
|
7963
|
+
"git",
|
|
7964
|
+
["rev-parse", "--verify", `${candidate}^{commit}`],
|
|
7965
|
+
{
|
|
7966
|
+
cwd: rootDir,
|
|
7967
|
+
reject: false
|
|
7968
|
+
}
|
|
7969
|
+
);
|
|
8186
7970
|
if ((result.exitCode ?? 1) === 0) {
|
|
8187
7971
|
return candidate;
|
|
8188
7972
|
}
|
|
8189
7973
|
}
|
|
8190
7974
|
return null;
|
|
8191
7975
|
};
|
|
8192
|
-
var routeFilesFor = (repoIndex) =>
|
|
7976
|
+
var routeFilesFor = (repoIndex) => uniqueSorted4(repoIndex.routeMap.routes.map((route) => route.sourcePath));
|
|
7977
|
+
var candidateStaleTruthDocsFor = (indexedTruthDocs, primaryTruthDocs) => {
|
|
7978
|
+
const primary = new Set(primaryTruthDocs);
|
|
7979
|
+
return indexedTruthDocs.filter((truthDoc) => !primary.has(truthDoc));
|
|
7980
|
+
};
|
|
8193
7981
|
var hasUnmappedFunctionalChange = (impactSet) => impactSet?.diagnostics.some(
|
|
8194
|
-
(diagnostic) => diagnostic.category === "impact" && /not mapped to a Truthmark route|no affected truth document/u.test(
|
|
7982
|
+
(diagnostic) => diagnostic.category === "impact" && /not mapped to a Truthmark route|no affected truth document/u.test(
|
|
7983
|
+
diagnostic.message
|
|
7984
|
+
)
|
|
8195
7985
|
) ?? false;
|
|
8196
7986
|
var applicabilityFor = (workflow, diagnostics, impactSet) => {
|
|
8197
7987
|
const reasons = [];
|
|
8198
|
-
if (diagnostics.some(
|
|
7988
|
+
if (diagnostics.some(
|
|
7989
|
+
(diagnostic) => diagnostic.message.includes("Missing .truthmark/config.yml")
|
|
7990
|
+
)) {
|
|
8199
7991
|
reasons.push("Missing .truthmark/config.yml.");
|
|
8200
|
-
return {
|
|
7992
|
+
return {
|
|
7993
|
+
state: isWriteCapable(workflow) ? "needs_manual_review" : "not_applicable",
|
|
7994
|
+
reasons
|
|
7995
|
+
};
|
|
8201
7996
|
}
|
|
8202
7997
|
if (workflow === "truthmark-sync" && !impactSet) {
|
|
8203
|
-
reasons.push(
|
|
8204
|
-
|
|
7998
|
+
reasons.push(
|
|
7999
|
+
"Choose a comparison base with --base <ref> when workflow status cannot infer one automatically."
|
|
8000
|
+
);
|
|
8001
|
+
return { state: "needs_manual_review", reasons };
|
|
8205
8002
|
}
|
|
8206
8003
|
if (workflow === "truthmark-realize" && !impactSet) {
|
|
8207
|
-
reasons.push(
|
|
8208
|
-
|
|
8004
|
+
reasons.push(
|
|
8005
|
+
"Choose a comparison base with --base <ref> so the helper can suggest bounded code-write paths."
|
|
8006
|
+
);
|
|
8007
|
+
return { state: "needs_manual_review", reasons };
|
|
8209
8008
|
}
|
|
8210
8009
|
if (hasUnmappedFunctionalChange(impactSet)) {
|
|
8211
|
-
reasons.push(
|
|
8212
|
-
|
|
8010
|
+
reasons.push(
|
|
8011
|
+
"Changed functional files need route ownership review before truth-doc suggestions are reliable."
|
|
8012
|
+
);
|
|
8013
|
+
return { state: "needs_routing_review", reasons };
|
|
8213
8014
|
}
|
|
8214
8015
|
if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
8215
|
-
reasons.push(
|
|
8216
|
-
|
|
8016
|
+
reasons.push(
|
|
8017
|
+
"Existing diagnostics need manual review before using helper write suggestions."
|
|
8018
|
+
);
|
|
8019
|
+
return { state: "needs_manual_review", reasons };
|
|
8217
8020
|
}
|
|
8218
|
-
return { state: "
|
|
8021
|
+
return { state: "ready", reasons };
|
|
8022
|
+
};
|
|
8023
|
+
var workflowCardFor = (workflow, applicability, diagnostics, impactSet) => {
|
|
8024
|
+
const helpers = helperCommandsFor2(workflow);
|
|
8025
|
+
const diagnosticQuestions = diagnostics.filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.message);
|
|
8026
|
+
return {
|
|
8027
|
+
affectedFiles: uniqueSorted4(
|
|
8028
|
+
impactSet?.changedFiles.map((file) => file.path) ?? []
|
|
8029
|
+
),
|
|
8030
|
+
likelyRouteOwners: uniqueSorted4(
|
|
8031
|
+
impactSet?.affectedRoutes.map((route) => route.sourcePath) ?? []
|
|
8032
|
+
),
|
|
8033
|
+
suggestedTruthDocs: applicability.state === "needs_routing_review" ? [] : uniqueSorted4(impactSet?.affectedTruthDocs ?? []),
|
|
8034
|
+
openQuestions: uniqueSorted4([
|
|
8035
|
+
...applicability.reasons,
|
|
8036
|
+
...diagnosticQuestions
|
|
8037
|
+
]),
|
|
8038
|
+
skippedHelperStatus: helpers.map((helper) => ({
|
|
8039
|
+
helper: helper.id,
|
|
8040
|
+
status: "skipped",
|
|
8041
|
+
reason: "workflow status does not run optional helpers; inspect the checkout directly or run the helper manually when useful."
|
|
8042
|
+
}))
|
|
8043
|
+
};
|
|
8219
8044
|
};
|
|
8220
8045
|
var contextDataFor = (workflow, repoIndex, config, impactSet) => {
|
|
8221
8046
|
if (!config) {
|
|
8222
8047
|
return {};
|
|
8223
8048
|
}
|
|
8224
8049
|
const routeFiles = routeFilesFor(repoIndex);
|
|
8225
|
-
const
|
|
8226
|
-
|
|
8050
|
+
const indexedTruthDocs = uniqueSorted4(
|
|
8051
|
+
repoIndex.routeMap.routes.flatMap((route) => route.truthDocs)
|
|
8227
8052
|
);
|
|
8053
|
+
const primaryTruthDocs = uniqueSorted4(impactSet?.affectedTruthDocs ?? []);
|
|
8054
|
+
const candidateStaleTruthDocs = workflow === "truthmark-sync" ? candidateStaleTruthDocsFor(indexedTruthDocs, primaryTruthDocs) : [];
|
|
8055
|
+
const truthDocs = workflow === "truthmark-sync" ? uniqueSorted4([...primaryTruthDocs, ...candidateStaleTruthDocs]) : uniqueSorted4(impactSet?.affectedTruthDocs ?? indexedTruthDocs);
|
|
8228
8056
|
return {
|
|
8229
8057
|
routeIndexPath: config.truthmark.paths.routesIndex,
|
|
8230
8058
|
routeFiles,
|
|
8231
8059
|
truthRoot: config.truthmark.paths.truthRoot,
|
|
8232
8060
|
truthDocs,
|
|
8061
|
+
primaryTruthDocs: workflow === "truthmark-sync" ? primaryTruthDocs : truthDocs,
|
|
8062
|
+
candidateStaleTruthDocs,
|
|
8233
8063
|
starterTruthDocs: workflow === "truthmark-structure" ? truthDocs : [],
|
|
8234
|
-
codeWritePaths: workflow === "truthmark-realize" ?
|
|
8064
|
+
codeWritePaths: workflow === "truthmark-realize" ? uniqueSorted4(
|
|
8065
|
+
impactSet?.affectedRoutes.flatMap((route) => route.codeSurface) ?? []
|
|
8066
|
+
) : [],
|
|
8235
8067
|
portalEnabled: config.truthmark.generated.portal.enabled,
|
|
8236
8068
|
portalOutputPath: config.truthmark.paths.portalOutput,
|
|
8237
8069
|
routes: repoIndex.routeMap.routes
|
|
8238
8070
|
};
|
|
8239
8071
|
};
|
|
8240
8072
|
var nextStepsFor = (workflow, applicability, comparisonBase) => {
|
|
8241
|
-
if (applicability.state === "
|
|
8242
|
-
return [
|
|
8073
|
+
if (applicability.state === "needs_routing_review") {
|
|
8074
|
+
return [
|
|
8075
|
+
"Run Truth Structure or repair route ownership before writing truth docs."
|
|
8076
|
+
];
|
|
8243
8077
|
}
|
|
8244
|
-
if ((workflow === "truthmark-sync" || workflow === "truthmark-realize") && applicability.state === "
|
|
8078
|
+
if ((workflow === "truthmark-sync" || workflow === "truthmark-realize") && applicability.state === "needs_manual_review" && !comparisonBase) {
|
|
8245
8079
|
return [
|
|
8246
|
-
workflow === "truthmark-sync" ? "Rerun with --base <ref> so
|
|
8080
|
+
workflow === "truthmark-sync" ? "Rerun with --base <ref> so the helper can suggest changed-file impact." : "Rerun with --base <ref> so the helper can suggest bounded code-write paths."
|
|
8247
8081
|
];
|
|
8248
8082
|
}
|
|
8249
8083
|
return [];
|
|
@@ -8258,28 +8092,47 @@ var buildWorkflowState = async (cwd, options) => {
|
|
|
8258
8092
|
const loadResult = await loadConfig(rootDir);
|
|
8259
8093
|
const comparisonBase = options.base ? options.base : options.workflow === "truthmark-sync" ? await selectComparisonBase(rootDir) : null;
|
|
8260
8094
|
const impactSet = comparisonBase ? await buildImpactSet(rootDir, { base: comparisonBase }) : null;
|
|
8261
|
-
const checkResult = await runCheck(
|
|
8095
|
+
const checkResult = await runCheck(
|
|
8096
|
+
cwd,
|
|
8097
|
+
comparisonBase ? { base: comparisonBase } : {}
|
|
8098
|
+
);
|
|
8262
8099
|
const diagnostics = [
|
|
8263
8100
|
...loadResult.diagnostics,
|
|
8264
8101
|
...repoIndex.diagnostics,
|
|
8265
8102
|
...impactSet?.diagnostics ?? [],
|
|
8266
8103
|
...checkResult.diagnostics
|
|
8267
8104
|
];
|
|
8268
|
-
const applicability = applicabilityFor(
|
|
8269
|
-
|
|
8105
|
+
const applicability = applicabilityFor(
|
|
8106
|
+
options.workflow,
|
|
8107
|
+
diagnostics,
|
|
8108
|
+
impactSet
|
|
8109
|
+
);
|
|
8110
|
+
const actionData = applicability.state === "needs_manual_review" || applicability.state === "needs_routing_review" ? {} : contextDataFor(
|
|
8111
|
+
options.workflow,
|
|
8112
|
+
repoIndex,
|
|
8113
|
+
loadResult.config,
|
|
8114
|
+
impactSet
|
|
8115
|
+
);
|
|
8270
8116
|
return {
|
|
8271
8117
|
schemaVersion: "truthmark-workflow/v0",
|
|
8272
8118
|
workflow: options.workflow,
|
|
8273
8119
|
applicability,
|
|
8274
8120
|
actionContext: buildWorkflowActionContext(manifestEntry, actionData),
|
|
8121
|
+
workflowCard: workflowCardFor(
|
|
8122
|
+
options.workflow,
|
|
8123
|
+
applicability,
|
|
8124
|
+
diagnostics,
|
|
8125
|
+
impactSet
|
|
8126
|
+
),
|
|
8275
8127
|
changedFiles: impactSet?.changedFiles ?? [],
|
|
8276
8128
|
affectedRoutes: impactSet?.affectedRoutes ?? [],
|
|
8277
|
-
targetTruthDocs: applicability.state === "
|
|
8129
|
+
targetTruthDocs: applicability.state === "needs_routing_review" ? [] : impactSet?.affectedTruthDocs ?? [],
|
|
8278
8130
|
diagnostics,
|
|
8279
8131
|
checks: {
|
|
8280
|
-
|
|
8132
|
+
reviewChecklist: [...manifestEntry.reviewQuestions],
|
|
8281
8133
|
recommended: [...manifestEntry.positiveTriggers],
|
|
8282
|
-
helpers: helperCommandsFor2(options.workflow)
|
|
8134
|
+
helpers: helperCommandsFor2(options.workflow),
|
|
8135
|
+
affectedTests: impactSet?.affectedTests ?? []
|
|
8283
8136
|
},
|
|
8284
8137
|
nextSteps: nextStepsFor(options.workflow, applicability, comparisonBase),
|
|
8285
8138
|
reportSections: [...manifestEntry.reportSections]
|
|
@@ -8287,7 +8140,7 @@ var buildWorkflowState = async (cwd, options) => {
|
|
|
8287
8140
|
};
|
|
8288
8141
|
|
|
8289
8142
|
// src/cli/handlers.ts
|
|
8290
|
-
import
|
|
8143
|
+
import fs20 from "fs/promises";
|
|
8291
8144
|
|
|
8292
8145
|
// src/agents/workflow-helper-validation.ts
|
|
8293
8146
|
import { parse as parseYaml2 } from "yaml";
|
|
@@ -8398,6 +8251,7 @@ var validateTruthSyncReportText = (text) => {
|
|
|
8398
8251
|
["Changed code reviewed", report.changedCode],
|
|
8399
8252
|
["Ownership reviewed", report.ownershipReviewed],
|
|
8400
8253
|
["Truth docs updated", report.truthDocsUpdated],
|
|
8254
|
+
["Decision/rationale captured", report.decisionRationaleCaptured],
|
|
8401
8255
|
["Notes", report.notes]
|
|
8402
8256
|
]) {
|
|
8403
8257
|
if (items.length === 0) {
|
|
@@ -8413,6 +8267,9 @@ var validateTruthSyncReportText = (text) => {
|
|
|
8413
8267
|
"Evidence checked entries include Claim:, Evidence:, and Result: supported | narrowed | removed | blocked"
|
|
8414
8268
|
);
|
|
8415
8269
|
}
|
|
8270
|
+
if (report.syncIntent !== void 0) {
|
|
8271
|
+
checks.push("Sync Intent");
|
|
8272
|
+
}
|
|
8416
8273
|
validateHelperScriptEntries(
|
|
8417
8274
|
report.helperScripts,
|
|
8418
8275
|
["validate-write-lease"],
|
|
@@ -8649,12 +8506,6 @@ var runImpact = async (options) => {
|
|
|
8649
8506
|
}
|
|
8650
8507
|
};
|
|
8651
8508
|
};
|
|
8652
|
-
var isContextPackWorkflow = (value) => {
|
|
8653
|
-
return value === "truth-sync" || value === "truth-document" || value === "truth-realize";
|
|
8654
|
-
};
|
|
8655
|
-
var isContextMarkdownFormat = (value) => {
|
|
8656
|
-
return value === void 0 || value === "markdown";
|
|
8657
|
-
};
|
|
8658
8509
|
var isTruthmarkWorkflowId = (value) => {
|
|
8659
8510
|
return typeof value === "string" && TRUTHMARK_WORKFLOW_IDS.includes(value);
|
|
8660
8511
|
};
|
|
@@ -8674,7 +8525,7 @@ var invalidWorkflowResult = (command, workflow) => ({
|
|
|
8674
8525
|
});
|
|
8675
8526
|
var readHelperFile = async (filePath, helper) => {
|
|
8676
8527
|
try {
|
|
8677
|
-
return await
|
|
8528
|
+
return await fs20.readFile(filePath, "utf8");
|
|
8678
8529
|
} catch (error) {
|
|
8679
8530
|
const message = error instanceof Error ? error.message : String(error);
|
|
8680
8531
|
return { ok: false, helper, errors: [`could not read file: ${message}`] };
|
|
@@ -8699,63 +8550,6 @@ var runValidateWriteLease = async (leaseFile, changedFilesFile) => {
|
|
|
8699
8550
|
}
|
|
8700
8551
|
return validateWriteLeaseText(leaseText, changedText);
|
|
8701
8552
|
};
|
|
8702
|
-
var runContext = async (options) => {
|
|
8703
|
-
if (!isContextPackWorkflow(options.workflow)) {
|
|
8704
|
-
return {
|
|
8705
|
-
command: "context",
|
|
8706
|
-
summary: "Truthmark context requires a supported --workflow value.",
|
|
8707
|
-
diagnostics: [
|
|
8708
|
-
{
|
|
8709
|
-
category: "context-pack",
|
|
8710
|
-
severity: "error",
|
|
8711
|
-
message: "truthmark context requires --workflow truth-sync, truth-document, or truth-realize."
|
|
8712
|
-
}
|
|
8713
|
-
]
|
|
8714
|
-
};
|
|
8715
|
-
}
|
|
8716
|
-
if (options.format === "json") {
|
|
8717
|
-
return {
|
|
8718
|
-
command: "context",
|
|
8719
|
-
summary: "Truthmark context no longer supports JSON ContextPack output.",
|
|
8720
|
-
diagnostics: [
|
|
8721
|
-
{
|
|
8722
|
-
category: "context-pack",
|
|
8723
|
-
severity: "error",
|
|
8724
|
-
message: "JSON ContextPack output was removed in v2; use --format markdown."
|
|
8725
|
-
}
|
|
8726
|
-
]
|
|
8727
|
-
};
|
|
8728
|
-
}
|
|
8729
|
-
if (!isContextMarkdownFormat(options.format)) {
|
|
8730
|
-
return {
|
|
8731
|
-
command: "context",
|
|
8732
|
-
summary: "Truthmark context requires a supported --format value.",
|
|
8733
|
-
diagnostics: [
|
|
8734
|
-
{
|
|
8735
|
-
category: "context-pack",
|
|
8736
|
-
severity: "error",
|
|
8737
|
-
message: "truthmark context supports only --format markdown; JSON ContextPack output was removed in v2."
|
|
8738
|
-
}
|
|
8739
|
-
]
|
|
8740
|
-
};
|
|
8741
|
-
}
|
|
8742
|
-
const contextPack = await buildContextPack(process.cwd(), {
|
|
8743
|
-
workflow: options.workflow,
|
|
8744
|
-
base: options.base
|
|
8745
|
-
});
|
|
8746
|
-
const diagnostics = contextPack.warnings;
|
|
8747
|
-
const summary = `Truthmark context generated ${contextPack.workflow} ContextPack with ${diagnostics.length} warnings.`;
|
|
8748
|
-
const markdown = renderContextPackMarkdown(contextPack);
|
|
8749
|
-
return {
|
|
8750
|
-
command: "context",
|
|
8751
|
-
summary,
|
|
8752
|
-
diagnostics,
|
|
8753
|
-
data: {
|
|
8754
|
-
markdown,
|
|
8755
|
-
summary
|
|
8756
|
-
}
|
|
8757
|
-
};
|
|
8758
|
-
};
|
|
8759
8553
|
var runWorkflowStatus = async (options) => {
|
|
8760
8554
|
if (!isTruthmarkWorkflowId(options.workflow)) {
|
|
8761
8555
|
return invalidWorkflowResult("workflow status", options.workflow);
|
|
@@ -8790,14 +8584,6 @@ var writeResult = (result, options) => {
|
|
|
8790
8584
|
`);
|
|
8791
8585
|
markFailedWhenErrorDiagnosticsExist(result);
|
|
8792
8586
|
};
|
|
8793
|
-
var writeContextResult = (result, options) => {
|
|
8794
|
-
if (!options.json && typeof result.data?.markdown === "string") {
|
|
8795
|
-
process.stdout.write(result.data.markdown);
|
|
8796
|
-
markFailedWhenErrorDiagnosticsExist(result);
|
|
8797
|
-
return;
|
|
8798
|
-
}
|
|
8799
|
-
writeResult(result, options);
|
|
8800
|
-
};
|
|
8801
8587
|
var renderValidationHuman = (result) => {
|
|
8802
8588
|
if (result.ok === true) {
|
|
8803
8589
|
return [`${result.helper}: ok`, ...result.checks.map((check) => `- ${check}`)].join("\n");
|
|
@@ -8842,7 +8628,7 @@ var buildProgram = () => {
|
|
|
8842
8628
|
writeResult(await runCheck2({ base: options.base }), options);
|
|
8843
8629
|
});
|
|
8844
8630
|
addJsonOption(
|
|
8845
|
-
program.command("index").description("
|
|
8631
|
+
program.command("index").description("Inspect derived Truthmark workflow routing metadata for the current checkout.")
|
|
8846
8632
|
).action(async (options) => {
|
|
8847
8633
|
writeResult(await runIndex(), options);
|
|
8848
8634
|
});
|
|
@@ -8851,18 +8637,6 @@ var buildProgram = () => {
|
|
|
8851
8637
|
).action(async (options) => {
|
|
8852
8638
|
writeResult(await runImpact({ base: options.base }), options);
|
|
8853
8639
|
});
|
|
8854
|
-
addJsonOption(
|
|
8855
|
-
program.command("context").description("Generate a bounded workflow context pack.").requiredOption("--workflow <workflow>", "Workflow name: truth-sync, truth-document, or truth-realize").option("--base <ref>", "Base Git ref for impact-backed packs").option("--format <format>", "Output format: markdown", "markdown")
|
|
8856
|
-
).action(async (options) => {
|
|
8857
|
-
writeContextResult(
|
|
8858
|
-
await runContext({
|
|
8859
|
-
workflow: options.workflow,
|
|
8860
|
-
base: options.base,
|
|
8861
|
-
format: options.format
|
|
8862
|
-
}),
|
|
8863
|
-
options
|
|
8864
|
-
);
|
|
8865
|
-
});
|
|
8866
8640
|
const workflow = program.command("workflow").description("Inspect agent-facing Truthmark workflow state.");
|
|
8867
8641
|
addJsonOption(
|
|
8868
8642
|
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")
|