spec-controller 0.1.0-alpha.3 → 0.1.0-alpha.30

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.
Files changed (76) hide show
  1. package/README.md +24 -4
  2. package/dist/cli-allocate/cli.d.ts +2 -0
  3. package/dist/cli-allocate/cli.d.ts.map +1 -0
  4. package/dist/cli-allocate/cli.js +45 -0
  5. package/dist/cli-allocate/cli.js.map +1 -0
  6. package/dist/cli-allocate/readHistory.d.ts +21 -0
  7. package/dist/cli-allocate/readHistory.d.ts.map +1 -0
  8. package/dist/cli-allocate/readHistory.js +97 -0
  9. package/dist/cli-allocate/readHistory.js.map +1 -0
  10. package/dist/cli-allocate/readParts.d.ts +3 -0
  11. package/dist/cli-allocate/readParts.d.ts.map +1 -0
  12. package/dist/cli-allocate/readParts.js +25 -0
  13. package/dist/cli-allocate/readParts.js.map +1 -0
  14. package/dist/cli-allocate/readTree.d.ts +17 -0
  15. package/dist/cli-allocate/readTree.d.ts.map +1 -0
  16. package/dist/cli-allocate/readTree.js +63 -0
  17. package/dist/cli-allocate/readTree.js.map +1 -0
  18. package/dist/cli-allocate/request.d.ts +11 -0
  19. package/dist/cli-allocate/request.d.ts.map +1 -0
  20. package/dist/cli-allocate/request.js +29 -0
  21. package/dist/cli-allocate/request.js.map +1 -0
  22. package/dist/cli-allocate/stderr.d.ts +14 -0
  23. package/dist/cli-allocate/stderr.d.ts.map +1 -0
  24. package/dist/cli-allocate/stderr.js +33 -0
  25. package/dist/cli-allocate/stderr.js.map +1 -0
  26. package/dist/cli-args.d.ts +70 -14
  27. package/dist/cli-args.d.ts.map +1 -1
  28. package/dist/cli-args.js +239 -18
  29. package/dist/cli-args.js.map +1 -1
  30. package/dist/cli-balance/cli.d.ts +18 -1
  31. package/dist/cli-balance/cli.d.ts.map +1 -1
  32. package/dist/cli-balance/cli.js +185 -161
  33. package/dist/cli-balance/cli.js.map +1 -1
  34. package/dist/cli-registry.d.ts +28 -0
  35. package/dist/cli-registry.d.ts.map +1 -1
  36. package/dist/cli-registry.js +39 -34
  37. package/dist/cli-registry.js.map +1 -1
  38. package/dist/cli.d.ts +4 -5
  39. package/dist/cli.d.ts.map +1 -1
  40. package/dist/cli.js +26 -12
  41. package/dist/cli.js.map +1 -1
  42. package/dist/deferralTags.d.ts +2 -2
  43. package/dist/deferralTags.js +2 -2
  44. package/dist/host.d.ts +25 -1
  45. package/dist/host.d.ts.map +1 -1
  46. package/dist/host.js +119 -15
  47. package/dist/host.js.map +1 -1
  48. package/dist/ingest/gherkinValidation.d.ts +9 -12
  49. package/dist/ingest/gherkinValidation.d.ts.map +1 -1
  50. package/dist/ingest/gherkinValidation.js +9 -12
  51. package/dist/ingest/gherkinValidation.js.map +1 -1
  52. package/dist/ingest/ingestQualityChecks.d.ts +3 -49
  53. package/dist/ingest/ingestQualityChecks.d.ts.map +1 -1
  54. package/dist/ingest/ingestQualityChecks.js +27 -120
  55. package/dist/ingest/ingestQualityChecks.js.map +1 -1
  56. package/dist/ingest/ingestScenarios.d.ts +8 -14
  57. package/dist/ingest/ingestScenarios.d.ts.map +1 -1
  58. package/dist/ingest/ingestScenarios.js +32 -55
  59. package/dist/ingest/ingestScenarios.js.map +1 -1
  60. package/package.json +2 -7
  61. package/dist/corpus/cli.d.ts +0 -34
  62. package/dist/corpus/cli.d.ts.map +0 -1
  63. package/dist/corpus/cli.js +0 -128
  64. package/dist/corpus/cli.js.map +0 -1
  65. package/dist/mutation-ratchet/index.d.ts +0 -46
  66. package/dist/mutation-ratchet/index.d.ts.map +0 -1
  67. package/dist/mutation-ratchet/index.js +0 -46
  68. package/dist/mutation-ratchet/index.js.map +0 -1
  69. package/dist/mutation-ratchet/record.d.ts +0 -178
  70. package/dist/mutation-ratchet/record.d.ts.map +0 -1
  71. package/dist/mutation-ratchet/record.js +0 -314
  72. package/dist/mutation-ratchet/record.js.map +0 -1
  73. package/dist/mutation-ratchet/report.d.ts +0 -109
  74. package/dist/mutation-ratchet/report.d.ts.map +0 -1
  75. package/dist/mutation-ratchet/report.js +0 -156
  76. package/dist/mutation-ratchet/report.js.map +0 -1
@@ -15,25 +15,6 @@
15
15
  import { type GherkinParseError } from "./gherkinValidation.js";
16
16
  import { type EvidenceObligations } from "@3f-consulting/spec-controller-core";
17
17
  import type { EvidenceReconciliation } from "@3f-consulting/spec-controller-core";
