wicked-crew 0.7.30 → 0.7.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.
Files changed (115) hide show
  1. package/dist/api/chat-scope.d.ts +15 -2
  2. package/dist/api/chat-scope.d.ts.map +1 -1
  3. package/dist/api/chat-scope.js +7 -2
  4. package/dist/api/chat-scope.js.map +1 -1
  5. package/dist/api/eval-sample.d.ts +12 -12
  6. package/dist/api/routes.d.ts +10 -0
  7. package/dist/api/routes.d.ts.map +1 -1
  8. package/dist/api/routes.js +231 -37
  9. package/dist/api/routes.js.map +1 -1
  10. package/dist/api/run-files.d.ts +26 -0
  11. package/dist/api/run-files.d.ts.map +1 -1
  12. package/dist/api/run-files.js +78 -0
  13. package/dist/api/run-files.js.map +1 -1
  14. package/dist/api/seat-health.d.ts +33 -0
  15. package/dist/api/seat-health.d.ts.map +1 -1
  16. package/dist/api/seat-health.js +86 -2
  17. package/dist/api/seat-health.js.map +1 -1
  18. package/dist/api/seat-signin.d.ts +8 -0
  19. package/dist/api/seat-signin.d.ts.map +1 -1
  20. package/dist/api/seat-signin.js +48 -3
  21. package/dist/api/seat-signin.js.map +1 -1
  22. package/dist/api/seat-standing.d.ts +14 -2
  23. package/dist/api/seat-standing.d.ts.map +1 -1
  24. package/dist/api/seat-standing.js +14 -4
  25. package/dist/api/seat-standing.js.map +1 -1
  26. package/dist/api/server.d.ts.map +1 -1
  27. package/dist/api/server.js +41 -4
  28. package/dist/api/server.js.map +1 -1
  29. package/dist/api/skills.d.ts +2 -2
  30. package/dist/api/testing.d.ts +28 -1
  31. package/dist/api/testing.d.ts.map +1 -1
  32. package/dist/api/testing.js +190 -0
  33. package/dist/api/testing.js.map +1 -1
  34. package/dist/campaigns/rollup.d.ts +4 -0
  35. package/dist/campaigns/rollup.d.ts.map +1 -1
  36. package/dist/campaigns/rollup.js.map +1 -1
  37. package/dist/campaigns/routes.d.ts +3 -1
  38. package/dist/campaigns/routes.d.ts.map +1 -1
  39. package/dist/campaigns/routes.js +3 -0
  40. package/dist/campaigns/routes.js.map +1 -1
  41. package/dist/core/adapter.d.ts.map +1 -1
  42. package/dist/core/adapter.js +23 -2
  43. package/dist/core/adapter.js.map +1 -1
  44. package/dist/core/deliver-text.d.ts +7 -0
  45. package/dist/core/deliver-text.d.ts.map +1 -1
  46. package/dist/core/deliver-text.js +41 -2
  47. package/dist/core/deliver-text.js.map +1 -1
  48. package/dist/core/engine-roster.d.ts +68 -0
  49. package/dist/core/engine-roster.d.ts.map +1 -0
  50. package/dist/core/engine-roster.js +156 -0
  51. package/dist/core/engine-roster.js.map +1 -0
  52. package/dist/interactive/chat-events.d.ts.map +1 -1
  53. package/dist/interactive/chat-events.js +26 -4
  54. package/dist/interactive/chat-events.js.map +1 -1
  55. package/dist/interactive/council-outcome.d.ts +28 -1
  56. package/dist/interactive/council-outcome.d.ts.map +1 -1
  57. package/dist/interactive/council-outcome.js +51 -1
  58. package/dist/interactive/council-outcome.js.map +1 -1
  59. package/dist/interactive/demo-events.d.ts.map +1 -1
  60. package/dist/interactive/demo-events.js +26 -4
  61. package/dist/interactive/demo-events.js.map +1 -1
  62. package/dist/interactive/doc-run-index.d.ts +56 -0
  63. package/dist/interactive/doc-run-index.d.ts.map +1 -0
  64. package/dist/interactive/doc-run-index.js +100 -0
  65. package/dist/interactive/doc-run-index.js.map +1 -0
  66. package/dist/interactive/docs-index.d.ts +68 -0
  67. package/dist/interactive/docs-index.d.ts.map +1 -0
  68. package/dist/interactive/docs-index.js +114 -0
  69. package/dist/interactive/docs-index.js.map +1 -0
  70. package/dist/interactive/draft-events.d.ts +18 -0
  71. package/dist/interactive/draft-events.d.ts.map +1 -1
  72. package/dist/interactive/draft-events.js +36 -3
  73. package/dist/interactive/draft-events.js.map +1 -1
  74. package/dist/interactive/edit-events.d.ts.map +1 -1
  75. package/dist/interactive/edit-events.js +26 -4
  76. package/dist/interactive/edit-events.js.map +1 -1
  77. package/dist/interactive/ledger.d.ts +2 -0
  78. package/dist/interactive/ledger.d.ts.map +1 -1
  79. package/dist/interactive/ledger.js +4 -0
  80. package/dist/interactive/ledger.js.map +1 -1
  81. package/dist/qe/acceptance.d.ts +72 -3
  82. package/dist/qe/acceptance.d.ts.map +1 -1
  83. package/dist/qe/acceptance.js +137 -17
  84. package/dist/qe/acceptance.js.map +1 -1
  85. package/dist/qe/author-workflow.d.ts +165 -0
  86. package/dist/qe/author-workflow.d.ts.map +1 -0
  87. package/dist/qe/author-workflow.js +377 -0
  88. package/dist/qe/author-workflow.js.map +1 -0
  89. package/dist/qe/ledger.d.ts +128 -28
  90. package/dist/qe/ledger.d.ts.map +1 -1
  91. package/dist/qe/ledger.js +272 -42
  92. package/dist/qe/ledger.js.map +1 -1
  93. package/dist/qe/test-sets.d.ts +112 -0
  94. package/dist/qe/test-sets.d.ts.map +1 -0
  95. package/dist/qe/test-sets.js +170 -0
  96. package/dist/qe/test-sets.js.map +1 -0
  97. package/dist/skills/refs.d.ts +37 -0
  98. package/dist/skills/refs.d.ts.map +1 -1
  99. package/dist/skills/refs.js +22 -0
  100. package/dist/skills/refs.js.map +1 -1
  101. package/dist/skills/runtime.d.ts +13 -1
  102. package/dist/skills/runtime.d.ts.map +1 -1
  103. package/dist/skills/runtime.js +88 -1
  104. package/dist/skills/runtime.js.map +1 -1
  105. package/dist/skills/store.d.ts +82 -5
  106. package/dist/skills/store.d.ts.map +1 -1
  107. package/dist/skills/store.js +109 -21
  108. package/dist/skills/store.js.map +1 -1
  109. package/dist/studio/assets/{index-_ydd-jKd.css → index-C0UZ2BxT.css} +1 -1
  110. package/dist/studio/assets/index-Clm0MXDF.js +544 -0
  111. package/dist/studio/index.html +2 -2
  112. package/dist/studio/testid-inventory.json +251 -5
  113. package/endpoint-manifest.json +24 -1
  114. package/package.json +3 -3
  115. package/dist/studio/assets/index-CWUS9Ump.js +0 -542
