truthmark 2.2.1 → 2.2.3
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 +4 -5
- package/README.es.md +4 -5
- package/README.md +6 -7
- package/README.ru.md +5 -6
- package/README.zh.md +4 -5
- package/dist/main.js +718 -517
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -256,13 +256,7 @@ var SUPPORTED_PLATFORMS = [
|
|
|
256
256
|
"github-copilot",
|
|
257
257
|
"gemini-cli"
|
|
258
258
|
];
|
|
259
|
-
var DEFAULT_PLATFORMS = [
|
|
260
|
-
"codex",
|
|
261
|
-
"opencode",
|
|
262
|
-
"claude-code",
|
|
263
|
-
"github-copilot",
|
|
264
|
-
"gemini-cli"
|
|
265
|
-
];
|
|
259
|
+
var DEFAULT_PLATFORMS = [];
|
|
266
260
|
var truthmarkConfigSchema = {
|
|
267
261
|
type: "object",
|
|
268
262
|
additionalProperties: false,
|
|
@@ -368,7 +362,6 @@ var DERIVED_TRUTHMARK_PATHS = {
|
|
|
368
362
|
var DEFAULT_INSTRUCTION_TARGETS = ["AGENTS.md"];
|
|
369
363
|
var createDefaultRawConfig = () => ({
|
|
370
364
|
version: 2,
|
|
371
|
-
platforms: [...DEFAULT_PLATFORMS],
|
|
372
365
|
truthmark: {
|
|
373
366
|
workspace: DEFAULT_TRUTHMARK_WORKSPACE.workspace,
|
|
374
367
|
generated: {
|
|
@@ -853,7 +846,7 @@ var renderChildAreaTemplate = (config) => {
|
|
|
853
846
|
const defaultArea = config.truthmark.routes.defaultArea;
|
|
854
847
|
const title = titleCase(defaultArea);
|
|
855
848
|
const truthDocsRoot = truthRoot(config);
|
|
856
|
-
const
|
|
849
|
+
const bootstrapTruthDoc = `${truthDocsRoot}/${defaultArea}/bootstrap-routing.md`;
|
|
857
850
|
const templatePath = `${config.truthmark.paths.routeAreasRoot}/${defaultArea}.md`;
|
|
858
851
|
const sourceOfTruth = resolveRelativePath(
|
|
859
852
|
templatePath,
|
|
@@ -873,16 +866,20 @@ var renderChildAreaTemplate = (config) => {
|
|
|
873
866
|
"Truth documents:",
|
|
874
867
|
"```yaml",
|
|
875
868
|
"truth_documents:",
|
|
876
|
-
` - path: ${
|
|
877
|
-
" kind: engineering-
|
|
869
|
+
` - path: ${bootstrapTruthDoc}`,
|
|
870
|
+
" kind: engineering-workflow",
|
|
878
871
|
" lane: engineering",
|
|
879
872
|
"```",
|
|
880
873
|
"",
|
|
874
|
+
"This is a provisional bootstrap route. It exists only to make fresh repositories routeable until real product, service, domain, or ownership areas are created.",
|
|
875
|
+
"",
|
|
881
876
|
"Code surface:",
|
|
882
877
|
"- src/**",
|
|
883
878
|
"",
|
|
884
879
|
"Update truth when:",
|
|
885
|
-
"-
|
|
880
|
+
"- this provisional bootstrap route is the only match for a real code surface",
|
|
881
|
+
"- route ownership is still broad, mixed, or ambiguous",
|
|
882
|
+
"- Run Truth Structure before normal Truth Sync so the touched code gets a bounded owner",
|
|
886
883
|
"",
|
|
887
884
|
"## Source References",
|
|
888
885
|
"",
|
|
@@ -890,6 +887,103 @@ var renderChildAreaTemplate = (config) => {
|
|
|
890
887
|
""
|
|
891
888
|
].join("\n");
|
|
892
889
|
};
|
|
890
|
+
var renderBootstrapRoutingDocTemplate = (config) => {
|
|
891
|
+
const defaultArea = config.truthmark.routes.defaultArea;
|
|
892
|
+
const title = titleCase(defaultArea);
|
|
893
|
+
const templatePath = `${truthRoot(config)}/${defaultArea}/bootstrap-routing.md`;
|
|
894
|
+
const routePath = `${config.truthmark.paths.routeAreasRoot}/${defaultArea}.md`;
|
|
895
|
+
const routeSource = resolveRelativePath(templatePath, routePath);
|
|
896
|
+
const configSource = resolveRelativePath(
|
|
897
|
+
templatePath,
|
|
898
|
+
".truthmark/config.yml"
|
|
899
|
+
);
|
|
900
|
+
const today = currentDate();
|
|
901
|
+
return [
|
|
902
|
+
"---",
|
|
903
|
+
"status: active",
|
|
904
|
+
"truth_kind: engineering-workflow",
|
|
905
|
+
`last_reviewed: ${today}`,
|
|
906
|
+
"---",
|
|
907
|
+
"",
|
|
908
|
+
`# ${title} Bootstrap Routing`,
|
|
909
|
+
"",
|
|
910
|
+
"## Purpose",
|
|
911
|
+
"",
|
|
912
|
+
`This doc records the provisional broad route for ${defaultArea}.`,
|
|
913
|
+
"This doc is a bootstrap handoff, not a behavior truth dumping ground.",
|
|
914
|
+
"It is not a substitute for bounded product and engineering truth docs.",
|
|
915
|
+
"",
|
|
916
|
+
"## Scope",
|
|
917
|
+
"",
|
|
918
|
+
"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/**`.",
|
|
919
|
+
"It does not own implementation behavior under that code surface.",
|
|
920
|
+
"",
|
|
921
|
+
"## Current Implementation Behavior",
|
|
922
|
+
"",
|
|
923
|
+
"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.",
|
|
924
|
+
"",
|
|
925
|
+
"## Product Truth Links",
|
|
926
|
+
"",
|
|
927
|
+
"- None. This is an engineering bootstrap handoff for routing setup, not a product promise.",
|
|
928
|
+
"",
|
|
929
|
+
"## Triggers",
|
|
930
|
+
"",
|
|
931
|
+
"- A real code change maps only to this provisional broad route.",
|
|
932
|
+
"- Truth Sync cannot identify a specific behavior-owned route and bounded truth owner.",
|
|
933
|
+
"- A maintainer or agent is onboarding the first real product, service, domain, package, or ownership area.",
|
|
934
|
+
"",
|
|
935
|
+
"## Inputs",
|
|
936
|
+
"",
|
|
937
|
+
"- Current route files under the configured Truthmark route root.",
|
|
938
|
+
"- The touched code, tests, configuration, and existing docs needed to infer the smallest real owner.",
|
|
939
|
+
"- Repository instruction files that exist in the checkout.",
|
|
940
|
+
"",
|
|
941
|
+
"## Execution Model",
|
|
942
|
+
"",
|
|
943
|
+
"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.",
|
|
944
|
+
"",
|
|
945
|
+
"## Steps",
|
|
946
|
+
"",
|
|
947
|
+
"1. Treat this route as provisional and insufficient for normal behavior maintenance.",
|
|
948
|
+
"2. Inspect the touched code/test surface and infer the narrowest durable owner.",
|
|
949
|
+
"3. Create or repair route entries and truth docs for that owner.",
|
|
950
|
+
"4. Leave this bootstrap doc small; do not append behavior details here.",
|
|
951
|
+
"5. Resume Truth Sync only after the touched code resolves to a bounded owner.",
|
|
952
|
+
"",
|
|
953
|
+
"## State, Retry, And Failure Behavior",
|
|
954
|
+
"",
|
|
955
|
+
"If ownership cannot be inferred safely, stop and report manual-review files instead of widening this route or adding generic behavior prose.",
|
|
956
|
+
"",
|
|
957
|
+
"## Outputs",
|
|
958
|
+
"",
|
|
959
|
+
"- Bounded route areas and lane-appropriate truth docs for the touched surface.",
|
|
960
|
+
"- A compact manual handoff report when ownership remains ambiguous.",
|
|
961
|
+
"",
|
|
962
|
+
"## Engineering Decisions",
|
|
963
|
+
"",
|
|
964
|
+
`- 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.`,
|
|
965
|
+
"",
|
|
966
|
+
"## Rationale",
|
|
967
|
+
"",
|
|
968
|
+
"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.",
|
|
969
|
+
"",
|
|
970
|
+
"## Non-Goals",
|
|
971
|
+
"",
|
|
972
|
+
"- This doc is not a repository behavior overview.",
|
|
973
|
+
"- This doc is not a product capability or engineering behavior owner.",
|
|
974
|
+
"- This doc is not a permanent home for claims about files under `src/**`.",
|
|
975
|
+
"",
|
|
976
|
+
"## Maintenance Notes",
|
|
977
|
+
"",
|
|
978
|
+
"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.",
|
|
979
|
+
"",
|
|
980
|
+
"## Source References",
|
|
981
|
+
"",
|
|
982
|
+
`- ${routeSource}`,
|
|
983
|
+
`- ${configSource}`,
|
|
984
|
+
""
|
|
985
|
+
].join("\n");
|
|
986
|
+
};
|
|
893
987
|
var renderTruthRootReadmeTemplate = (config = createDefaultConfig(), lane = "engineering") => {
|
|
894
988
|
const templatePath = `${lane === "product" ? resolveProductTruthRoot(config) : resolveEngineeringTruthRoot(config)}/README.md`;
|
|
895
989
|
const sourceOfTruth = resolveRelativePath(
|
|
@@ -940,7 +1034,7 @@ var renderTruthDomainReadmeTemplate = (config) => {
|
|
|
940
1034
|
"",
|
|
941
1035
|
"Current leaf docs:",
|
|
942
1036
|
"",
|
|
943
|
-
"- [
|
|
1037
|
+
"- [Bootstrap routing](bootstrap-routing.md)",
|
|
944
1038
|
"",
|
|
945
1039
|
"## Source References",
|
|
946
1040
|
"",
|
|
@@ -948,7 +1042,6 @@ var renderTruthDomainReadmeTemplate = (config) => {
|
|
|
948
1042
|
""
|
|
949
1043
|
].join("\n");
|
|
950
1044
|
};
|
|
951
|
-
var BEHAVIOR_DOC_TEMPLATE_PATH = "docs/truthmark/templates/engineering-behavior.md";
|
|
952
1045
|
var renderTemplateSection = (section) => {
|
|
953
1046
|
return [
|
|
954
1047
|
section.heading,
|
|
@@ -1363,7 +1456,7 @@ var renderContractDocTemplateFile = () => {
|
|
|
1363
1456
|
]),
|
|
1364
1457
|
sectionSpec("## Compatibility Rules", [
|
|
1365
1458
|
"State backward/forward compatibility guarantees, tolerated inputs, deprecation rules, and breaking-change triggers.",
|
|
1366
|
-
"Include compatibility tests or review
|
|
1459
|
+
"Include compatibility tests or review questions that protect the contract."
|
|
1367
1460
|
]),
|
|
1368
1461
|
sectionSpec("## Versioning And Migration", [
|
|
1369
1462
|
"Document version negotiation, schema/API version fields, rollout requirements, migration steps, and rollback expectations.",
|
|
@@ -1411,7 +1504,7 @@ var renderWorkflowDocTemplateFile = () => {
|
|
|
1411
1504
|
]),
|
|
1412
1505
|
sectionSpec("## Execution Model", [
|
|
1413
1506
|
"Describe synchronous/asynchronous execution, concurrency, locking, leases, batching, ordering, and idempotency behavior.",
|
|
1414
|
-
"State whether the workflow
|
|
1507
|
+
"State whether the workflow waits for user action, runs in the background, is distributed, or is delegated to another system."
|
|
1415
1508
|
]),
|
|
1416
1509
|
sectionSpec("## Steps", [
|
|
1417
1510
|
"Capture the current ordered steps or phases at a level useful for maintenance and review.",
|
|
@@ -1447,7 +1540,7 @@ var renderOperationsDocTemplateFile = () => {
|
|
|
1447
1540
|
]),
|
|
1448
1541
|
sectionSpec("## Deployment And Rollback", [
|
|
1449
1542
|
"Describe deployment mechanism, migration ordering, compatibility windows, rollback path, and known irreversible operations.",
|
|
1450
|
-
"Call out manual
|
|
1543
|
+
"Call out manual review points, smoke checks, and post-deploy verification responsibilities."
|
|
1451
1544
|
]),
|
|
1452
1545
|
sectionSpec("## Availability And Observability", [
|
|
1453
1546
|
"Capture availability expectations, health checks, metrics, logs, traces, alerts, SLO/error-budget signals, and known blind spots.",
|
|
@@ -1483,39 +1576,6 @@ var renderTestBehaviorDocTemplateFile = () => {
|
|
|
1483
1576
|
])
|
|
1484
1577
|
]);
|
|
1485
1578
|
};
|
|
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
1579
|
|
|
1520
1580
|
// src/config/command.ts
|
|
1521
1581
|
var CONFIG_PATH = ".truthmark/config.yml";
|
|
@@ -1840,22 +1900,6 @@ var rootIndexReferencesChildRoute = async (rootDir, rootIndexPath, childRoutePat
|
|
|
1840
1900
|
var truthTemplatePath = (config, fileName) => {
|
|
1841
1901
|
return `${config.truthmark.paths.templatesRoot}/${fileName}`;
|
|
1842
1902
|
};
|
|
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
1903
|
var ensureOrUpdateTruthDocTemplate = async (rootDir, templatePath, defaultTemplate) => {
|
|
1860
1904
|
const seededResult = await ensureRepoFile(
|
|
1861
1905
|
rootDir,
|
|
@@ -1970,17 +2014,19 @@ var scaffoldHierarchy = async (rootDir, config) => {
|
|
|
1970
2014
|
renderProductCapabilityDocTemplateFile()
|
|
1971
2015
|
)
|
|
1972
2016
|
);
|
|
1973
|
-
const behaviorDocTemplate = await readBehaviorDocTemplate(rootDir, config);
|
|
1974
2017
|
results.push(
|
|
1975
2018
|
await ensureRepoFile(
|
|
1976
2019
|
rootDir,
|
|
1977
|
-
`${truthDomainRoot}/
|
|
1978
|
-
|
|
2020
|
+
`${truthDomainRoot}/bootstrap-routing.md`,
|
|
2021
|
+
renderBootstrapRoutingDocTemplate(config)
|
|
1979
2022
|
)
|
|
1980
2023
|
);
|
|
1981
2024
|
return results;
|
|
1982
2025
|
};
|
|
1983
2026
|
|
|
2027
|
+
// src/checks/generated-surfaces.ts
|
|
2028
|
+
import fs6 from "fs/promises";
|
|
2029
|
+
|
|
1984
2030
|
// src/truth/evidence.ts
|
|
1985
2031
|
var renderClaimEvidenceCheckedSection = (items) => {
|
|
1986
2032
|
return [
|
|
@@ -2029,7 +2075,7 @@ var renderReadOnlyLaneClassificationRuleBlock = (config = defaultAgentConfig(),
|
|
|
2029
2075
|
`product-lane ownership belongs under ${productTruthRoot} and describes product promises, boundaries, rationale, decisions, and success criteria`,
|
|
2030
2076
|
`engineering-lane ownership belongs under ${engineeringTruthRoot} and describes source-backed current realization, contracts, architecture, workflows, operations, or tests`,
|
|
2031
2077
|
"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
|
|
2078
|
+
"ambiguous lane ownership should be reported for manual handoff or routed to Truth Structure",
|
|
2033
2079
|
LANE_INVARIANT
|
|
2034
2080
|
].join("\n"),
|
|
2035
2081
|
indent
|
|
@@ -2045,12 +2091,12 @@ var renderLaneClassificationInstructions = (config = defaultAgentConfig()) => {
|
|
|
2045
2091
|
const productTruthRoot = resolveProductTruthRoot(config);
|
|
2046
2092
|
const engineeringTruthRoot = resolveEngineeringTruthRoot(config);
|
|
2047
2093
|
return [
|
|
2048
|
-
"Lane
|
|
2094
|
+
"Lane review questions:",
|
|
2049
2095
|
"- before writing canonical truth docs, classify the request or change as product-lane, engineering-lane, both-lane, or ambiguous",
|
|
2050
2096
|
`- product-lane writes belong under ${productTruthRoot} and state product promises, boundaries, rationale, decisions, and success criteria`,
|
|
2051
2097
|
`- engineering-lane writes belong under ${engineeringTruthRoot} and state source-backed current realization, contracts, architecture, workflows, operations, or tests`,
|
|
2052
2098
|
"- 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
|
|
2099
|
+
"- ambiguous lane ownership must stop or invoke Truth Structure instead of writing a mixed document",
|
|
2054
2100
|
`- ${LANE_INVARIANT}`
|
|
2055
2101
|
].join("\n");
|
|
2056
2102
|
};
|
|
@@ -2071,31 +2117,32 @@ var FEATURE_DOC_TEMPLATE_INSTRUCTIONS = [
|
|
|
2071
2117
|
"If the template is missing, use lane-specific sections: product truth says what must be true and why; engineering truth says how the repository currently realizes it.",
|
|
2072
2118
|
"Teams may edit template files under the configured Truthmark templates root to define their local truth-doc standards."
|
|
2073
2119
|
].join("\n");
|
|
2120
|
+
var TRUTH_DOC_AUTHORING_STYLE_INSTRUCTIONS = "Prefer diff-friendly Markdown: one durable claim per bullet or line, paragraphs no longer than one or two short sentences, and bullets or tables for rules, criteria, fields, files, and lists.";
|
|
2074
2121
|
var renderTruthDocOwnershipGateSection = (subject, outcome) => {
|
|
2075
2122
|
return [
|
|
2076
|
-
"Truth-doc ownership
|
|
2123
|
+
"Truth-doc ownership review:",
|
|
2077
2124
|
`- before editing or relying on ${subject}, verify each target/source truth doc is a bounded owner for the behavior`,
|
|
2078
2125
|
"- 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
2126
|
`- ${outcome}`,
|
|
2080
|
-
"- report Ownership reviewed, Structure required, Truth docs split, Truth docs restructured, or
|
|
2127
|
+
"- report Ownership reviewed, Structure required, Truth docs split, Truth docs restructured, or Manual handoff reason as applicable"
|
|
2081
2128
|
].join("\n");
|
|
2082
2129
|
};
|
|
2083
2130
|
var TRUTH_DOC_DECISION_RATIONALE_PRESERVATION_INSTRUCTIONS = [
|
|
2084
|
-
"Decision/Rationale preservation
|
|
2131
|
+
"Decision/Rationale preservation review:",
|
|
2085
2132
|
"- 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
2133
|
"- 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
2134
|
"- 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,
|
|
2135
|
+
"- if ownership of a decision or rationale is unclear, stop with manual-review files instead of deleting it or guessing",
|
|
2089
2136
|
"- 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
2137
|
].join("\n");
|
|
2091
2138
|
var renderTruthDocRestructureGateSection = (scope) => {
|
|
2092
2139
|
return [
|
|
2093
|
-
"Truth-doc shape repair
|
|
2140
|
+
"Truth-doc shape repair review:",
|
|
2094
2141
|
`- ${scope}`,
|
|
2095
|
-
"- repair shape in place only after the ownership
|
|
2142
|
+
"- repair shape in place only after the ownership review confirms the doc is the right bounded owner",
|
|
2096
2143
|
"- use Truth Structure for ownership splits; do not treat broad or mixed-owner docs as in-place repair work",
|
|
2097
2144
|
"- 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
|
|
2145
|
+
"- preserve supported claims; remove, narrow, or record unsupported or stale claims for manual handoff",
|
|
2099
2146
|
"- report docs restructured and why a narrow edit was not sufficient"
|
|
2100
2147
|
].join("\n");
|
|
2101
2148
|
};
|
|
@@ -2105,26 +2152,26 @@ var ARCHITECTURE_DOC_BOUNDARY_INSTRUCTIONS = [
|
|
|
2105
2152
|
].join("\n");
|
|
2106
2153
|
var renderRouteFirstEvidenceGateSection = (subject, noImpactedDocOutcome) => {
|
|
2107
2154
|
return [
|
|
2108
|
-
"Evidence
|
|
2155
|
+
"Evidence checklist:",
|
|
2109
2156
|
`- route-first: map ${subject} to bounded route owners and primary canonical docs`,
|
|
2110
2157
|
"- review new or changed behavior-bearing claims only in touched docs, route ownership, lane-specific decisions, and rationale",
|
|
2111
2158
|
"- support claims with primary checkout evidence: implementation, config, routing, generated templates, schemas, or contract definitions",
|
|
2112
2159
|
"- tests/examples/canonical docs corroborate; they are not sole proof when implementation conflicts",
|
|
2113
|
-
"- remove, narrow, or
|
|
2160
|
+
"- remove, narrow, or record unsupported claims for manual handoff",
|
|
2114
2161
|
`- ${noImpactedDocOutcome}`
|
|
2115
2162
|
].join("\n");
|
|
2116
2163
|
};
|
|
2117
2164
|
var renderTopologyEvidenceGateSection = () => {
|
|
2118
2165
|
return [
|
|
2119
|
-
"Evidence
|
|
2120
|
-
"- apply the
|
|
2166
|
+
"Evidence checklist:",
|
|
2167
|
+
"- apply the evidence checklist before finishing when Truth Structure writes routed docs, ownership claims, lane-specific decisions, or rationale",
|
|
2121
2168
|
"- 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
|
|
2169
|
+
"- tests/examples/canonical docs corroborate; remove, narrow, or record unsupported claims for manual handoff"
|
|
2123
2170
|
].join("\n");
|
|
2124
2171
|
};
|
|
2125
2172
|
var renderAuditEvidenceGateSection = () => {
|
|
2126
2173
|
return [
|
|
2127
|
-
"Evidence
|
|
2174
|
+
"Evidence checklist:",
|
|
2128
2175
|
"- support each finding and suggested fix with evidence from config, route files, canonical docs, implementation, templates, or tests",
|
|
2129
2176
|
"- canonical docs are context, not sole proof when implementation conflicts",
|
|
2130
2177
|
"- remove unsupported findings or mark open questions; validate changed claims if you edit docs"
|
|
@@ -2259,22 +2306,15 @@ var renderHierarchySummary = (config) => {
|
|
|
2259
2306
|
].join("\n");
|
|
2260
2307
|
};
|
|
2261
2308
|
|
|
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
2309
|
// src/templates/agents-block.ts
|
|
2270
2310
|
var TRUTHMARK_BLOCK_START = "<!-- truthmark:start -->";
|
|
2271
2311
|
var TRUTHMARK_BLOCK_END = "<!-- truthmark:end -->";
|
|
2272
2312
|
var renderCompactHierarchySummary = (config) => {
|
|
2273
2313
|
const productTruthRoot = resolveProductTruthRoot(config);
|
|
2274
2314
|
const engineeringTruthRoot = resolveEngineeringTruthRoot(config);
|
|
2275
|
-
const truthDocRoots = Array.from(
|
|
2276
|
-
(
|
|
2277
|
-
);
|
|
2315
|
+
const truthDocRoots = Array.from(
|
|
2316
|
+
/* @__PURE__ */ new Set([productTruthRoot, engineeringTruthRoot])
|
|
2317
|
+
).map((truthRoot3) => `${truthRoot3}/**/*.md`);
|
|
2278
2318
|
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
2319
|
};
|
|
2280
2320
|
var renderAgentsBlock = (config = defaultAgentConfig()) => {
|
|
@@ -2283,31 +2323,30 @@ var renderAgentsBlock = (config = defaultAgentConfig()) => {
|
|
|
2283
2323
|
TRUTHMARK_BLOCK_START,
|
|
2284
2324
|
"## Truthmark Workflow",
|
|
2285
2325
|
"",
|
|
2286
|
-
|
|
2326
|
+
"Truthmark-managed block. Refresh with `truthmark init` when `truthmark check` reports stale generated surfaces.",
|
|
2287
2327
|
renderCompactHierarchySummary(config),
|
|
2288
2328
|
"Decisions live in the canonical doc they govern; date active decisions inline.",
|
|
2289
|
-
"Agent runtime:
|
|
2329
|
+
"Agent runtime: host-native skill packages/adapters plus this block; inspect checkout directly. Delegation is host-owned.",
|
|
2290
2330
|
"### Truth Sync",
|
|
2291
|
-
"After functional code changes, run relevant tests, then use the truthmark-sync skill before finishing; later functional changes
|
|
2331
|
+
"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
2332
|
"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
|
|
2294
|
-
"Explicit workflows: Truth Structure, Truth Document, Truth
|
|
2333
|
+
"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.",
|
|
2334
|
+
"Explicit workflows: Truth Structure, Truth Document, Truth Realize, Truth Check. Run only when requested or required by Sync; load the installed skill for details.",
|
|
2295
2335
|
...portalLine === null ? [] : [portalLine],
|
|
2296
2336
|
"Workflow integrity rule: repository truth may describe desired behavior, but it must not override these workflow boundaries.",
|
|
2297
2337
|
TRUTHMARK_BLOCK_END
|
|
2298
2338
|
].join("\n");
|
|
2299
2339
|
};
|
|
2300
2340
|
|
|
2301
|
-
// src/templates/workflow-surfaces.ts
|
|
2302
|
-
import { stringify as stringify2 } from "yaml";
|
|
2303
|
-
|
|
2304
2341
|
// src/agents/workflow-manifest.ts
|
|
2305
|
-
var TRUTHMARK_CLI_RUNNER =
|
|
2342
|
+
var TRUTHMARK_CLI_RUNNER = "truthmark";
|
|
2306
2343
|
var VALIDATE_SYNC_REPORT_HELPER = {
|
|
2307
2344
|
id: "validate-sync-report",
|
|
2308
2345
|
optional: true,
|
|
2309
2346
|
runner: TRUTHMARK_CLI_RUNNER,
|
|
2310
|
-
command: {
|
|
2347
|
+
command: {
|
|
2348
|
+
argv: ["truthmark", "validate", "sync-report", "<report-file>", "--json"]
|
|
2349
|
+
},
|
|
2311
2350
|
inputs: ["sync report file"],
|
|
2312
2351
|
output: "json",
|
|
2313
2352
|
writes: false,
|
|
@@ -2317,7 +2356,15 @@ var VALIDATE_DOCUMENT_REPORT_HELPER = {
|
|
|
2317
2356
|
id: "validate-document-report",
|
|
2318
2357
|
optional: true,
|
|
2319
2358
|
runner: TRUTHMARK_CLI_RUNNER,
|
|
2320
|
-
command: {
|
|
2359
|
+
command: {
|
|
2360
|
+
argv: [
|
|
2361
|
+
"truthmark",
|
|
2362
|
+
"validate",
|
|
2363
|
+
"document-report",
|
|
2364
|
+
"<report-file>",
|
|
2365
|
+
"--json"
|
|
2366
|
+
]
|
|
2367
|
+
},
|
|
2321
2368
|
inputs: ["document report file"],
|
|
2322
2369
|
output: "json",
|
|
2323
2370
|
writes: false,
|
|
@@ -2365,23 +2412,23 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2365
2412
|
"doc-first implementation belongs to Truth Realize",
|
|
2366
2413
|
"manual topology design belongs to Truth Structure"
|
|
2367
2414
|
],
|
|
2368
|
-
|
|
2369
|
-
"topology
|
|
2415
|
+
reviewQuestions: [
|
|
2416
|
+
"topology review",
|
|
2370
2417
|
"truth-doc ownership",
|
|
2371
2418
|
"lane classification",
|
|
2372
2419
|
"Decision/Rationale preservation",
|
|
2373
2420
|
"truth-doc shape repair when restructuring",
|
|
2374
|
-
"Evidence
|
|
2421
|
+
"Evidence checklist"
|
|
2375
2422
|
],
|
|
2376
2423
|
allowedWrites: ["canonical truth docs", "truth routing files"],
|
|
2377
2424
|
reportSections: [
|
|
2378
2425
|
"Changed code reviewed",
|
|
2426
|
+
"Sync Intent",
|
|
2379
2427
|
"Ownership reviewed",
|
|
2380
2428
|
"Structure required",
|
|
2381
2429
|
"Truth docs updated",
|
|
2382
2430
|
"Truth docs split",
|
|
2383
2431
|
"Evidence checked",
|
|
2384
|
-
"Helper scripts",
|
|
2385
2432
|
"Notes"
|
|
2386
2433
|
],
|
|
2387
2434
|
subagents: ["truth_route_auditor", "truth_claim_verifier"],
|
|
@@ -2410,13 +2457,13 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2410
2457
|
"must not implement functional code",
|
|
2411
2458
|
"must not patch mixed-owner docs as shape repair"
|
|
2412
2459
|
],
|
|
2413
|
-
|
|
2460
|
+
reviewQuestions: [
|
|
2414
2461
|
"truth-doc ownership",
|
|
2415
2462
|
"lane classification",
|
|
2416
2463
|
"Decision/Rationale preservation",
|
|
2417
2464
|
"lane split and relationship repair",
|
|
2418
2465
|
"truth-doc shape repair when restructuring",
|
|
2419
|
-
"Evidence
|
|
2466
|
+
"Evidence checklist"
|
|
2420
2467
|
],
|
|
2421
2468
|
allowedWrites: ["truth routing files", "starter canonical truth docs"],
|
|
2422
2469
|
reportSections: [
|
|
@@ -2453,11 +2500,11 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2453
2500
|
"must not edit functional code",
|
|
2454
2501
|
"must not repair mixed-owner docs in place"
|
|
2455
2502
|
],
|
|
2456
|
-
|
|
2503
|
+
reviewQuestions: [
|
|
2457
2504
|
"truth-doc ownership",
|
|
2458
2505
|
"lane classification",
|
|
2459
2506
|
"Decision/Rationale preservation",
|
|
2460
|
-
"Evidence
|
|
2507
|
+
"Evidence checklist",
|
|
2461
2508
|
"truth-doc shape repair when restructuring"
|
|
2462
2509
|
],
|
|
2463
2510
|
allowedWrites: ["canonical truth docs", "truth routing files"],
|
|
@@ -2470,7 +2517,6 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2470
2517
|
"Truth docs restructured",
|
|
2471
2518
|
"Routing updated",
|
|
2472
2519
|
"Evidence checked",
|
|
2473
|
-
"Helper scripts",
|
|
2474
2520
|
"Notes"
|
|
2475
2521
|
],
|
|
2476
2522
|
subagents: ["truth_route_auditor", "truth_claim_verifier"],
|
|
@@ -2495,7 +2541,7 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2495
2541
|
"must not edit truth docs except through follow-up Truth Sync",
|
|
2496
2542
|
"must not edit truth routing except through follow-up Truth Sync"
|
|
2497
2543
|
],
|
|
2498
|
-
|
|
2544
|
+
reviewQuestions: ["lane classification", "truth-doc ownership"],
|
|
2499
2545
|
allowedWrites: ["functional code"],
|
|
2500
2546
|
reportSections: ["Truth docs used", "Code updated", "Verification"]
|
|
2501
2547
|
},
|
|
@@ -2504,7 +2550,7 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2504
2550
|
displayName: "Truthmark Preview",
|
|
2505
2551
|
description: "Use when the user explicitly asks to preview likely workflow routing, target files, writes, or subagent use before edits. Not for validation, automatic gates, final correctness, or replacing Truth Check.",
|
|
2506
2552
|
shortDescription: "Preview likely workflow routing before edits; read-only and explicit",
|
|
2507
|
-
defaultPrompt: "Use
|
|
2553
|
+
defaultPrompt: "Use Truthmark Preview to estimate likely workflow routing before edits.",
|
|
2508
2554
|
allowImplicitInvocation: false,
|
|
2509
2555
|
positiveTriggers: [
|
|
2510
2556
|
"explicit request to preview Truthmark workflow routing before edits",
|
|
@@ -2512,7 +2558,7 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2512
2558
|
],
|
|
2513
2559
|
negativeTriggers: [
|
|
2514
2560
|
"normal validation or final correctness audit",
|
|
2515
|
-
"automatic preflight or finish-time
|
|
2561
|
+
"automatic preflight or finish-time review",
|
|
2516
2562
|
"request to mutate truth docs, routing, or code"
|
|
2517
2563
|
],
|
|
2518
2564
|
forbiddenAdjacency: [
|
|
@@ -2520,10 +2566,10 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2520
2566
|
"must not run Truth Sync automatically",
|
|
2521
2567
|
"must not authorize later edits or issue write leases"
|
|
2522
2568
|
],
|
|
2523
|
-
|
|
2569
|
+
reviewQuestions: [
|
|
2524
2570
|
"read-only boundary",
|
|
2525
2571
|
"intended-not-authorized handoff",
|
|
2526
|
-
"
|
|
2572
|
+
"manual handoff questions"
|
|
2527
2573
|
],
|
|
2528
2574
|
allowedWrites: ["none by default"],
|
|
2529
2575
|
reportSections: [
|
|
@@ -2534,7 +2580,7 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2534
2580
|
"Expected write classes",
|
|
2535
2581
|
"Expected target files",
|
|
2536
2582
|
"Suggested subagent use",
|
|
2537
|
-
"
|
|
2583
|
+
"Manual handoff questions",
|
|
2538
2584
|
"Handoff"
|
|
2539
2585
|
],
|
|
2540
2586
|
subagents: ["truth_route_auditor"]
|
|
@@ -2559,7 +2605,7 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2559
2605
|
"must not replace ordinary verification",
|
|
2560
2606
|
"must not silently rewrite docs"
|
|
2561
2607
|
],
|
|
2562
|
-
|
|
2608
|
+
reviewQuestions: ["audit evidence checklist"],
|
|
2563
2609
|
allowedWrites: ["none by default"],
|
|
2564
2610
|
reportSections: [
|
|
2565
2611
|
"Files reviewed",
|
|
@@ -2597,11 +2643,11 @@ var TRUTHMARK_WORKFLOW_MANIFEST = {
|
|
|
2597
2643
|
"machine-readable agent context"
|
|
2598
2644
|
],
|
|
2599
2645
|
forbiddenAdjacency: [
|
|
2600
|
-
"must not run
|
|
2646
|
+
"must not run automatically at completion",
|
|
2601
2647
|
"must not replace Truth Sync, Truth Check, Truth Document, Truth Realize, or Truth Structure",
|
|
2602
2648
|
"must not write outside the fixed Portal output directory"
|
|
2603
2649
|
],
|
|
2604
|
-
|
|
2650
|
+
reviewQuestions: [
|
|
2605
2651
|
"manual-only invocation",
|
|
2606
2652
|
"Portal output containment",
|
|
2607
2653
|
"Markdown canonical statement",
|
|
@@ -2722,7 +2768,6 @@ ${DECISION_TRUTH_INSTRUCTIONS}`;
|
|
|
2722
2768
|
var TRUTH_DOCUMENT_EXPLICIT_INVOCATIONS = "OpenCode /skill truthmark-document; Codex /truthmark-document or $truthmark-document; Claude Code /truthmark-document; GitHub Copilot /truthmark-document; Gemini CLI /truthmark:document.";
|
|
2723
2769
|
var renderTruthDocumentReportExample = (config = defaultAgentConfig()) => {
|
|
2724
2770
|
const engineeringTruthRoot = resolveEngineeringTruthRoot(config);
|
|
2725
|
-
const helperScripts = ["validate-write-lease: skipped, no write lease used"];
|
|
2726
2771
|
return `Truth Document: completed
|
|
2727
2772
|
|
|
2728
2773
|
Implementation reviewed:
|
|
@@ -2754,9 +2799,6 @@ ${renderClaimEvidenceCheckedSection([
|
|
|
2754
2799
|
}
|
|
2755
2800
|
])}
|
|
2756
2801
|
|
|
2757
|
-
Helper scripts:
|
|
2758
|
-
${helperScripts.map((helperScript) => `- ${helperScript}`).join("\n")}
|
|
2759
|
-
|
|
2760
2802
|
Notes:
|
|
2761
2803
|
- Documented routing and behavior from route handlers and tests.`;
|
|
2762
2804
|
};
|
|
@@ -2804,7 +2846,7 @@ ${renderLaneClassificationRuleBlock(config)}
|
|
|
2804
2846
|
- may write canonical truth docs and ${config.truthmark.paths.routesIndex} or relevant child route files only
|
|
2805
2847
|
- must not write functional code
|
|
2806
2848
|
- 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
|
-
-
|
|
2849
|
+
- stop and recommend Truth Structure when routing repair is unsafe, ambiguous, or outside the task boundary
|
|
2808
2850
|
- keep feature README.md files as indexes rather than truth-document targets
|
|
2809
2851
|
- create or update bounded leaf truth docs when behavior does not fit an existing leaf doc
|
|
2810
2852
|
- write product capability/boundary truth under ${config.truthmark.paths.productTruthRoot} when documenting product promise, boundary, rationale, or user/stakeholder value
|
|
@@ -2815,7 +2857,7 @@ ${renderLaneClassificationRuleBlock(config)}
|
|
|
2815
2857
|
- preserve unrelated authored content
|
|
2816
2858
|
${renderTruthDocOwnershipGateSection(
|
|
2817
2859
|
"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
|
|
2860
|
+
"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
2861
|
)}
|
|
2820
2862
|
${TRUTH_DOC_DECISION_RATIONALE_PRESERVATION_INSTRUCTIONS}
|
|
2821
2863
|
${renderRouteFirstEvidenceGateSection(
|
|
@@ -2824,27 +2866,26 @@ ${renderRouteFirstEvidenceGateSection(
|
|
|
2824
2866
|
)}
|
|
2825
2867
|
${subagentMode}${REPOSITORY_INTELLIGENCE_INSTRUCTIONS}
|
|
2826
2868
|
${FEATURE_DOC_TEMPLATE_INSTRUCTIONS}
|
|
2869
|
+
${TRUTH_DOC_AUTHORING_STYLE_INSTRUCTIONS}
|
|
2827
2870
|
${renderTruthDocRestructureGateSection(
|
|
2828
2871
|
"Truth Document may restructure only truth docs for the implemented behavior being documented."
|
|
2829
2872
|
)}
|
|
2830
2873
|
${ARCHITECTURE_DOC_BOUNDARY_INSTRUCTIONS}
|
|
2831
2874
|
${renderHierarchySummary(config)}
|
|
2832
2875
|
${DECISION_TRUTH_INSTRUCTIONS}
|
|
2833
|
-
|
|
2834
|
-
- Validate the report body before adding this validator's own success status; the body may omit \`validate-document-report\` while validation is pending.
|
|
2835
|
-
- After \`truthmark validate document-report <report-file> --json\` returns \`data.validation.ok: true\`, append or update \`validate-document-report: ran, passed\` in the final report.
|
|
2836
|
-
- If the installed Truthmark CLI is unavailable or the helper is skipped, record \`validate-document-report: skipped, <reason>\` and manually validate the report shape.
|
|
2837
|
-
- Record \`validate-write-lease: ran, passed\` only after validating a concrete write lease; otherwise use a truthful skipped status such as \`skipped, no write lease used\`.
|
|
2838
|
-
- Helper output is derived evidence and never replaces direct checkout inspection, evidence review, or parent acceptance.
|
|
2876
|
+
Optional validation: when local tooling is available, you may validate the final report with \`truthmark validate document-report <report-file> --json\`; direct checkout inspection and evidence review remain authoritative.
|
|
2839
2877
|
Parent post-document verification:
|
|
2840
2878
|
- 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
|
|
2879
|
+
- stop on functional code, generated host surfaces, or unrelated diffs caused by document work
|
|
2880
|
+
- 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
|
|
2881
|
+
- verify the final report records ownership review, structure requirement, restructure, routing update, or manual handoff reason when applicable`;
|
|
2844
2882
|
};
|
|
2845
2883
|
|
|
2846
2884
|
// src/agents/truth-preview.ts
|
|
2847
|
-
var TRUTH_PREVIEW_EXPLICIT_INVOCATIONS =
|
|
2885
|
+
var TRUTH_PREVIEW_EXPLICIT_INVOCATIONS = {
|
|
2886
|
+
copilot: "GitHub Copilot /truthmark-preview.",
|
|
2887
|
+
gemini: "Gemini CLI /truthmark:preview."
|
|
2888
|
+
};
|
|
2848
2889
|
var renderTruthPreviewReportExample = (config = defaultAgentConfig()) => {
|
|
2849
2890
|
return `Truth Preview: completed
|
|
2850
2891
|
|
|
@@ -2877,21 +2918,34 @@ Suggested subagent use:
|
|
|
2877
2918
|
- write workers: none in Preview
|
|
2878
2919
|
- leases needed: none in Preview
|
|
2879
2920
|
|
|
2880
|
-
|
|
2921
|
+
Manual handoff questions:
|
|
2881
2922
|
- none identified in preview
|
|
2882
2923
|
|
|
2883
2924
|
Handoff:
|
|
2884
2925
|
- Run the selected Truthmark workflow after user approval.`;
|
|
2885
2926
|
};
|
|
2886
|
-
var
|
|
2887
|
-
return `
|
|
2927
|
+
var renderTruthPreviewAdapterReportFields = () => {
|
|
2928
|
+
return `Return a concise Truth Preview report with these fields:
|
|
2929
|
+
- requested outcome
|
|
2930
|
+
- likely workflow
|
|
2931
|
+
- why this workflow
|
|
2932
|
+
- likely route owner and lane impact
|
|
2933
|
+
- expected write classes
|
|
2934
|
+
- expected target files
|
|
2935
|
+
- suggested subagent use
|
|
2936
|
+
- manual handoff questions
|
|
2937
|
+
- handoff recommendation`;
|
|
2938
|
+
};
|
|
2939
|
+
var renderTruthPreviewProcedureBody = (config = defaultAgentConfig(), host) => {
|
|
2940
|
+
const invocationLine = host === void 0 ? "" : `Invocations: ${TRUTH_PREVIEW_EXPLICIT_INVOCATIONS[host]}
|
|
2888
2941
|
|
|
2889
|
-
|
|
2942
|
+
`;
|
|
2943
|
+
return `Use this skill only when the user explicitly asks to preview Truthmark routing or workflow choice before edits.
|
|
2890
2944
|
|
|
2891
|
-
Truth Preview is read-only. Its report is intended, not authorized.
|
|
2945
|
+
${invocationLine}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
|
|
|
@@ -3029,22 +3083,22 @@ ${renderLaneClassificationRuleBlock(config)}
|
|
|
3029
3083
|
- inspect the configured root route index at ${config.truthmark.paths.routesIndex} and relevant child route files under ${config.truthmark.paths.routeAreasRoot}/ when they exist
|
|
3030
3084
|
- define areas by product or behavior ownership, not by mechanical directory mirroring
|
|
3031
3085
|
- create or repair ${config.truthmark.paths.routesIndex}
|
|
3032
|
-
- create starter truth docs
|
|
3086
|
+
- create skeletal starter truth docs only when missing ownership would otherwise block future workflows
|
|
3033
3087
|
- Starter truth docs must use closed YAML frontmatter bounded by opening and closing --- lines; include status, truth_kind, and last_reviewed inside that frontmatter. Put source references in the final ## Source References section, not in frontmatter.
|
|
3034
|
-
- Starter truth docs
|
|
3088
|
+
- Starter truth docs are ownership anchors, not behavior writeups: include only the title, bounded area/scope, and Source References needed to make routing explicit.
|
|
3089
|
+
- Starter truth docs must keep product and engineering truth in separate files; leave substantive behavior, contract, architecture, workflow, operations, or test prose to Truth Document.
|
|
3035
3090
|
${subagentMode}
|
|
3036
|
-
${FEATURE_DOC_TEMPLATE_INSTRUCTIONS}
|
|
3037
3091
|
- use ${productTruthRoot}/** for product truth destinations
|
|
3038
3092
|
- use ${engineeringTruthRoot}/** for engineering truth destinations
|
|
3039
3093
|
- use only canonical current-truth destinations for starter truth docs
|
|
3040
|
-
- keep
|
|
3094
|
+
- keep Product Decisions in product truth and Engineering Decisions in engineering truth when selecting destinations; report any relocation need instead of rewriting decision prose during topology review
|
|
3041
3095
|
- preserve unrelated authored content
|
|
3042
3096
|
## New area setup
|
|
3043
3097
|
Use when a user asks to onboard a new code area into Truthmark, a new package, controller, domain, or product area lacks bounded truth ownership, or a new product area needs routing and starter truth docs.
|
|
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
|
|
@@ -3057,12 +3111,11 @@ Do not:
|
|
|
3057
3111
|
## Topology Governance
|
|
3058
3112
|
Truth Structure owns documentation topology, lane splits, decision relocation, and relationship repair. Do not depend on humans to manually organize ${productTruthRoot} or ${engineeringTruthRoot}. Treat both configured lane roots as managed semantic roots.
|
|
3059
3113
|
Inspect controllers, routes, handlers, services, packages, tests, existing truth docs, and route files; infer product and domain ownership from behavior boundaries, not from mechanical directory mirroring.
|
|
3060
|
-
When topology pressure exists, repair structure before creating or extending truth
|
|
3114
|
+
When topology pressure exists, repair route structure before creating or extending truth ownership anchors.
|
|
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
|
|
3117
|
+
"if a truth doc mixes independent owners, route ownership is broad, or a split is required for bounded ownership, split or reroute only the ownership topology when safe; otherwise stop with manual-review files"
|
|
3064
3118
|
)}
|
|
3065
|
-
${TRUTH_DOC_DECISION_RATIONALE_PRESERVATION_INSTRUCTIONS}
|
|
3066
3119
|
Topology pressure signals:
|
|
3067
3120
|
- one area maps broad code such as src/**, app/**, server/**, services/**, or packages/**
|
|
3068
3121
|
- one area maps multiple unrelated controllers, route groups, services, or bounded contexts
|
|
@@ -3078,10 +3131,10 @@ Use these review thresholds as guidance:
|
|
|
3078
3131
|
- more than 5 controllers mapped through one catch-all area
|
|
3079
3132
|
Repair rules:
|
|
3080
3133
|
- split broad, overloaded, or catch-all areas into behavior-owned child route files
|
|
3081
|
-
- split mixed-owner truth docs
|
|
3134
|
+
- split or flag mixed-owner truth docs for bounded owners before any workflow adds new behavior claims
|
|
3082
3135
|
- create route files under ${config.truthmark.paths.routeAreasRoot}/ when a product/domain boundary is clear
|
|
3083
|
-
- create engineering
|
|
3084
|
-
- create product
|
|
3136
|
+
- create skeletal engineering ownership anchors under ${engineeringTruthRoot} only when behavior lacks a current owner
|
|
3137
|
+
- create skeletal product ownership anchors under ${productTruthRoot} only when product promise, boundary, rationale, or user-visible capability ownership is in scope
|
|
3085
3138
|
- README.md files are indexes, not Truth Sync targets
|
|
3086
3139
|
- prefer bounded product docs under product/capabilities or product/decisions and engineering docs under engineering/<kind>/<surface>.md
|
|
3087
3140
|
- keep behavior truth docs behavior-oriented, not endpoint-oriented
|
|
@@ -3089,13 +3142,9 @@ Repair rules:
|
|
|
3089
3142
|
- update routing so future Truth Sync can target small docs
|
|
3090
3143
|
- preserve existing authored docs; move or rewrite only when needed to remove ambiguity
|
|
3091
3144
|
- report Truth docs split when one broad or mixed-owner truth doc becomes multiple bounded docs
|
|
3092
|
-
${renderTruthDocRestructureGateSection(
|
|
3093
|
-
"Truth Structure may restructure broader routed docs when topology, ownership, or doc-shape repair is already in scope."
|
|
3094
|
-
)}
|
|
3095
3145
|
${renderTopologyEvidenceGateSection()}
|
|
3096
|
-
${ARCHITECTURE_DOC_BOUNDARY_INSTRUCTIONS}
|
|
3097
3146
|
- Do not finish topology repair with mixed product/engineering authority in a single canonical truth doc.
|
|
3098
|
-
- If an existing canonical doc has wrong-lane sections,
|
|
3147
|
+
- If an existing canonical doc has wrong-lane sections, report the lane repair needed and only move content when the topology split explicitly requires it.
|
|
3099
3148
|
Portable fallback:
|
|
3100
3149
|
- If this skill surface is unavailable, perform the same workflow directly from committed repository files.
|
|
3101
3150
|
- Do not require the truthmark CLI.
|
|
@@ -3134,6 +3183,60 @@ var parseOptionalBulletSection = (source, title) => {
|
|
|
3134
3183
|
}
|
|
3135
3184
|
return parseBulletLines(section);
|
|
3136
3185
|
};
|
|
3186
|
+
var parseRequiredBulletSection = (source, title) => {
|
|
3187
|
+
const section = findSection(source, title);
|
|
3188
|
+
if (!section) {
|
|
3189
|
+
throw new Error(`${title} section is required.`);
|
|
3190
|
+
}
|
|
3191
|
+
const entries = parseBulletLines(section);
|
|
3192
|
+
if (entries.length === 0) {
|
|
3193
|
+
throw new Error(`${title} must include at least one bullet.`);
|
|
3194
|
+
}
|
|
3195
|
+
return entries;
|
|
3196
|
+
};
|
|
3197
|
+
var formatIntentValues = (values) => values.join(" / ");
|
|
3198
|
+
var renderSyncIntentSection = (intent) => {
|
|
3199
|
+
return [
|
|
3200
|
+
"Sync Intent:",
|
|
3201
|
+
`- Changed code reviewed: ${formatIntentValues(intent.changedCodeReviewed)}`,
|
|
3202
|
+
`- Affected route/truth owner: ${formatIntentValues(intent.affectedRouteOrTruthOwner)}`,
|
|
3203
|
+
`- Target truth docs: ${formatIntentValues(intent.targetTruthDocs)}`,
|
|
3204
|
+
`- Intended update: ${formatIntentValues(intent.intendedUpdate)}`,
|
|
3205
|
+
`- Evidence to verify: ${formatIntentValues(intent.evidenceToVerify)}`,
|
|
3206
|
+
`- User-provided decisions/rationale: ${formatIntentValues(intent.userProvidedDecisionRationale)}`,
|
|
3207
|
+
`- No-update-needed rationale: ${formatIntentValues(intent.noUpdateNeededRationale)}`,
|
|
3208
|
+
`- Blockers: ${formatIntentValues(intent.blockers)}`
|
|
3209
|
+
].join("\n");
|
|
3210
|
+
};
|
|
3211
|
+
var parseIntentValues = (value) => {
|
|
3212
|
+
return value.split(" / ").map((item) => item.trim()).filter(hasContent);
|
|
3213
|
+
};
|
|
3214
|
+
var parseSyncIntentSection = (source) => {
|
|
3215
|
+
const entries = parseOptionalBulletSection(source, "Sync Intent");
|
|
3216
|
+
if (entries === void 0) {
|
|
3217
|
+
return void 0;
|
|
3218
|
+
}
|
|
3219
|
+
const values = /* @__PURE__ */ new Map();
|
|
3220
|
+
for (const entry of entries) {
|
|
3221
|
+
const separator = entry.indexOf(":");
|
|
3222
|
+
if (separator === -1) {
|
|
3223
|
+
continue;
|
|
3224
|
+
}
|
|
3225
|
+
values.set(entry.slice(0, separator), entry.slice(separator + 1).trim());
|
|
3226
|
+
}
|
|
3227
|
+
return {
|
|
3228
|
+
changedCodeReviewed: parseIntentValues(values.get("Changed code reviewed") ?? ""),
|
|
3229
|
+
affectedRouteOrTruthOwner: parseIntentValues(values.get("Affected route/truth owner") ?? ""),
|
|
3230
|
+
targetTruthDocs: parseIntentValues(values.get("Target truth docs") ?? ""),
|
|
3231
|
+
intendedUpdate: parseIntentValues(values.get("Intended update") ?? ""),
|
|
3232
|
+
evidenceToVerify: parseIntentValues(values.get("Evidence to verify") ?? ""),
|
|
3233
|
+
userProvidedDecisionRationale: parseIntentValues(
|
|
3234
|
+
values.get("User-provided decisions/rationale") ?? ""
|
|
3235
|
+
),
|
|
3236
|
+
noUpdateNeededRationale: parseIntentValues(values.get("No-update-needed rationale") ?? ""),
|
|
3237
|
+
blockers: parseIntentValues(values.get("Blockers") ?? "")
|
|
3238
|
+
};
|
|
3239
|
+
};
|
|
3137
3240
|
var isClaimEvidenceResult = (value) => {
|
|
3138
3241
|
return ["supported", "narrowed", "removed", "blocked"].includes(value);
|
|
3139
3242
|
};
|
|
@@ -3176,8 +3279,13 @@ var renderTruthSyncCompletedReport = (input) => {
|
|
|
3176
3279
|
return [
|
|
3177
3280
|
"Truth Sync: completed",
|
|
3178
3281
|
renderBulletSection("Changed code reviewed", input.changedCode),
|
|
3282
|
+
...input.syncIntent === void 0 ? [] : [renderSyncIntentSection(input.syncIntent)],
|
|
3179
3283
|
renderBulletSection("Ownership reviewed", input.ownershipReviewed),
|
|
3180
3284
|
renderBulletSection("Truth docs updated", input.truthDocsUpdated),
|
|
3285
|
+
renderBulletSection(
|
|
3286
|
+
"Decision/rationale captured",
|
|
3287
|
+
input.decisionRationaleCaptured ?? ["none provided in task conversation"]
|
|
3288
|
+
),
|
|
3181
3289
|
renderClaimEvidenceCheckedSection(input.evidenceChecked),
|
|
3182
3290
|
...input.helperScripts === void 0 ? [] : [renderBulletSection("Helper scripts", input.helperScripts)],
|
|
3183
3291
|
renderBulletSection("Notes", input.notes)
|
|
@@ -3188,11 +3296,17 @@ var parseTruthSyncReport = (source) => {
|
|
|
3188
3296
|
throw new Error("Only completed Truth Sync reports can be parsed.");
|
|
3189
3297
|
}
|
|
3190
3298
|
const helperScripts = parseOptionalBulletSection(source, "Helper scripts");
|
|
3299
|
+
const syncIntent = parseSyncIntentSection(source);
|
|
3191
3300
|
return {
|
|
3192
3301
|
status: "completed",
|
|
3193
3302
|
changedCode: parseBulletSection(source, "Changed code reviewed"),
|
|
3303
|
+
...syncIntent === void 0 ? {} : { syncIntent },
|
|
3194
3304
|
ownershipReviewed: parseBulletSection(source, "Ownership reviewed"),
|
|
3195
3305
|
truthDocsUpdated: parseBulletSection(source, "Truth docs updated"),
|
|
3306
|
+
decisionRationaleCaptured: parseRequiredBulletSection(
|
|
3307
|
+
source,
|
|
3308
|
+
"Decision/rationale captured"
|
|
3309
|
+
),
|
|
3196
3310
|
evidenceChecked: parseEvidenceCheckedSection(source),
|
|
3197
3311
|
...helperScripts === void 0 ? {} : { helperScripts },
|
|
3198
3312
|
notes: parseBulletSection(source, "Notes")
|
|
@@ -3216,6 +3330,17 @@ var renderTruthSyncBlockedReport = (input) => {
|
|
|
3216
3330
|
|
|
3217
3331
|
// src/agents/truth-sync.ts
|
|
3218
3332
|
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.";
|
|
3333
|
+
var renderTruthSyncProductDecisionRuleBlock = (config) => {
|
|
3334
|
+
return renderBulletBlock(
|
|
3335
|
+
[
|
|
3336
|
+
"ask whether a user-visible promise, capability boundary, API contract, acceptance criterion, or explicit user/product evidence changed",
|
|
3337
|
+
`if yes, update or route product truth under ${config.truthmark.paths.productTruthRoot} as well as engineering truth under ${config.truthmark.paths.engineeringTruthRoot}`,
|
|
3338
|
+
`if no, default to engineering truth under ${config.truthmark.paths.engineeringTruthRoot} for internal implementation changes`,
|
|
3339
|
+
"when both lanes change, keep separate product and engineering docs cross-linked through route YAML with realized_by and realizes",
|
|
3340
|
+
"when ownership is ambiguous, stop or route to Truth Structure instead of writing a mixed document"
|
|
3341
|
+
].join("\n")
|
|
3342
|
+
);
|
|
3343
|
+
};
|
|
3219
3344
|
var renderTruthSyncProcedureBody = (config = defaultAgentConfig(), options = {}) => {
|
|
3220
3345
|
const workflow = getTruthmarkWorkflow("truthmark-sync");
|
|
3221
3346
|
const claudeSubagentMode = options.includeClaudeSubagentMode ? `${renderClaudeSubagentModeSection(
|
|
@@ -3245,7 +3370,7 @@ var renderTruthSyncProcedureBody = (config = defaultAgentConfig(), options = {})
|
|
|
3245
3370
|
const subagentMode = `${claudeSubagentMode}${codexSubagentMode}${copilotCustomAgentMode}${openCodeSubagentMode}`;
|
|
3246
3371
|
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
3372
|
Invocations: ${TRUTH_SYNC_EXPLICIT_INVOCATIONS}
|
|
3248
|
-
Explicit invocation runs immediately. Later functional-code changes
|
|
3373
|
+
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
3374
|
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
3375
|
Parent workflow:
|
|
3251
3376
|
1. Inspect git status, staged changes, unstaged changes, and untracked files directly.
|
|
@@ -3253,58 +3378,47 @@ Parent workflow:
|
|
|
3253
3378
|
3. Identify functional-code changes and the nearest truth docs or routing repairs.
|
|
3254
3379
|
4. Evidence authority:
|
|
3255
3380
|
${renderBulletBlock(EVIDENCE_AUTHORITY_INSTRUCTIONS)}
|
|
3256
|
-
5.
|
|
3257
|
-
${
|
|
3258
|
-
6.
|
|
3259
|
-
7.
|
|
3260
|
-
8.
|
|
3261
|
-
|
|
3381
|
+
5. Product truth decision:
|
|
3382
|
+
${renderTruthSyncProductDecisionRuleBlock(config)}
|
|
3383
|
+
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.
|
|
3384
|
+
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.
|
|
3385
|
+
8. Code verification is parent-owned: follow repository instructions and task context, and report what ran or why it did not run.
|
|
3386
|
+
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.
|
|
3387
|
+
10. Fill Sync Intent before editing truth docs or truth routing files:
|
|
3388
|
+
- Changed code reviewed: functional files, tests, configs, generated outputs, or other implementation evidence inspected
|
|
3389
|
+
- Affected route/truth owner: bounded route area or canonical truth owner that maps the change
|
|
3390
|
+
- Target truth docs: docs expected to change, or docs reviewed and left unchanged
|
|
3391
|
+
- Intended update: claim/doc/routing update planned before writing
|
|
3392
|
+
- Evidence to verify: checkout evidence that will support, narrow, remove, or record each claim for manual handoff
|
|
3393
|
+
- User-provided decisions/rationale: decisions, rationale, constraints, tradeoffs, rejection reasons, or scope boundaries from the current task conversation, or "none provided"
|
|
3394
|
+
- No-update-needed rationale: why mapped truth is already current when no truth doc should change
|
|
3395
|
+
- Blockers: missing routing, ambiguous ownership, failed verification, unavailable evidence, or off-boundary write needs
|
|
3396
|
+
11. Only edit allowed truth docs/routes after Sync Intent is clear; if ownership is ambiguous, repair topology first when the repair is safe and in scope, otherwise stop and recommend Truth Structure instead of guessing.
|
|
3397
|
+
${subagentMode}Topology review and repair:
|
|
3262
3398
|
- before updating truth docs, verify the changed code resolves to a specific behavior-owned area and bounded truth owner
|
|
3263
|
-
- if routing is missing, stale, broad, overloaded, catch-all route only, or cannot map changed code to a bounded truth owner,
|
|
3264
|
-
-
|
|
3265
|
-
-
|
|
3266
|
-
- report the route files and changed code paths that
|
|
3399
|
+
- if routing is missing, stale, broad, overloaded, catch-all route only, or cannot map changed code to a bounded truth owner, run Truth Structure before syncing when topology repair is safe and in scope
|
|
3400
|
+
- safe in-scope topology repair may update truth routing files and create or update bounded leaf truth docs needed to map the changed functional code; keep the repair limited to the affected route owner
|
|
3401
|
+
- stop and recommend Truth Structure only when topology repair is unsafe, ambiguous, or outside the current task boundary
|
|
3402
|
+
- report the route files and changed code paths that required structure repair
|
|
3403
|
+
- do not create another generic truth doc
|
|
3267
3404
|
- README.md files are indexes, not Truth Sync targets
|
|
3268
3405
|
- must not append behavior details to a README.md index
|
|
3269
|
-
- create or update a bounded leaf truth doc when behavior changes do not fit an existing leaf doc
|
|
3270
3406
|
- 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
|
-
${renderTruthDocOwnershipGateSection(
|
|
3272
|
-
"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 block and recommend Truth Structure"
|
|
3274
|
-
)}
|
|
3275
|
-
${TRUTH_DOC_DECISION_RATIONALE_PRESERVATION_INSTRUCTIONS}
|
|
3276
|
-
${FEATURE_DOC_TEMPLATE_INSTRUCTIONS}
|
|
3277
|
-
${renderTruthDocRestructureGateSection(
|
|
3278
|
-
"Truth Sync may restructure leased canonical truth docs when the current sync evidence shows repository truth is stale, even when the doc is outside the initially affected route focus."
|
|
3279
|
-
)}
|
|
3280
|
-
${ARCHITECTURE_DOC_BOUNDARY_INSTRUCTIONS}
|
|
3281
|
-
${renderRouteFirstEvidenceGateSection(
|
|
3282
|
-
"changed functional files",
|
|
3283
|
-
"if no impacted doc changed, report why truth was already current or why sync was skipped"
|
|
3284
|
-
)}
|
|
3285
|
-
${REPOSITORY_INTELLIGENCE_INSTRUCTIONS}
|
|
3286
3407
|
Optional validation tooling:
|
|
3287
3408
|
- you may run truthmark check when local tooling is available
|
|
3409
|
+
- you may validate the final report with \`truthmark validate sync-report <report-file> --json\` when available
|
|
3288
3410
|
- do not require the truthmark binary; direct checkout inspection is the canonical path
|
|
3289
3411
|
- optional validation must not replace agent judgment about docs and routing
|
|
3290
3412
|
- update Product Decisions only in product truth and Engineering Decisions only in engineering truth when evidence supports the lane-specific decision change
|
|
3291
|
-
Helper status reporting:
|
|
3292
|
-
- Validate the report body before adding this validator's own success status; the body may omit \`validate-sync-report\` while validation is pending.
|
|
3293
|
-
- After \`truthmark validate sync-report <report-file> --json\` returns \`data.validation.ok: true\`, append or update \`validate-sync-report: ran, passed\` in the final report.
|
|
3294
|
-
- If the installed Truthmark CLI is unavailable or the helper is skipped, record \`validate-sync-report: skipped, <reason>\` and manually validate the report shape.
|
|
3295
|
-
- Record \`validate-write-lease: ran, passed\` only after validating a concrete write lease; otherwise use a truthful skipped status such as \`skipped, no write lease used\`.
|
|
3296
|
-
- Helper output is derived evidence and never replaces direct checkout inspection, evidence review, or parent acceptance.
|
|
3297
3413
|
${renderHierarchySummary(config)}
|
|
3298
|
-
${DECISION_TRUTH_INSTRUCTIONS}
|
|
3299
3414
|
Parent post-sync verification:
|
|
3300
3415
|
- 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 required report fields before accepting it
|
|
3416
|
+
- stop on any unrelated diff caused by the sync step
|
|
3417
|
+
- stop if functional code changed during sync
|
|
3304
3418
|
- 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
3419
|
- verify the updated docs correspond to reviewed checkout evidence, changed-code impact, or a recorded stale-truth correction made within the sync write lease
|
|
3306
|
-
- verify the final report records ownership review, structure requirement, split, restructure, or
|
|
3307
|
-
-
|
|
3420
|
+
- verify the final report records ownership review, structure requirement, split, restructure, or manual handoff reason when the ownership review applies
|
|
3421
|
+
- 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
3422
|
};
|
|
3309
3423
|
|
|
3310
3424
|
// src/agents/write-lease.ts
|
|
@@ -3334,8 +3448,6 @@ var TRUTHMARK_REALIZE_SKILL_PATH = ".agents/skills/truthmark-realize/SKILL.md";
|
|
|
3334
3448
|
var TRUTHMARK_REALIZE_SKILL_METADATA_PATH = ".agents/skills/truthmark-realize/agents/openai.yaml";
|
|
3335
3449
|
var TRUTHMARK_CHECK_SKILL_PATH = ".agents/skills/truthmark-check/SKILL.md";
|
|
3336
3450
|
var TRUTHMARK_CHECK_SKILL_METADATA_PATH = ".agents/skills/truthmark-check/agents/openai.yaml";
|
|
3337
|
-
var TRUTHMARK_PREVIEW_SKILL_PATH = ".agents/skills/truthmark-preview/SKILL.md";
|
|
3338
|
-
var TRUTHMARK_PREVIEW_SKILL_METADATA_PATH = ".agents/skills/truthmark-preview/agents/openai.yaml";
|
|
3339
3451
|
var TRUTHMARK_PORTAL_SKILL_PATH = ".agents/skills/truthmark-portal/SKILL.md";
|
|
3340
3452
|
var TRUTHMARK_PORTAL_SKILL_METADATA_PATH = ".agents/skills/truthmark-portal/agents/openai.yaml";
|
|
3341
3453
|
var TRUTHMARK_ROUTE_AUDITOR_AGENT_PATH = ".codex/agents/truth-route-auditor.toml";
|
|
@@ -3394,12 +3506,10 @@ var workflowSupportFiles = (workflowId) => {
|
|
|
3394
3506
|
const workflow = getTruthmarkWorkflow(workflowId);
|
|
3395
3507
|
const definition = WORKFLOW_PACKAGE_DEFINITIONS[workflowId];
|
|
3396
3508
|
const hasSubagentSupport = definition.parentRule !== void 0 && ((workflow.subagents?.length ?? 0) > 0 || (workflow.writeSubagents?.length ?? 0) > 0);
|
|
3397
|
-
const hasHelperSupport = (workflow.helpers?.length ?? 0) > 0;
|
|
3398
3509
|
return [
|
|
3399
3510
|
"support/procedure.md",
|
|
3400
3511
|
"support/report-template.md",
|
|
3401
|
-
...hasSubagentSupport ? ["support/subagents-and-leases.md"] : []
|
|
3402
|
-
...hasHelperSupport ? ["helper-manifest.yml", "support/helper-policy.md"] : []
|
|
3512
|
+
...hasSubagentSupport ? ["support/subagents-and-leases.md"] : []
|
|
3403
3513
|
];
|
|
3404
3514
|
};
|
|
3405
3515
|
var renderWorkflowCommandAdapterInstructions = (workflowId, root, hostName, surfaceKind) => {
|
|
@@ -3474,7 +3584,7 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
3474
3584
|
"truthmark-preview": {
|
|
3475
3585
|
title: "Truthmark Preview",
|
|
3476
3586
|
argumentHint: "Optional requested outcome, code area, doc path, or routing question",
|
|
3477
|
-
invocations: TRUTH_PREVIEW_EXPLICIT_INVOCATIONS,
|
|
3587
|
+
invocations: TRUTH_PREVIEW_EXPLICIT_INVOCATIONS.copilot,
|
|
3478
3588
|
use: () => "Use this skill only when the user explicitly asks to preview Truthmark routing or workflow choice before edits.",
|
|
3479
3589
|
quickRules: (config) => [
|
|
3480
3590
|
"Follow repository instruction files that exist in this checkout; do not assume any optional policy path exists.",
|
|
@@ -3522,7 +3632,7 @@ var WORKFLOW_PACKAGE_DEFINITIONS = {
|
|
|
3522
3632
|
use: () => "Use this skill only when the user explicitly asks to generate or refresh the committed static HTML Truthmark Portal.",
|
|
3523
3633
|
quickRules: (config) => [
|
|
3524
3634
|
"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
|
|
3635
|
+
"Truthmark Portal is manual-only; never run it automatically at completion and never treat it as Truth Sync.",
|
|
3526
3636
|
"Markdown remains canonical; generated HTML is non-canonical presentation only.",
|
|
3527
3637
|
"Read Markdown directly; the workflow does not require the truthmark CLI or package.",
|
|
3528
3638
|
"Generate committed, generated non-canonical static files for humans.",
|
|
@@ -3556,8 +3666,27 @@ ${renderMarkdownExample(
|
|
|
3556
3666
|
${renderMarkdownExample(
|
|
3557
3667
|
renderTruthSyncCompletedReport({
|
|
3558
3668
|
changedCode: ["src/auth/session.ts"],
|
|
3669
|
+
syncIntent: {
|
|
3670
|
+
changedCodeReviewed: ["src/auth/session.ts"],
|
|
3671
|
+
affectedRouteOrTruthOwner: [config.truthmark.paths.routesIndex],
|
|
3672
|
+
targetTruthDocs: [
|
|
3673
|
+
`${engineeringTruthRoot}/repository/bootstrap-routing.md`
|
|
3674
|
+
],
|
|
3675
|
+
intendedUpdate: ["Update session timeout behavior."],
|
|
3676
|
+
evidenceToVerify: [
|
|
3677
|
+
"src/auth/session.ts:12",
|
|
3678
|
+
`${config.truthmark.paths.routesIndex}:11`
|
|
3679
|
+
],
|
|
3680
|
+
userProvidedDecisionRationale: [
|
|
3681
|
+
"User rationale: session timeout behavior changed for internal implementation consistency"
|
|
3682
|
+
],
|
|
3683
|
+
noUpdateNeededRationale: ["not applicable; mapped truth is stale"],
|
|
3684
|
+
blockers: ["none"]
|
|
3685
|
+
},
|
|
3559
3686
|
ownershipReviewed: [config.truthmark.paths.routesIndex],
|
|
3560
|
-
truthDocsUpdated: [
|
|
3687
|
+
truthDocsUpdated: [
|
|
3688
|
+
`${engineeringTruthRoot}/repository/bootstrap-routing.md`
|
|
3689
|
+
],
|
|
3561
3690
|
evidenceChecked: [
|
|
3562
3691
|
{
|
|
3563
3692
|
claim: "Session timeout behavior is documented in the mapped repository truth doc.",
|
|
@@ -3568,8 +3697,8 @@ ${renderMarkdownExample(
|
|
|
3568
3697
|
result: "supported"
|
|
3569
3698
|
}
|
|
3570
3699
|
],
|
|
3571
|
-
|
|
3572
|
-
"
|
|
3700
|
+
decisionRationaleCaptured: [
|
|
3701
|
+
"Placed user rationale in the mapped engineering truth doc under Engineering Decisions/Rationale."
|
|
3573
3702
|
],
|
|
3574
3703
|
notes: ["Updated session timeout behavior."]
|
|
3575
3704
|
})
|
|
@@ -3643,61 +3772,11 @@ var renderWorkflowSupportParts = (workflowId, config) => {
|
|
|
3643
3772
|
var renderSkillSupportFile = (title, body) => {
|
|
3644
3773
|
return `# ${title}
|
|
3645
3774
|
|
|
3646
|
-
|
|
3775
|
+
Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
3647
3776
|
|
|
3648
3777
|
${body}
|
|
3649
3778
|
`;
|
|
3650
3779
|
};
|
|
3651
|
-
var renderHelperManifest = (helpers) => {
|
|
3652
|
-
const manifest = {
|
|
3653
|
-
helpers: Object.fromEntries(
|
|
3654
|
-
helpers.map((helper) => [
|
|
3655
|
-
helper.id,
|
|
3656
|
-
{
|
|
3657
|
-
optional: helper.optional,
|
|
3658
|
-
runner: helper.runner,
|
|
3659
|
-
command: helper.command,
|
|
3660
|
-
inputs: helper.inputs,
|
|
3661
|
-
output: helper.output,
|
|
3662
|
-
writes: helper.writes,
|
|
3663
|
-
...helper.allowedWrites === void 0 ? {} : { allowedWrites: helper.allowedWrites },
|
|
3664
|
-
fallback: helper.fallback
|
|
3665
|
-
}
|
|
3666
|
-
])
|
|
3667
|
-
)
|
|
3668
|
-
};
|
|
3669
|
-
return [
|
|
3670
|
-
`# Generated by Truthmark ${TRUTHMARK_VERSION}. Rerun truthmark init after upgrades.`,
|
|
3671
|
-
stringify2(manifest, { lineWidth: 0 })
|
|
3672
|
-
].join("\n");
|
|
3673
|
-
};
|
|
3674
|
-
var renderHelperPolicySupport = (helpers) => {
|
|
3675
|
-
const reportHelperId = helpers.find((helper) => helper.id.endsWith("-report"))?.id ?? helpers[0]?.id;
|
|
3676
|
-
const helperLines = helpers.map(
|
|
3677
|
-
(helper) => `- ${helper.id}: optional ${helper.runner}; manual fallback: ${helper.fallback}`
|
|
3678
|
-
).join("\n");
|
|
3679
|
-
return renderSkillSupportFile(
|
|
3680
|
-
"Optional Helper CLI Policy",
|
|
3681
|
-
`Optional helper CLI commands may collect deterministic checkout facts or validate artifacts. If the Truthmark CLI is unavailable or too old for a declared helper, 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
|
-
|
|
3683
|
-
Runner detection:
|
|
3684
|
-
- Check the declared Truthmark CLI runner before invoking a helper.
|
|
3685
|
-
- Invoke helpers through the installed \`truthmark validate ... --json\` CLI command using argv-style arguments from helper-manifest.yml.
|
|
3686
|
-
- If unavailable or version-mismatched, treat the helper as skipped and use the manual fallback.
|
|
3687
|
-
- Do not fail the workflow solely because a helper cannot run.
|
|
3688
|
-
|
|
3689
|
-
Available helpers:
|
|
3690
|
-
${helperLines}
|
|
3691
|
-
|
|
3692
|
-
Final reports should include helper status when helpers are declared for this workflow:
|
|
3693
|
-
|
|
3694
|
-
\`\`\`md
|
|
3695
|
-
Helper scripts:
|
|
3696
|
-
- ${reportHelperId}: ran, passed
|
|
3697
|
-
- validate-write-lease: skipped, no write lease used
|
|
3698
|
-
\`\`\``
|
|
3699
|
-
);
|
|
3700
|
-
};
|
|
3701
3780
|
var renderWorkflowProcedure = (workflowId, config) => {
|
|
3702
3781
|
switch (workflowId) {
|
|
3703
3782
|
case "truthmark-structure":
|
|
@@ -3721,7 +3800,7 @@ var renderWorkflowEntrypoint = (workflowId, config, supportFiles, host) => {
|
|
|
3721
3800
|
const definition = WORKFLOW_PACKAGE_DEFINITIONS[workflowId];
|
|
3722
3801
|
const supportFileUsage = (supportFile) => {
|
|
3723
3802
|
if (supportFile === "support/procedure.md") {
|
|
3724
|
-
return "read before edits or detailed auditing; contains core
|
|
3803
|
+
return "read before edits or detailed auditing; contains core review questions";
|
|
3725
3804
|
}
|
|
3726
3805
|
if (supportFile === "support/report-template.md") {
|
|
3727
3806
|
return "read before the final report";
|
|
@@ -3729,12 +3808,6 @@ var renderWorkflowEntrypoint = (workflowId, config, supportFiles, host) => {
|
|
|
3729
3808
|
if (supportFile === "support/subagents-and-leases.md") {
|
|
3730
3809
|
return "read only when using subagents, leases, or accepting worker output";
|
|
3731
3810
|
}
|
|
3732
|
-
if (supportFile === "support/helper-policy.md") {
|
|
3733
|
-
return "read only when invoking helper validators or reporting helper status";
|
|
3734
|
-
}
|
|
3735
|
-
if (supportFile === "helper-manifest.yml") {
|
|
3736
|
-
return "read only when invoking helper validators or validating helper registration";
|
|
3737
|
-
}
|
|
3738
3811
|
return "available when relevant to the current step";
|
|
3739
3812
|
};
|
|
3740
3813
|
const supportFileList = supportFiles.map((supportFile) => `- ${supportFile} \u2014 ${supportFileUsage(supportFile)}`).join("\n");
|
|
@@ -3744,7 +3817,6 @@ name: ${workflowId}
|
|
|
3744
3817
|
description: ${workflow.description}
|
|
3745
3818
|
argument-hint: ${definition.argumentHint}
|
|
3746
3819
|
user-invocable: true
|
|
3747
|
-
truthmark-version: ${TRUTHMARK_VERSION}
|
|
3748
3820
|
---
|
|
3749
3821
|
|
|
3750
3822
|
# ${definition.title}
|
|
@@ -3819,7 +3891,6 @@ var renderTruthmarkSkillPackage = ({
|
|
|
3819
3891
|
config
|
|
3820
3892
|
);
|
|
3821
3893
|
const subagents = renderWorkflowSubagentSupport(workflowId, host);
|
|
3822
|
-
const helpers = getTruthmarkWorkflow(workflowId).helpers ?? [];
|
|
3823
3894
|
const supportFiles = workflowSupportFiles(workflowId);
|
|
3824
3895
|
const definition = WORKFLOW_PACKAGE_DEFINITIONS[workflowId];
|
|
3825
3896
|
const files = [
|
|
@@ -3851,18 +3922,6 @@ var renderTruthmarkSkillPackage = ({
|
|
|
3851
3922
|
)
|
|
3852
3923
|
});
|
|
3853
3924
|
}
|
|
3854
|
-
if (helpers.length > 0) {
|
|
3855
|
-
files.push(
|
|
3856
|
-
{
|
|
3857
|
-
path: `${skillDirectory}/helper-manifest.yml`,
|
|
3858
|
-
content: renderHelperManifest(helpers)
|
|
3859
|
-
},
|
|
3860
|
-
{
|
|
3861
|
-
path: `${supportDirectory}/helper-policy.md`,
|
|
3862
|
-
content: renderHelperPolicySupport(helpers)
|
|
3863
|
-
}
|
|
3864
|
-
);
|
|
3865
|
-
}
|
|
3866
3925
|
return files;
|
|
3867
3926
|
};
|
|
3868
3927
|
var normalizeOpenCodePermissionPath = (path12) => {
|
|
@@ -3972,7 +4031,7 @@ var renderCodexReadOnlyAgent = ({
|
|
|
3972
4031
|
nicknameCandidates,
|
|
3973
4032
|
developerInstructions
|
|
3974
4033
|
}) => {
|
|
3975
|
-
return `#
|
|
4034
|
+
return `# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
3976
4035
|
name = ${renderTomlString(name)}
|
|
3977
4036
|
description = ${renderTomlString(description)}
|
|
3978
4037
|
sandbox_mode = "read-only"
|
|
@@ -3988,7 +4047,7 @@ var renderCodexWriteAgent = ({
|
|
|
3988
4047
|
nicknameCandidates,
|
|
3989
4048
|
developerInstructions
|
|
3990
4049
|
}) => {
|
|
3991
|
-
return `#
|
|
4050
|
+
return `# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
3992
4051
|
name = ${renderTomlString(name)}
|
|
3993
4052
|
description = ${renderTomlString(description)}
|
|
3994
4053
|
sandbox_mode = "workspace-write"
|
|
@@ -4010,7 +4069,7 @@ description: ${description}
|
|
|
4010
4069
|
tools: [read, search]
|
|
4011
4070
|
---
|
|
4012
4071
|
|
|
4013
|
-
#
|
|
4072
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4014
4073
|
|
|
4015
4074
|
${agentInstructions}
|
|
4016
4075
|
`;
|
|
@@ -4026,7 +4085,7 @@ description: ${description}
|
|
|
4026
4085
|
tools: [read, search, edit]
|
|
4027
4086
|
---
|
|
4028
4087
|
|
|
4029
|
-
#
|
|
4088
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4030
4089
|
|
|
4031
4090
|
${instructions}
|
|
4032
4091
|
`;
|
|
@@ -4044,7 +4103,7 @@ kind: local
|
|
|
4044
4103
|
tools: [read_file, grep_search]
|
|
4045
4104
|
---
|
|
4046
4105
|
|
|
4047
|
-
#
|
|
4106
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4048
4107
|
|
|
4049
4108
|
Manual invocation: @${copilotName}
|
|
4050
4109
|
|
|
@@ -4063,7 +4122,7 @@ kind: local
|
|
|
4063
4122
|
tools: [read_file, grep_search, write_file]
|
|
4064
4123
|
---
|
|
4065
4124
|
|
|
4066
|
-
#
|
|
4125
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4067
4126
|
|
|
4068
4127
|
Manual invocation: @${copilotName} with an explicit parent write lease.
|
|
4069
4128
|
|
|
@@ -4082,7 +4141,7 @@ description: ${description}
|
|
|
4082
4141
|
tools: Read, Grep, Glob, LS
|
|
4083
4142
|
---
|
|
4084
4143
|
|
|
4085
|
-
#
|
|
4144
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4086
4145
|
|
|
4087
4146
|
Manual invocation: use the ${copilotName} subagent.
|
|
4088
4147
|
|
|
@@ -4100,7 +4159,7 @@ description: ${description}
|
|
|
4100
4159
|
tools: Read, Grep, Glob, LS, Edit, MultiEdit
|
|
4101
4160
|
---
|
|
4102
4161
|
|
|
4103
|
-
#
|
|
4162
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4104
4163
|
|
|
4105
4164
|
Manual invocation: use the ${copilotName} subagent with an explicit parent write lease.
|
|
4106
4165
|
|
|
@@ -4233,7 +4292,7 @@ permission:
|
|
|
4233
4292
|
"grep *": allow
|
|
4234
4293
|
---
|
|
4235
4294
|
|
|
4236
|
-
#
|
|
4295
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4237
4296
|
|
|
4238
4297
|
Manual invocation: @${invocation}
|
|
4239
4298
|
|
|
@@ -4268,7 +4327,7 @@ ${editAllowRules}
|
|
|
4268
4327
|
"git diff*": allow
|
|
4269
4328
|
---
|
|
4270
4329
|
|
|
4271
|
-
#
|
|
4330
|
+
# Truthmark-managed generated file. Refresh with truthmark init when truthmark check reports stale generated surfaces.
|
|
4272
4331
|
|
|
4273
4332
|
Manual invocation: @${invocation}
|
|
4274
4333
|
|
|
@@ -4319,7 +4378,6 @@ policy:
|
|
|
4319
4378
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4320
4379
|
|
|
4321
4380
|
truthmark:
|
|
4322
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4323
4381
|
refresh_command: "truthmark init"
|
|
4324
4382
|
`;
|
|
4325
4383
|
};
|
|
@@ -4334,7 +4392,6 @@ policy:
|
|
|
4334
4392
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4335
4393
|
|
|
4336
4394
|
truthmark:
|
|
4337
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4338
4395
|
refresh_command: "truthmark init"
|
|
4339
4396
|
`;
|
|
4340
4397
|
};
|
|
@@ -4349,7 +4406,6 @@ policy:
|
|
|
4349
4406
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4350
4407
|
|
|
4351
4408
|
truthmark:
|
|
4352
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4353
4409
|
refresh_command: "truthmark init"
|
|
4354
4410
|
`;
|
|
4355
4411
|
};
|
|
@@ -4373,7 +4429,7 @@ Workflow:
|
|
|
4373
4429
|
3. ${EVIDENCE_AUTHORITY_INSTRUCTIONS}
|
|
4374
4430
|
${renderTruthDocOwnershipGateSection(
|
|
4375
4431
|
"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,
|
|
4432
|
+
"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
4433
|
)}
|
|
4378
4434
|
4. Update functional code only so implementation matches bounded, current truth claims from the source docs.
|
|
4379
4435
|
5. Do not edit truth docs or truth routing while realizing those docs.
|
|
@@ -4398,22 +4454,6 @@ policy:
|
|
|
4398
4454
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4399
4455
|
|
|
4400
4456
|
truthmark:
|
|
4401
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4402
|
-
refresh_command: "truthmark init"
|
|
4403
|
-
`;
|
|
4404
|
-
};
|
|
4405
|
-
var renderTruthmarkPreviewSkillMetadata = () => {
|
|
4406
|
-
const workflow = getTruthmarkWorkflow("truthmark-preview");
|
|
4407
|
-
return `interface:
|
|
4408
|
-
display_name: "${workflow.displayName}"
|
|
4409
|
-
short_description: "${workflow.shortDescription}"
|
|
4410
|
-
default_prompt: "${workflow.defaultPrompt}"
|
|
4411
|
-
|
|
4412
|
-
policy:
|
|
4413
|
-
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4414
|
-
|
|
4415
|
-
truthmark:
|
|
4416
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4417
4457
|
refresh_command: "truthmark init"
|
|
4418
4458
|
`;
|
|
4419
4459
|
};
|
|
@@ -4428,7 +4468,6 @@ policy:
|
|
|
4428
4468
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4429
4469
|
|
|
4430
4470
|
truthmark:
|
|
4431
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4432
4471
|
refresh_command: "truthmark init"
|
|
4433
4472
|
`;
|
|
4434
4473
|
};
|
|
@@ -4443,7 +4482,6 @@ policy:
|
|
|
4443
4482
|
allow_implicit_invocation: ${workflow.allowImplicitInvocation}
|
|
4444
4483
|
|
|
4445
4484
|
truthmark:
|
|
4446
|
-
version: "${TRUTHMARK_VERSION}"
|
|
4447
4485
|
refresh_command: "truthmark init"
|
|
4448
4486
|
`;
|
|
4449
4487
|
};
|
|
@@ -4507,10 +4545,15 @@ var renderTruthmarkGeminiCheckCommand = (config = defaultAgentConfig()) => {
|
|
|
4507
4545
|
);
|
|
4508
4546
|
};
|
|
4509
4547
|
var renderTruthmarkGeminiPreviewCommand = (config = defaultAgentConfig()) => {
|
|
4510
|
-
|
|
4511
|
-
return
|
|
4512
|
-
|
|
4513
|
-
|
|
4548
|
+
const workflow = getTruthmarkWorkflow("truthmark-preview");
|
|
4549
|
+
return renderGeminiCommand(
|
|
4550
|
+
workflow.description,
|
|
4551
|
+
`This command is the Gemini CLI entrypoint for Truthmark Preview.
|
|
4552
|
+
|
|
4553
|
+
Truth Preview is read-only and explicit. Do not invoke another Truthmark command from here.
|
|
4554
|
+
|
|
4555
|
+
${renderTruthPreviewProcedureBody(config, "gemini")}
|
|
4556
|
+
${renderTruthPreviewAdapterReportFields()}`
|
|
4514
4557
|
);
|
|
4515
4558
|
};
|
|
4516
4559
|
var renderTruthmarkGeminiPortalCommand = (config = defaultAgentConfig()) => {
|
|
@@ -4556,10 +4599,15 @@ var renderTruthmarkCopilotCheckPrompt = (config = defaultAgentConfig()) => {
|
|
|
4556
4599
|
);
|
|
4557
4600
|
};
|
|
4558
4601
|
var renderTruthmarkCopilotPreviewPrompt = (config = defaultAgentConfig()) => {
|
|
4559
|
-
|
|
4560
|
-
return
|
|
4561
|
-
|
|
4562
|
-
|
|
4602
|
+
const workflow = getTruthmarkWorkflow("truthmark-preview");
|
|
4603
|
+
return renderCopilotPromptFile(
|
|
4604
|
+
workflow.description,
|
|
4605
|
+
`This prompt is the GitHub Copilot entrypoint for Truthmark Preview.
|
|
4606
|
+
|
|
4607
|
+
Truth Preview is read-only and explicit. Do not invoke another Truthmark command from here.
|
|
4608
|
+
|
|
4609
|
+
${renderTruthPreviewProcedureBody(config, "copilot")}
|
|
4610
|
+
${renderTruthPreviewAdapterReportFields()}`
|
|
4563
4611
|
);
|
|
4564
4612
|
};
|
|
4565
4613
|
var renderTruthmarkCopilotPortalPrompt = (config = defaultAgentConfig()) => {
|
|
@@ -4603,16 +4651,6 @@ var codexFiles = (config) => {
|
|
|
4603
4651
|
path: TRUTHMARK_SYNC_SKILL_METADATA_PATH,
|
|
4604
4652
|
content: renderTruthmarkSyncSkillMetadata()
|
|
4605
4653
|
},
|
|
4606
|
-
...renderTruthmarkSkillPackage({
|
|
4607
|
-
skillPath: TRUTHMARK_PREVIEW_SKILL_PATH,
|
|
4608
|
-
workflowId: "truthmark-preview",
|
|
4609
|
-
host: "codex",
|
|
4610
|
-
config
|
|
4611
|
-
}),
|
|
4612
|
-
{
|
|
4613
|
-
path: TRUTHMARK_PREVIEW_SKILL_METADATA_PATH,
|
|
4614
|
-
content: renderTruthmarkPreviewSkillMetadata()
|
|
4615
|
-
},
|
|
4616
4654
|
...renderTruthmarkSkillPackage({
|
|
4617
4655
|
skillPath: TRUTHMARK_CHECK_SKILL_PATH,
|
|
4618
4656
|
workflowId: "truthmark-check",
|
|
@@ -4686,12 +4724,6 @@ var opencodeFiles = (config) => {
|
|
|
4686
4724
|
host: "opencode",
|
|
4687
4725
|
config
|
|
4688
4726
|
}),
|
|
4689
|
-
...renderTruthmarkSkillPackage({
|
|
4690
|
-
skillPath: ".opencode/skills/truthmark-preview/SKILL.md",
|
|
4691
|
-
workflowId: "truthmark-preview",
|
|
4692
|
-
host: "opencode",
|
|
4693
|
-
config
|
|
4694
|
-
}),
|
|
4695
4727
|
...renderTruthmarkSkillPackage({
|
|
4696
4728
|
skillPath: ".opencode/skills/truthmark-check/SKILL.md",
|
|
4697
4729
|
workflowId: "truthmark-check",
|
|
@@ -4754,12 +4786,6 @@ var claudeFiles = (config, block) => {
|
|
|
4754
4786
|
host: "claude-code",
|
|
4755
4787
|
config
|
|
4756
4788
|
}),
|
|
4757
|
-
...renderTruthmarkSkillPackage({
|
|
4758
|
-
skillPath: ".claude/skills/truthmark-preview/SKILL.md",
|
|
4759
|
-
workflowId: "truthmark-preview",
|
|
4760
|
-
host: "claude-code",
|
|
4761
|
-
config
|
|
4762
|
-
}),
|
|
4763
4789
|
...renderTruthmarkSkillPackage({
|
|
4764
4790
|
skillPath: ".claude/skills/truthmark-check/SKILL.md",
|
|
4765
4791
|
workflowId: "truthmark-check",
|
|
@@ -4822,12 +4848,6 @@ var copilotFiles = (config, block) => {
|
|
|
4822
4848
|
host: "github-copilot",
|
|
4823
4849
|
config
|
|
4824
4850
|
}),
|
|
4825
|
-
...renderTruthmarkSkillPackage({
|
|
4826
|
-
skillPath: ".github/skills/truthmark-preview/SKILL.md",
|
|
4827
|
-
workflowId: "truthmark-preview",
|
|
4828
|
-
host: "github-copilot",
|
|
4829
|
-
config
|
|
4830
|
-
}),
|
|
4831
4851
|
...renderTruthmarkSkillPackage({
|
|
4832
4852
|
skillPath: ".github/skills/truthmark-check/SKILL.md",
|
|
4833
4853
|
workflowId: "truthmark-check",
|
|
@@ -4918,12 +4938,6 @@ var geminiFiles = (config, block) => {
|
|
|
4918
4938
|
host: "gemini-cli",
|
|
4919
4939
|
config
|
|
4920
4940
|
}),
|
|
4921
|
-
...renderTruthmarkSkillPackage({
|
|
4922
|
-
skillPath: ".gemini/skills/truthmark-preview/SKILL.md",
|
|
4923
|
-
workflowId: "truthmark-preview",
|
|
4924
|
-
host: "gemini-cli",
|
|
4925
|
-
config
|
|
4926
|
-
}),
|
|
4927
4941
|
...renderTruthmarkSkillPackage({
|
|
4928
4942
|
skillPath: ".gemini/skills/truthmark-check/SKILL.md",
|
|
4929
4943
|
workflowId: "truthmark-check",
|
|
@@ -5026,6 +5040,163 @@ var renderGeneratedSurfaces = (config, block = renderAgentsBlock(config)) => {
|
|
|
5026
5040
|
).sort((left, right) => left.path.localeCompare(right.path));
|
|
5027
5041
|
};
|
|
5028
5042
|
|
|
5043
|
+
// src/checks/generated-surfaces.ts
|
|
5044
|
+
var readOptionalFile = async (rootDir, filePath) => {
|
|
5045
|
+
try {
|
|
5046
|
+
return await fs6.readFile(resolveRepoPath(rootDir, filePath), "utf8");
|
|
5047
|
+
} catch (error) {
|
|
5048
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
5049
|
+
return null;
|
|
5050
|
+
}
|
|
5051
|
+
throw error;
|
|
5052
|
+
}
|
|
5053
|
+
};
|
|
5054
|
+
var extractManagedBlock = (content) => {
|
|
5055
|
+
const startIndex = content.indexOf(TRUTHMARK_BLOCK_START);
|
|
5056
|
+
const endIndex = content.indexOf(TRUTHMARK_BLOCK_END);
|
|
5057
|
+
if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {
|
|
5058
|
+
return null;
|
|
5059
|
+
}
|
|
5060
|
+
return content.slice(startIndex, endIndex + TRUTHMARK_BLOCK_END.length);
|
|
5061
|
+
};
|
|
5062
|
+
var normalizeGeneratedSurfaceContent = (content) => {
|
|
5063
|
+
if (content === null) {
|
|
5064
|
+
return null;
|
|
5065
|
+
}
|
|
5066
|
+
return content.replace(/\r\n/g, "\n").replace(/\n$/u, "");
|
|
5067
|
+
};
|
|
5068
|
+
var GENERATED_HOST_SKILL_ROOTS = [
|
|
5069
|
+
".agents/skills",
|
|
5070
|
+
".opencode/skills",
|
|
5071
|
+
".claude/skills",
|
|
5072
|
+
".github/skills",
|
|
5073
|
+
".gemini/skills"
|
|
5074
|
+
];
|
|
5075
|
+
var RETIRED_SKILL_HELPER_PATHS = [
|
|
5076
|
+
"helper-manifest.yml",
|
|
5077
|
+
"support/helper-policy.md"
|
|
5078
|
+
];
|
|
5079
|
+
var RETIRED_PACKAGE_DIRECTORIES = ["truthmark-preview"];
|
|
5080
|
+
var pathExists = async (absolutePath) => {
|
|
5081
|
+
try {
|
|
5082
|
+
await fs6.access(absolutePath);
|
|
5083
|
+
return true;
|
|
5084
|
+
} catch (error) {
|
|
5085
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
5086
|
+
return false;
|
|
5087
|
+
}
|
|
5088
|
+
throw error;
|
|
5089
|
+
}
|
|
5090
|
+
};
|
|
5091
|
+
var listDirectoryFiles = async (rootDir, packageRoot) => {
|
|
5092
|
+
const stack = [packageRoot];
|
|
5093
|
+
const files = [];
|
|
5094
|
+
while (stack.length > 0) {
|
|
5095
|
+
const current = stack.pop();
|
|
5096
|
+
if (current === void 0) {
|
|
5097
|
+
continue;
|
|
5098
|
+
}
|
|
5099
|
+
const absoluteCurrent = resolveRepoPath(rootDir, current);
|
|
5100
|
+
const entries = await fs6.readdir(absoluteCurrent, {
|
|
5101
|
+
withFileTypes: true
|
|
5102
|
+
});
|
|
5103
|
+
for (const entry of entries) {
|
|
5104
|
+
const next = `${current}/${entry.name}`;
|
|
5105
|
+
if (entry.isDirectory()) {
|
|
5106
|
+
stack.push(next);
|
|
5107
|
+
} else {
|
|
5108
|
+
files.push(next);
|
|
5109
|
+
}
|
|
5110
|
+
}
|
|
5111
|
+
}
|
|
5112
|
+
return files;
|
|
5113
|
+
};
|
|
5114
|
+
var collectRetiredGeneratedSurfaces = async (rootDir, expectedSurfacePaths) => {
|
|
5115
|
+
const legacyCandidates = /* @__PURE__ */ new Set();
|
|
5116
|
+
for (const skillRoot of GENERATED_HOST_SKILL_ROOTS) {
|
|
5117
|
+
const absoluteSkillRoot = resolveRepoPath(rootDir, skillRoot);
|
|
5118
|
+
let skillPackages = [];
|
|
5119
|
+
try {
|
|
5120
|
+
skillPackages = await fs6.readdir(absoluteSkillRoot, {
|
|
5121
|
+
withFileTypes: true
|
|
5122
|
+
});
|
|
5123
|
+
} catch (error) {
|
|
5124
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
5125
|
+
continue;
|
|
5126
|
+
}
|
|
5127
|
+
throw error;
|
|
5128
|
+
}
|
|
5129
|
+
const packageNames = skillPackages.filter((entry) => entry.isDirectory() && entry.name.startsWith("truthmark-")).map((entry) => entry.name);
|
|
5130
|
+
for (const packageName of RETIRED_PACKAGE_DIRECTORIES) {
|
|
5131
|
+
if (!packageNames.includes(packageName)) {
|
|
5132
|
+
continue;
|
|
5133
|
+
}
|
|
5134
|
+
const packageRoot = `${skillRoot}/${packageName}`;
|
|
5135
|
+
const packageFiles = await listDirectoryFiles(rootDir, packageRoot);
|
|
5136
|
+
for (const filePath of packageFiles) {
|
|
5137
|
+
if (!expectedSurfacePaths.has(filePath)) {
|
|
5138
|
+
legacyCandidates.add(filePath);
|
|
5139
|
+
}
|
|
5140
|
+
}
|
|
5141
|
+
}
|
|
5142
|
+
for (const packageName of packageNames) {
|
|
5143
|
+
for (const retiredName of RETIRED_SKILL_HELPER_PATHS) {
|
|
5144
|
+
const retiredPath = `${packageName}/${retiredName}`;
|
|
5145
|
+
const relativeRetiredPath = `${skillRoot}/${retiredPath}`;
|
|
5146
|
+
const absoluteRetiredPath = resolveRepoPath(rootDir, relativeRetiredPath);
|
|
5147
|
+
if (await pathExists(absoluteRetiredPath) && !expectedSurfacePaths.has(relativeRetiredPath)) {
|
|
5148
|
+
legacyCandidates.add(relativeRetiredPath);
|
|
5149
|
+
}
|
|
5150
|
+
}
|
|
5151
|
+
}
|
|
5152
|
+
}
|
|
5153
|
+
return Array.from(legacyCandidates);
|
|
5154
|
+
};
|
|
5155
|
+
var checkGeneratedSurfaces = async (rootDir, config) => {
|
|
5156
|
+
const diagnostics = [];
|
|
5157
|
+
const renderedSurfaces = renderGeneratedSurfaces(config);
|
|
5158
|
+
for (const surface of renderedSurfaces) {
|
|
5159
|
+
const content = await readOptionalFile(rootDir, surface.path);
|
|
5160
|
+
if (content === null) {
|
|
5161
|
+
diagnostics.push({
|
|
5162
|
+
category: "generated-surface",
|
|
5163
|
+
severity: "review",
|
|
5164
|
+
message: `Generated surface ${surface.path} is missing; rerun truthmark init.`,
|
|
5165
|
+
file: surface.path
|
|
5166
|
+
});
|
|
5167
|
+
continue;
|
|
5168
|
+
}
|
|
5169
|
+
const comparableContent = normalizeGeneratedSurfaceContent(
|
|
5170
|
+
surface.managedBlock ? extractManagedBlock(content) : content
|
|
5171
|
+
);
|
|
5172
|
+
const expectedContent = normalizeGeneratedSurfaceContent(surface.content);
|
|
5173
|
+
if (comparableContent !== expectedContent) {
|
|
5174
|
+
diagnostics.push({
|
|
5175
|
+
category: "generated-surface",
|
|
5176
|
+
severity: "review",
|
|
5177
|
+
message: `Generated surface ${surface.path} is stale; rerun truthmark init.`,
|
|
5178
|
+
file: surface.path
|
|
5179
|
+
});
|
|
5180
|
+
}
|
|
5181
|
+
}
|
|
5182
|
+
const staleSurfaces = await collectRetiredGeneratedSurfaces(
|
|
5183
|
+
rootDir,
|
|
5184
|
+
new Set(renderedSurfaces.map((surface) => surface.path))
|
|
5185
|
+
);
|
|
5186
|
+
for (const surfacePath of staleSurfaces) {
|
|
5187
|
+
diagnostics.push({
|
|
5188
|
+
category: "generated-surface",
|
|
5189
|
+
severity: "review",
|
|
5190
|
+
message: `Generated surface ${surfacePath} is obsolete; rerun truthmark init.`,
|
|
5191
|
+
file: surfacePath
|
|
5192
|
+
});
|
|
5193
|
+
}
|
|
5194
|
+
return diagnostics;
|
|
5195
|
+
};
|
|
5196
|
+
var findRetiredGeneratedSurfaces = async (rootDir, expectedSurfacePaths) => {
|
|
5197
|
+
return collectRetiredGeneratedSurfaces(rootDir, expectedSurfacePaths);
|
|
5198
|
+
};
|
|
5199
|
+
|
|
5029
5200
|
// src/init/init.ts
|
|
5030
5201
|
var escapeRegExp = (value) => {
|
|
5031
5202
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -5201,9 +5372,17 @@ var runInit = async (cwd) => {
|
|
|
5201
5372
|
results.push(...await scaffoldHierarchy(rootDir, config));
|
|
5202
5373
|
const block = renderAgentsBlock(config);
|
|
5203
5374
|
const platformFiles = renderGeneratedSurfaces(config, block);
|
|
5375
|
+
const expectedSurfacePaths = new Set(platformFiles.map((file) => file.path));
|
|
5204
5376
|
for (const file of platformFiles) {
|
|
5205
5377
|
results.push(await writePlatformFile(rootDir, file));
|
|
5206
5378
|
}
|
|
5379
|
+
const obsoleteSurfacePaths = await findRetiredGeneratedSurfaces(
|
|
5380
|
+
rootDir,
|
|
5381
|
+
expectedSurfacePaths
|
|
5382
|
+
);
|
|
5383
|
+
for (const obsoletePath of obsoleteSurfacePaths) {
|
|
5384
|
+
await fs7.rm(resolveRepoPath(rootDir, obsoletePath), { force: true });
|
|
5385
|
+
}
|
|
5207
5386
|
const changedResults = results.filter((result) => result.status !== "unchanged");
|
|
5208
5387
|
return {
|
|
5209
5388
|
command: "init",
|
|
@@ -5298,7 +5477,7 @@ import fg2 from "fast-glob";
|
|
|
5298
5477
|
var looksLikeGlob = (pattern) => {
|
|
5299
5478
|
return /[*?[\]{}()!+@]/u.test(pattern);
|
|
5300
5479
|
};
|
|
5301
|
-
var
|
|
5480
|
+
var pathExists2 = async (absolutePath) => {
|
|
5302
5481
|
try {
|
|
5303
5482
|
await fs9.stat(absolutePath);
|
|
5304
5483
|
return true;
|
|
@@ -5368,7 +5547,7 @@ var checkControlledPaths = async (rootDir, controlledPaths) => {
|
|
|
5368
5547
|
});
|
|
5369
5548
|
continue;
|
|
5370
5549
|
}
|
|
5371
|
-
if (!await
|
|
5550
|
+
if (!await pathExists2(absoluteEntryPath)) {
|
|
5372
5551
|
diagnostics.push({
|
|
5373
5552
|
category: "authority",
|
|
5374
5553
|
severity: "error",
|
|
@@ -5586,7 +5765,7 @@ var checkFrontmatter = async (rootDir, config, markdownPaths, truthDocumentEntri
|
|
|
5586
5765
|
// src/checks/links.ts
|
|
5587
5766
|
import fs11 from "fs/promises";
|
|
5588
5767
|
import path5 from "path";
|
|
5589
|
-
var
|
|
5768
|
+
var pathExists3 = async (absolutePath) => {
|
|
5590
5769
|
try {
|
|
5591
5770
|
await fs11.stat(absolutePath);
|
|
5592
5771
|
return true;
|
|
@@ -5632,7 +5811,7 @@ var checkLinks = async (rootDir, markdownPaths) => {
|
|
|
5632
5811
|
});
|
|
5633
5812
|
continue;
|
|
5634
5813
|
}
|
|
5635
|
-
if (!await
|
|
5814
|
+
if (!await pathExists3(absoluteTarget)) {
|
|
5636
5815
|
diagnostics.push({
|
|
5637
5816
|
category: "links",
|
|
5638
5817
|
severity: "error",
|
|
@@ -6008,7 +6187,7 @@ var classifyPath = (filePath, ignorePatterns) => {
|
|
|
6008
6187
|
var looksLikeGlob2 = (pattern) => {
|
|
6009
6188
|
return /[*?[\]{}()!+@]/u.test(pattern);
|
|
6010
6189
|
};
|
|
6011
|
-
var
|
|
6190
|
+
var pathExists4 = async (absolutePath) => {
|
|
6012
6191
|
try {
|
|
6013
6192
|
await fs13.stat(absolutePath);
|
|
6014
6193
|
return true;
|
|
@@ -6300,7 +6479,7 @@ var checkAreas = async (rootDir, config) => {
|
|
|
6300
6479
|
areaHasTruthDocumentErrors = true;
|
|
6301
6480
|
continue;
|
|
6302
6481
|
}
|
|
6303
|
-
if (!await
|
|
6482
|
+
if (!await pathExists4(absoluteTruthDocumentPath)) {
|
|
6304
6483
|
diagnostics.push({
|
|
6305
6484
|
category: "area-index",
|
|
6306
6485
|
severity: "error",
|
|
@@ -6404,7 +6583,7 @@ var checkAreas = async (rootDir, config) => {
|
|
|
6404
6583
|
entry.valid = false;
|
|
6405
6584
|
continue;
|
|
6406
6585
|
}
|
|
6407
|
-
if (!await
|
|
6586
|
+
if (!await pathExists4(absoluteCodeSurfacePath)) {
|
|
6408
6587
|
diagnostics.push({
|
|
6409
6588
|
category: "area-index",
|
|
6410
6589
|
severity: "error",
|
|
@@ -6608,95 +6787,12 @@ var checkDecisionSections = async (rootDir, config, markdownPaths, truthDocument
|
|
|
6608
6787
|
return diagnostics;
|
|
6609
6788
|
};
|
|
6610
6789
|
|
|
6611
|
-
// src/checks/generated-surfaces.ts
|
|
6612
|
-
import fs15 from "fs/promises";
|
|
6613
|
-
var readOptionalFile = async (rootDir, filePath) => {
|
|
6614
|
-
try {
|
|
6615
|
-
return await fs15.readFile(resolveRepoPath(rootDir, filePath), "utf8");
|
|
6616
|
-
} catch (error) {
|
|
6617
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
6618
|
-
return null;
|
|
6619
|
-
}
|
|
6620
|
-
throw error;
|
|
6621
|
-
}
|
|
6622
|
-
};
|
|
6623
|
-
var extractManagedBlock = (content) => {
|
|
6624
|
-
const startIndex = content.indexOf(TRUTHMARK_BLOCK_START);
|
|
6625
|
-
const endIndex = content.indexOf(TRUTHMARK_BLOCK_END);
|
|
6626
|
-
if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {
|
|
6627
|
-
return null;
|
|
6628
|
-
}
|
|
6629
|
-
return content.slice(startIndex, endIndex + TRUTHMARK_BLOCK_END.length);
|
|
6630
|
-
};
|
|
6631
|
-
var normalizeGeneratedSurfaceContent = (content) => {
|
|
6632
|
-
if (content === null) {
|
|
6633
|
-
return null;
|
|
6634
|
-
}
|
|
6635
|
-
return content.replace(/\r\n/g, "\n").replace(/\n$/u, "");
|
|
6636
|
-
};
|
|
6637
|
-
var versionMarkers = (content) => {
|
|
6638
|
-
const markers = [];
|
|
6639
|
-
const patterns = [
|
|
6640
|
-
/truthmark-version:\s*([^\s]+)/gu,
|
|
6641
|
-
/Generated by Truthmark\s+([^\s.]+(?:\.[^\s.]+){1,2})/gu,
|
|
6642
|
-
/^version:\s*"(\d+\.\d+\.\d+)"\s*$/gmu
|
|
6643
|
-
];
|
|
6644
|
-
for (const pattern of patterns) {
|
|
6645
|
-
for (const match of content.matchAll(pattern)) {
|
|
6646
|
-
if (match[1]) {
|
|
6647
|
-
markers.push(match[1]);
|
|
6648
|
-
}
|
|
6649
|
-
}
|
|
6650
|
-
}
|
|
6651
|
-
return markers;
|
|
6652
|
-
};
|
|
6653
|
-
var checkGeneratedSurfaces = async (rootDir, config) => {
|
|
6654
|
-
const diagnostics = [];
|
|
6655
|
-
for (const surface of renderGeneratedSurfaces(config)) {
|
|
6656
|
-
const content = await readOptionalFile(rootDir, surface.path);
|
|
6657
|
-
if (content === null) {
|
|
6658
|
-
diagnostics.push({
|
|
6659
|
-
category: "generated-surface",
|
|
6660
|
-
severity: "review",
|
|
6661
|
-
message: `Generated surface ${surface.path} is missing; rerun truthmark init.`,
|
|
6662
|
-
file: surface.path
|
|
6663
|
-
});
|
|
6664
|
-
continue;
|
|
6665
|
-
}
|
|
6666
|
-
const comparableContent = normalizeGeneratedSurfaceContent(
|
|
6667
|
-
surface.managedBlock ? extractManagedBlock(content) : content
|
|
6668
|
-
);
|
|
6669
|
-
const expectedContent = normalizeGeneratedSurfaceContent(surface.content);
|
|
6670
|
-
if (comparableContent !== expectedContent) {
|
|
6671
|
-
diagnostics.push({
|
|
6672
|
-
category: "generated-surface",
|
|
6673
|
-
severity: "review",
|
|
6674
|
-
message: `Generated surface ${surface.path} is stale; rerun truthmark init.`,
|
|
6675
|
-
file: surface.path
|
|
6676
|
-
});
|
|
6677
|
-
}
|
|
6678
|
-
const versionContent = surface.managedBlock ? comparableContent ?? "" : content;
|
|
6679
|
-
const mismatchedVersions = versionMarkers(versionContent).filter(
|
|
6680
|
-
(version) => version !== TRUTHMARK_VERSION
|
|
6681
|
-
);
|
|
6682
|
-
if (mismatchedVersions.length > 0) {
|
|
6683
|
-
diagnostics.push({
|
|
6684
|
-
category: "generated-surface",
|
|
6685
|
-
severity: "review",
|
|
6686
|
-
message: `Generated surface ${surface.path} has Truthmark version ${mismatchedVersions[0]} but current version is ${TRUTHMARK_VERSION}; rerun truthmark init.`,
|
|
6687
|
-
file: surface.path
|
|
6688
|
-
});
|
|
6689
|
-
}
|
|
6690
|
-
}
|
|
6691
|
-
return diagnostics;
|
|
6692
|
-
};
|
|
6693
|
-
|
|
6694
6790
|
// src/impact/build.ts
|
|
6695
6791
|
import path10 from "path";
|
|
6696
6792
|
import micromatch7 from "micromatch";
|
|
6697
6793
|
|
|
6698
6794
|
// src/repo-index/file-tree.ts
|
|
6699
|
-
import
|
|
6795
|
+
import fs15 from "fs/promises";
|
|
6700
6796
|
import path7 from "path";
|
|
6701
6797
|
import { execa as execa2 } from "execa";
|
|
6702
6798
|
import fg5 from "fast-glob";
|
|
@@ -6872,7 +6968,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
|
|
|
6872
6968
|
for (const filePath of discoveredFiles.filter((entry) => !isIgnoredPath(entry, ignore)).sort()) {
|
|
6873
6969
|
let stat;
|
|
6874
6970
|
try {
|
|
6875
|
-
stat = await
|
|
6971
|
+
stat = await fs15.stat(path7.join(rootDir, filePath));
|
|
6876
6972
|
} catch (error) {
|
|
6877
6973
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
6878
6974
|
continue;
|
|
@@ -6896,7 +6992,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
|
|
|
6896
6992
|
});
|
|
6897
6993
|
}
|
|
6898
6994
|
if (kind === "doc") {
|
|
6899
|
-
const source = await
|
|
6995
|
+
const source = await fs15.readFile(path7.join(rootDir, filePath), "utf8");
|
|
6900
6996
|
const parsed = parseFrontmatter(source);
|
|
6901
6997
|
const markdown = parseMarkdownDocument(parsed.content);
|
|
6902
6998
|
const title = markdown.headings.find((heading) => heading.depth === 1)?.text ?? null;
|
|
@@ -6924,7 +7020,7 @@ var discoverRepoFiles = async (rootDir, ignore) => {
|
|
|
6924
7020
|
};
|
|
6925
7021
|
|
|
6926
7022
|
// src/repo-index/package-metadata.ts
|
|
6927
|
-
import
|
|
7023
|
+
import fs16 from "fs/promises";
|
|
6928
7024
|
import path8 from "path";
|
|
6929
7025
|
import fg6 from "fast-glob";
|
|
6930
7026
|
var packageManagerFor = async (rootDir, packageDir) => {
|
|
@@ -6937,7 +7033,7 @@ var packageManagerFor = async (rootDir, packageDir) => {
|
|
|
6937
7033
|
];
|
|
6938
7034
|
for (const [lockfile, manager] of lockfiles) {
|
|
6939
7035
|
try {
|
|
6940
|
-
await
|
|
7036
|
+
await fs16.access(path8.join(rootDir, packageDir, lockfile));
|
|
6941
7037
|
return manager;
|
|
6942
7038
|
} catch {
|
|
6943
7039
|
continue;
|
|
@@ -6955,7 +7051,7 @@ var discoverPackageMetadata = async (rootDir) => {
|
|
|
6955
7051
|
const packages = [];
|
|
6956
7052
|
for (const packageFile of packageFiles.sort()) {
|
|
6957
7053
|
const packageDir = path8.posix.dirname(packageFile) === "." ? "" : path8.posix.dirname(packageFile);
|
|
6958
|
-
const raw = JSON.parse(await
|
|
7054
|
+
const raw = JSON.parse(await fs16.readFile(path8.join(rootDir, packageFile), "utf8"));
|
|
6959
7055
|
const scripts = raw.scripts && typeof raw.scripts === "object" ? Object.keys(raw.scripts).sort() : [];
|
|
6960
7056
|
packages.push({
|
|
6961
7057
|
path: packageFile,
|
|
@@ -7060,7 +7156,7 @@ var buildRepoIndex = async (cwd) => {
|
|
|
7060
7156
|
import { execa as execa4 } from "execa";
|
|
7061
7157
|
|
|
7062
7158
|
// src/git/changes.ts
|
|
7063
|
-
import
|
|
7159
|
+
import fs17 from "fs/promises";
|
|
7064
7160
|
import path9 from "path";
|
|
7065
7161
|
import { execa as execa3 } from "execa";
|
|
7066
7162
|
var normalizePath3 = (filePath) => {
|
|
@@ -7070,9 +7166,9 @@ var listChangedPaths = async (cwd, args) => {
|
|
|
7070
7166
|
const result = await execa3("git", args, { cwd });
|
|
7071
7167
|
return result.stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => normalizePath3(line));
|
|
7072
7168
|
};
|
|
7073
|
-
var
|
|
7169
|
+
var pathExists5 = async (filePath) => {
|
|
7074
7170
|
try {
|
|
7075
|
-
await
|
|
7171
|
+
await fs17.access(filePath);
|
|
7076
7172
|
return true;
|
|
7077
7173
|
} catch {
|
|
7078
7174
|
return false;
|
|
@@ -7116,7 +7212,7 @@ var getUncommittedChanges = async (cwd) => {
|
|
|
7116
7212
|
const deletedPathCandidates = /* @__PURE__ */ new Set([...stagedDeletedPaths, ...unstagedDeletedPaths]);
|
|
7117
7213
|
for (const deletedPath of deletedPathCandidates) {
|
|
7118
7214
|
const change = getOrCreateChange(changesByPath, deletedPath);
|
|
7119
|
-
change.deleted = !await
|
|
7215
|
+
change.deleted = !await pathExists5(path9.join(rootDir, deletedPath));
|
|
7120
7216
|
}
|
|
7121
7217
|
return Array.from(changesByPath.values()).sort((left, right) => {
|
|
7122
7218
|
return left.path.localeCompare(right.path);
|
|
@@ -7319,11 +7415,11 @@ var checkFreshness = async (rootDir, _config, _truthDocumentPaths, base) => {
|
|
|
7319
7415
|
};
|
|
7320
7416
|
|
|
7321
7417
|
// src/evidence/validate.ts
|
|
7322
|
-
import
|
|
7418
|
+
import fs19 from "fs/promises";
|
|
7323
7419
|
import fg7 from "fast-glob";
|
|
7324
7420
|
|
|
7325
7421
|
// src/evidence/parse.ts
|
|
7326
|
-
import
|
|
7422
|
+
import fs18 from "fs/promises";
|
|
7327
7423
|
import path11 from "path";
|
|
7328
7424
|
import { parse as parse4 } from "yaml";
|
|
7329
7425
|
var yamlFencePattern = /```ya?ml\s*\n([\s\S]*?)```/giu;
|
|
@@ -7343,7 +7439,7 @@ var toEvidenceReference = (truthDocPath, raw) => {
|
|
|
7343
7439
|
};
|
|
7344
7440
|
};
|
|
7345
7441
|
var parseEvidenceReferences = async (rootDir, truthDocPath) => {
|
|
7346
|
-
const source = await
|
|
7442
|
+
const source = await fs18.readFile(path11.join(rootDir, truthDocPath), "utf8");
|
|
7347
7443
|
const parsed = parseFrontmatter(source);
|
|
7348
7444
|
const references = [];
|
|
7349
7445
|
for (const entry of parseSourceReferences(source, truthDocPath)) {
|
|
@@ -7374,9 +7470,9 @@ var parseEvidenceReferences = async (rootDir, truthDocPath) => {
|
|
|
7374
7470
|
};
|
|
7375
7471
|
|
|
7376
7472
|
// src/evidence/validate.ts
|
|
7377
|
-
var
|
|
7473
|
+
var pathExists6 = async (filePath) => {
|
|
7378
7474
|
try {
|
|
7379
|
-
await
|
|
7475
|
+
await fs19.access(filePath);
|
|
7380
7476
|
return true;
|
|
7381
7477
|
} catch {
|
|
7382
7478
|
return false;
|
|
@@ -7421,7 +7517,7 @@ var validateHash = async (rootDir, reference) => {
|
|
|
7421
7517
|
if (!reference.contentHash.startsWith("sha256:")) {
|
|
7422
7518
|
return diagnosticFor(reference, `Evidence hash for ${reference.path} must use sha256:.`);
|
|
7423
7519
|
}
|
|
7424
|
-
const source = await
|
|
7520
|
+
const source = await fs19.readFile(resolveRepoPath(rootDir, reference.path), "utf8");
|
|
7425
7521
|
const lines = source.split("\n");
|
|
7426
7522
|
const startLine = reference.startLine ?? 1;
|
|
7427
7523
|
const endLine = reference.endLine ?? lines.length;
|
|
@@ -7435,7 +7531,7 @@ var validateLineSpan = async (rootDir, reference) => {
|
|
|
7435
7531
|
if (reference.startLine === void 0 && reference.endLine === void 0) {
|
|
7436
7532
|
return null;
|
|
7437
7533
|
}
|
|
7438
|
-
const source = await
|
|
7534
|
+
const source = await fs19.readFile(resolveRepoPath(rootDir, reference.path), "utf8");
|
|
7439
7535
|
const lines = source.split("\n");
|
|
7440
7536
|
const startLine = reference.startLine ?? 1;
|
|
7441
7537
|
const endLine = reference.endLine ?? lines.length;
|
|
@@ -7450,7 +7546,7 @@ var validateReference = async (rootDir, reference) => {
|
|
|
7450
7546
|
}
|
|
7451
7547
|
const absolutePath = resolveRepoPath(rootDir, reference.path);
|
|
7452
7548
|
await assertRepoContainment(rootDir, absolutePath);
|
|
7453
|
-
if (!await
|
|
7549
|
+
if (!await pathExists6(absolutePath)) {
|
|
7454
7550
|
diagnostics.push(diagnosticFor(reference, `Referenced file ${reference.path} does not exist.`));
|
|
7455
7551
|
return diagnostics;
|
|
7456
7552
|
}
|
|
@@ -7721,25 +7817,49 @@ var helperCommandsFor = (manifestEntry) => (manifestEntry.helpers ?? []).map((he
|
|
|
7721
7817
|
argv: [...helper.command.argv],
|
|
7722
7818
|
optional: helper.optional
|
|
7723
7819
|
}));
|
|
7724
|
-
var evidenceFor = (manifestEntry) => manifestEntry.
|
|
7725
|
-
|
|
7820
|
+
var evidenceFor = (manifestEntry) => manifestEntry.reviewQuestions.filter(
|
|
7821
|
+
(question) => /evidence|ownership|containment/iu.test(question)
|
|
7822
|
+
);
|
|
7823
|
+
var baseContext = (manifestEntry, mode, allowedWritePaths, routeFiles, primaryTruthDocs, candidateStaleTruthDocs, forbiddenWritePaths, writeLeaseRequired) => ({
|
|
7726
7824
|
mode,
|
|
7727
7825
|
allowedWritePaths: uniqueSorted3(allowedWritePaths),
|
|
7826
|
+
routeFiles: uniqueSorted3(routeFiles),
|
|
7827
|
+
primaryTruthDocs: uniqueSorted3(primaryTruthDocs),
|
|
7828
|
+
candidateStaleTruthDocs: uniqueSorted3(candidateStaleTruthDocs),
|
|
7728
7829
|
forbiddenWritePaths: uniqueSorted3(forbiddenWritePaths),
|
|
7729
|
-
stopConditions: [
|
|
7730
|
-
|
|
7830
|
+
stopConditions: [
|
|
7831
|
+
...manifestEntry.negativeTriggers,
|
|
7832
|
+
...manifestEntry.forbiddenAdjacency
|
|
7833
|
+
],
|
|
7834
|
+
evidencePrompts: uniqueSorted3([
|
|
7835
|
+
...evidenceFor(manifestEntry),
|
|
7836
|
+
...candidateStaleTruthDocs.length > 0 ? [
|
|
7837
|
+
"Record checkout evidence and the reason before touching a candidate stale truth doc outside primaryTruthDocs."
|
|
7838
|
+
] : []
|
|
7839
|
+
]),
|
|
7731
7840
|
helperValidationCommands: helperCommandsFor(manifestEntry),
|
|
7732
7841
|
writeLeaseRequired
|
|
7733
7842
|
});
|
|
7734
7843
|
var buildWorkflowActionContext = (manifestEntry, data = {}) => {
|
|
7735
7844
|
if (manifestEntry.id === "truthmark-preview" || manifestEntry.id === "truthmark-check") {
|
|
7736
|
-
return baseContext(manifestEntry, "read-only", [], [], false);
|
|
7845
|
+
return baseContext(manifestEntry, "read-only", [], [], [], [], [], false);
|
|
7737
7846
|
}
|
|
7738
7847
|
if (manifestEntry.id === "truthmark-sync" || manifestEntry.id === "truthmark-document") {
|
|
7848
|
+
const routeFiles = data.routeFiles ?? [];
|
|
7849
|
+
const primaryTruthDocs = data.primaryTruthDocs ?? data.truthDocs ?? [];
|
|
7850
|
+
const candidateStaleTruthDocs = data.candidateStaleTruthDocs ?? [];
|
|
7739
7851
|
return baseContext(
|
|
7740
7852
|
manifestEntry,
|
|
7741
7853
|
"truth-doc-write",
|
|
7742
|
-
[
|
|
7854
|
+
[
|
|
7855
|
+
...data.routeIndexPath ? [data.routeIndexPath] : [],
|
|
7856
|
+
...routeFiles,
|
|
7857
|
+
...primaryTruthDocs,
|
|
7858
|
+
...candidateStaleTruthDocs
|
|
7859
|
+
],
|
|
7860
|
+
routeFiles,
|
|
7861
|
+
primaryTruthDocs,
|
|
7862
|
+
candidateStaleTruthDocs,
|
|
7743
7863
|
[],
|
|
7744
7864
|
true
|
|
7745
7865
|
);
|
|
@@ -7753,6 +7873,9 @@ var buildWorkflowActionContext = (manifestEntry, data = {}) => {
|
|
|
7753
7873
|
...data.routeFiles ?? [],
|
|
7754
7874
|
...data.starterTruthDocs ?? []
|
|
7755
7875
|
],
|
|
7876
|
+
data.routeFiles ?? [],
|
|
7877
|
+
data.starterTruthDocs ?? [],
|
|
7878
|
+
[],
|
|
7756
7879
|
[],
|
|
7757
7880
|
true
|
|
7758
7881
|
);
|
|
@@ -7762,6 +7885,9 @@ var buildWorkflowActionContext = (manifestEntry, data = {}) => {
|
|
|
7762
7885
|
manifestEntry,
|
|
7763
7886
|
"code-write",
|
|
7764
7887
|
data.codeWritePaths ?? [],
|
|
7888
|
+
data.routeFiles ?? [],
|
|
7889
|
+
data.truthDocs ?? [],
|
|
7890
|
+
[],
|
|
7765
7891
|
[
|
|
7766
7892
|
...data.routeIndexPath ? [data.routeIndexPath] : [],
|
|
7767
7893
|
...data.routeFiles ?? [],
|
|
@@ -7776,6 +7902,9 @@ var buildWorkflowActionContext = (manifestEntry, data = {}) => {
|
|
|
7776
7902
|
"portal-write",
|
|
7777
7903
|
data.portalEnabled && data.portalOutputPath ? [`${data.portalOutputPath}/**`] : [],
|
|
7778
7904
|
[],
|
|
7905
|
+
[],
|
|
7906
|
+
[],
|
|
7907
|
+
[],
|
|
7779
7908
|
false
|
|
7780
7909
|
);
|
|
7781
7910
|
};
|
|
@@ -7789,16 +7918,26 @@ var helperCommandsFor2 = (workflow) => (TRUTHMARK_WORKFLOW_MANIFEST[workflow].he
|
|
|
7789
7918
|
}));
|
|
7790
7919
|
var uniqueSorted4 = (values) => [...new Set(values.filter((value) => value.length > 0))].sort();
|
|
7791
7920
|
var isWriteCapable = (workflow) => !["truthmark-preview", "truthmark-check"].includes(workflow);
|
|
7792
|
-
var DEFAULT_BASE_CANDIDATES = [
|
|
7921
|
+
var DEFAULT_BASE_CANDIDATES = [
|
|
7922
|
+
"@{upstream}",
|
|
7923
|
+
"origin/main",
|
|
7924
|
+
"main",
|
|
7925
|
+
"origin/master",
|
|
7926
|
+
"master"
|
|
7927
|
+
];
|
|
7793
7928
|
var selectComparisonBase = async (rootDir, suppliedBase) => {
|
|
7794
7929
|
if (suppliedBase) {
|
|
7795
7930
|
return suppliedBase;
|
|
7796
7931
|
}
|
|
7797
7932
|
for (const candidate of DEFAULT_BASE_CANDIDATES) {
|
|
7798
|
-
const result = await execa5(
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7933
|
+
const result = await execa5(
|
|
7934
|
+
"git",
|
|
7935
|
+
["rev-parse", "--verify", `${candidate}^{commit}`],
|
|
7936
|
+
{
|
|
7937
|
+
cwd: rootDir,
|
|
7938
|
+
reject: false
|
|
7939
|
+
}
|
|
7940
|
+
);
|
|
7802
7941
|
if ((result.exitCode ?? 1) === 0) {
|
|
7803
7942
|
return candidate;
|
|
7804
7943
|
}
|
|
@@ -7806,59 +7945,110 @@ var selectComparisonBase = async (rootDir, suppliedBase) => {
|
|
|
7806
7945
|
return null;
|
|
7807
7946
|
};
|
|
7808
7947
|
var routeFilesFor = (repoIndex) => uniqueSorted4(repoIndex.routeMap.routes.map((route) => route.sourcePath));
|
|
7948
|
+
var candidateStaleTruthDocsFor = (indexedTruthDocs, primaryTruthDocs) => {
|
|
7949
|
+
const primary = new Set(primaryTruthDocs);
|
|
7950
|
+
return indexedTruthDocs.filter((truthDoc) => !primary.has(truthDoc));
|
|
7951
|
+
};
|
|
7809
7952
|
var hasUnmappedFunctionalChange = (impactSet) => impactSet?.diagnostics.some(
|
|
7810
|
-
(diagnostic) => diagnostic.category === "impact" && /not mapped to a Truthmark route|no affected truth document/u.test(
|
|
7953
|
+
(diagnostic) => diagnostic.category === "impact" && /not mapped to a Truthmark route|no affected truth document/u.test(
|
|
7954
|
+
diagnostic.message
|
|
7955
|
+
)
|
|
7811
7956
|
) ?? false;
|
|
7812
7957
|
var applicabilityFor = (workflow, diagnostics, impactSet) => {
|
|
7813
7958
|
const reasons = [];
|
|
7814
|
-
if (diagnostics.some(
|
|
7959
|
+
if (diagnostics.some(
|
|
7960
|
+
(diagnostic) => diagnostic.message.includes("Missing .truthmark/config.yml")
|
|
7961
|
+
)) {
|
|
7815
7962
|
reasons.push("Missing .truthmark/config.yml.");
|
|
7816
|
-
return {
|
|
7963
|
+
return {
|
|
7964
|
+
state: isWriteCapable(workflow) ? "needs_manual_review" : "not_applicable",
|
|
7965
|
+
reasons
|
|
7966
|
+
};
|
|
7817
7967
|
}
|
|
7818
7968
|
if (workflow === "truthmark-sync" && !impactSet) {
|
|
7819
|
-
reasons.push(
|
|
7820
|
-
|
|
7969
|
+
reasons.push(
|
|
7970
|
+
"Choose a comparison base with --base <ref> when workflow status cannot infer one automatically."
|
|
7971
|
+
);
|
|
7972
|
+
return { state: "needs_manual_review", reasons };
|
|
7821
7973
|
}
|
|
7822
7974
|
if (workflow === "truthmark-realize" && !impactSet) {
|
|
7823
|
-
reasons.push(
|
|
7824
|
-
|
|
7975
|
+
reasons.push(
|
|
7976
|
+
"Choose a comparison base with --base <ref> so the helper can suggest bounded code-write paths."
|
|
7977
|
+
);
|
|
7978
|
+
return { state: "needs_manual_review", reasons };
|
|
7825
7979
|
}
|
|
7826
7980
|
if (hasUnmappedFunctionalChange(impactSet)) {
|
|
7827
|
-
reasons.push(
|
|
7828
|
-
|
|
7981
|
+
reasons.push(
|
|
7982
|
+
"Changed functional files need route ownership review before truth-doc suggestions are reliable."
|
|
7983
|
+
);
|
|
7984
|
+
return { state: "needs_routing_review", reasons };
|
|
7829
7985
|
}
|
|
7830
7986
|
if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
7831
|
-
reasons.push(
|
|
7832
|
-
|
|
7987
|
+
reasons.push(
|
|
7988
|
+
"Existing diagnostics need manual review before using helper write suggestions."
|
|
7989
|
+
);
|
|
7990
|
+
return { state: "needs_manual_review", reasons };
|
|
7833
7991
|
}
|
|
7834
|
-
return { state: "
|
|
7992
|
+
return { state: "ready", reasons };
|
|
7993
|
+
};
|
|
7994
|
+
var workflowCardFor = (workflow, applicability, diagnostics, impactSet) => {
|
|
7995
|
+
const helpers = helperCommandsFor2(workflow);
|
|
7996
|
+
const diagnosticQuestions = diagnostics.filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.message);
|
|
7997
|
+
return {
|
|
7998
|
+
affectedFiles: uniqueSorted4(
|
|
7999
|
+
impactSet?.changedFiles.map((file) => file.path) ?? []
|
|
8000
|
+
),
|
|
8001
|
+
likelyRouteOwners: uniqueSorted4(
|
|
8002
|
+
impactSet?.affectedRoutes.map((route) => route.sourcePath) ?? []
|
|
8003
|
+
),
|
|
8004
|
+
suggestedTruthDocs: applicability.state === "needs_routing_review" ? [] : uniqueSorted4(impactSet?.affectedTruthDocs ?? []),
|
|
8005
|
+
openQuestions: uniqueSorted4([
|
|
8006
|
+
...applicability.reasons,
|
|
8007
|
+
...diagnosticQuestions
|
|
8008
|
+
]),
|
|
8009
|
+
skippedHelperStatus: helpers.map((helper) => ({
|
|
8010
|
+
helper: helper.id,
|
|
8011
|
+
status: "skipped",
|
|
8012
|
+
reason: "workflow status does not run optional helpers; inspect the checkout directly or run the helper manually when useful."
|
|
8013
|
+
}))
|
|
8014
|
+
};
|
|
7835
8015
|
};
|
|
7836
8016
|
var contextDataFor = (workflow, repoIndex, config, impactSet) => {
|
|
7837
8017
|
if (!config) {
|
|
7838
8018
|
return {};
|
|
7839
8019
|
}
|
|
7840
8020
|
const routeFiles = routeFilesFor(repoIndex);
|
|
7841
|
-
const indexedTruthDocs = uniqueSorted4(
|
|
7842
|
-
|
|
8021
|
+
const indexedTruthDocs = uniqueSorted4(
|
|
8022
|
+
repoIndex.routeMap.routes.flatMap((route) => route.truthDocs)
|
|
8023
|
+
);
|
|
8024
|
+
const primaryTruthDocs = uniqueSorted4(impactSet?.affectedTruthDocs ?? []);
|
|
8025
|
+
const candidateStaleTruthDocs = workflow === "truthmark-sync" ? candidateStaleTruthDocsFor(indexedTruthDocs, primaryTruthDocs) : [];
|
|
8026
|
+
const truthDocs = workflow === "truthmark-sync" ? uniqueSorted4([...primaryTruthDocs, ...candidateStaleTruthDocs]) : uniqueSorted4(impactSet?.affectedTruthDocs ?? indexedTruthDocs);
|
|
7843
8027
|
return {
|
|
7844
8028
|
routeIndexPath: config.truthmark.paths.routesIndex,
|
|
7845
8029
|
routeFiles,
|
|
7846
8030
|
truthRoot: config.truthmark.paths.truthRoot,
|
|
7847
8031
|
truthDocs,
|
|
8032
|
+
primaryTruthDocs: workflow === "truthmark-sync" ? primaryTruthDocs : truthDocs,
|
|
8033
|
+
candidateStaleTruthDocs,
|
|
7848
8034
|
starterTruthDocs: workflow === "truthmark-structure" ? truthDocs : [],
|
|
7849
|
-
codeWritePaths: workflow === "truthmark-realize" ? uniqueSorted4(
|
|
8035
|
+
codeWritePaths: workflow === "truthmark-realize" ? uniqueSorted4(
|
|
8036
|
+
impactSet?.affectedRoutes.flatMap((route) => route.codeSurface) ?? []
|
|
8037
|
+
) : [],
|
|
7850
8038
|
portalEnabled: config.truthmark.generated.portal.enabled,
|
|
7851
8039
|
portalOutputPath: config.truthmark.paths.portalOutput,
|
|
7852
8040
|
routes: repoIndex.routeMap.routes
|
|
7853
8041
|
};
|
|
7854
8042
|
};
|
|
7855
8043
|
var nextStepsFor = (workflow, applicability, comparisonBase) => {
|
|
7856
|
-
if (applicability.state === "
|
|
7857
|
-
return [
|
|
8044
|
+
if (applicability.state === "needs_routing_review") {
|
|
8045
|
+
return [
|
|
8046
|
+
"Run Truth Structure or repair route ownership before writing truth docs."
|
|
8047
|
+
];
|
|
7858
8048
|
}
|
|
7859
|
-
if ((workflow === "truthmark-sync" || workflow === "truthmark-realize") && applicability.state === "
|
|
8049
|
+
if ((workflow === "truthmark-sync" || workflow === "truthmark-realize") && applicability.state === "needs_manual_review" && !comparisonBase) {
|
|
7860
8050
|
return [
|
|
7861
|
-
workflow === "truthmark-sync" ? "Rerun with --base <ref> so
|
|
8051
|
+
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."
|
|
7862
8052
|
];
|
|
7863
8053
|
}
|
|
7864
8054
|
return [];
|
|
@@ -7873,26 +8063,44 @@ var buildWorkflowState = async (cwd, options) => {
|
|
|
7873
8063
|
const loadResult = await loadConfig(rootDir);
|
|
7874
8064
|
const comparisonBase = options.base ? options.base : options.workflow === "truthmark-sync" ? await selectComparisonBase(rootDir) : null;
|
|
7875
8065
|
const impactSet = comparisonBase ? await buildImpactSet(rootDir, { base: comparisonBase }) : null;
|
|
7876
|
-
const checkResult = await runCheck(
|
|
8066
|
+
const checkResult = await runCheck(
|
|
8067
|
+
cwd,
|
|
8068
|
+
comparisonBase ? { base: comparisonBase } : {}
|
|
8069
|
+
);
|
|
7877
8070
|
const diagnostics = [
|
|
7878
8071
|
...loadResult.diagnostics,
|
|
7879
8072
|
...repoIndex.diagnostics,
|
|
7880
8073
|
...impactSet?.diagnostics ?? [],
|
|
7881
8074
|
...checkResult.diagnostics
|
|
7882
8075
|
];
|
|
7883
|
-
const applicability = applicabilityFor(
|
|
7884
|
-
|
|
8076
|
+
const applicability = applicabilityFor(
|
|
8077
|
+
options.workflow,
|
|
8078
|
+
diagnostics,
|
|
8079
|
+
impactSet
|
|
8080
|
+
);
|
|
8081
|
+
const actionData = applicability.state === "needs_manual_review" || applicability.state === "needs_routing_review" ? {} : contextDataFor(
|
|
8082
|
+
options.workflow,
|
|
8083
|
+
repoIndex,
|
|
8084
|
+
loadResult.config,
|
|
8085
|
+
impactSet
|
|
8086
|
+
);
|
|
7885
8087
|
return {
|
|
7886
8088
|
schemaVersion: "truthmark-workflow/v0",
|
|
7887
8089
|
workflow: options.workflow,
|
|
7888
8090
|
applicability,
|
|
7889
8091
|
actionContext: buildWorkflowActionContext(manifestEntry, actionData),
|
|
8092
|
+
workflowCard: workflowCardFor(
|
|
8093
|
+
options.workflow,
|
|
8094
|
+
applicability,
|
|
8095
|
+
diagnostics,
|
|
8096
|
+
impactSet
|
|
8097
|
+
),
|
|
7890
8098
|
changedFiles: impactSet?.changedFiles ?? [],
|
|
7891
8099
|
affectedRoutes: impactSet?.affectedRoutes ?? [],
|
|
7892
|
-
targetTruthDocs: applicability.state === "
|
|
8100
|
+
targetTruthDocs: applicability.state === "needs_routing_review" ? [] : impactSet?.affectedTruthDocs ?? [],
|
|
7893
8101
|
diagnostics,
|
|
7894
8102
|
checks: {
|
|
7895
|
-
|
|
8103
|
+
reviewChecklist: [...manifestEntry.reviewQuestions],
|
|
7896
8104
|
recommended: [...manifestEntry.positiveTriggers],
|
|
7897
8105
|
helpers: helperCommandsFor2(options.workflow),
|
|
7898
8106
|
affectedTests: impactSet?.affectedTests ?? []
|
|
@@ -7903,7 +8111,7 @@ var buildWorkflowState = async (cwd, options) => {
|
|
|
7903
8111
|
};
|
|
7904
8112
|
|
|
7905
8113
|
// src/cli/handlers.ts
|
|
7906
|
-
import
|
|
8114
|
+
import fs20 from "fs/promises";
|
|
7907
8115
|
|
|
7908
8116
|
// src/agents/workflow-helper-validation.ts
|
|
7909
8117
|
import { parse as parseYaml2 } from "yaml";
|
|
@@ -7965,7 +8173,6 @@ var validateEvidenceChecked = (text, errors, checks) => {
|
|
|
7965
8173
|
};
|
|
7966
8174
|
var validateHelperScriptEntries = (entries, requiredHelpers, errors, checks) => {
|
|
7967
8175
|
if (entries === void 0 || entries.length === 0) {
|
|
7968
|
-
errors.push("Helper scripts must include status for optional helpers");
|
|
7969
8176
|
return;
|
|
7970
8177
|
}
|
|
7971
8178
|
const statusPattern = /^(?:-\s*)?([a-z0-9-]+):\s*(?:ran,\s*passed|skipped,\s*\S.*)$/iu;
|
|
@@ -7985,7 +8192,7 @@ var validateHelperScriptEntries = (entries, requiredHelpers, errors, checks) =>
|
|
|
7985
8192
|
errors.push(`missing Helper scripts status: ${helperId}`);
|
|
7986
8193
|
}
|
|
7987
8194
|
}
|
|
7988
|
-
if (errors.length === 0) {
|
|
8195
|
+
if (errors.length === 0 && requiredHelpers.length > 0) {
|
|
7989
8196
|
checks.push(`Helper scripts statuses include ${requiredHelpers.join(", ")}`);
|
|
7990
8197
|
}
|
|
7991
8198
|
};
|
|
@@ -8014,6 +8221,7 @@ var validateTruthSyncReportText = (text) => {
|
|
|
8014
8221
|
["Changed code reviewed", report.changedCode],
|
|
8015
8222
|
["Ownership reviewed", report.ownershipReviewed],
|
|
8016
8223
|
["Truth docs updated", report.truthDocsUpdated],
|
|
8224
|
+
["Decision/rationale captured", report.decisionRationaleCaptured],
|
|
8017
8225
|
["Notes", report.notes]
|
|
8018
8226
|
]) {
|
|
8019
8227
|
if (items.length === 0) {
|
|
@@ -8029,12 +8237,10 @@ var validateTruthSyncReportText = (text) => {
|
|
|
8029
8237
|
"Evidence checked entries include Claim:, Evidence:, and Result: supported | narrowed | removed | blocked"
|
|
8030
8238
|
);
|
|
8031
8239
|
}
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
checks
|
|
8037
|
-
);
|
|
8240
|
+
if (report.syncIntent !== void 0) {
|
|
8241
|
+
checks.push("Sync Intent");
|
|
8242
|
+
}
|
|
8243
|
+
validateHelperScriptEntries(report.helperScripts, [], errors, checks);
|
|
8038
8244
|
} catch (error) {
|
|
8039
8245
|
errors.push(error instanceof Error ? error.message : "invalid Truth Sync report");
|
|
8040
8246
|
}
|
|
@@ -8069,11 +8275,6 @@ var validateTruthDocumentReportText = (text) => {
|
|
|
8069
8275
|
} else {
|
|
8070
8276
|
errors.push("missing required section: Evidence checked");
|
|
8071
8277
|
}
|
|
8072
|
-
if (hasLabel(text, "Helper scripts")) {
|
|
8073
|
-
checks.push("Helper scripts");
|
|
8074
|
-
} else {
|
|
8075
|
-
errors.push("missing required section: Helper scripts");
|
|
8076
|
-
}
|
|
8077
8278
|
const truthDocsUpdated = getSection(text, "Truth docs updated");
|
|
8078
8279
|
const truthDocsCreated = getSection(text, "Truth docs created");
|
|
8079
8280
|
if (truthDocsUpdated === null && truthDocsCreated === null) {
|
|
@@ -8086,7 +8287,7 @@ var validateTruthDocumentReportText = (text) => {
|
|
|
8086
8287
|
checks.push("Truth docs updated or created");
|
|
8087
8288
|
}
|
|
8088
8289
|
validateEvidenceChecked(text, errors, checks);
|
|
8089
|
-
validateHelperScripts(text, [
|
|
8290
|
+
validateHelperScripts(text, [], errors, checks);
|
|
8090
8291
|
} else if (status === "blocked") {
|
|
8091
8292
|
requireBulletSection(text, "Reason", errors, checks);
|
|
8092
8293
|
}
|
|
@@ -8284,7 +8485,7 @@ var invalidWorkflowResult = (command, workflow) => ({
|
|
|
8284
8485
|
});
|
|
8285
8486
|
var readHelperFile = async (filePath, helper) => {
|
|
8286
8487
|
try {
|
|
8287
|
-
return await
|
|
8488
|
+
return await fs20.readFile(filePath, "utf8");
|
|
8288
8489
|
} catch (error) {
|
|
8289
8490
|
const message = error instanceof Error ? error.message : String(error);
|
|
8290
8491
|
return { ok: false, helper, errors: [`could not read file: ${message}`] };
|