18
- /**
19
- * A reader over ONE resolution pass's records.
20
- *
21
- * EACH RECORD IS OPENED ONCE, WHICH IS WHY THIS IS A CLOSURE AND NOT A FUNCTION PER REFERENCE. A
22
- * manifest may name several gates of one record, and a reader opening it per entry would let two
23
- * references answer for two different states of the same file — a rewrite landing mid-pass, a gate
24
- * present for one and gone for the next. One read makes the pass's bars one reading of one record.
25
- *
26
- * THE REFUSAL IS REMEMBERED TOO. Cacheing only the successes would re-open an absent or unreadable
27
- * record per reference, which is the same disagreement by the other door — and the likelier door,
28
- * since a record being rewritten is unreadable for exactly the window that matters.
29
- *
30
- * THE SUBTRACTION IS THE RECORD'S, NOT THIS EDGE'S. `killFloorOf` spends the allowance; an edge
31
- * computing `detected - slack` itself would be a second place holding a fact about the record.
32
- */
33
- export declare function killFloorReaderFor(root: string): (reference: {
34
- readonly mutationGate: string;
35
- readonly recordFile?: string;
36
- }) => number | null;
37
18
  /**
38
19
  * The reconciler's resolved input paths — the CLI options threaded to the readers.
39
20
  * Every path is optional; a missing result file is tolerated (its evidence is
@@ -48,12 +29,6 @@ export interface ReconcileOptions {
48
29
  cucumber?: string;
49
30
  ci?: string;
50
31
  packageJson?: string;
51
- /**
52
- * The measurement artefact (@SCN-CIP-003) — the number each measured check posted, and
53
- * the credit side of every measured fitness check. Optional like the other result files: absent,
54
- * it contributes nothing, and each measured obligation it would have answered reads `missing`.
55
- */
56
- measurements?: string;
57
32
  /**
58
33
  * The CI RUN'S OWN RECORD of what each of its steps did (@SCN-LNT-021, @SCN-LNT-028) — the credit
59
34
  * side of every CONVERTED fitness check, written by the consumer's own pipeline and by neither
@@ -85,26 +60,6 @@ export interface ReconcileOptions {
85
60
  * refusal is not ours to interpret and rethrows.
86
61
  */
87
62
  export declare function featureCorpusParseErrors(featuresDir: string | undefined): GherkinParseError[];
88
- /**
89
- * The `@SCN` ids the feature corpus CARRIES MORE THAN ONCE (@SCN-LDG-020's guardrail, 3F-1775).
90
- *
91
- * An `@SCN` identifies exactly ONE scenario — that is the whole basis of attributing evidence to
92
- * it. If two scenarios carry the same id, a test citing it could be proving EITHER, and the tool
93
- * cannot know which: the corpus is ambiguous, and any verdict over it is a guess.
94
- *
95
- * WHY THIS EXISTS. Tag inheritance made this reachable: an `@SCN` hoisted to a `Feature:`/`Rule:`
96
- * is inherited by every scenario beneath it (cucumber does this, so we do — parity). Left
97
- * unhandled, ONE passing test then marked EVERY scenario sharing that id `balanced`, including
98
- * scenarios nothing proves — a BALANCED-WHEN-BROKEN run, exit 0. A new silent false-green,
99
- * introduced by the very fix that closed the last one.
100
- *
101
- * Refused at the edge (exit 2) rather than reconciled, under the Rule already shipped for the
102
- * observed side and the corrupt corpus (@SCN-CLI-014/015/016): an input the tool cannot read is a
103
- * usage error, never a verdict. Whether hoisting an `@SCN` is an authoring fault worth a richer
104
- * diagnostic is the deferred policy question (3F-1774's sibling); refusing to reconcile a corpus we
105
- * cannot read is not a policy — it is the floor.
106
- */
107
- export declare function featureCorpusDuplicateScnIds(featuresDir: string | undefined): string[];
108
63
  /**
109
64
  * Read the target's `feature-code → slug` map over the same `discoverFeatures` corpus
110
65
  * (@SCN-RPT-006) — each coded feature file's `@<FFF>` header (`featureCode`)
@@ -113,10 +68,9 @@ export declare function featureCorpusDuplicateScnIds(featuresDir: string | undef
113
68
  * the balance, threaded to `renderMarkdown` (via `writeOutputs`) as the 5th sibling arg
114
69
  * (markdown-only — the by-feature cut is markdown-only, so it is NOT serialised to JSON).
115
70
  * Uncoded files (`featureCode === null`) are omitted, so the by-feature emitter falls
116
- * back to the bare `FFF` for any id whose code no file registers. One-prefix-one-file holds
117
- * by invariant (`corpusFaults` raises `duplicate-feature-code`, @SCN-STG-010), so the map is
118
- * unambiguous. That invariant was `lintFeatureCodes`' until 3F-3313; it is the shipped command's
119
- * now, which is the first time a CONSUMER of this reader has it too.
71
+ * back to the bare `FFF` for any id whose code no file registers. When two files carry one
72
+ * code the map keeps the last, and that run lists `duplicate-feature-code` under Scenario code
73
+ * errors and fails (@SCN-STG-010, 3F-3635), so a report whose map is ambiguous never passes.
120
74
  */
121
75
  export declare function readFeatureNames(featuresDir?: string): Map<string, string>;
