@encore-os/eos-spec 0.15.1 → 0.17.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.
Files changed (55) hide show
  1. package/dist/commands/close-out.js +62 -3
  2. package/dist/commands/close-out.js.map +1 -1
  3. package/dist/commands/doctor.d.ts +9 -0
  4. package/dist/commands/doctor.js +82 -0
  5. package/dist/commands/doctor.js.map +1 -0
  6. package/dist/commands/index-spec.js +22 -4
  7. package/dist/commands/index-spec.js.map +1 -1
  8. package/dist/commands/next.d.ts +6 -0
  9. package/dist/commands/next.js +11 -1
  10. package/dist/commands/next.js.map +1 -1
  11. package/dist/commands/reconcile.js +6 -2
  12. package/dist/commands/reconcile.js.map +1 -1
  13. package/dist/commands/scorecard.js +51 -0
  14. package/dist/commands/scorecard.js.map +1 -1
  15. package/dist/commands/signoff.js +12 -1
  16. package/dist/commands/signoff.js.map +1 -1
  17. package/dist/commands/verify-acs.d.ts +7 -0
  18. package/dist/commands/verify-acs.js +81 -13
  19. package/dist/commands/verify-acs.js.map +1 -1
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/lib/closeout-core.d.ts +29 -5
  23. package/dist/lib/closeout-core.js +57 -6
  24. package/dist/lib/closeout-core.js.map +1 -1
  25. package/dist/lib/config.d.ts +31 -2
  26. package/dist/lib/config.js.map +1 -1
  27. package/dist/lib/doctor-core.d.ts +63 -0
  28. package/dist/lib/doctor-core.js +98 -0
  29. package/dist/lib/doctor-core.js.map +1 -0
  30. package/dist/lib/frontmatter.js +31 -5
  31. package/dist/lib/frontmatter.js.map +1 -1
  32. package/dist/lib/reconcile-core.d.ts +1 -1
  33. package/dist/lib/reconcile-core.js +10 -2
  34. package/dist/lib/reconcile-core.js.map +1 -1
  35. package/dist/lib/rtm-scorecard-run.d.ts +38 -0
  36. package/dist/lib/rtm-scorecard-run.js +70 -0
  37. package/dist/lib/rtm-scorecard-run.js.map +1 -0
  38. package/dist/lib/rtm-scorecard.d.ts +151 -0
  39. package/dist/lib/rtm-scorecard.js +197 -0
  40. package/dist/lib/rtm-scorecard.js.map +1 -0
  41. package/dist/lib/spec-pure.d.ts +38 -0
  42. package/dist/lib/spec-pure.js +162 -17
  43. package/dist/lib/spec-pure.js.map +1 -1
  44. package/dist/lib/specs.d.ts +20 -0
  45. package/dist/lib/specs.js +37 -1
  46. package/dist/lib/specs.js.map +1 -1
  47. package/dist/lib/verify-core.d.ts +5 -0
  48. package/dist/lib/verify-core.js +5 -1
  49. package/dist/lib/verify-core.js.map +1 -1
  50. package/dist/lib/version.d.ts +1 -1
  51. package/dist/lib/version.js +1 -1
  52. package/dist/pure.d.ts +1 -0
  53. package/dist/pure.js +3 -0
  54. package/dist/pure.js.map +1 -1
  55. package/package.json +1 -1
