@encore-os/eos-spec 0.3.1

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 (256) hide show
  1. package/LICENSE +17 -0
  2. package/bin/check-dist-fresh.js +51 -0
  3. package/bin/eos-spec.js +7 -0
  4. package/dist/commands/ac-backfill.d.ts +31 -0
  5. package/dist/commands/ac-backfill.js +198 -0
  6. package/dist/commands/ac-backfill.js.map +1 -0
  7. package/dist/commands/archive.d.ts +2 -0
  8. package/dist/commands/archive.js +101 -0
  9. package/dist/commands/archive.js.map +1 -0
  10. package/dist/commands/audit.d.ts +30 -0
  11. package/dist/commands/audit.js +107 -0
  12. package/dist/commands/audit.js.map +1 -0
  13. package/dist/commands/backfill.d.ts +41 -0
  14. package/dist/commands/backfill.js +239 -0
  15. package/dist/commands/backfill.js.map +1 -0
  16. package/dist/commands/checkArtifacts.d.ts +2 -0
  17. package/dist/commands/checkArtifacts.js +48 -0
  18. package/dist/commands/checkArtifacts.js.map +1 -0
  19. package/dist/commands/checkPlacement.d.ts +2 -0
  20. package/dist/commands/checkPlacement.js +31 -0
  21. package/dist/commands/checkPlacement.js.map +1 -0
  22. package/dist/commands/claim.d.ts +4 -0
  23. package/dist/commands/claim.js +46 -0
  24. package/dist/commands/claim.js.map +1 -0
  25. package/dist/commands/complete.d.ts +2 -0
  26. package/dist/commands/complete.js +138 -0
  27. package/dist/commands/complete.js.map +1 -0
  28. package/dist/commands/conformance.d.ts +23 -0
  29. package/dist/commands/conformance.js +124 -0
  30. package/dist/commands/conformance.js.map +1 -0
  31. package/dist/commands/create.d.ts +6 -0
  32. package/dist/commands/create.js +277 -0
  33. package/dist/commands/create.js.map +1 -0
  34. package/dist/commands/deferred.d.ts +2 -0
  35. package/dist/commands/deferred.js +85 -0
  36. package/dist/commands/deferred.js.map +1 -0
  37. package/dist/commands/delta.d.ts +17 -0
  38. package/dist/commands/delta.js +292 -0
  39. package/dist/commands/delta.js.map +1 -0
  40. package/dist/commands/freshness.d.ts +13 -0
  41. package/dist/commands/freshness.js +72 -0
  42. package/dist/commands/freshness.js.map +1 -0
  43. package/dist/commands/funnel.d.ts +2 -0
  44. package/dist/commands/funnel.js +61 -0
  45. package/dist/commands/funnel.js.map +1 -0
  46. package/dist/commands/gen-tests.d.ts +2 -0
  47. package/dist/commands/gen-tests.js +53 -0
  48. package/dist/commands/gen-tests.js.map +1 -0
  49. package/dist/commands/init.d.ts +2 -0
  50. package/dist/commands/init.js +64 -0
  51. package/dist/commands/init.js.map +1 -0
  52. package/dist/commands/list.d.ts +9 -0
  53. package/dist/commands/list.js +61 -0
  54. package/dist/commands/list.js.map +1 -0
  55. package/dist/commands/metrics.d.ts +2 -0
  56. package/dist/commands/metrics.js +66 -0
  57. package/dist/commands/metrics.js.map +1 -0
  58. package/dist/commands/next.d.ts +48 -0
  59. package/dist/commands/next.js +344 -0
  60. package/dist/commands/next.js.map +1 -0
  61. package/dist/commands/precedent.d.ts +66 -0
  62. package/dist/commands/precedent.js +343 -0
  63. package/dist/commands/precedent.js.map +1 -0
  64. package/dist/commands/ready.d.ts +28 -0
  65. package/dist/commands/ready.js +147 -0
  66. package/dist/commands/ready.js.map +1 -0
  67. package/dist/commands/reconcile.d.ts +44 -0
  68. package/dist/commands/reconcile.js +187 -0
  69. package/dist/commands/reconcile.js.map +1 -0
  70. package/dist/commands/rollup.d.ts +2 -0
  71. package/dist/commands/rollup.js +43 -0
  72. package/dist/commands/rollup.js.map +1 -0
  73. package/dist/commands/scorecard.d.ts +11 -0
  74. package/dist/commands/scorecard.js +55 -0
  75. package/dist/commands/scorecard.js.map +1 -0
  76. package/dist/commands/signoff.d.ts +2 -0
  77. package/dist/commands/signoff.js +114 -0
  78. package/dist/commands/signoff.js.map +1 -0
  79. package/dist/commands/status.d.ts +2 -0
  80. package/dist/commands/status.js +100 -0
  81. package/dist/commands/status.js.map +1 -0
  82. package/dist/commands/tasks.d.ts +13 -0
  83. package/dist/commands/tasks.js +81 -0
  84. package/dist/commands/tasks.js.map +1 -0
  85. package/dist/commands/validate.d.ts +16 -0
  86. package/dist/commands/validate.js +89 -0
  87. package/dist/commands/validate.js.map +1 -0
  88. package/dist/commands/verify-acs.d.ts +15 -0
  89. package/dist/commands/verify-acs.js +196 -0
  90. package/dist/commands/verify-acs.js.map +1 -0
  91. package/dist/commands/verifyRollout.d.ts +2 -0
  92. package/dist/commands/verifyRollout.js +125 -0
  93. package/dist/commands/verifyRollout.js.map +1 -0
  94. package/dist/index.d.ts +21 -0
  95. package/dist/index.js +138 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/lib/ac-draft-prompt.d.ts +18 -0
  98. package/dist/lib/ac-draft-prompt.js +38 -0
  99. package/dist/lib/ac-draft-prompt.js.map +1 -0
  100. package/dist/lib/ac-response-parser.d.ts +18 -0
  101. package/dist/lib/ac-response-parser.js +57 -0
  102. package/dist/lib/ac-response-parser.js.map +1 -0
  103. package/dist/lib/ai-call.d.ts +13 -0
  104. package/dist/lib/ai-call.js +17 -0
  105. package/dist/lib/ai-call.js.map +1 -0
  106. package/dist/lib/archive.d.ts +21 -0
  107. package/dist/lib/archive.js +136 -0
  108. package/dist/lib/archive.js.map +1 -0
  109. package/dist/lib/artifact-integrity.d.ts +5 -0
  110. package/dist/lib/artifact-integrity.js +65 -0
  111. package/dist/lib/artifact-integrity.js.map +1 -0
  112. package/dist/lib/backfill-queue.d.ts +11 -0
  113. package/dist/lib/backfill-queue.js +14 -0
  114. package/dist/lib/backfill-queue.js.map +1 -0
  115. package/dist/lib/blockers.d.ts +66 -0
  116. package/dist/lib/blockers.js +103 -0
  117. package/dist/lib/blockers.js.map +1 -0
  118. package/dist/lib/claims.d.ts +39 -0
  119. package/dist/lib/claims.js +133 -0
  120. package/dist/lib/claims.js.map +1 -0
  121. package/dist/lib/classify-debt.d.ts +17 -0
  122. package/dist/lib/classify-debt.js +13 -0
  123. package/dist/lib/classify-debt.js.map +1 -0
  124. package/dist/lib/code-evidence.d.ts +35 -0
  125. package/dist/lib/code-evidence.js +171 -0
  126. package/dist/lib/code-evidence.js.map +1 -0
  127. package/dist/lib/coherence.d.ts +14 -0
  128. package/dist/lib/coherence.js +188 -0
  129. package/dist/lib/coherence.js.map +1 -0
  130. package/dist/lib/completion-gate.d.ts +17 -0
  131. package/dist/lib/completion-gate.js +28 -0
  132. package/dist/lib/completion-gate.js.map +1 -0
  133. package/dist/lib/compliance-signoff.d.ts +39 -0
  134. package/dist/lib/compliance-signoff.js +81 -0
  135. package/dist/lib/compliance-signoff.js.map +1 -0
  136. package/dist/lib/config.d.ts +63 -0
  137. package/dist/lib/config.js +40 -0
  138. package/dist/lib/config.js.map +1 -0
  139. package/dist/lib/cost.d.ts +12 -0
  140. package/dist/lib/cost.js +12 -0
  141. package/dist/lib/cost.js.map +1 -0
  142. package/dist/lib/debt-dashboard.d.ts +25 -0
  143. package/dist/lib/debt-dashboard.js +36 -0
  144. package/dist/lib/debt-dashboard.js.map +1 -0
  145. package/dist/lib/embedding-store.d.ts +103 -0
  146. package/dist/lib/embedding-store.js +133 -0
  147. package/dist/lib/embedding-store.js.map +1 -0
  148. package/dist/lib/embeddings-source.d.ts +36 -0
  149. package/dist/lib/embeddings-source.js +62 -0
  150. package/dist/lib/embeddings-source.js.map +1 -0
  151. package/dist/lib/enforcement-allowlist.d.ts +12 -0
  152. package/dist/lib/enforcement-allowlist.js +25 -0
  153. package/dist/lib/enforcement-allowlist.js.map +1 -0
  154. package/dist/lib/events.d.ts +47 -0
  155. package/dist/lib/events.js +75 -0
  156. package/dist/lib/events.js.map +1 -0
  157. package/dist/lib/exec.d.ts +8 -0
  158. package/dist/lib/exec.js +25 -0
  159. package/dist/lib/exec.js.map +1 -0
  160. package/dist/lib/frontmatter.d.ts +112 -0
  161. package/dist/lib/frontmatter.js +261 -0
  162. package/dist/lib/frontmatter.js.map +1 -0
  163. package/dist/lib/funnel.d.ts +22 -0
  164. package/dist/lib/funnel.js +41 -0
  165. package/dist/lib/funnel.js.map +1 -0
  166. package/dist/lib/git-mtime.d.ts +4 -0
  167. package/dist/lib/git-mtime.js +23 -0
  168. package/dist/lib/git-mtime.js.map +1 -0
  169. package/dist/lib/metrics.d.ts +42 -0
  170. package/dist/lib/metrics.js +92 -0
  171. package/dist/lib/metrics.js.map +1 -0
  172. package/dist/lib/migration-collision.d.ts +20 -0
  173. package/dist/lib/migration-collision.js +61 -0
  174. package/dist/lib/migration-collision.js.map +1 -0
  175. package/dist/lib/next-action.d.ts +44 -0
  176. package/dist/lib/next-action.js +138 -0
  177. package/dist/lib/next-action.js.map +1 -0
  178. package/dist/lib/ollama-embed.d.ts +21 -0
  179. package/dist/lib/ollama-embed.js +65 -0
  180. package/dist/lib/ollama-embed.js.map +1 -0
  181. package/dist/lib/output.d.ts +35 -0
  182. package/dist/lib/output.js +46 -0
  183. package/dist/lib/output.js.map +1 -0
  184. package/dist/lib/pipeline-config.d.ts +20 -0
  185. package/dist/lib/pipeline-config.js +208 -0
  186. package/dist/lib/pipeline-config.js.map +1 -0
  187. package/dist/lib/pipeline-map.d.ts +68 -0
  188. package/dist/lib/pipeline-map.js +256 -0
  189. package/dist/lib/pipeline-map.js.map +1 -0
  190. package/dist/lib/pipeline-schema.d.ts +48 -0
  191. package/dist/lib/pipeline-schema.js +22 -0
  192. package/dist/lib/pipeline-schema.js.map +1 -0
  193. package/dist/lib/placement.d.ts +6 -0
  194. package/dist/lib/placement.js +40 -0
  195. package/dist/lib/placement.js.map +1 -0
  196. package/dist/lib/precedent-search.d.ts +18 -0
  197. package/dist/lib/precedent-search.js +160 -0
  198. package/dist/lib/precedent-search.js.map +1 -0
  199. package/dist/lib/predicate-registry.d.ts +6 -0
  200. package/dist/lib/predicate-registry.js +40 -0
  201. package/dist/lib/predicate-registry.js.map +1 -0
  202. package/dist/lib/recall-cases.d.ts +15 -0
  203. package/dist/lib/recall-cases.js +39 -0
  204. package/dist/lib/recall-cases.js.map +1 -0
  205. package/dist/lib/repo.d.ts +20 -0
  206. package/dist/lib/repo.js +82 -0
  207. package/dist/lib/repo.js.map +1 -0
  208. package/dist/lib/requirements.d.ts +91 -0
  209. package/dist/lib/requirements.js +179 -0
  210. package/dist/lib/requirements.js.map +1 -0
  211. package/dist/lib/rollup.d.ts +24 -0
  212. package/dist/lib/rollup.js +34 -0
  213. package/dist/lib/rollup.js.map +1 -0
  214. package/dist/lib/scorecard-run.d.ts +6 -0
  215. package/dist/lib/scorecard-run.js +48 -0
  216. package/dist/lib/scorecard-run.js.map +1 -0
  217. package/dist/lib/scorecard.d.ts +69 -0
  218. package/dist/lib/scorecard.js +113 -0
  219. package/dist/lib/scorecard.js.map +1 -0
  220. package/dist/lib/semantic-search.d.ts +36 -0
  221. package/dist/lib/semantic-search.js +56 -0
  222. package/dist/lib/semantic-search.js.map +1 -0
  223. package/dist/lib/session-state.d.ts +8 -0
  224. package/dist/lib/session-state.js +25 -0
  225. package/dist/lib/session-state.js.map +1 -0
  226. package/dist/lib/spec-debt.d.ts +8 -0
  227. package/dist/lib/spec-debt.js +17 -0
  228. package/dist/lib/spec-debt.js.map +1 -0
  229. package/dist/lib/spec-pure.d.ts +59 -0
  230. package/dist/lib/spec-pure.js +215 -0
  231. package/dist/lib/spec-pure.js.map +1 -0
  232. package/dist/lib/specs.d.ts +148 -0
  233. package/dist/lib/specs.js +373 -0
  234. package/dist/lib/specs.js.map +1 -0
  235. package/dist/lib/test-generator.d.ts +5 -0
  236. package/dist/lib/test-generator.js +72 -0
  237. package/dist/lib/test-generator.js.map +1 -0
  238. package/dist/lib/user-stories.d.ts +6 -0
  239. package/dist/lib/user-stories.js +56 -0
  240. package/dist/lib/user-stories.js.map +1 -0
  241. package/dist/lib/verifies-scan.d.ts +9 -0
  242. package/dist/lib/verifies-scan.js +73 -0
  243. package/dist/lib/verifies-scan.js.map +1 -0
  244. package/dist/lib/verify-core.d.ts +23 -0
  245. package/dist/lib/verify-core.js +48 -0
  246. package/dist/lib/verify-core.js.map +1 -0
  247. package/dist/lib/with-event-log.d.ts +12 -0
  248. package/dist/lib/with-event-log.js +65 -0
  249. package/dist/lib/with-event-log.js.map +1 -0
  250. package/dist/lib/wizard-specs.d.ts +17 -0
  251. package/dist/lib/wizard-specs.js +34 -0
  252. package/dist/lib/wizard-specs.js.map +1 -0
  253. package/dist/pure.d.ts +19 -0
  254. package/dist/pure.js +31 -0
  255. package/dist/pure.js.map +1 -0
  256. package/package.json +69 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-state.js","sourceRoot":"","sources":["../../src/lib/session-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,MAAM,OAAO,GAAG,4DAA4D,CAAC;AAC7E,MAAM,OAAO,GAAG,mCAAmC,CAAC;AAEpD,gGAAgG;AAChG,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;AACzE,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAC9D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,iBAAiB,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Single-spec verification-debt flag (A7): true when the spec CLAIMS complete but has zero
