ccqa 1.53.0 → 1.55.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.
File without changes
@@ -0,0 +1,122 @@
1
+ //#region src/cases/contract.d.ts
2
+ /**
3
+ * What a test case is, and how ccqa is handed one.
4
+ *
5
+ * ccqa reads its own `spec.yaml` and nothing else. Every other way of writing
6
+ * a test case — a team's markdown, a spreadsheet export, rows in a tracker —
7
+ * is read by a module that project owns and points `targets.<id>.cases` at.
8
+ * This file is the whole contract between the two, and the only part of ccqa a
9
+ * reader module sees.
10
+ *
11
+ * Published as the `ccqa/case-source` subpath, and **types only**: a reader
12
+ * imports nothing at runtime, so `// @ts-check` plus a JSDoc
13
+ * `@type {import("ccqa/case-source").CaseSourceFactory}` is the whole
14
+ * integration, and ccqa's own dependencies never reach the consumer's build.
15
+ * (The gate that refuses `ccqa/*` imports inside an emitted test does not
16
+ * apply here — a reader module is not a test.)
17
+ *
18
+ * What ccqa decides, not the reader: step numbering, where the case's working
19
+ * files live, and where its recording goes. A reader that assigned them would
20
+ * be answering for a directory it cannot see.
21
+ */
22
+ /** One step of a case, as the document that states it wrote it. */
23
+ interface CaseStep {
24
+ /** What to do. */
25
+ instruction: string;
26
+ /**
27
+ * What this step must make true. Omit it when the case states its
28
+ * expectations once for the whole flow rather than per step — placing them
29
+ * is then the recorder's reading, not the reader's.
30
+ */
31
+ expected?: string;
32
+ }
33
+ /**
34
+ * One test case, as a source answers for it.
35
+ *
36
+ * Read strictly: an unknown key is refused rather than ignored, so a typo in a
37
+ * reader is reported where it was made instead of quietly costing a field.
38
+ */
39
+ interface Case {
40
+ /**
41
+ * Path-shaped, `/`-separated, no extension — `todo/add_item`. No leading
42
+ * `/`, no `..`, no backslashes. Unique within the source and stable for the
43
+ * life of the case: ccqa files the case's recording and evidence under it,
44
+ * and it is what `{case}` expands to in the target's `testPath`.
45
+ */
46
+ id: string;
47
+ /** Absolute path of the file that states the case. */
48
+ path: string;
49
+ /** That file verbatim. The audit and the failure classifier read it as written. */
50
+ text: string;
51
+ title: string;
52
+ /**
53
+ * `live` drives the case through the browser agent on every run;
54
+ * `deterministic` records it once and compiles a test.
55
+ *
56
+ * Two values, no spellings. A source that reads "manual" or "wip" out of its
57
+ * own documents decides for itself what that means — reading an unrecognised
58
+ * word as "record and generate" is how a case gets automated that nobody
59
+ * meant to automate.
60
+ */
61
+ mode: "deterministic" | "live";
62
+ /** What to do, in order. A case with no steps describes nothing to record. */
63
+ steps: CaseStep[];
64
+ /** What to undo afterwards, whatever the outcome. Emitted into `afterEach`. */
65
+ cleanup?: CaseStep[];
66
+ /**
67
+ * What the case says must be true, unattached to any step. Which step
68
+ * delivers which is a reading of the flow, so ccqa's recorder makes it.
69
+ */
70
+ expectations?: string[];
71
+ /**
72
+ * What the cleanup itself must make true. Kept apart from `expectations`,
73
+ * which are about the flow: these are decided where the undo runs, and
74
+ * asserting them among the case's own steps would check them too early.
75
+ */
76
+ cleanupExpectations?: string[];
77
+ /**
78
+ * Sections ccqa does not interpret, handed to the recorder as context. A
79
+ * precondition belongs here: it says which account to sign in as and what
80
+ * must already exist, which is what whoever runs the case needs to read.
81
+ */
82
+ context?: Array<{
83
+ heading: string;
84
+ body: string;
85
+ }>;
86
+ /**
87
+ * Values the target's `header` template and `titleTags.field` refer to, by
88
+ * whatever names this source gives them. ccqa passes them through and never
89
+ * reads one itself.
90
+ */
91
+ fields?: Record<string, string>;
92
+ /** In the source, but opted out of runs and audits. */
93
+ disabled?: boolean;
94
+ }
95
+ /** Where a project's test cases come from. */
96
+ interface CaseSource {
97
+ /**
98
+ * Every case id this source holds. This is what a sweep audits and what
99
+ * `ccqa run` expands "all cases" to, so return only what you can load: a
100
+ * README sitting beside the cases is not one. ccqa sorts and de-duplicates.
101
+ */
102
+ list(): Promise<string[]> | string[];
103
+ /**
104
+ * One case, by its id or by the path a CLI argument named — both spellings
105
+ * reach the same case, and the returned `id` is the authoritative one.
106
+ *
107
+ * Throw when the case cannot be read; ccqa reports what you threw and never
108
+ * reads a failure as an empty answer.
109
+ */
110
+ load(ref: string): Promise<Case> | Case;
111
+ }
112
+ /**
113
+ * The module's default export: a function of the project root.
114
+ *
115
+ * `cwd` is the root ccqa resolved (`--cwd`, else the working directory), so a
116
+ * reader resolves its own paths against it rather than against `process.cwd()`.
117
+ */
118
+ type CaseSourceFactory = (ctx: {
119
+ cwd: string;
120
+ }) => CaseSource | Promise<CaseSource>;
121
+ //#endregion
122
+ export { Case, CaseSource, CaseSourceFactory, CaseStep };
@@ -0,0 +1,122 @@
1
+ //#region src/cases/contract.d.ts
2
+ /**
3
+ * What a test case is, and how ccqa is handed one.
4
+ *
5
+ * ccqa reads its own `spec.yaml` and nothing else. Every other way of writing
6
+ * a test case — a team's markdown, a spreadsheet export, rows in a tracker —
7
+ * is read by a module that project owns and points `targets.<id>.cases` at.
8
+ * This file is the whole contract between the two, and the only part of ccqa a
9
+ * reader module sees.
10
+ *
11
+ * Published as the `ccqa/case-source` subpath, and **types only**: a reader
12
+ * imports nothing at runtime, so `// @ts-check` plus a JSDoc
13
+ * `@type {import("ccqa/case-source").CaseSourceFactory}` is the whole
14
+ * integration, and ccqa's own dependencies never reach the consumer's build.
15
+ * (The gate that refuses `ccqa/*` imports inside an emitted test does not
16
+ * apply here — a reader module is not a test.)
17
+ *
18
+ * What ccqa decides, not the reader: step numbering, where the case's working
19
+ * files live, and where its recording goes. A reader that assigned them would
20
+ * be answering for a directory it cannot see.
21
+ */
22
+ /** One step of a case, as the document that states it wrote it. */
23
+ interface CaseStep {
24
+ /** What to do. */
25
+ instruction: string;
26
+ /**
27
+ * What this step must make true. Omit it when the case states its
28
+ * expectations once for the whole flow rather than per step — placing them
29
+ * is then the recorder's reading, not the reader's.
30
+ */
31
+ expected?: string;
32
+ }
33
+ /**
34
+ * One test case, as a source answers for it.
35
+ *
36
+ * Read strictly: an unknown key is refused rather than ignored, so a typo in a
37
+ * reader is reported where it was made instead of quietly costing a field.
38
+ */
39
+ interface Case {
40
+ /**
41
+ * Path-shaped, `/`-separated, no extension — `todo/add_item`. No leading
42
+ * `/`, no `..`, no backslashes. Unique within the source and stable for the
43
+ * life of the case: ccqa files the case's recording and evidence under it,
44
+ * and it is what `{case}` expands to in the target's `testPath`.
45
+ */
46
+ id: string;
47
+ /** Absolute path of the file that states the case. */
48
+ path: string;
49
+ /** That file verbatim. The audit and the failure classifier read it as written. */
50
+ text: string;
51
+ title: string;
52
+ /**
53
+ * `live` drives the case through the browser agent on every run;
54
+ * `deterministic` records it once and compiles a test.
55
+ *
56
+ * Two values, no spellings. A source that reads "manual" or "wip" out of its
57
+ * own documents decides for itself what that means — reading an unrecognised
58
+ * word as "record and generate" is how a case gets automated that nobody
59
+ * meant to automate.
60
+ */
61
+ mode: "deterministic" | "live";
62
+ /** What to do, in order. A case with no steps describes nothing to record. */
63
+ steps: CaseStep[];
64
+ /** What to undo afterwards, whatever the outcome. Emitted into `afterEach`. */
65
+ cleanup?: CaseStep[];
66
+ /**
67
+ * What the case says must be true, unattached to any step. Which step
68
+ * delivers which is a reading of the flow, so ccqa's recorder makes it.
69
+ */
70
+ expectations?: string[];
71
+ /**
72
+ * What the cleanup itself must make true. Kept apart from `expectations`,
73
+ * which are about the flow: these are decided where the undo runs, and
74
+ * asserting them among the case's own steps would check them too early.
75
+ */
76
+ cleanupExpectations?: string[];
77
+ /**
78
+ * Sections ccqa does not interpret, handed to the recorder as context. A
79
+ * precondition belongs here: it says which account to sign in as and what
80
+ * must already exist, which is what whoever runs the case needs to read.
81
+ */
82
+ context?: Array<{
83
+ heading: string;
84
+ body: string;
85
+ }>;
86
+ /**
87
+ * Values the target's `header` template and `titleTags.field` refer to, by
88
+ * whatever names this source gives them. ccqa passes them through and never
89
+ * reads one itself.
90
+ */
91
+ fields?: Record<string, string>;
92
+ /** In the source, but opted out of runs and audits. */
93
+ disabled?: boolean;
94
+ }
95
+ /** Where a project's test cases come from. */
96
+ interface CaseSource {
97
+ /**
98
+ * Every case id this source holds. This is what a sweep audits and what
99
+ * `ccqa run` expands "all cases" to, so return only what you can load: a
100
+ * README sitting beside the cases is not one. ccqa sorts and de-duplicates.
101
+ */
102
+ list(): Promise<string[]> | string[];
103
+ /**
104
+ * One case, by its id or by the path a CLI argument named — both spellings
105
+ * reach the same case, and the returned `id` is the authoritative one.
106
+ *
107
+ * Throw when the case cannot be read; ccqa reports what you threw and never
108
+ * reads a failure as an empty answer.
109
+ */
110
+ load(ref: string): Promise<Case> | Case;
111
+ }
112
+ /**
113
+ * The module's default export: a function of the project root.
114
+ *
115
+ * `cwd` is the root ccqa resolved (`--cwd`, else the working directory), so a
116
+ * reader resolves its own paths against it rather than against `process.cwd()`.
117
+ */
118
+ type CaseSourceFactory = (ctx: {
119
+ cwd: string;
120
+ }) => CaseSource | Promise<CaseSource>;
121
+ //#endregion
122
+ export { Case, CaseSource, CaseSourceFactory, CaseStep };
@@ -0,0 +1 @@
1
+ export {};
@@ -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";
38
39
  run: "run";