@@ -0,0 +1,197 @@
1
+ /**
2
+ * The FR/SC Requirements-Traceability-Matrix (RTM) scorecard — a NON-VACUOUS coverage
3
+ * derivation folded into the engine from encoreos's repo-local `scripts/specs/verify-acs.mjs`
4
+ * (issue #79). It answers "how much of this spec's Definition of Done (its functional
5
+ * requirements FR-N and success criteria SC-N) is actually proven?" — where "proven" is
6
+ * defined so a coverage-free corpus cannot read 100% (the vacuity guard):
7
+ *
8
+ * - an **FR** is verified iff it has ≥1 covering AC and every covering AC is verified,
9
+ * OR it carries its own proof. An FR with zero covering ACs and no own-proof is NOT
10
+ * verified — that is the property the vacuity guard pins.
11
+ * - an **SC** is met iff it references ≥1 AC, at least one referenced AC is verified,
12
+ * AND every NFR it gates is green. An SC that references no AC is NOT met.
13
+ *
14
+ * PURE (fs-free): it reads a spec's body text + already-parsed `acceptance_criteria`, so it
15
+ * sits on the `/pure` graph and a browser bundle (Spec Studio) can compute the scorecard
16
+ * client-side. The corpus walk + file IO that feed it stay in the command layer. Trace edges
17
+ * (AC→FR via `fr_refs`, SC→AC/NFR via reference parens) mirror the engine's existing model.
18
+ */
19
+ /** Parse a single body line into `{ id, kind: 'US' | 'FR', status }`, or null. US and FR
20
+ * share one list format (`- **FR-1** [verified] …`); FR is first-class verifiable. */
21
+ export function matchBodyRequirement(line) {
22
+ const m = /^\s*-\s+\*{0,2}((US|FR)-\d+)\*{0,2}\s+\[([a-z_]+)\]/.exec(line);
23
+ return m ? { id: m[1], kind: m[2], status: m[3] } : null;
24
+ }
25
+ /** Parse one body line into a success criterion `{ id, status, ac_refs, nfr_refs }`, or null.
26
+ * SC lines carry trailing reference parens — `(US-3, AC-6)`, `(US-1, FR-1, NFR-1)` — from
27
+ * which the AC and NFR ids are scraped (dedup'd). */
28
+ export function matchSuccessCriterion(line) {
29
+ const m = /^\s*-\s+\*\*(SC-\d+(?:\.\d+)*)\b[^*\n]*\*\*\s*(?:\[([a-z_]+)\])?\s*(.*)$/.exec(line);
30
+ if (!m)
31
+ return null;
32
+ const tail = m[3] ?? '';
33
+ return {
34
+ id: m[1],
35
+ status: m[2] ?? 'planned',
36
+ ac_refs: [...new Set(tail.match(/\bAC-\d+(?:\.\d+)*/g) ?? [])],
37
+ nfr_refs: [...new Set(tail.match(/\bNFR(?:-[a-z]+)?-\d+(?:\.\d+)*/g) ?? [])],
38
+ };
39
+ }
40
+ /** Match a body NFR item (`- **NFR-perf-1** [status] …`) — the gates an SC verdict checks. */
41
+ export function matchNfrItem(line) {
42
+ const m = /^\s*-\s+\*{0,2}(NFR(?:-[a-z]+)?-\d+(?:\.\d+)*)\*{0,2}(?:\s+\[([a-z_]+)\])?/.exec(line);
43
+ return m ? { id: m[1], status: m[2] ?? 'planned' } : null;
44
+ }
45
+ /** Invert the AC→FR trace edges (each AC's `fr_refs`) into `FR id → [covering AC ids]`. */
46
+ export function coveringAcsByFr(acBlocks) {
47
+ const map = new Map();
48
+ for (const ac of acBlocks) {
49
+ for (const fr of ac.fr_refs ?? []) {
50
+ if (!map.has(fr))
51
+ map.set(fr, []);
52
+ map.get(fr).push(ac.id);
53
+ }
54
+ }
55
+ return map;
56
+ }
57
+ /** True if an AC is verified — status `verified` on disk OR proven in this run. */
58
+ export function isAcVerified(acId, acStatusById, provenAcIds) {
59
+ return acStatusById.get(acId) === 'verified' || provenAcIds.has(acId);
60
+ }
61
+ /**
62
+ * The NON-VACUOUS FR verdict. An FR counts verified iff (a) it has ≥1 covering AC and every
63
+ * covering AC is verified, or (b) the FR itself is proven. Zero covering ACs and no own-proof
64
+ * ⇒ NOT verified (the vacuity guard: a coverage-free corpus cannot read 100%).
65
+ */
66
+ export function frVerdict(opts) {
67
+ const { coveringAcs = [], acStatusById, provenAcIds, frProven = false, frVerifiedOnDisk = false } = opts;
68
+ if (frProven || frVerifiedOnDisk) {
69
+ return { verified: true, reason: 'own @verifies proof', coveringAcs };
70
+ }
71
+ if (coveringAcs.length === 0) {
72
+ return { verified: false, reason: 'no covering AC and no own proof', coveringAcs };
73
+ }
74
+ const unverified = coveringAcs.filter((ac) => !isAcVerified(ac, acStatusById, provenAcIds));
75
+ if (unverified.length === 0) {
76
+ return { verified: true, reason: `${coveringAcs.length} covering AC(s) all verified`, coveringAcs };
77
+ }
78
+ return { verified: false, reason: `covering AC(s) not yet verified: ${unverified.join(', ')}`, coveringAcs };
79
+ }
80
+ /**
81
+ * The NON-VACUOUS SC verdict. An SC counts met iff (a) it references ≥1 AC and at least one
82
+ * referenced AC is verified, and (b) every NFR it gates is green. A missing NFR (not in the
83
+ * spec) is ungreen — a dangling gate cannot be assumed satisfied. An SC referencing no AC is
84
+ * NOT met.
85
+ */
86
+ export function scVerdict(sc, opts) {
87
+ const { acStatusById, provenAcIds, nfrVerifiedById } = opts;
88
+ const acRefs = sc.ac_refs ?? [];
89
+ const nfrRefs = sc.nfr_refs ?? [];
90
+ if (acRefs.length === 0) {
91
+ return { met: false, reason: 'references no AC (cannot be proven)' };
92
+ }
93
+ const verifiedAcs = acRefs.filter((ac) => isAcVerified(ac, acStatusById, provenAcIds));
94
+ if (verifiedAcs.length === 0) {
95
+ return { met: false, reason: `no referenced AC is verified (${acRefs.join(', ')})` };
96
+ }
97
+ const ungreenNfrs = nfrRefs.filter((nfr) => nfrVerifiedById.get(nfr) !== true);
98
+ if (ungreenNfrs.length > 0) {
99
+ return { met: false, reason: `NFR gate(s) not green: ${ungreenNfrs.join(', ')}` };
100
+ }
101
+ return { met: true, reason: `${verifiedAcs.length}/${acRefs.length} referenced AC(s) verified, NFR gates green` };
102
+ }
103
+ /**
104
+ * Collect the scorecard items for one spec from its BODY text (FR/US/NFR/SC list lines) and
105
+ * its already-parsed `acceptance_criteria` frontmatter (AC id/status/fr_refs/nfr_refs). Pure —
106
+ * the caller supplies the body (frontmatter-stripped) and the raw AC array. US items are not
107
+ * scored (they trace via ACs) so they are not returned.
108
+ */
109
+ export function collectScorecardItems(bodyText, rawAcs) {
110
+ const acBlocks = Array.isArray(rawAcs)
111
+ ? rawAcs
112
+ .filter((e) => !!e && typeof e === 'object')
113
+ .map((e) => ({
114
+ id: typeof e.id === 'string' ? e.id : '',
115
+ status: typeof e.status === 'string' ? e.status : undefined,
116
+ fr_refs: coerceIdList(e.fr_refs),
117
+ nfr_refs: coerceIdList(e.nfr_refs),
118
+ }))
119
+ .filter((a) => a.id !== '')
120
+ : [];
121
+ const frItems = [];
122
+ const nfrItems = [];
123
+ const scItems = [];
124
+ for (const line of bodyText.split('\n')) {
125
+ const req = matchBodyRequirement(line);
126
+ if (req) {
127
+ if (req.kind === 'FR')
128
+ frItems.push({ id: req.id, status: req.status });
129
+ continue; // US items are traced via ACs, not scored directly
130
+ }
131
+ const nfr = matchNfrItem(line);
132
+ if (nfr) {
133
+ nfrItems.push(nfr);
134
+ continue;
135
+ }
136
+ const sc = matchSuccessCriterion(line);
137
+ if (sc)
138
+ scItems.push(sc);
139
+ }
140
+ return { frItems, acBlocks, scItems, nfrItems };
141
+ }
142
+ /** Coerce a frontmatter ref field (`fr_refs`/`nfr_refs`) to a string id list — accepts a YAML
143
+ * array of strings or a single string; anything else ⇒ []. */
144
+ function coerceIdList(v) {
145
+ if (typeof v === 'string')
146
+ return v.trim() === '' ? [] : [v.trim()];
147
+ if (Array.isArray(v))
148
+ return v.filter((x) => typeof x === 'string' && x.trim() !== '').map((x) => x.trim());
149
+ return [];
150
+ }
151
+ /** Derive the whole FR/SC scorecard for one spec — the single non-vacuous entry point. */
152
+ export function deriveScorecard(spec, proof = {}) {
153
+ const { frItems = [], acBlocks = [], scItems = [], nfrItems = [] } = spec;
154
+ const provenAcIds = proof.provenAcIds ?? new Set();
155
+ const provenFrIds = proof.provenFrIds ?? new Set();
156
+ const acStatusById = new Map(acBlocks.map((a) => [a.id, a.status]));
157
+ const nfrVerifiedById = new Map(nfrItems.map((n) => [n.id, n.status === 'verified']));
158
+ const coveringByFr = coveringAcsByFr(acBlocks);
159
+ const frRows = frItems.map((fr) => ({
160
+ id: fr.id,
161
+ ...frVerdict({
162
+ coveringAcs: coveringByFr.get(fr.id) ?? [],
163
+ acStatusById,
164
+ provenAcIds,
165
+ frProven: provenFrIds.has(fr.id),
166
+ frVerifiedOnDisk: fr.status === 'verified',
167
+ }),
168
+ }));
169
+ const scRows = scItems.map((sc) => ({ id: sc.id, ...scVerdict(sc, { acStatusById, provenAcIds, nfrVerifiedById }) }));
170
+ return {
171
+ fr: { total: frRows.length, verified: frRows.filter((r) => r.verified).length, rows: frRows },
172
+ sc: { total: scRows.length, met: scRows.filter((r) => r.met).length, rows: scRows },
173
+ };
174
+ }
175
+ /** Advisory coverage ratchet floors (warn-level, NEVER fatal): the fraction of FRs that clear
176
+ * the non-vacuous bar and of SCs that are met. Conservative defaults; a consumer overrides via
177
+ * `rtm.ratchets` in `specs/config.yaml`. Kept advisory so a young/low-coverage corpus stays
178
+ * green while a real regression below the floor still surfaces. */
179
+ export const COVERAGE_RATCHET_FLOORS = { frVerifiedPct: 0, scMetPct: 0 };
180
+ /** Resolve the opt-in RTM scorecard config; defaults to disabled + the built-in floors, so a
181
+ * repo without an `rtm:` block (or with `enabled` falsy) gets no scorecard. */
182
+ export function resolveRtm(config) {
183
+ const rtm = config?.rtm;
184
+ return { enabled: rtm?.enabled === true, ratchets: rtm?.ratchets ?? COVERAGE_RATCHET_FLOORS };
185
+ }
186
+ /** Compare measured coverage (`{ frVerifiedPct, scMetPct, … }`, each 0–1) against the advisory
187
+ * floors. Returns the breaches (empty ⇒ none). Pure — the caller decides whether to print/warn. */
188
+ export function checkCoverageRatchets(measured, floors = COVERAGE_RATCHET_FLOORS) {
189
+ const breaches = [];
190
+ for (const [metric, floor] of Object.entries(floors)) {
191
+ const value = measured[metric];
192
+ if (typeof value === 'number' && value < floor)
193
+ breaches.push({ metric, floor, value });
194
+ }
195
+ return breaches;
196
+ }
197
+ //# sourceMappingURL=rtm-scorecard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rtm-scorecard.js","sourceRoot":"","sources":["../../src/lib/rtm-scorecard.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,22 @@ export declare const SPEC_ID_GRAMMAR: {
8
8
  readonly pattern: "<core>(-[A-Z]{1,4})?-<n>(.<n>)?(-(EN-<n>|E<n>|P<n>(.<n>)?(-EN-<n>)?))?";
