ccqa 1.47.0 → 1.47.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
@@ -13535,7 +13535,27 @@ const STEP_EVIDENCE_BEFORE = "ccqaStepBefore";
13535
13535
  const STEP_EVIDENCE_AFTER = "ccqaStepAfter";
13536
13536
  /** The exact boundary call for one step, as emitted and as the gate greps for it. */
13537
13537
  function stepEvidenceCall(fn, marker) {
13538
- return `await ${fn}(page, ${j(marker.stepId)}, ${j(marker.source)});`;
13538
+ return injectedCall(fn, [j(marker.stepId), j(marker.source)]);
13539
+ }
13540
+ /**
13541
+ * A receiver expression: anything up to the argument's comma, allowing one
13542
+ * level of parentheses so `await ctx.newPage()` and `page.context().pages()[1]`
13543
+ * — how a rewrite names a tab a click opened — count as receivers.
13544
+ */
13545
+ const RECEIVER = String.raw`(?:[^,()]|\([^()]*\))+`;
13546
+ /**
13547
+ * The call as emitted, plus the pattern that accepts it back on any page. The
13548
+ * receiver is the one part a rewrite is right to change: a click that opens a
13549
+ * new tab has to act on that tab. Everything else is required verbatim, the
13550
+ * `await` and `;` included — an unawaited capture races the end of the test,
13551
+ * and a mention in a comment is not a call.
13552
+ */
13553
+ function injectedCall(name, args) {
13554
+ const tail = args.map(escapeRegExp).join(String.raw`\s*,\s*`);
13555
+ return {
13556
+ code: `await ${name}(page, ${args.join(", ")});`,
13557
+ pattern: new RegExp(String.raw`await\s+${name}\s*\(\s*${RECEIVER}\s*,\s*${tail}\s*,?\s*\)\s*;`)
13558
+ };
13539
13559
  }
13540
13560
  /**
13541
13561
  * The "preserve the step-evidence calls" rule the library-rewrite prompt must
@@ -13545,7 +13565,7 @@ function stepEvidenceCall(fn, marker) {
13545
13565
  * engine, and the prompt then omits the rule entirely.
13546
13566
  */
13547
13567
  function stepEvidencePreserveRule() {
13548
- return `**Keep the \`${STEP_EVIDENCE_MODULE}\` calls.** The draft's \`await ${STEP_EVIDENCE_BEFORE}(page, ...)\` / \`await ${STEP_EVIDENCE_AFTER}(page, ...)\` lines are load-bearing: ccqa run reads the per-step screenshots they capture. Keep both calls for every step, in place around that step's actions, with their exact \`(page, "<stepId>", "<source>")\` arguments — and keep the import. If you move a step's actions into a page-object method, leave these two calls in the test body around the call to that method; do NOT move them inside the page object. Never wrap a step in a closure to hold them.`;
13568
+ return `**Keep the \`${STEP_EVIDENCE_MODULE}\` calls.** The draft's \`await ${STEP_EVIDENCE_BEFORE}(page, ...)\` / \`await ${STEP_EVIDENCE_AFTER}(page, ...)\` lines are load-bearing: ccqa run reads the per-step screenshots they capture. Keep both calls for every step, in place around that step's actions, with their exact \`"<stepId>", "<source>"\` arguments — and keep the import. Pass a different page only when the step genuinely acts on one (a click that opens a new tab). If you move a step's actions into a page-object method, leave these two calls in the test body around the call to that method; do NOT move them inside the page object. Never wrap a step in a closure to hold them.`;
13549
13569
  }
