@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.
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +149 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/delta.d.ts +1 -0
- package/dist/commands/delta.js +19 -0
- package/dist/commands/delta.js.map +1 -0
- package/dist/commands/extract-prompt.d.ts +2 -0
- package/dist/commands/extract-prompt.js +35 -0
- package/dist/commands/extract-prompt.js.map +1 -0
- package/dist/commands/extract-validate.d.ts +2 -0
- package/dist/commands/extract-validate.js +21 -0
- package/dist/commands/extract-validate.js.map +1 -0
- package/dist/commands/finalize.d.ts +1 -0
- package/dist/commands/finalize.js +64 -0
- package/dist/commands/finalize.js.map +1 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +33 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/match-prompt.d.ts +2 -0
- package/dist/commands/match-prompt.js +51 -0
- package/dist/commands/match-prompt.js.map +1 -0
- package/dist/commands/match-validate.d.ts +2 -0
- package/dist/commands/match-validate.js +15 -0
- package/dist/commands/match-validate.js.map +1 -0
- package/dist/commands/presets.d.ts +1 -0
- package/dist/commands/presets.js +14 -0
- package/dist/commands/presets.js.map +1 -0
- package/dist/commands/remainder.d.ts +1 -0
- package/dist/commands/remainder.js +19 -0
- package/dist/commands/remainder.js.map +1 -0
- package/dist/commands/reverse-extract-validate.d.ts +1 -0
- package/dist/commands/reverse-extract-validate.js +20 -0
- package/dist/commands/reverse-extract-validate.js.map +1 -0
- package/dist/commands/reverse-match-prompt.d.ts +2 -0
- package/dist/commands/reverse-match-prompt.js +50 -0
- package/dist/commands/reverse-match-prompt.js.map +1 -0
- package/dist/commands/reverse-match-validate.d.ts +1 -0
- package/dist/commands/reverse-match-validate.js +14 -0
- package/dist/commands/reverse-match-validate.js.map +1 -0
- package/dist/commands/split.d.ts +1 -0
- package/dist/commands/split.js +26 -0
- package/dist/commands/split.js.map +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +34 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +5 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/extract/extract.d.ts +37 -0
- package/dist/extract/extract.js +158 -0
- package/dist/extract/extract.js.map +1 -0
- package/dist/extract/remainder.d.ts +16 -0
- package/dist/extract/remainder.js +34 -0
- package/dist/extract/remainder.js.map +1 -0
- package/dist/match/coverage.d.ts +64 -0
- package/dist/match/coverage.js +375 -0
- package/dist/match/coverage.js.map +1 -0
- package/dist/output/delta.d.ts +46 -0
- package/dist/output/delta.js +155 -0
- package/dist/output/delta.js.map +1 -0
- package/dist/output/trace-map.d.ts +89 -0
- package/dist/output/trace-map.js +135 -0
- package/dist/output/trace-map.js.map +1 -0
- package/dist/parse/clause-split.d.ts +20 -0
- package/dist/parse/clause-split.js +185 -0
- package/dist/parse/clause-split.js.map +1 -0
- package/dist/parse/discover-inputs.d.ts +16 -0
- package/dist/parse/discover-inputs.js +97 -0
- package/dist/parse/discover-inputs.js.map +1 -0
- package/dist/parse/parse-document.d.ts +28 -0
- package/dist/parse/parse-document.js +141 -0
- package/dist/parse/parse-document.js.map +1 -0
- package/dist/preset.d.ts +18 -0
- package/dist/preset.js +85 -0
- package/dist/preset.js.map +1 -0
- package/package.json +58 -0
- package/presets/spec-coverage.json +15 -0
- package/prompts/coverage_check.txt +38 -0
- package/prompts/extract.txt +40 -0
- package/prompts/reverse_check.txt +36 -0
- 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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"}
|
package/dist/preset.d.ts
ADDED
|
@@ -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
|