9
9
  readonly caseInsensitive: true;
10
10
  };
11
+ /**
12
+ * Remove regions that legitimately QUOTE compliance-sounding words as examples,
13
+ * so the keyword heuristic in {@link detectSensitive} reads only load-bearing
14
+ * prose. Strips, per line: fenced code blocks (``` … ``` / ~~~ … ~~~), a
15
+ * `## Examples` heading section — an EXACT `Examples`/`Example` heading title only
16
+ * (so `## Examples of Regulated Data` is a normal heading, its body left as
17
+ * scannable prose) — down to the next same-or-higher heading, and Gherkin step
18
+ * lines. `Scenario:` / `Example(s):` / `Given` / `When` / `Then` openers strip
19
+ * unconditionally; `And` / `But` strip ONLY when the immediately preceding
20
+ * non-blank line (no intervening blank line) was itself a Gherkin step/opener —
21
+ * a standalone `And`/`But` prose line (e.g. a paragraph continuation) is left in
22
+ * place. Pure string transform — no fs, stays on the `/pure` graph. Callers apply
23
+ * it to the keyword-match INPUT only; opt-in/opt-out markers are still read from
24
+ * the raw text.
25
+ */
26
+ export declare function stripExampleRegions(text: string): string;
11
27
  /** True when a generated test source has ≥1 ACTIVE `test(` block (not `test.fixme`/
12
28
  * `test.describe`) and is not an AC-less placeholder. Pure (operates on source text). */