13550
13570
  /**
13551
13571
  * Told to the rewrite, because the alternative failure is silent: a claim
@@ -13564,22 +13584,22 @@ function emitPlaywrightDraft(input) {
13564
13584
  const flushJudgements = (afterActionIndex) => {
13565
13585
  for (const { step } of judgements.filter((j) => j.afterActionIndex === afterActionIndex)) {
13566
13586
  if (openMarker) {
13567
- lines.push(stepEvidenceCall(STEP_EVIDENCE_AFTER, openMarker));
13587
+ lines.push(stepEvidenceCall(STEP_EVIDENCE_AFTER, openMarker).code);
13568
13588
  openMarker = null;
13569
13589
  }
13570
13590
  if (lines.length > 0) lines.push("");
13571
13591
  lines.push(`// step: ${step.id} [${step.source}]`);
13572
- lines.push(judgeCall(step));
13592
+ lines.push(judgeCall(step).code);
13573
13593
  }
13574
13594
  };
13575
13595
  flushJudgements(-1);
13576
13596
  for (let i = 0; i < actions.length; i++) {
13577
13597
  const marker = markerByIndex.get(i);
13578
13598
  if (marker) {
13579
- if (openMarker) lines.push(stepEvidenceCall(STEP_EVIDENCE_AFTER, openMarker));
13599
+ if (openMarker) lines.push(stepEvidenceCall(STEP_EVIDENCE_AFTER, openMarker).code);
13580
13600
  if (lines.length > 0) lines.push("");
13581
13601
  lines.push(`// step: ${marker.stepId} [${marker.source}]`);
13582
- lines.push(stepEvidenceCall(STEP_EVIDENCE_BEFORE, marker));
13602
+ lines.push(stepEvidenceCall(STEP_EVIDENCE_BEFORE, marker).code);
13583
13603
  openMarker = marker;
13584
13604
  }
13585
13605
  const action = actions[i];
@@ -13591,7 +13611,7 @@ function emitPlaywrightDraft(input) {
13591
13611
  }
13592
13612
  flushJudgements(i);
13593
13613
  }
13594
- if (openMarker) lines.push(stepEvidenceCall(STEP_EVIDENCE_AFTER, openMarker));
13614
+ if (openMarker) lines.push(stepEvidenceCall(STEP_EVIDENCE_AFTER, openMarker).code);
13595
13615
  if (judgements.length > 0) lines.unshift("test.slow();", "");
13596
13616
  const body = lines.map((l) => l === "" ? "" : ` ${l}`).join("\n");
13597
13617
  return [
@@ -13779,8 +13799,9 @@ const j = (s) => JSON.stringify(s);
13779
13799
  const jExpr = (s) => envRefsToJsExpression(s);
13780
13800
  /** One claim, asserted through the judge. Exported so the generation gate can require it back. */
13781
13801
  function judgeCall(step) {
13782
- const from = step.from === void 0 ? "" : `, ${jExpr(step.from)}`;
13783
- return `await ${JUDGE_CALL}(page, ${bracedRefsToJsExpression(step.judgeByLlm.trim())}${from});`;
13802
+ const args = [bracedRefsToJsExpression(step.judgeByLlm.trim())];
13803
+ if (step.from !== void 0) args.push(jExpr(step.from));
13804
+ return injectedCall(JUDGE_CALL, args);
13784
13805
  }
13785
13806
  //#endregion
13786
13807
  //#region src/targets/playwright/browser-server.ts
@@ -14031,12 +14052,12 @@ async function missingInjectedCalls(result, markers, judgements) {
14031
14052
  const corpus = (await Promise.all(result.files.filter((f) => f.kind === "test").map((f) => readFile(f.path, "utf8").catch(() => "")))).join("\n");
14032
14053
  const warnings = [];
14033
14054
  for (const m of markers) {
14034
- const hasBefore = corpus.includes(stepEvidenceCall(STEP_EVIDENCE_BEFORE, m));
14035
- const hasAfter = corpus.includes(stepEvidenceCall(STEP_EVIDENCE_AFTER, m));
14055
+ const hasBefore = stepEvidenceCall(STEP_EVIDENCE_BEFORE, m).pattern.test(corpus);
14056
+ const hasAfter = stepEvidenceCall(STEP_EVIDENCE_AFTER, m).pattern.test(corpus);
14036
14057
  if (!hasBefore || !hasAfter) warnings.push(`step ${m.stepId}: generated test is missing its ${STEP_EVIDENCE_BEFORE}/${STEP_EVIDENCE_AFTER} call(s) — that step will have no report screenshots. A rewrite pass must not drop them.`);
14037
14058
  }
14038
14059
  for (const { step } of judgements) {
14039
- if (corpus.includes(judgeCall(step))) continue;
14060
+ if (judgeCall(step).pattern.test(corpus)) continue;
14040
14061
  warnings.push(`step ${step.id}: generated test is missing its ${JUDGE_CALL} call — that claim is never decided and the spec passes without testing it. A rewrite pass must not drop or reword it.`);
14041
14062
  }
14042
14063
  return warnings;
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccqa",
3
- "version": "1.47.0",
3
+ "version": "1.47.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.47.0",
3
+ "version": "1.47.1",
4
4
  "type": "module",
5
5
  "description": "Browser test recorder powered by Claude Code and agent-browser",
6
6
  "repository": {