ccqa 1.58.2 → 1.58.3

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
@@ -7868,7 +7868,7 @@ function reuseFirstContract(hasDraft, draftInvariant) {
7868
7868
  "3. **Match the examples.** Write in the same style as the conventions examples: naming, structure, assertion phrasing, import layout. Where you follow an existing file rather than the plain reading of a guideline — a waiting pattern, a locator shape, a teardown structure — say which file, by path, in the comment. \"Following the existing implementation\" without naming it cannot be checked by the next reader, and it is how a pattern that was right once outlives the reason for it."
7869
7869
  ];
7870
7870
  if (hasDraft) {
7871
- rules.push("4. **The draft is ground truth.** The mechanical draft below encodes the recorded route. Do not change the meaning of its operation sequence or its assertions — no reordering, dropping, or weakening. You may rewrite locators when the conventions or the sources you read justify a better one.");
7871
+ rules.push("4. **The draft is ground truth for the route, not for the form of its checks.** Keep its operation sequence: no reordering or dropping of the actions the case needs. Its `expect` lines are what the recorder observed on the way, written in one fixed form (`await expect(...)`, one per observation). Which of them the test keeps, and in which form — hard or soft, where the check lives, how the locator is scoped — is decided by the case's expected results and the project's conventions, never by the draft. Where the conventions and the draft disagree, follow the conventions. What an expected result claims must still be checked: never loosen the value it states.");
7872
7872
  if (draftInvariant) rules.push(`5. ${draftInvariant}`);
7873
7873
  }
7874
7874
  return `## Reuse contract\n\n${rules.join("\n")}`;