13
29
  export declare function hasActiveGeneratedTestSource(source: string): boolean;
@@ -88,6 +104,28 @@ export declare function declaredSpecId(declaredId: string | null | undefined, co
88
104
  * Returns the resolved spec id, or null when the file is not a primary spec.
89
105
  */
90
106
  export declare function resolveSpecFileId(core: string, filename: string, declaredId?: string | null): string | null;
107
+ /**
108
+ * True when an on-disk filename PARSES to a well-formed primary-spec id yet the corpus
109
+ * loader would silently DROP it ({@link resolveSpecFileId} → null) for a reason a
110
+ * frontmatter `id:` could rescue — a reserved lifecycle word (REVIEW / ANALYSIS /
111
+ * CLARIFICATION …) landed inside a real feature slug and no `id:` disambiguates it. This
112
+ * is the silent-authoring-drop class issue #76 surfaces: the file reads as spec X to a
113
+ * human but never reaches spec-index.json / navigator / reconcile / recall.
114
+ *
115
+ * Deliberately EXCLUDES bona-fide sibling artifacts that legitimately carry a parent id
116
+ * and are SUPPOSED to be non-primary, so the surfacer stays quiet on a healthy corpus:
117
+ * - dedicated CONTEXT/CLARIFICATION/ENHANCEMENTS/EXPANSION files ({@link isDedicatedArtifactFile}),
118
+ * - `{ID}-PHASE…` lifecycle files ({@link isPhaseLifecycleFile}),
119
+ * - descriptor-suffix artifacts (`-TASKS`/`-PLAN`/`-RESEARCH`/… — {@link DERIVATIVE_DESCRIPTOR_RE}),
120
+ * - and a LONE terminal descriptor (`{ID}-REVIEW`, `{ID}-ANALYSIS`): only files with real
121
+ * slug content AFTER the id (≥2 slug words) are flagged, i.e. the reserved word is
122
+ * embedded in a feature name (`{ID}-compliance-review-workflow`), not the whole slug.
123
+ *
124
+ * The residue is exactly the case the mint-time guard (spec-id-guard → {@link resolveSpecFileId})
125
+ * blocks on `create`/`promote`, made visible for files already on disk. Pure (fs-free); the
126
+ * fs walk that feeds it lives in specs.ts `findSkippedSpecFiles`.
127
+ */
128
+ export declare function isSilentlySkippedSpecFile(core: string, filename: string, declaredId?: string | null): boolean;
91
129
  /** The skip rules `collectSpecsFromDirectory` applies to one filename. */
92
130
  export declare function isPrimarySpecFile(filename: string): boolean;
93
131
  /**
@@ -27,27 +27,125 @@ export const SPEC_ID_GRAMMAR = {
27
27
  };
28
28
  /** The marker the generator emits for an AC-less spec. */
29
29
  const GENERATED_NO_AC_MARKER = 'no acceptance_criteria — run encore-spec-ac-draft first';
30
- /** Keywords that mark a spec as carrying sensitive data — PHI/PII/billing/
31
- * credentialing. The `/spec-pipeline` step-3b compliance gate fires on these
32
- * mentions in ANY core (not just the regulated `cl/pm/hr/rh/gr` set), so the
33
- * engine must detect them to gate `hasComplianceForSensitive` at parity with the
34
- * command prose. Matched at word boundaries, case-insensitively, over frontmatter
35
- * + body. Kept to high-signal regime tokens so an incidental mention never trips
36
- * it; a spec can also opt in explicitly via a `<!-- sensitive -->`/`<!-- sensitive: -->`
37
- * marker or a `sensitive`/`phi`/`pii`/`billing`/`credentialing` tag. */
38
- const SENSITIVE_KEYWORD_RE = /\b(PHI|PII|protected health information|personally identifiable information|billing|claims?|reimbursement|remittance advice|credentialing|payer enrollment|HIPAA|42 CFR Part 2)\b/i;
39
- /** True when the spec text (frontmatter + body) signals sensitive data: a
40
- * high-signal regime keyword, an explicit `<!-- sensitive -->` marker, or a
41
- * `sensitive`/`phi`/`pii`/`billing`/`credentialing` tag. Pure (operates on the raw
42
- * spec text + the parsed `tags` array). Independent of `isRegulated` a non-
43
- * regulated FA billing or PF credentialing spec is `isSensitive` without being a
44
- * regulated core, which is exactly the parity gap Finding A item 2 names. */
30
+ /**
31
+ * Remove regions that legitimately QUOTE compliance-sounding words as examples,
32
+ * so the keyword heuristic in {@link detectSensitive} reads only load-bearing
33
+ * prose. Strips, per line: fenced code blocks (``` ``` / ~~~ … ~~~), a
34
+ * `## Examples` heading section an EXACT `Examples`/`Example` heading title only
35
+ * (so `## Examples of Regulated Data` is a normal heading, its body left as
36
+ * scannable prose) down to the next same-or-higher heading, and Gherkin step
37
+ * lines. `Scenario:` / `Example(s):` / `Given` / `When` / `Then` openers strip
38
+ * unconditionally; `And` / `But` strip ONLY when the immediately preceding
39
+ * non-blank line (no intervening blank line) was itself a Gherkin step/opener —
40
+ * a standalone `And`/`But` prose line (e.g. a paragraph continuation) is left in
41
+ * place. Pure string transform — no fs, stays on the `/pure` graph. Callers apply
42
+ * it to the keyword-match INPUT only; opt-in/opt-out markers are still read from
43
+ * the raw text.
44
+ */
45
+ export function stripExampleRegions(text) {
46
+ const out = [];
47
+ let inFence = false;
48
+ let exampleHeadingDepth = 0; // 0 ⇒ not inside a `## Examples` section
49
+ let inGherkinStep = false; // true right after a line that opened/continued a Gherkin step sequence
50
+ for (const line of text.split('\n')) {
51
+ const trimmed = line.trim();
52
+ if (trimmed.startsWith('```') || trimmed.startsWith('~~~')) {
53
+ inFence = !inFence;
54
+ inGherkinStep = false;
55
+ continue;
56
+ }
57
+ if (inFence)
58
+ continue;
59
+ const heading = /^(#{1,6})\s+(.*)$/.exec(trimmed);
60
+ if (heading) {
61
+ inGherkinStep = false;
62
+ const depth = heading[1].length;
63
+ if (exampleHeadingDepth && depth <= exampleHeadingDepth)
64
+ exampleHeadingDepth = 0;
65
+ if (!exampleHeadingDepth && /^\s*examples?\s*$/i.test(heading[2])) {
66
+ exampleHeadingDepth = depth;
67
+ continue;
68
+ }
69
+ if (exampleHeadingDepth)
70
+ continue;
71
+ out.push(line);
72
+ continue;
73
+ }
74
+ if (exampleHeadingDepth) {
75
+ inGherkinStep = false;
76
+ continue;
77
+ }
78
+ if (/^(Scenario|Examples?|Given|When|Then)\b/i.test(trimmed)) {
79
+ inGherkinStep = true;
80
+ continue;
81
+ }
82
+ if (inGherkinStep && /^(And|But)\b/i.test(trimmed))
83
+ continue;
84
+ inGherkinStep = false;
85
+ out.push(line);
86
+ }
87
+ return out.join('\n');
88
+ }
89
+ /** Compliance keyword matcher, split by signal strength. Matched at word boundaries,
90
+ * case-insensitively. HIGH-signal tokens are checked against the RAW text (a regime
91
+ * token is meaningful wherever it sits, even inside an example); LOW-signal tokens are
92
+ * checked against the EXAMPLE-STRIPPED text (see {@link stripExampleRegions}) so a
93
+ * billing/claims word quoted inside a Gherkin or fenced example on a non-regulated spec
94
+ * no longer trips the compliance gate. See {@link detectSensitive} for exactly which
95
+ * text each regex runs over.
96
+ *
97
+ * HIGH-signal tokens name a regulated regime directly, so a SINGLE hit flags the
98
+ * spec. The three low-signal tokens are common non-regulated business words, so
99
+ * they flag only on ≥2 DISTINCT token TYPES (see {@link countDistinctLowSignalTypes});
100
+ * two hits of the SAME token do not qualify. A spec can still opt in/out explicitly
101
+ * (see {@link detectSensitive}). */
102
+ const HIGH_SIGNAL_RE = /\b(PHI|PII|protected health information|personally identifiable information|remittance advice|credentialing|payer enrollment|HIPAA|42 CFR Part 2)\b/i;
103
+ /** Low-signal compliance tokens — global so {@link countDistinctLowSignalTypes} can
104
+ * enumerate every occurrence. */
105
+ const LOW_SIGNAL_RE = /\b(billing|claims?|reimbursement)\b/gi;
106
+ /** Count of DISTINCT low-signal token types in `text` (`claim`/`claims` collapse to
107
+ * one type). ≥2 is the low-signal fire threshold. */
108
+ function countDistinctLowSignalTypes(text) {
109
+ const types = new Set();
110
+ for (const match of text.matchAll(LOW_SIGNAL_RE)) {
111
+ const token = match[1].toLowerCase();
112
+ types.add(token.startsWith('claim') ? 'claims' : token);
113
+ }
114
+ return types.size;
115
+ }
116
+ /** True when the spec text (frontmatter + body) signals sensitive data. Precedence:
117
+ * an explicit opt-IN wins TRUE (an anchored `<!-- sensitive -->`/`<!-- sensitive: -->`
118
+ * marker or a `sensitive`/`phi`/`pii`/`billing`/`credentialing` tag), then an explicit
119
+ * opt-OUT wins FALSE (an anchored `<!-- not-sensitive -->` marker or a `not-sensitive`
120
+ * tag), otherwise the keyword heuristic: a HIGH-signal hit against the RAW text (so a
121
+ * regime token cannot be hidden by sitting on a Given/When/Then/Scenario/Examples opener
122
+ * line that {@link stripExampleRegions} would otherwise strip — a hard-gate fail-open),
123
+ * OR ≥2 distinct LOW-signal token types against the EXAMPLE-STRIPPED text (so ordinary
124
+ * billing/claims prose quoted in a Gherkin/fenced example does not false-positive).
125
+ * Marker/tag detection runs on the RAW text (a deliberate marker anywhere is intentional)
126
+ * BEFORE any stripping; the markers stay fully anchored so `not-sensitive` can never
127
+ * substring-match `sensitive`. Pure; independent of `isRegulated`. */
45
128
  function detectSensitive(text, tags) {
46
129
  if (/<!--\s*sensitive\s*(?::[^>]*)?-->/i.test(text))
47
130
  return true;
48
131
  if (tags.some((t) => /^(sensitive|phi|pii|billing|credentialing)$/i.test(String(t).trim())))
49
132
  return true;
50
- return SENSITIVE_KEYWORD_RE.test(text);
133
+ if (/<!--\s*not-sensitive\s*(?::[^>]*)?-->/i.test(text))
134
+ return false;
135
+ if (tags.some((t) => /^not-sensitive$/i.test(String(t).trim())))
136
+ return false;
137
+ // HIGH-signal tokens are matched on the RAW text: a regime token (PHI, HIPAA, 42 CFR
138
+ // Part 2, …) is meaningful wherever it appears, INCLUDING inside a Gherkin/example
139
+ // region — a spec that says "Given the PHI we store is encrypted" is still describing
140
+ // PHI, and stripExampleRegions unconditionally strips bare Given/When/Then/Scenario/
141
+ // Examples opener lines, which would otherwise remove the token before it is ever
142
+ // matched (a hard-gate fail-open). Only the LOW-signal English words (billing/claims/
143
+ // reimbursement — common outside any regulated context) are the example-block
144
+ // false-positive source stripExampleRegions exists to suppress (#64c), so that scan
145
+ // alone runs on the stripped text.
146
+ if (HIGH_SIGNAL_RE.test(text))
147
+ return true;
148
+ return countDistinctLowSignalTypes(stripExampleRegions(text)) >= 2;
51
149
  }
52
150
  /** True when a generated test source has ≥1 ACTIVE `test(` block (not `test.fixme`/
53
151
  * `test.describe`) and is not an AC-less placeholder. Pure (operates on source text). */
@@ -192,13 +290,19 @@ export function isLifecycleArtifact(filename) {
192
290
  return new RegExp(`${left}${esc}${right}`).test(upper);
193
291
  });
194
292
  }
