specweave 1.0.571 → 1.0.573
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/bin/specweave.js +36 -2
- package/dist/src/cli/commands/hooks-cmd.d.ts +27 -0
- package/dist/src/cli/commands/hooks-cmd.d.ts.map +1 -0
- package/dist/src/cli/commands/hooks-cmd.js +145 -0
- package/dist/src/cli/commands/hooks-cmd.js.map +1 -0
- package/dist/src/cli/commands/update.d.ts.map +1 -1
- package/dist/src/cli/commands/update.js +9 -2
- package/dist/src/cli/commands/update.js.map +1 -1
- package/dist/src/cli/helpers/init/smart-defaults.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/smart-defaults.js +0 -6
- package/dist/src/cli/helpers/init/smart-defaults.js.map +1 -1
- package/dist/src/core/doctor/checkers/hooks-checker.js +2 -2
- package/dist/src/core/doctor/checkers/hooks-checker.js.map +1 -1
- package/dist/src/core/doctor/doctor.js +1 -1
- package/dist/src/core/doctor/doctor.js.map +1 -1
- package/dist/src/core/hooks/handlers/hook-router.d.ts.map +1 -1
- package/dist/src/core/hooks/handlers/hook-router.js +34 -2
- package/dist/src/core/hooks/handlers/hook-router.js.map +1 -1
- package/dist/src/core/hooks/handlers/utils.d.ts +2 -1
- package/dist/src/core/hooks/handlers/utils.d.ts.map +1 -1
- package/dist/src/core/hooks/handlers/utils.js +14 -1
- package/dist/src/core/hooks/handlers/utils.js.map +1 -1
- package/dist/src/core/hooks/hook-health-tracker.js +2 -2
- package/dist/src/core/hooks/hook-health-tracker.js.map +1 -1
- package/dist/src/core/hooks/hook-logger.d.ts +1 -1
- package/dist/src/core/hooks/hook-logger.js +1 -1
- package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
- package/dist/src/core/increment/completion-validator.js +60 -0
- package/dist/src/core/increment/completion-validator.js.map +1 -1
- package/dist/src/core/increment/template-creator.d.ts.map +1 -1
- package/dist/src/core/increment/template-creator.js +19 -0
- package/dist/src/core/increment/template-creator.js.map +1 -1
- package/dist/src/core/rubric/index.d.ts +6 -0
- package/dist/src/core/rubric/index.d.ts.map +1 -0
- package/dist/src/core/rubric/index.js +6 -0
- package/dist/src/core/rubric/index.js.map +1 -0
- package/dist/src/core/rubric/rubric-evaluator.d.ts +17 -0
- package/dist/src/core/rubric/rubric-evaluator.d.ts.map +1 -0
- package/dist/src/core/rubric/rubric-evaluator.js +141 -0
- package/dist/src/core/rubric/rubric-evaluator.js.map +1 -0
- package/dist/src/core/rubric/rubric-generator.d.ts +13 -0
- package/dist/src/core/rubric/rubric-generator.d.ts.map +1 -0
- package/dist/src/core/rubric/rubric-generator.js +126 -0
- package/dist/src/core/rubric/rubric-generator.js.map +1 -0
- package/dist/src/core/rubric/rubric-merger.d.ts +14 -0
- package/dist/src/core/rubric/rubric-merger.d.ts.map +1 -0
- package/dist/src/core/rubric/rubric-merger.js +78 -0
- package/dist/src/core/rubric/rubric-merger.js.map +1 -0
- package/dist/src/core/rubric/rubric-parser.d.ts +10 -0
- package/dist/src/core/rubric/rubric-parser.d.ts.map +1 -0
- package/dist/src/core/rubric/rubric-parser.js +172 -0
- package/dist/src/core/rubric/rubric-parser.js.map +1 -0
- package/dist/src/core/rubric/types.d.ts +74 -0
- package/dist/src/core/rubric/types.d.ts.map +1 -0
- package/dist/src/core/rubric/types.js +16 -0
- package/dist/src/core/rubric/types.js.map +1 -0
- package/package.json +2 -2
- package/plugins/specweave/agents/sw-planner.md +22 -0
- package/plugins/specweave/defaults/rubric-defaults.md +53 -0
- package/plugins/specweave/skills/auto/SKILL.md +0 -1
- package/plugins/specweave/skills/code-reviewer/SKILL.md +5 -0
- package/plugins/specweave/skills/done/SKILL.md +2 -0
- package/plugins/specweave/skills/grill/SKILL.md +6 -0
- package/plugins/specweave/skills/judge-llm/SKILL.md +7 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface GenerateOptions {
|
|
2
|
+
coverageTarget?: number;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Generate rubric.md markdown from spec.md content.
|
|
6
|
+
*/
|
|
7
|
+
export declare function generateRubric(incrementId: string, specContent: string, options?: GenerateOptions): string;
|
|
8
|
+
/**
|
|
9
|
+
* Generate rubric.md and write to the increment directory.
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateRubricFile(incrementId: string, incrementPath: string, options?: GenerateOptions): Promise<void>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=rubric-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rubric-generator.d.ts","sourceRoot":"","sources":["../../../../src/core/rubric/rubric-generator.ts"],"names":[],"mappings":"AAGA,UAAU,eAAe;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AA8BD;;GAEG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,eAAoB,GAC5B,MAAM,CAkGR;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAKf"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as fs from '../../utils/fs-native.js';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
function extractACs(specContent) {
|
|
4
|
+
const acs = [];
|
|
5
|
+
const lines = specContent.split('\n');
|
|
6
|
+
let currentUS = '';
|
|
7
|
+
for (const line of lines) {
|
|
8
|
+
const usMatch = line.match(/^###\s+US-(\d+)/);
|
|
9
|
+
if (usMatch) {
|
|
10
|
+
currentUS = `US-${usMatch[1]}`;
|
|
11
|
+
}
|
|
12
|
+
const acMatch = line.match(/\*\*AC-(US\d+-\d+)\*\*:\s*(.+)/);
|
|
13
|
+
if (acMatch) {
|
|
14
|
+
acs.push({
|
|
15
|
+
id: `AC-${acMatch[1]}`,
|
|
16
|
+
description: acMatch[2].trim(),
|
|
17
|
+
userStory: currentUS,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return acs;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generate rubric.md markdown from spec.md content.
|
|
25
|
+
*/
|
|
26
|
+
export function generateRubric(incrementId, specContent, options = {}) {
|
|
27
|
+
const acs = extractACs(specContent);
|
|
28
|
+
const coverageTarget = options.coverageTarget ?? 90;
|
|
29
|
+
const lines = [];
|
|
30
|
+
let counter = 1;
|
|
31
|
+
const pad = (n) => String(n).padStart(3, '0');
|
|
32
|
+
// Frontmatter
|
|
33
|
+
lines.push('---');
|
|
34
|
+
lines.push(`increment: ${incrementId}`);
|
|
35
|
+
lines.push(`title: Rubric for ${incrementId}`);
|
|
36
|
+
lines.push(`generated: ${new Date().toISOString()}`);
|
|
37
|
+
lines.push('source: spec.md (auto-generated from ACs)');
|
|
38
|
+
lines.push('version: "1.0"');
|
|
39
|
+
lines.push('status: pending');
|
|
40
|
+
lines.push('---');
|
|
41
|
+
lines.push('');
|
|
42
|
+
lines.push(`# Rubric: ${incrementId}`);
|
|
43
|
+
lines.push('');
|
|
44
|
+
lines.push('> Auto-generated from spec.md acceptance criteria. Review and customize before implementation.');
|
|
45
|
+
lines.push('> All **[blocking]** criteria must pass before `sw:done` can close the increment.');
|
|
46
|
+
lines.push('');
|
|
47
|
+
// Functional criteria from ACs
|
|
48
|
+
if (acs.length > 0) {
|
|
49
|
+
lines.push('---');
|
|
50
|
+
lines.push('');
|
|
51
|
+
lines.push('## Functional Correctness');
|
|
52
|
+
lines.push('');
|
|
53
|
+
// Group ACs by user story
|
|
54
|
+
const byUS = new Map();
|
|
55
|
+
for (const ac of acs) {
|
|
56
|
+
const key = ac.userStory || 'unknown';
|
|
57
|
+
if (!byUS.has(key))
|
|
58
|
+
byUS.set(key, []);
|
|
59
|
+
byUS.get(key).push(ac);
|
|
60
|
+
}
|
|
61
|
+
for (const [, usAcs] of byUS) {
|
|
62
|
+
for (const ac of usAcs) {
|
|
63
|
+
const id = `R-${pad(counter++)}`;
|
|
64
|
+
lines.push(`### ${id}: ${ac.description} [blocking]`);
|
|
65
|
+
lines.push(`- **Source**: ${ac.id}`);
|
|
66
|
+
lines.push('- **Evaluator**: sw:grill');
|
|
67
|
+
lines.push(`- **Verify**: ${ac.description}`);
|
|
68
|
+
lines.push('- **Threshold**: AC passes');
|
|
69
|
+
lines.push('- **Result**: [ ] PENDING');
|
|
70
|
+
lines.push('');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Standard categories
|
|
75
|
+
lines.push('---');
|
|
76
|
+
lines.push('');
|
|
77
|
+
lines.push('## Test Coverage');
|
|
78
|
+
lines.push('');
|
|
79
|
+
lines.push(`### R-D01: Unit test coverage meets target [blocking]`);
|
|
80
|
+
lines.push('- **Source**: project-default');
|
|
81
|
+
lines.push('- **Evaluator**: coverage');
|
|
82
|
+
lines.push(`- **Verify**: Coverage output on new/modified files`);
|
|
83
|
+
lines.push(`- **Threshold**: >= ${coverageTarget}% line coverage`);
|
|
84
|
+
lines.push('- **Result**: [ ] PENDING');
|
|
85
|
+
lines.push('');
|
|
86
|
+
lines.push('---');
|
|
87
|
+
lines.push('');
|
|
88
|
+
lines.push('## Code Quality');
|
|
89
|
+
lines.push('');
|
|
90
|
+
lines.push('### R-D02: No critical, high, or medium code review findings [blocking]');
|
|
91
|
+
lines.push('- **Source**: project-default');
|
|
92
|
+
lines.push('- **Evaluator**: sw:code-reviewer');
|
|
93
|
+
lines.push('- **Verify**: code-review-report.json summary');
|
|
94
|
+
lines.push('- **Threshold**: critical === 0 AND high === 0 AND medium === 0');
|
|
95
|
+
lines.push('- **Result**: [ ] PENDING');
|
|
96
|
+
lines.push('');
|
|
97
|
+
lines.push('---');
|
|
98
|
+
lines.push('');
|
|
99
|
+
lines.push('## Independent Evaluation');
|
|
100
|
+
lines.push('');
|
|
101
|
+
lines.push('### R-D03: Ship readiness verified [blocking]');
|
|
102
|
+
lines.push('- **Source**: project-default');
|
|
103
|
+
lines.push('- **Evaluator**: sw:grill');
|
|
104
|
+
lines.push('- **Verify**: grill-report.json shipReadiness');
|
|
105
|
+
lines.push('- **Threshold**: shipReadiness !== "NOT READY"');
|
|
106
|
+
lines.push('- **Result**: [ ] PENDING');
|
|
107
|
+
lines.push('');
|
|
108
|
+
lines.push('### R-D04: LLM judge verdict acceptable [blocking]');
|
|
109
|
+
lines.push('- **Source**: project-default');
|
|
110
|
+
lines.push('- **Evaluator**: sw:judge-llm');
|
|
111
|
+
lines.push('- **Verify**: judge-llm-report.json verdict');
|
|
112
|
+
lines.push('- **Threshold**: verdict !== "REJECTED"');
|
|
113
|
+
lines.push('- **Result**: [ ] PENDING');
|
|
114
|
+
lines.push('');
|
|
115
|
+
return lines.join('\n');
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Generate rubric.md and write to the increment directory.
|
|
119
|
+
*/
|
|
120
|
+
export async function generateRubricFile(incrementId, incrementPath, options = {}) {
|
|
121
|
+
const specPath = path.join(incrementPath, 'spec.md');
|
|
122
|
+
const specContent = await fs.readFile(specPath, 'utf-8');
|
|
123
|
+
const rubricContent = generateRubric(incrementId, specContent, options);
|
|
124
|
+
await fs.writeFile(path.join(incrementPath, 'rubric.md'), rubricContent, 'utf-8');
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=rubric-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rubric-generator.js","sourceRoot":"","sources":["../../../../src/core/rubric/rubric-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RubricDocument } from './types.js';
|
|
2
|
+
interface MergeOptions {
|
|
3
|
+
globalPath?: string;
|
|
4
|
+
projectPath?: string;
|
|
5
|
+
incrementPath?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Merge rubric criteria from three tiers: global → project → increment.
|
|
9
|
+
* Lower tier overrides higher on ID collision (whole-criterion replacement).
|
|
10
|
+
* Returns null if no rubric files exist at any tier.
|
|
11
|
+
*/
|
|
12
|
+
export declare function mergeRubricLayers(projectRoot: string, incrementId: string, incrementPath: string, options?: MergeOptions): Promise<RubricDocument | null>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=rubric-merger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rubric-merger.d.ts","sourceRoot":"","sources":["../../../../src/core/rubric/rubric-merger.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAmB,MAAM,YAAY,CAAC;AAGlE,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAiBD;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAwDhC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as fs from '../../utils/fs-native.js';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { parseRubric } from './rubric-parser.js';
|
|
4
|
+
async function tryParseFile(filePath) {
|
|
5
|
+
try {
|
|
6
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
7
|
+
const doc = parseRubric(content);
|
|
8
|
+
return doc.criteria;
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
// File not found → silently skip (expected for missing tiers)
|
|
12
|
+
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
// Parse errors and other failures → re-throw so caller can warn
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Merge rubric criteria from three tiers: global → project → increment.
|
|
21
|
+
* Lower tier overrides higher on ID collision (whole-criterion replacement).
|
|
22
|
+
* Returns null if no rubric files exist at any tier.
|
|
23
|
+
*/
|
|
24
|
+
export async function mergeRubricLayers(projectRoot, incrementId, incrementPath, options = {}) {
|
|
25
|
+
const globalFile = options.globalPath ??
|
|
26
|
+
path.join(projectRoot, 'plugins', 'specweave', 'defaults', 'rubric-defaults.md');
|
|
27
|
+
const projectFile = options.projectPath ??
|
|
28
|
+
path.join(projectRoot, '.specweave', 'rubric-defaults.md');
|
|
29
|
+
const incrementFile = options.incrementPath ??
|
|
30
|
+
path.join(incrementPath, 'rubric.md');
|
|
31
|
+
const globalCriteria = await tryParseFile(globalFile);
|
|
32
|
+
const projectCriteria = await tryParseFile(projectFile);
|
|
33
|
+
const incrementCriteria = await tryParseFile(incrementFile);
|
|
34
|
+
if (!globalCriteria && !projectCriteria && !incrementCriteria) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
// Build merged criteria map: global → project → increment (last wins)
|
|
38
|
+
const merged = new Map();
|
|
39
|
+
if (globalCriteria) {
|
|
40
|
+
for (const c of globalCriteria)
|
|
41
|
+
merged.set(c.id, c);
|
|
42
|
+
}
|
|
43
|
+
if (projectCriteria) {
|
|
44
|
+
for (const c of projectCriteria)
|
|
45
|
+
merged.set(c.id, c);
|
|
46
|
+
}
|
|
47
|
+
if (incrementCriteria) {
|
|
48
|
+
for (const c of incrementCriteria)
|
|
49
|
+
merged.set(c.id, c);
|
|
50
|
+
}
|
|
51
|
+
// Stable ordering: functional-correctness first, then standard categories
|
|
52
|
+
const categoryOrder = [
|
|
53
|
+
'functional-correctness',
|
|
54
|
+
'test-coverage',
|
|
55
|
+
'code-quality',
|
|
56
|
+
'security',
|
|
57
|
+
'performance',
|
|
58
|
+
'documentation',
|
|
59
|
+
'independent-evaluation',
|
|
60
|
+
];
|
|
61
|
+
const sortedCriteria = [...merged.values()].sort((a, b) => {
|
|
62
|
+
const ai = categoryOrder.indexOf(a.category);
|
|
63
|
+
const bi = categoryOrder.indexOf(b.category);
|
|
64
|
+
if (ai !== bi)
|
|
65
|
+
return ai - bi;
|
|
66
|
+
return a.id.localeCompare(b.id);
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
incrementId,
|
|
70
|
+
title: `Merged Rubric for ${incrementId}`,
|
|
71
|
+
generated: new Date().toISOString(),
|
|
72
|
+
source: 'merged',
|
|
73
|
+
version: '1.0',
|
|
74
|
+
status: 'pending',
|
|
75
|
+
criteria: sortedCriteria,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=rubric-merger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rubric-merger.js","sourceRoot":"","sources":["../../../../src/core/rubric/rubric-merger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RubricDocument } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parse rubric.md markdown content into a structured RubricDocument.
|
|
4
|
+
*/
|
|
5
|
+
export declare function parseRubric(content: string): RubricDocument;
|
|
6
|
+
/**
|
|
7
|
+
* Parse rubric.md from a file path.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseRubricFile(filePath: string): Promise<RubricDocument>;
|
|
10
|
+
//# sourceMappingURL=rubric-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rubric-parser.d.ts","sourceRoot":"","sources":["../../../../src/core/rubric/rubric-parser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EAMf,MAAM,YAAY,CAAC;AAyCpB;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CA6H3D;AAgBD;;GAEG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAG/E"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import * as fs from '../../utils/fs-native.js';
|
|
2
|
+
import { RubricParseError } from './types.js';
|
|
3
|
+
const CRITERION_HEADER_RE = /^###\s+(R-[A-Z0-9-]+\d+):\s+(.+?)\s+\[(blocking|advisory)\]\s*$/;
|
|
4
|
+
const CATEGORY_HEADING_RE = /^##\s+(.+)$/;
|
|
5
|
+
const FIELD_RE = /^-\s+\*\*(\w[\w\s]*)\*\*:\s+(.+)$/;
|
|
6
|
+
const RESULT_PASS_RE = /^\[x\]\s*PASS/i;
|
|
7
|
+
const RESULT_FAIL_RE = /^\[!\]\s*FAIL(?:\s*(?:—|-)\s*(.*))?$/i;
|
|
8
|
+
const RESULT_PENDING_RE = /^\[\s*\]\s*PENDING/i;
|
|
9
|
+
function toKebabCategory(heading) {
|
|
10
|
+
const kebab = heading.trim().toLowerCase().replace(/\s+/g, '-');
|
|
11
|
+
const validCategories = [
|
|
12
|
+
'functional-correctness',
|
|
13
|
+
'test-coverage',
|
|
14
|
+
'code-quality',
|
|
15
|
+
'security',
|
|
16
|
+
'performance',
|
|
17
|
+
'documentation',
|
|
18
|
+
'independent-evaluation',
|
|
19
|
+
];
|
|
20
|
+
return (validCategories.find(c => c === kebab) ?? 'functional-correctness');
|
|
21
|
+
}
|
|
22
|
+
function parseResultField(value) {
|
|
23
|
+
const trimmed = value.trim();
|
|
24
|
+
if (RESULT_PASS_RE.test(trimmed)) {
|
|
25
|
+
return { status: 'pass', evidence: '', evaluatedAt: '' };
|
|
26
|
+
}
|
|
27
|
+
const failMatch = trimmed.match(RESULT_FAIL_RE);
|
|
28
|
+
if (failMatch) {
|
|
29
|
+
return { status: 'fail', evidence: failMatch[1]?.trim() ?? '', evaluatedAt: '' };
|
|
30
|
+
}
|
|
31
|
+
if (RESULT_PENDING_RE.test(trimmed)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse rubric.md markdown content into a structured RubricDocument.
|
|
38
|
+
*/
|
|
39
|
+
export function parseRubric(content) {
|
|
40
|
+
const lines = content.split('\n');
|
|
41
|
+
let state = 'SCANNING_FRONTMATTER';
|
|
42
|
+
const frontmatter = {};
|
|
43
|
+
let currentCategory = 'functional-correctness';
|
|
44
|
+
let currentCriterion = null;
|
|
45
|
+
const criteria = [];
|
|
46
|
+
let frontmatterStart = -1;
|
|
47
|
+
for (let i = 0; i < lines.length; i++) {
|
|
48
|
+
const line = lines[i];
|
|
49
|
+
const lineNum = i + 1;
|
|
50
|
+
switch (state) {
|
|
51
|
+
case 'SCANNING_FRONTMATTER':
|
|
52
|
+
if (line.trim() === '---') {
|
|
53
|
+
state = 'IN_FRONTMATTER';
|
|
54
|
+
frontmatterStart = lineNum;
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
case 'IN_FRONTMATTER':
|
|
58
|
+
if (line.trim() === '---') {
|
|
59
|
+
state = 'SCANNING';
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const colonIdx = line.indexOf(':');
|
|
63
|
+
if (colonIdx > 0) {
|
|
64
|
+
const key = line.substring(0, colonIdx).trim();
|
|
65
|
+
const val = line.substring(colonIdx + 1).trim().replace(/^["']|["']$/g, '');
|
|
66
|
+
frontmatter[key] = val;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
case 'SCANNING':
|
|
71
|
+
case 'IN_CRITERION': {
|
|
72
|
+
const categoryMatch = line.match(CATEGORY_HEADING_RE);
|
|
73
|
+
if (categoryMatch) {
|
|
74
|
+
if (currentCriterion) {
|
|
75
|
+
criteria.push(finalizeCriterion(currentCriterion));
|
|
76
|
+
currentCriterion = null;
|
|
77
|
+
}
|
|
78
|
+
currentCategory = toKebabCategory(categoryMatch[1]);
|
|
79
|
+
state = 'SCANNING';
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
const criterionMatch = line.match(CRITERION_HEADER_RE);
|
|
83
|
+
if (criterionMatch) {
|
|
84
|
+
if (currentCriterion) {
|
|
85
|
+
criteria.push(finalizeCriterion(currentCriterion));
|
|
86
|
+
}
|
|
87
|
+
currentCriterion = {
|
|
88
|
+
id: criterionMatch[1],
|
|
89
|
+
title: criterionMatch[2],
|
|
90
|
+
severity: criterionMatch[3],
|
|
91
|
+
category: currentCategory,
|
|
92
|
+
sourceACs: [],
|
|
93
|
+
evaluator: 'sw:grill',
|
|
94
|
+
verify: '',
|
|
95
|
+
threshold: '',
|
|
96
|
+
result: null,
|
|
97
|
+
};
|
|
98
|
+
state = 'IN_CRITERION';
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
if (state === 'IN_CRITERION' && currentCriterion) {
|
|
102
|
+
const fieldMatch = line.match(FIELD_RE);
|
|
103
|
+
if (fieldMatch) {
|
|
104
|
+
const fieldName = fieldMatch[1].trim().toLowerCase();
|
|
105
|
+
const fieldValue = fieldMatch[2].trim();
|
|
106
|
+
switch (fieldName) {
|
|
107
|
+
case 'source':
|
|
108
|
+
currentCriterion.sourceACs = fieldValue
|
|
109
|
+
.split(',')
|
|
110
|
+
.map(s => s.trim())
|
|
111
|
+
.filter(Boolean);
|
|
112
|
+
break;
|
|
113
|
+
case 'evaluator': {
|
|
114
|
+
const validEvaluators = ['sw:grill', 'sw:code-reviewer', 'sw:judge-llm', 'coverage', 'manual'];
|
|
115
|
+
currentCriterion.evaluator = validEvaluators.includes(fieldValue)
|
|
116
|
+
? fieldValue
|
|
117
|
+
: 'sw:grill'; // default for unknown evaluators
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
case 'verify':
|
|
121
|
+
currentCriterion.verify = fieldValue;
|
|
122
|
+
break;
|
|
123
|
+
case 'threshold':
|
|
124
|
+
currentCriterion.threshold = fieldValue;
|
|
125
|
+
break;
|
|
126
|
+
case 'result':
|
|
127
|
+
currentCriterion.result = parseResultField(fieldValue);
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (state === 'IN_FRONTMATTER') {
|
|
137
|
+
throw new RubricParseError('Unterminated frontmatter — missing closing ---', frontmatterStart, '---');
|
|
138
|
+
}
|
|
139
|
+
if (currentCriterion) {
|
|
140
|
+
criteria.push(finalizeCriterion(currentCriterion));
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
incrementId: frontmatter['increment'] ?? '',
|
|
144
|
+
title: frontmatter['title'] ?? '',
|
|
145
|
+
generated: frontmatter['generated'] ?? '',
|
|
146
|
+
source: frontmatter['source'] ?? '',
|
|
147
|
+
version: frontmatter['version'] ?? '1.0',
|
|
148
|
+
status: frontmatter['status'] ?? 'pending',
|
|
149
|
+
criteria,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function finalizeCriterion(partial) {
|
|
153
|
+
return {
|
|
154
|
+
id: partial.id ?? 'R-000',
|
|
155
|
+
title: partial.title ?? '',
|
|
156
|
+
severity: partial.severity ?? 'blocking',
|
|
157
|
+
sourceACs: partial.sourceACs ?? [],
|
|
158
|
+
evaluator: partial.evaluator ?? 'sw:grill',
|
|
159
|
+
category: partial.category ?? 'functional-correctness',
|
|
160
|
+
verify: partial.verify ?? '',
|
|
161
|
+
threshold: partial.threshold ?? '',
|
|
162
|
+
result: partial.result ?? null,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Parse rubric.md from a file path.
|
|
167
|
+
*/
|
|
168
|
+
export async function parseRubricFile(filePath) {
|
|
169
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
170
|
+
return parseRubric(content);
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=rubric-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rubric-parser.js","sourceRoot":"","sources":["../../../../src/core/rubric/rubric-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAS/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAI9C,MAAM,mBAAmB,GAAG,iEAAiE,CAAC;AAC9F,MAAM,mBAAmB,GAAG,aAAa,CAAC;AAC1C,MAAM,QAAQ,GAAG,mCAAmC,CAAC;AACrD,MAAM,cAAc,GAAG,gBAAgB,CAAC;AACxC,MAAM,cAAc,GAAG,uCAAuC,CAAC;AAC/D,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AAEhD,SAAS,eAAe,CAAC,OAAe;IACtC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChE,MAAM,eAAe,GAAqB;QACxC,wBAAwB;QACxB,eAAe;QACf,cAAc;QACd,UAAU;QACV,aAAa;QACb,eAAe;QACf,wBAAwB;KACzB,CAAC;IACF,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,wBAAwB,CAAmB,CAAC;AAChG,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IAC3D,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAChD,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IACnF,CAAC;IACD,IAAI,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,KAAK,GAAgB,sBAAsB,CAAC;IAChD,MAAM,WAAW,GAA2B,EAAE,CAAC;IAC/C,IAAI,eAAe,GAAmB,wBAAwB,CAAC;IAC/D,IAAI,gBAAgB,GAAoC,IAAI,CAAC;IAC7D,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,IAAI,gBAAgB,GAAG,CAAC,CAAC,CAAC;IAE1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;QAEtB,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,sBAAsB;gBACzB,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,KAAK,EAAE,CAAC;oBAC1B,KAAK,GAAG,gBAAgB,CAAC;oBACzB,gBAAgB,GAAG,OAAO,CAAC;gBAC7B,CAAC;gBACD,MAAM;YAER,KAAK,gBAAgB;gBACnB,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,KAAK,EAAE,CAAC;oBAC1B,KAAK,GAAG,UAAU,CAAC;gBACrB,CAAC;qBAAM,CAAC;oBACN,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;oBACnC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;wBACjB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;wBAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;wBAC5E,WAAW,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;oBACzB,CAAC;gBACH,CAAC;gBACD,MAAM;YAER,KAAK,UAAU,CAAC;YAChB,KAAK,cAAc,CAAC,CAAC,CAAC;gBACpB,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;gBACtD,IAAI,aAAa,EAAE,CAAC;oBAClB,IAAI,gBAAgB,EAAE,CAAC;wBACrB,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC,CAAC;wBACnD,gBAAgB,GAAG,IAAI,CAAC;oBAC1B,CAAC;oBACD,eAAe,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;oBACpD,KAAK,GAAG,UAAU,CAAC;oBACnB,MAAM;gBACR,CAAC;gBAED,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;gBACvD,IAAI,cAAc,EAAE,CAAC;oBACnB,IAAI,gBAAgB,EAAE,CAAC;wBACrB,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC,CAAC;oBACrD,CAAC;oBACD,gBAAgB,GAAG;wBACjB,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;wBACrB,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC;wBACxB,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAmB;wBAC7C,QAAQ,EAAE,eAAe;wBACzB,SAAS,EAAE,EAAE;wBACb,SAAS,EAAE,UAAU;wBACrB,MAAM,EAAE,EAAE;wBACV,SAAS,EAAE,EAAE;wBACb,MAAM,EAAE,IAAI;qBACb,CAAC;oBACF,KAAK,GAAG,cAAc,CAAC;oBACvB,MAAM;gBACR,CAAC;gBAED,IAAI,KAAK,KAAK,cAAc,IAAI,gBAAgB,EAAE,CAAC;oBACjD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBACxC,IAAI,UAAU,EAAE,CAAC;wBACf,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;wBACrD,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;wBAExC,QAAQ,SAAS,EAAE,CAAC;4BAClB,KAAK,QAAQ;gCACX,gBAAgB,CAAC,SAAS,GAAG,UAAU;qCACpC,KAAK,CAAC,GAAG,CAAC;qCACV,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;qCAClB,MAAM,CAAC,OAAO,CAAC,CAAC;gCACnB,MAAM;4BACR,KAAK,WAAW,CAAC,CAAC,CAAC;gCACjB,MAAM,eAAe,GAAkB,CAAC,UAAU,EAAE,kBAAkB,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;gCAC9G,gBAAgB,CAAC,SAAS,GAAG,eAAe,CAAC,QAAQ,CAAC,UAAyB,CAAC;oCAC9E,CAAC,CAAC,UAAyB;oCAC3B,CAAC,CAAC,UAAU,CAAC,CAAC,iCAAiC;gCACjD,MAAM;4BACR,CAAC;4BACD,KAAK,QAAQ;gCACX,gBAAgB,CAAC,MAAM,GAAG,UAAU,CAAC;gCACrC,MAAM;4BACR,KAAK,WAAW;gCACd,gBAAgB,CAAC,SAAS,GAAG,UAAU,CAAC;gCACxC,MAAM;4BACR,KAAK,QAAQ;gCACX,gBAAgB,CAAC,MAAM,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;gCACvD,MAAM;wBACV,CAAC;oBACH,CAAC;gBACH,CAAC;gBACD,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,KAAK,KAAK,gBAAgB,EAAE,CAAC;QAC/B,MAAM,IAAI,gBAAgB,CACxB,gDAAgD,EAChD,gBAAgB,EAChB,KAAK,CACN,CAAC;IACJ,CAAC;IAED,IAAI,gBAAgB,EAAE,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,OAAO;QACL,WAAW,EAAE,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE;QAC3C,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE;QACjC,SAAS,EAAE,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE;QACzC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE;QACnC,OAAO,EAAE,WAAW,CAAC,SAAS,CAAC,IAAI,KAAK;QACxC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,IAAI,SAAS;QAC1C,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAiC;IAC1D,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,OAAO;QACzB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE;QAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,UAAU;QACxC,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE;QAClC,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,UAAU;QAC1C,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,wBAAwB;QACtD,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;QAC5B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE;QAClC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;KAC/B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB;IACpD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACrD,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rubric Quality Contracts — Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* A rubric is a per-increment quality contract that defines pass/fail criteria
|
|
5
|
+
* for closure gates. Auto-generated from spec.md ACs, customizable by the user.
|
|
6
|
+
*/
|
|
7
|
+
/** Two severity levels only — blocking must pass, advisory is informational */
|
|
8
|
+
export type RubricSeverity = 'blocking' | 'advisory';
|
|
9
|
+
/** Gate that evaluates this criterion */
|
|
10
|
+
export type EvaluatorId = 'sw:grill' | 'sw:code-reviewer' | 'sw:judge-llm' | 'coverage' | 'manual';
|
|
11
|
+
/** Standard rubric categories */
|
|
12
|
+
export type RubricCategory = 'functional-correctness' | 'test-coverage' | 'code-quality' | 'security' | 'performance' | 'documentation' | 'independent-evaluation';
|
|
13
|
+
/** Result of evaluating a single criterion */
|
|
14
|
+
export interface CriterionResult {
|
|
15
|
+
status: 'pass' | 'fail' | 'skip';
|
|
16
|
+
evidence: string;
|
|
17
|
+
evaluatedAt: string;
|
|
18
|
+
}
|
|
19
|
+
/** A single rubric criterion */
|
|
20
|
+
export interface RubricCriterion {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
severity: RubricSeverity;
|
|
24
|
+
sourceACs: string[];
|
|
25
|
+
evaluator: EvaluatorId;
|
|
26
|
+
category: RubricCategory;
|
|
27
|
+
verify: string;
|
|
28
|
+
threshold: string;
|
|
29
|
+
result: CriterionResult | null;
|
|
30
|
+
}
|
|
31
|
+
/** Parsed rubric document */
|
|
32
|
+
export interface RubricDocument {
|
|
33
|
+
incrementId: string;
|
|
34
|
+
title: string;
|
|
35
|
+
generated: string;
|
|
36
|
+
source: string;
|
|
37
|
+
version: string;
|
|
38
|
+
status: string;
|
|
39
|
+
criteria: RubricCriterion[];
|
|
40
|
+
}
|
|
41
|
+
/** A single layer in the three-tier inheritance chain */
|
|
42
|
+
export interface RubricLayer {
|
|
43
|
+
path: string;
|
|
44
|
+
criteria: RubricCriterion[];
|
|
45
|
+
}
|
|
46
|
+
/** Error thrown when rubric markdown is malformed */
|
|
47
|
+
export declare class RubricParseError extends Error {
|
|
48
|
+
readonly lineNumber: number;
|
|
49
|
+
readonly rawLine: string;
|
|
50
|
+
constructor(message: string, lineNumber: number, rawLine: string);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Summary of rubric evaluation results.
|
|
54
|
+
*
|
|
55
|
+
* For blocking criteria: `failed` = explicit fail status, `unevaluated` = skip or null (pending).
|
|
56
|
+
* Both `failed > 0` and `unevaluated > 0` produce verdict FAIL.
|
|
57
|
+
* For advisory criteria: only explicit fail counts, skip/null are ignored.
|
|
58
|
+
*/
|
|
59
|
+
export interface RubricSummary {
|
|
60
|
+
total: number;
|
|
61
|
+
blocking: {
|
|
62
|
+
total: number;
|
|
63
|
+
passed: number;
|
|
64
|
+
failed: number;
|
|
65
|
+
unevaluated: number;
|
|
66
|
+
};
|
|
67
|
+
advisory: {
|
|
68
|
+
total: number;
|
|
69
|
+
passed: number;
|
|
70
|
+
failed: number;
|
|
71
|
+
};
|
|
72
|
+
verdict: 'PASS' | 'FAIL';
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/rubric/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,+EAA+E;AAC/E,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,UAAU,CAAC;AAErD,yCAAyC;AACzC,MAAM,MAAM,WAAW,GACnB,UAAU,GACV,kBAAkB,GAClB,cAAc,GACd,UAAU,GACV,QAAQ,CAAC;AAEb,iCAAiC;AACjC,MAAM,MAAM,cAAc,GACtB,wBAAwB,GACxB,eAAe,GACf,cAAc,GACd,UAAU,GACV,aAAa,GACb,eAAe,GACf,wBAAwB,CAAC;AAE7B,8CAA8C;AAC9C,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,gCAAgC;AAChC,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,cAAc,CAAC;IACzB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,WAAW,CAAC;IACvB,QAAQ,EAAE,cAAc,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;CAChC;AAED,6BAA6B;AAC7B,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,qDAAqD;AACrD,qBAAa,gBAAiB,SAAQ,KAAK;aAGvB,UAAU,EAAE,MAAM;aAClB,OAAO,EAAE,MAAM;gBAF/B,OAAO,EAAE,MAAM,EACC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM;CAKlC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IACjF,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rubric Quality Contracts — Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* A rubric is a per-increment quality contract that defines pass/fail criteria
|
|
5
|
+
* for closure gates. Auto-generated from spec.md ACs, customizable by the user.
|
|
6
|
+
*/
|
|
7
|
+
/** Error thrown when rubric markdown is malformed */
|
|
8
|
+
export class RubricParseError extends Error {
|
|
9
|
+
constructor(message, lineNumber, rawLine) {
|
|
10
|
+
super(`Rubric parse error at line ${lineNumber}: ${message}`);
|
|
11
|
+
this.lineNumber = lineNumber;
|
|
12
|
+
this.rawLine = rawLine;
|
|
13
|
+
this.name = 'RubricParseError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/core/rubric/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA4DH,qDAAqD;AACrD,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YACE,OAAe,EACC,UAAkB,EAClB,OAAe;QAE/B,KAAK,CAAC,8BAA8B,UAAU,KAAK,OAAO,EAAE,CAAC,CAAC;QAH9C,eAAU,GAAV,UAAU,CAAQ;QAClB,YAAO,GAAP,OAAO,CAAQ;QAG/B,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specweave",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.573",
|
|
4
4
|
"description": "100+ domain-expert AI skills — PM, Architect, Frontend, QA, Security and more. Skills learn your team's patterns permanently. Spec-first planning, autonomous execution, multi-agent teams, synced to GitHub/JIRA. Claude Code, Cursor, Copilot & more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"LICENSE"
|
|
92
92
|
],
|
|
93
93
|
"dependencies": {
|
|
94
|
-
"@anthropic-ai/sdk": "^0.
|
|
94
|
+
"@anthropic-ai/sdk": "^0.88.0",
|
|
95
95
|
"@inquirer/prompts": "^8.1.0",
|
|
96
96
|
"@octokit/rest": "^22.0.1",
|
|
97
97
|
"@types/handlebars": "^4.0.40",
|
|
@@ -52,6 +52,28 @@ Every task MUST have a `**Test**:` block. No exceptions.
|
|
|
52
52
|
- Group tasks by user story — one section per US
|
|
53
53
|
- Coverage targets: unit 95%, integration 90%, E2E 100% of AC scenarios
|
|
54
54
|
|
|
55
|
+
## Step: Generate rubric.md
|
|
56
|
+
|
|
57
|
+
After writing tasks.md, generate the quality contract rubric for this increment:
|
|
58
|
+
|
|
59
|
+
1. Read spec.md to extract all acceptance criteria (AC-IDs)
|
|
60
|
+
2. Generate `rubric.md` with:
|
|
61
|
+
- One criterion per AC (functional correctness category, evaluator: sw:grill)
|
|
62
|
+
- Standard infrastructure criteria (test coverage, code quality, independent evaluation)
|
|
63
|
+
- All criteria start as `[ ] PENDING`
|
|
64
|
+
3. Write rubric.md to the increment directory
|
|
65
|
+
4. Inform the user: "rubric.md has been generated. Review and customize criteria (change severity, add/remove criteria) before implementation begins."
|
|
66
|
+
|
|
67
|
+
The rubric uses this format per criterion:
|
|
68
|
+
```
|
|
69
|
+
### R-001: Title [blocking]
|
|
70
|
+
- **Source**: AC-US1-01
|
|
71
|
+
- **Evaluator**: sw:grill
|
|
72
|
+
- **Verify**: Description
|
|
73
|
+
- **Threshold**: Pass condition
|
|
74
|
+
- **Result**: [ ] PENDING
|
|
75
|
+
```
|
|
76
|
+
|
|
55
77
|
## Critical Reminders
|
|
56
78
|
|
|
57
79
|
- **ONE user story per response** — never generate all tasks at once (prevents crashes)
|