ccqa 1.37.0 → 1.37.1

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 CHANGED
@@ -3726,9 +3726,19 @@ function scrubOutcome(outcome, scrubMap) {
3726
3726
  }
3727
3727
  };
3728
3728
  }
3729
+ /**
3730
+ * Pause before the single retry of an errored classification call. Long enough
3731
+ * to ride out a transient network/model hiccup, short enough not to stall the
3732
+ * report when the error is persistent.
3733
+ */
3734
+ const RETRY_DELAY_MS = 2e3;
3735
+ function sleep(ms) {
3736
+ return new Promise((resolve) => setTimeout(resolve, ms));
3737
+ }
3729
3738
  async function classifyFailure(input, options) {
3730
- const { result: raw, isError } = await invokeClaudeStreaming({
3731
- prompt: buildFailureAnalysisPrompt(input),
3739
+ const prompt = buildFailureAnalysisPrompt(input);
3740
+ const invoke = () => invokeClaudeStreaming({
3741
+ prompt,
3732
3742
  allowedTools: [
3733
3743
  "Read",
3734
3744
  "Grep",
@@ -3741,11 +3751,22 @@ async function classifyFailure(input, options) {
3741
3751
  ...options.model ? { model: options.model } : {},
3742
3752
  ...options.cwd ? { cwd: options.cwd } : {}
3743
3753
  }, () => {});
3744
- if (isError || !raw) return {
3745
- analysis: unknownAnalysis(isError ? "Claude returned an error result" : "Claude returned no output"),
3746
- raw: raw ?? "",
3747
- sdkError: isError
3748
- };
3754
+ let { result: raw, isError } = await invoke();
3755
+ let retried = false;
3756
+ if (isError) {
3757
+ warn("failure analysis: Claude invocation errored — retrying once");
3758
+ await sleep(RETRY_DELAY_MS);
3759
+ ({result: raw, isError} = await invoke());
3760
+ retried = true;
3761
+ }
3762
+ if (isError || !raw) {
3763
+ const cause = isError ? "Claude returned an error result" : "Claude returned no output";
3764
+ return {
3765
+ analysis: unknownAnalysis(retried ? `${cause} (after 1 retry)` : cause),
3766
+ raw: raw ?? "",
3767
+ sdkError: isError
3768
+ };
3769
+ }
3749
3770
  let sawParseableJson = false;
3750
3771
  for (const candidate of extractJsonCandidates(raw)) {
3751
3772
  let parsed;
@@ -8147,8 +8168,9 @@ const C$1 = {
8147
8168
  * diff — so report rows and CI logs look the same whichever target a
8148
8169
  * project's specs use.
8149
8170
  *
8150
- * One Claude call per failing spec, which reads the source itself rather than
8151
- * deferring to a drift audit run beforehand. It is still one *phase*:
8171
+ * One Claude call per failing spec (two when the first errors and is retried
8172
+ * once), which reads the source itself rather than deferring to a drift audit
8173
+ * run beforehand. It is still one *phase*:
8152
8174
  * `beginFailureAnalysis` hands back the state every path shares, so a mixed
8153
8175
  * run prints one `failure analysis` banner in one place rather than one per
8154
8176
  * execution path. It runs after every spec has executed, so no Claude turn is
@@ -9065,8 +9087,10 @@ z.object({ error: z.object({
9065
9087
  * One spec's record of a single run, as stored in a ledger bucket. Identical
9066
9088
  * to `LastGreenEntry` plus the commit the environment was running at the time
9067
9089
  * — without it a bucket entry can be ordered in wall-clock time but not
9068
- * *positioned* against the deploy log, which is the only ordering re-run
9069
- * selection may use (ADR-0010).
9090
+ * *positioned* against the deploy log, which is the only ordering the
9091
+ * staleness verdict may use (ADR-0010). Wall-clock order is fit only for
9092
+ * scheduling within an already-decided set, where a mis-ranking delays a
9093
+ * spec rather than excusing it.
9070
9094
  */
9071
9095
  const SpecLedgerEntrySchema = z.object({
9072
9096
  gitHead: z.string(),
@@ -9651,7 +9675,10 @@ function selectSpecsNeedingRerun(specs, report) {
9651
9675
  const entry = report.specs[specKey(spec)];
9652
9676
  const verdict = entry?.verdict ?? "inProgress";
9653
9677
  counts.set(verdict, (counts.get(verdict) ?? 0) + 1);
9654
- if (verdict === "rerunNeeded") selected.push(spec);
9678
+ if (verdict === "rerunNeeded") selected.push({
9679
+ spec,
9680
+ lastRunAt: entry?.lastRun?.at ?? ""
9681
+ });
9655
9682
  else if (verdict === "inProgress") {
9656
9683
  excludedInProgress++;
9657
9684
  if (!entry) excludedUnknownToHub++;
@@ -9661,8 +9688,9 @@ function selectSpecsNeedingRerun(specs, report) {
9661
9688
  }
9662
9689
  }
9663
9690
  }
9691
+ selected.sort((a, b) => a.lastRunAt.localeCompare(b.lastRunAt));
9664
9692
  return {
9665
- selected,
9693
+ selected: selected.map((s) => s.spec),
9666
9694
  summary: formatCounts(SUMMARY_ORDER$1, counts),
9667
9695
  excludedInProgress,
9668
9696
  excludedUnknownToHub,
@@ -11583,6 +11611,15 @@ ${stepsText}
11583
11611
  - Do not invent success when blocked: fail honestly with a short reason.
11584
11612
  - **Evidence discipline**: when the assertion target is a specific row / message / banner / URL, scroll it into view (or focus the relevant pane) before letting the step end. The "after" screenshot is captured for you automatically — your job is to make sure that screenshot shows the thing your STEP_RESULT line is talking about.
11585
11613
 
11614
+ ### Waiting for asynchronous responses
11615
+
11616
+ Some expected outcomes arrive asynchronously — an automated reply, a background job finishing, a list refreshing. Waiting for them is fine, but the wait has a budget:
11617
+
11618
+ - Prefer bounded probes (\`agent-browser wait --text "..."\`, or a short pause followed by a fresh \`snapshot\`) over long blind sleeps, and keep a rough running total of how long you have waited within this step.
11619
+ - **The total wait within one step must not exceed 3 minutes**, unless the step's own instruction explicitly names a longer wait. Do not keep adding "one more" sleep past the budget.
11620
+ - When the budget is spent and the expected outcome has still not appeared, STOP waiting and emit \`STEP_RESULT|<stepId>|fail|...\`. **Never end your turn without a STEP_RESULT because you were still waiting** — a silent timeout is recorded as a protocol failure and hides the real cause from failure analysis.
11621
+ - The fail reason must state what you waited for, roughly how long in total, and what you observed instead (e.g. "waited ~3 min for a reply to appear after submitting; none appeared, the view still shows only the submitted item").
11622
+
11586
11623
  ### Output contract (STRICT)
11587
11624
 
11588
11625
  Your final assistant message MUST contain exactly one line of the form:
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccqa",
3
- "version": "1.37.0",
3
+ "version": "1.37.1",
4
4
  "type": "module",
5
5
  "description": "Browser test recorder powered by Claude Code and agent-browser",
6
6
  "repository": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccqa",
3
- "version": "1.37.0",
3
+ "version": "1.37.1",
4
4
  "type": "module",
5
5
  "description": "Browser test recorder powered by Claude Code and agent-browser",
6
6
  "repository": {