293
+ /** Uppercase descriptor suffixes that mark a file as a derivative artifact OF a primary
294
+ * spec (a `{ID}-TASKS.md`, `{ID}-PLAN.md`, …) rather than a spec itself. Factored out so
295
+ * {@link isDerivativeArtifact} and {@link isSilentlySkippedSpecFile} agree byte-for-byte
296
+ * on which reserved suffixes are EXPECTED non-primaries. Matched against the UPPER-CASED
297
+ * basename, so no `i` flag is needed. */
298
+ const DERIVATIVE_DESCRIPTOR_RE = /-(TASKS|PLAN|CONTEXT|RESEARCH|NAVIGATION|RECOMMENDATION|RECOMMENDATIONS|DECISION|DECISIONS|SUMMARY|IMPLEMENTATION|WORKSTREAM|AUDIT|REPORT|SIGNOFF|READINESS|EXPANSION|WORKSHEET|PATTERNS)/;
195
299
  /** True when a filename + its containing dir indicate a derivative artifact, not a primary spec. */
196
300
  export function isDerivativeArtifact(dirName, filename) {
197
301
  if (NON_PRIMARY_DIRS.has(dirName))
198
302
  return true;
199
303
  if (isLifecycleArtifact(filename) || isPhaseLifecycleFile(filename))
200
304
  return true;
201
- return /-(TASKS|PLAN|CONTEXT|RESEARCH|NAVIGATION|RECOMMENDATION|RECOMMENDATIONS|DECISION|DECISIONS|SUMMARY|IMPLEMENTATION|WORKSTREAM|AUDIT|REPORT|SIGNOFF|READINESS|EXPANSION|WORKSHEET|PATTERNS)/i.test(specBasename(filename).toUpperCase());
305
+ return DERIVATIVE_DESCRIPTOR_RE.test(specBasename(filename).toUpperCase());
202
306
  }
