supercov 0.0.51 → 0.0.52

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.
@@ -70,7 +70,7 @@ examined and acknowledged.
70
70
  | `appliesTo` | Select tests by project-relative file and exact displayed test name. |
71
71
  | `nodes`, `edges` | Record source locations and relationships ending at `$assertion`. |
72
72
  | `countsAsAsserted` | List the node IDs you judge to be checked by the assertion. |
73
- | `watch` | List additional files the explanation depends on, such as helpers or configuration. Manifests, lockfiles and runner configuration are already tracked for the whole run; naming one here catches nothing, and the report says so. |
73
+ | `watch` | List additional files the explanation depends on, such as helpers or configuration. A watched file is depended on **as a whole**: any change to it that is not a comment is a review. Use it for a file your claim reasons about but holds no node of yours -- including claims about what a file does *not* contain, which no node can anchor. Naming a file that already holds this flow's nodes widens the flow from those declarations to the whole file, so a neighbouring function's body becomes a review again; that is sometimes what you mean, and the report says when you have done it. Manifests, lockfiles and runner configuration are already tracked for the whole run; naming one catches nothing, and the report says so. A redundant entry is free to take back out: it is not part of what the acknowledgement rests on, so removing one keeps the flow's credit. |
74
74
  | `questions` | Record unresolved investigation questions. Questions inside a flow block its credit. |
75
75
 
76
76
  Source anchors use project-relative paths with `/`, one-based lines and one-based
@@ -279,12 +279,25 @@ response in the map's top-level `changeAssessments` array:
279
279
  }