122
76
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ingestQualityChecks.d.ts","sourceRoot":"","sources":["../../src/ingest/ingestQualityChecks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAYH,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,qCAAqC,CAAC;AA+B7C,OAAO,KAAK,EAIV,sBAAsB,EACvB,MAAM,qCAAqC,CAAC;AAwD7C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,GAMX,CAAC,SAAS,EAAE;IAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,MAAM,GAAG,IAAI,CAc/F;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA8ED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,EAAE,CAQ7F;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAStF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAM1E;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAqB9E;AA2CD,wBAAgB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,sBAAsB,CAgE3E"}
1
+ {"version":3,"file":"ingestQualityChecks.d.ts","sourceRoot":"","sources":["../../src/ingest/ingestQualityChecks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAaH,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,qCAAqC,CAAC;AAkB7C,OAAO,KAAK,EAIV,sBAAsB,EACvB,MAAM,qCAAqC,CAAC;AA4B7C;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA8ED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,EAAE,CAQ7F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAM1E;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAuB9E;AAyCD,wBAAgB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,sBAAsB,CAoE3E"}
@@ -13,10 +13,10 @@
13
13
  * `package.json` to observe each fitness check the corpus cites.
14
14
  */
15
15
  import { readFileSync, existsSync } from "node:fs";
16
- import { basename, join } from "node:path";
16
+ import { basename } from "node:path";
17
17
  import { ciSourcePathsOf } from "./ciSourcePaths.js";
18
18
  import { discoverFeatures } from "./ingestScenarios.js";
19
- import { parseScenarios, countScnOccurrences, parseUntaggedScenarios, proseOnlyCodes, } from "@3f-consulting/spec-controller-core";
19
+ import { parseScenarios, countScnOccurrences, parseUntaggedScenarios, proseOnlyCodes, sharedScnCodesOf, } from "@3f-consulting/spec-controller-core";
20
20
  import { MissingFeatureCorpusError, UnreadableFeatureCorpusError, } from "./gherkinValidation.js";
21
21
  import { DEFAULT_DEFERRAL_TAGS } from "../deferralTags.js";
22
22
  import { evidenceObligations, } from "@3f-consulting/spec-controller-core";
@@ -30,12 +30,6 @@ import { parseCucumberEvidenceObservations, } from "@3f-consulting/spec-controll
30
30
  // their CALLER. What is left here is the reads: this edge opens the files and hands the bytes over.
31
31
  import { evidenceReconciliationFromSources } from "@3f-consulting/spec-controller-core";
32
32
  import { resolveTargetRoot } from "../run-management/resolveRunInputs.js";
33
- // The record reader, reached for the RECORD half of a bar that names a gate (@SCN-MUT-012): its
34
- // conventional record name, its parser, and the floor a gate's own figures imply. Through the
35
- // barrel, which is the surface a caller holds. (The directory is still spelled `mutation-ratchet`
36
- // and the ratchet it was named for is deleted — 3F-3337 left the name standing rather than move a
37
- // module inside a delete; filed as a finding.)
38
- import { gateRecordFor, killFloorOf, MalformedRecordError, parseGateRecords, RECORD_FILE, } from "../mutation-ratchet/index.js";
39
33
  /**
40
34
  * The per-input FALLBACK quality-check for each JS-ecosystem result source — the
41
35
  * source→quality-check mapping, SET at the CLI edge rather than hardcoded in the core
@@ -54,74 +48,12 @@ const CUCUMBER_QUALITY_CHECK = "integration";
54
48
  * publishes and `balance` is the command that reconciles; the engine holds neither, and a core
55
49
  * that assumed them would be asserting something about a command line it never sees.
56
50
  *
57
- * BOTH TOKENS, AND THE SECOND IS NOT DECORATION. This repository's own pipeline runs
58
- * `pnpm spec-controller tags` beside its balance step — the same binary, doing something that is
59
- * an ordinary step of the pipeline being reconciled — so an invocation naming the binary alone
60
- * would take that step off the books. The comparison is whole, adjacent shell tokens
51
+ * BOTH TOKENS, AND THE SECOND IS NOT DECORATION. A step running the same binary for anything but
52
+ * `balance` is an ordinary step of the pipeline being reconciled, so an invocation naming the
53
+ * binary alone would take that step off the books. The comparison is whole, adjacent shell tokens
61
54
  * (`parse/commandRuns`), so this names the reconciliation and nothing else the tool can do.
62
55
  */
63
56
  const RECONCILER_INVOCATION = "spec-controller balance";
64
- /**
65
- * A refusal met with an absent bar rather than let out of this edge.
66
- *
67
- * NOTHING IS THROWN OUT OF HERE, and that is the stance rather than caution. A refusal that escapes
68
- * costs the whole run its verdict over one bad character in a file nobody may even be reading — a
69
- * fault that is loud nowhere, where an absent bar is loud on the ledger. It is `thresholdsOf`'s own
70
- * stance for a malformed entry, applied to the half of a bar that now lives in a second file.
71
- *
72
- * TWO CLASSES AND NO MORE. The record library normalises every fault it can see — the YAML parser's
73
- * own included — into `MalformedRecordError` precisely so a caller catches all of them by catching
74
- * one; beside it sits the read that never got as far as a record. A bare catch would swallow a
75
- * defect in this edge as well, and answer it with the same silent absence.
76
- */
77
- function orNoBar(read) {
78
- try {
79
- return read();
80
- }
81
- catch (cause) {
82
- if (cause instanceof MalformedRecordError || isReadRefusal(cause))
83
- return null;
84
- throw cause;
85
- }
86
- }
87
- /** A filesystem refusal — the record was not there, or could not be opened. */
88
- function isReadRefusal(cause) {
89
- return cause instanceof Error && typeof cause.code === "string";
90
- }
91
- /**
92
- * A reader over ONE resolution pass's records.
93
- *
94
- * EACH RECORD IS OPENED ONCE, WHICH IS WHY THIS IS A CLOSURE AND NOT A FUNCTION PER REFERENCE. A
95
- * manifest may name several gates of one record, and a reader opening it per entry would let two
96
- * references answer for two different states of the same file — a rewrite landing mid-pass, a gate
97
- * present for one and gone for the next. One read makes the pass's bars one reading of one record.
98
- *
99
- * THE REFUSAL IS REMEMBERED TOO. Cacheing only the successes would re-open an absent or unreadable
100
- * record per reference, which is the same disagreement by the other door — and the likelier door,
101
- * since a record being rewritten is unreadable for exactly the window that matters.
102
- *
103
- * THE SUBTRACTION IS THE RECORD'S, NOT THIS EDGE'S. `killFloorOf` spends the allowance; an edge
104
- * computing `detected - slack` itself would be a second place holding a fact about the record.
105
- */
106
- export function killFloorReaderFor(root
107
- // The reader's shape is SPELLED here rather than imported, and that is the boundary working
108
- // rather than a duplication. `KillFloorReader` and the reference it takes are core-internal —
109
- // the registry-home question (increment D) still governs them — and this module is published, so
110
- // naming them by their `./internal` types would put an unpublished specifier back in the tarball
111
- // for a type alone. Structural typing is what makes the closure the engine's parameter accepts.
112
- ) {
113
- const opened = new Map();
114
- return (reference) => {
115
- const recordFile = reference.recordFile ?? RECORD_FILE;
116
- if (!opened.has(recordFile)) {
117
- opened.set(recordFile, orNoBar(() => parseGateRecords(readFileSync(join(root, recordFile), "utf8"), recordFile)));
118
- }
119
- const records = opened.get(recordFile);
120
- if (records == null)
121
- return null;
122
- return orNoBar(() => killFloorOf(gateRecordFor(reference.mutationGate, records, recordFile)));
123
- };
124
- }
125
57
  /** Read + JSON-parse a file if present, else return undefined. */
126
58
  function readJsonIfPresent(path) {
127
59
  if (!path || !existsSync(path))
@@ -142,7 +74,7 @@ function readJsonIfPresent(path) {
142
74
  * this by hand is five chances for one to stay intolerant, and a run that reports on a corpus for
143
75
  * four of its readings and throws on the fifth is worse than one that throws. The translation is
144
76
  * also why `discoverFeatures` is not the place to be tolerant — see its own note, and
145
- * `MissingFeatureCorpusError`'s: `tags` reaches the same reader and keeps refusing.
77
+ * `MissingFeatureCorpusError`'s.
146
78
  *
147
79
  * IT IS `[]`, NOT A VERDICT. Zero rows is what @SCN-CLI-019 reads as NOTHING TO RECONCILE, and a
148
80
  * corpus-less target that still has uncited credits keeps its suspense row and its exit 1
@@ -217,35 +149,6 @@ export function featureCorpusParseErrors(featuresDir) {
217
149
  throw err;
218
150
  }
219
151
  }
220
- /**
221
- * The `@SCN` ids the feature corpus CARRIES MORE THAN ONCE (@SCN-LDG-020's guardrail, 3F-1775).
222
- *
223
- * An `@SCN` identifies exactly ONE scenario — that is the whole basis of attributing evidence to
224
- * it. If two scenarios carry the same id, a test citing it could be proving EITHER, and the tool
225
- * cannot know which: the corpus is ambiguous, and any verdict over it is a guess.
226
- *
227
- * WHY THIS EXISTS. Tag inheritance made this reachable: an `@SCN` hoisted to a `Feature:`/`Rule:`
228
- * is inherited by every scenario beneath it (cucumber does this, so we do — parity). Left
229
- * unhandled, ONE passing test then marked EVERY scenario sharing that id `balanced`, including
230
- * scenarios nothing proves — a BALANCED-WHEN-BROKEN run, exit 0. A new silent false-green,
231
- * introduced by the very fix that closed the last one.
232
- *
233
- * Refused at the edge (exit 2) rather than reconciled, under the Rule already shipped for the
234
- * observed side and the corrupt corpus (@SCN-CLI-014/015/016): an input the tool cannot read is a
235
- * usage error, never a verdict. Whether hoisting an `@SCN` is an authoring fault worth a richer
236
- * diagnostic is the deferred policy question (3F-1774's sibling); refusing to reconcile a corpus we
237
- * cannot read is not a policy — it is the floor.
238
- */
239
- export function featureCorpusDuplicateScnIds(featuresDir) {
240
- const seen = new Set();
241
- const duplicates = new Set();
242
- for (const scenario of scenariosFromFeatures(featuresDir)) {
243
- if (seen.has(scenario.scnId))
244
- duplicates.add(scenario.scnId);
245
- seen.add(scenario.scnId);
246
- }
247
- return [...duplicates].sort();
248
- }
249
152
  /**
250
153
  * Read the target's `feature-code → slug` map over the same `discoverFeatures` corpus
251
154
  * (@SCN-RPT-006) — each coded feature file's `@<FFF>` header (`featureCode`)
@@ -254,10 +157,9 @@ export function featureCorpusDuplicateScnIds(featuresDir) {
254
157
  * the balance, threaded to `renderMarkdown` (via `writeOutputs`) as the 5th sibling arg
255
158
  * (markdown-only — the by-feature cut is markdown-only, so it is NOT serialised to JSON).
256
159
  * Uncoded files (`featureCode === null`) are omitted, so the by-feature emitter falls
257
- * back to the bare `FFF` for any id whose code no file registers. One-prefix-one-file holds
258
- * by invariant (`corpusFaults` raises `duplicate-feature-code`, @SCN-STG-010), so the map is
259
- * unambiguous. That invariant was `lintFeatureCodes`' until 3F-3313; it is the shipped command's
260
- * now, which is the first time a CONSUMER of this reader has it too.
160
+ * back to the bare `FFF` for any id whose code no file registers. When two files carry one
161
+ * code the map keeps the last, and that run lists `duplicate-feature-code` under Scenario code
162
+ * errors and fails (@SCN-STG-010, 3F-3635), so a report whose map is ambiguous never passes.
261
163
  */
262
164
  export function readFeatureNames(featuresDir) {
263
165
  const map = new Map();
@@ -283,9 +185,11 @@ export function readEvidenceObligations(features) {
283
185
  let scenarioCount = 0;
284
186
  const untagged = [];
285
187
  const sources = [];
188
+ const files = [];
286
189
  for (const f of featureFiles) {
287
190
  const source = readFileSync(f.path, "utf8");
288
191
  sources.push(source);
192
+ files.push({ feature: `${f.slug}.feature`, source });
289
193
  raw += countScnOccurrences(source);
290
194
  scenarioCount += parseScenarios(source).length;
291
195
  for (const scenario of parseUntaggedScenarios(source)) {
@@ -295,7 +199,7 @@ export function readEvidenceObligations(features) {
295
199
  untagged.sort((a, b) => a.feature.localeCompare(b.feature) || a.scenario.localeCompare(b.scenario));
296
200
  // The prose-only codes are a CROSS-CORPUS difference (a code tagged in any file isn't
297
201
  // dangling), so it reads all sources at once — not per-file like the others.
298
- return evidenceObligations(raw, scenarioCount, untagged, proseOnlyCodes(sources));
202
+ return evidenceObligations(raw, scenarioCount, untagged, proseOnlyCodes(sources), sharedScnCodesOf(files));
299
203
  }
300
204
  /**
301
205
  * Parse the target's RUNTIME observations ONCE — the tag-derived, per-tier observation set
@@ -323,9 +227,8 @@ export function runReconcile(opts) {
323
227
  const pkgPath = opts.packageJson ?? "package.json";
324
228
  const packageJson = readJsonIfPresent(pkgPath) ?? {};
325
229
  // The TARGET repo root — the credit locators are made relative to it, so a phantom-scenario row's
326
- // where-to-fix (ruling Q5) stays portable in a banked artefact, and a bar naming a mutation gate
327
- // finds that target's record rather than this tool's own (@SCN-MUT-012). Resolved ONCE here and
328
- // threaded, which is also what keeps `execFileSync` and `process.cwd()` out of the engine.
230
+ // where-to-fix (ruling Q5) stays portable in a banked artefact. Resolved ONCE here and threaded,
231
+ // which is also what keeps `execFileSync` and `process.cwd()` out of the engine.
329
232
  const root = resolveTargetRoot({ features: opts.features });
330
233
  // `--ci` names a workflow FILE or a DIRECTORY of them, and defaults to the directory
331
234
  // (@SCN-USG-006, 3F-2238): a pipeline split across files enforces its checks just as one
@@ -340,11 +243,19 @@ export function runReconcile(opts) {
340
243
  // keeping the core that way is the point. Resolving the list twice would be two chances for the
341
244
  // bytes and the names to fall out of the order they are paired by.
342
245
  const ciPaths = ciSourcePathsOf(ciPath);
246
+ // The corpus, discovered and read ONCE: its bytes, and — paired by index — which file each came
247
+ // from and the header code it carries, for the scenario code errors (3F-3635).
248
+ const corpusFiles = balanceCorpus(opts.features).map((f) => ({
249
+ slug: f.slug,
250
+ featureCode: f.featureCode,
251
+ source: readFileSync(f.path, "utf8"),
252
+ }));
343
253
  return evidenceReconciliationFromSources({
344
254
  // The corpus's bytes, read ONCE at this impure edge (3F-3088). The engine composes the three
345
255
  // readings a citing run needs from this one array, so they cannot be assembled from two
346
256
  // discoveries that disagree about the corpus (@SCN-LNT-009).
347
- featureSources: featureSources(opts.features),
257
+ featureSources: corpusFiles.map((f) => f.source),
258
+ featureFiles: corpusFiles.map(({ slug, featureCode }) => ({ slug, featureCode })),
348
259
  // The untagged half by its `<feature> : <scenario>` locator — parsed here because the locator
349
260
  // names the FILE, which the discovery knows and a source string does not.
350
261
  untaggedScenarios: untaggedScenariosFromFeatures(opts.features),
@@ -355,26 +266,22 @@ export function runReconcile(opts) {
355
266
  // half of the entry that does not come out of the workflow's own text. The directory it sits
356
267
  // in is the caller's `--ci`, not the consumer's subject, so it is not part of what they write.
357
268
  ciSourceNames: ciPaths.map((path) => basename(path)),
358
- measurements: readJsonIfPresent(opts.measurements) ?? {},
359
269
  // The CONVERTED checks' credit stream, read at this edge like every other report. Absent it is
360
270
  // undefined rather than empty, and that distinction reaches the reader (@SCN-LNT-027): "no
361
271
  // record" is a run nobody recorded, "a record holding nothing" is a run that recorded none of
362
272
  // these steps, and those are opposite facts.
363
- // STRUCTURALLY, NOT BY NAME, for the reason `measurements` above is read that way: this module
364
- // travels in the tarball and reaches core through the PUBLIC barrel alone (3F-3086), and
273
+ // STRUCTURALLY, NOT BY NAME: this module travels in the tarball and reaches core through the PUBLIC barrel alone (3F-3086), and
365
274
  // `CiStepRecord` is not on it. The shape is the document's, which is the consumer's, so what is
366
275
  // written here is what the file holds rather than a contract owed to anyone.
367
276
  ciStepRecord: readJsonIfPresent(opts.ciSteps),
368
277
  runtimeObservations: parseRuntimeObservations(opts, root),
369
- // The three things the engine's resolvers cannot hold, and each for its own reason: the
370
- // starter deferral set is the application layer's own policy, how this tool is invoked to
371
- // reconcile is a fact about this product's command line, and the reader opens a file on the
372
- // target's tree. THE PREFIX SET USED TO SIT HERE AND IS GONE (@SCN-USG-004, 3F-3343): the
278
+ // The two things the engine's resolvers cannot hold, and each for its own reason: the
279
+ // starter deferral set is the application layer's own policy, and how this tool is invoked to
280
+ // reconcile is a fact about this product's command line. THE PREFIX SET USED TO SIT HERE AND IS GONE (@SCN-USG-004, 3F-3343): the
373
281
  // engine no longer decides a check's membership by its name, so there is no convention left
374
282
  // for this edge to own.
375
283
  deferralTags: DEFAULT_DEFERRAL_TAGS,
376
284
  reconcilerInvocation: RECONCILER_INVOCATION,
377
- killFloor: killFloorReaderFor(root),
378
285
  });
379
286
  }
380
287
  // The static-check kind-counts are no longer read as a parallel census (@SCN-USG-001 / 3F-2118):
@@ -1 +1 @@
1
- {"version":3,"file":"ingestQualityChecks.js","sourceRoot":"","sources":["../../src/ingest/ingestQualityChecks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAA0B,MAAM,sBAAsB,CAAC;AAChF,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,sBAAsB,EACtB,cAAc,GACf,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,yBAAyB,EACzB,4BAA4B,GAE7B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EACL,mBAAmB,GAGpB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,+BAA+B,GAEhC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,iCAAiC,GAElC,MAAM,qCAAqC,CAAC;AAC7C,mGAAmG;AACnG,yFAAyF;AACzF,yFAAyF;AACzF,gGAAgG;AAChG,iGAAiG;AACjG,oGAAoG;AACpG,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AAExF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC1E,gGAAgG;AAChG,8FAA8F;AAC9F,kGAAkG;AAClG,kGAAkG;AAClG,+CAA+C;AAC/C,OAAO,EACL,aAAa,EACb,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,GAEZ,MAAM,8BAA8B,CAAC;AAQtC;;;;;;GAMG;AACH,MAAM,oBAAoB,GAAwB,MAAM,CAAC;AACzD,MAAM,sBAAsB,GAAwB,aAAa,CAAC;AAElE;;;;;;;;;;;;;;GAcG;AACH,MAAM,qBAAqB,GAAG,yBAAyB,CAAC;AAExD;;;;;;;;;;;;GAYG;AACH,SAAS,OAAO,CAAI,IAAa;IAC/B,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,oBAAoB,IAAI,aAAa,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAC/E,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,KAAK,YAAY,KAAK,IAAI,OAAQ,KAA+B,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC7F,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAY;AACZ,4FAA4F;AAC5F,8FAA8F;AAC9F,iGAAiG;AACjG,iGAAiG;AACjG,gGAAgG;;IAEhG,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkD,CAAC;IACzE,OAAO,CAAC,SAAS,EAAE,EAAE;QACnB,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,IAAI,WAAW,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,CACR,UAAU,EACV,OAAO,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAC1F,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACvC,IAAI,OAAO,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QACjC,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,aAAa,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAChG,CAAC,CAAC;AACJ,CAAC;AAqCD,kEAAkE;AAClE,SAAS,iBAAiB,CAAI,IAAwB;IACpD,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAM,CAAC;AACrD,CAAC;AACD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,SAAS,aAAa,CAAC,WAA+B;IACpD,IAAI,CAAC;QACH,OAAO,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACxF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,yBAAyB;YAAE,OAAO,EAAE,CAAC;QACxD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,cAAc,CAAC,WAA+B;IACrD,OAAO,aAAa,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,qBAAqB,CAC5B,WAA+B,EAC/B,eAAkC,qBAAqB;IAEvD,OAAO,cAAc,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,6BAA6B,CAAC,WAA+B;IACpE,OAAO,aAAa,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAC9C,sBAAsB,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CACtD,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,cAAc,QAAQ,EAAE,CAChD,CACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,wBAAwB,CAAC,WAA+B;IACtE,IAAI,CAAC;QACH,aAAa,CAAC,WAAW,CAAC,CAAC;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,4BAA4B;YAAE,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;QACxE,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,4BAA4B,CAAC,WAA+B;IAC1E,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IAErC,KAAK,MAAM,QAAQ,IAAI,qBAAqB,CAAC,WAAW,CAAC,EAAE,CAAC;QAC1D,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC7D,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC;AAChC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAoB;IACnD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,KAAK,MAAM,CAAC,IAAI,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;QAC3C,IAAI,CAAC,CAAC,WAAW,KAAK,IAAI;YAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAiB;IACvD,MAAM,YAAY,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,MAAM,QAAQ,GAAuB,EAAE,CAAC;IACxC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5C,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,GAAG,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACnC,aAAa,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;QAC/C,KAAK,MAAM,QAAQ,IAAI,sBAAsB,CAAC,MAAM,CAAC,EAAE,CAAC;YACtD,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IACD,QAAQ,CAAC,IAAI,CACX,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CACrF,CAAC;IACF,sFAAsF;IACtF,6EAA6E;IAC7E,OAAO,mBAAmB,CAAC,GAAG,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;AACpF,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,wBAAwB,CAAC,IAAsB,EAAE,IAAwB;IAChF,MAAM,GAAG,GAA0B,EAAE,CAAC;IACtC,MAAM,YAAY,GAAG,iBAAiB,CAAmB,IAAI,CAAC,MAAM,CAAC,CAAC;IACtE,IAAI,YAAY;QAAE,GAAG,CAAC,IAAI,CAAC,GAAG,+BAA+B,CAAC,YAAY,EAAE,oBAAoB,EAAE,IAAI,CAAC,CAAC,CAAC;IACzG,MAAM,cAAc,GAAG,iBAAiB,CAAqB,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5E,IAAI,cAAc;QAAE,GAAG,CAAC,IAAI,CAAC,GAAG,iCAAiC,CAAC,cAAc,EAAE,sBAAsB,EAAE,IAAI,CAAC,CAAC,CAAC;IACjH,OAAO,GAAG,CAAC;AACb,CAAC;AA0BD,MAAM,UAAU,YAAY,CAAC,IAAsB;IACjD,gGAAgG;IAChG,gGAAgG;IAChG,yFAAyF;IACzF,iCAAiC;IACjC,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,IAAI,cAAc,CAAC;IACnD,MAAM,WAAW,GAAG,iBAAiB,CAAiB,OAAO,CAAC,IAAI,EAAE,CAAC;IACrE,kGAAkG;IAClG,iGAAiG;IACjG,gGAAgG;IAChG,2FAA2F;IAC3F,MAAM,IAAI,GAAG,iBAAiB,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC5D,qFAAqF;IACrF,yFAAyF;IACzF,wFAAwF;IACxF,oFAAoF;IACpF,0FAA0F;IAC1F,8BAA8B;IAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,IAAI,mBAAmB,CAAC;IAC9C,gGAAgG;IAChG,6FAA6F;IAC7F,+FAA+F;IAC/F,gGAAgG;IAChG,mEAAmE;IACnE,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAExC,OAAO,iCAAiC,CAAC;QACvC,6FAA6F;QAC7F,wFAAwF;QACxF,6DAA6D;QAC7D,cAAc,EAAE,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC7C,8FAA8F;QAC9F,0EAA0E;QAC1E,iBAAiB,EAAE,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC/D,WAAW;QACX,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5D,6FAA6F;QAC7F,2FAA2F;QAC3F,6FAA6F;QAC7F,+FAA+F;QAC/F,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpD,YAAY,EAAE,iBAAiB,CAA6B,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;QACpF,+FAA+F;QAC/F,2FAA2F;QAC3F,8FAA8F;QAC9F,6CAA6C;QAC7C,+FAA+F;QAC/F,yFAAyF;QACzF,gGAAgG;QAChG,6EAA6E;QAC7E,YAAY,EAAE,iBAAiB,CAC7B,IAAI,CAAC,OAAO,CACb;QACD,mBAAmB,EAAE,wBAAwB,CAAC,IAAI,EAAE,IAAI,CAAC;QACzD,wFAAwF;QACxF,0FAA0F;QAC1F,4FAA4F;QAC5F,0FAA0F;QAC1F,4FAA4F;QAC5F,wBAAwB;QACxB,YAAY,EAAE,qBAAqB;QACnC,oBAAoB,EAAE,qBAAqB;QAC3C,SAAS,EAAE,kBAAkB,CAAC,IAAI,CAAC;KACpC,CAAC,CAAC;AACL,CAAC;AAED,iGAAiG;AACjG,8FAA8F;AAC9F,gGAAgG;AAChG,+FAA+F;AAC/F,oGAAoG;AACpG,yFAAyF"}
1
+ {"version":3,"file":"ingestQualityChecks.js","sourceRoot":"","sources":["../../src/ingest/ingestQualityChecks.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,14 @@
1
1
  /**
2
2
  * Ingest the target's feature files — the IMPURE reader that walks features/ on disk
3
- * and parses each file's feature code + scenario codes into DiscoveredFeature
4
- * records (split out of the old spec/featureCodes.ts). The pure per-source
5
- * parsers (extractFeatureCode / parseScenarioCodes) live here with their sole consumer;
6
- * the `scnId → feature-code` grammar helper is engine-side (packages/core/src/lib/
3
+ * and reads each file's header code into DiscoveredFeature records (split out of the old
4
+ * spec/featureCodes.ts). The pure header parser (extractFeatureCode) lives here with its sole
5
+ * consumer; the `scnId → feature-code` grammar helper is engine-side (packages/core/src/lib/
7
6
  * featureCode.ts); the duplicate-code lint is engine-side too since 3F-3313, as @SCN-STG-010 in
8
- * parse/corpusFaults.ts, and no longer a private check under tests/checks/.
7
+ * parse/scenarioCodeErrors.ts, and no longer a private check under tests/checks/.
8
+ *
9
+ * NO PER-FILE LIST OF SCENARIO CODES (3F-3636). One used to ride here, read by a copy of the
10
+ * @SCN grammar of its own, and nothing but its own test read it; a scenario's code is read by the
11
+ * engine's scenario walk, which is the reading the ledger's accounts are minted from.
9
12
  *
10
13
  * The foundation, folded into the @LDG runtime spine.
11
14
  */
@@ -16,8 +19,6 @@ export interface DiscoveredFeature {
16
19
  /** Absolute path to the feature file. */
17
20
  path: string;
18
21
  featureCode: string | null;
19
- /** Every @SCN-<FFF>-<NNN> scenario code in the file, in order, duplicates kept. */
20
- scenarioCodes: string[];
21
22
  }
22
23
  /**
23
24
  * Read the Feature-level @<FFF> header tag from a feature file's source — the
@@ -25,13 +26,6 @@ export interface DiscoveredFeature {
25
26
  * or a recognised evidence/routing tag. Returns null if absent.
26
27
  */
27
28
  export declare function extractFeatureCode(source: string): string | null;
28
- /**
29
- * Parse every @SCN-<FFF>-<NNN> scenario code in a feature file's source, in
30
- * document order, duplicates preserved. Only genuine TAG LINES are scanned (a
31
- * line whose first non-blank character is `@` and whose every whitespace-
32
- * separated token is a tag) — prose that merely mentions a code is skipped.
33
- */
34
- export declare function parseScenarioCodes(source: string): string[];
35
29
  /**
36
30
  * Discover every feature file on disk and its header code — REFUSING a corpus the Gherkin
37
31
  * parser rejects (@SCN-GPG-001, 3F-2817).
@@ -1 +1 @@
1
- {"version":3,"file":"ingestScenarios.d.ts","sourceRoot":"","sources":["../../src/ingest/ingestScenarios.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAeH,iFAAiF;AACjF,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mFAAmF;IACnF,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAsBhE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAc3D;AAwCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAsB,GAAG,iBAAiB,EAAE,CAkBjF"}
1
+ {"version":3,"file":"ingestScenarios.d.ts","sourceRoot":"","sources":["../../src/ingest/ingestScenarios.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAeH,iFAAiF;AACjF,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKhE;AA8DD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAsB,GAAG,iBAAiB,EAAE,CAiBjF"}
@@ -1,11 +1,14 @@
1
1
  /**
2
2
  * Ingest the target's feature files — the IMPURE reader that walks features/ on disk
3
- * and parses each file's feature code + scenario codes into DiscoveredFeature
4
- * records (split out of the old spec/featureCodes.ts). The pure per-source
5
- * parsers (extractFeatureCode / parseScenarioCodes) live here with their sole consumer;
6
- * the `scnId → feature-code` grammar helper is engine-side (packages/core/src/lib/
3
+ * and reads each file's header code into DiscoveredFeature records (split out of the old
4
+ * spec/featureCodes.ts). The pure header parser (extractFeatureCode) lives here with its sole
5
+ * consumer; the `scnId → feature-code` grammar helper is engine-side (packages/core/src/lib/
7
6
  * featureCode.ts); the duplicate-code lint is engine-side too since 3F-3313, as @SCN-STG-010 in
8
- * parse/corpusFaults.ts, and no longer a private check under tests/checks/.
7
+ * parse/scenarioCodeErrors.ts, and no longer a private check under tests/checks/.
8
+ *
9
+ * NO PER-FILE LIST OF SCENARIO CODES (3F-3636). One used to ride here, read by a copy of the
10
+ * @SCN grammar of its own, and nothing but its own test read it; a scenario's code is read by the
11
+ * engine's scenario walk, which is the reading the ledger's accounts are minted from.
9
12
  *
10
13
  * The foundation, folded into the @LDG runtime spine.
11
14
  */
@@ -21,56 +24,32 @@ const CODE_PATTERN = /^[A-Z0-9]{2,5}$/;
21
24
  */
22
25
  export function extractFeatureCode(source) {
23
26
  const lines = source.split(/\r?\n/);
24
- for (let i = 0; i < lines.length; i++) {
25
- const line = lines[i].trim();
26
- if (line === "" || line.startsWith("#"))
27
- continue;
28
- if (line.startsWith("Feature:")) {
29
- for (let j = i - 1; j >= 0; j--) {
30
- const prev = lines[j].trim();
31
- if (prev === "")
32
- continue;
33
- if (!prev.startsWith("@"))
34
- break;
35
- const tags = prev.split(/\s+/).filter((t) => t.startsWith("@"));
36
- for (const tag of tags) {
37
- const bare = tag.slice(1);
38
- if (bare.startsWith("SCN-"))
39
- continue;
40
- if (["unit", "integration", "e2e", "performance", "smoke"].includes(bare))
41
- continue;
42
- if (CODE_PATTERN.test(bare))
43
- return bare;
44
- }
45
- }
46
- return null;
47
- }
48
- }
49
- return null;
27
+ const featureLine = lines.findIndex((line) => line.trim().startsWith("Feature:"));
28
+ if (featureLine < 0)
29
+ return null;
30
+ return headerTagsAbove(lines, featureLine).find(isHeaderCode) ?? null;
31
+ }
32
+ /** The tags a tier or routing concern owns, which are never a file's header code. */
33
+ const NON_CODE_TAGS = ["unit", "integration", "e2e", "performance", "smoke"];
34
+ /** Is this bare tag the file's header code — not an @SCN id, not a tier tag, and code-shaped? */
35
+ function isHeaderCode(bare) {
36
+ return !bare.startsWith("SCN-") && !NON_CODE_TAGS.includes(bare) && CODE_PATTERN.test(bare);
50
37
  }
51
38
  /**
52
- * Parse every @SCN-<FFF>-<NNN> scenario code in a feature file's source, in
53
- * document order, duplicates preserved. Only genuine TAG LINES are scanned (a
54
- * line whose first non-blank character is `@` and whose every whitespace-
55
- * separated token is a tag) — prose that merely mentions a code is skipped.
39
+ * The bare tags on the tag lines directly above the `Feature:` line, nearest line first and each
40
+ * line left to right. Blank lines are passed over; the first line that is not a tag line ends it.
56
41
  */
57
- export function parseScenarioCodes(source) {
58
- const codes = [];
59
- const codePattern = /^SCN-[A-Z0-9]+-\d+$/;
60
- for (const rawLine of source.split(/\r?\n/)) {
61
- const line = rawLine.trim();
62
- if (!line.startsWith("@"))
63
- continue;
64
- const tokens = line.split(/\s+/);
65
- if (!tokens.every((t) => t.startsWith("@")))
42
+ function headerTagsAbove(lines, featureLine) {
43
+ const tags = [];
44
+ for (let j = featureLine - 1; j >= 0; j--) {
45
+ const prev = lines[j].trim();
46
+ if (prev === "")
66
47
  continue;
67
- for (const token of tokens) {
68
- const bare = token.slice(1);
69
- if (codePattern.test(bare))
70
- codes.push(bare);
71
- }
48
+ if (!prev.startsWith("@"))
49
+ break;
50
+ tags.push(...prev.split(/\s+/).filter((t) => t.startsWith("@")).map((t) => t.slice(1)));
72
51
  }
73
- return codes;
52
+ return tags;
74
53
  }
75
54
  /**
76
55
  * Locate the default features/ directory — `./features` under the current
@@ -88,10 +67,9 @@ function featuresDir() {
88
67
  *
89
68
  * Absence stays a REFUSAL AT THE READER — what changed is that it is typed, so a caller can decide.
90
69
  * `balance` opts in to reporting on a target that has no corpus (most targets have no `features/`
91
- * at all, and `new-client` is a supported adoption state); `tags` keeps refusing, unedited, because
92
- * a `MissingFeatureCorpusError` is still an `Error` and still lands in its generic catch. Returning
93
- * `[]` here instead would make that decision for both, and measured it turns two named refusals
94
- * into `tags: OK …` at exit 0.
70
+ * at all, and `new-client` is a supported adoption state) by catching `MissingFeatureCorpusError` by
71
+ * name. Returning `[]` here instead would make that decision for every caller, and one that
72
+ * certifies a corpus would certify an absent one as empty.
95
73
  *
96
74
  * ONLY `ENOENT`. A `features` path that exists but is a FILE, or one the process may not read, is
97
75
  * a different fault and is not ours to reinterpret — it rethrows.
@@ -142,7 +120,6 @@ export function discoverFeatures(dir = featuresDir()) {
142
120
  slug: name.replace(/\.feature$/, ""),
143
121
  path,
144
122
  featureCode: extractFeatureCode(source),
145
- scenarioCodes: parseScenarioCodes(source),
146
123
  }));
147
124
  }
148
125
  //# sourceMappingURL=ingestScenarios.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ingestScenarios.js","sourceRoot":"","sources":["../../src/ingest/ingestScenarios.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ingestScenarios.js","sourceRoot":"","sources":["../../src/ingest/ingestScenarios.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-controller",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-alpha.30",
4
4
  "description": "Reconcile the behaviour your scenarios name against the evidence that actually ran — the deterministic `balance` CLI.",
5
5
  "keywords": [
6
6
  "reconciliation",
@@ -10,11 +10,6 @@
10
10
  "double-entry",
11
11
  "cli"
12
12
  ],
13
- "repository": {
14
- "type": "git",
15
- "url": "git+https://github.com/3f-consulting/spec-controller.git",
16
- "directory": "packages/spec-controller"
17
- },
18
13
  "license": "MIT",
19
14
  "type": "module",
20
15
  "engines": {
@@ -30,7 +25,7 @@
30
25
  "@cucumber/gherkin": "^41.0.0",
31
26
  "@cucumber/messages": "^34.0.1",
32
27
  "yaml": "^2.9.0",
33
- "@3f-consulting/spec-controller-core": "0.1.0-alpha.3"
28
+ "@3f-consulting/spec-controller-core": "0.1.0-alpha.30"
34
29
  },
35
30
  "publishConfig": {
36
31
  "access": "public"
@@ -1,34 +0,0 @@
1
- /**
2
- * The `tags` command — `spec-controller tags`, the `@SCN` scheme policing itself over a
3
- * target's Gherkin corpus.
4
- *
5
- * THIS EDGE OWNS ONLY THE READS. The rule is `corpusFaults`, in core, reached through the ROOT
6
- * barrel and nothing else: `./internal` is dropped by `publishConfig.exports` at pack time, and
7
- * this module travels in the tarball. That is not a style preference — an installed
8
- * `balance --help` once threw ERR_PACKAGE_PATH_NOT_EXPORTED behind thirteen green gates because a
9
- * published module reached that subpath (3F-3086). So what happens here is: discover the corpus,
10
- * read the manifest, build the mutation-record reader, hand over the bytes.
11
- *
12
- * ITS OWN COMMAND RATHER THAN A `balance` FLAG. `balance`'s exit code IS its verdict — 0
13
- * balanced, 1 out of balance, 3 pending-under-strict, 4 unsound, 5 nothing to reconcile — and a
14
- * corpus FAULT is not a reconciliation verdict. A flag would either overload one of those codes or
15
- * add a further meaning to a status a consumer's CI branches on. A second command costs one registry entry and gives
16
- * the fault its own ladder:
17
- *
18
- * 0 — the corpus obeys the scheme.
19
- * 1 — findings; the corpus was read and it breaks a rule.
20
- * 2 — the corpus (or the manifest) could not be READ at all. Not a verdict, because nothing was
21
- * concluded. Sibling to @SCN-CLI-014/015/016 on `balance`, and the same argument: an input
22
- * the tool cannot read is a usage error, never a verdict.
23
- *
24
- * Usage:
25
- * spec-controller tags [--features <dir>] [--package-json <path>]
26
- */
27
- /**
28
- * Police a target's corpus against the `@SCN` scheme's own rules.
29
- *
30
- * `process.exitCode` RATHER THAN `process.exit`, so whatever has been written is flushed intact
31
- * rather than truncated mid-write — the convention `balance`'s own gate settled on.
32
- */
33
- export declare function runTags(argv: string[]): void;
34
- //# sourceMappingURL=cli.d.ts.map