203
307
  /** Uppercase derivative-artifact descriptors that, by repo convention, live ALONGSIDE
204
308
  * primary specs in `specs/{core}/specs|ux/` AND deliberately carry the PARENT spec's
@@ -279,6 +383,47 @@ export function resolveSpecFileId(core, filename, declaredId) {
279
383
  return id;
280
384
  return null;
281
385
  }
386
+ /**
387
+ * True when an on-disk filename PARSES to a well-formed primary-spec id yet the corpus
388
+ * loader would silently DROP it ({@link resolveSpecFileId} → null) for a reason a
389
+ * frontmatter `id:` could rescue — a reserved lifecycle word (REVIEW / ANALYSIS /
390
+ * CLARIFICATION …) landed inside a real feature slug and no `id:` disambiguates it. This
391
+ * is the silent-authoring-drop class issue #76 surfaces: the file reads as spec X to a
392
+ * human but never reaches spec-index.json / navigator / reconcile / recall.
393
+ *
394
+ * Deliberately EXCLUDES bona-fide sibling artifacts that legitimately carry a parent id
395
+ * and are SUPPOSED to be non-primary, so the surfacer stays quiet on a healthy corpus:
396
+ * - dedicated CONTEXT/CLARIFICATION/ENHANCEMENTS/EXPANSION files ({@link isDedicatedArtifactFile}),
397
+ * - `{ID}-PHASE…` lifecycle files ({@link isPhaseLifecycleFile}),
398
+ * - descriptor-suffix artifacts (`-TASKS`/`-PLAN`/`-RESEARCH`/… — {@link DERIVATIVE_DESCRIPTOR_RE}),
399
+ * - and a LONE terminal descriptor (`{ID}-REVIEW`, `{ID}-ANALYSIS`): only files with real
400
+ * slug content AFTER the id (≥2 slug words) are flagged, i.e. the reserved word is
401
+ * embedded in a feature name (`{ID}-compliance-review-workflow`), not the whole slug.
402
+ *
403
+ * The residue is exactly the case the mint-time guard (spec-id-guard → {@link resolveSpecFileId})
404
+ * blocks on `create`/`promote`, made visible for files already on disk. Pure (fs-free); the
405
+ * fs walk that feeds it lives in specs.ts `findSkippedSpecFiles`.
406
+ */
407
+ export function isSilentlySkippedSpecFile(core, filename, declaredId) {
408
+ if (!filename.endsWith('.md'))
409
+ return false;
410
+ const base = specBasename(filename);
411
+ const id = extractSpecId(filename, core);
412
+ if (!id)
413
+ return false; // basename doesn't parse to a spec id → not an intended primary spec
414
+ if (resolveSpecFileId(core, filename, declaredId) !== null)
415
+ return false; // loader already indexes it → fine
416
+ if (isDedicatedArtifactFile(filename))
417
+ return false; // CONTEXT/CLARIFICATION/ENHANCEMENTS/EXPANSION — legit sibling
418
+ if (isPhaseLifecycleFile(filename))
419
+ return false; // {ID}-PHASE… — legit lifecycle artifact
420
+ if (DERIVATIVE_DESCRIPTOR_RE.test(base.toUpperCase()))
421
+ return false; // -TASKS/-PLAN/-RESEARCH/… — legit descriptor
422
+ // Only flag when real slug content follows the id — a lone terminal descriptor
423
+ // (`{ID}-REVIEW`, `{ID}-ANALYSIS`) is an artifact, not a dropped spec.
424
+ const remainder = base.slice(id.length).replace(/^-+/, '');
425
+ return remainder.includes('-');
426
+ }
282
427
  /** The skip rules `collectSpecsFromDirectory` applies to one filename. */