@@ -7916,6 +7916,8 @@ function buildLlmGenPrompt(input) {
7916
7916
  sections.push(input.taskInstructions);
7917
7917
  const steps = input.steps.map(formatStep).join("\n");
7918
7918
  sections.push(`## Test spec\n\nTitle: ${input.specTitle}\n\nSteps:\n${steps}`);
7919
+ const facts = caseFacts(input);
7920
+ if (facts) sections.push(facts);
7919
7921
  if (input.draft) sections.push(`## Mechanical draft (recorded ground truth)\n\nPath: ${input.draft.path}\n\n\`\`\`
7920
7922
  ` + input.draft.contents + "\n```");
7921
7923
  if (input.resources.length > 0) {
@@ -7936,6 +7938,19 @@ These are the project's rules for the code you write, not background reading. Co
7936
7938
  return sections.join("\n\n") + languageDirective(input.language);
7937
7939
  }
7938
7940
  /**
7941
+ * What the case states beyond its steps. Project conventions key rules on
7942
+ * these (which checks are the case's, where it may run), and the review that
7943
+ * follows already reads them — a rewrite without them can only guess.
7944
+ */
7945
+ function caseFacts(input) {
7946
+ const parts = [];
7947
+ if (input.expectations && input.expectations.length > 0) parts.push(`### Expected results\n\nThe case states these for the flow as a whole. They are the checks the test exists for:\n\n${input.expectations.map((e) => `- ${body(e)}`).join("\n")}`);
7948
+ if (input.cleanup && input.cleanup.length > 0) parts.push(`### Cleanup steps\n\n${input.cleanup.map(formatStep).join("\n")}`);
7949
+ if (input.cleanupExpectations && input.cleanupExpectations.length > 0) parts.push(`### Expected results of the cleanup\n\n${input.cleanupExpectations.map((e) => `- ${body(e)}`).join("\n")}`);
7950
+ for (const c of input.context ?? []) parts.push(`### ${c.heading}\n\n${c.body.trim()}`);
7951
+ return parts.length === 0 ? "" : `## About this case\n\n${parts.join("\n\n")}`;
7952
+ }
7953
+ /**
7939
7954
  * Prompt for one iteration of the verification loop: the generated files
7940
7955
  * failed their run command; ask for corrected files under the same contract.
7941
7956
  */
@@ -8304,7 +8319,7 @@ function weakerTwin(file, source) {
8304
8319
  function decidesNothing(input) {
8305
8320
  const source = input.files.get(input.testPath);
8306
8321
  if (source === void 0 || input.caseText.join("").trim().length === 0) return [];
8307
- if (/\bexpect\(|\bjudgeByLlm\b/.test(source)) return [];
8322
+ if (/\bexpect(?:\.soft)?\(|\bjudgeByLlm\b/.test(source)) return [];
8308
8323
  return [{
8309
8324
  file: input.testPath,
8310
8325
  line: 1,
@@ -8373,7 +8388,7 @@ function containerOfPageText(file, source) {
8373
8388
  */
8374
8389
  function explained(all, i) {
8375
8390
  let above = i - 1;
8376
- while (above >= 0 && /\bexpect\(|^\s*$|^\s*\)/.test(all[above] ?? "")) above -= 1;
8391
+ while (above >= 0 && /\bexpect(?:\.soft)?\(|^\s*$|^\s*\)/.test(all[above] ?? "")) above -= 1;
8377
8392
  return /^\s*\/\//.test(all[above] ?? "");
8378
8393
  }
8379
8394
  /**
@@ -8388,7 +8403,7 @@ function explained(all, i) {
8388
8403
  function unjustifiedFirst(file, source) {
8389
8404
  const all = lines(source);
8390
8405
  return all.flatMap((text, i) => {
8391
- if (!/\bexpect\(/.test(text) || !/\.first\(\)/.test(text)) return [];
8406
+ if (!/\bexpect(?:\.soft)?\(/.test(text) || !/\.first\(\)/.test(text)) return [];
8392
8407
  if (explained(all, i)) return [];
8393
8408
  return [{
8394
8409
  file,
@@ -8463,7 +8478,7 @@ function assertedStrings(text, locators) {
8463
8478
  function unaskedAssertions(file, source, locators, said) {
8464
8479
  const all = lines(source);
8465
8480
  return all.flatMap((text, i) => {
8466
- if (!/\bexpect\(/.test(text)) return [];
8481
+ if (!/\bexpect(?:\.soft)?\(/.test(text)) return [];
8467
8482
  const strings = assertedStrings(text, locators);
8468
8483
  if (strings.length === 0 || strings.some((s) => said.includes(s))) return [];
8469
8484
  if (explained(all, i)) return [];
@@ -14783,6 +14798,10 @@ async function generateWithLlmEngine(req) {
14783
14798
  specTitle: ctx.spec.title,
14784
14799
  steps: req.steps,
14785
14800
  draft: req.draft,
14801
+ expectations: ctx.expectations,
14802
+ cleanup: ctx.cleanup,
14803
+ cleanupExpectations: ctx.cleanupExpectations,
14804
+ context: ctx.context ?? [],
14786
14805
  ...req.draftInvariant ? { draftInvariant: req.draftInvariant } : {},
14787
14806
  resources: resources.map(toPromptResource),
14788
14807
  conventionSections: conventions.sections,
@@ -15467,7 +15486,7 @@ function headerPreserveRule(header, titleSuffix) {
15467
15486
  * rejection, which costs a whole round.
15468
15487
  */
15469
15488
  function stepCommentPreserveRule() {
15470
- return "**Keep every `test.step(...)` block, with its title exactly as the draft wrote it.** Those titles are how the evidence table, the run's per-step screenshots and the review of this test say which assertions belong to which step. A reshaped one parses as no step at all, and every step then reads as deciding nothing. Keep the wording, the numbering and the punctuation, and keep each step's actions inside its own block — a step's call to a page-object method belongs in the block, not outside it. Do not merge, split or reorder the blocks. A test already at this path may have been written by an older ccqa in a different shape: the draft is what this file must look like, not that one.";
15489
+ return "**Keep every `test.step(...)` block, with its title exactly as the draft wrote it.** Those titles are how the evidence table, the run's per-step screenshots and the review of this test say which assertions belong to which step. A reshaped one parses as no step at all, and every step then reads as deciding nothing. Keep the wording, the numbering and the punctuation, and keep each step's actions inside its own block — a step's call to a page-object method belongs in the block, not outside it. Do not merge, split or reorder the blocks. A test already at this path may have been written by an older ccqa in a different shape: the draft's step blocks are what this file must keep, not that one's.";
15471
15490
  }
15472
15491
  /**
15473
15492
  * Told to the rewrite, because the alternative failure is silent: a claim
@@ -26481,6 +26500,7 @@ async function runGenerateLocked(resolved, opts, cwd) {
26481
26500
  cleanup: testCase.cleanup,
26482
26501
  expectations: testCase.expectations,
26483
26502
  cleanupExpectations: testCase.cleanupExpectations,
26503
+ ...testCase.context.length > 0 ? { context: testCase.context } : {},
26484
26504
  fields: testCase.fields,
26485
26505
  cwd,
26486
26506
  testPath,
@@ -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;
@@ -831,9 +831,9 @@ type ReportSpecResult = z.infer<typeof ReportSpecResultSchema>;
831
831
  declare const RunReportDataSchema: z.ZodObject<{
832
832
  schemaVersion: z.ZodLiteral<1>;
833
833
  kind: z.ZodDefault<z.ZodEnum<{
834
- record: "record";
835
834
  run: "run";
836
835
  drift: "drift";
836
+ record: "record";
837
837
  }>>;
838
838
  createdAt: z.ZodString;
839
839
  runId: z.ZodNullable<z.ZodString>;
@@ -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;
@@ -831,9 +831,9 @@ type ReportSpecResult = z.infer<typeof ReportSpecResultSchema>;
831
831
  declare const RunReportDataSchema: z.ZodObject<{
832
832
  schemaVersion: z.ZodLiteral<1>;
833
833
  kind: z.ZodDefault<z.ZodEnum<{
834
- record: "record";
835
834
  run: "run";
836
835
  drift: "drift";
836
+ record: "record";
837
837
  }>>;
838
838
  createdAt: z.ZodString;
839
839
  runId: z.ZodNullable<z.ZodString>;
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccqa",
3
- "version": "1.58.2",
3
+ "version": "1.58.3",
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.58.2",
3
+ "version": "1.58.3",
4
4
  "type": "module",
5
5
  "description": "Browser test recorder powered by Claude Code and agent-browser",
6
6
  "repository": {