3
+ * @verifies-backed requirements — the scorecard's `claimedCompleteButUnverified` column for
4
+ * one id. Reuses buildScorecardForCore + scanVerifies (the same machinery `eos-spec scorecard`
5
+ * runs) rather than re-deriving the predicate, so `next`/`status` and the scorecard never drift.
6
+ * Returns false for an unknown id (no row).
7
+ */
8
+ export declare function isClaimedCompleteUnverified(repoRoot: string, specId: string): boolean;
@@ -0,0 +1,17 @@
1
+ import { buildScorecardForCore } from './scorecard-run.js';
2
+ import { scanVerifies } from './verifies-scan.js';
3
+ /**
4
+ * Single-spec verification-debt flag (A7): true when the spec CLAIMS complete but has zero
5
+ * @verifies-backed requirements — the scorecard's `claimedCompleteButUnverified` column for
6
+ * one id. Reuses buildScorecardForCore + scanVerifies (the same machinery `eos-spec scorecard`
7
+ * runs) rather than re-deriving the predicate, so `next`/`status` and the scorecard never drift.
8
+ * Returns false for an unknown id (no row).
9
+ */
10
+ export function isClaimedCompleteUnverified(repoRoot, specId) {
11
+ const core = specId.split('-')[0]?.toLowerCase();
12
+ const verifiedBySpec = scanVerifies(repoRoot);
13
+ const report = buildScorecardForCore(repoRoot, core, verifiedBySpec);
14
+ const row = report.rows.find((r) => r.id.toUpperCase() === specId.toUpperCase());
15
+ return row?.claimedCompleteButUnverified ?? false;
16
+ }
17
+ //# sourceMappingURL=spec-debt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-debt.js","sourceRoot":"","sources":["../../src/lib/spec-debt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CAAC,QAAgB,EAAE,MAAc;IAC1E,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;IACjD,MAAM,cAAc,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,qBAAqB,CAAC,QAAQ,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;IACrE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IACjF,OAAO,GAAG,EAAE,4BAA4B,IAAI,KAAK,CAAC;AACpD,CAAC"}
@@ -0,0 +1,59 @@
1
+ import type { SpecSignals } from './specs.js';
2
+ /** Cores requiring regulatory grounding + compliance review. */
3
+ export declare const REGULATED_CORES: Set<string>;
4
+ /** True when a generated test source has ≥1 ACTIVE `test(` block (not `test.fixme`/
5
+ * `test.describe`) and is not an AC-less placeholder. Pure (operates on source text). */
6
+ export declare function hasActiveGeneratedTestSource(source: string): boolean;
7
+ /** File-existence + fs-derived flags `deriveSignalsFromText` cannot compute from
8
+ * content alone; the fs-bound `deriveSignals` (specs.ts) supplies them, a browser
9
+ * consumer passes what it knows (e.g. `reviewFileExists`) and the rest default false. */
10
+ export type SpecSignalFlags = {
11
+ researchFileExists?: boolean;
12
+ reviewFileExists?: boolean;
13
+ generatedTestExists?: boolean;
14
+ complianceSignoffFileExists?: boolean;
15
+ docshotImageExists?: boolean;
16
+ activeGeneratedTests?: boolean;
17
+ isCreateWizardSpec?: boolean;
18
+ submitE2eProvesPersist?: boolean;
19
+ linkedLocalTestExists?: boolean;
20
+ };
21
+ /**
22
+ * Derive a spec's pipeline signals from its core + raw markdown text, using only
23
+ * content (frontmatter + body regexes) plus the caller-supplied file-existence
24
+ * flags. The single source of truth for signal derivation: the fs-bound
25
+ * `deriveSignals` in specs.ts is a thin positional-args adapter over this.
26
+ */
27
+ export declare function deriveSignalsFromText(core: string, text: string, flags?: SpecSignalFlags): SpecSignals;
28
+ /**
29
+ * The spec id parsed from `filename`, anchored to the EXACT `core` (any length).
30
+ * The id grammar is `{core}[-{SUB}]-{num}[.{minor}][-{EN-n|En|Pn[.n][-EN-n]}]`,
31
+ * matched case-insensitively. Anchoring to the literal core (instead of a generic
32
+ * `[A-Z]{2,N}` class + `startsWith`) is what lets the same helper serve encoreos's
33
+ * 2-char cores (PF/CE) AND builder's longer cores (RUNNER, tool) without
34
+ * over-crediting a foreign id (e.g. `FARM-12` is never read as core `fa`). Proven
35
+ * zero-diff against all 1287 encoreos spec files and 50/50 on the builder corpus
36
+ * (epic #300, Wave-1 G3).
37
+ */
38
+ export declare function extractSpecId(filename: string, core: string): string | null;
39
+ /** A `{ID}-PHASE…` lifecycle file. `[A-Z]{2,6}` so it skips builder phase files
40
+ * (e.g. `RUNNER-…-PHASE2-…`) as well as encoreos's 2–3-char cores. */
41
+ export declare function isPhaseLifecycleFile(filename: string): boolean;
42
+ export declare function isLifecycleArtifact(filename: string): boolean;
43
+ /** True when a filename + its containing dir indicate a derivative artifact, not a primary spec. */
44
+ export declare function isDerivativeArtifact(dirName: string, filename: string): boolean;
45
+ /** The skip rules `collectSpecsFromDirectory` applies to one filename. */
46
+ export declare function isPrimarySpecFile(filename: string): boolean;
47
+ /** True when a repo-relative path is a primary spec directly under specs/{core}/specs|ux/. */
48
+ export declare function isPrimarySpecPath(path: string): boolean;
49
+ /** The spec id for a primary spec path, or null if the path isn't a primary spec. */
50
+ export declare function specIdForPath(path: string): string | null;
51
+ /** Deterministic winner (path-sorted, mirroring the CLI's findSpec) for a spec id among `allPaths`. */
52
+ export declare function findSpecPath(specId: string, allPaths: string[]): string | null;
53
+ /**
54
+ * Path-Set equivalent of specs.ts's `hasArtifact`/`resolveArtifactPath`: is there a
55
+ * `specs/{core}/{folder}/{id}-{suffix}.md` (flat dir only) in `paths`? A child
56
+ * sub-spec's artifact (`{id}-EN-01-TASKS.md`, `{id}-P2-TASKS.md`) must not credit
57
+ * the parent id — same boundary rule the fs walker uses.
58
+ */
59
+ export declare function hasArtifactInPaths(paths: Set<string>, core: string, folder: 'plans' | 'tasks', id: string, suffix: string): boolean;
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Pure, fs-free spec helpers — the browser-safe core a client (e.g. Spec Studio)
3
+ * can import without pulling node:fs / child_process. Everything here takes spec
4
+ * CONTENT and PATH STRINGS (or a Set of paths), never reads the filesystem, so it
5
+ * is re-exported from `/pure` and guarded by `pure-purity.test.ts`.
6
+ *
7
+ * `specs.ts` (the fs-bound corpus walker) DELEGATES to these: `deriveSignals`
8
+ * calls `deriveSignalsFromText`, and the path classifiers / artifact-existence
9
+ * checks are the single source of truth re-exported from there. That keeps the
10
+ * fs and browser code paths from drifting — the whole reason this module exists.
11
+ *
12
+ * The `import type` of `SpecSignals` from the fs-bound `specs.ts` is erased by
13
+ * tsc, so it does NOT pull specs.ts into the `/pure` runtime closure (asserted by
14
+ * pure-purity.test.ts).
15
+ */
16
+ import { collectSpecIds } from './blockers.js';
17
+ import { readFrontmatter } from './frontmatter.js';
18
+ import { acStatusCounts, allAcceptanceCriteriaTerminal } from './requirements.js';
19
+ /** Cores requiring regulatory grounding + compliance review. */
20
+ export const REGULATED_CORES = new Set(['cl', 'pm', 'hr', 'rh', 'gr']);
21
+ /** The marker the generator emits for an AC-less spec. */
22
+ const GENERATED_NO_AC_MARKER = 'no acceptance_criteria — run encore-spec-ac-draft first';
23
+ /** True when a generated test source has ≥1 ACTIVE `test(` block (not `test.fixme`/
24
+ * `test.describe`) and is not an AC-less placeholder. Pure (operates on source text). */
25
+ export function hasActiveGeneratedTestSource(source) {
26
+ if (source.includes(GENERATED_NO_AC_MARKER))
27
+ return false;
28
+ const withoutLineComments = source.replace(/\/\/[^\n]*/g, '');
29
+ return /(?<![.\w])test\s*\(/.test(withoutLineComments);
30
+ }
31
+ /**
32
+ * Derive a spec's pipeline signals from its core + raw markdown text, using only
33
+ * content (frontmatter + body regexes) plus the caller-supplied file-existence
34
+ * flags. The single source of truth for signal derivation: the fs-bound
35
+ * `deriveSignals` in specs.ts is a thin positional-args adapter over this.
36
+ */
37
+ export function deriveSignalsFromText(core, text, flags = {}) {
38
+ const { researchFileExists = false, reviewFileExists = false, generatedTestExists = false, complianceSignoffFileExists = false, docshotImageExists = false, activeGeneratedTests = false, isCreateWizardSpec = false, submitE2eProvesPersist = false, linkedLocalTestExists = false, } = flags;
39
+ const fm = readFrontmatter(text);
40
+ const nonEmptyArray = (v) => Array.isArray(v) && v.length > 0;
41
+ const hasUserStoriesSection = /^#{1,3}\s+User Stories\b/im.test(text);
42
+ const affectedFiles = Array.isArray(fm.affected_files) ? fm.affected_files.map(String) : [];
43
+ const uiAffectedFiles = affectedFiles.some((f) => /\/(components|pages|wizards)\//.test(f) || f.endsWith('.tsx'));
44
+ return {
45
+ isRegulated: REGULATED_CORES.has(core.toLowerCase()),
46
+ pipelineStatus: typeof fm.pipeline_status === 'string' ? fm.pipeline_status : undefined,
47
+ hasResearch: researchFileExists || typeof fm.research_appendix === 'string',
48
+ hasAcceptanceCriteria: nonEmptyArray(fm.acceptance_criteria),
49
+ hasVerifiedAcceptanceCriteria: allAcceptanceCriteriaTerminal(fm.acceptance_criteria),
50
+ hasLinkedLocalTest: nonEmptyArray(fm.e2e_tests) && linkedLocalTestExists,
51
+ hasUserStories: nonEmptyArray(fm.user_stories) || hasUserStoriesSection,
52
+ hasGeneratedTests: nonEmptyArray(fm.e2e_tests) || generatedTestExists,
53
+ hasActiveGeneratedTests: activeGeneratedTests,
54
+ hasSeedFixture: /tests\/fixtures\/[a-z0-9-]+\/[A-Za-z0-9]+Seed\.ts/.test(text),
55
+ hasSpecReview: reviewFileExists || /<!--\s*spec-reviewed:/i.test(text),
56
+ hasValidationSignoff: /<!--\s*validated:/i.test(text),
57
+ hasComplianceSignoff: complianceSignoffFileExists || /<!--\s*compliance-reviewed:/i.test(text),
58
+ hasUxSignoff: /<!--\s*ux-reviewed:/i.test(text),
59
+ isUiBearing: uiAffectedFiles || /^#{1,3}\s*UI\s*\/?\s*UX\b/im.test(text),
60
+ hasDocshots: docshotImageExists || /-docs-shots\.spec\.ts/.test(text) || /<!--\s*docshots:/i.test(text),
61
+ hasHealSignoff: /<!--\s*heal-verified:/i.test(text),
62
+ hasUiGateWaiver: /<!--\s*ui-gate-waived:/i.test(text),
63
+ isCreateWizardSpec,
64
+ hasSubmitE2e: submitE2eProvesPersist || /<!--\s*submit-e2e-waived:/i.test(text),
65
+ blockedBy: collectSpecIds(fm.blocked_by),
66
+ hasVerificationSection: /^#{1,3}\s+Verification\b/im.test(text),
67
+ acStatus: acStatusCounts(fm.acceptance_criteria),
68
+ };
69
+ }
70
+ // ── pure path classification (the fs-free half of specs.ts's directory walk) ──
71
+ /** Escape a core for use inside a RegExp. */
72
+ function escapeForRegExp(s) {
73
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
74
+ }
75
+ const LIFECYCLE_ARTIFACTS = [
76
+ 'CLARIFICATION',
77
+ 'REVIEW',
78
+ 'COMPLIANCE-REPORT',
79
+ 'IMPLEMENTATION_PLAN',
80
+ 'IMPLEMENTATION-PLAN',
81
+ '-ENHANCEMENT-',
82
+ '-ANALYSIS',
83
+ ];
84
+ /** Subdirectories that hold derivative docs, not primary specs. */
85
+ const NON_PRIMARY_DIRS = new Set([
86
+ 'analysis',
87
+ 'decisions',
88
+ 'contexts',
89
+ 'navigation',
90
+ 'recommendations',
91
+ 'enhancements',
92
+ 'reviews',
93
+ 'reports',
94
+ 'research',
95
+ 'summaries',
96
+ 'plans',
97
+ 'tasks',
98
+ ]);
99
+ /** Filename without directory or trailing `.md`. */
100
+ function specBasename(path) {
101
+ const file = path.split('/').pop() ?? path;
102
+ return file.endsWith('.md') ? file.slice(0, -3) : file;
103
+ }
104
+ /**
105
+ * The spec id parsed from `filename`, anchored to the EXACT `core` (any length).
106
+ * The id grammar is `{core}[-{SUB}]-{num}[.{minor}][-{EN-n|En|Pn[.n][-EN-n]}]`,
107
+ * matched case-insensitively. Anchoring to the literal core (instead of a generic
108
+ * `[A-Z]{2,N}` class + `startsWith`) is what lets the same helper serve encoreos's
109
+ * 2-char cores (PF/CE) AND builder's longer cores (RUNNER, tool) without
110
+ * over-crediting a foreign id (e.g. `FARM-12` is never read as core `fa`). Proven
111
+ * zero-diff against all 1287 encoreos spec files and 50/50 on the builder corpus
112
+ * (epic #300, Wave-1 G3).
113
+ */
114
+ export function extractSpecId(filename, core) {
115
+ const re = new RegExp(`^(${escapeForRegExp(core)}(?:-[A-Z]{1,4})?-\\d+(?:\\.\\d+)?(?:-(?:EN-\\d+|E\\d+|P\\d+(?:\\.\\d+)?(?:-EN-\\d+)?))?)`, 'i');
116
+ const match = re.exec(specBasename(filename));
117
+ return match ? match[1] : null;
118
+ }
119
+ /** A `{ID}-PHASE…` lifecycle file. `[A-Z]{2,6}` so it skips builder phase files
120
+ * (e.g. `RUNNER-…-PHASE2-…`) as well as encoreos's 2–3-char cores. */
121
+ export function isPhaseLifecycleFile(filename) {
122
+ return /^[A-Z]{2,6}-\d+(?:\.\d+)?-(?:[A-Z]+-)?PHASE\d?-/i.test(specBasename(filename).toUpperCase());
123
+ }
124
+ export function isLifecycleArtifact(filename) {
125
+ const upper = specBasename(filename).toUpperCase();
126
+ // Match each token at DASH boundaries, preserving the token's own leading/trailing
127
+ // '-'. Plain `includes` over-excluded valid primary specs: 'REVIEW' matched
128
+ // `WEB-3-REVIEWED`/`...-PREVIEW-...`, 'COMPLIANCE-REPORT' matched
129
+ // `...-COMPLIANCE-REPORTING`, and stripping the dashes off '-ENHANCEMENT-' wrongly
130
+ // caught a `...-enhancement` SUFFIX. The token's own dashes are the boundary; the
131
+ // other side anchors on a dash or the string end.
132
+ return LIFECYCLE_ARTIFACTS.some((token) => {
133
+ const esc = token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
134
+ const left = token.startsWith('-') ? '' : '(?:^|-)';
135
+ const right = token.endsWith('-') ? '' : '(?:-|$)';
136
+ return new RegExp(`${left}${esc}${right}`).test(upper);
137
+ });
138
+ }
139
+ /** True when a filename + its containing dir indicate a derivative artifact, not a primary spec. */
140
+ export function isDerivativeArtifact(dirName, filename) {
141
+ if (NON_PRIMARY_DIRS.has(dirName))
142
+ return true;
143
+ if (isLifecycleArtifact(filename) || isPhaseLifecycleFile(filename))
144
+ return true;
145
+ 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());
146
+ }
147
+ /** The skip rules `collectSpecsFromDirectory` applies to one filename. */
148
+ export function isPrimarySpecFile(filename) {
149
+ if (!filename.endsWith('.md'))
150
+ return false;
151
+ if (filename.includes('CONTEXT'))
152
+ return false;
153
+ if (filename.startsWith('SPEC_INDEX'))
154
+ return false;
155
+ if (filename.startsWith('README'))
156
+ return false;
157
+ if (filename.includes('ENHANCEMENTS'))
158
+ return false;
159
+ if (filename.includes('EXPANSION'))
160
+ return false;
161
+ if (isPhaseLifecycleFile(filename))
162
+ return false;
163
+ if (isLifecycleArtifact(filename))
164
+ return false;
165
+ return true;
166
+ }
167
+ /** True when a repo-relative path is a primary spec directly under specs/{core}/specs|ux/. */
168
+ export function isPrimarySpecPath(path) {
169
+ const parts = path.split('/');
170
+ if (parts.length !== 4 || parts[0] !== 'specs')
171
+ return false;
172
+ if (parts[2] !== 'specs' && parts[2] !== 'ux')
173
+ return false;
174
+ if (!isPrimarySpecFile(parts[3]))
175
+ return false;
176
+ return extractSpecId(parts[3], parts[1]) != null;
177
+ }
178
+ /** The spec id for a primary spec path, or null if the path isn't a primary spec. */
179
+ export function specIdForPath(path) {
180
+ if (!isPrimarySpecPath(path))
181
+ return null;
182
+ const parts = path.split('/');
183
+ return extractSpecId(parts[3], parts[1]);
184
+ }
185
+ /** Deterministic winner (path-sorted, mirroring the CLI's findSpec) for a spec id among `allPaths`. */
186
+ export function findSpecPath(specId, allPaths) {
187
+ const up = specId.toUpperCase();
188
+ const matches = allPaths.filter((p) => specIdForPath(p)?.toUpperCase() === up).sort((a, b) => a.localeCompare(b));
189
+ return matches[0] ?? null;
190
+ }
191
+ /**
192
+ * Path-Set equivalent of specs.ts's `hasArtifact`/`resolveArtifactPath`: is there a
193
+ * `specs/{core}/{folder}/{id}-{suffix}.md` (flat dir only) in `paths`? A child
194
+ * sub-spec's artifact (`{id}-EN-01-TASKS.md`, `{id}-P2-TASKS.md`) must not credit
195
+ * the parent id — same boundary rule the fs walker uses.
196
+ */
197
+ export function hasArtifactInPaths(paths, core, folder, id, suffix) {
198
+ const prefix = `specs/${core}/${folder}/`;
199
+ for (const p of paths) {
200
+ if (!p.startsWith(prefix))
201
+ continue;
202
+ const file = p.slice(prefix.length);
203
+ if (file.includes('/'))
204
+ continue; // flat readdir parity with the CLI
205
+ if (file === `${id}-${suffix}.md`)
206
+ return true;
207
+ if (!file.startsWith(`${id}-`) || !file.endsWith(`-${suffix}.md`))
208
+ continue;
209
+ const middle = file.slice(id.length + 1, file.length - `-${suffix}.md`.length);
210
+ if (!/^(EN-\d+|P\d+)(-|$)/i.test(middle))
211
+ return true;
212
+ }
213
+ return false;
214
+ }
215
+ //# sourceMappingURL=spec-pure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-pure.js","sourceRoot":"","sources":["../../src/lib/spec-pure.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,148 @@
1
+ import type { AcStatusCounts } from './requirements.js';
2
+ import { extractSpecId, isDerivativeArtifact } from './spec-pure.js';
3
+ export { extractSpecId, isDerivativeArtifact };
4
+ /** True when a generated test source has ≥1 ACTIVE `test(` block (not `test.fixme`/
5
+ * `test.describe`) and is not an AC-less placeholder. Mirrors the active-test regex
6
+ * in scripts/specs/check-generated-tests.ts (flagWeakTest): the negative lookbehind
7
+ * rejects `test.fixme(`, `test.describe(`, and `/re/.test(`. Line comments are stripped
8
+ * first so a `test(` mentioned in a `//` comment is not a false positive (the canonical
9
+ * regex runs on real generated files, which never carry such comments). */
10
+ export declare function hasActiveGeneratedTestSource(source: string): boolean;
11
+ /** Cores requiring regulatory grounding + compliance review (matches scripts/specs/validate-compliance-gate.ts). */
12
+ export declare const REGULATED_CORES: Set<string>;
13
+ export type SpecSignals = {
14
+ isRegulated: boolean;
15
+ pipelineStatus?: string;
16
+ hasResearch: boolean;
17
+ hasAcceptanceCriteria: boolean;
18
+ hasUserStories: boolean;
19
+ hasGeneratedTests: boolean;
20
+ /** A generated suite exists AND has ≥1 active test() block (not stub-only
21
+ * test.fixme scaffolds, and not an AC-less placeholder). Distinguishes
22
+ * tests-generated from tests-stubbed in the navigator funnel (B5). */
23
+ hasActiveGeneratedTests: boolean;
24
+ /** Every acceptance_criteria entry is in a TERMINAL status (`verified` |
25
+ * `hand_closed` | `deferred`) and there is ≥1 — i.e. none remain `planned`.
26
+ * Proof the ACs are terminally resolved (a `verified` AC is backed by a passing
27
+ * test set by verify-acs; `hand_closed`/`deferred` are the ADR-0015 audit-closed
28
+ * statuses, issue #174), so the generate-tests gate is satisfied even without a
29
+ * generated scaffold (C1). Computed via the typed model's
30
+ * `allAcceptanceCriteriaTerminal`. The name is retained for signal-shape
31
+ * stability across consumers; "verified" here means "terminally resolved". */
32
+ hasVerifiedAcceptanceCriteria: boolean;
33
+ /** A hand-authored `tests/e2e/<core>/<id>(-slug)?-local.spec.ts` carries a
34
+ * `@verifies <ID>` backlink AND the spec declares `e2e_tests` — the
35
+ * superpowers / non-generated test path (C1). */
36
+ hasLinkedLocalTest: boolean;
37
+ /** Advisory (Phase 3 canonical seed-fixture): the spec declares a Lane-4 seed
38
+ * fixture — a `tests/fixtures/<core>/<spec>Seed.ts` reference in its body, per the
39
+ * "### Seed Fixtures / Test Data" template section. Not a pipeline gate. */
40
+ hasSeedFixture?: boolean;
41
+ hasSpecReview: boolean;
42
+ hasValidationSignoff: boolean;
43
+ hasComplianceSignoff: boolean;
44
+ hasUxSignoff: boolean;
45
+ /** The spec ships user-facing UI (a populated "UI / UX" template section, or
46
+ * affected_files reaching into components/pages/wizards). Gates the verification
47
+ * tail (docshots + browser auto-heal) — data/backend-only specs skip both. */
48
+ isUiBearing: boolean;
49
+ /** Docshot evidence exists: a `docs/images/<area>/<spec-id>-*.png` on disk, a
50
+ * `*-docs-shots.spec.ts` referenced by the spec, or an explicit
51
+ * `<!-- docshots: -->` marker (legacy shot naming / not-applicable escape hatch,
52
+ * written via `eos-spec signoff --type docshots`). */
53
+ hasDocshots: boolean;
54
+ /** Browser auto-heal verdict recorded: `<!-- heal-verified: -->` marker, written
55
+ * via `eos-spec signoff --type heal-verified` after the browser-autoheal skill
56
+ * reaches HEAL_VERDICT=green. */
57
+ hasHealSignoff: boolean;
58
+ /** A `<!-- ui-gate-waived: <date> reason -->` marker exists, written via
59
+ * `eos-spec signoff --type ui-gate-waived`. Documented escape for the C1
60
+ * UI-evidence gate (e.g. a backend spec that `isUiBearing` false-positives on). */
61
+ hasUiGateWaiver: boolean;
62
+ /** Phase 3 (#1185): the spec is a kind:'create' wizard per the generated
63
+ * wizard-create-specs bridge. Gates the submit→DB E2E completion check. */
64
+ isCreateWizardSpec?: boolean;
65
+ /** Phase 3 (#1185): a tests/e2e/<core>/<id>-local.spec.ts proves a persisted row
66
+ * (imports createClient + a `.from('<table>') ... .select(` read-back of the target
67
+ * table — the post-submit verification read, not a bare cleanup `.delete()`, #1327),
68
+ * OR a `<!-- submit-e2e-waived: -->` marker is present. */
69
+ hasSubmitE2e?: boolean;
70
+ /** Canonical cross-spec blockers, parsed from `blocked_by` frontmatter (upper-cased spec ids). */
71
+ blockedBy: string[];
72
+ /** A `## Verification` section/heading is present in the spec body. A `hand_closed`
73
+ * AC (proven by a CI suite the src-lane prover can't reach) REQUIRES this backlink;
74
+ * the audit flags a hand_closed AC with no Verification section as a violation (#174).
75
+ * Optional on the type for signal-shape stability; always populated by deriveSignals. */
76
+ hasVerificationSection?: boolean;
77
+ /** Per-AC status tally (planned vs verified vs hand_closed vs deferred) read from
78
+ * frontmatter — the typed AC model the `audit` command consumes (#174). Optional on
79
+ * the type for signal-shape stability; always populated by deriveSignals. */
80
+ acStatus?: AcStatusCounts;
81
+ };
82
+ export type SpecFile = {
83
+ id: string;
84
+ core: string;
85
+ path: string;
86
+ title?: string;
87
+ status?: string;
88
+ /** The id of the spec that supersedes this one (frontmatter `superseded_by`). Present ⇒ terminal. */
89
+ supersededBy?: string;
90
+ hasPlan: boolean;
91
+ hasTasks: boolean;
92
+ hasContext: boolean;
93
+ signals: SpecSignals;
94
+ archived: boolean;
95
+ };
96
+ /**
97
+ * Derive pipeline signals from a spec's core + raw text. File-existence flags are
98
+ * supplied by the caller. Thin positional-args adapter over the pure
99
+ * {@link deriveSignalsFromText} (spec-pure.ts) — the single source of signal
100
+ * derivation, shared with browser consumers via `/pure`. Keep this signature
101
+ * stable; route new signal logic through `deriveSignalsFromText`.
102
+ */
103
+ export declare function deriveSignals(core: string, text: string, researchFileExists: boolean, reviewFileExists?: boolean, generatedTestExists?: boolean, complianceSignoffFileExists?: boolean, docshotImageExists?: boolean, activeGeneratedTests?: boolean, isCreateWizardSpec?: boolean, submitE2eProvesPersist?: boolean, linkedLocalTestExists?: boolean): SpecSignals;
104
+ export declare function listSpecs(repoRoot: string, coreFilter?: string, options?: {
105
+ includeArchived?: boolean;
106
+ }): SpecFile[];
107
+ /**
108
+ * Resolve the on-disk path of a spec artifact (plan/tasks) for `id`, or null when none exists.
109
+ * Folder-agnostic: the artifact dir is `specs/{core}/{folder}/`, independent of whether the
110
+ * spec itself lives under `specs/{core}/specs/` or `specs/{core}/ux/` — which is exactly why
111
+ * the old `/\/specs\/[^/]+\.md$/` path-derivation regex in next.ts silently failed for UX specs.
112
+ */
113
+ export declare function resolveArtifactPath(repoRoot: string, core: string, folder: 'plans' | 'tasks', id: string, suffix: string): string | null;
114
+ /** True when a tests/e2e/<core>/<id-lower>(-<slug>)?-local.spec.ts exists AND
115
+ * proves a persisted row was *read back* after submit: it imports createClient AND
116
+ * contains a `.from('<targetTable>')` chained to a `.select(` — the post-submit
117
+ * verification read — not merely a `.from('<targetTable>')` that could be a
118
+ * setup/cleanup `.delete()` (#1327). Quote style is free (`'`, `"`, or backtick),
119
+ * and arbitrary whitespace/newlines/chaining is allowed between the `.from(...)`
120
+ * and the `.select(` (the Supabase builder is fluent, so the read-back commonly
121
+ * spans lines: `db\n .from('t')\n .select(...)`). The submit→DB completion signal.
122
+ * The repo convention is `<id>-<slug>-local.spec.ts` (e.g.
123
+ * gr-ux-10-contract-creation-local.spec.ts), so the file is matched by prefix —
124
+ * the same prefix-glob `generatedTestExistsFor` uses — not an exact filename. */
125
+ export declare function submitE2eProvesPersistFor(repoRoot: string, core: string, id: string, targetTable: string): boolean;
126
+ /**
127
+ * All specs whose ID matches `specId` (case-insensitive) across `specs/` and
128
+ * `ux/` — plus `archive/` when `options.includeArchived` is set. Duplicate spec
129
+ * IDs are live in the corpus today (e.g. PF-76, PF-77); callers that
130
+ * gate on identity (create/archive collision checks, the navigator) must use
131
+ * this instead of `findSpec` so collisions are visible, never silently resolved.
132
+ */
133
+ export declare function findSpecsById(repoRoot: string, specId: string, options?: {
134
+ includeArchived?: boolean;
135
+ }): SpecFile[];
136
+ export declare function findSpec(repoRoot: string, specId: string, options?: {
137
+ includeArchived?: boolean;
138
+ }): SpecFile | null;
139
+ export type MissingSpecDiagnosis = {
140
+ foundPath?: string;
141
+ reason: string;
142
+ };
143
+ /**
144
+ * When findSpec returns null, look for a markdown file on disk whose name starts
145
+ * with the id so we can give honest advice instead of always suggesting
146
+ * create-spec. Walks `specs/{core}/` recursively.
147
+ */
148
+ export declare function diagnoseMissingSpec(repoRoot: string, specId: string): MissingSpecDiagnosis;