@tracecart/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +149 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/delta.d.ts +1 -0
  7. package/dist/commands/delta.js +19 -0
  8. package/dist/commands/delta.js.map +1 -0
  9. package/dist/commands/extract-prompt.d.ts +2 -0
  10. package/dist/commands/extract-prompt.js +35 -0
  11. package/dist/commands/extract-prompt.js.map +1 -0
  12. package/dist/commands/extract-validate.d.ts +2 -0
  13. package/dist/commands/extract-validate.js +21 -0
  14. package/dist/commands/extract-validate.js.map +1 -0
  15. package/dist/commands/finalize.d.ts +1 -0
  16. package/dist/commands/finalize.js +64 -0
  17. package/dist/commands/finalize.js.map +1 -0
  18. package/dist/commands/init.d.ts +1 -0
  19. package/dist/commands/init.js +33 -0
  20. package/dist/commands/init.js.map +1 -0
  21. package/dist/commands/match-prompt.d.ts +2 -0
  22. package/dist/commands/match-prompt.js +51 -0
  23. package/dist/commands/match-prompt.js.map +1 -0
  24. package/dist/commands/match-validate.d.ts +2 -0
  25. package/dist/commands/match-validate.js +15 -0
  26. package/dist/commands/match-validate.js.map +1 -0
  27. package/dist/commands/presets.d.ts +1 -0
  28. package/dist/commands/presets.js +14 -0
  29. package/dist/commands/presets.js.map +1 -0
  30. package/dist/commands/remainder.d.ts +1 -0
  31. package/dist/commands/remainder.js +19 -0
  32. package/dist/commands/remainder.js.map +1 -0
  33. package/dist/commands/reverse-extract-validate.d.ts +1 -0
  34. package/dist/commands/reverse-extract-validate.js +20 -0
  35. package/dist/commands/reverse-extract-validate.js.map +1 -0
  36. package/dist/commands/reverse-match-prompt.d.ts +2 -0
  37. package/dist/commands/reverse-match-prompt.js +50 -0
  38. package/dist/commands/reverse-match-prompt.js.map +1 -0
  39. package/dist/commands/reverse-match-validate.d.ts +1 -0
  40. package/dist/commands/reverse-match-validate.js +14 -0
  41. package/dist/commands/reverse-match-validate.js.map +1 -0
  42. package/dist/commands/split.d.ts +1 -0
  43. package/dist/commands/split.js +26 -0
  44. package/dist/commands/split.js.map +1 -0
  45. package/dist/commands/status.d.ts +1 -0
  46. package/dist/commands/status.js +34 -0
  47. package/dist/commands/status.js.map +1 -0
  48. package/dist/commands/update.d.ts +1 -0
  49. package/dist/commands/update.js +5 -0
  50. package/dist/commands/update.js.map +1 -0
  51. package/dist/extract/extract.d.ts +37 -0
  52. package/dist/extract/extract.js +158 -0
  53. package/dist/extract/extract.js.map +1 -0
  54. package/dist/extract/remainder.d.ts +16 -0
  55. package/dist/extract/remainder.js +34 -0
  56. package/dist/extract/remainder.js.map +1 -0
  57. package/dist/match/coverage.d.ts +64 -0
  58. package/dist/match/coverage.js +375 -0
  59. package/dist/match/coverage.js.map +1 -0
  60. package/dist/output/delta.d.ts +46 -0
  61. package/dist/output/delta.js +155 -0
  62. package/dist/output/delta.js.map +1 -0
  63. package/dist/output/trace-map.d.ts +89 -0
  64. package/dist/output/trace-map.js +135 -0
  65. package/dist/output/trace-map.js.map +1 -0
  66. package/dist/parse/clause-split.d.ts +20 -0
  67. package/dist/parse/clause-split.js +185 -0
  68. package/dist/parse/clause-split.js.map +1 -0
  69. package/dist/parse/discover-inputs.d.ts +16 -0
  70. package/dist/parse/discover-inputs.js +97 -0
  71. package/dist/parse/discover-inputs.js.map +1 -0
  72. package/dist/parse/parse-document.d.ts +28 -0
  73. package/dist/parse/parse-document.js +141 -0
  74. package/dist/parse/parse-document.js.map +1 -0
  75. package/dist/preset.d.ts +18 -0
  76. package/dist/preset.js +85 -0
  77. package/dist/preset.js.map +1 -0
  78. package/package.json +58 -0
  79. package/presets/spec-coverage.json +15 -0
  80. package/prompts/coverage_check.txt +38 -0
  81. package/prompts/extract.txt +40 -0
  82. package/prompts/reverse_check.txt +36 -0
  83. package/templates/claude/commands/tracecart.md +217 -0