package/dist/qe/ledger.js CHANGED
@@ -6,20 +6,35 @@
6
6
  * this daemon owns: a DomainStore rooted at `<repo>/.wicked-qe/` (legacy
7
7
  * `<repo>/.wicked-testing/` — see qeLedgerRoot) holding `runs` and
8
8
  * `verdicts`, plus the public evidence manifest at
9
- * `<root>/evidence/<run-id>/manifest.json`. This module reads that state
10
- * through the wicked-ledger package API (public entry, no deep imports) and
11
- * never writes a domain record — the QE pipeline is the only writer.
9
+ * `<root>/evidence/<run-id>/manifest.json`.
12
10
  *
13
- * ONE deliberate mutation is allowed: `rebuildIndex()`, which regenerates the
14
- * store's DERIVED SQLite index from the canonical JSON files when the two
15
- * disagree (see `readAcceptanceState`). Canonical JSON is authoritative by the
16
- * ledger's own schema contract; the index is a cache, and rebuilding a cache
17
- * is not a write to the ledger's record of what happened.
11
+ * READ-ONLY means read-only (F-E2E-013). This module reads the ledger's
12
+ * CANONICAL JSON files directly and never opens a `DomainStore`: opening one
13
+ * is a write — `new DomainStore(root)` mkdirs the root, reaps `.tmp.*` files,
14
+ * creates `wicked-qe.db` (+ WAL/SHM) inside the checkout, runs migrations and
15
+ * a stale-run UPDATE sweep — and the index-healing `rebuildIndex()` this
16
+ * module used to call on top of that bulk-inserted every canonical row into
17
+ * the fresh index, which is where a legacy ledger's scenarios without
18
+ * `format_version` failed ("SQLite write failed … NOT NULL constraint"). A
19
+ * GET must leave the repository byte-identical. Canonical JSON is
20
+ * authoritative by the ledger's own schema contract (the SQLite index is a
21
+ * derived cache), so reading it directly loses nothing — and a record that
22
+ * cannot be parsed is surfaced as a read failure (`error`), never skipped
23
+ * into a cleaner answer.
24
+ *
25
+ * ATTRIBUTION (F-E2E-013): the gate answers about ONE crew run, so a verdict
26
+ * is served only when it can be tied to that run — see {@link ReadSubject}.
27
+ * "The newest PASS in the store" is not evidence about a run that recorded
28
+ * none (the regression: an onboarding run that failed at plan time was shown a
29
+ * two-month-old PASS from the repo's committed legacy ledger). Among the QE
30
+ * runs that ARE this run's, deny-dominates holds ACROSS them: each QE run's
31
+ * newest verdict is its current judgment, and one non-PASS among those denies
32
+ * (review of #539, F2 — a later PASS on scenario Y must not mask a FAIL on
33
+ * scenario X inside the same crew run).
18
34
  */
19
- import { existsSync, readdirSync } from 'node:fs';
35
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
20
36
  import { readFile } from 'node:fs/promises';
21
37
  import { isAbsolute, join } from 'node:path';
22
- import { createDomainStore } from 'wicked-ledger';
23
38
  /**
24
39
  * The ledger root directory name under a repo root.
25
40
  *
@@ -31,6 +46,15 @@ import { createDomainStore } from 'wicked-ledger';
31
46
  */
32
47
  export const DEFAULT_QE_LEDGER_DIRNAME = '.wicked-qe';
33
48
  export const LEGACY_QE_LEDGER_DIRNAME = '.wicked-testing';
49
+ /**
50
+ * The field a QE writer running INSIDE a governed run stamps on the ledger run
51
+ * (and/or verdict) it records: the crew run id the engine hands every worker as
52
+ * `WICKED_RUN_ID`. Canonical JSON preserves any field a writer supplies, so this
53
+ * needs no ledger schema change; a record carrying it is attributed to that run
54
+ * regardless of timing. (garden's QE runner does not stamp it yet — a wave-7
55
+ * follow-up; until then lifetime linkage below is the inferred fallback.)
56
+ */
57
+ export const CREW_RUN_ID_FIELD = 'crew_run_id';
34
58
  /** The configured ledger dir name: `WICKED_QE_LEDGER_DIR` env override, else the default. */
35
59
  export function qeLedgerDirName() {
36
60
  const env = process.env['WICKED_QE_LEDGER_DIR']?.trim();
@@ -57,32 +81,232 @@ export function qeLedgerRoot(repoRoot) {
57
81
  return legacy;
58
82
  return current;
59
83
  }
60
- /** True when the canonical JSON dir for a table has at least one record file. */
61
- function hasCanonicalRows(root, table) {
84
+ /**
85
+ * One line a human can read on `gate.reason`: HOW the served verdict was tied to
86
+ * the run, with the inferred kind labelled as such — an operator must be able to
87
+ * tell "the writer said so" from "it happened while the run was live".
88
+ */
89
+ export function describeAttribution(a) {
90
+ switch (a.kind) {
91
+ case 'pinned':
92
+ return `pinned to QE run ${a.qeRunId} by ?qeRun — caller-asserted linkage`;
93
+ case 'stamped':
94
+ return `QE run ${a.qeRunId} is stamped ${CREW_RUN_ID_FIELD} with this run's id by the writer`;
95
+ case 'run-window':
96
+ return (`INFERRED from this run's lifetime — QE run ${a.qeRunId} started ${a.qeRunStartedAt} inside it; ` +
97
+ `not stamped by the writer`);
98
+ default:
99
+ return a.reason;
100
+ }
101
+ }
102
+ /** Read one canonical JSON record file; throws a message naming the file on anything but a valid record. */
103
+ function readRecordFile(path, label) {
104
+ let raw;
62
105
  try {
63
- return readdirSync(join(root, table)).some((f) => f.endsWith('.json') && !f.includes('.tmp.'));
106
+ raw = readFileSync(path, 'utf8');
64
107
  }
65
- catch {
66
- return false;
108
+ catch (err) {
109
+ throw new Error(`${label}: ${err instanceof Error ? err.message : String(err)}`);
110
+ }
111
+ let parsed;
112
+ try {
113
+ parsed = JSON.parse(raw);
114
+ }
115
+ catch (err) {
116
+ throw new Error(`${label}: not valid JSON (${err instanceof Error ? err.message : String(err)})`);
117
+ }
118
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
119
+ throw new Error(`${label}: not a record object`);
120
+ }
121
+ const rec = parsed;
122
+ if (typeof rec['id'] !== 'string' || rec['id'] === '') {
123
+ throw new Error(`${label}: record has no id`);
124
+ }
125
+ return rec;
126
+ }
127
+ /**
128
+ * Every live record of a table, newest first — the canonical JSON files, read
129
+ * as the ledger's own JSON-only mode reads them (`*.json`, in-flight `.tmp.*`
130
+ * files skipped, soft-deleted rows dropped, `created_at` descending). A table
131
+ * dir that does not exist is an empty table; a file that cannot be parsed is a
132
+ * read FAILURE the caller must surface, not a row to skip.
133
+ */
134
+ function listCanonical(root, table) {
135
+ const dir = join(root, table);
136
+ let names;
137
+ try {
138
+ names = readdirSync(dir);
139
+ }
140
+ catch (err) {
141
+ if (err.code === 'ENOENT')
142
+ return [];
143
+ throw new Error(`${table}/: ${err instanceof Error ? err.message : String(err)}`);
144
+ }
145
+ const rows = [];
146
+ for (const name of names) {
147
+ if (!name.endsWith('.json') || /\.tmp\.\d+$/.test(name))
148
+ continue;
149
+ const rec = readRecordFile(join(dir, name), `${table}/${name}`);
150
+ if (rec.deleted)
151
+ continue;
152
+ rows.push(rec);
153
+ }
154
+ return rows.sort((a, b) => (b.created_at || '').localeCompare(a.created_at || ''));
155
+ }
156
+ /** One live record by id, or null when its file is absent (soft-deleted reads as absent too). */
157
+ function getCanonical(root, table, id) {
158
+ const path = join(root, table, `${id}.json`);
159
+ if (!existsSync(path))
160
+ return null;
161
+ const rec = readRecordFile(path, `${table}/${id}.json`);
162
+ return rec.deleted ? null : rec;
163
+ }
164
+ /** The `crew_run_id` a writer stamped on a record, when it carries one (the typed schema has no such column; canonical JSON keeps it). */
165
+ function stampedCrewRun(rec) {
166
+ const v = rec?.[CREW_RUN_ID_FIELD];
167
+ return typeof v === 'string' && v !== '' ? v : null;
168
+ }
169
+ /** Epoch millis of an ISO timestamp, or null when it does not parse. */
170
+ function epoch(iso) {
171
+ if (typeof iso !== 'string')
172
+ return null;
173
+ const ms = Date.parse(iso);
174
+ return Number.isNaN(ms) ? null : ms;
175
+ }
176
+ /**
177
+ * Pick the verdict attributable to `subject` from the ledger's live verdicts
178
+ * (newest first), resolving each verdict's run lazily.
179
+ *
180
+ * An explicit pin serves that QE run's newest verdict. Otherwise the ledger is
181
+ * folded to one entry PER QE RUN (its newest verdict — a QE run re-reviewed
182
+ * after its manifest was built keeps the newer row, as before), and a QE run
183
+ * is ATTRIBUTED when its run row or any of its verdicts is stamped with this
184
+ * crew run's id, or when its run row is dated and that start falls inside the
185
+ * crew run's lifetime. A QE run with no dated run row is never placed by
186
+ * inference (review of #539, F3): a verdict's own `created_at` says when it
187
+ * was RECORDED, not when the QE run ran, and using it would attribute more
188
+ * eagerly, not less. Across the attributed set deny-dominates holds: the newest
189
+ * non-PASS governs when there is one, else the newest PASS (F2). Nothing else
190
+ * in the store is this run's evidence, however recent or however green.
191
+ */
192
+ function attribute(root, verdicts, subject) {
193
+ if ('qeRunId' in subject) {
194
+ const verdict = verdicts.find((v) => v.run_id === subject.qeRunId) ?? null;
195
+ if (verdict === null) {
196
+ return {
197
+ verdict: null,
198
+ run: null,
199
+ attribution: { kind: 'none', reason: `QE run ${subject.qeRunId} has no verdict recorded` },
200
+ attributedVerdicts: 0,
201
+ };
202
+ }
203
+ return {
204
+ verdict,
205
+ run: getCanonical(root, 'runs', verdict.run_id),
206
+ attribution: { kind: 'pinned', qeRunId: verdict.run_id },
207
+ attributedVerdicts: 1,
208
+ };
209
+ }
210
+ const { runId, startedAt, finishedAt, logUnreadable } = subject.run;
211
+ if (verdicts.length === 0) {
212
+ return {
213
+ verdict: null,
214
+ run: null,
215
+ attribution: { kind: 'none', reason: 'the ledger records no verdict' },
216
+ attributedVerdicts: 0,
217
+ };
218
+ }
219
+ // Fold to one entry per QE run, in newest-verdict order (verdicts are newest first, so the
220
+ // first verdict seen for a run is its newest, and Map insertion order keeps runs newest-first).
221
+ const byRun = new Map();
222
+ for (const v of verdicts) {
223
+ let entry = byRun.get(v.run_id);
224
+ if (entry === undefined) {
225
+ entry = { newest: v, run: getCanonical(root, 'runs', v.run_id), stamped: false };
226
+ byRun.set(v.run_id, entry);
227
+ }
228
+ if (stampedCrewRun(v) === runId)
229
+ entry.stamped = true;
230
+ }
231
+ for (const entry of byRun.values()) {
232
+ if (stampedCrewRun(entry.run) === runId)
233
+ entry.stamped = true;
234
+ }
235
+ const windowEnd = finishedAt ?? Date.now();
236
+ const attributed = [];
237
+ let undatedRuns = 0;
238
+ for (const [qeRunId, e] of byRun) {
239
+ if (e.stamped) {
240
+ attributed.push({ verdict: e.newest, run: e.run, attribution: { kind: 'stamped', qeRunId } });
241
+ continue;
242
+ }
243
+ if (startedAt === null)
244
+ continue;
245
+ // The QE run's own start (the row's `started_at`, else its `created_at`) is the linkage
246
+ // instant. No dated run row ⇒ no inferred linkage: this QE run can only be attributed by
247
+ // stamp or pin, and is counted so the denial can say so.
248
+ const at = epoch(e.run?.started_at) ?? epoch(e.run?.created_at);
249
+ if (at === null) {
250
+ undatedRuns++;
251
+ continue;
252
+ }
253
+ if (at >= startedAt && at <= windowEnd) {
254
+ attributed.push({
255
+ verdict: e.newest,
256
+ run: e.run,
257
+ attribution: { kind: 'run-window', qeRunId, qeRunStartedAt: new Date(at).toISOString() },
258
+ });
259
+ }
67
260
  }
261
+ if (attributed.length > 0) {
262
+ // Deny-dominates ACROSS the attributed QE runs: any newest-per-run non-PASS governs (the
263
+ // newest such, so the reason cites the latest failure); only when every attributed QE run's
264
+ // newest verdict is PASS does the newest PASS govern.
265
+ const denying = attributed.find((a) => a.verdict.verdict !== 'PASS');
266
+ const pick = denying ?? attributed[0];
267
+ return { ...pick, attributedVerdicts: attributed.length };
268
+ }
269
+ // Nothing attributed. Say what the ledger DOES hold, so the answer is checkable.
270
+ const newest = verdicts[0];
271
+ const held = `the ledger holds ${verdicts.length} verdict${verdicts.length === 1 ? '' : 's'} across ${byRun.size} QE run${byRun.size === 1 ? '' : 's'}, ` +
272
+ `newest ${newest.verdict} (${newest.id}) at ${newest.created_at}`;
273
+ const notStamped = `none is stamped with run ${runId}`;
274
+ const undated = undatedRuns > 0
275
+ ? `; ${undatedRuns} QE run${undatedRuns === 1 ? ' has' : 's have'} no dated run row and cannot be placed by inference`
276
+ : '';
277
+ const none = (reason) => ({
278
+ verdict: null,
279
+ run: null,
280
+ attribution: { kind: 'none', reason },
281
+ attributedVerdicts: 0,
282
+ });
283
+ if (logUnreadable !== undefined) {
284
+ return none(`${held}; ${notStamped}, and the run's event log could not be read (${logUnreadable}), ` +
285
+ `so no verdict can be placed inside its lifetime${undated}`);
286
+ }
287
+ if (startedAt === null) {
288
+ return none(`${held}; ${notStamped}, and the run's start is not recorded ` +
289
+ `(no sessionStarted in its event log), so no verdict can be placed inside its lifetime${undated}`);
290
+ }
291
+ const started = new Date(startedAt).toISOString();
292
+ const newestAt = epoch(newest.created_at);
293
+ const placement = newestAt !== null && newestAt < startedAt
294
+ ? `before this run started (${started})`
295
+ : `outside this run's lifetime (started ${started}${finishedAt !== null ? `, finished ${new Date(finishedAt).toISOString()}` : ', still live'})`;
296
+ return none(`${held}, recorded ${placement}; ${notStamped}${undated}`);
68
297
  }
69
298
  /**
70
- * Read the ledger's current acceptance state for a repo.
299
+ * Read the ledger's acceptance state for a repo, scoped to `subject`.
71
300
  *
72
- * The newest verdict row governs (`opts.qeRunId` narrows to one QE run's
73
- * newest verdict instead). The verdicts TABLE is the system of record — the
74
- * manifest is the public artifact built from it at run finalization, returned
75
- * here as display evidence, never as a gate input: a run can legitimately be
76
- * re-reviewed after its manifest was built, and the newer verdict row wins.
301
+ * The verdicts TABLE is the system of record — the manifest is the public
302
+ * artifact built from it at run finalization, returned here as display
303
+ * evidence, never as a gate input: a run can legitimately be re-reviewed
304
+ * after its manifest was built, and the newer verdict row wins.
77
305
  *
78
- * Index-drift healing: the store is dual-write (canonical JSON + a derived
79
- * SQLite index) and a writer that degraded to JSON-only leaves the index
80
- * behind. When the index answers "no verdicts" but canonical verdict files
81
- * exist, the index is provably stale, so it is rebuilt from JSON once and
82
- * re-read — otherwise a ledger full of evidence would read as "missing
83
- * evidence ⇒ deny", a wrong answer in the honest direction but still wrong.
306
+ * Reads canonical JSON only (see the module header): nothing under the ledger
307
+ * root is created, modified or removed by this call.
84
308
  */
85
- export async function readAcceptanceState(repoRoot, opts) {
309
+ export async function readAcceptanceState(repoRoot, subject) {
86
310
  const root = qeLedgerRoot(repoRoot);
87
311
  const state = {
88
312
  root,
@@ -91,25 +315,25 @@ export async function readAcceptanceState(repoRoot, opts) {
91
315
  verdict: null,
92
316
  manifest: null,
93
317
  manifestPath: null,
318
+ attribution: { kind: 'none', reason: 'no ledger' },
319
+ ledgerVerdicts: 0,
320
+ attributedVerdicts: 0,
94
321
  };
95
- // Probe before opening: createDomainStore CREATES its root when absent, and
96
- // a read must not install an empty ledger into a repo that never had one.
322
+ // Probe before reading: a read must not install an empty ledger into a repo
323
+ // that never had one, and a missing root is its own answer.
97
324
  if (!existsSync(root))
98
325
  return state;
99
326
  state.found = true;
100
327
  try {
101
- const store = createDomainStore({ root });
102
- const filter = opts?.qeRunId !== undefined ? { run_id: opts.qeRunId } : undefined;
103
- let verdicts = store.list('verdicts', filter);
104
- if (verdicts.length === 0 && store.mode === 'sqlite+json' && hasCanonicalRows(root, 'verdicts')) {
105
- store.rebuildIndex();
106
- verdicts = store.list('verdicts', filter);
107
- }
108
- const verdict = verdicts[0] ?? null;
109
- state.verdict = verdict;
110
- if (verdict !== null) {
111
- state.run = store.get('runs', verdict.run_id);
112
- state.manifestPath = join(root, 'evidence', verdict.run_id, 'manifest.json');
328
+ const verdicts = listCanonical(root, 'verdicts');
329
+ state.ledgerVerdicts = verdicts.length;
330
+ const picked = attribute(root, verdicts, subject);
331
+ state.verdict = picked.verdict;
332
+ state.run = picked.run;
333
+ state.attribution = picked.attribution;
334
+ state.attributedVerdicts = picked.attributedVerdicts;
335
+ if (picked.verdict !== null) {
336
+ state.manifestPath = join(root, 'evidence', picked.verdict.run_id, 'manifest.json');
113
337
  state.manifest = await readManifest(state.manifestPath);
114
338
  }
115
339
  }
@@ -118,6 +342,12 @@ export async function readAcceptanceState(repoRoot, opts) {
118
342
  // still denies (deny-dominates), but the reason must name the read failure
119
343
  // so an operator fixes the store instead of hunting for a missing run.
120
344
  state.error = err instanceof Error ? err.message : String(err);
345
+ state.verdict = null;
346
+ state.run = null;
347
+ state.manifest = null;
348
+ state.manifestPath = null;
349
+ state.attribution = { kind: 'none', reason: `the ledger could not be read: ${state.error}` };
350
+ state.attributedVerdicts = 0;
121
351
  }
122
352
  return state;
123
353
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/qe/ledger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/qe/ledger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Test-set registration — the produced tests of a completed `qe-author-tests` run become a durable
3
+ * record the Test landing can show (wave 6, F-7R2-014).
4
+ *
5
+ * The landing (`GET /campaigns` → studio `/testing/campaigns`) used to stay "No tests yet" after a
6
+ * completed New test: a single-repo launch registered no engine campaign (`campaignRegistered:
7
+ * false`), no label group, and its verdicts existed nowhere the Testing UI reads. Two things fix it:
8
+ *
9
+ * - the launch files the run under a LABEL GROUP (`groupLabel`, `RunGroup` on `GET /campaigns`) —
10
+ * the run is on the surface from the moment it launches;
11
+ * - at the run's TERMINAL frame this module reads the verify phase's transcript (the
12
+ * `QE-VERIFY` report — what was produced, what ran, what passed), and registers a {@link TestSet}:
13
+ * a durable `testing.testset.registered` audit entry (the daemon's system of record for run
14
+ * provenance, task #88) plus this read-side index, hydrated from the trail at boot like the
15
+ * retry/group/delivery indexes. `GET /campaigns` serves the sets as `test_sets`.
16
+ *
17
+ * NOT an engine campaign: `launchCampaign` schedules NODES that would RUN; a finished test set is a
18
+ * record of tests that exist, keyed by the run that produced them — a campaign-shaped read over a
19
+ * completed run, never a second execution.
20
+ *
21
+ * Deny-dominates on the wire: a run whose verify phase FAILED (a produced test failed, or was never
22
+ * executed) is registered too, `verified: false` with the counts — the landing must show a red set
23
+ * honestly rather than hide the run. A run that never reached its verify verdict registers with a
24
+ * `null` report.
25
+ */
26
+ import type { CoreAdapter } from '../core/adapter.js';
27
+ import type { Actor, AuditEntry, SessionView, WorkflowDef } from '../core/types.js';
28
+ import type { AuditLog } from '../api/audit.js';
29
+ import { QE_AUTHOR_TESTS_WORKFLOW, type QeVerifyReport } from './author-workflow.js';
30
+ /** The audit action a registration writes — the durable record the index hydrates from. */
31
+ export declare const TEST_SET_REGISTERED_ACTION = "testing.testset.registered";
32
+ /** The actor a daemon-side registration writes under. */
33
+ export declare const TEST_SET_ACTOR: Actor;
34
+ /** A registered test set — published as `TestSet` in wicked-crew-api-types (0.36.0). */
35
+ export interface TestSet {
36
+ /** `testset-<run id>`: one set per producing run. */
37
+ id: string;
38
+ run_id: string;
39
+ workflow_id: typeof QE_AUTHOR_TESTS_WORKFLOW;
40
+ /** The label group the launch filed the run under, when it did (`RunGroup.label`). */
41
+ label?: string;
42
+ repo_ref: string | null;
43
+ repo_name?: string;
44
+ /** Unix millis. */
45
+ registered_at: number;
46
+ /** The producing run's terminal status (`completed` | `failed` | `cancelled`). */
47
+ run_status: string;
48
+ /** The verify unit's status (`done` | `rejected` | …), `null` when the run never planned one. */
49
+ verify_status: string | null;
50
+ /** `true` only when the verify phase PASSED: every produced test executed and green, plan present. */
51
+ verified: boolean;
52
+ /** The produced test files and the harness verdict on each (empty when no report). */
53
+ files: Array<{
54
+ path: string;
55
+ harness: string;
56
+ status: 'passed' | 'failed' | 'not-executed';
57
+ }>;
58
+ produced: number;
59
+ executed: number;
60
+ passed: number;
61
+ failed: number;
62
+ not_executed: number;
63
+ /** The produced `tests/PLAN-*.md`, `null` when none was produced. */
64
+ plan: string | null;
65
+ /** The harnesses the produced tests ran under (unique, in first-use order). */
66
+ harnesses: string[];
67
+ /** The delivered PR, when the engine's deliver phase opened one. */
68
+ deliverUrl?: string;
69
+ }
70
+ /** Read-side index over the trail's `testing.testset.registered` entries. */
71
+ export declare class TestSetIndex {
72
+ private readonly byRun;
73
+ /** Hydrate from trail entries (newest first, as `readAll` returns them) — the first seen per run wins. */
74
+ hydrateFromEntries(entries: AuditEntry[]): void;
75
+ hydrate(audit: AuditLog, log?: (msg: string) => void): Promise<void>;
76
+ set(set: TestSet): void;
77
+ forRun(runId: string): TestSet | undefined;
78
+ /** Every set, newest registration first. */
79
+ list(): TestSet[];
80
+ }
81
+ /** The label group `POST /testing/author` files a run under — `qe-tests-<repo name>` (one group per
82
+ * repo, so the landing shows every authoring run over a repo together), `qe-tests` when unscoped. */
83
+ export declare function qeTestsGroupLabel(repoName: string | null | undefined): string;
84
+ export interface RegisterTestSetDeps {
85
+ adapter: Pick<CoreAdapter, 'sessionsDetail' | 'workOutput' | 'listRepos'>;
86
+ audit: AuditLog;
87
+ index: TestSetIndex;
88
+ /** The registry the run's workflow is resolved against (`adapter.listWorkflows()`). */
89
+ workflows: () => WorkflowDef[];
90
+ /** The delivered PR URL for a run, when one is recorded (`DeliveryIndex.urlFor`). */
91
+ deliveryUrlFor?: (runId: string) => string | undefined;
92
+ /** The launch-time label group, when one was filed (`GroupIndex.attachOf`). */
93
+ labelFor?: (runId: string) => string | undefined;
94
+ log?: (msg: string) => void;
95
+ }
96
+ /** Whether this run IS a `qe-author-tests` run (by id, or by phase sequence for an instance id). */
97
+ export declare function isQeAuthorRun(run: SessionView, workflows: WorkflowDef[]): boolean;
98
+ /** Build the record from the run view + the verify report (pure — tests drive it directly). */
99
+ export declare function buildTestSet(view: SessionView, report: QeVerifyReport | null, extra: {
100
+ registeredAt: number;
101
+ label?: string;
102
+ repoName?: string;
103
+ deliverUrl?: string;
104
+ }): TestSet;
105
+ /**
106
+ * Register the test set of a TERMINAL run when it is a `qe-author-tests` run. Idempotent per run
107
+ * (a resume/retry re-terminal never double-writes the trail). Best-effort by construction — a
108
+ * failure here is logged and never fails the run. Returns the set, or `null` when the run is not a
109
+ * qe-author run (or is unknown).
110
+ */
111
+ export declare function registerTestSetForRun(deps: RegisterTestSetDeps, runId: string): Promise<TestSet | null>;
112
+ //# sourceMappingURL=test-sets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-sets.d.ts","sourceRoot":"","sources":["../../src/qe/test-sets.ts"],"names":[],"mappings":"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"}