280
280
  ```
281
281
 
282
- This is an excerpt to add to your existing map. Include every entry in `knownFlows`
283
- that still exists and any additional affected flows. `knownFlows` are the flows
284
- the change has already made stale. `exposed` counts the flows whose selected
285
- tests ran the changed code and names those tests -- not stale for it, but the
286
- ones to think about; add the flows the change actually reaches. An empty list needs an explanation of why
287
- existing claims are unaffected; it does not mean the changed code is tested.
282
+ This is an excerpt to add to your existing map. `affectedFlows` is your
283
+ judgement: the flows this change invalidates, and only those. It is not a
284
+ restatement of `knownFlows` -- Supercov already holds that list and does not
285
+ ask you to retype it. **Every flow you name loses its acknowledgement and has
286
+ to be reread**, so naming a flow is a decision, and naming all of them is the
287
+ expensive one.
288
+
289
+ To make that judgement, read what the change view gives you. `knownFlows`
290
+ counts the flows the change has already made stale and samples them. `exposed`
291
+ counts the flows whose selected tests ran the changed code and names those
292
+ tests -- not stale for it, but the ones most worth thinking about. Neither is
293
+ a verdict: under an integration suite every test runs most of the code, so
294
+ exposure is a place to look, not a list to copy.
295
+
296
+ An empty list is the normal outcome for a change that touches no claim, and it
297
+ is the contract this channel exists to provide: one explanation answers for the
298
+ change and every dependent keeps its credit. It still needs an explanation of
299
+ why existing claims are unaffected, and it does not mean the changed code is
300
+ tested.
288
301
 
289
302
  Save the assessments and graph edits, validate, and copy the examined change
290
303
  tokens. Save again, then validate once more before copying final flow tokens.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supercov",
3
- "version": "0.0.51",
3
+ "version": "0.0.52",
4
4
  "description": "Coverage for coding agents and software factories \ud83c\udf19",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -97,14 +97,14 @@
97
97
  "test:launcher": "go vet ./cmd/... && go test ./cmd/..."
98
98
  },
99
99
  "optionalDependencies": {
100
- "@supercov/cli-darwin-arm64": "0.0.51",
101
- "@supercov/cli-darwin-x64": "0.0.51",
102
- "@supercov/cli-linux-arm64-gnu": "0.0.51",
103
- "@supercov/cli-linux-arm64-musl": "0.0.51",
104
- "@supercov/cli-linux-x64-gnu": "0.0.51",
105
- "@supercov/cli-linux-x64-musl": "0.0.51",
106
- "@supercov/cli-win32-arm64": "0.0.51",
107
- "@supercov/cli-win32-x64": "0.0.51"
100
+ "@supercov/cli-darwin-arm64": "0.0.52",
101
+ "@supercov/cli-darwin-x64": "0.0.52",
102
+ "@supercov/cli-linux-arm64-gnu": "0.0.52",
103
+ "@supercov/cli-linux-arm64-musl": "0.0.52",
104
+ "@supercov/cli-linux-x64-gnu": "0.0.52",
105
+ "@supercov/cli-linux-x64-musl": "0.0.52",
106
+ "@supercov/cli-win32-arm64": "0.0.52",
107
+ "@supercov/cli-win32-x64": "0.0.52"
108
108
  },
109
109
  "peerDependencies": {
110
110
  "@playwright/test": ">=1.55.0",
@@ -4,13 +4,50 @@ const NORMALIZED_KINDS = [
4
4
  ["integration", /(^|[/_.-])(integration|int)([/_.-]|$)/i],
5
5
  ["e2e", /(^|[/_.-])(e2e|end-to-end|offline|online)([/_.-]|$)/i],
6
6
  ];
7
- function classifiedKind(value) {
8
- if (!value)
7
+ // A runner that can only drive the whole system through its external interface
8
+ // is evidence, not a guess. Nothing else here is: a token in a filename is a
9
+ // convention someone may not have followed.
10
+ const STRONG_RUNNER_KINDS = { playwright: "e2e" };
11
+ // The most specific token in a segment wins, not the first one this list
12
+ // happens to hold. `checkout-integration-e2e` is an e2e spec whose name notes
13
+ // what it integrates; ordering by array position called it an integration.
14
+ function segmentKind(segment) {
15
+ if (!segment)
9
16
  return undefined;
10
17
  // gatewayE2e.test.ts and responseIntegration.test.ts are conventional
11
18
  // camel-case paths too. Do not infer kinds from test titles or API usage.
12
- const words = value.replace(/([a-z0-9])([A-Z])/g, "$1-$2");
13
- return NORMALIZED_KINDS.find(([, pattern]) => pattern.test(words))?.[0];
19
+ const words = segment.replace(/([a-z0-9])([A-Z])/g, "$1-$2");
20
+ let best;
21
+ for (const [kind, pattern] of NORMALIZED_KINDS) {
22
+ const found = words.search(pattern);
23
+ if (found >= 0 && (best === undefined || found > best.at))
24
+ best = { kind, at: found };
25
+ }
26
+ return best?.kind;
27
+ }
28
+ function classifiedKind(value) {
29
+ if (!value)
30
+ return undefined;
31
+ return segmentKind(value);
32
+ }
33
+ // A directory is a deliberate choice about where a suite lives; a filename is
34
+ // often just a description of the thing under test. Read directories from the
35
+ // deepest inwards, so the nearest enclosing suite wins.
36
+ function directoryKind(file) {
37
+ if (!file)
38
+ return undefined;
39
+ const segments = file.split(/[/\\]/).slice(0, -1);
40
+ for (let index = segments.length - 1; index >= 0; index -= 1) {
41
+ const kind = segmentKind(segments[index]);
42
+ if (kind)
43
+ return kind;
44
+ }
45
+ return undefined;
46
+ }
47
+ function basenameKind(file) {
48
+ if (!file)
49
+ return undefined;
50
+ return segmentKind(file.split(/[/\\]/).pop());
14
51
  }
15
52
  export function inferTestProvenance({ runner, file, project, explicitKind, }) {
16
53
  if (explicitKind?.trim()) {
@@ -30,20 +67,42 @@ export function inferTestProvenance({ runner, file, project, explicitKind, }) {
30
67
  source: "project",
31
68
  };
32
69
  }
33
- const pathKind = classifiedKind(file);
34
- if (pathKind) {
70
+ // A directory outranks the runner: putting a Playwright spec under
71
+ // `tests/integration/` is a statement about that suite.
72
+ const directory = directoryKind(file);
73
+ if (directory) {
74
+ return {
75
+ runner,
76
+ kind: directory,
77
+ ...(project ? { project } : {}),
78
+ source: "path",
79
+ };
80
+ }
81
+ // A filename token does not. `storefront-empire-integration.spec.ts` drove
82
+ // a real browser; reading `integration` out of its name reported 102 lines
83
+ // as untouched by E2E while a browser had rendered them. The runner knows
84
+ // better than the filename here, so it is asked first.
85
+ const strong = STRONG_RUNNER_KINDS[runner];
86
+ if (strong) {
87
+ return {
88
+ runner,
89
+ kind: strong,
90
+ ...(project ? { project } : {}),
91
+ source: "runner-default",
92
+ };
93
+ }
94
+ const basename = basenameKind(file);
95
+ if (basename) {
35
96
  return {
36
97
  runner,
37
- kind: pathKind,
98
+ kind: basename,
38
99
  ...(project ? { project } : {}),
39
100
  source: "path",
40
101
  };
41
102
  }
42
- const defaultKind = runner === "playwright"
43
- ? "e2e"
44
- : runner === "vitest" || runner === "jest" || runner === "node:test"
45
- ? "unit"
46
- : "unknown";
103
+ const defaultKind = runner === "vitest" || runner === "jest" || runner === "node:test"
104
+ ? "unit"
105
+ : "unknown";
47
106
  return {
48
107
  runner,
49
108
  kind: defaultKind,