@@ -0,0 +1,141 @@
1
+ import * as fs from "node:fs";
2
+ export function findChapterBoundaries(lines) {
3
+ const pattern = /^(\d+)\\?\.\s+(.+)$/;
4
+ const allHeaders = [];
5
+ for (let i = 0; i < lines.length; i++) {
6
+ const m = pattern.exec(lines[i].trim());
7
+ if (m) {
8
+ allHeaders.push({
9
+ number: parseInt(m[1], 10),
10
+ title: m[2].trim(),
11
+ line: i,
12
+ });
13
+ }
14
+ }
15
+ if (allHeaders.length === 0)
16
+ return [];
17
+ const blocks = [];
18
+ let currentBlock = [allHeaders[0]];
19
+ for (let i = 1; i < allHeaders.length; i++) {
20
+ const h = allHeaders[i];
21
+ const prev = currentBlock[currentBlock.length - 1];
22
+ if (h.number <= 1 && prev.number > 5) {
23
+ blocks.push(currentBlock);
24
+ currentBlock = [h];
25
+ }
26
+ else {
27
+ currentBlock.push(h);
28
+ }
29
+ }
30
+ blocks.push(currentBlock);
31
+ let mainBlock = null;
32
+ for (const block of blocks) {
33
+ if (block.length < 3)
34
+ continue;
35
+ const avgGap = (block[block.length - 1].line - block[0].line) / block.length;
36
+ if (avgGap > 10) {
37
+ mainBlock = block;
38
+ break;
39
+ }
40
+ }
41
+ if (mainBlock === null) {
42
+ mainBlock = blocks.reduce((best, b) => b[b.length - 1].line - b[0].line > best[best.length - 1].line - best[0].line
43
+ ? b
44
+ : best);
45
+ }
46
+ const chapters = [];
47
+ for (let i = 0; i < mainBlock.length; i++) {
48
+ const h = mainBlock[i];
49
+ let endLine;
50
+ if (i + 1 < mainBlock.length) {
51
+ endLine = mainBlock[i + 1].line;
52
+ }
53
+ else {
54
+ endLine = lines.length;
55
+ for (const block of blocks) {
56
+ if (block[0].line > h.line && block !== mainBlock) {
57
+ endLine = block[0].line;
58
+ break;
59
+ }
60
+ }
61
+ }
62
+ chapters.push({
63
+ number: h.number,
64
+ title: h.title,
65
+ start_line: h.line,
66
+ end_line: endLine,
67
+ });
68
+ }
69
+ return chapters;
70
+ }
71
+ export function extractChapterText(lines, chapter) {
72
+ return lines.slice(chapter.start_line, chapter.end_line).join("\n");
73
+ }
74
+ export function countContentLines(text) {
75
+ let count = 0;
76
+ for (const line of text.split("\n")) {
77
+ const stripped = line.trim();
78
+ if (stripped && !stripped.startsWith("#") && !/^\d+\.\d+/.test(stripped)) {
79
+ count++;
80
+ }
81
+ }
82
+ return count;
83
+ }
84
+ export function parseSections(text, chapterNum) {
85
+ const pattern = new RegExp(`^${chapterNum}\\.(\\d+)\\s+(.+)$`);
86
+ const sections = [];
87
+ for (const line of text.split("\n")) {
88
+ const m = pattern.exec(line.trim());
89
+ if (m) {
90
+ sections.push({
91
+ number: `${chapterNum}.${m[1]}`,
92
+ title: m[2].trim(),
93
+ });
94
+ }
95
+ }
96
+ return sections;
97
+ }
98
+ function main() {
99
+ const args = process.argv.slice(2);
100
+ if (args.length === 0) {
101
+ process.stderr.write("Usage: parse-document.ts <document_file> [chapter_number]\n");
102
+ process.exit(1);
103
+ }
104
+ const specPath = args[0];
105
+ const targetChapter = args.length > 1 ? parseInt(args[1], 10) : null;
106
+ if (!fs.existsSync(specPath)) {
107
+ process.stderr.write(`Error: ${specPath} not found\n`);
108
+ process.exit(1);
109
+ }
110
+ const lines = fs.readFileSync(specPath, "utf-8").split("\n");
111
+ let chapters = findChapterBoundaries(lines);
112
+ if (targetChapter !== null) {
113
+ chapters = chapters.filter((c) => c.number === targetChapter);
114
+ if (chapters.length === 0) {
115
+ process.stderr.write(`Error: chapter ${targetChapter} not found\n`);
116
+ process.exit(1);
117
+ }
118
+ }
119
+ const result = [];
120
+ for (const ch of chapters) {
121
+ const text = extractChapterText(lines, ch);
122
+ const sections = parseSections(text, ch.number);
123
+ const contentLines = countContentLines(text);
124
+ result.push({
125
+ number: ch.number,
126
+ title: ch.title,
127
+ start_line: ch.start_line + 1,
128
+ end_line: ch.end_line,
129
+ sections,
130
+ content_lines: contentLines,
131
+ text,
132
+ });
133
+ }
134
+ process.stdout.write(JSON.stringify(result, null, 2));
135
+ }
136
+ const isMain = process.argv[1] &&
137
+ fs.realpathSync(process.argv[1]) === fs.realpathSync(import.meta.url.replace("file://", ""));
138
+ if (isMain) {
139
+ main();
140
+ }
141
+ //# sourceMappingURL=parse-document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-document.js","sourceRoot":"","sources":["../../src/parse/parse-document.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface Preset {
2
+ name: string;
3
+ description?: string;
4
+ version?: string;
5
+ trace_types: string[];
6
+ coverage_statuses: string[];
7
+ prompts: Record<string, string>;
8
+ defaults?: Record<string, string>;
9
+ }
10
+ export declare function validatePreset(data: unknown): Preset;
11
+ export declare function resolvePreset(name: string, pkgRoot: string): string | null;
12
+ export declare function loadPreset(name: string, pkgRoot: string): Preset;
13
+ export interface PresetInfo {
14
+ name: string;
15
+ description: string;
16
+ source: 'built-in' | 'project-local';
17
+ }
18
+ export declare function listPresets(pkgRoot: string): PresetInfo[];
package/dist/preset.js ADDED
@@ -0,0 +1,85 @@
1
+ import { readFileSync, readdirSync, existsSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ const REQUIRED_FIELDS = ['name', 'trace_types', 'coverage_statuses', 'prompts'];
4
+ export function validatePreset(data) {
5
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) {
6
+ throw new Error('Preset must be a JSON object');
7
+ }
8
+ const obj = data;
9
+ for (const field of REQUIRED_FIELDS) {
10
+ if (!(field in obj)) {
11
+ throw new Error(`Preset missing required field: ${field}`);
12
+ }
13
+ }
14
+ if (typeof obj.name !== 'string') {
15
+ throw new Error('Preset "name" must be a string');
16
+ }
17
+ if (!Array.isArray(obj.trace_types) || obj.trace_types.some((t) => typeof t !== 'string')) {
18
+ throw new Error('Preset "trace_types" must be a string array');
19
+ }
20
+ if (!Array.isArray(obj.coverage_statuses) || obj.coverage_statuses.some((s) => typeof s !== 'string')) {
21
+ throw new Error('Preset "coverage_statuses" must be a string array');
22
+ }
23
+ if (typeof obj.prompts !== 'object' || obj.prompts === null || Array.isArray(obj.prompts)) {
24
+ throw new Error('Preset "prompts" must be an object');
25
+ }
26
+ return obj;
27
+ }
28
+ export function resolvePreset(name, pkgRoot) {
29
+ const localPath = resolve(process.cwd(), 'presets', `${name}.json`);
30
+ if (existsSync(localPath))
31
+ return localPath;
32
+ const builtinPath = resolve(pkgRoot, 'presets', `${name}.json`);
33
+ if (existsSync(builtinPath))
34
+ return builtinPath;
35
+ return null;
36
+ }
37
+ export function loadPreset(name, pkgRoot) {
38
+ const path = resolvePreset(name, pkgRoot);
39
+ if (!path) {
40
+ const available = listPresets(pkgRoot);
41
+ const names = available.map(p => p.name).join(', ');
42
+ throw new Error(`Preset "${name}" not found. Available: ${names}`);
43
+ }
44
+ const raw = JSON.parse(readFileSync(path, 'utf-8'));
45
+ return validatePreset(raw);
46
+ }
47
+ export function listPresets(pkgRoot) {
48
+ const presets = new Map();
49
+ const builtinDir = resolve(pkgRoot, 'presets');
50
+ if (existsSync(builtinDir)) {
51
+ for (const file of readdirSync(builtinDir)) {
52
+ if (!file.endsWith('.json'))
53
+ continue;
54
+ try {
55
+ const data = JSON.parse(readFileSync(resolve(builtinDir, file), 'utf-8'));
56
+ const preset = validatePreset(data);
57
+ presets.set(preset.name, {
58
+ name: preset.name,
59
+ description: preset.description ?? '',
60
+ source: 'built-in',
61
+ });
62
+ }
63
+ catch { /* skip invalid */ }
64
+ }
65
+ }
66
+ const localDir = resolve(process.cwd(), 'presets');
67
+ if (existsSync(localDir)) {
68
+ for (const file of readdirSync(localDir)) {
69
+ if (!file.endsWith('.json'))
70
+ continue;
71
+ try {
72
+ const data = JSON.parse(readFileSync(resolve(localDir, file), 'utf-8'));
73
+ const preset = validatePreset(data);
74
+ presets.set(preset.name, {
75
+ name: preset.name,
76
+ description: preset.description ?? '',
77
+ source: 'project-local',
78
+ });
79
+ }
80
+ catch { /* skip invalid */ }
81
+ }
82
+ }
83
+ return [...presets.values()];
84
+ }
85
+ //# sourceMappingURL=preset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preset.js","sourceRoot":"","sources":["../src/preset.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@tracecart/cli",
3
+ "version": "0.1.0",
4
+ "description": "Claim traceability tool — extract traces from source documents, verify coverage against targets",
5
+ "keywords": [
6
+ "traceability",
7
+ "requirements",
8
+ "coverage",
9
+ "specification",
10
+ "verification",
11
+ "llm",
12
+ "claude-code",
13
+ "reverse-trace"
14
+ ],
15
+ "homepage": "https://github.com/tatargabor/tracecart",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/tatargabor/tracecart"
19
+ },
20
+ "license": "MIT",
21
+ "type": "module",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/preset.d.ts",
25
+ "import": "./dist/preset.js"
26
+ },
27
+ "./preset": {
28
+ "types": "./dist/preset.d.ts",
29
+ "import": "./dist/preset.js"
30
+ }
31
+ },
32
+ "bin": {
33
+ "tracecart": "./dist/cli.js"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "presets",
38
+ "prompts",
39
+ "templates"
40
+ ],
41
+ "scripts": {
42
+ "build": "tsc",
43
+ "dev": "tsc --watch",
44
+ "clean": "rm -rf dist",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "lint": "tsc --noEmit",
48
+ "prepublishOnly": "tsc"
49
+ },
50
+ "engines": {
51
+ "node": ">=18"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^25.9.2",
55
+ "typescript": "^5.7.0",
56
+ "vitest": "^3.0.0"
57
+ }
58
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "spec-coverage",
3
+ "description": "Source requirements → target spec coverage verification",
4
+ "version": "1.0",
5
+ "trace_types": ["REQUIREMENT", "DECISION", "WISH", "OPEN_QUESTION", "EXCLUSION"],
6
+ "coverage_statuses": ["COVERED", "PARTIAL", "MISSING", "DEFERRED", "N/A"],
7
+ "prompts": {
8
+ "extract": "extract.txt",
9
+ "coverage_check": "coverage_check.txt",
10
+ "reverse_check": "reverse_check.txt"
11
+ },
12
+ "defaults": {
13
+ "language": "en"
14
+ }
15
+ }
@@ -0,0 +1,38 @@
1
+ You are verifying specification coverage.
2
+
3
+ For EACH requirement below, check if the spec sections address it:
4
+
5
+ Status options:
6
+ {coverage_statuses}
7
+
8
+ Status descriptions:
9
+ - COVERED: the spec FULLY addresses this specific point. Cite spec file + section.
10
+ - PARTIAL: the spec addresses part but misses something. Explain what's missing.
11
+ - MISSING: not addressed in the specs AND not in the scope exclusions.
12
+ - DEFERRED: the topic is explicitly in the scope exclusions list (M2+).
13
+ - N/A: meta-statement or framing decision that doesn't need implementable spec coverage.
14
+
15
+ STRICT RULES:
16
+ - COVERED requires the spec to address the SPECIFIC point, not just mention the topic area
17
+ - Check modal verbs: if requirement says "may send" but spec says "sends" → PARTIAL
18
+ - EXCLUSION-type requirements are scope boundaries — check if specs ALSO state this boundary
19
+ - For [implicit] requirements: if the implicit claim is about HOW to implement (data model, real-time vs batch, configurability) rather than WHAT the system must do, mark N/A — specs are not required to mirror implementation assumptions
20
+
21
+ SCOPE EXCLUSIONS (M2+):
22
+ {exclusions}
23
+
24
+ REQUIREMENTS TO CHECK:
25
+ {requirements}
26
+
27
+ SPEC SECTIONS:
28
+ {specs}
29
+
30
+ OUTPUT — JSON array only:
31
+ [
32
+ {
33
+ "sentence_id": "CS-9-001",
34
+ "status": "COVERED",
35
+ "spec_ref": "01-order-intake.md §2.1",
36
+ "notes": ""
37
+ }
38
+ ]
@@ -0,0 +1,40 @@
1
+ You are extracting atomic requirements from a specification document.
2
+ The text has been pre-split into numbered clauses. Each clause has a clause_id (e.g., L27-C3).
3
+
4
+ RULES:
5
+ - Every clause that carries meaning must produce at least one requirement entry
6
+ - If a clause contains multiple independent facts joined by "or", split them
7
+ - IMPLICIT requirements — extract ONLY when the source text LINGUISTICALLY implies them:
8
+ - Modal verbs: "may send" ≠ "sends" → note optionality
9
+ - Scope markers: "exclusively" → hard boundary that needs enforcement
10
+ - Negation scope: "does not apply to X" → exclusion boundary needs category logic
11
+ - Domain terms: "B2B" → implies not end-consumer
12
+ DO NOT extract implicit requirements based on logical inference or implementation guessing:
13
+ - BAD: "considers warehouse stock" → "needs real-time stock data" (implementation guess)
14
+ - BAD: "minimum order quantity" → "must be configurable" (logical inference)
15
+ - GOOD: "exclusively from FSC suppliers" → "system must block non-FSC orders" (linguistic: "exclusively" = enforcement)
16
+ The test: could a reader DISAGREE with the implicit requirement while agreeing with the source text? If yes, don't extract it.
17
+ - NEGATIVE requirements: "will not", "not needed", "not a goal" → type: EXCLUSION
18
+ - Tag each entry:
19
+ - type: {trace_types}
20
+ - tags (one or more): orders, email, ai, materials, manufacturing, logistics, communication, crm, partners, billing, products, finishing, capacity, shipping, documents, design, portal, pricing
21
+
22
+ OUTPUT FORMAT — JSON array only, no other text:
23
+ [
24
+ {
25
+ "id": "CS-{chapter}-{NNN}",
26
+ "clause_id": "L27-C3",
27
+ "type": "REQUIREMENT",
28
+ "tags": ["orders", "manufacturing"],
29
+ "text": "Confirmation must consider CNC capacity",
30
+ "triple": {"subject": "confirmation", "attribute": "must consider", "object": "CNC capacity"},
31
+ "implicit": false
32
+ }
33
+ ]
34
+
35
+ For implicit requirements, set "implicit": true and add "signal" field explaining what word/phrase triggered it.
36
+
37
+ CLAUSE LIST:
38
+ {clauses}
39
+
40
+ AFTER extraction, CHECK: list any clause_ids from the input that are NOT cited by any requirement. These are potential gaps — extract requirements from them too.
@@ -0,0 +1,36 @@
1
+ You are verifying reverse traceability — checking whether target claims can be traced back to source documents.
2
+
3
+ For EACH target claim below, check if the source traces support it:
4
+
5
+ Status options:
6
+ - TRACED: a source trace FULLY supports this specific claim. Cite the source trace ID.
7
+ - PARTIAL_SOURCE: the source mentions the topic but the claim adds unsupported specifics. Explain what's unsupported.
8
+ - UNTRACED_IN_SOURCE: no source trace supports this claim — it may be hallucinated, creative addition, or from an unknown source.
9
+
10
+ STRICT RULES:
11
+ - TRACED requires a source trace that supports the SPECIFIC claim, not just the topic area
12
+ - Check modal verbs: if target says "sends" but source says "may send" → PARTIAL_SOURCE
13
+ - If untraced or partial, identify the NEAREST source trace (most semantically similar) and explain the gap
14
+
15
+ TARGET CLAIMS TO CHECK:
16
+ {claims}
17
+
18
+ SOURCE TRACES:
19
+ {evidence}
20
+
21
+ OUTPUT — JSON array only:
22
+ [
23
+ {
24
+ "claim_id": "RT-a3f2c1-012-0",
25
+ "status": "TRACED",
26
+ "source_trace_id": "T-b2d4e7-047-0",
27
+ "nearest_source_trace": null,
28
+ "similarity_note": "",
29
+ "notes": ""
30
+ }
31
+ ]
32
+
33
+ IMPORTANT for UNTRACED_IN_SOURCE and PARTIAL_SOURCE:
34
+ - Set "source_trace_id" to null
35
+ - Set "nearest_source_trace" to the ID of the most similar source trace (or null if none is even remotely related)
36
+ - Set "similarity_note" to explain why it's close but not sufficient (or "no related source trace found" if no match)
@@ -0,0 +1,217 @@
1
+ <!-- tracecart v0.2.1 -->
2
+ ---
3
+ name: "Tracecart"
4
+ description: "Run the full trace pipeline: source → target → trace-map.json. Deterministic CLI steps + LLM subagent extraction and matching."
5
+ ---
6
+
7
+ # Trace Pipeline
8
+
9
+ Run the complete tracecart pipeline to extract traces from source documents and verify coverage against target documents.
10
+
11
+ **Input**: `/tracecart <source> <target> [--preset <name>] [--reverse]`
12
+
13
+ - `<source>`: path to the source document (authoritative input, e.g., meeting notes)
14
+ - `<target>`: path to the target document (verified for completeness, e.g., spec)
15
+ - `--preset`: preset name, defaults to `spec-coverage`. Run `tracecart presets` to list available presets.
16
+ - `--reverse`: also run reverse tracing (target → source) to detect unsupported claims in the target
17
+
18
+ Parse `$ARGUMENTS` to extract these. If arguments are missing, ask the user.
19
+
20
+ ## Presets
21
+
22
+ Presets define the domain vocabulary (trace types, coverage statuses) and prompt templates used by the pipeline. The same pipeline structure works for different use cases by switching presets.
23
+
24
+ **Built-in presets:**
25
+ - `spec-coverage` — Source requirements → target spec coverage verification (default)
26
+
27
+ **Custom presets:** Place a JSON file in `./presets/<name>.json` in your project. Project-local presets override built-in ones by name.
28
+
29
+ **Preset format:**
30
+ ```json
31
+ {
32
+ "name": "my-preset",
33
+ "description": "What this preset checks",
34
+ "version": "1.0",
35
+ "trace_types": ["TYPE_A", "TYPE_B"],
36
+ "coverage_statuses": ["STATUS_A", "STATUS_B"],
37
+ "prompts": {
38
+ "extract": "extract.txt",
39
+ "coverage_check": "coverage_check.txt",
40
+ "reverse_check": "reverse_check.txt"
41
+ }
42
+ }
43
+ ```
44
+
45
+ ## Pipeline Steps
46
+
47
+ Execute these steps in order. Each step is explicit — follow exactly as written.
48
+
49
+ ### Step 1: Split source into clauses
50
+
51
+ ```bash
52
+ tracecart split <source> > /tmp/tracecart-clauses.json
53
+ ```
54
+
55
+ Report: "Split complete: N content clauses from M lines."
56
+
57
+ ### Step 2: Extract traces (LLM loop — max 3 iterations)
58
+
59
+ Set `iteration = 1` and `previous_remaining = Infinity`.
60
+
61
+ **Loop:**
62
+
63
+ #### 2a. Generate extraction prompt
64
+
65
+ ```bash
66
+ tracecart extract-prompt /tmp/tracecart-clauses.json --source <source> --preset <preset> > /tmp/tracecart-extract-prompt.txt
67
+ ```
68
+
69
+ #### 2b. Send to subagent
70
+
71
+ Use the **Agent tool** to spawn a subagent with the content of `/tmp/tracecart-extract-prompt.txt` as the prompt. The subagent should return a JSON array of extracted traces.
72
+
73
+ Save the subagent's full response to `/tmp/tracecart-extract-raw.txt`.
74
+
75
+ #### 2c. Validate extraction
76
+
77
+ ```bash
78
+ tracecart extract-validate /tmp/tracecart-extract-raw.txt /tmp/tracecart-clauses.json --source <source> --preset <preset> > /tmp/tracecart-traces.json
79
+ ```
80
+
81
+ Report any validation errors from the output.
82
+
83
+ #### 2d. Check remainder
84
+
85
+ ```bash
86
+ tracecart remainder /tmp/tracecart-clauses.json /tmp/tracecart-traces.json > /tmp/tracecart-remainder.json
87
+ ```
88
+
89
+ Read the output and check:
90
+ - `stats.clauses_remaining`: number of uncovered clauses
91
+ - `stats.coverage_pct`: coverage percentage
92
+
93
+ **Decision:**
94
+ - If `clauses_remaining == 0`: extraction complete, go to Step 3
95
+ - If `clauses_remaining < previous_remaining` AND `iteration < 3`:
96
+ set `previous_remaining = clauses_remaining`, increment `iteration`, go back to 2a
97
+ - Otherwise (stalled or max iterations): report remainder count, go to Step 3
98
+
99
+ Report: "Extraction iteration N: coverage_pct% (remaining clauses)."
100
+
101
+ ### Step 3: Match traces against target (LLM)
102
+
103
+ #### 3a. Generate matching prompt
104
+
105
+ ```bash
106
+ tracecart match-prompt /tmp/tracecart-traces.json <target> --preset <preset> > /tmp/tracecart-match-prompt.txt
107
+ ```
108
+
109
+ #### 3b. Send to subagent
110
+
111
+ Use the **Agent tool** to spawn a subagent with the content of `/tmp/tracecart-match-prompt.txt` as the prompt. The subagent should return a JSON array of coverage assessments.
112
+
113
+ Save the subagent's full response to `/tmp/tracecart-match-raw.txt`.
114
+
115
+ #### 3c. Validate matching
116
+
117
+ ```bash
118
+ tracecart match-validate /tmp/tracecart-match-raw.txt /tmp/tracecart-traces.json --preset <preset> > /tmp/tracecart-matches.json
119
+ ```
120
+
121
+ Report any validation errors.
122
+
123
+ ### Step 4: Finalize
124
+
125
+ ```bash
126
+ tracecart finalize /tmp/tracecart-traces.json /tmp/tracecart-matches.json --source <source> --target <target> --output trace-map.json
127
+ ```
128
+
129
+ ### Step 5: Report
130
+
131
+ Read `trace-map.json` and report the coverage summary:
132
+ - Total traces
133
+ - COVERED / PARTIAL / MISSING / DEFERRED / N/A counts
134
+ - Coverage score percentage
135
+
136
+ If there are MISSING traces, list the top 5 with their text.
137
+
138
+ ## Reverse Tracing (only if `--reverse` is set)
139
+
140
+ If `--reverse` was NOT specified, stop here. Otherwise continue with steps R1–R5.
141
+
142
+ Reverse tracing checks whether target claims can be traced BACK to the source — catching hallucinations, creative additions, or unsupported specifics in the target document.
143
+
144
+ ### Step R1: Split target into clauses
145
+
146
+ ```bash
147
+ tracecart split <target> > /tmp/tracecart-target-clauses.json
148
+ ```
149
+
150
+ Report: "Target split: N content clauses."
151
+
152
+ ### Step R2: Extract target claims (LLM)
153
+
154
+ #### R2a. Generate extraction prompt
155
+
156
+ ```bash
157
+ tracecart extract-prompt /tmp/tracecart-target-clauses.json --source <target> --preset <preset> > /tmp/tracecart-reverse-extract-prompt.txt
158
+ ```
159
+
160
+ #### R2b. Send to subagent
161
+
162
+ Use the **Agent tool** to spawn a subagent with the content of `/tmp/tracecart-reverse-extract-prompt.txt` as the prompt. The subagent should return a JSON array of extracted claims.
163
+
164
+ Save the subagent's full response to `/tmp/tracecart-reverse-extract-raw.txt`.
165
+
166
+ #### R2c. Validate extraction (RT- prefix)
167
+
168
+ ```bash
169
+ tracecart reverse-extract-validate /tmp/tracecart-reverse-extract-raw.txt /tmp/tracecart-target-clauses.json --target <target> --preset <preset> > /tmp/tracecart-reverse-traces.json
170
+ ```
171
+
172
+ Report any validation errors.
173
+
174
+ ### Step R3: Match target claims against source traces (LLM)
175
+
176
+ #### R3a. Generate reverse matching prompt
177
+
178
+ ```bash
179
+ tracecart reverse-match-prompt /tmp/tracecart-reverse-traces.json /tmp/tracecart-traces.json --preset <preset> > /tmp/tracecart-reverse-match-prompt.txt
180
+ ```
181
+
182
+ #### R3b. Send to subagent
183
+
184
+ Use the **Agent tool** to spawn a subagent with the content of `/tmp/tracecart-reverse-match-prompt.txt` as the prompt. The subagent should return a JSON array of reverse traceability assessments.
185
+
186
+ Save the subagent's full response to `/tmp/tracecart-reverse-match-raw.txt`.
187
+
188
+ #### R3c. Validate reverse matching
189
+
190
+ ```bash
191
+ tracecart reverse-match-validate /tmp/tracecart-reverse-match-raw.txt /tmp/tracecart-reverse-traces.json > /tmp/tracecart-reverse-matches.json
192
+ ```
193
+
194
+ Report any validation errors.
195
+
196
+ ### Step R4: Finalize with reverse traces
197
+
198
+ ```bash
199
+ tracecart finalize /tmp/tracecart-traces.json /tmp/tracecart-matches.json --source <source> --target <target> --reverse-traces /tmp/tracecart-reverse-matches.json --output trace-map.json
200
+ ```
201
+
202
+ ### Step R5: Reverse report
203
+
204
+ Read `trace-map.json` and report the reverse traceability summary:
205
+ - Total target claims
206
+ - TRACED / PARTIAL_SOURCE / UNTRACED_IN_SOURCE counts
207
+ - Reverse coverage percentage
208
+
209
+ If there are UNTRACED_IN_SOURCE claims, list the top 5 with their text and the nearest source trace (if any).
210
+
211
+ ## Notes
212
+
213
+ - All intermediate files go to `/tmp/tracecart-*` to avoid polluting the project directory
214
+ - The extraction loop (Step 2) runs max 3 iterations and stops early if remainder doesn't shrink
215
+ - Each subagent call is independent — the prompt contains all context the subagent needs
216
+ - If any step fails, report the error and stop — don't continue with partial data
217
+ - Reverse tracing (Steps R1–R5) only runs when `--reverse` is specified