ccqa 1.49.0 → 1.50.0
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/dist/bin/ccqa.mjs +86 -5
- package/dist/hub-client/index.d.mts +8 -4
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/bin/ccqa.mjs
CHANGED
|
@@ -1502,6 +1502,8 @@ const ReportSpecResultSchema = z.object({
|
|
|
1502
1502
|
failed: z.number()
|
|
1503
1503
|
}).nullable(),
|
|
1504
1504
|
durationMs: z.number().nullable(),
|
|
1505
|
+
startedAt: z.string().optional(),
|
|
1506
|
+
finishedAt: z.string().optional(),
|
|
1505
1507
|
assertions: z.array(ReportAssertionSchema).nullable(),
|
|
1506
1508
|
analysis: FailureAnalysisSchema.nullable(),
|
|
1507
1509
|
analysisSkipped: z.string().nullable(),
|
|
@@ -6198,6 +6200,7 @@ async function runOneSpec$1(ref, opts, blocks) {
|
|
|
6198
6200
|
meta("command", command);
|
|
6199
6201
|
blank();
|
|
6200
6202
|
const started = Date.now();
|
|
6203
|
+
const startedAt = new Date(started).toISOString();
|
|
6201
6204
|
let outcome;
|
|
6202
6205
|
let spawnFailure;
|
|
6203
6206
|
let measured;
|
|
@@ -6218,6 +6221,7 @@ async function runOneSpec$1(ref, opts, blocks) {
|
|
|
6218
6221
|
const coverageFields = coverageRowFields(opts, measured, attachError);
|
|
6219
6222
|
if (spawnFailure !== void 0 || outcome === void 0) return {
|
|
6220
6223
|
...didNotExecute(`could not spawn runCommand: ${spawnFailure ?? "unknown error"}`, "the runCommand could not be spawned"),
|
|
6224
|
+
startedAt,
|
|
6221
6225
|
...coverageFields
|
|
6222
6226
|
};
|
|
6223
6227
|
const durationMs = Date.now() - started;
|
|
@@ -6244,6 +6248,8 @@ async function runOneSpec$1(ref, opts, blocks) {
|
|
|
6244
6248
|
status: "passed"
|
|
6245
6249
|
}),
|
|
6246
6250
|
target: opts.targetId,
|
|
6251
|
+
startedAt,
|
|
6252
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6247
6253
|
durationMs,
|
|
6248
6254
|
...artifactFields,
|
|
6249
6255
|
...evidenceFields,
|
|
@@ -6251,6 +6257,8 @@ async function runOneSpec$1(ref, opts, blocks) {
|
|
|
6251
6257
|
};
|
|
6252
6258
|
return {
|
|
6253
6259
|
...failedRow([`command failed (exit ${outcome.exitCode}): ${command}`, outcome.tail.length > 0 ? `--- output (tail) ---\n${outcome.tail}` : null].filter((p) => p !== null).join("\n")),
|
|
6260
|
+
startedAt,
|
|
6261
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6254
6262
|
durationMs,
|
|
6255
6263
|
...artifactFields,
|
|
6256
6264
|
...evidenceFields,
|
|
@@ -10189,6 +10197,36 @@ function foldTouchIndex(current, entry, selection) {
|
|
|
10189
10197
|
}
|
|
10190
10198
|
return out;
|
|
10191
10199
|
}
|
|
10200
|
+
/**
|
|
10201
|
+
* How much clock skew between the runner (which stamps a row's window) and
|
|
10202
|
+
* the hub (which stamps a deploy) the placement tolerates. The window is
|
|
10203
|
+
* widened by this on both ends, so a skewed clock costs a row its credit
|
|
10204
|
+
* rather than crediting a row that straddled.
|
|
10205
|
+
*/
|
|
10206
|
+
const PLACEMENT_SKEW_MS = 1e4;
|
|
10207
|
+
/**
|
|
10208
|
+
* Place one row's execution window against the deploy log (ADR-0027).
|
|
10209
|
+
*
|
|
10210
|
+
* The window is widened by {@link PLACEMENT_SKEW_MS} and its start is
|
|
10211
|
+
* exclusive, so a deploy landing exactly as the spec began reads as a
|
|
10212
|
+
* straddle. Both choices err the same way: toward `ambiguous`, never toward
|
|
10213
|
+
* crediting a spec with a commit it may not have exercised.
|
|
10214
|
+
*
|
|
10215
|
+
* `entries` must be in append order, which the log guarantees and which is
|
|
10216
|
+
* time order.
|
|
10217
|
+
*/
|
|
10218
|
+
function placeRowInDeployLog(entries, window) {
|
|
10219
|
+
const before = (ms, inclusive) => entries.findLast((e) => {
|
|
10220
|
+
const at = Date.parse(e.at);
|
|
10221
|
+
return inclusive ? at <= ms : at < ms;
|
|
10222
|
+
});
|
|
10223
|
+
const opened = before(window.startMs - PLACEMENT_SKEW_MS, false);
|
|
10224
|
+
const closed = before(Math.max(window.startMs, window.endMs) + PLACEMENT_SKEW_MS, true);
|
|
10225
|
+
return {
|
|
10226
|
+
deployedSha: opened?.sha ?? null,
|
|
10227
|
+
deployedShaAmbiguous: opened?.sha !== closed?.sha
|
|
10228
|
+
};
|
|
10229
|
+
}
|
|
10192
10230
|
//#endregion
|
|
10193
10231
|
//#region src/coverage/resolve-stream.ts
|
|
10194
10232
|
/**
|
|
@@ -11774,6 +11812,8 @@ async function liveRunToReportResult(args) {
|
|
|
11774
11812
|
target: AGENT_BROWSER_TARGET,
|
|
11775
11813
|
status: result.status,
|
|
11776
11814
|
testCounts: null,
|
|
11815
|
+
startedAt: result.startedAt,
|
|
11816
|
+
finishedAt: new Date(Date.parse(result.startedAt) + result.durationMs).toISOString(),
|
|
11777
11817
|
durationMs: result.durationMs,
|
|
11778
11818
|
assertions: null,
|
|
11779
11819
|
analysis: null,
|
|
@@ -15729,6 +15769,7 @@ async function runOneDeterministicSpec(spec, index, ctx) {
|
|
|
15729
15769
|
CCQA_RUN_ID: runId
|
|
15730
15770
|
};
|
|
15731
15771
|
if (evidenceDir) specEnv[EVIDENCE_DIR_ENV] = evidenceDir;
|
|
15772
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
15732
15773
|
const proc = spawnVitestStreaming([
|
|
15733
15774
|
"run",
|
|
15734
15775
|
"--config",
|
|
@@ -15745,11 +15786,14 @@ async function runOneDeterministicSpec(spec, index, ctx) {
|
|
|
15745
15786
|
await Promise.all([streamFiltered(proc.stdout, sink, tail), streamFiltered(proc.stderr, sink, tail)]);
|
|
15746
15787
|
const specExitCode = await proc.exited;
|
|
15747
15788
|
blank();
|
|
15789
|
+
const report = await readReport$1(reportFile);
|
|
15748
15790
|
return {
|
|
15749
15791
|
featureName,
|
|
15750
15792
|
specName,
|
|
15751
15793
|
scriptFile,
|
|
15752
|
-
|
|
15794
|
+
startedAt,
|
|
15795
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15796
|
+
report,
|
|
15753
15797
|
exitCode: specExitCode,
|
|
15754
15798
|
outputTail: tail ? tail.toString() : null,
|
|
15755
15799
|
evidenceDir
|
|
@@ -15778,6 +15822,8 @@ async function analyzeDeterministicSummaries(summaries, cwd, reportDir, { pass,
|
|
|
15778
15822
|
spec: s.specName,
|
|
15779
15823
|
title: parsedSpec?.title ?? null,
|
|
15780
15824
|
target: AGENT_BROWSER_TARGET,
|
|
15825
|
+
startedAt: s.startedAt,
|
|
15826
|
+
finishedAt: s.finishedAt,
|
|
15781
15827
|
testCounts: s.report ? {
|
|
15782
15828
|
total: s.report.numTotalTests,
|
|
15783
15829
|
passed: s.report.numPassedTests,
|
|
@@ -20186,17 +20232,20 @@ function countSpecs(results) {
|
|
|
20186
20232
|
async function updateSpecLedger(storage, run, results) {
|
|
20187
20233
|
const { gitHead, branch } = run;
|
|
20188
20234
|
if (run.kind !== "run" || !gitHead || !branch) return;
|
|
20189
|
-
const
|
|
20235
|
+
const placeRow = await rowDeployPlacer(storage, run);
|
|
20236
|
+
const base = {
|
|
20190
20237
|
gitHead,
|
|
20191
20238
|
runId: run.id,
|
|
20192
|
-
at: run.reportCreatedAt
|
|
20193
|
-
deployedSha: run.deployedSha ?? null,
|
|
20194
|
-
deployedShaAmbiguous: run.deployedShaAmbiguous ?? false
|
|
20239
|
+
at: run.reportCreatedAt
|
|
20195
20240
|
};
|
|
20196
20241
|
const ledger = emptyLedger();
|
|
20197
20242
|
for (const row of results) {
|
|
20198
20243
|
if (row.status === "skipped") continue;
|
|
20199
20244
|
const key = `${row.feature}/${row.spec}`;
|
|
20245
|
+
const entry = {
|
|
20246
|
+
...base,
|
|
20247
|
+
...placeRow(row)
|
|
20248
|
+
};
|
|
20200
20249
|
ledger.run[key] = entry;
|
|
20201
20250
|
if (row.status === "passed") ledger.green[key] = entry;
|
|
20202
20251
|
else ledger.red[key] = redEntry(entry, row);
|
|
@@ -20299,6 +20348,38 @@ async function resolveDeployedSha(storage, kind, project, profile, explicit) {
|
|
|
20299
20348
|
};
|
|
20300
20349
|
}
|
|
20301
20350
|
/**
|
|
20351
|
+
* Place each row against the deploy log by its own execution window rather
|
|
20352
|
+
* than the run's (ADR-0027), so a run that outlives a deploy loses only the
|
|
20353
|
+
* specs that straddled it.
|
|
20354
|
+
*
|
|
20355
|
+
* Falls back to the run's placement where a finer answer isn't available: a
|
|
20356
|
+
* client-asserted sha is the caller's claim about its whole run, and a row
|
|
20357
|
+
* with no `startedAt` (an older client) has nothing finer to say. A row with
|
|
20358
|
+
* no end is measured to now, which only widens its window.
|
|
20359
|
+
*/
|
|
20360
|
+
async function rowDeployPlacer(storage, run) {
|
|
20361
|
+
const runPlacement = {
|
|
20362
|
+
deployedSha: run.deployedSha ?? null,
|
|
20363
|
+
deployedShaAmbiguous: run.deployedShaAmbiguous ?? false
|
|
20364
|
+
};
|
|
20365
|
+
if (run.deployedShaSource !== "hub-deploy-log") return () => runPlacement;
|
|
20366
|
+
const log = await storage.deploys.getLog(run.project, run.profile ?? "default").catch((err) => {
|
|
20367
|
+
console.error(`hub: could not read the deploy log to place rows of run "${run.id}": ${errMsg(err)}`);
|
|
20368
|
+
return null;
|
|
20369
|
+
});
|
|
20370
|
+
if (!log) return () => runPlacement;
|
|
20371
|
+
const sealedAt = Date.now();
|
|
20372
|
+
return (row) => {
|
|
20373
|
+
const startMs = row.startedAt ? Date.parse(row.startedAt) : NaN;
|
|
20374
|
+
if (Number.isNaN(startMs)) return runPlacement;
|
|
20375
|
+
const endMs = row.finishedAt ? Date.parse(row.finishedAt) : sealedAt;
|
|
20376
|
+
return placeRowInDeployLog(log.entries, {
|
|
20377
|
+
startMs,
|
|
20378
|
+
endMs: Number.isNaN(endMs) ? sealedAt : endMs
|
|
20379
|
+
});
|
|
20380
|
+
};
|
|
20381
|
+
}
|
|
20382
|
+
/**
|
|
20302
20383
|
* True when the deploy-log head moved while the run was open: the run
|
|
20303
20384
|
* straddled a deploy, so which commit it exercised is not knowable and re-run
|
|
20304
20385
|
* selection must report `unknown` instead of picking one. Only meaningful for
|
|
@@ -35,9 +35,9 @@ declare const RunSchema: z.ZodObject<{
|
|
|
35
35
|
running: "running";
|
|
36
36
|
}>;
|
|
37
37
|
kind: z.ZodDefault<z.ZodEnum<{
|
|
38
|
-
record: "record";
|
|
39
38
|
run: "run";
|
|
40
39
|
drift: "drift";
|
|
40
|
+
record: "record";
|
|
41
41
|
}>>;
|
|
42
42
|
drift: z.ZodDefault<z.ZodNullable<z.ZodObject<{
|
|
43
43
|
specs: z.ZodNumber;
|
|
@@ -640,8 +640,8 @@ declare const ReportSpecResultSchema: z.ZodObject<{
|
|
|
640
640
|
title: z.ZodNullable<z.ZodString>;
|
|
641
641
|
target: z.ZodOptional<z.ZodString>;
|
|
642
642
|
mode: z.ZodOptional<z.ZodEnum<{
|
|
643
|
-
deterministic: "deterministic";
|
|
644
643
|
live: "live";
|
|
644
|
+
deterministic: "deterministic";
|
|
645
645
|
}>>;
|
|
646
646
|
status: z.ZodEnum<{
|
|
647
647
|
passed: "passed";
|
|
@@ -655,6 +655,8 @@ declare const ReportSpecResultSchema: z.ZodObject<{
|
|
|
655
655
|
failed: z.ZodNumber;
|
|
656
656
|
}, z.core.$strip>>;
|
|
657
657
|
durationMs: z.ZodNullable<z.ZodNumber>;
|
|
658
|
+
startedAt: z.ZodOptional<z.ZodString>;
|
|
659
|
+
finishedAt: z.ZodOptional<z.ZodString>;
|
|
658
660
|
assertions: z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
659
661
|
name: z.ZodString;
|
|
660
662
|
status: z.ZodEnum<{
|
|
@@ -811,9 +813,9 @@ type ReportSpecResult = z.infer<typeof ReportSpecResultSchema>;
|
|
|
811
813
|
declare const RunReportDataSchema: z.ZodObject<{
|
|
812
814
|
schemaVersion: z.ZodLiteral<1>;
|
|
813
815
|
kind: z.ZodDefault<z.ZodEnum<{
|
|
814
|
-
record: "record";
|
|
815
816
|
run: "run";
|
|
816
817
|
drift: "drift";
|
|
818
|
+
record: "record";
|
|
817
819
|
}>>;
|
|
818
820
|
createdAt: z.ZodString;
|
|
819
821
|
runId: z.ZodNullable<z.ZodString>;
|
|
@@ -854,8 +856,8 @@ declare const RunReportDataSchema: z.ZodObject<{
|
|
|
854
856
|
title: z.ZodNullable<z.ZodString>;
|
|
855
857
|
target: z.ZodOptional<z.ZodString>;
|
|
856
858
|
mode: z.ZodOptional<z.ZodEnum<{
|
|
857
|
-
deterministic: "deterministic";
|
|
858
859
|
live: "live";
|
|
860
|
+
deterministic: "deterministic";
|
|
859
861
|
}>>;
|
|
860
862
|
status: z.ZodEnum<{
|
|
861
863
|
passed: "passed";
|
|
@@ -869,6 +871,8 @@ declare const RunReportDataSchema: z.ZodObject<{
|
|
|
869
871
|
failed: z.ZodNumber;
|
|
870
872
|
}, z.core.$strip>>;
|
|
871
873
|
durationMs: z.ZodNullable<z.ZodNumber>;
|
|
874
|
+
startedAt: z.ZodOptional<z.ZodString>;
|
|
875
|
+
finishedAt: z.ZodOptional<z.ZodString>;
|
|
872
876
|
assertions: z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
873
877
|
name: z.ZodString;
|
|
874
878
|
status: z.ZodEnum<{
|
package/dist/package.json
CHANGED