283
428
  export function isPrimarySpecFile(filename) {
284
429
  if (!filename.endsWith('.md'))
@@ -1 +1 @@
1
- {"version":3,"file":"spec-pure.js","sourceRoot":"","sources":["../../src/lib/spec-pure.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spec-pure.js","sourceRoot":"","sources":["../../src/lib/spec-pure.ts"],"names":[],"mappings":"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"}
@@ -138,6 +138,26 @@ export declare function deriveSignals(core: string, text: string, researchFileEx
138
138
  export declare function listSpecs(repoRoot: string, coreFilter?: string, options?: {
139
139
  includeArchived?: boolean;
140
140
  }): SpecFile[];
141
+ /** One on-disk spec file the corpus loader silently drops (issue #76): its filename
142
+ * reads as a primary spec id, but a reserved lifecycle word in the slug (with no
143
+ * frontmatter `id:` to disambiguate) makes {@link resolveSpecFileId} classify it
144
+ * derivative, so it never reaches spec-index.json / navigator / reconcile / recall. */
145
+ export interface SkippedSpecFile {
146
+ path: string;
147
+ core: string;
148
+ filename: string;
149
+ /** The spec id the filename parses to — what an author clearly intended. */
150
+ wouldBeId: string;
151
+ }
152
+ /**
153
+ * Scan `specs/{core}/specs|ux/` for `.md` files the loader silently drops despite a
154
+ * filename that parses to a valid spec id (issue #76). Reads each candidate's
155
+ * frontmatter `id:` so a file the loader WOULD rescue (id agrees) is correctly excluded.
156
+ * Active dirs only — a dropped `archive/` artifact is not a silent-authoring bug.
157
+ * Deterministically ordered by path. The classification predicate is the fs-free
158
+ * {@link isSilentlySkippedSpecFile}; this adds only the directory walk + frontmatter read.
159
+ */
160
+ export declare function findSkippedSpecFiles(repoRoot: string, coreFilter?: string): SkippedSpecFile[];
141
161
  /**
142
162
  * Resolve the on-disk path of a spec artifact (plan/tasks) for `id`, or null when none exists.
143
163
  * Folder-agnostic: the artifact dir is `specs/{core}/{folder}/`, independent of whether the
package/dist/lib/specs.js CHANGED
@@ -3,7 +3,7 @@ import { join } from 'node:path';
3
3
  import { parseComplianceSignoff } from './compliance-signoff.js';
4
4
  import { readFrontmatter, stripFrontmatter } from './frontmatter.js';
5
5
  import { coresList, specsRoot } from './repo.js';
6
- import { deriveSignalsFromText, extractSpecId, findDuplicateSpecIds, isDedicatedArtifactFile, isDerivativeArtifact, resolveSpecFileId, } from './spec-pure.js';
6
+ import { deriveSignalsFromText, extractSpecId, findDuplicateSpecIds, isDedicatedArtifactFile, isDerivativeArtifact, isSilentlySkippedSpecFile, resolveSpecFileId, } from './spec-pure.js';
7
7
  import { getWizardCreateSpec } from './wizard-specs.js';
8
8
  // Path classification lives in the fs-free spec-pure.ts (single source); re-export
9
9
  // the public helpers so existing importers of specs.ts (reconcile, placement) keep
@@ -75,6 +75,42 @@ export function listSpecs(repoRoot, coreFilter, options) {
75
75
  }
76
76
  return out;
77
77
  }
78
+ /**
79
+ * Scan `specs/{core}/specs|ux/` for `.md` files the loader silently drops despite a
80
+ * filename that parses to a valid spec id (issue #76). Reads each candidate's
81
+ * frontmatter `id:` so a file the loader WOULD rescue (id agrees) is correctly excluded.
82
+ * Active dirs only — a dropped `archive/` artifact is not a silent-authoring bug.
83
+ * Deterministically ordered by path. The classification predicate is the fs-free
84
+ * {@link isSilentlySkippedSpecFile}; this adds only the directory walk + frontmatter read.
85
+ */
86
+ export function findSkippedSpecFiles(repoRoot, coreFilter) {
87
+ const out = [];
88
+ const cores = coreFilter ? [coreFilter.toLowerCase()] : coresList(repoRoot);
89
+ for (const core of cores) {
90
+ for (const sub of ['specs', 'ux']) {
91
+ const dir = join(specsRoot(repoRoot), core, sub);
92
+ if (!existsSync(dir))
93
+ continue;
94
+ for (const file of readdirSync(dir)) {
95
+ if (!file.endsWith('.md'))
96
+ continue;
97
+ let fmId;
98
+ try {
99
+ const fm = readFrontmatter(readFileSync(join(dir, file), 'utf8'));
100
+ fmId = typeof fm.id === 'string' ? fm.id : undefined;
101
+ }
102
+ catch {
103
+ // unreadable — classify on the filename alone (declaredId undefined)
104
+ }
105
+ if (!isSilentlySkippedSpecFile(core, file, fmId))
106
+ continue;
107
+ out.push({ path: join(dir, file), core, filename: file, wouldBeId: extractSpecId(file, core) });
108
+ }
109
+ }
110
+ }
111
+ out.sort((a, b) => a.path.localeCompare(b.path));
112
+ return out;
113
+ }
78
114
  function collectSpecsFromDirectory(repoRoot, core, dir, archived, out) {
79
115
  for (const file of readdirSync(dir)) {
80
116
  if (!file.endsWith('.md'))