@telora/daemon 0.23.29 → 0.23.32
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/build-info.json +9 -3
- package/dist/backends/claude/one-shot-model.d.ts.map +1 -1
- package/dist/backends/claude/one-shot-model.js +1 -0
- package/dist/backends/claude/one-shot-model.js.map +1 -1
- package/dist/composition/daemon-contexts.d.ts.map +1 -1
- package/dist/composition/daemon-contexts.js +2 -0
- package/dist/composition/daemon-contexts.js.map +1 -1
- package/dist/composition/qa-author-pass-context.d.ts +7 -0
- package/dist/composition/qa-author-pass-context.d.ts.map +1 -0
- package/dist/composition/qa-author-pass-context.js +68 -0
- package/dist/composition/qa-author-pass-context.js.map +1 -0
- package/dist/composition/spawn-pass-deps.d.ts +1 -1
- package/dist/composition/spawn-pass-deps.d.ts.map +1 -1
- package/dist/config/live-daemon-config.js +1 -1
- package/dist/daemon-state/provider-window.d.ts.map +1 -1
- package/dist/daemon-state/provider-window.js +1 -0
- package/dist/daemon-state/provider-window.js.map +1 -1
- package/dist/focus/daemon-contexts.d.ts +3 -0
- package/dist/focus/daemon-contexts.d.ts.map +1 -1
- package/dist/focus/daemon-contexts.js.map +1 -1
- package/dist/focus/focus-loop-registry.d.ts.map +1 -1
- package/dist/focus/focus-loop-registry.js +21 -0
- package/dist/focus/focus-loop-registry.js.map +1 -1
- package/dist/git/types.d.ts.map +1 -1
- package/dist/git/types.js +1 -0
- package/dist/git/types.js.map +1 -1
- package/dist/prompt-sections/qa-author-section.d.ts +54 -0
- package/dist/prompt-sections/qa-author-section.d.ts.map +1 -0
- package/dist/prompt-sections/qa-author-section.js +189 -0
- package/dist/prompt-sections/qa-author-section.js.map +1 -0
- package/dist/qa/qa-author-faithfulness.d.ts +67 -0
- package/dist/qa/qa-author-faithfulness.d.ts.map +1 -0
- package/dist/qa/qa-author-faithfulness.js +157 -0
- package/dist/qa/qa-author-faithfulness.js.map +1 -0
- package/dist/qa/qa-author-mode.d.ts +34 -0
- package/dist/qa/qa-author-mode.d.ts.map +1 -0
- package/dist/qa/qa-author-mode.js +29 -0
- package/dist/qa/qa-author-mode.js.map +1 -0
- package/dist/qa/qa-author-output.d.ts +23 -0
- package/dist/qa/qa-author-output.d.ts.map +1 -0
- package/dist/qa/qa-author-output.js +66 -0
- package/dist/qa/qa-author-output.js.map +1 -0
- package/dist/qa/qa-author-pass.d.ts +60 -0
- package/dist/qa/qa-author-pass.d.ts.map +1 -0
- package/dist/qa/qa-author-pass.js +163 -0
- package/dist/qa/qa-author-pass.js.map +1 -0
- package/dist/qa/qa-author-runner.d.ts +60 -0
- package/dist/qa/qa-author-runner.d.ts.map +1 -0
- package/dist/qa/qa-author-runner.js +175 -0
- package/dist/qa/qa-author-runner.js.map +1 -0
- package/dist/qa/qa-focus-changes.d.ts +32 -0
- package/dist/qa/qa-focus-changes.d.ts.map +1 -0
- package/dist/qa/qa-focus-changes.js +59 -0
- package/dist/qa/qa-focus-changes.js.map +1 -0
- package/dist/queries/api-client/shared.d.ts +1 -0
- package/dist/queries/api-client/shared.d.ts.map +1 -1
- package/dist/queries/api-client/shared.js +14 -1
- package/dist/queries/api-client/shared.js.map +1 -1
- package/dist/queries/deliveries/deliveries.d.ts +15 -27
- package/dist/queries/deliveries/deliveries.d.ts.map +1 -1
- package/dist/queries/deliveries/deliveries.js +5 -37
- package/dist/queries/deliveries/deliveries.js.map +1 -1
- package/dist/queries/index.d.ts +1 -1
- package/dist/queries/index.d.ts.map +1 -1
- package/dist/queries/index.js +1 -1
- package/dist/queries/index.js.map +1 -1
- package/dist/queries/qa-packets/qa-packets.d.ts +83 -0
- package/dist/queries/qa-packets/qa-packets.d.ts.map +1 -0
- package/dist/queries/qa-packets/qa-packets.js +79 -0
- package/dist/queries/qa-packets/qa-packets.js.map +1 -0
- package/dist/testing/fixtures/api-client.js +1 -1
- package/dist/testing/fixtures/index.js +1 -1
- package/dist/types/qa-packet.d.ts +28 -0
- package/dist/types/qa-packet.d.ts.map +1 -0
- package/dist/types/qa-packet.js +36 -0
- package/dist/types/qa-packet.js.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/prompt-sections/qa-author-section.test.ts,
|
|
3
|
+
// packages/daemon/src/qa/qa-author-unfaithful-answer.test.ts
|
|
4
|
+
// PINNED-BY-END
|
|
5
|
+
import { QA_AUTHOR_MODE } from '../types/qa-packet.js';
|
|
6
|
+
/** The system prompt every QA-author call runs under (a resumed session may keep its recorded one). */
|
|
7
|
+
export const QA_AUTHOR_SYSTEM_PROMPT = 'You write UI test packets: click-level test cases a person runs by hand in a web browser against a ' +
|
|
8
|
+
'QA environment. You never edit files and never run a command that changes anything. You end every ' +
|
|
9
|
+
'answer with exactly one fenced json block.';
|
|
10
|
+
/** The heading the prompt opens with -- the anchor tests and logs name it by. */
|
|
11
|
+
export const QA_AUTHOR_HEADING = '# WRITE THE UI TEST PACKET FOR THIS FOCUS';
|
|
12
|
+
function verbatim(text, none) {
|
|
13
|
+
const t = (text ?? '').trim();
|
|
14
|
+
return t ? ['```text', t, '```'] : [none];
|
|
15
|
+
}
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
/** The armed baseline criteria, each quoted as the arm stamped it. */
|
|
20
|
+
function armedBaselineLines(criteria) {
|
|
21
|
+
if (criteria === null || criteria === undefined)
|
|
22
|
+
return ['(the arm stamped no criteria)'];
|
|
23
|
+
if (!Array.isArray(criteria))
|
|
24
|
+
return ['```json', JSON.stringify(criteria, null, 2), '```'];
|
|
25
|
+
const lines = [];
|
|
26
|
+
for (const entry of criteria) {
|
|
27
|
+
if (isRecord(entry) && typeof entry.criterion === 'string' && entry.criterion.trim()) {
|
|
28
|
+
lines.push(...verbatim(entry.criterion, ''));
|
|
29
|
+
if (entry.drafted === true)
|
|
30
|
+
lines.push('(machine-drafted, accepted at the arm)');
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
lines.push('```json', JSON.stringify(entry), '```');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return lines.length > 0 ? lines : ['(the arm stamped no criteria)'];
|
|
37
|
+
}
|
|
38
|
+
function targetLines(candidate, reviewedSha) {
|
|
39
|
+
const { environment, baseUrl, accessNote } = candidate.qaTarget;
|
|
40
|
+
return [
|
|
41
|
+
'## The QA target',
|
|
42
|
+
`- Environment: ${environment?.trim() || '(not set)'}`,
|
|
43
|
+
baseUrl?.trim()
|
|
44
|
+
? `- Base URL: ${baseUrl.trim()}`
|
|
45
|
+
: '- Base URL: (not set) -- write every path relative anyway; the human resolves it against the environment they test in.',
|
|
46
|
+
`- Access: ${accessNote?.trim() || '(no access note)'}`,
|
|
47
|
+
`- The packet describes commit \`${reviewedSha}\`.`,
|
|
48
|
+
'',
|
|
49
|
+
'Every case\'s `path` is RELATIVE to the base URL (for example `/settings?tab=billing`). Never write a scheme, a host, or `..`.',
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
function criteriaLines(candidate) {
|
|
53
|
+
const lines = [
|
|
54
|
+
'## The acceptance criteria -- quote them VERBATIM',
|
|
55
|
+
'Each case names ONE of the criteria below in its `criterion` field, copied EXACTLY: one whole line of a criteria ' +
|
|
56
|
+
'block (without its leading bullet or number) or one whole block. A paraphrase, a summary, a fragment or two ' +
|
|
57
|
+
'criteria merged into one is REFUSED. Only the focus\'s acceptance criteria, the armed baseline criteria and each ' +
|
|
58
|
+
'delivery\'s "Acceptance criteria" count -- a description is not a criterion.',
|
|
59
|
+
'',
|
|
60
|
+
`### Focus "${candidate.focusName}"`,
|
|
61
|
+
...verbatim(candidate.focusAcceptanceCriteria, '(the focus declares no acceptance criteria)'),
|
|
62
|
+
'',
|
|
63
|
+
'### Armed baseline criteria (what the human held this focus to when they armed it)',
|
|
64
|
+
...armedBaselineLines(candidate.armedBaselineCriteria),
|
|
65
|
+
];
|
|
66
|
+
for (const d of candidate.deliveries) {
|
|
67
|
+
lines.push('', `### Delivery "${d.name}" (${d.executionStatus ?? 'status unknown'})`);
|
|
68
|
+
if (d.description?.trim())
|
|
69
|
+
lines.push(...verbatim(d.description, ''));
|
|
70
|
+
lines.push('Acceptance criteria:', ...verbatim(d.acceptanceCriteria, '(none declared)'));
|
|
71
|
+
}
|
|
72
|
+
return lines;
|
|
73
|
+
}
|
|
74
|
+
function retestLines(candidate) {
|
|
75
|
+
const failed = candidate.previousPacket?.failedCases ?? [];
|
|
76
|
+
if (failed.length === 0)
|
|
77
|
+
return [];
|
|
78
|
+
const lines = [
|
|
79
|
+
'## Re-test: the previous QA cycle FAILED',
|
|
80
|
+
'A human failed these cases when they ran the previous packet. For each one whose criterion is still among the ' +
|
|
81
|
+
'criteria above, a case with `"retest": true` and `"retestOf": "<its id>"` that quotes THAT SAME criterion is ' +
|
|
82
|
+
'REQUIRED -- the packet is refused without it, and a retest quoting another criterion does not count. Drop one ' +
|
|
83
|
+
'only when its criterion is no longer listed above. While any of them still applies, the no-UI-surface marker is ' +
|
|
84
|
+
'refused too.',
|
|
85
|
+
];
|
|
86
|
+
for (const c of failed) {
|
|
87
|
+
lines.push(`- \`${c.id}\` -- criterion: ${JSON.stringify(c.criterion)}; path \`${c.path}\`; ` +
|
|
88
|
+
`steps: ${JSON.stringify(c.steps)}; expected: ${JSON.stringify(c.expected)}`);
|
|
89
|
+
}
|
|
90
|
+
return lines;
|
|
91
|
+
}
|
|
92
|
+
function knowledgeLines(candidate, mode, fresh) {
|
|
93
|
+
if (mode === QA_AUTHOR_MODE.RESUMED) {
|
|
94
|
+
return [
|
|
95
|
+
'## What you know',
|
|
96
|
+
'You built this focus: this conversation is the coding session that built it, and the review has passed. ' +
|
|
97
|
+
'Describe what you built in your own words. You may read files in the working directory (Read, Grep, Glob) ' +
|
|
98
|
+
'to confirm a route, a label or a button name. Change nothing.',
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
const lines = [
|
|
102
|
+
'## What was built',
|
|
103
|
+
'You did not build this focus. Work from its deliveries above, the review summary, its commits and its diff below.',
|
|
104
|
+
'',
|
|
105
|
+
'### Review summary',
|
|
106
|
+
...verbatim(candidate.latestReviewSummary, '(no review summary was recorded)'),
|
|
107
|
+
'',
|
|
108
|
+
'### Commits',
|
|
109
|
+
...verbatim(fresh?.commitLog, '(no commits could be listed)'),
|
|
110
|
+
'',
|
|
111
|
+
'### Diff',
|
|
112
|
+
];
|
|
113
|
+
if (fresh?.note)
|
|
114
|
+
lines.push(`(${fresh.note})`);
|
|
115
|
+
lines.push(...(fresh?.diff.trim() ? ['```diff', fresh.diff, '```'] : ['(no diff could be read)']));
|
|
116
|
+
if (fresh?.truncated)
|
|
117
|
+
lines.push('(The diff was cut at its size cap; the rest of the change is not shown.)');
|
|
118
|
+
return lines;
|
|
119
|
+
}
|
|
120
|
+
const RULE_LINES = [
|
|
121
|
+
'## The rules',
|
|
122
|
+
'- Write ONLY UI-facing cases: something a person opens, clicks or types into in the browser and sees the result of. ' +
|
|
123
|
+
'No API calls, CLI commands, database queries, log reading or code inspection.',
|
|
124
|
+
'- One case per behavior a person can observe, tied to ONE criterion. A criterion nothing in the UI can show gets no case.',
|
|
125
|
+
'- `id`: short and unique within the packet (c1, c2, ...).',
|
|
126
|
+
'- `criterion`: that criterion, copied exactly from the list above -- one line without its bullet, or one whole block.',
|
|
127
|
+
'- `built`: what was built for it, in a sentence or two.',
|
|
128
|
+
'- `path`: where to start, relative to the base URL.',
|
|
129
|
+
'- `steps`: click-level actions in order, one action each ("Click **Save**").',
|
|
130
|
+
'- `expected`: what the person should see when the criterion is met.',
|
|
131
|
+
'- If nothing in this focus changes anything a person can click or see, answer with the no-UI-surface marker instead of cases.',
|
|
132
|
+
'- Do not ask questions. Do not edit files. Do not run git or any command that writes.',
|
|
133
|
+
];
|
|
134
|
+
const OUTPUT_LINES = [
|
|
135
|
+
'## Your answer',
|
|
136
|
+
'End your answer with exactly ONE fenced json block and nothing after it. Either the cases:',
|
|
137
|
+
'```json',
|
|
138
|
+
'{"cases": [{"id": "c1", "criterion": "<verbatim criterion>", "built": "<what was built>", "path": "/relative/path", ' +
|
|
139
|
+
'"steps": ["<step 1>", "<step 2>"], "expected": "<what the person sees>"}]}',
|
|
140
|
+
'```',
|
|
141
|
+
'or, when nothing in the focus has a UI surface:',
|
|
142
|
+
'```json',
|
|
143
|
+
'{"noUiSurface": true}',
|
|
144
|
+
'```',
|
|
145
|
+
'Add `"retest": true, "retestOf": "<previous case id>"` only to a case that re-tests a previously failed one.',
|
|
146
|
+
];
|
|
147
|
+
/** The heading a corrective attempt's addition opens with. */
|
|
148
|
+
export const QA_AUTHOR_CORRECTION_HEADING = '## YOUR PREVIOUS ANSWER WAS REFUSED';
|
|
149
|
+
/**
|
|
150
|
+
* What a corrective attempt appends to the prompt: the rules the previous
|
|
151
|
+
* answer broke, one per line, and the instruction to write the whole packet
|
|
152
|
+
* again. Pure; the same text for every engine.
|
|
153
|
+
*/
|
|
154
|
+
export function buildQaAuthorCorrection(violations) {
|
|
155
|
+
return [
|
|
156
|
+
QA_AUTHOR_CORRECTION_HEADING,
|
|
157
|
+
'You answered this once already, and that packet was refused because it broke these rules:',
|
|
158
|
+
...violations.map((v) => `- ${v}`),
|
|
159
|
+
'',
|
|
160
|
+
'Write the WHOLE packet again, following every rule above: copy each `criterion` exactly from the criteria ' +
|
|
161
|
+
'listed, and add every required retest case. End with exactly one fenced json block.',
|
|
162
|
+
].join('\n') + '\n';
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The QA-author prompt for one candidate. `reviewedSha` is the commit the
|
|
166
|
+
* packet is pinned to; `fresh` carries the change for a fresh pass (ignored
|
|
167
|
+
* when resumed). Pure: the same inputs give the same text.
|
|
168
|
+
*/
|
|
169
|
+
export function buildQaAuthorPrompt(candidate, mode, reviewedSha, fresh = null) {
|
|
170
|
+
const intro = [
|
|
171
|
+
QA_AUTHOR_HEADING,
|
|
172
|
+
`The team's work on the focus "${candidate.focusName}" is finished and reviewed. A HUMAN will now check it by hand: ` +
|
|
173
|
+
'they open the product in a web browser at the QA target below and run the test packet you write, case by case. ' +
|
|
174
|
+
'Write that packet.',
|
|
175
|
+
];
|
|
176
|
+
if (candidate.focusDescription?.trim())
|
|
177
|
+
intro.push('', 'The focus:', ...verbatim(candidate.focusDescription, ''));
|
|
178
|
+
const sections = [
|
|
179
|
+
intro,
|
|
180
|
+
targetLines(candidate, reviewedSha),
|
|
181
|
+
criteriaLines(candidate),
|
|
182
|
+
retestLines(candidate),
|
|
183
|
+
knowledgeLines(candidate, mode, fresh),
|
|
184
|
+
[...RULE_LINES],
|
|
185
|
+
[...OUTPUT_LINES],
|
|
186
|
+
].filter((s) => s.length > 0);
|
|
187
|
+
return sections.map((s) => s.join('\n')).join('\n\n') + '\n';
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=qa-author-section.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-author-section.js","sourceRoot":"","sources":["../../src/prompt-sections/qa-author-section.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is a QA-author answer FAITHFUL to its focus? Every case quotes one of the
|
|
3
|
+
* criteria the pass was shown (the focus's acceptance criteria, each
|
|
4
|
+
* non-cancelled delivery's, each armed baseline criterion -- normalized, never
|
|
5
|
+
* paraphrased), and on a re-entry after a QA fail every failed case whose
|
|
6
|
+
* criterion still applies is re-tested (retest: true, retestOf: <its id>).
|
|
7
|
+
*
|
|
8
|
+
* The daemon runs this BEFORE it asks for the ready write, so an unfaithful
|
|
9
|
+
* answer can be re-asked (qa-author-runner.ts) instead of only refused. The
|
|
10
|
+
* authoritative rule is the edge's: checkQaPacketFaithful in
|
|
11
|
+
* src/lib/qa/qa-packet.ts (mirrored to supabase/functions/_shared), which
|
|
12
|
+
* daemon_qa_packet_write applies before a ready or no_ui_surface write. The
|
|
13
|
+
* daemon cannot import src/lib, so this is its own copy; qa-author-
|
|
14
|
+
* faithfulness.test.ts runs the SAME conformance table
|
|
15
|
+
* (src/lib/qa/__fixtures__/qa-faithfulness-cases.json) the canonical vitest
|
|
16
|
+
* runs, so the two cannot drift.
|
|
17
|
+
*/
|
|
18
|
+
import type { QaPacketCandidate } from '../queries/qa-packets/qa-packets.js';
|
|
19
|
+
/** What the rule reads of one case. */
|
|
20
|
+
export interface QaFaithfulnessCase {
|
|
21
|
+
id: string;
|
|
22
|
+
criterion: string;
|
|
23
|
+
retest?: boolean;
|
|
24
|
+
retestOf?: string;
|
|
25
|
+
}
|
|
26
|
+
/** What the rule reads of a packet and its focus. */
|
|
27
|
+
export interface QaPacketFaithfulnessInput {
|
|
28
|
+
/** The packet's cases ([] for a no-UI-surface packet). */
|
|
29
|
+
cases: readonly QaFaithfulnessCase[];
|
|
30
|
+
noUiSurface: boolean;
|
|
31
|
+
/** The quotable criteria (qaCriteriaFromTexts); normalized again here. */
|
|
32
|
+
criteria: readonly string[];
|
|
33
|
+
/** The cases a human failed in the previous QA cycle ([] when it did not fail). */
|
|
34
|
+
previousFailedCases: ReadonlyArray<Pick<QaFaithfulnessCase, 'id' | 'criterion'>>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The one normalization both sides of a criterion comparison get. Pure and
|
|
38
|
+
* idempotent: trim; strip surrounding quotes and a leading list marker, one
|
|
39
|
+
* layer at a time until neither is left; collapse whitespace runs to one
|
|
40
|
+
* space. Case, punctuation and wording are kept.
|
|
41
|
+
*/
|
|
42
|
+
export declare function normalizeQaCriterion(text: string): string;
|
|
43
|
+
/** Every whole text block and each of its non-empty lines, normalized and de-duplicated. Pure. */
|
|
44
|
+
export declare function qaCriteriaFromTexts(texts: ReadonlyArray<string | null | undefined>): string[];
|
|
45
|
+
/** The criterion strings of an armed_baseline_criteria array; anything else yields none. Pure. */
|
|
46
|
+
export declare function qaArmedBaselineCriterionTexts(value: unknown): string[];
|
|
47
|
+
/**
|
|
48
|
+
* Is the packet faithful? Pure. One human-readable line per violation; [] is
|
|
49
|
+
* faithful. Rules: (1) every criterion is one of `criteria`; (2) every
|
|
50
|
+
* previously failed case whose criterion still applies is re-tested by a case
|
|
51
|
+
* quoting that same criterion; (3) a retest case carries both flag and
|
|
52
|
+
* retestOf, naming a failed case, and quotes the criterion that case tested;
|
|
53
|
+
* (4) no UI surface while a retest is owed is refused.
|
|
54
|
+
*/
|
|
55
|
+
export declare function checkQaPacketFaithful(input: QaPacketFaithfulnessInput): string[];
|
|
56
|
+
/**
|
|
57
|
+
* The violations of one parsed answer against what the candidate read handed
|
|
58
|
+
* the author -- the same inputs daemon_qa_packet_write checks the write
|
|
59
|
+
* against. Pure.
|
|
60
|
+
*/
|
|
61
|
+
export declare function qaAnswerFaithfulnessViolations(candidate: Pick<QaPacketCandidate, 'focusAcceptanceCriteria' | 'deliveries' | 'armedBaselineCriteria' | 'previousPacket'>, answer: {
|
|
62
|
+
kind: 'cases';
|
|
63
|
+
cases: readonly unknown[];
|
|
64
|
+
} | {
|
|
65
|
+
kind: 'no_ui_surface';
|
|
66
|
+
}): string[];
|
|
67
|
+
//# sourceMappingURL=qa-author-faithfulness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-author-faithfulness.d.ts","sourceRoot":"","sources":["../../src/qa/qa-author-faithfulness.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAE7E,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qDAAqD;AACrD,MAAM,WAAW,yBAAyB;IACxC,0DAA0D;IAC1D,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,WAAW,EAAE,OAAO,CAAC;IACrB,0EAA0E;IAC1E,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,mFAAmF;IACnF,mBAAmB,EAAE,aAAa,CAAC,IAAI,CAAC,kBAAkB,EAAE,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC;CAClF;AAsBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQzD;AAED,kGAAkG;AAClG,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,MAAM,EAAE,CAU7F;AAMD,kGAAkG;AAClG,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAOtE;AAqCD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,yBAAyB,GAAG,MAAM,EAAE,CAgChF;AAcD;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,yBAAyB,GAAG,YAAY,GAAG,uBAAuB,GAAG,gBAAgB,CAAC,EACzH,MAAM,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,SAAS,OAAO,EAAE,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GAC/E,MAAM,EAAE,CAYV"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/qa/qa-author-faithfulness.test.ts
|
|
3
|
+
// PINNED-BY-END
|
|
4
|
+
const QUOTE_PAIRS = [
|
|
5
|
+
['"', '"'],
|
|
6
|
+
["'", "'"],
|
|
7
|
+
['“', '”'],
|
|
8
|
+
['‘', '’'],
|
|
9
|
+
];
|
|
10
|
+
const LIST_MARKER = /^(?:[-*+•]|\d+[.)])\s+/;
|
|
11
|
+
const QUOTE_MAX_CHARS = 160;
|
|
12
|
+
function stripQuotePair(text) {
|
|
13
|
+
for (const [open, close] of QUOTE_PAIRS) {
|
|
14
|
+
if (text.length >= 2 && text.startsWith(open) && text.endsWith(close))
|
|
15
|
+
return text.slice(1, -1).trim();
|
|
16
|
+
}
|
|
17
|
+
return text;
|
|
18
|
+
}
|
|
19
|
+
function stripListMarker(text) {
|
|
20
|
+
return text.replace(LIST_MARKER, '').trim();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The one normalization both sides of a criterion comparison get. Pure and
|
|
24
|
+
* idempotent: trim; strip surrounding quotes and a leading list marker, one
|
|
25
|
+
* layer at a time until neither is left; collapse whitespace runs to one
|
|
26
|
+
* space. Case, punctuation and wording are kept.
|
|
27
|
+
*/
|
|
28
|
+
export function normalizeQaCriterion(text) {
|
|
29
|
+
let t = text.trim();
|
|
30
|
+
for (;;) {
|
|
31
|
+
const next = stripListMarker(stripQuotePair(t));
|
|
32
|
+
if (next === t)
|
|
33
|
+
break;
|
|
34
|
+
t = next;
|
|
35
|
+
}
|
|
36
|
+
return t.replace(/\s+/g, ' ');
|
|
37
|
+
}
|
|
38
|
+
/** Every whole text block and each of its non-empty lines, normalized and de-duplicated. Pure. */
|
|
39
|
+
export function qaCriteriaFromTexts(texts) {
|
|
40
|
+
const out = new Set();
|
|
41
|
+
for (const text of texts) {
|
|
42
|
+
if (typeof text !== 'string')
|
|
43
|
+
continue;
|
|
44
|
+
for (const piece of [text, ...text.split(/\r?\n/)]) {
|
|
45
|
+
const normalized = normalizeQaCriterion(piece);
|
|
46
|
+
if (normalized)
|
|
47
|
+
out.add(normalized);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return [...out];
|
|
51
|
+
}
|
|
52
|
+
function isRecord(value) {
|
|
53
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
54
|
+
}
|
|
55
|
+
/** The criterion strings of an armed_baseline_criteria array; anything else yields none. Pure. */
|
|
56
|
+
export function qaArmedBaselineCriterionTexts(value) {
|
|
57
|
+
if (!Array.isArray(value))
|
|
58
|
+
return [];
|
|
59
|
+
return value.flatMap((entry) => {
|
|
60
|
+
if (!isRecord(entry))
|
|
61
|
+
return [];
|
|
62
|
+
const criterion = entry.criterion;
|
|
63
|
+
return typeof criterion === 'string' && criterion.trim().length > 0 ? [criterion] : [];
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function quoteForViolation(text) {
|
|
67
|
+
const t = text.trim();
|
|
68
|
+
return JSON.stringify(t.length > QUOTE_MAX_CHARS ? `${t.slice(0, QUOTE_MAX_CHARS)}...` : t);
|
|
69
|
+
}
|
|
70
|
+
/** Rules 1 and 3 for one case: it quotes a criterion; its retest flag and retestOf agree and name a failed case. */
|
|
71
|
+
function caseViolations(c, quotable, failedCriteria) {
|
|
72
|
+
const out = [];
|
|
73
|
+
if (!quotable.has(normalizeQaCriterion(c.criterion))) {
|
|
74
|
+
out.push(`case ${c.id}: criterion ${quoteForViolation(c.criterion)} is not one of the focus's acceptance criteria ` +
|
|
75
|
+
'(copy one line, without its bullet, or one whole block, exactly)');
|
|
76
|
+
}
|
|
77
|
+
const retestOf = typeof c.retestOf === 'string' ? c.retestOf.trim() : '';
|
|
78
|
+
const failedList = failedCriteria.size > 0 ? `failed: ${[...failedCriteria.keys()].join(', ')}` : 'it failed no case';
|
|
79
|
+
if (c.retest === true && !retestOf) {
|
|
80
|
+
out.push(`case ${c.id}: is flagged "retest": true but names no retestOf`);
|
|
81
|
+
}
|
|
82
|
+
else if (retestOf && c.retest !== true) {
|
|
83
|
+
out.push(`case ${c.id}: names retestOf ${quoteForViolation(retestOf)} but is not flagged "retest": true`);
|
|
84
|
+
}
|
|
85
|
+
else if (retestOf && !failedCriteria.has(retestOf)) {
|
|
86
|
+
out.push(`case ${c.id}: retestOf ${quoteForViolation(retestOf)} is not a case the human failed in the previous QA cycle (${failedList})`);
|
|
87
|
+
}
|
|
88
|
+
else if (retestOf && failedCriteria.get(retestOf) !== normalizeQaCriterion(c.criterion)) {
|
|
89
|
+
out.push(`case ${c.id}: re-tests failed case ${retestOf} but quotes a different criterion -- a retest quotes the ` +
|
|
90
|
+
`criterion the failed case tested, ${quoteForViolation(failedCriteria.get(retestOf) ?? '')}`);
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Is the packet faithful? Pure. One human-readable line per violation; [] is
|
|
96
|
+
* faithful. Rules: (1) every criterion is one of `criteria`; (2) every
|
|
97
|
+
* previously failed case whose criterion still applies is re-tested by a case
|
|
98
|
+
* quoting that same criterion; (3) a retest case carries both flag and
|
|
99
|
+
* retestOf, naming a failed case, and quotes the criterion that case tested;
|
|
100
|
+
* (4) no UI surface while a retest is owed is refused.
|
|
101
|
+
*/
|
|
102
|
+
export function checkQaPacketFaithful(input) {
|
|
103
|
+
const quotable = new Set(input.criteria.map(normalizeQaCriterion).filter((c) => c.length > 0));
|
|
104
|
+
const failedCriteria = new Map(input.previousFailedCases.map((f) => [f.id.trim(), normalizeQaCriterion(f.criterion)]));
|
|
105
|
+
const violations = input.cases.flatMap((c) => caseViolations(c, quotable, failedCriteria));
|
|
106
|
+
const required = input.previousFailedCases.filter((f) => quotable.has(normalizeQaCriterion(f.criterion)));
|
|
107
|
+
if (input.noUiSurface && required.length > 0) {
|
|
108
|
+
violations.push(`the packet marks no UI surface, but the previous QA cycle failed UI case(s) ${required.map((f) => f.id).join(', ')} ` +
|
|
109
|
+
'whose criteria still apply; each needs a retest case');
|
|
110
|
+
return violations;
|
|
111
|
+
}
|
|
112
|
+
for (const f of required) {
|
|
113
|
+
const id = f.id.trim();
|
|
114
|
+
const criterion = normalizeQaCriterion(f.criterion);
|
|
115
|
+
const retested = input.cases.some((c) => c.retest === true &&
|
|
116
|
+
typeof c.retestOf === 'string' &&
|
|
117
|
+
c.retestOf.trim() === id &&
|
|
118
|
+
normalizeQaCriterion(c.criterion) === criterion);
|
|
119
|
+
if (!retested) {
|
|
120
|
+
violations.push(`previous failed case ${id} (criterion ${quoteForViolation(f.criterion)}) still applies but no case re-tests it: ` +
|
|
121
|
+
`add a case quoting that criterion with "retest": true, "retestOf": "${id}"`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return violations;
|
|
125
|
+
}
|
|
126
|
+
/** The fields the rule reads of a structurally parsed answer case (qa-author-output.ts checked id and criterion). */
|
|
127
|
+
function toFaithfulnessCase(value) {
|
|
128
|
+
const c = isRecord(value) ? value : {};
|
|
129
|
+
const out = {
|
|
130
|
+
id: typeof c.id === 'string' ? c.id.trim() : '',
|
|
131
|
+
criterion: typeof c.criterion === 'string' ? c.criterion : '',
|
|
132
|
+
};
|
|
133
|
+
if (c.retest === true)
|
|
134
|
+
out.retest = true;
|
|
135
|
+
if (typeof c.retestOf === 'string' && c.retestOf.trim())
|
|
136
|
+
out.retestOf = c.retestOf.trim();
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The violations of one parsed answer against what the candidate read handed
|
|
141
|
+
* the author -- the same inputs daemon_qa_packet_write checks the write
|
|
142
|
+
* against. Pure.
|
|
143
|
+
*/
|
|
144
|
+
export function qaAnswerFaithfulnessViolations(candidate, answer) {
|
|
145
|
+
const criteria = qaCriteriaFromTexts([
|
|
146
|
+
candidate.focusAcceptanceCriteria,
|
|
147
|
+
...candidate.deliveries.map((d) => d.acceptanceCriteria),
|
|
148
|
+
...qaArmedBaselineCriterionTexts(candidate.armedBaselineCriteria),
|
|
149
|
+
]);
|
|
150
|
+
return checkQaPacketFaithful({
|
|
151
|
+
cases: answer.kind === 'cases' ? answer.cases.map(toFaithfulnessCase) : [],
|
|
152
|
+
noUiSurface: answer.kind === 'no_ui_surface',
|
|
153
|
+
criteria,
|
|
154
|
+
previousFailedCases: candidate.previousPacket?.failedCases ?? [],
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=qa-author-faithfulness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-author-faithfulness.js","sourceRoot":"","sources":["../../src/qa/qa-author-faithfulness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which way the QA-author pass runs for one focus: RESUMED -- a fork of the
|
|
3
|
+
* coding team's own Claude session, which already knows what it built -- or
|
|
4
|
+
* FRESH, over the delivery summaries and the diff.
|
|
5
|
+
*
|
|
6
|
+
* Resume only when the focus's coding lineage holds a CLAUDE session id (a
|
|
7
|
+
* Codex id cannot be resumed by the claude CLI; session/session-lineage.ts
|
|
8
|
+
* keys Claude under the bare lineage) AND the focus worktree that session ran
|
|
9
|
+
* in still exists: the CLI finds a session's transcript by the directory it
|
|
10
|
+
* ran in, and the session's file reads are relative to it. Anything else runs
|
|
11
|
+
* fresh, with the reason recorded in the log.
|
|
12
|
+
*/
|
|
13
|
+
import type { QaPacketCandidate } from '../queries/qa-packets/qa-packets.js';
|
|
14
|
+
import { QA_AUTHOR_MODE } from '../types/qa-packet.js';
|
|
15
|
+
/** The lineage whose session built the focus. */
|
|
16
|
+
export declare const QA_CODING_LINEAGE = "coding";
|
|
17
|
+
/** What the decision reads besides the candidate. */
|
|
18
|
+
export interface QaAuthorModeFacts {
|
|
19
|
+
/** The focus worktree the coding session ran in, or null when none is known. */
|
|
20
|
+
worktreePath: string | null;
|
|
21
|
+
/** Whether that directory still exists. */
|
|
22
|
+
worktreeExists: boolean;
|
|
23
|
+
}
|
|
24
|
+
export type QaAuthorModeDecision = {
|
|
25
|
+
mode: typeof QA_AUTHOR_MODE.RESUMED;
|
|
26
|
+
sessionId: string;
|
|
27
|
+
cwd: string;
|
|
28
|
+
} | {
|
|
29
|
+
mode: typeof QA_AUTHOR_MODE.FRESH;
|
|
30
|
+
reason: string;
|
|
31
|
+
};
|
|
32
|
+
/** Decide resumed vs fresh. Pure. */
|
|
33
|
+
export declare function decideQaAuthorMode(candidate: Pick<QaPacketCandidate, 'codingSessionIds'>, facts: QaAuthorModeFacts): QaAuthorModeDecision;
|
|
34
|
+
//# sourceMappingURL=qa-author-mode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-author-mode.d.ts","sourceRoot":"","sources":["../../src/qa/qa-author-mode.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAE7E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,qDAAqD;AACrD,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,2CAA2C;IAC3C,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,OAAO,cAAc,CAAC,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,IAAI,EAAE,OAAO,cAAc,CAAC,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,qCAAqC;AACrC,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,EACtD,KAAK,EAAE,iBAAiB,GACvB,oBAAoB,CAmBtB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/qa/qa-author-mode.test.ts
|
|
3
|
+
// PINNED-BY-END
|
|
4
|
+
import { sessionMapKey } from '../session/session-lineage.js';
|
|
5
|
+
import { QA_AUTHOR_MODE } from '../types/qa-packet.js';
|
|
6
|
+
/** The lineage whose session built the focus. */
|
|
7
|
+
export const QA_CODING_LINEAGE = 'coding';
|
|
8
|
+
/** Decide resumed vs fresh. Pure. */
|
|
9
|
+
export function decideQaAuthorMode(candidate, facts) {
|
|
10
|
+
const sessions = candidate.codingSessionIds;
|
|
11
|
+
const sessionId = sessions[sessionMapKey('claude', QA_CODING_LINEAGE)];
|
|
12
|
+
if (!sessionId) {
|
|
13
|
+
const codex = sessions[sessionMapKey('codex', QA_CODING_LINEAGE)];
|
|
14
|
+
return {
|
|
15
|
+
mode: QA_AUTHOR_MODE.FRESH,
|
|
16
|
+
reason: codex
|
|
17
|
+
? 'the coding session ran on codex, which the claude CLI cannot resume'
|
|
18
|
+
: 'no coding session is recorded for the focus',
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (!facts.worktreePath) {
|
|
22
|
+
return { mode: QA_AUTHOR_MODE.FRESH, reason: 'no focus worktree is known to resume the coding session in' };
|
|
23
|
+
}
|
|
24
|
+
if (!facts.worktreeExists) {
|
|
25
|
+
return { mode: QA_AUTHOR_MODE.FRESH, reason: `the focus worktree ${facts.worktreePath} no longer exists` };
|
|
26
|
+
}
|
|
27
|
+
return { mode: QA_AUTHOR_MODE.RESUMED, sessionId, cwd: facts.worktreePath };
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=qa-author-mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-author-mode.js","sourceRoot":"","sources":["../../src/qa/qa-author-mode.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,2DAA2D;AAC3D,gBAAgB;AAgBhB,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,iDAAiD;AACjD,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAc1C,qCAAqC;AACrC,MAAM,UAAU,kBAAkB,CAChC,SAAsD,EACtD,KAAwB;IAExB,MAAM,QAAQ,GAAG,SAAS,CAAC,gBAAgB,CAAC;IAC5C,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACvE,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC;QAClE,OAAO;YACL,IAAI,EAAE,cAAc,CAAC,KAAK;YAC1B,MAAM,EAAE,KAAK;gBACX,CAAC,CAAC,qEAAqE;gBACvE,CAAC,CAAC,6CAA6C;SAClD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;QACxB,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,4DAA4D,EAAE,CAAC;IAC9G,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;QAC1B,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,sBAAsB,KAAK,CAAC,YAAY,mBAAmB,EAAE,CAAC;IAC7G,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the QA-author pass's answer: the LAST fenced ```json block of the
|
|
3
|
+
* output, holding either {"cases": [...]} or {"noUiSurface": true} (the output
|
|
4
|
+
* contract prompt-sections/qa-author-section.ts states).
|
|
5
|
+
*
|
|
6
|
+
* The check here is structural only -- an array of objects with string fields,
|
|
7
|
+
* click-level steps, a path that is not absolute -- so an answer that could
|
|
8
|
+
* never be a packet is caught before it is written. The authoritative case
|
|
9
|
+
* contract is the edge's (supabase/functions/_shared/qa-packet.ts
|
|
10
|
+
* parseQaCases): a ready write it refuses is recorded as a failed packet.
|
|
11
|
+
*/
|
|
12
|
+
export type QaAuthorOutput = {
|
|
13
|
+
kind: 'cases';
|
|
14
|
+
cases: unknown[];
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'no_ui_surface';
|
|
17
|
+
} | {
|
|
18
|
+
kind: 'error';
|
|
19
|
+
error: string;
|
|
20
|
+
};
|
|
21
|
+
/** Parse a QA-author answer. Pure. */
|
|
22
|
+
export declare function parseQaAuthorOutput(text: string): QaAuthorOutput;
|
|
23
|
+
//# sourceMappingURL=qa-author-output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-author-output.d.ts","sourceRoot":"","sources":["../../src/qa/qa-author-output.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,EAAE,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AA+BrC,sCAAsC;AACtC,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAyBhE"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/prompt-sections/qa-author-section.test.ts,
|
|
3
|
+
// packages/daemon/src/qa/qa-author-output.test.ts
|
|
4
|
+
// PINNED-BY-END
|
|
5
|
+
const FENCED_JSON = /```json[^\S\n]*\n([\s\S]*?)```/gi;
|
|
6
|
+
const CASE_STRING_FIELDS = ['id', 'criterion', 'built', 'path', 'expected'];
|
|
7
|
+
function isRecord(value) {
|
|
8
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
/** Whether `path` reaches off the QA target: a scheme, a protocol-relative or backslash start. */
|
|
11
|
+
function isAbsolutePath(path) {
|
|
12
|
+
const p = path.trim();
|
|
13
|
+
return /^[a-z][a-z0-9+.-]*:/i.test(p) || p.startsWith('//') || p.startsWith('\\');
|
|
14
|
+
}
|
|
15
|
+
/** The first structural problem of one case, or null. */
|
|
16
|
+
function caseProblem(value, index) {
|
|
17
|
+
const at = `case ${index + 1}`;
|
|
18
|
+
if (!isRecord(value))
|
|
19
|
+
return `${at} is not an object`;
|
|
20
|
+
for (const field of CASE_STRING_FIELDS) {
|
|
21
|
+
const v = value[field];
|
|
22
|
+
if (typeof v !== 'string' || v.trim() === '')
|
|
23
|
+
return `${at}: ${field} must be a non-empty string`;
|
|
24
|
+
}
|
|
25
|
+
if (isAbsolutePath(value.path))
|
|
26
|
+
return `${at}: path must be relative to the QA target base URL`;
|
|
27
|
+
const steps = value.steps;
|
|
28
|
+
if (!Array.isArray(steps) || steps.length === 0 || !steps.every((s) => typeof s === 'string' && s.trim() !== '')) {
|
|
29
|
+
return `${at}: steps must be a non-empty array of strings`;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
/** Parse a QA-author answer. Pure. */
|
|
34
|
+
export function parseQaAuthorOutput(text) {
|
|
35
|
+
const blocks = [...text.matchAll(FENCED_JSON)].map((m) => m[1] ?? '');
|
|
36
|
+
const last = blocks.at(-1);
|
|
37
|
+
if (last === undefined)
|
|
38
|
+
return { kind: 'error', error: 'the answer has no fenced json block' };
|
|
39
|
+
let parsed;
|
|
40
|
+
try {
|
|
41
|
+
parsed = JSON.parse(last);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
return { kind: 'error', error: `the last json block is not valid JSON: ${err.message}` };
|
|
45
|
+
}
|
|
46
|
+
if (!isRecord(parsed))
|
|
47
|
+
return { kind: 'error', error: 'the last json block is not an object' };
|
|
48
|
+
const cases = parsed.cases;
|
|
49
|
+
const hasCases = Array.isArray(cases) && cases.length > 0;
|
|
50
|
+
if (parsed.noUiSurface === true) {
|
|
51
|
+
return hasCases
|
|
52
|
+
? { kind: 'error', error: 'the answer both marks no UI surface and lists cases' }
|
|
53
|
+
: { kind: 'no_ui_surface' };
|
|
54
|
+
}
|
|
55
|
+
if (!Array.isArray(cases))
|
|
56
|
+
return { kind: 'error', error: 'the last json block has neither "cases" nor "noUiSurface": true' };
|
|
57
|
+
if (cases.length === 0)
|
|
58
|
+
return { kind: 'error', error: 'the answer lists no cases and does not mark no UI surface' };
|
|
59
|
+
for (let i = 0; i < cases.length; i++) {
|
|
60
|
+
const problem = caseProblem(cases[i], i);
|
|
61
|
+
if (problem)
|
|
62
|
+
return { kind: 'error', error: problem };
|
|
63
|
+
}
|
|
64
|
+
return { kind: 'cases', cases };
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=qa-author-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-author-output.js","sourceRoot":"","sources":["../../src/qa/qa-author-output.ts"],"names":[],"mappings":"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"}
|