39
40
  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";
834
835
  run: "run";
835
836
  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";
38
39
  run: "run";
39
40
  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";
834
835
  run: "run";
835
836
  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.53.0",
3
+ "version": "1.55.0",
4
4
  "type": "module",
5
5
  "description": "Browser test recorder powered by Claude Code and agent-browser",
6
6
  "repository": {
@@ -54,6 +54,16 @@
54
54
  "types": "./dist/runtime/judge.d.cts",
55
55
  "default": "./dist/runtime/judge.cjs"
56
56
  }
57
+ },
58
+ "./case-source": {
59
+ "import": {
60
+ "types": "./dist/cases/contract.d.mts",
61
+ "default": "./dist/cases/contract.mjs"
62
+ },
63
+ "require": {
64
+ "types": "./dist/cases/contract.d.cts",
65
+ "default": "./dist/cases/contract.cjs"
66
+ }
57
67
  }
58
68
  },
59
69
  "typesVersions": {
@@ -69,6 +79,9 @@
69
79
  ],
70
80
  "judge": [
71
81
  "dist/runtime/judge.d.mts"
82
+ ],
83
+ "case-source": [
84
+ "dist/cases/contract.d.mts"
72
85
  ]
73
86
  }
74
87
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccqa",
3
- "version": "1.53.0",
3
+ "version": "1.55.0",
4
4
  "type": "module",
5
5
  "description": "Browser test recorder powered by Claude Code and agent-browser",
6
6
  "repository": {
@@ -54,6 +54,16 @@
54
54
  "types": "./dist/runtime/judge.d.cts",
55
55
  "default": "./dist/runtime/judge.cjs"
56
56
  }
57
+ },
58
+ "./case-source": {
59
+ "import": {
60
+ "types": "./dist/cases/contract.d.mts",
61
+ "default": "./dist/cases/contract.mjs"
62
+ },
63
+ "require": {
64
+ "types": "./dist/cases/contract.d.cts",
65
+ "default": "./dist/cases/contract.cjs"
66
+ }
57
67
  }
58
68
  },
59
69
  "typesVersions": {
@@ -69,6 +79,9 @@
69
79
  ],
70
80
  "judge": [
71
81
  "dist/runtime/judge.d.mts"
82
+ ],
83
+ "case-source": [
84
+ "dist/cases/contract.d.mts"
72
85
  ]
73
86
  }
74
87
  },