@planu/cli 5.7.5 → 5.7.7
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/CHANGELOG.md +88 -0
- package/README.md +3 -3
- package/dist/.planu-build.json +1 -1
- package/dist/cli/commands/package-handoff.js +30 -5
- package/dist/config/compliance-profiles.json +36 -3
- package/dist/config/environment-schema.json +21 -0
- package/dist/config/hook-templates/planu-review-panel-trigger.sh +2 -3
- package/dist/config/hook-templates/planu-session-safeguard.sh +22 -15
- package/dist/config/official-sdd-tools.d.ts +1 -1
- package/dist/config/official-sdd-tools.js +11 -24
- package/dist/config/registries/hosts/codex.json +14 -30
- package/dist/config/registries/hosts/opencode.json +1 -6
- package/dist/config/server-instructions.js +0 -21
- package/dist/config/skill-templates/planu-context-assets.md +3 -6
- package/dist/config/skill-templates/planu-implement.md +2 -2
- package/dist/config/skill-templates/planu-multi-teammate-review.md +8 -12
- package/dist/config/skill-templates/planu-release.md +2 -7
- package/dist/config/skill-templates/planu-validate.md +3 -3
- package/dist/config/spec-templates/crud-rest-api/template.json +5 -1
- package/dist/config/spec-templates/file-upload-s3/template.json +5 -1
- package/dist/config/spec-templates/stripe-payments/template.json +5 -1
- package/dist/config/spec-templates/webhook-system/template.json +5 -1
- package/dist/config/subagent-templates/planu-challenger.md +4 -9
- package/dist/config/subagent-templates/planu-spec-implementer.md +2 -4
- package/dist/config/subagent-templates/planu-validator.md +5 -8
- package/dist/config/tool-groups.json +6 -144
- package/dist/engine/ambiguity-scorer.js +40 -1
- package/dist/engine/autopilot/bootstrap.js +1 -1
- package/dist/engine/code-impact-analyzer.js +2 -6
- package/dist/engine/context-orchestrator/index.js +2 -2
- package/dist/engine/convention-scanner/codebase-scanner.js +2 -2
- package/dist/engine/dogfooding/runtime-gap-detector.js +2 -2
- package/dist/engine/evidence-gates/artifact-reader.d.ts +1 -0
- package/dist/engine/evidence-gates/artifact-reader.js +1 -1
- package/dist/engine/evidence-gates/evidence-autofill.d.ts +13 -5
- package/dist/engine/evidence-gates/evidence-autofill.js +106 -43
- package/dist/engine/evidence-gates/evidence-skeletons.js +3 -1
- package/dist/engine/evidence-gates/lifecycle-gate.js +7 -4
- package/dist/engine/evidence-index/index-builder.js +21 -4
- package/dist/engine/execution/job-runtime.js +1 -1
- package/dist/engine/execution/package-handoff-job-executor.d.ts +22 -0
- package/dist/engine/execution/package-handoff-job-executor.js +490 -0
- package/dist/engine/execution/validate-job-executor.js +20 -8
- package/dist/engine/git/exec-git.d.ts +4 -0
- package/dist/engine/git/exec-git.js +14 -0
- package/dist/engine/handoff-artifacts/implementation-review-reader.js +15 -2
- package/dist/engine/handoff-artifacts/schemas.d.ts +2 -0
- package/dist/engine/handoff-artifacts/schemas.js +1 -0
- package/dist/engine/handoff-format.d.ts +36 -0
- package/dist/engine/handoff-format.js +314 -0
- package/dist/engine/handoff-packager.d.ts +5 -3
- package/dist/engine/handoff-packager.js +89 -32
- package/dist/engine/host-tool-filter.js +20 -49
- package/dist/engine/human-summary.js +14 -3
- package/dist/engine/implementation-contract/common.d.ts +8 -3
- package/dist/engine/implementation-contract/common.js +9 -6
- package/dist/engine/implementation-contract/evaluator.js +4 -6
- package/dist/engine/implementation-contract/renderer.js +18 -23
- package/dist/engine/project-health-checker.js +2 -2
- package/dist/engine/readiness-checker.js +43 -5
- package/dist/engine/reconcile/apply-changes.js +4 -0
- package/dist/engine/reconcile/propagate-mirrors.d.ts +3 -0
- package/dist/engine/reconcile/propagate-mirrors.js +311 -0
- package/dist/engine/self-healing/healer.js +12 -5
- package/dist/engine/self-healing/strategies/lint-fix.d.ts +1 -1
- package/dist/engine/self-healing/strategies/lint-fix.js +4 -1
- package/dist/engine/self-healing/strategies/test-fix.d.ts +4 -3
- package/dist/engine/self-healing/strategies/test-fix.js +32 -14
- package/dist/engine/self-healing/strategies/typescript-fix.d.ts +1 -1
- package/dist/engine/self-healing/strategies/typescript-fix.js +28 -10
- package/dist/engine/session/checkpoint-writer.js +8 -4
- package/dist/engine/session/session-tracker.d.ts +5 -16
- package/dist/engine/session/session-tracker.js +29 -26
- package/dist/engine/session-state/writer.js +3 -11
- package/dist/engine/skill-generator/workflow-skill-generator.d.ts +4 -2
- package/dist/engine/skill-generator/workflow-skill-generator.js +113 -38
- package/dist/engine/spec-format/bdd-parser.d.ts +1 -11
- package/dist/engine/spec-format/bdd-parser.js +40 -11
- package/dist/engine/spec-format/lean-spec-generator.js +32 -12
- package/dist/engine/spec-format/retired-scaffold.d.ts +3 -0
- package/dist/engine/spec-format/retired-scaffold.js +5 -0
- package/dist/engine/spec-format/technical-md-populator.d.ts +1 -0
- package/dist/engine/spec-format/technical-md-populator.js +27 -0
- package/dist/engine/spec-format/unified-spec-builder.d.ts +1 -0
- package/dist/engine/spec-format/unified-spec-builder.js +30 -20
- package/dist/engine/spec-grounding/contract.d.ts +3 -3
- package/dist/engine/spec-grounding/contract.js +65 -4
- package/dist/engine/spec-grounding/grounding-entry-parser.d.ts +5 -0
- package/dist/engine/spec-grounding/grounding-entry-parser.js +185 -0
- package/dist/engine/spec-quality/generic-output-gate.js +37 -3
- package/dist/engine/technical-enricher/index.js +4 -1
- package/dist/engine/type-safety-gate.js +3 -11
- package/dist/engine/universal-rules/catalog.js +0 -2
- package/dist/engine/universal-rules/rules/planu-workflow.js +1 -1
- package/dist/engine/validation/durable-validation.d.ts +3 -2
- package/dist/engine/validation/durable-validation.js +106 -40
- package/dist/engine/validation/validation-freshness.d.ts +3 -2
- package/dist/engine/validation/validation-freshness.js +22 -16
- package/dist/engine/validation/validation-worktree.js +34 -0
- package/dist/hosts/claude-code/ux/mcp-prompts.js +8 -8
- package/dist/index.js +3 -17
- package/dist/resources/process.js +38 -65
- package/dist/storage/session-state-store.js +2 -0
- package/dist/storage/spec-store.js +8 -1
- package/dist/tools/bump-spec-version.js +5 -5
- package/dist/tools/challenge-spec/scenarios-utils.js +2 -2
- package/dist/tools/configure-checkpoint-policy.js +3 -3
- package/dist/tools/create-spec-helpers.js +2 -5
- package/dist/tools/create-spec.js +21 -4
- package/dist/tools/facilitate.js +3 -6
- package/dist/tools/git/branch-ops.js +16 -4
- package/dist/tools/github-release-handler.js +1 -20
- package/dist/tools/init-project/agents-md-writer.js +6 -6
- package/dist/tools/init-project/claude-md-generator.d.ts +0 -1
- package/dist/tools/init-project/claude-md-generator.js +0 -35
- package/dist/tools/init-project/handler.js +7 -5
- package/dist/tools/init-project/per-client-files-writer.js +5 -5
- package/dist/tools/init-project/planu-workflow-generator.js +2 -13
- package/dist/tools/init-project/portable-index-reconciler.d.ts +2 -1
- package/dist/tools/init-project/portable-index-reconciler.js +7 -2
- package/dist/tools/init-project/rules-generator.d.ts +0 -11
- package/dist/tools/init-project/rules-generator.js +4 -85
- package/dist/tools/init-project/scaffold-writer.d.ts +0 -1
- package/dist/tools/init-project/scaffold-writer.js +1 -12
- package/dist/tools/init-project/skills-multi-teammate-review-writer.js +1 -1
- package/dist/tools/jobs/handlers.d.ts +0 -6
- package/dist/tools/jobs/handlers.js +13 -40
- package/dist/tools/list-specs.js +6 -21
- package/dist/tools/multi-teammate-review.js +1 -14
- package/dist/tools/package-handoff.d.ts +3 -2
- package/dist/tools/package-handoff.js +153 -427
- package/dist/tools/reconcile-session-safeguard-hook.js +22 -15
- package/dist/tools/register-platform-tools/design-stack-tools.js +3 -3
- package/dist/tools/register-sdd-tools.js +0 -23
- package/dist/tools/register-spec-tools/analysis-tools.js +22 -9
- package/dist/tools/register-spec-tools/core-spec-tools.js +115 -70
- package/dist/tools/schemas/index.d.ts +1 -1
- package/dist/tools/schemas/index.js +1 -1
- package/dist/tools/schemas/output-schemas.d.ts +1 -18
- package/dist/tools/schemas/output-schemas.js +1 -11
- package/dist/tools/schemas/package-handoff-output-schema.d.ts +40 -0
- package/dist/tools/schemas/package-handoff-output-schema.js +68 -0
- package/dist/tools/schemas/validate-output-schema.d.ts +1 -2
- package/dist/tools/schemas/validate-output-schema.js +3 -2
- package/dist/tools/session-checkpoint.js +2 -2
- package/dist/tools/skill-registry/index.d.ts +0 -1
- package/dist/tools/skill-registry/index.js +0 -1
- package/dist/tools/sync-spec-state-handler.d.ts +2 -2
- package/dist/tools/sync-spec-state-handler.js +23 -3
- package/dist/tools/tool-registry/core-tools.js +72 -164
- package/dist/tools/tool-registry/group-infra.js +2 -39
- package/dist/tools/tool-registry/group-quality-compliance.js +20 -15
- package/dist/tools/tool-registry-helpers.js +6 -2
- package/dist/tools/update-status/batch.js +17 -0
- package/dist/tools/update-status/dod-gates.js +14 -1
- package/dist/tools/update-status/done-receipt-verifier.d.ts +22 -0
- package/dist/tools/update-status/done-receipt-verifier.js +56 -26
- package/dist/tools/update-status/evidence-gate.js +10 -21
- package/dist/tools/update-status/file-sync.d.ts +1 -0
- package/dist/tools/update-status/file-sync.js +6 -0
- package/dist/tools/update-status/index.d.ts +6 -0
- package/dist/tools/update-status/index.js +39 -44
- package/dist/tools/update-status-actions.js +2 -7
- package/dist/tools/validate.js +34 -28
- package/dist/transports/oauth-validator.js +12 -1
- package/dist/transports/transport-factory.d.ts +2 -1
- package/dist/transports/transport-factory.js +19 -0
- package/dist/types/common/primitives.d.ts +2 -0
- package/dist/types/durable-job.d.ts +45 -0
- package/dist/types/durable-validation.d.ts +1 -1
- package/dist/types/evidence-autofill.d.ts +13 -1
- package/dist/types/handoff-artifacts.d.ts +2 -1
- package/dist/types/readiness.d.ts +8 -1
- package/dist/types/reconcile.d.ts +6 -0
- package/dist/types/skill-registry.d.ts +26 -1
- package/dist/types/spec/core.d.ts +5 -0
- package/dist/types/spec/inputs.d.ts +2 -2
- package/dist/types/spec-format.d.ts +11 -1
- package/dist/types/spec-grounding.d.ts +26 -0
- package/dist/types/transport.d.ts +1 -0
- package/dist/types/validation-receipt.d.ts +27 -0
- package/package.json +3 -2
- package/planu-plugin.json +13 -26
- package/src/i18n/messages/en.json +1 -1
- package/src/i18n/messages/es.json +1 -1
- package/src/i18n/messages/pt.json +1 -1
- package/dist/engine/skill-generator/conventions-hasher.d.ts +0 -7
- package/dist/engine/skill-generator/conventions-hasher.js +0 -24
- package/dist/engine/universal-rules/rules/agent-teams.d.ts +0 -3
- package/dist/engine/universal-rules/rules/agent-teams.js +0 -63
- package/dist/tools/clarify-requirements/multiple-choice.d.ts +0 -30
- package/dist/tools/clarify-requirements/multiple-choice.js +0 -306
- package/dist/tools/clarify-requirements/questions-context.d.ts +0 -9
- package/dist/tools/clarify-requirements/questions-context.js +0 -74
- package/dist/tools/clarify-requirements/questions.d.ts +0 -16
- package/dist/tools/clarify-requirements/questions.js +0 -96
- package/dist/tools/clarify-requirements.d.ts +0 -4
- package/dist/tools/clarify-requirements.js +0 -314
- package/dist/tools/code-graph-handler.d.ts +0 -6
- package/dist/tools/code-graph-handler.js +0 -72
- package/dist/tools/create-rule.d.ts +0 -4
- package/dist/tools/create-rule.js +0 -107
- package/dist/tools/feedback-handler.d.ts +0 -7
- package/dist/tools/feedback-handler.js +0 -150
- package/dist/tools/semantic-search-handler.d.ts +0 -7
- package/dist/tools/semantic-search-handler.js +0 -71
- package/dist/tools/skill-registry/search.d.ts +0 -11
- package/dist/tools/skill-registry/search.js +0 -159
- package/dist/tools/tool-registry/deprecated-stubs.d.ts +0 -19
- package/dist/tools/tool-registry/deprecated-stubs.js +0 -65
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
// engine/spec-format/bdd-parser.ts — BDD/Gherkin parser and emitter (SPEC-481)
|
|
2
|
+
import { consumePathRunPreservingCase } from './technical-md-populator.js';
|
|
3
|
+
const TEST_MARKER_RE = /\bTESTS?\s*:/gi;
|
|
4
|
+
export function extractInlineTestPaths(text) {
|
|
5
|
+
const paths = [];
|
|
6
|
+
const seen = new Set();
|
|
7
|
+
for (const marker of text.matchAll(TEST_MARKER_RE)) {
|
|
8
|
+
const start = marker.index + marker[0].length;
|
|
9
|
+
for (const path of consumePathRunPreservingCase(text.slice(start))) {
|
|
10
|
+
if (!seen.has(path)) {
|
|
11
|
+
seen.add(path);
|
|
12
|
+
paths.push(path);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return paths;
|
|
17
|
+
}
|
|
2
18
|
const KEYWORD_MAP = {
|
|
3
19
|
given: 'given',
|
|
4
20
|
when: 'when',
|
|
@@ -17,22 +33,37 @@ const KEYWORD_MAP = {
|
|
|
17
33
|
* And ...
|
|
18
34
|
* But ...
|
|
19
35
|
*/
|
|
36
|
+
const THEN_WORD_RE = /\bTHEN\b/i;
|
|
37
|
+
const WHEN_WORD_RE = /\bWHEN\b/i;
|
|
38
|
+
function hasThenSignal(scenario) {
|
|
39
|
+
return scenario.steps.some((step) => step.keyword === 'then' || THEN_WORD_RE.test(step.text));
|
|
40
|
+
}
|
|
41
|
+
function isSingleLineGivenWhenThenText(text) {
|
|
42
|
+
return WHEN_WORD_RE.test(text) && THEN_WORD_RE.test(text);
|
|
43
|
+
}
|
|
44
|
+
function opensHeaderlessScenario(rawKeyword, text) {
|
|
45
|
+
return rawKeyword === 'given' || isSingleLineGivenWhenThenText(text);
|
|
46
|
+
}
|
|
20
47
|
export function parseBddScenarios(markdown) {
|
|
21
48
|
const scenarios = [];
|
|
22
49
|
const lines = markdown.split('\n');
|
|
23
50
|
let current = null;
|
|
24
51
|
// Tracks whether the current scenario was auto-created (no Scenario: header)
|
|
25
52
|
let currentIsHeaderless = false;
|
|
53
|
+
const closeCurrent = () => {
|
|
54
|
+
if (current && hasThenSignal(current)) {
|
|
55
|
+
scenarios.push(current);
|
|
56
|
+
}
|
|
57
|
+
current = null;
|
|
58
|
+
currentIsHeaderless = false;
|
|
59
|
+
};
|
|
26
60
|
for (const raw of lines) {
|
|
27
61
|
const line = raw.trim();
|
|
28
62
|
// Detect scenario header
|
|
29
63
|
const scenarioMatch = /^scenario:\s*(.+)$/i.exec(line);
|
|
30
64
|
if (scenarioMatch) {
|
|
31
|
-
|
|
32
|
-
scenarios.push(current);
|
|
33
|
-
}
|
|
65
|
+
closeCurrent();
|
|
34
66
|
current = { title: (scenarioMatch[1] ?? '').trim(), steps: [], done: false };
|
|
35
|
-
currentIsHeaderless = false;
|
|
36
67
|
continue;
|
|
37
68
|
}
|
|
38
69
|
// Detect step keywords
|
|
@@ -47,12 +78,12 @@ export function parseBddScenarios(markdown) {
|
|
|
47
78
|
// steps. This handles both multiple headerless blocks and headerless blocks following
|
|
48
79
|
// an explicit Scenario: header block.
|
|
49
80
|
if (rawKeyword === 'given' && current !== null && current.steps.length > 0) {
|
|
50
|
-
|
|
51
|
-
current = null;
|
|
52
|
-
currentIsHeaderless = false;
|
|
81
|
+
closeCurrent();
|
|
53
82
|
}
|
|
54
|
-
// Auto-create a scenario when a step is found without a preceding Scenario header
|
|
55
83
|
if (!current) {
|
|
84
|
+
if (!opensHeaderlessScenario(rawKeyword, text)) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
56
87
|
current = { title: text, steps: [], done: false };
|
|
57
88
|
currentIsHeaderless = true;
|
|
58
89
|
}
|
|
@@ -62,9 +93,7 @@ export function parseBddScenarios(markdown) {
|
|
|
62
93
|
current.title = text;
|
|
63
94
|
}
|
|
64
95
|
}
|
|
65
|
-
|
|
66
|
-
scenarios.push(current);
|
|
67
|
-
}
|
|
96
|
+
closeCurrent();
|
|
68
97
|
return scenarios;
|
|
69
98
|
}
|
|
70
99
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// engine/spec-format/lean-spec-generator.ts — Generates lean spec.md with YAML frontmatter (SPEC-461)
|
|
2
2
|
// Output: ~30-50 lines. No generic sections, no OWASP, no STRIDE, no resilience criteria.
|
|
3
|
-
import { parseBddScenarios, renderBddScenariosYaml, convertCheckboxToBdd } from './bdd-parser.js';
|
|
3
|
+
import { parseBddScenarios, renderBddScenariosYaml, convertCheckboxToBdd, extractInlineTestPaths, } from './bdd-parser.js';
|
|
4
4
|
import { decideMetadataSerialization, } from './metadata-value-policy.js';
|
|
5
5
|
import { renderGroundingFrontmatter, renderTechnicalReferenceGroundingFrontmatter, } from '../spec-grounding/contract.js';
|
|
6
6
|
/** Strip a redundant spec-ID prefix from spec titles.
|
|
@@ -117,15 +117,21 @@ function buildBddLines(description, extraCriteria, criteriaOverride, scenarioTes
|
|
|
117
117
|
done: false,
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
|
+
appendUncoveredCheckboxCriteria(scenarios, description);
|
|
120
121
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
tests:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
122
|
+
scenarios = scenarios.map((scenario) => {
|
|
123
|
+
const inlinePaths = extractInlineTestPaths([scenario.title, ...scenario.steps.map((step) => step.text)].join('\n'));
|
|
124
|
+
if (inlinePaths.length > 0) {
|
|
125
|
+
return { ...scenario, tests: inlinePaths.map((path) => ({ path })) };
|
|
126
|
+
}
|
|
127
|
+
if (scenario.tests && scenario.tests.length > 0) {
|
|
128
|
+
return scenario;
|
|
129
|
+
}
|
|
130
|
+
if (scenarioTestPaths.length > 0) {
|
|
131
|
+
return { ...scenario, tests: scenarioTestPaths.map((path) => ({ path })) };
|
|
132
|
+
}
|
|
133
|
+
return scenario;
|
|
134
|
+
});
|
|
129
135
|
return renderBddScenariosYaml(scenarios);
|
|
130
136
|
}
|
|
131
137
|
/** Extract acceptance criteria explicitly present in the description.
|
|
@@ -148,7 +154,7 @@ export function extractCriteria(description) {
|
|
|
148
154
|
/** Extract checkbox-style criteria: `- [ ] text` or `- [x] text`. */
|
|
149
155
|
function extractCheckboxCriteria(description) {
|
|
150
156
|
const criteria = [];
|
|
151
|
-
const checkboxRegex = /^[
|
|
157
|
+
const checkboxRegex = /^[-*+]\s*\[([ xX])\]\s*(.+)$/gm;
|
|
152
158
|
let match = checkboxRegex.exec(description);
|
|
153
159
|
while (match) {
|
|
154
160
|
criteria.push({
|
|
@@ -166,7 +172,7 @@ function extractCheckboxCriteria(description) {
|
|
|
166
172
|
function extractGivenWhenThenCriteria(description) {
|
|
167
173
|
const criteria = [];
|
|
168
174
|
// Single-line: GIVEN ... WHEN ... THEN ...
|
|
169
|
-
const singleLineRegex = /^[ \t]*(?:[
|
|
175
|
+
const singleLineRegex = /^[ \t]*(?:[-*+>]\s*|\d+[.)]\s*)?GIVEN\s+.+\s+WHEN\s+.+\s+THEN\s+(.+)$/gim;
|
|
170
176
|
let match = singleLineRegex.exec(description);
|
|
171
177
|
while (match) {
|
|
172
178
|
const thenText = match[1]?.trim() ?? '';
|
|
@@ -199,7 +205,7 @@ function extractPlainListCriteria(description) {
|
|
|
199
205
|
}
|
|
200
206
|
const criteria = [];
|
|
201
207
|
// Plain list: `- text` or `* text`, not followed by `[`
|
|
202
|
-
const plainListRegex = /^[
|
|
208
|
+
const plainListRegex = /^[-*+]\s+(?!\[)(.+)$/gm;
|
|
203
209
|
let match = plainListRegex.exec(acceptanceSection);
|
|
204
210
|
while (match) {
|
|
205
211
|
const text = match[1]?.trim() ?? '';
|
|
@@ -214,4 +220,18 @@ function extractPlainListCriteria(description) {
|
|
|
214
220
|
function escapeYaml(value) {
|
|
215
221
|
return value.replace(/"/g, '\\"');
|
|
216
222
|
}
|
|
223
|
+
function normalizeForDedupe(value) {
|
|
224
|
+
return value.trim().toLowerCase();
|
|
225
|
+
}
|
|
226
|
+
function appendUncoveredCheckboxCriteria(scenarios, description) {
|
|
227
|
+
const existingTexts = new Set(scenarios.flatMap((scenario) => [
|
|
228
|
+
normalizeForDedupe(scenario.title),
|
|
229
|
+
...scenario.steps.map((step) => normalizeForDedupe(step.text)),
|
|
230
|
+
]));
|
|
231
|
+
for (const criterion of extractCheckboxCriteria(description)) {
|
|
232
|
+
if (!existingTexts.has(normalizeForDedupe(criterion.text))) {
|
|
233
|
+
scenarios.push(...convertCheckboxToBdd([criterion]));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
217
237
|
//# sourceMappingURL=lean-spec-generator.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const RETIRED_SCAFFOLD_PREFIX = 'GIVEN the requested change is implemented WHEN the behavior is exercised';
|
|
2
|
+
export function isRetiredScaffoldText(text) {
|
|
3
|
+
return text.toLowerCase().includes(RETIRED_SCAFFOLD_PREFIX.toLowerCase());
|
|
4
|
+
}
|
|
5
|
+
//# sourceMappingURL=retired-scaffold.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { LeanFileEntry } from '../../types/index.js';
|
|
2
|
+
export declare function consumePathRunPreservingCase(remainder: string): string[];
|
|
2
3
|
/**
|
|
3
4
|
* Extract the path list declared immediately after each FILES:/FILE:/TEST:/TESTS: marker.
|
|
4
5
|
* Returns null when no such marker is present (caller falls back to whole-text grounding).
|
|
@@ -137,6 +137,33 @@ function consumePathRun(remainder) {
|
|
|
137
137
|
}
|
|
138
138
|
return paths;
|
|
139
139
|
}
|
|
140
|
+
export function consumePathRunPreservingCase(remainder) {
|
|
141
|
+
const paths = [];
|
|
142
|
+
const seen = new Set();
|
|
143
|
+
let cursor = remainder;
|
|
144
|
+
for (;;) {
|
|
145
|
+
cursor = cursor.replace(PATH_SEPARATOR_RE, '');
|
|
146
|
+
const tokenMatch = PATH_TOKEN_RE.exec(cursor);
|
|
147
|
+
if (tokenMatch?.[1] === undefined) {
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
const raw = tokenMatch[1];
|
|
151
|
+
const endsWithTerminator = /[.;:]+$/.test(raw);
|
|
152
|
+
const trimmed = raw.replace(TRAILING_PUNCTUATION_RE, '');
|
|
153
|
+
if (!isPathShaped(trimmed)) {
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
if (!seen.has(trimmed)) {
|
|
157
|
+
seen.add(trimmed);
|
|
158
|
+
paths.push(trimmed);
|
|
159
|
+
}
|
|
160
|
+
if (endsWithTerminator) {
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
cursor = cursor.slice(tokenMatch[0].length);
|
|
164
|
+
}
|
|
165
|
+
return paths;
|
|
166
|
+
}
|
|
140
167
|
/**
|
|
141
168
|
* Extract the path list declared immediately after each FILES:/FILE:/TEST:/TESTS: marker.
|
|
142
169
|
* Returns null when no such marker is present (caller falls back to whole-text grounding).
|
|
@@ -21,4 +21,5 @@ export declare function containsUnresolvedContractPlaceholder(rawText: string, o
|
|
|
21
21
|
export declare function validateUnifiedSpecCandidate(candidate: string, options?: {
|
|
22
22
|
groundedFilePaths?: Iterable<string>;
|
|
23
23
|
}): UnifiedSpecCandidateValidation;
|
|
24
|
+
export declare function maskFencedAndQuotedText(body: string): string;
|
|
24
25
|
//# sourceMappingURL=unified-spec-builder.d.ts.map
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// two-file spec.md + technical.md pair only to be merged on the next list_specs run.
|
|
4
4
|
import { stripFrontmatter } from '../frontmatter-parser.js';
|
|
5
5
|
import { extractAcceptanceCriteriaTexts } from './acceptance-criteria.js';
|
|
6
|
+
import { isRetiredScaffoldText } from './retired-scaffold.js';
|
|
6
7
|
import { escapeRegex } from '../../core/shared/strings.js';
|
|
7
8
|
const REQUIRED_SECTIONS = [
|
|
8
9
|
'Problem',
|
|
@@ -24,7 +25,7 @@ export function buildCanonicalUnifiedSpecContent(input) {
|
|
|
24
25
|
const goal = firstNonEmpty(explicitGoal, input.criteria[0]?.text, 'Needs decision: define the goal this spec must achieve.');
|
|
25
26
|
const explicitTechnical = extractTopLevelSectionBody(source, 'Technical');
|
|
26
27
|
const generatedNotes = extractTopLevelSectionBody(input.technicalBody, 'Implementation Notes');
|
|
27
|
-
const technical = firstNonEmpty(explicitTechnical, generatedNotes,
|
|
28
|
+
const technical = firstNonEmpty(explicitTechnical, generatedNotes, groundedTechnicalFallback(input.files, input.criteria));
|
|
28
29
|
const acceptance = extractExecutableCriteria(source, input.criteria);
|
|
29
30
|
const explicitOutOfScope = extractTopLevelSectionBody(source, 'Out of scope');
|
|
30
31
|
const outOfScope = input.outOfScope.length > 0
|
|
@@ -224,8 +225,12 @@ export function validateUnifiedSpecCandidate(candidate, options = {}) {
|
|
|
224
225
|
});
|
|
225
226
|
}
|
|
226
227
|
const acceptanceSection = extractTopLevelSectionBody(scannable, 'Acceptance Criteria');
|
|
227
|
-
|
|
228
|
-
|
|
228
|
+
const acceptanceSectionForBddCheck = acceptanceSection
|
|
229
|
+
?.split('\n')
|
|
230
|
+
.filter((line) => !isRetiredScaffoldText(line))
|
|
231
|
+
.join('\n');
|
|
232
|
+
if (!acceptanceSectionForBddCheck ||
|
|
233
|
+
!/\bGIVEN\b[\s\S]*\bWHEN\b[\s\S]*\bTHEN\b/i.test(acceptanceSectionForBddCheck)) {
|
|
229
234
|
issues.push({
|
|
230
235
|
code: 'MISSING_EXECUTABLE_CRITERIA',
|
|
231
236
|
section: 'Acceptance Criteria',
|
|
@@ -248,7 +253,7 @@ export function validateUnifiedSpecCandidate(candidate, options = {}) {
|
|
|
248
253
|
}
|
|
249
254
|
return { valid: issues.length === 0, issues };
|
|
250
255
|
}
|
|
251
|
-
function maskFencedAndQuotedText(body) {
|
|
256
|
+
export function maskFencedAndQuotedText(body) {
|
|
252
257
|
const lines = body.split('\n');
|
|
253
258
|
let fence = null;
|
|
254
259
|
return lines
|
|
@@ -305,27 +310,18 @@ function firstNonEmpty(...values) {
|
|
|
305
310
|
return (values.find((value) => value !== undefined && value !== null && value.length > 0) ?? '');
|
|
306
311
|
}
|
|
307
312
|
function fallbackCriteriaBullets(criteria) {
|
|
308
|
-
return criteria
|
|
309
|
-
.map((criterion) => `- GIVEN the requested change is implemented WHEN the behavior is exercised THEN ${criterion.text}`)
|
|
310
|
-
.join('\n');
|
|
313
|
+
return criteria.map((criterion) => `- ${criterion.text}`).join('\n');
|
|
311
314
|
}
|
|
312
|
-
// Same BDD step vocabulary `acceptance-criteria.ts` recognizes (English + Spanish).
|
|
313
|
-
// A criterion already starting with one of these is a genuine BDD fragment
|
|
314
|
-
// (a full GIVEN/WHEN/THEN block, or an AND continuation of one) — leave it as-is.
|
|
315
|
-
const BDD_STEP_START = /^(GIVEN|WHEN|THEN|AND|DADO|CUANDO|ENTONCES|Y)\b/i;
|
|
316
315
|
/**
|
|
317
|
-
* Render one real criterion as
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
316
|
+
* Render one real criterion as a bullet. Criteria already written in BDD form
|
|
317
|
+
* pass through untouched; plain-text criteria (e.g. checkbox items) render as
|
|
318
|
+
* plain bullets — SPEC-1435 retires the constant GIVEN/WHEN/THEN scaffold that
|
|
319
|
+
* used to wrap them, since it satisfied `MISSING_EXECUTABLE_CRITERIA` without
|
|
320
|
+
* adding any spec-specific content.
|
|
322
321
|
*/
|
|
323
322
|
function toExecutableBullet(text) {
|
|
324
323
|
const cleaned = text.trim().replace(/[.\s]+$/, '.');
|
|
325
|
-
|
|
326
|
-
return `- ${cleaned}`;
|
|
327
|
-
}
|
|
328
|
-
return `- GIVEN the requested change is implemented WHEN the behavior is exercised THEN ${cleaned}`;
|
|
324
|
+
return `- ${cleaned}`;
|
|
329
325
|
}
|
|
330
326
|
/**
|
|
331
327
|
* Criteria resolve through the canonical parser in `acceptance-criteria.ts`
|
|
@@ -339,6 +335,20 @@ function extractExecutableCriteria(source, criteria) {
|
|
|
339
335
|
}
|
|
340
336
|
return parsed.map(toExecutableBullet).join('\n');
|
|
341
337
|
}
|
|
338
|
+
/**
|
|
339
|
+
* SPEC-1435: replaces the constant Technical fallback sentence. When neither an
|
|
340
|
+
* author-written `## Technical` body nor generated implementation notes exist, compose
|
|
341
|
+
* the section from the spec's own grounded material instead of a fixed sentence that
|
|
342
|
+
* never varies with the spec: the grounded file records when present, otherwise the
|
|
343
|
+
* criteria texts.
|
|
344
|
+
*/
|
|
345
|
+
function groundedTechnicalFallback(files, criteria) {
|
|
346
|
+
const groundedFiles = [...files.create, ...files.modify, ...files.test];
|
|
347
|
+
if (groundedFiles.length > 0) {
|
|
348
|
+
return groundedFiles.map((entry) => `- \`${entry.path}\``).join('\n');
|
|
349
|
+
}
|
|
350
|
+
return criteria.map((criterion) => `- ${criterion.text}`).join('\n');
|
|
351
|
+
}
|
|
342
352
|
function renderOwnedFiles(files) {
|
|
343
353
|
const sections = [];
|
|
344
354
|
for (const [title, entries] of [
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Spec } from '../../types/index.js';
|
|
2
|
-
import type { CriterionGroundingRecord, GroundingGateResult, TechnicalReferenceGroundingRecord } from '../../types/spec-grounding.js';
|
|
2
|
+
import type { CriterionGroundingRecord, GroundingGateResult, GroundingParseResult, TechnicalReferenceGroundingRecord } from '../../types/spec-grounding.js';
|
|
3
3
|
export { normalizeCriterionText } from '../criterion-identity.js';
|
|
4
4
|
export declare function isGenericCriterion(text: string): boolean;
|
|
5
5
|
export declare function filterGroundedCriteria(criteria: string[]): string[];
|
|
@@ -19,8 +19,8 @@ export declare function getAdvisoryCriteria(records: CriterionGroundingRecord[])
|
|
|
19
19
|
export declare function renderGroundingFrontmatter(records: CriterionGroundingRecord[]): string[];
|
|
20
20
|
export declare function renderTechnicalReferenceGroundingFrontmatter(records: TechnicalReferenceGroundingRecord[]): string[];
|
|
21
21
|
export declare function checkGroundedSpecContract(spec: Spec, content: string): GroundingGateResult;
|
|
22
|
-
export declare function parseCriterionGroundingRecords(frontmatter: string): CriterionGroundingRecord
|
|
23
|
-
export declare function parseTechnicalReferenceGroundingRecords(frontmatter: string): TechnicalReferenceGroundingRecord
|
|
22
|
+
export declare function parseCriterionGroundingRecords(frontmatter: string): GroundingParseResult<CriterionGroundingRecord>;
|
|
23
|
+
export declare function parseTechnicalReferenceGroundingRecords(frontmatter: string): GroundingParseResult<TechnicalReferenceGroundingRecord>;
|
|
24
24
|
export declare function parseApprovedFileLevelWorkPlanPaths(content: string): string[];
|
|
25
25
|
export declare function parseGroundedTechnicalPaths(content: string): string[];
|
|
26
26
|
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import YAML from 'yaml';
|
|
1
2
|
import { createCriterionIdentity, normalizeCriterionText } from '../criterion-identity.js';
|
|
3
|
+
import { isRecord, toCriterionGroundingRecord, toTechnicalReferenceGroundingRecord, } from './grounding-entry-parser.js';
|
|
2
4
|
export { normalizeCriterionText } from '../criterion-identity.js';
|
|
3
5
|
const GENERIC_CRITERION_PATTERNS = [
|
|
4
6
|
/\bimplementation complete\b/i,
|
|
@@ -102,7 +104,7 @@ export function renderTechnicalReferenceGroundingFrontmatter(records) {
|
|
|
102
104
|
export function checkGroundedSpecContract(spec, content) {
|
|
103
105
|
const frontmatter = FRONTMATTER_RE.exec(content)?.[1] ?? '';
|
|
104
106
|
const required = /^grounding_required:\s*true\s*$/m.test(frontmatter);
|
|
105
|
-
const records = parseCriterionGroundingRecords(frontmatter);
|
|
107
|
+
const records = parseCriterionGroundingRecords(frontmatter).records;
|
|
106
108
|
if (!required) {
|
|
107
109
|
return { passed: true, required: false, issues: [], records };
|
|
108
110
|
}
|
|
@@ -118,7 +120,7 @@ export function checkGroundedSpecContract(spec, content) {
|
|
|
118
120
|
issues.push(`Criterion has no grounding evidence: ${record.text}`);
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
|
-
const technicalRecords = parseTechnicalReferenceGroundingRecords(frontmatter);
|
|
123
|
+
const technicalRecords = parseTechnicalReferenceGroundingRecords(frontmatter).records;
|
|
122
124
|
for (const record of technicalRecords) {
|
|
123
125
|
if (record.source === 'ungrounded_advisory' || record.source === 'documented_assumption') {
|
|
124
126
|
issues.push(`Technical reference is advisory-only and cannot be contract: ${record.path}`);
|
|
@@ -135,6 +137,65 @@ export function checkGroundedSpecContract(spec, content) {
|
|
|
135
137
|
};
|
|
136
138
|
}
|
|
137
139
|
export function parseCriterionGroundingRecords(frontmatter) {
|
|
140
|
+
const outcome = parseFrontmatterYaml(frontmatter);
|
|
141
|
+
if (!outcome.parsed) {
|
|
142
|
+
return { records: parseCriterionGroundingRecordsLegacy(frontmatter), diagnostics: [] };
|
|
143
|
+
}
|
|
144
|
+
const grounding = readGroundingSection(outcome.document);
|
|
145
|
+
const entries = Array.isArray(grounding?.criteria) ? grounding.criteria : [];
|
|
146
|
+
const records = [];
|
|
147
|
+
const diagnostics = [];
|
|
148
|
+
for (const [index, entry] of entries.entries()) {
|
|
149
|
+
const entryOutcome = toCriterionGroundingRecord(entry, index);
|
|
150
|
+
if (entryOutcome.record) {
|
|
151
|
+
records.push(entryOutcome.record);
|
|
152
|
+
}
|
|
153
|
+
diagnostics.push(...entryOutcome.diagnostics);
|
|
154
|
+
}
|
|
155
|
+
return { records, diagnostics };
|
|
156
|
+
}
|
|
157
|
+
export function parseTechnicalReferenceGroundingRecords(frontmatter) {
|
|
158
|
+
const outcome = parseFrontmatterYaml(frontmatter);
|
|
159
|
+
if (!outcome.parsed) {
|
|
160
|
+
return {
|
|
161
|
+
records: parseTechnicalReferenceGroundingRecordsLegacy(frontmatter),
|
|
162
|
+
diagnostics: [],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const grounding = readGroundingSection(outcome.document);
|
|
166
|
+
const entries = grounding
|
|
167
|
+
? Array.isArray(grounding.technicalReferences)
|
|
168
|
+
? grounding.technicalReferences
|
|
169
|
+
: []
|
|
170
|
+
: Array.isArray(outcome.document?.technicalReferences)
|
|
171
|
+
? outcome.document.technicalReferences
|
|
172
|
+
: [];
|
|
173
|
+
const records = [];
|
|
174
|
+
const diagnostics = [];
|
|
175
|
+
for (const [index, entry] of entries.entries()) {
|
|
176
|
+
const entryOutcome = toTechnicalReferenceGroundingRecord(entry, index);
|
|
177
|
+
if (entryOutcome.record) {
|
|
178
|
+
records.push(entryOutcome.record);
|
|
179
|
+
}
|
|
180
|
+
diagnostics.push(...entryOutcome.diagnostics);
|
|
181
|
+
}
|
|
182
|
+
return { records, diagnostics };
|
|
183
|
+
}
|
|
184
|
+
function parseFrontmatterYaml(frontmatter) {
|
|
185
|
+
let value;
|
|
186
|
+
try {
|
|
187
|
+
value = YAML.parse(frontmatter);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return { parsed: false };
|
|
191
|
+
}
|
|
192
|
+
return { parsed: true, document: isRecord(value) ? value : undefined };
|
|
193
|
+
}
|
|
194
|
+
function readGroundingSection(document) {
|
|
195
|
+
const grounding = document?.grounding;
|
|
196
|
+
return isRecord(grounding) ? grounding : undefined;
|
|
197
|
+
}
|
|
198
|
+
function parseCriterionGroundingRecordsLegacy(frontmatter) {
|
|
138
199
|
const records = [];
|
|
139
200
|
const chunks = frontmatter.split(/\n\s{4}- text:\s*/).slice(1);
|
|
140
201
|
for (const chunk of chunks) {
|
|
@@ -154,7 +215,7 @@ export function parseCriterionGroundingRecords(frontmatter) {
|
|
|
154
215
|
}
|
|
155
216
|
return records;
|
|
156
217
|
}
|
|
157
|
-
|
|
218
|
+
function parseTechnicalReferenceGroundingRecordsLegacy(frontmatter) {
|
|
158
219
|
const records = [];
|
|
159
220
|
const chunks = frontmatter.split(/\n\s{4}- path:\s*/).slice(1);
|
|
160
221
|
for (const chunk of chunks) {
|
|
@@ -197,7 +258,7 @@ export function parseGroundedTechnicalPaths(content) {
|
|
|
197
258
|
const frontmatter = FRONTMATTER_RE.exec(content)?.[1] ?? '';
|
|
198
259
|
return [
|
|
199
260
|
...new Set([
|
|
200
|
-
...parseTechnicalReferenceGroundingRecords(frontmatter).map((record) => record.path),
|
|
261
|
+
...parseTechnicalReferenceGroundingRecords(frontmatter).records.map((record) => record.path),
|
|
201
262
|
...parseApprovedFileLevelWorkPlanPaths(content),
|
|
202
263
|
]),
|
|
203
264
|
];
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CriterionGroundingRecord, GroundingEntryOutcome, TechnicalReferenceGroundingRecord } from '../../types/spec-grounding.js';
|
|
2
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
3
|
+
export declare function toCriterionGroundingRecord(entry: unknown, index: number): GroundingEntryOutcome<CriterionGroundingRecord>;
|
|
4
|
+
export declare function toTechnicalReferenceGroundingRecord(entry: unknown, index: number): GroundingEntryOutcome<TechnicalReferenceGroundingRecord>;
|
|
5
|
+
//# sourceMappingURL=grounding-entry-parser.d.ts.map
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
const GROUNDING_SOURCES = [
|
|
2
|
+
'user_input',
|
|
3
|
+
'project_evidence',
|
|
4
|
+
'documented_assumption',
|
|
5
|
+
'ungrounded_advisory',
|
|
6
|
+
];
|
|
7
|
+
const GROUNDING_CONFIDENCES = ['low', 'medium', 'high'];
|
|
8
|
+
const TECHNICAL_REFERENCE_SECTIONS = [
|
|
9
|
+
'create',
|
|
10
|
+
'modify',
|
|
11
|
+
'test',
|
|
12
|
+
];
|
|
13
|
+
export function isRecord(value) {
|
|
14
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
15
|
+
}
|
|
16
|
+
function stringifyDiagnosticValue(value) {
|
|
17
|
+
if (typeof value === 'string') {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
if (value === undefined) {
|
|
21
|
+
return 'undefined';
|
|
22
|
+
}
|
|
23
|
+
if (value === null) {
|
|
24
|
+
return 'null';
|
|
25
|
+
}
|
|
26
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
27
|
+
return value.toString();
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return JSON.stringify(value);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return 'unstringifiable value';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function nonObjectEntryDiagnostic(entry, index) {
|
|
37
|
+
return {
|
|
38
|
+
identifier: `entry[${String(index)}]`,
|
|
39
|
+
field: 'entry',
|
|
40
|
+
value: stringifyDiagnosticValue(entry),
|
|
41
|
+
allowed: [],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function toGroundingSource(value) {
|
|
45
|
+
return typeof value === 'string' && GROUNDING_SOURCES.includes(value)
|
|
46
|
+
? value
|
|
47
|
+
: null;
|
|
48
|
+
}
|
|
49
|
+
function toGroundingConfidence(value) {
|
|
50
|
+
return typeof value === 'string' && GROUNDING_CONFIDENCES.includes(value)
|
|
51
|
+
? value
|
|
52
|
+
: null;
|
|
53
|
+
}
|
|
54
|
+
function toTechnicalReferenceSection(value) {
|
|
55
|
+
return typeof value === 'string' &&
|
|
56
|
+
TECHNICAL_REFERENCE_SECTIONS.includes(value)
|
|
57
|
+
? value
|
|
58
|
+
: null;
|
|
59
|
+
}
|
|
60
|
+
function toStringArrayWithDiagnostics(value, identifier) {
|
|
61
|
+
if (!Array.isArray(value)) {
|
|
62
|
+
return { evidence: [], diagnostics: [] };
|
|
63
|
+
}
|
|
64
|
+
const evidence = [];
|
|
65
|
+
const diagnostics = [];
|
|
66
|
+
for (const item of value) {
|
|
67
|
+
if (typeof item === 'string') {
|
|
68
|
+
evidence.push(item);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
diagnostics.push({
|
|
72
|
+
identifier,
|
|
73
|
+
field: 'evidence',
|
|
74
|
+
value: stringifyDiagnosticValue(item),
|
|
75
|
+
allowed: ['string'],
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { evidence, diagnostics };
|
|
80
|
+
}
|
|
81
|
+
export function toCriterionGroundingRecord(entry, index) {
|
|
82
|
+
if (!isRecord(entry)) {
|
|
83
|
+
return { record: null, diagnostics: [nonObjectEntryDiagnostic(entry, index)] };
|
|
84
|
+
}
|
|
85
|
+
const text = typeof entry.text === 'string' && entry.text.length > 0 ? entry.text : null;
|
|
86
|
+
const identifier = text ?? `entry[${String(index)}]`;
|
|
87
|
+
const source = toGroundingSource(entry.source);
|
|
88
|
+
if (!source) {
|
|
89
|
+
return {
|
|
90
|
+
record: null,
|
|
91
|
+
diagnostics: [
|
|
92
|
+
{
|
|
93
|
+
identifier,
|
|
94
|
+
field: 'source',
|
|
95
|
+
value: stringifyDiagnosticValue(entry.source),
|
|
96
|
+
allowed: GROUNDING_SOURCES,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const confidence = toGroundingConfidence(entry.confidence);
|
|
102
|
+
if (!confidence) {
|
|
103
|
+
return {
|
|
104
|
+
record: null,
|
|
105
|
+
diagnostics: [
|
|
106
|
+
{
|
|
107
|
+
identifier,
|
|
108
|
+
field: 'confidence',
|
|
109
|
+
value: stringifyDiagnosticValue(entry.confidence),
|
|
110
|
+
allowed: GROUNDING_CONFIDENCES,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (!text) {
|
|
116
|
+
return {
|
|
117
|
+
record: null,
|
|
118
|
+
diagnostics: [
|
|
119
|
+
{ identifier, field: 'text', value: stringifyDiagnosticValue(entry.text), allowed: [] },
|
|
120
|
+
],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const { evidence, diagnostics } = toStringArrayWithDiagnostics(entry.evidence, identifier);
|
|
124
|
+
return { record: { text, source, evidence, confidence }, diagnostics };
|
|
125
|
+
}
|
|
126
|
+
export function toTechnicalReferenceGroundingRecord(entry, index) {
|
|
127
|
+
if (!isRecord(entry)) {
|
|
128
|
+
return { record: null, diagnostics: [nonObjectEntryDiagnostic(entry, index)] };
|
|
129
|
+
}
|
|
130
|
+
const path = typeof entry.path === 'string' && entry.path.length > 0 ? entry.path : null;
|
|
131
|
+
const identifier = path ?? `entry[${String(index)}]`;
|
|
132
|
+
const section = toTechnicalReferenceSection(entry.section);
|
|
133
|
+
if (!section) {
|
|
134
|
+
return {
|
|
135
|
+
record: null,
|
|
136
|
+
diagnostics: [
|
|
137
|
+
{
|
|
138
|
+
identifier,
|
|
139
|
+
field: 'section',
|
|
140
|
+
value: stringifyDiagnosticValue(entry.section),
|
|
141
|
+
allowed: TECHNICAL_REFERENCE_SECTIONS,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const source = toGroundingSource(entry.source);
|
|
147
|
+
if (!source) {
|
|
148
|
+
return {
|
|
149
|
+
record: null,
|
|
150
|
+
diagnostics: [
|
|
151
|
+
{
|
|
152
|
+
identifier,
|
|
153
|
+
field: 'source',
|
|
154
|
+
value: stringifyDiagnosticValue(entry.source),
|
|
155
|
+
allowed: GROUNDING_SOURCES,
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const confidence = toGroundingConfidence(entry.confidence);
|
|
161
|
+
if (!confidence) {
|
|
162
|
+
return {
|
|
163
|
+
record: null,
|
|
164
|
+
diagnostics: [
|
|
165
|
+
{
|
|
166
|
+
identifier,
|
|
167
|
+
field: 'confidence',
|
|
168
|
+
value: stringifyDiagnosticValue(entry.confidence),
|
|
169
|
+
allowed: GROUNDING_CONFIDENCES,
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (!path) {
|
|
175
|
+
return {
|
|
176
|
+
record: null,
|
|
177
|
+
diagnostics: [
|
|
178
|
+
{ identifier, field: 'path', value: stringifyDiagnosticValue(entry.path), allowed: [] },
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const { evidence, diagnostics } = toStringArrayWithDiagnostics(entry.evidence, identifier);
|
|
183
|
+
return { record: { path, section, source, evidence, confidence }, diagnostics };
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=grounding-entry-parser.js.map
|