mirror of
https://github.com/merlinhu1/truthmark.git
synced 2026-08-25 07:53:25 +02:00
fix(cli): fail on error diagnostics (#11)
* fix(cli): fail on error diagnostics * chore: release 1.6.1 * feat(init): refresh truth doc templates * fix(init): preserve custom template preambles * ci: limit GitHub token permissions * ci: update setup-node action --------- Co-authored-by: MerlinH <merlinh221@gmail.com>
This commit is contained in:
@@ -38,13 +38,21 @@ type ContextOptions = OutputOptions & {
|
||||
format?: string;
|
||||
};
|
||||
|
||||
const markFailedWhenErrorDiagnosticsExist = (result: CommandResult): void => {
|
||||
if (result.diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
||||
process.exitCode = 1;
|
||||
}
|
||||
};
|
||||
|
||||
const writeResult = (result: CommandResult, options: OutputOptions): void => {
|
||||
const output = options.json ? renderJson(result) : renderHuman(result);
|
||||
process.stdout.write(`${output}\n`);
|
||||
markFailedWhenErrorDiagnosticsExist(result);
|
||||
};
|
||||
const writeContextResult = (result: CommandResult, options: ContextOptions): void => {
|
||||
if (!options.json && options.format === "markdown" && typeof result.data?.markdown === "string") {
|
||||
process.stdout.write(result.data.markdown);
|
||||
markFailedWhenErrorDiagnosticsExist(result);
|
||||
return;
|
||||
}
|
||||
writeResult(result, options);
|
||||
|
||||
+41
-7
@@ -3,7 +3,7 @@ import fg from "fast-glob";
|
||||
import { DEFAULT_DOCS_HIERARCHY } from "../config/defaults.js";
|
||||
import type { TruthmarkConfig } from "../config/schema.js";
|
||||
import type { FileWriteResult } from "../fs/paths.js";
|
||||
import { ensureRepoFile, resolveRepoPath } from "../fs/paths.js";
|
||||
import { ensureRepoFile, resolveRepoPath, writeRepoFile } from "../fs/paths.js";
|
||||
import type { Diagnostic } from "../output/diagnostic.js";
|
||||
import { parseAreasMarkdown } from "../routing/areas.js";
|
||||
import { resolveTruthDocsRoot } from "../truth/docs.js";
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
TEST_BEHAVIOR_DOC_TEMPLATE_PATH,
|
||||
WORKFLOW_DOC_TEMPLATE_PATH,
|
||||
renderChildAreaTemplate,
|
||||
mergeTruthDocTemplate,
|
||||
renderArchitectureDocTemplateFile,
|
||||
renderBehaviorDocTemplateFile,
|
||||
renderContractDocTemplateFile,
|
||||
@@ -70,6 +71,23 @@ const readBehaviorDocTemplate = async (rootDir: string): Promise<string> => {
|
||||
}
|
||||
};
|
||||
|
||||
const ensureOrUpdateTruthDocTemplate = async (
|
||||
rootDir: string,
|
||||
templatePath: string,
|
||||
defaultTemplate: string,
|
||||
): Promise<FileWriteResult> => {
|
||||
const seededResult = await ensureRepoFile(rootDir, templatePath, defaultTemplate);
|
||||
|
||||
if (seededResult.status !== "unchanged") {
|
||||
return seededResult;
|
||||
}
|
||||
|
||||
const existingTemplate = await fs.readFile(resolveRepoPath(rootDir, templatePath), "utf8");
|
||||
const mergedTemplate = mergeTruthDocTemplate(existingTemplate, defaultTemplate);
|
||||
|
||||
return writeRepoFile(rootDir, templatePath, mergedTemplate);
|
||||
};
|
||||
|
||||
export const scaffoldHierarchy = async (
|
||||
rootDir: string,
|
||||
config: TruthmarkConfig,
|
||||
@@ -106,26 +124,42 @@ export const scaffoldHierarchy = async (
|
||||
),
|
||||
);
|
||||
results.push(
|
||||
await ensureRepoFile(rootDir, BEHAVIOR_DOC_TEMPLATE_PATH, renderBehaviorDocTemplateFile()),
|
||||
await ensureOrUpdateTruthDocTemplate(
|
||||
rootDir,
|
||||
BEHAVIOR_DOC_TEMPLATE_PATH,
|
||||
renderBehaviorDocTemplateFile(),
|
||||
),
|
||||
);
|
||||
results.push(
|
||||
await ensureRepoFile(rootDir, CONTRACT_DOC_TEMPLATE_PATH, renderContractDocTemplateFile()),
|
||||
await ensureOrUpdateTruthDocTemplate(
|
||||
rootDir,
|
||||
CONTRACT_DOC_TEMPLATE_PATH,
|
||||
renderContractDocTemplateFile(),
|
||||
),
|
||||
);
|
||||
results.push(
|
||||
await ensureRepoFile(
|
||||
await ensureOrUpdateTruthDocTemplate(
|
||||
rootDir,
|
||||
ARCHITECTURE_DOC_TEMPLATE_PATH,
|
||||
renderArchitectureDocTemplateFile(),
|
||||
),
|
||||
);
|
||||
results.push(
|
||||
await ensureRepoFile(rootDir, WORKFLOW_DOC_TEMPLATE_PATH, renderWorkflowDocTemplateFile()),
|
||||
await ensureOrUpdateTruthDocTemplate(
|
||||
rootDir,
|
||||
WORKFLOW_DOC_TEMPLATE_PATH,
|
||||
renderWorkflowDocTemplateFile(),
|
||||
),
|
||||
);
|
||||
results.push(
|
||||
await ensureRepoFile(rootDir, OPERATIONS_DOC_TEMPLATE_PATH, renderOperationsDocTemplateFile()),
|
||||
await ensureOrUpdateTruthDocTemplate(
|
||||
rootDir,
|
||||
OPERATIONS_DOC_TEMPLATE_PATH,
|
||||
renderOperationsDocTemplateFile(),
|
||||
),
|
||||
);
|
||||
results.push(
|
||||
await ensureRepoFile(
|
||||
await ensureOrUpdateTruthDocTemplate(
|
||||
rootDir,
|
||||
TEST_BEHAVIOR_DOC_TEMPLATE_PATH,
|
||||
renderTestBehaviorDocTemplateFile(),
|
||||
|
||||
+347
-87
@@ -212,6 +212,144 @@ export const WORKFLOW_DOC_TEMPLATE_PATH = "docs/templates/workflow-doc.md";
|
||||
export const OPERATIONS_DOC_TEMPLATE_PATH = "docs/templates/operations-doc.md";
|
||||
export const TEST_BEHAVIOR_DOC_TEMPLATE_PATH = "docs/templates/test-behavior-doc.md";
|
||||
|
||||
type TemplateSectionSpec = {
|
||||
heading: string;
|
||||
placeholder: string;
|
||||
guidance: string[];
|
||||
};
|
||||
|
||||
type ParsedTemplateSection = {
|
||||
heading: string;
|
||||
block: string;
|
||||
};
|
||||
|
||||
const renderTemplateSection = (section: TemplateSectionSpec): string[] => {
|
||||
return [
|
||||
section.heading,
|
||||
"",
|
||||
"<!--",
|
||||
...section.guidance,
|
||||
"-->",
|
||||
"",
|
||||
`{{${section.placeholder}}}`,
|
||||
"",
|
||||
];
|
||||
};
|
||||
|
||||
const titleToPlaceholder = (title: string): string => {
|
||||
return title
|
||||
.replace(/^#+\s+/u, "")
|
||||
.toLowerCase()
|
||||
.replaceAll(/[^a-z0-9]+/g, "_")
|
||||
.replace(/^_+|_+$/g, "");
|
||||
};
|
||||
|
||||
const findTemplateSectionHeadings = (template: string): Array<{ heading: string; index: number }> => {
|
||||
const matches: Array<{ heading: string; index: number }> = [];
|
||||
let fencedCodeMarker: "`" | "~" | null = null;
|
||||
let fencedCodeLength = 0;
|
||||
|
||||
for (const lineMatch of template.matchAll(/^.*(?:\r?\n|$)/gm)) {
|
||||
const rawLine = lineMatch[0];
|
||||
if (rawLine.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const line = rawLine.replace(/\r?\n$/u, "");
|
||||
const fenceMatch = /^(?: {0,3})(`{3,}|~{3,})/u.exec(line);
|
||||
|
||||
if (fenceMatch) {
|
||||
const marker = fenceMatch[1]?.[0] as "`" | "~";
|
||||
const length = fenceMatch[1]?.length ?? 0;
|
||||
|
||||
if (fencedCodeMarker === null) {
|
||||
fencedCodeMarker = marker;
|
||||
fencedCodeLength = length;
|
||||
} else if (marker === fencedCodeMarker && length >= fencedCodeLength) {
|
||||
fencedCodeMarker = null;
|
||||
fencedCodeLength = 0;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fencedCodeMarker === null && /^## .+$/u.test(line)) {
|
||||
matches.push({ heading: line.trim(), index: lineMatch.index });
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
const parseTemplateSections = (template: string): { preamble: string; sections: ParsedTemplateSection[] } => {
|
||||
const matches = findTemplateSectionHeadings(template);
|
||||
|
||||
if (matches.length === 0) {
|
||||
return { preamble: template.trimEnd(), sections: [] };
|
||||
}
|
||||
|
||||
const sections = matches.map((match, index) => {
|
||||
const start = match.index;
|
||||
const next = matches[index + 1];
|
||||
const end = next?.index ?? template.length;
|
||||
|
||||
return {
|
||||
heading: match.heading,
|
||||
block: template.slice(start, end).trimEnd(),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
preamble: template.slice(0, matches[0]?.index ?? 0).trimEnd(),
|
||||
sections,
|
||||
};
|
||||
};
|
||||
|
||||
export const mergeTruthDocTemplate = (existingTemplate: string, defaultTemplate: string): string => {
|
||||
if (existingTemplate.trim().length === 0) {
|
||||
return defaultTemplate;
|
||||
}
|
||||
|
||||
const defaultParsed = parseTemplateSections(defaultTemplate);
|
||||
const existingParsed = parseTemplateSections(existingTemplate);
|
||||
const defaultHeadings = new Set(defaultParsed.sections.map((section) => section.heading));
|
||||
const customBeforeDefault = new Map<string, ParsedTemplateSection[]>();
|
||||
const trailingCustomSections: ParsedTemplateSection[] = [];
|
||||
|
||||
existingParsed.sections.forEach((section, index) => {
|
||||
if (defaultHeadings.has(section.heading)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextDefaultSection = existingParsed.sections
|
||||
.slice(index + 1)
|
||||
.find((candidate) => defaultHeadings.has(candidate.heading));
|
||||
|
||||
if (nextDefaultSection) {
|
||||
const bucket = customBeforeDefault.get(nextDefaultSection.heading) ?? [];
|
||||
bucket.push(section);
|
||||
customBeforeDefault.set(nextDefaultSection.heading, bucket);
|
||||
return;
|
||||
}
|
||||
|
||||
trailingCustomSections.push(section);
|
||||
});
|
||||
|
||||
const mergedSections = defaultParsed.sections.flatMap((section) => [
|
||||
...(customBeforeDefault.get(section.heading) ?? []),
|
||||
section,
|
||||
]);
|
||||
|
||||
return [
|
||||
existingParsed.preamble,
|
||||
...mergedSections.map((section) => section.block),
|
||||
...trailingCustomSections.map((section) => section.block),
|
||||
"",
|
||||
]
|
||||
.filter((block) => block.length > 0)
|
||||
.join("\n\n");
|
||||
};
|
||||
|
||||
export const renderBehaviorDocTemplateFile = (): string => {
|
||||
return [
|
||||
"---",
|
||||
@@ -227,92 +365,147 @@ export const renderBehaviorDocTemplateFile = (): string => {
|
||||
"",
|
||||
"## Purpose",
|
||||
"",
|
||||
"<!-- State why this feature exists, the user or system outcome it protects, and the problem it solves. Keep roadmap or implementation plans out of this section. -->",
|
||||
"<!--",
|
||||
"State the user/system outcome this behavior protects and why it exists.",
|
||||
"Include the problem boundary and durable value; exclude roadmap, implementation plan, and historical narrative.",
|
||||
"List the code, config, docs, or tests that support the claim in source_of_truth rather than prose-only assertion.",
|
||||
"-->",
|
||||
"",
|
||||
"{{purpose}}",
|
||||
"",
|
||||
"## Scope",
|
||||
"",
|
||||
"{{scope}}",
|
||||
"",
|
||||
"<!--",
|
||||
"This doc must own one coherent behavior surface.",
|
||||
"Split into another leaf doc when content introduces:",
|
||||
"- a distinct user or system outcome",
|
||||
"- a separate lifecycle or state machine",
|
||||
"- an unrelated rule family",
|
||||
"- a different external contract",
|
||||
"- code that should route through a different owner",
|
||||
"Define the one coherent behavior surface this document owns.",
|
||||
"Include in-scope actors, entrypoints, state/data owned by this doc, and explicit handoffs to neighboring truth docs.",
|
||||
"Split into another leaf doc when content introduces a distinct outcome, state machine, rule family, external contract, or route owner.",
|
||||
"Keep README.md files as indexes only.",
|
||||
"-->",
|
||||
"",
|
||||
"{{scope}}",
|
||||
"",
|
||||
"This doc was created from the editable behavior-doc template at {{template_path}}.",
|
||||
"",
|
||||
"## Current Behavior",
|
||||
"",
|
||||
"<!-- Describe implemented behavior in present tense. Do not include desired future behavior. -->",
|
||||
"<!--",
|
||||
"Describe only current implemented behavior in present tense.",
|
||||
"Cover observable behavior, important defaults, and user/system-visible effects; exclude desired future behavior and speculative design.",
|
||||
"Every non-obvious claim should be checkable from source_of_truth evidence.",
|
||||
"-->",
|
||||
"",
|
||||
"{{current_behavior}}",
|
||||
"",
|
||||
"## Core Rules",
|
||||
"",
|
||||
"<!-- Capture stable business rules, invariants, precedence rules, validation rules, and must-never constraints. Omit incidental implementation details. -->",
|
||||
"<!--",
|
||||
"Capture stable business rules, invariants, precedence rules, validation rules, and must-never constraints.",
|
||||
"Separate rules from incidental implementation details; cite current implementation or tests for rule enforcement.",
|
||||
"-->",
|
||||
"",
|
||||
"{{core_rules}}",
|
||||
"",
|
||||
"## Flows And States",
|
||||
"",
|
||||
"<!-- Use for route switches, state transitions, lifecycle stages, retries, fallbacks, and important error paths. Write 'None beyond current behavior.' when no distinct flow or state model exists. -->",
|
||||
"<!--",
|
||||
"Document state transitions, lifecycle stages, retries, fallbacks, route switches, and important error paths.",
|
||||
"State 'None beyond current behavior.' when this behavior has no distinct flow or state model.",
|
||||
"-->",
|
||||
"",
|
||||
"{{flows_and_states}}",
|
||||
"",
|
||||
"## Contracts",
|
||||
"",
|
||||
"<!-- Capture user-visible or integration contracts: CLI/API shape, inputs, outputs, diagnostics, files, events, permissions, or links to canonical contract docs. Avoid duplicating a separate canonical contract doc. -->",
|
||||
"<!--",
|
||||
"Capture user-visible or integration contracts: CLI/API shape, inputs, outputs, diagnostics, files, events, permissions, or links to canonical contract docs.",
|
||||
"Avoid duplicating a separate canonical contract doc; link to it when contract ownership lives elsewhere.",
|
||||
"-->",
|
||||
"",
|
||||
"{{contracts}}",
|
||||
"",
|
||||
"## Product Decisions",
|
||||
"",
|
||||
"<!-- Keep active decisions only. Replace stale decisions instead of appending historical logs. -->",
|
||||
"<!--",
|
||||
"Keep active decisions only, dated inline when added or changed.",
|
||||
"Explain decisions that shape behavior, boundaries, rejected alternatives, or migration constraints; replace stale decisions instead of appending historical logs.",
|
||||
"-->",
|
||||
"",
|
||||
"{{decision}}",
|
||||
"",
|
||||
"## Rationale",
|
||||
"",
|
||||
"<!-- Explain why the current behavior and active decisions are this way, including tradeoffs. -->",
|
||||
"<!--",
|
||||
"Explain why the current behavior and active decisions are this way, including tradeoffs and constraints.",
|
||||
"Tie rationale to evidence-backed behavior; do not use this as a changelog.",
|
||||
"-->",
|
||||
"",
|
||||
"{{rationale}}",
|
||||
"",
|
||||
"## Non-Goals",
|
||||
"",
|
||||
"<!-- Name adjacent behavior this doc intentionally does not own, especially tempting future expansions. -->",
|
||||
"<!--",
|
||||
"Name adjacent behavior this doc intentionally does not own, especially tempting future expansions or neighboring route owners.",
|
||||
"Use this section to prevent scope creep and duplicate truth ownership.",
|
||||
"-->",
|
||||
"",
|
||||
"{{non_goals}}",
|
||||
"",
|
||||
"## Maintenance Notes",
|
||||
"",
|
||||
"<!-- List related tests, routing cautions, migration notes, and common drift risks for future agents. Keep this operational, not historical. -->",
|
||||
"<!--",
|
||||
"List related tests, routing cautions, migration notes, evidence drift risks, and review triggers for future maintainers or agents.",
|
||||
"Keep this operational and current-state focused, not historical.",
|
||||
"-->",
|
||||
"",
|
||||
"{{maintenance_notes}}",
|
||||
"",
|
||||
].join("\n");
|
||||
};
|
||||
|
||||
const sectionSpec = (
|
||||
heading: string,
|
||||
guidance: string[],
|
||||
placeholder = titleToPlaceholder(heading),
|
||||
): TemplateSectionSpec => ({ heading, guidance, placeholder });
|
||||
|
||||
const PURPOSE_SECTION = sectionSpec("## Purpose", [
|
||||
"State the software-engineering outcome this document protects and why the documented surface exists.",
|
||||
"Include durable value, impacted users/systems, and the problem boundary; exclude roadmap, implementation plans, and historical narrative.",
|
||||
"Keep claims traceable to source_of_truth evidence rather than prose-only assertion.",
|
||||
]);
|
||||
|
||||
const SCOPE_SECTION = sectionSpec("## Scope", [
|
||||
"Define the one coherent surface this document owns, including actors, entrypoints, owned state/data, and handoffs to neighboring truth docs.",
|
||||
"Call out important out-of-scope boundaries here or in Non-Goals; split the doc when it mixes distinct outcomes, lifecycles, contracts, or owners.",
|
||||
]);
|
||||
|
||||
const PRODUCT_DECISIONS_SECTION = sectionSpec("## Product Decisions", [
|
||||
"Keep active decisions only, dated inline when added or changed.",
|
||||
"Capture decisions that shape behavior, interfaces, boundaries, compatibility, risk acceptance, or migration constraints.",
|
||||
"Replace stale decisions instead of appending historical logs.",
|
||||
], "decision");
|
||||
|
||||
const RATIONALE_SECTION = sectionSpec("## Rationale", [
|
||||
"Explain why the current behavior, structure, or contract is this way, including tradeoffs and constraints.",
|
||||
"Tie rationale to evidence-backed facts and active decisions; do not use this as a changelog.",
|
||||
]);
|
||||
|
||||
const NON_GOALS_SECTION = sectionSpec("## Non-Goals", [
|
||||
"Name adjacent behavior, responsibilities, interfaces, or future expansions this doc intentionally does not own.",
|
||||
"Use this section to prevent scope creep and duplicate truth ownership.",
|
||||
]);
|
||||
|
||||
const MAINTENANCE_NOTES_SECTION = sectionSpec("## Maintenance Notes", [
|
||||
"List related tests, routing cautions, migration notes, compatibility risks, evidence drift risks, and review triggers for future maintainers or agents.",
|
||||
"Keep this operational and current-state focused, not historical.",
|
||||
]);
|
||||
|
||||
const renderTypedTruthDocTemplate = (
|
||||
truthKind: string,
|
||||
docType: string,
|
||||
title: string,
|
||||
sections: string[],
|
||||
sections: TemplateSectionSpec[],
|
||||
): string => {
|
||||
const placeholderNameForSection = (section: string): string => {
|
||||
return section
|
||||
.replace(/^#+\s+/u, "")
|
||||
.toLowerCase()
|
||||
.replaceAll(/[^a-z0-9]+/g, "_")
|
||||
.replace(/^_+|_+$/g, "");
|
||||
};
|
||||
|
||||
return [
|
||||
"---",
|
||||
"status: active",
|
||||
@@ -325,91 +518,158 @@ const renderTypedTruthDocTemplate = (
|
||||
"",
|
||||
`# ${title}`,
|
||||
"",
|
||||
"## Purpose",
|
||||
"",
|
||||
"{{purpose}}",
|
||||
"",
|
||||
"## Scope",
|
||||
"",
|
||||
"{{scope}}",
|
||||
"",
|
||||
...sections.flatMap((section) => [
|
||||
section,
|
||||
"",
|
||||
`{{${placeholderNameForSection(section)}}}`,
|
||||
"",
|
||||
]),
|
||||
"## Product Decisions",
|
||||
"",
|
||||
"{{decision}}",
|
||||
"",
|
||||
"## Rationale",
|
||||
"",
|
||||
"{{rationale}}",
|
||||
"",
|
||||
"## Non-Goals",
|
||||
"",
|
||||
"{{non_goals}}",
|
||||
"",
|
||||
"## Maintenance Notes",
|
||||
"",
|
||||
"{{maintenance_notes}}",
|
||||
"",
|
||||
...renderTemplateSection(PURPOSE_SECTION),
|
||||
...renderTemplateSection(SCOPE_SECTION),
|
||||
...sections.flatMap(renderTemplateSection),
|
||||
...renderTemplateSection(PRODUCT_DECISIONS_SECTION),
|
||||
...renderTemplateSection(RATIONALE_SECTION),
|
||||
...renderTemplateSection(NON_GOALS_SECTION),
|
||||
...renderTemplateSection(MAINTENANCE_NOTES_SECTION),
|
||||
].join("\n");
|
||||
};
|
||||
|
||||
export const renderContractDocTemplateFile = (): string => {
|
||||
return renderTypedTruthDocTemplate("contract", "contract", "{{title}}", [
|
||||
"## Contract Surface",
|
||||
"## Inputs",
|
||||
"## Outputs",
|
||||
"## Errors And Diagnostics",
|
||||
"## Compatibility Rules",
|
||||
"## Versioning And Migration",
|
||||
sectionSpec("## Contract Surface", [
|
||||
"Identify the owned API, CLI, file format, event, protocol, permission boundary, or integration surface.",
|
||||
"State consumers/producers, stability level, and the source files/tests that define the contract.",
|
||||
]),
|
||||
sectionSpec("## Inputs", [
|
||||
"Document accepted parameters, payloads, files, environment/config keys, permissions, and validation rules.",
|
||||
"Include required/optional status, defaults, constraints, and normalization behavior.",
|
||||
]),
|
||||
sectionSpec("## Outputs", [
|
||||
"Document returned values, emitted files/events, state changes, side effects, and success diagnostics.",
|
||||
"Make externally observable behavior explicit enough for compatibility review.",
|
||||
]),
|
||||
sectionSpec("## Errors And Diagnostics", [
|
||||
"List error classes, exit/status codes, user-facing diagnostics, retries, and recoverability expectations.",
|
||||
"Distinguish validation errors, dependency failures, authorization failures, and internal faults when applicable.",
|
||||
]),
|
||||
sectionSpec("## Compatibility Rules", [
|
||||
"State backward/forward compatibility guarantees, tolerated inputs, deprecation rules, and breaking-change triggers.",
|
||||
"Include compatibility tests or review gates that protect the contract.",
|
||||
]),
|
||||
sectionSpec("## Versioning And Migration", [
|
||||
"Document version negotiation, schema/API version fields, rollout requirements, migration steps, and rollback expectations.",
|
||||
"State 'Not versioned' only when the implementation truly has no versioning or migration surface.",
|
||||
]),
|
||||
]);
|
||||
};
|
||||
|
||||
export const renderArchitectureDocTemplateFile = (): string => {
|
||||
return renderTypedTruthDocTemplate("architecture", "architecture", "{{title}}", [
|
||||
"## System Role",
|
||||
"## Boundaries",
|
||||
"## Components",
|
||||
"## Data And Control Flow",
|
||||
"## Ownership",
|
||||
"## Cross-Cutting Constraints",
|
||||
sectionSpec("## System Role", [
|
||||
"Describe the current architectural role of this subsystem/component in the larger system.",
|
||||
"State the primary responsibilities, consumers, providers, and why this boundary exists now.",
|
||||
]),
|
||||
sectionSpec("## Boundaries", [
|
||||
"Define owned code/config/data, external dependencies, trust boundaries, and interfaces crossed by this architecture.",
|
||||
"Name what is deliberately outside the boundary and link neighboring architecture or contract docs when they own it.",
|
||||
]),
|
||||
sectionSpec("## Components", [
|
||||
"List the major runtime/build-time components, modules, services, jobs, or generated artifacts and their responsibilities.",
|
||||
"Keep the component list current and evidence-backed; avoid speculative target architecture.",
|
||||
]),
|
||||
sectionSpec("## Data And Control Flow", [
|
||||
"Describe important data movement, command/control paths, synchronization points, state ownership, and failure paths.",
|
||||
"Call out persistence, queues, caches, external calls, and security-sensitive transitions where relevant.",
|
||||
]),
|
||||
sectionSpec("## Ownership", [
|
||||
"Document team/module ownership, review responsibility, operational responsibility, and escalation paths if known.",
|
||||
"If ownership is inferred from codeowners, config, or repository structure, cite that evidence.",
|
||||
]),
|
||||
sectionSpec("## Cross-Cutting Constraints", [
|
||||
"Record active constraints such as security, privacy, reliability, performance, portability, maintainability, compliance, and cost.",
|
||||
"Tie constraints to source evidence, tests, standards, or operational requirements where available.",
|
||||
]),
|
||||
]);
|
||||
};
|
||||
|
||||
export const renderWorkflowDocTemplateFile = (): string => {
|
||||
return renderTypedTruthDocTemplate("workflow", "behavior", "{{title}}", [
|
||||
"## Triggers",
|
||||
"## Inputs",
|
||||
"## Execution Model",
|
||||
"## Steps",
|
||||
"## State, Retry, And Failure Behavior",
|
||||
"## Outputs",
|
||||
sectionSpec("## Triggers", [
|
||||
"List events, commands, schedules, user actions, webhooks, or dependency signals that start this workflow.",
|
||||
"Include preconditions, authorization requirements, debounce/coalescing behavior, and disabled states when applicable.",
|
||||
]),
|
||||
sectionSpec("## Inputs", [
|
||||
"Document data, files, config, context, credentials, and environmental assumptions consumed by the workflow.",
|
||||
"Include validation, defaults, and normalization that happen before execution.",
|
||||
]),
|
||||
sectionSpec("## Execution Model", [
|
||||
"Describe synchronous/asynchronous execution, concurrency, locking, leases, batching, ordering, and idempotency behavior.",
|
||||
"State whether the workflow is user-blocking, background, distributed, or delegated to another system.",
|
||||
]),
|
||||
sectionSpec("## Steps", [
|
||||
"Capture the current ordered steps or phases at a level useful for maintenance and review.",
|
||||
"Reference implementation entrypoints instead of duplicating line-by-line code behavior.",
|
||||
]),
|
||||
sectionSpec("## State, Retry, And Failure Behavior", [
|
||||
"Document state transitions, retries, timeouts, compensation, fallback, partial-success, and terminal-failure behavior.",
|
||||
"Make externally visible failure semantics and recovery responsibilities clear.",
|
||||
]),
|
||||
sectionSpec("## Outputs", [
|
||||
"List artifacts, state changes, notifications, logs, metrics, diagnostics, and downstream triggers produced by the workflow.",
|
||||
"Include success criteria and handoff points to other truth docs or systems.",
|
||||
]),
|
||||
]);
|
||||
};
|
||||
|
||||
export const renderOperationsDocTemplateFile = (): string => {
|
||||
return renderTypedTruthDocTemplate("operations", "behavior", "{{title}}", [
|
||||
"## Operational Surface",
|
||||
"## Runtime Topology",
|
||||
"## Configuration",
|
||||
"## Permissions",
|
||||
"## Deployment And Rollback",
|
||||
"## Availability And Observability",
|
||||
sectionSpec("## Operational Surface", [
|
||||
"Describe what operators, maintainers, or automated systems can observe or control for this surface.",
|
||||
"Include commands, dashboards, alerts, runbooks, jobs, or operational APIs that define current operations.",
|
||||
]),
|
||||
sectionSpec("## Runtime Topology", [
|
||||
"Document services, processes, containers, hosts, regions, dependencies, queues, stores, and network boundaries involved at runtime.",
|
||||
"State single-node/local behavior explicitly when there is no distributed topology.",
|
||||
]),
|
||||
sectionSpec("## Configuration", [
|
||||
"List operational config, environment variables, feature flags, secrets references, defaults, and reload/restart requirements.",
|
||||
"Do not include secret values; describe storage and rotation expectations instead.",
|
||||
]),
|
||||
sectionSpec("## Permissions", [
|
||||
"Document required identities, roles, scopes, filesystem/network permissions, and least-privilege boundaries.",
|
||||
"Include user-facing authorization behavior and operator access requirements when relevant.",
|
||||
]),
|
||||
sectionSpec("## Deployment And Rollback", [
|
||||
"Describe deployment mechanism, migration ordering, compatibility windows, rollback path, and known irreversible operations.",
|
||||
"Call out manual gates, smoke checks, and post-deploy verification responsibilities.",
|
||||
]),
|
||||
sectionSpec("## Availability And Observability", [
|
||||
"Capture availability expectations, health checks, metrics, logs, traces, alerts, SLO/error-budget signals, and known blind spots.",
|
||||
"Include what maintainers should inspect first during incidents or degraded behavior.",
|
||||
]),
|
||||
]);
|
||||
};
|
||||
|
||||
export const renderTestBehaviorDocTemplateFile = (): string => {
|
||||
return renderTypedTruthDocTemplate("test-behavior", "behavior", "{{title}}", [
|
||||
"## Test Surface",
|
||||
"## Fixtures And Data Model",
|
||||
"## Execution Model",
|
||||
"## Assertions And Invariants",
|
||||
"## Isolation Rules",
|
||||
"## Reporting And Failure Semantics",
|
||||
sectionSpec("## Test Surface", [
|
||||
"Define the behavior, contract, architecture, or workflow surface these tests verify.",
|
||||
"Link the canonical truth docs and code paths the tests are meant to protect.",
|
||||
]),
|
||||
sectionSpec("## Fixtures And Data Model", [
|
||||
"Document fixtures, factories, seeds, mocks/fakes, test repositories, external-service substitutes, and data lifecycle rules.",
|
||||
"Include cleanup, determinism, privacy, and cross-test contamination constraints.",
|
||||
]),
|
||||
sectionSpec("## Execution Model", [
|
||||
"Describe how tests run: command, framework, parallelism, isolation, network/filesystem assumptions, and required services.",
|
||||
"State whether tests are unit, integration, e2e, contract, smoke, regression, or generated checks.",
|
||||
]),
|
||||
sectionSpec("## Assertions And Invariants", [
|
||||
"List the critical assertions, invariants, failure modes, and negative cases that make the tests meaningful.",
|
||||
"Tie assertions to product/contract rules rather than incidental implementation details.",
|
||||
]),
|
||||
sectionSpec("## Isolation Rules", [
|
||||
"Document transaction boundaries, temp directories, fake clocks, network blocking, shared resources, and teardown rules.",
|
||||
"Call out known order dependencies or flake risks and how they are controlled.",
|
||||
]),
|
||||
sectionSpec("## Reporting And Failure Semantics", [
|
||||
"Describe diagnostics, snapshots, logs, coverage signals, retry policy, and how maintainers should interpret failures.",
|
||||
"Include escalation or quarantine criteria for flaky or environment-sensitive tests.",
|
||||
]),
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user