@telora/daemon 0.23.29 → 0.23.46
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 +10 -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/completion/completion-event/event.d.ts +12 -0
- package/dist/completion/completion-event/event.d.ts.map +1 -1
- package/dist/completion/completion-event/event.js +1 -0
- package/dist/completion/completion-event/event.js.map +1 -1
- package/dist/completion/completion-event/unfinished-work.d.ts +134 -0
- package/dist/completion/completion-event/unfinished-work.d.ts.map +1 -0
- package/dist/completion/completion-event/unfinished-work.js +140 -0
- package/dist/completion/completion-event/unfinished-work.js.map +1 -0
- package/dist/completion/team-outcome/apply-turn.d.ts.map +1 -1
- package/dist/completion/team-outcome/apply-turn.js +18 -0
- package/dist/completion/team-outcome/apply-turn.js.map +1 -1
- package/dist/completion/team-outcome/decide-turn-entry-rows.d.ts +16 -0
- package/dist/completion/team-outcome/decide-turn-entry-rows.d.ts.map +1 -1
- package/dist/completion/team-outcome/decide-turn-entry-rows.js +75 -11
- package/dist/completion/team-outcome/decide-turn-entry-rows.js.map +1 -1
- package/dist/completion/team-outcome/decide-turn-work-rows.d.ts.map +1 -1
- package/dist/completion/team-outcome/decide-turn-work-rows.js +10 -1
- package/dist/completion/team-outcome/decide-turn-work-rows.js.map +1 -1
- package/dist/completion/team-outcome/decide.d.ts.map +1 -1
- package/dist/completion/team-outcome/decide.js +1 -0
- package/dist/completion/team-outcome/decide.js.map +1 -1
- package/dist/completion/team-outcome/exit-action-roles.d.ts.map +1 -1
- package/dist/completion/team-outcome/exit-action-roles.js +2 -0
- package/dist/completion/team-outcome/exit-action-roles.js.map +1 -1
- package/dist/completion/team-outcome/exit-actions.d.ts +4 -0
- package/dist/completion/team-outcome/exit-actions.d.ts.map +1 -1
- package/dist/completion/team-outcome/exit-limits.d.ts +8 -0
- package/dist/completion/team-outcome/exit-limits.d.ts.map +1 -1
- package/dist/completion/team-outcome/exit-limits.js +9 -0
- package/dist/completion/team-outcome/exit-limits.js.map +1 -1
- package/dist/completion/team-outcome/exit-pipeline-state.d.ts +7 -0
- package/dist/completion/team-outcome/exit-pipeline-state.d.ts.map +1 -1
- package/dist/completion/team-outcome/exit-pipeline-state.js +1 -0
- package/dist/completion/team-outcome/exit-pipeline-state.js.map +1 -1
- package/dist/completion/team-outcome/team-outcome.d.ts.map +1 -1
- package/dist/completion/team-outcome/team-outcome.js +2 -1
- package/dist/completion/team-outcome/team-outcome.js.map +1 -1
- package/dist/completion/team-teardown/session-hold-bounds.d.ts +13 -1
- package/dist/completion/team-teardown/session-hold-bounds.d.ts.map +1 -1
- package/dist/completion/team-teardown/session-hold-bounds.js +15 -0
- package/dist/completion/team-teardown/session-hold-bounds.js.map +1 -1
- package/dist/completion/team-teardown/session-hold.d.ts.map +1 -1
- package/dist/completion/team-teardown/session-hold.js +4 -0
- package/dist/completion/team-teardown/session-hold.js.map +1 -1
- package/dist/composition/completion-event-context.d.ts.map +1 -1
- package/dist/composition/completion-event-context.js +8 -0
- package/dist/composition/completion-event-context.js.map +1 -1
- package/dist/composition/daemon-contexts.d.ts.map +1 -1
- package/dist/composition/daemon-contexts.js +3 -1
- 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/runtime-poll-context.d.ts +32 -4
- package/dist/composition/runtime-poll-context.d.ts.map +1 -1
- package/dist/composition/runtime-poll-context.js +61 -5
- package/dist/composition/runtime-poll-context.js.map +1 -1
- package/dist/composition/spawn-pass-deps.d.ts +1 -1
- package/dist/composition/spawn-pass-deps.d.ts.map +1 -1
- package/dist/composition/verification-context.js +1 -1
- package/dist/composition/verify-rework-context.d.ts +12 -3
- package/dist/composition/verify-rework-context.d.ts.map +1 -1
- package/dist/composition/verify-rework-context.js +27 -10
- package/dist/composition/verify-rework-context.js.map +1 -1
- package/dist/config/live-daemon-config.js +1 -1
- package/dist/daemon-state/focus-team-state.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/delivery/advance-hold.d.ts +25 -0
- package/dist/delivery/advance-hold.d.ts.map +1 -1
- package/dist/delivery/advance-hold.js +52 -1
- package/dist/delivery/advance-hold.js.map +1 -1
- package/dist/delivery/delivery-reconciler.d.ts +17 -2
- package/dist/delivery/delivery-reconciler.d.ts.map +1 -1
- package/dist/delivery/delivery-reconciler.js +54 -30
- package/dist/delivery/delivery-reconciler.js.map +1 -1
- package/dist/delivery/recorded-concerns.d.ts.map +1 -1
- package/dist/delivery/recorded-concerns.js +1 -0
- package/dist/delivery/recorded-concerns.js.map +1 -1
- package/dist/directive/directive-queue.d.ts +27 -0
- package/dist/directive/directive-queue.d.ts.map +1 -1
- package/dist/directive/directive-queue.js +36 -1
- package/dist/directive/directive-queue.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 +1 -1
- package/dist/focus/daemon-contexts.js.map +1 -1
- package/dist/focus/failed-head-wedge.d.ts +42 -13
- package/dist/focus/failed-head-wedge.d.ts.map +1 -1
- package/dist/focus/failed-head-wedge.js +45 -10
- package/dist/focus/failed-head-wedge.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/focus/focus-poller-spawn.js +1 -1
- package/dist/focus/focus-poller-state.js +1 -1
- package/dist/focus/pending-spawn-review-next-step.d.ts.map +1 -1
- package/dist/focus/pending-spawn-review-next-step.js +9 -1
- package/dist/focus/pending-spawn-review-next-step.js.map +1 -1
- package/dist/focus/uncommitted-work-backstop.d.ts +115 -0
- package/dist/focus/uncommitted-work-backstop.d.ts.map +1 -0
- package/dist/focus/uncommitted-work-backstop.js +259 -0
- package/dist/focus/uncommitted-work-backstop.js.map +1 -0
- package/dist/focus/verify-routing-passes.d.ts +5 -1
- package/dist/focus/verify-routing-passes.d.ts.map +1 -1
- package/dist/focus/verify-routing-passes.js +1 -0
- package/dist/focus/verify-routing-passes.js.map +1 -1
- package/dist/git/index.d.ts.map +1 -1
- package/dist/git/index.js +2 -1
- package/dist/git/index.js.map +1 -1
- package/dist/git/review-window-commits.d.ts.map +1 -1
- package/dist/git/review-window-commits.js +1 -0
- package/dist/git/review-window-commits.js.map +1 -1
- package/dist/git/types.d.ts.map +1 -1
- package/dist/git/types.js +1 -19
- package/dist/git/types.js.map +1 -1
- package/dist/prompt-sections/qa-author-section.d.ts +58 -0
- package/dist/prompt-sections/qa-author-section.d.ts.map +1 -0
- package/dist/prompt-sections/qa-author-section.js +226 -0
- package/dist/prompt-sections/qa-author-section.js.map +1 -0
- package/dist/qa/qa-author-faithfulness.d.ts +139 -0
- package/dist/qa/qa-author-faithfulness.d.ts.map +1 -0
- package/dist/qa/qa-author-faithfulness.js +323 -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 +26 -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 +63 -0
- package/dist/qa/qa-author-pass.d.ts.map +1 -0
- package/dist/qa/qa-author-pass.js +164 -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 +180 -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 +6 -57
- package/dist/queries/deliveries/deliveries.js.map +1 -1
- package/dist/queries/delivery-gate-state/delivery-gate-holds.d.ts +8 -3
- package/dist/queries/delivery-gate-state/delivery-gate-holds.d.ts.map +1 -1
- package/dist/queries/delivery-gate-state/delivery-gate-state.js +1 -1
- package/dist/queries/escalations/escalations.d.ts.map +1 -1
- package/dist/queries/escalations/escalations.js +1 -0
- package/dist/queries/escalations/escalations.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 +156 -0
- package/dist/queries/qa-packets/qa-packets.d.ts.map +1 -0
- package/dist/queries/qa-packets/qa-packets.js +105 -0
- package/dist/queries/qa-packets/qa-packets.js.map +1 -0
- package/dist/supervision/escalation-filing.js +1 -1
- package/dist/supervision/spawn-cooldown.js +1 -1
- package/dist/supervision/stop-registry.d.ts +5 -0
- package/dist/supervision/stop-registry.d.ts.map +1 -1
- package/dist/supervision/stop-rows-team-session.d.ts +6 -1
- package/dist/supervision/stop-rows-team-session.d.ts.map +1 -1
- package/dist/supervision/stop-rows-team-session.js +34 -1
- package/dist/supervision/stop-rows-team-session.js.map +1 -1
- package/dist/supervision/team-work.d.ts.map +1 -1
- package/dist/supervision/team-work.js +1 -0
- package/dist/supervision/team-work.js.map +1 -1
- package/dist/testing/fixtures/api-client.js +1 -1
- package/dist/testing/fixtures/approved-head-inert-deps.js +1 -1
- package/dist/testing/fixtures/approved-head-inert-deps.js.map +1 -1
- package/dist/testing/fixtures/completion-event-deps.d.ts +2 -1
- package/dist/testing/fixtures/completion-event-deps.d.ts.map +1 -1
- package/dist/testing/fixtures/completion-event-deps.js +5 -1
- package/dist/testing/fixtures/completion-event-deps.js.map +1 -1
- package/dist/testing/fixtures/index.js +1 -1
- package/dist/testing/fixtures/pending-spawn-world.d.ts.map +1 -1
- package/dist/testing/fixtures/pending-spawn-world.js +2 -1
- package/dist/testing/fixtures/pending-spawn-world.js.map +1 -1
- package/dist/testing/fixtures/review-exit-deps.js +2 -2
- package/dist/testing/fixtures/review-exit-deps.js.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/qa-packet.d.ts +39 -0
- package/dist/types/qa-packet.d.ts.map +1 -0
- package/dist/types/qa-packet.js +50 -0
- package/dist/types/qa-packet.js.map +1 -0
- package/dist/verify/attribution/rework-attribution.d.ts +50 -111
- package/dist/verify/attribution/rework-attribution.d.ts.map +1 -1
- package/dist/verify/attribution/rework-attribution.js +215 -71
- package/dist/verify/attribution/rework-attribution.js.map +1 -1
- package/dist/verify/gate-verdicts/commit-verdict-record.d.ts +9 -0
- package/dist/verify/gate-verdicts/commit-verdict-record.d.ts.map +1 -1
- package/dist/verify/gate-verdicts/commit-verdict-record.js +27 -1
- package/dist/verify/gate-verdicts/commit-verdict-record.js.map +1 -1
- package/dist/verify/gate-verdicts/judged-ancestor.d.ts.map +1 -1
- package/dist/verify/gate-verdicts/judged-ancestor.js +2 -1
- package/dist/verify/gate-verdicts/judged-ancestor.js.map +1 -1
- package/dist/verify/outcome-routing/gate-rework-reopen.d.ts +30 -0
- package/dist/verify/outcome-routing/gate-rework-reopen.d.ts.map +1 -0
- package/dist/verify/outcome-routing/gate-rework-reopen.js +37 -0
- package/dist/verify/outcome-routing/gate-rework-reopen.js.map +1 -0
- package/dist/verify/outcome-routing/verification-rework-issue.d.ts +114 -25
- package/dist/verify/outcome-routing/verification-rework-issue.d.ts.map +1 -1
- package/dist/verify/outcome-routing/verification-rework-issue.js +147 -88
- package/dist/verify/outcome-routing/verification-rework-issue.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,323 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* Is a QA-author answer FAITHFUL to its focus? Every case quotes one of the
|
|
6
|
+
* criteria the pass was shown (the focus's acceptance criteria, each
|
|
7
|
+
* non-cancelled delivery's, each armed baseline criterion -- normalized, never
|
|
8
|
+
* paraphrased), and on a re-entry after a QA fail every case of the previous
|
|
9
|
+
* packet whose criterion still applies is carried forward: a case names it
|
|
10
|
+
* with priorCaseId and quotes the criterion it tested.
|
|
11
|
+
*
|
|
12
|
+
* What a carried case IS comes from the record, not from the answer:
|
|
13
|
+
* stampQaPriorResults marks it from the result a person recorded for the case
|
|
14
|
+
* it carries (failed: a retest, with retest: true and retestOf; passed:
|
|
15
|
+
* previously passed; not run: needs a run), and removes a mark the model wrote
|
|
16
|
+
* that the record does not bear out. judgeQaAnswer stamps an answer once and
|
|
17
|
+
* checks the stamped cases, so the cases the pass writes are the cases that
|
|
18
|
+
* were judged.
|
|
19
|
+
*
|
|
20
|
+
* The daemon runs this BEFORE it asks for the ready write, so an unfaithful
|
|
21
|
+
* answer can be re-asked (qa-author-runner.ts) instead of only refused. The
|
|
22
|
+
* authoritative rule is the edge's: checkQaPacketFaithful in
|
|
23
|
+
* src/lib/qa/qa-packet.ts (mirrored to supabase/functions/_shared), which
|
|
24
|
+
* daemon_qa_packet_write applies before a ready or no_ui_surface write. The
|
|
25
|
+
* daemon cannot import src/lib, so this is its own copy; qa-author-
|
|
26
|
+
* faithfulness.test.ts runs the SAME conformance tables
|
|
27
|
+
* (src/lib/qa/__fixtures__/qa-faithfulness-cases.json) the canonical vitest
|
|
28
|
+
* runs, so the two cannot drift.
|
|
29
|
+
*/
|
|
30
|
+
import { qaPreviousCases } from '../queries/qa-packets/qa-packets.js';
|
|
31
|
+
import { QA_CASE_RESULT } from '../types/qa-packet.js';
|
|
32
|
+
const QUOTE_PAIRS = [
|
|
33
|
+
['"', '"'],
|
|
34
|
+
["'", "'"],
|
|
35
|
+
['“', '”'],
|
|
36
|
+
['‘', '’'],
|
|
37
|
+
];
|
|
38
|
+
const LIST_MARKER = /^(?:[-*+•]|\d+[.)])\s+/;
|
|
39
|
+
const QUOTE_MAX_CHARS = 160;
|
|
40
|
+
function stripQuotePair(text) {
|
|
41
|
+
for (const [open, close] of QUOTE_PAIRS) {
|
|
42
|
+
if (text.length >= 2 && text.startsWith(open) && text.endsWith(close))
|
|
43
|
+
return text.slice(1, -1).trim();
|
|
44
|
+
}
|
|
45
|
+
return text;
|
|
46
|
+
}
|
|
47
|
+
function stripListMarker(text) {
|
|
48
|
+
return text.replace(LIST_MARKER, '').trim();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The one normalization both sides of a criterion comparison get. Pure and
|
|
52
|
+
* idempotent: trim; strip surrounding quotes and a leading list marker, one
|
|
53
|
+
* layer at a time until neither is left; collapse whitespace runs to one
|
|
54
|
+
* space. Case, punctuation and wording are kept.
|
|
55
|
+
*/
|
|
56
|
+
export function normalizeQaCriterion(text) {
|
|
57
|
+
let t = text.trim();
|
|
58
|
+
for (;;) {
|
|
59
|
+
const next = stripListMarker(stripQuotePair(t));
|
|
60
|
+
if (next === t)
|
|
61
|
+
break;
|
|
62
|
+
t = next;
|
|
63
|
+
}
|
|
64
|
+
return t.replace(/\s+/g, ' ');
|
|
65
|
+
}
|
|
66
|
+
/** Every whole text block and each of its non-empty lines, normalized and de-duplicated. Pure. */
|
|
67
|
+
export function qaCriteriaFromTexts(texts) {
|
|
68
|
+
const out = new Set();
|
|
69
|
+
for (const text of texts) {
|
|
70
|
+
if (typeof text !== 'string')
|
|
71
|
+
continue;
|
|
72
|
+
for (const piece of [text, ...text.split(/\r?\n/)]) {
|
|
73
|
+
const normalized = normalizeQaCriterion(piece);
|
|
74
|
+
if (normalized)
|
|
75
|
+
out.add(normalized);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return [...out];
|
|
79
|
+
}
|
|
80
|
+
function isRecord(value) {
|
|
81
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
82
|
+
}
|
|
83
|
+
/** The criterion strings of an armed_baseline_criteria array; anything else yields none. Pure. */
|
|
84
|
+
export function qaArmedBaselineCriterionTexts(value) {
|
|
85
|
+
if (!Array.isArray(value))
|
|
86
|
+
return [];
|
|
87
|
+
return value.flatMap((entry) => {
|
|
88
|
+
if (!isRecord(entry))
|
|
89
|
+
return [];
|
|
90
|
+
const criterion = entry.criterion;
|
|
91
|
+
return typeof criterion === 'string' && criterion.trim().length > 0 ? [criterion] : [];
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function quoteForViolation(text) {
|
|
95
|
+
const t = text.trim();
|
|
96
|
+
return JSON.stringify(t.length > QUOTE_MAX_CHARS ? `${t.slice(0, QUOTE_MAX_CHARS)}...` : t);
|
|
97
|
+
}
|
|
98
|
+
function nonEmptyString(value) {
|
|
99
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
100
|
+
}
|
|
101
|
+
function trimmedString(value) {
|
|
102
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
103
|
+
}
|
|
104
|
+
/** Does a case's priorCaseId or retestOf name the previous case `id`? An absent field names nothing. */
|
|
105
|
+
function namesPreviousCase(field, id) {
|
|
106
|
+
return typeof field === 'string' && field.trim() === id;
|
|
107
|
+
}
|
|
108
|
+
/** A recorded result in the words a violation uses. */
|
|
109
|
+
function priorResultWords(result) {
|
|
110
|
+
if (result === QA_CASE_RESULT.FAILED)
|
|
111
|
+
return 'failed';
|
|
112
|
+
return result === QA_CASE_RESULT.PASSED ? 'passed' : 'not-run';
|
|
113
|
+
}
|
|
114
|
+
/** The previous case a case names -- its priorCaseId, else its retestOf -- trimmed; '' when it names none. */
|
|
115
|
+
function carriedCaseRef(c) {
|
|
116
|
+
if (nonEmptyString(c.priorCaseId))
|
|
117
|
+
return c.priorCaseId.trim();
|
|
118
|
+
return nonEmptyString(c.retestOf) ? c.retestOf.trim() : '';
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Mark each case that carries a previous case forward, from the result a
|
|
122
|
+
* person RECORDED for that case -- never from what the writer claimed. Pure:
|
|
123
|
+
* returns new objects and leaves its input untouched.
|
|
124
|
+
*
|
|
125
|
+
* A case names the previous case it carries with priorCaseId (or with retestOf
|
|
126
|
+
* alone). It names a previous case: priorCaseId and priorResult are set from
|
|
127
|
+
* the record; a recorded failure also sets retest: true and retestOf, and any
|
|
128
|
+
* other result REMOVES both. It names none: a priorResult the writer supplied
|
|
129
|
+
* is removed and the case is otherwise left alone. It names a case the
|
|
130
|
+
* previous cycle did not have: priorResult is removed and the rest stays as
|
|
131
|
+
* written, for checkQaPacketFaithful to report.
|
|
132
|
+
*/
|
|
133
|
+
export function stampQaPriorResults(cases, previousCases) {
|
|
134
|
+
const recorded = new Map(previousCases.map((p) => [p.id.trim(), p.result]));
|
|
135
|
+
return cases.map((c) => {
|
|
136
|
+
const stamped = { ...c };
|
|
137
|
+
delete stamped.priorResult;
|
|
138
|
+
const ref = carriedCaseRef(c);
|
|
139
|
+
const result = ref ? recorded.get(ref) : undefined;
|
|
140
|
+
if (result !== undefined) {
|
|
141
|
+
stamped.priorCaseId = ref;
|
|
142
|
+
stamped.priorResult = result;
|
|
143
|
+
if (result === QA_CASE_RESULT.FAILED) {
|
|
144
|
+
stamped.retest = true;
|
|
145
|
+
stamped.retestOf = ref;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
delete stamped.retest;
|
|
149
|
+
delete stamped.retestOf;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// A copy of `c` with only its four marks rewritten, so it is still a C.
|
|
153
|
+
return stamped;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/** Rule 3 for one case: its retest flag and retestOf agree, name a failed case and quote the criterion it tested. */
|
|
157
|
+
function retestViolation(c, failed) {
|
|
158
|
+
const retestOf = trimmedString(c.retestOf);
|
|
159
|
+
const failedList = failed.size > 0 ? `failed: ${[...failed.keys()].join(', ')}` : 'it failed no case';
|
|
160
|
+
if (c.retest === true && !retestOf)
|
|
161
|
+
return `case ${c.id}: is flagged "retest": true but names no retestOf`;
|
|
162
|
+
if (!retestOf)
|
|
163
|
+
return null;
|
|
164
|
+
if (c.retest !== true)
|
|
165
|
+
return `case ${c.id}: names retestOf ${quoteForViolation(retestOf)} but is not flagged "retest": true`;
|
|
166
|
+
const tested = failed.get(retestOf);
|
|
167
|
+
if (tested === undefined) {
|
|
168
|
+
return `case ${c.id}: retestOf ${quoteForViolation(retestOf)} is not a case the human failed in the previous QA cycle (${failedList})`;
|
|
169
|
+
}
|
|
170
|
+
if (tested !== normalizeQaCriterion(c.criterion)) {
|
|
171
|
+
return (`case ${c.id}: re-tests failed case ${retestOf} but quotes a different criterion -- a retest quotes the ` +
|
|
172
|
+
`criterion the failed case tested, ${quoteForViolation(tested)}`);
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Rule 5 for one case that names a priorCaseId: it is a case of the previous
|
|
178
|
+
* cycle, the case quotes the criterion that case tested, and a retestOf beside
|
|
179
|
+
* it names the same case. A well-formed retest of that case has its criterion
|
|
180
|
+
* judged by rule 3, so the mismatch is reported once.
|
|
181
|
+
*/
|
|
182
|
+
function carriedCaseViolations(c, previous) {
|
|
183
|
+
const priorCaseId = trimmedString(c.priorCaseId);
|
|
184
|
+
if (!priorCaseId)
|
|
185
|
+
return [];
|
|
186
|
+
const out = [];
|
|
187
|
+
const retestOf = trimmedString(c.retestOf);
|
|
188
|
+
const tested = previous.all.get(priorCaseId);
|
|
189
|
+
const judgedAsRetest = c.retest === true && retestOf === priorCaseId && previous.failed.has(priorCaseId);
|
|
190
|
+
if (tested === undefined) {
|
|
191
|
+
const known = previous.all.size > 0 ? `its cases: ${[...previous.all.keys()].join(', ')}` : 'it had no cases';
|
|
192
|
+
out.push(`case ${c.id}: priorCaseId ${quoteForViolation(priorCaseId)} is not a case of the previous QA cycle (${known})`);
|
|
193
|
+
}
|
|
194
|
+
else if (!judgedAsRetest && tested !== normalizeQaCriterion(c.criterion)) {
|
|
195
|
+
out.push(`case ${c.id}: carries previous case ${priorCaseId} forward but quotes a different criterion -- a carried ` +
|
|
196
|
+
`case quotes the criterion that case tested, ${quoteForViolation(tested)}`);
|
|
197
|
+
}
|
|
198
|
+
if (retestOf && retestOf !== priorCaseId) {
|
|
199
|
+
out.push(`case ${c.id}: names priorCaseId ${quoteForViolation(priorCaseId)} but retestOf ${quoteForViolation(retestOf)} ` +
|
|
200
|
+
'-- a case carries ONE previous case forward, so the two must be equal');
|
|
201
|
+
}
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
/** Rules 1, 3 and 5 for one case. */
|
|
205
|
+
function caseViolations(c, quotable, previous) {
|
|
206
|
+
const out = [];
|
|
207
|
+
if (!quotable.has(normalizeQaCriterion(c.criterion))) {
|
|
208
|
+
out.push(`case ${c.id}: criterion ${quoteForViolation(c.criterion)} is not one of the focus's acceptance criteria ` +
|
|
209
|
+
'(copy one line, without its bullet, or one whole block, exactly)');
|
|
210
|
+
}
|
|
211
|
+
const retest = retestViolation(c, previous.failed);
|
|
212
|
+
if (retest)
|
|
213
|
+
out.push(retest);
|
|
214
|
+
out.push(...carriedCaseViolations(c, previous));
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
/** Rule 4: what a no-UI-surface packet still owes the previous cycle, one line per kind of debt. */
|
|
218
|
+
function noUiSurfaceViolations(owedRetests, owedCarries) {
|
|
219
|
+
const out = [];
|
|
220
|
+
if (owedRetests.length > 0) {
|
|
221
|
+
out.push(`the packet marks no UI surface, but the previous QA cycle failed UI case(s) ${owedRetests.map((f) => f.id).join(', ')} ` +
|
|
222
|
+
'whose criteria still apply; each needs a retest case');
|
|
223
|
+
}
|
|
224
|
+
if (owedCarries.length > 0) {
|
|
225
|
+
const named = owedCarries.map((p) => `${p.id} (${priorResultWords(p.result)})`).join(', ');
|
|
226
|
+
out.push(`the packet marks no UI surface, but the previous QA cycle's UI case(s) ${named} still apply -- their ` +
|
|
227
|
+
'criteria are still listed; each needs a case that carries it forward');
|
|
228
|
+
}
|
|
229
|
+
return out;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Is the packet faithful? Pure. One human-readable line per violation; [] is
|
|
233
|
+
* faithful. Rules: (1) every criterion is one of `criteria`; (2) every
|
|
234
|
+
* previously FAILED case whose criterion still applies is re-tested by a case
|
|
235
|
+
* (retest: true, retestOf: its id) quoting that same criterion, whatever
|
|
236
|
+
* `carriesPriorResults` says; (3) a retest case carries both flag and
|
|
237
|
+
* retestOf, naming a failed case, and quotes the criterion that case tested;
|
|
238
|
+
* (4) no UI surface is refused while a retest is owed -- and, when
|
|
239
|
+
* `carriesPriorResults`, while ANY previous case still applies; (5) a case
|
|
240
|
+
* naming a priorCaseId names a case of the previous cycle, quotes the
|
|
241
|
+
* criterion it tested, and names the same case in any retestOf; (6) ONLY when
|
|
242
|
+
* `carriesPriorResults`: every previous case that passed or was not run, and
|
|
243
|
+
* whose criterion still applies, is carried forward by a case whose
|
|
244
|
+
* priorCaseId is its id, quoting that same criterion.
|
|
245
|
+
*/
|
|
246
|
+
export function checkQaPacketFaithful(input) {
|
|
247
|
+
const quotable = new Set(input.criteria.map(normalizeQaCriterion).filter((c) => c.length > 0));
|
|
248
|
+
const keyed = input.previousCases.map((p) => ({ id: p.id.trim(), criterion: normalizeQaCriterion(p.criterion), result: p.result }));
|
|
249
|
+
const previous = {
|
|
250
|
+
all: new Map(keyed.map((p) => [p.id, p.criterion])),
|
|
251
|
+
failed: new Map(keyed.filter((p) => p.result === QA_CASE_RESULT.FAILED).map((p) => [p.id, p.criterion])),
|
|
252
|
+
};
|
|
253
|
+
const violations = input.cases.flatMap((c) => caseViolations(c, quotable, previous));
|
|
254
|
+
const applies = input.previousCases.filter((p) => quotable.has(normalizeQaCriterion(p.criterion)));
|
|
255
|
+
const owedRetests = applies.filter((p) => p.result === QA_CASE_RESULT.FAILED);
|
|
256
|
+
const owedCarries = input.carriesPriorResults ? applies.filter((p) => p.result !== QA_CASE_RESULT.FAILED) : [];
|
|
257
|
+
if (input.noUiSurface && owedRetests.length + owedCarries.length > 0) {
|
|
258
|
+
return [...violations, ...noUiSurfaceViolations(owedRetests, owedCarries)];
|
|
259
|
+
}
|
|
260
|
+
// A writer that carries names the case; the retest flags are stamped from the record.
|
|
261
|
+
const retestHow = input.carriesPriorResults ? '"priorCaseId": ' : '"retest": true, "retestOf": ';
|
|
262
|
+
for (const f of owedRetests) {
|
|
263
|
+
const id = f.id.trim();
|
|
264
|
+
const criterion = normalizeQaCriterion(f.criterion);
|
|
265
|
+
const retested = input.cases.some((c) => c.retest === true && namesPreviousCase(c.retestOf, id) && normalizeQaCriterion(c.criterion) === criterion);
|
|
266
|
+
if (!retested) {
|
|
267
|
+
violations.push(`previous failed case ${id} (criterion ${quoteForViolation(f.criterion)}) still applies but no case re-tests it: ` +
|
|
268
|
+
`add a case quoting that criterion with ${retestHow}"${id}"`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
for (const p of owedCarries) {
|
|
272
|
+
const id = p.id.trim();
|
|
273
|
+
const criterion = normalizeQaCriterion(p.criterion);
|
|
274
|
+
const carried = input.cases.some((c) => namesPreviousCase(c.priorCaseId, id) && normalizeQaCriterion(c.criterion) === criterion);
|
|
275
|
+
if (!carried) {
|
|
276
|
+
violations.push(`previous ${priorResultWords(p.result)} case ${id} (criterion ${quoteForViolation(p.criterion)}) still applies ` +
|
|
277
|
+
`but no case carries it forward: add a case quoting that criterion with "priorCaseId": "${id}"`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return violations;
|
|
281
|
+
}
|
|
282
|
+
/** The fields the rule reads of a structurally parsed answer case (qa-author-output.ts checked id and criterion). */
|
|
283
|
+
function toFaithfulnessCase(c) {
|
|
284
|
+
const out = {
|
|
285
|
+
id: typeof c.id === 'string' ? c.id.trim() : '',
|
|
286
|
+
criterion: typeof c.criterion === 'string' ? c.criterion : '',
|
|
287
|
+
};
|
|
288
|
+
if (c.retest === true)
|
|
289
|
+
out.retest = true;
|
|
290
|
+
if (nonEmptyString(c.retestOf))
|
|
291
|
+
out.retestOf = c.retestOf.trim();
|
|
292
|
+
if (nonEmptyString(c.priorCaseId))
|
|
293
|
+
out.priorCaseId = c.priorCaseId.trim();
|
|
294
|
+
return out;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Judge one parsed answer against what the candidate read handed the author:
|
|
298
|
+
* stamp its cases from the previous cycle's recorded results, then check the
|
|
299
|
+
* STAMPED cases as a writer that carries every previous case forward. Pure.
|
|
300
|
+
* The one place an answer is stamped: the cases returned are the cases checked.
|
|
301
|
+
*/
|
|
302
|
+
export function judgeQaAnswer(candidate, answer) {
|
|
303
|
+
const previousCases = qaPreviousCases(candidate);
|
|
304
|
+
const written = answer.kind === 'cases' ? answer.cases.map((c) => (isRecord(c) ? c : {})) : [];
|
|
305
|
+
const cases = stampQaPriorResults(written, previousCases);
|
|
306
|
+
const violations = checkQaPacketFaithful({
|
|
307
|
+
cases: cases.map(toFaithfulnessCase),
|
|
308
|
+
noUiSurface: answer.kind === 'no_ui_surface',
|
|
309
|
+
criteria: qaCriteriaFromTexts([
|
|
310
|
+
candidate.focusAcceptanceCriteria,
|
|
311
|
+
...candidate.deliveries.map((d) => d.acceptanceCriteria),
|
|
312
|
+
...qaArmedBaselineCriterionTexts(candidate.armedBaselineCriteria),
|
|
313
|
+
]),
|
|
314
|
+
previousCases,
|
|
315
|
+
carriesPriorResults: true,
|
|
316
|
+
});
|
|
317
|
+
return { cases, violations };
|
|
318
|
+
}
|
|
319
|
+
/** The violations of one parsed answer (judgeQaAnswer, without the stamped cases). Pure. */
|
|
320
|
+
export function qaAnswerFaithfulnessViolations(candidate, answer) {
|
|
321
|
+
return judgeQaAnswer(candidate, answer).violations;
|
|
322
|
+
}
|
|
323
|
+
//# 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,26 @@
|
|
|
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. Every other field of a
|
|
9
|
+
* case is kept as written -- the priorCaseId that names the previous cycle's
|
|
10
|
+
* case it carries forward among them, which qa-author-faithfulness.ts reads.
|
|
11
|
+
* The authoritative case contract is the edge's
|
|
12
|
+
* (supabase/functions/_shared/qa-packet.ts parseQaCases): a ready write it
|
|
13
|
+
* refuses is recorded as a failed packet.
|
|
14
|
+
*/
|
|
15
|
+
export type QaAuthorOutput = {
|
|
16
|
+
kind: 'cases';
|
|
17
|
+
cases: unknown[];
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'no_ui_surface';
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'error';
|
|
22
|
+
error: string;
|
|
23
|
+
};
|
|
24
|
+
/** Parse a QA-author answer. Pure. */
|
|
25
|
+
export declare function parseQaAuthorOutput(text: string): QaAuthorOutput;
|
|
26
|
+
//# 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;;;;;;;;;;;;;GAaG;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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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The QA-author pass: for every configured product, the foci that wait in its
|
|
3
|
+
* QA queue with no packet for the cycle (or an abandoned pending one) get a UI
|
|
4
|
+
* test packet, one focus at a time.
|
|
5
|
+
*
|
|
6
|
+
* Per candidate: write a pending packet (or refresh the abandoned one) -> author
|
|
7
|
+
* it (qa-author-runner.ts) -> write it ready, no_ui_surface or failed with the
|
|
8
|
+
* reason. A ready or no_ui_surface write says the author was told to carry the
|
|
9
|
+
* previous QA cycle's cases forward (carriesPriorResults), so the edge holds
|
|
10
|
+
* the packet to that. A ready write the edge refuses is written failed with
|
|
11
|
+
* the edge's message. A provider rate limit leaves the packet pending: the
|
|
12
|
+
* edge re-offers a pending packet untouched for 30 minutes, which is the
|
|
13
|
+
* retry. The pass's only writes are those packet writes (product-api
|
|
14
|
+
* daemon_qa_packet_write); it never writes a focus's status, stage, execution
|
|
15
|
+
* status, review state or arm.
|
|
16
|
+
*
|
|
17
|
+
* IT NEVER DELAYS ARMED WORK. It acquires no ResourceGovernor slot and never
|
|
18
|
+
* waits on one; it reads the governor's utilization instead and does not start
|
|
19
|
+
* (or continue to the next focus) while every session slot is taken or a spawn
|
|
20
|
+
* is queued for one, nor while the claude provider's rate-limit window is
|
|
21
|
+
* open (nor while the daemon is read-only: it could write no packet). At most ONE pass runs per daemon: a tick that finds one in flight
|
|
22
|
+
* returns at once.
|
|
23
|
+
*/
|
|
24
|
+
import type { ResourceGovernor, ResourceUtilization } from '@telora/daemon-core';
|
|
25
|
+
import type { QaPacketCandidate, QaPacketWrite, QaPacketWriteResult } from '../queries/qa-packets/qa-packets.js';
|
|
26
|
+
import type { QaAuthorResult } from './qa-author-runner.js';
|
|
27
|
+
/** The loop's cadence. */
|
|
28
|
+
export declare const QA_AUTHOR_PASS_INTERVAL_MS = 60000;
|
|
29
|
+
export interface QaAuthorPassDeps {
|
|
30
|
+
products: ReadonlyArray<{
|
|
31
|
+
id: string;
|
|
32
|
+
}>;
|
|
33
|
+
readCandidates: (productId: string) => Promise<QaPacketCandidate[]>;
|
|
34
|
+
writePacket: (write: QaPacketWrite) => Promise<QaPacketWriteResult>;
|
|
35
|
+
author: (productId: string, candidate: QaPacketCandidate) => Promise<QaAuthorResult>;
|
|
36
|
+
/** Why the pass must not start or go on to the next focus now, or null. */
|
|
37
|
+
holdReason: () => string | null;
|
|
38
|
+
}
|
|
39
|
+
/** The pass's deps over the running daemon, or null before it has a live config (composition/qa-author-pass-context.ts). */
|
|
40
|
+
export type QaAuthorPassDepsSource = (governor: ResourceGovernor | null) => QaAuthorPassDeps | null;
|
|
41
|
+
export interface QaAuthorPassOutcome {
|
|
42
|
+
/** Why the pass stopped early or did not start, when it did. */
|
|
43
|
+
skipped: string | null;
|
|
44
|
+
/** Packets written ready or no_ui_surface. */
|
|
45
|
+
written: number;
|
|
46
|
+
/** Packets written failed. */
|
|
47
|
+
failed: number;
|
|
48
|
+
/** Packets left pending (provider rate limit). */
|
|
49
|
+
held: number;
|
|
50
|
+
/** Candidates no packet could be written for, and failed reads. */
|
|
51
|
+
errors: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Why armed work, the provider or the daemon's mode forbids a pass now, or
|
|
55
|
+
* null. Pure: armed work keeps priority while every slot is taken or a spawn
|
|
56
|
+
* waits for one; a read-only daemon could write no packet.
|
|
57
|
+
*/
|
|
58
|
+
export declare function qaPassHoldReason(utilization: ResourceUtilization | null, providerHoldUntilMs: number | null, readOnly: boolean): string | null;
|
|
59
|
+
/** Run one pass over every product. One product's failed read never stops another's. */
|
|
60
|
+
export declare function runQaAuthorPass(deps: QaAuthorPassDeps): Promise<QaAuthorPassOutcome>;
|
|
61
|
+
/** The loop tick: one pass over the live deps, never throwing into the timer. */
|
|
62
|
+
export declare function runQaAuthorPassSafely(source: QaAuthorPassDepsSource, governor: ResourceGovernor | null): Promise<void>;
|
|
63
|
+
//# sourceMappingURL=qa-author-pass.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-author-pass.d.ts","sourceRoot":"","sources":["../../src/qa/qa-author-pass.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAEjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAEjH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAI5D,0BAA0B;AAC1B,eAAO,MAAM,0BAA0B,QAAS,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxC,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACpE,WAAW,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACpE,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAiB,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACrF,2EAA2E;IAC3E,UAAU,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,4HAA4H;AAC5H,MAAM,MAAM,sBAAsB,GAAG,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,KAAK,gBAAgB,GAAG,IAAI,CAAC;AAEpG,MAAM,WAAW,mBAAmB;IAClC,gEAAgE;IAChE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,mBAAmB,GAAG,IAAI,EACvC,mBAAmB,EAAE,MAAM,GAAG,IAAI,EAClC,QAAQ,EAAE,OAAO,GAChB,MAAM,GAAG,IAAI,CAMf;AAoGD,wFAAwF;AACxF,wBAAsB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA0B1F;AAED,iFAAiF;AACjF,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,sBAAsB,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAc5H"}
|