@interf/compiler 0.1.11 → 0.2.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 (283) hide show
  1. package/README.md +254 -136
  2. package/dist/commands/benchmark.d.ts.map +1 -1
  3. package/dist/commands/benchmark.js +65 -84
  4. package/dist/commands/benchmark.js.map +1 -1
  5. package/dist/commands/compile.d.ts.map +1 -1
  6. package/dist/commands/compile.js +19 -3
  7. package/dist/commands/compile.js.map +1 -1
  8. package/dist/commands/create.d.ts +3 -0
  9. package/dist/commands/create.d.ts.map +1 -1
  10. package/dist/commands/create.js +34 -9
  11. package/dist/commands/create.js.map +1 -1
  12. package/dist/commands/default.d.ts.map +1 -1
  13. package/dist/commands/default.js +2 -0
  14. package/dist/commands/default.js.map +1 -1
  15. package/dist/commands/init.d.ts.map +1 -1
  16. package/dist/commands/init.js +3 -2
  17. package/dist/commands/init.js.map +1 -1
  18. package/dist/index.d.ts +11 -29
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +7 -16
  21. package/dist/index.js.map +1 -1
  22. package/dist/lib/agent-args.d.ts +4 -0
  23. package/dist/lib/agent-args.d.ts.map +1 -0
  24. package/dist/lib/agent-args.js +42 -0
  25. package/dist/lib/agent-args.js.map +1 -0
  26. package/dist/lib/agent-constants.d.ts +6 -0
  27. package/dist/lib/agent-constants.d.ts.map +1 -0
  28. package/dist/lib/agent-constants.js +29 -0
  29. package/dist/lib/agent-constants.js.map +1 -0
  30. package/dist/lib/agent-detection.d.ts +8 -0
  31. package/dist/lib/agent-detection.d.ts.map +1 -0
  32. package/dist/lib/agent-detection.js +66 -0
  33. package/dist/lib/agent-detection.js.map +1 -0
  34. package/dist/lib/agent-execution.d.ts +3 -0
  35. package/dist/lib/agent-execution.d.ts.map +1 -0
  36. package/dist/lib/agent-execution.js +207 -0
  37. package/dist/lib/agent-execution.js.map +1 -0
  38. package/dist/lib/agent-logs.d.ts +3 -0
  39. package/dist/lib/agent-logs.d.ts.map +1 -0
  40. package/dist/lib/agent-logs.js +18 -0
  41. package/dist/lib/agent-logs.js.map +1 -0
  42. package/dist/lib/agent-preflight.d.ts +8 -0
  43. package/dist/lib/agent-preflight.d.ts.map +1 -0
  44. package/dist/lib/agent-preflight.js +77 -0
  45. package/dist/lib/agent-preflight.js.map +1 -0
  46. package/dist/lib/agent-render.d.ts +9 -0
  47. package/dist/lib/agent-render.d.ts.map +1 -0
  48. package/dist/lib/agent-render.js +219 -0
  49. package/dist/lib/agent-render.js.map +1 -0
  50. package/dist/lib/agent-status.d.ts +4 -0
  51. package/dist/lib/agent-status.d.ts.map +1 -0
  52. package/dist/lib/agent-status.js +59 -0
  53. package/dist/lib/agent-status.js.map +1 -0
  54. package/dist/lib/agent-types.d.ts +31 -0
  55. package/dist/lib/agent-types.d.ts.map +1 -0
  56. package/dist/lib/agent-types.js +2 -0
  57. package/dist/lib/agent-types.js.map +1 -0
  58. package/dist/lib/agents.d.ts +7 -49
  59. package/dist/lib/agents.d.ts.map +1 -1
  60. package/dist/lib/agents.js +8 -554
  61. package/dist/lib/agents.js.map +1 -1
  62. package/dist/lib/benchmark-execution.d.ts +9 -0
  63. package/dist/lib/benchmark-execution.d.ts.map +1 -0
  64. package/dist/lib/benchmark-execution.js +488 -0
  65. package/dist/lib/benchmark-execution.js.map +1 -0
  66. package/dist/lib/benchmark-paths.d.ts +11 -0
  67. package/dist/lib/benchmark-paths.d.ts.map +1 -0
  68. package/dist/lib/benchmark-paths.js +38 -0
  69. package/dist/lib/benchmark-paths.js.map +1 -0
  70. package/dist/lib/benchmark-specs.d.ts +8 -0
  71. package/dist/lib/benchmark-specs.d.ts.map +1 -0
  72. package/dist/lib/benchmark-specs.js +115 -0
  73. package/dist/lib/benchmark-specs.js.map +1 -0
  74. package/dist/lib/benchmark-targets.d.ts +5 -0
  75. package/dist/lib/benchmark-targets.d.ts.map +1 -0
  76. package/dist/lib/benchmark-targets.js +72 -0
  77. package/dist/lib/benchmark-targets.js.map +1 -0
  78. package/dist/lib/benchmark-types.d.ts +19 -0
  79. package/dist/lib/benchmark-types.d.ts.map +1 -0
  80. package/dist/lib/benchmark-types.js +2 -0
  81. package/dist/lib/benchmark-types.js.map +1 -0
  82. package/dist/lib/benchmark.d.ts +4 -29
  83. package/dist/lib/benchmark.d.ts.map +1 -1
  84. package/dist/lib/benchmark.js +3 -324
  85. package/dist/lib/benchmark.js.map +1 -1
  86. package/dist/lib/bundled-templates.d.ts +5 -0
  87. package/dist/lib/bundled-templates.d.ts.map +1 -0
  88. package/dist/lib/bundled-templates.js +23 -0
  89. package/dist/lib/bundled-templates.js.map +1 -0
  90. package/dist/lib/config.d.ts +1 -0
  91. package/dist/lib/config.d.ts.map +1 -1
  92. package/dist/lib/config.js +2 -0
  93. package/dist/lib/config.js.map +1 -1
  94. package/dist/lib/eval-packs.d.ts +204 -0
  95. package/dist/lib/eval-packs.d.ts.map +1 -0
  96. package/dist/lib/eval-packs.js +177 -0
  97. package/dist/lib/eval-packs.js.map +1 -0
  98. package/dist/lib/execution-profile.d.ts +18 -0
  99. package/dist/lib/execution-profile.d.ts.map +1 -0
  100. package/dist/lib/execution-profile.js +85 -0
  101. package/dist/lib/execution-profile.js.map +1 -0
  102. package/dist/lib/interf-bootstrap.d.ts +4 -0
  103. package/dist/lib/interf-bootstrap.d.ts.map +1 -1
  104. package/dist/lib/interf-bootstrap.js +71 -68
  105. package/dist/lib/interf-bootstrap.js.map +1 -1
  106. package/dist/lib/interf-compile-plan.d.ts +12 -0
  107. package/dist/lib/interf-compile-plan.d.ts.map +1 -0
  108. package/dist/lib/interf-compile-plan.js +143 -0
  109. package/dist/lib/interf-compile-plan.js.map +1 -0
  110. package/dist/lib/interf-detect.d.ts.map +1 -1
  111. package/dist/lib/interf-detect.js +11 -10
  112. package/dist/lib/interf-detect.js.map +1 -1
  113. package/dist/lib/interf-scaffold.d.ts +1 -10
  114. package/dist/lib/interf-scaffold.d.ts.map +1 -1
  115. package/dist/lib/interf-scaffold.js +25 -362
  116. package/dist/lib/interf-scaffold.js.map +1 -1
  117. package/dist/lib/interf-workflow-package.d.ts +4 -0
  118. package/dist/lib/interf-workflow-package.d.ts.map +1 -0
  119. package/dist/lib/interf-workflow-package.js +131 -0
  120. package/dist/lib/interf-workflow-package.js.map +1 -0
  121. package/dist/lib/interf.d.ts +2 -1
  122. package/dist/lib/interf.d.ts.map +1 -1
  123. package/dist/lib/interf.js +2 -1
  124. package/dist/lib/interf.js.map +1 -1
  125. package/dist/lib/local-workflows.d.ts.map +1 -1
  126. package/dist/lib/local-workflows.js +8 -12
  127. package/dist/lib/local-workflows.js.map +1 -1
  128. package/dist/lib/logger.d.ts +4 -0
  129. package/dist/lib/logger.d.ts.map +1 -0
  130. package/dist/lib/logger.js +11 -0
  131. package/dist/lib/logger.js.map +1 -0
  132. package/dist/lib/obsidian.d.ts.map +1 -1
  133. package/dist/lib/obsidian.js +7 -3
  134. package/dist/lib/obsidian.js.map +1 -1
  135. package/dist/lib/parse.d.ts +2 -2
  136. package/dist/lib/parse.d.ts.map +1 -1
  137. package/dist/lib/parse.js +11 -7
  138. package/dist/lib/parse.js.map +1 -1
  139. package/dist/lib/registry.js +3 -3
  140. package/dist/lib/registry.js.map +1 -1
  141. package/dist/lib/runtime-acceptance.d.ts +4 -0
  142. package/dist/lib/runtime-acceptance.d.ts.map +1 -0
  143. package/dist/lib/runtime-acceptance.js +123 -0
  144. package/dist/lib/runtime-acceptance.js.map +1 -0
  145. package/dist/lib/runtime-contracts.d.ts +4 -0
  146. package/dist/lib/runtime-contracts.d.ts.map +1 -0
  147. package/dist/lib/runtime-contracts.js +63 -0
  148. package/dist/lib/runtime-contracts.js.map +1 -0
  149. package/dist/lib/runtime-paths.d.ts +8 -0
  150. package/dist/lib/runtime-paths.d.ts.map +1 -0
  151. package/dist/lib/runtime-paths.js +28 -0
  152. package/dist/lib/runtime-paths.js.map +1 -0
  153. package/dist/lib/runtime-prompt.d.ts +3 -0
  154. package/dist/lib/runtime-prompt.d.ts.map +1 -0
  155. package/dist/lib/runtime-prompt.js +59 -0
  156. package/dist/lib/runtime-prompt.js.map +1 -0
  157. package/dist/lib/runtime-reconcile.d.ts +6 -0
  158. package/dist/lib/runtime-reconcile.d.ts.map +1 -0
  159. package/dist/lib/runtime-reconcile.js +339 -0
  160. package/dist/lib/runtime-reconcile.js.map +1 -0
  161. package/dist/lib/runtime-runs.d.ts +12 -0
  162. package/dist/lib/runtime-runs.d.ts.map +1 -0
  163. package/dist/lib/runtime-runs.js +337 -0
  164. package/dist/lib/runtime-runs.js.map +1 -0
  165. package/dist/lib/runtime-types.d.ts +42 -0
  166. package/dist/lib/runtime-types.d.ts.map +1 -0
  167. package/dist/lib/runtime-types.js +2 -0
  168. package/dist/lib/runtime-types.js.map +1 -0
  169. package/dist/lib/runtime.d.ts +6 -58
  170. package/dist/lib/runtime.d.ts.map +1 -1
  171. package/dist/lib/runtime.js +5 -614
  172. package/dist/lib/runtime.js.map +1 -1
  173. package/dist/lib/schema.d.ts +156 -13
  174. package/dist/lib/schema.d.ts.map +1 -1
  175. package/dist/lib/schema.js +113 -4
  176. package/dist/lib/schema.js.map +1 -1
  177. package/dist/lib/source-config.d.ts +13 -0
  178. package/dist/lib/source-config.d.ts.map +1 -0
  179. package/dist/lib/source-config.js +75 -0
  180. package/dist/lib/source-config.js.map +1 -0
  181. package/dist/lib/state-artifacts.d.ts +15 -0
  182. package/dist/lib/state-artifacts.d.ts.map +1 -0
  183. package/dist/lib/state-artifacts.js +24 -0
  184. package/dist/lib/state-artifacts.js.map +1 -0
  185. package/dist/lib/state-health.d.ts +9 -0
  186. package/dist/lib/state-health.d.ts.map +1 -0
  187. package/dist/lib/state-health.js +330 -0
  188. package/dist/lib/state-health.js.map +1 -0
  189. package/dist/lib/state-io.d.ts +15 -0
  190. package/dist/lib/state-io.d.ts.map +1 -0
  191. package/dist/lib/state-io.js +219 -0
  192. package/dist/lib/state-io.js.map +1 -0
  193. package/dist/lib/state-paths.d.ts +5 -0
  194. package/dist/lib/state-paths.d.ts.map +1 -0
  195. package/dist/lib/state-paths.js +19 -0
  196. package/dist/lib/state-paths.js.map +1 -0
  197. package/dist/lib/state-view.d.ts +7 -0
  198. package/dist/lib/state-view.d.ts.map +1 -0
  199. package/dist/lib/state-view.js +147 -0
  200. package/dist/lib/state-view.js.map +1 -0
  201. package/dist/lib/state.d.ts +6 -46
  202. package/dist/lib/state.d.ts.map +1 -1
  203. package/dist/lib/state.js +5 -632
  204. package/dist/lib/state.js.map +1 -1
  205. package/dist/lib/summarize-plan.d.ts +1 -0
  206. package/dist/lib/summarize-plan.d.ts.map +1 -1
  207. package/dist/lib/summarize-plan.js +10 -0
  208. package/dist/lib/summarize-plan.js.map +1 -1
  209. package/dist/lib/user-config.js +2 -2
  210. package/dist/lib/user-config.js.map +1 -1
  211. package/dist/lib/validate-helpers.d.ts +21 -0
  212. package/dist/lib/validate-helpers.d.ts.map +1 -0
  213. package/dist/lib/validate-helpers.js +72 -0
  214. package/dist/lib/validate-helpers.js.map +1 -0
  215. package/dist/lib/validate-interface.d.ts +79 -0
  216. package/dist/lib/validate-interface.d.ts.map +1 -0
  217. package/dist/lib/validate-interface.js +535 -0
  218. package/dist/lib/validate-interface.js.map +1 -0
  219. package/dist/lib/validate-kb.d.ts +81 -0
  220. package/dist/lib/validate-kb.d.ts.map +1 -0
  221. package/dist/lib/validate-kb.js +252 -0
  222. package/dist/lib/validate-kb.js.map +1 -0
  223. package/dist/lib/validate.d.ts +17 -146
  224. package/dist/lib/validate.d.ts.map +1 -1
  225. package/dist/lib/validate.js +33 -709
  226. package/dist/lib/validate.js.map +1 -1
  227. package/dist/lib/workflow-definitions.d.ts +1 -1
  228. package/dist/lib/workflow-definitions.d.ts.map +1 -1
  229. package/dist/lib/workflow-definitions.js +90 -166
  230. package/dist/lib/workflow-definitions.js.map +1 -1
  231. package/dist/lib/workflow-helpers.d.ts.map +1 -1
  232. package/dist/lib/workflow-helpers.js +6 -3
  233. package/dist/lib/workflow-helpers.js.map +1 -1
  234. package/dist/lib/workflow-stage-runner.d.ts +41 -0
  235. package/dist/lib/workflow-stage-runner.d.ts.map +1 -0
  236. package/dist/lib/workflow-stage-runner.js +106 -0
  237. package/dist/lib/workflow-stage-runner.js.map +1 -0
  238. package/dist/lib/workflow-starter-docs.d.ts +9 -0
  239. package/dist/lib/workflow-starter-docs.d.ts.map +1 -0
  240. package/dist/lib/workflow-starter-docs.js +18 -0
  241. package/dist/lib/workflow-starter-docs.js.map +1 -0
  242. package/dist/lib/workflows-interface-contracts.d.ts +24 -0
  243. package/dist/lib/workflows-interface-contracts.d.ts.map +1 -0
  244. package/dist/lib/workflows-interface-contracts.js +304 -0
  245. package/dist/lib/workflows-interface-contracts.js.map +1 -0
  246. package/dist/lib/workflows-interface.d.ts +3 -10
  247. package/dist/lib/workflows-interface.d.ts.map +1 -1
  248. package/dist/lib/workflows-interface.js +117 -365
  249. package/dist/lib/workflows-interface.js.map +1 -1
  250. package/dist/lib/workflows-kb.d.ts.map +1 -1
  251. package/dist/lib/workflows-kb.js +79 -55
  252. package/dist/lib/workflows-kb.js.map +1 -1
  253. package/dist/lib/workflows.d.ts +1 -1
  254. package/dist/lib/workflows.d.ts.map +1 -1
  255. package/dist/lib/workflows.js +1 -1
  256. package/dist/lib/workflows.js.map +1 -1
  257. package/package.json +15 -4
  258. package/skills/interface/analyze/SKILL.md +79 -28
  259. package/skills/interface/compile/SKILL.md +27 -28
  260. package/skills/interface/create/SKILL.md +53 -230
  261. package/skills/interface/create/references/compile-plan-format.md +31 -31
  262. package/skills/interface/create/references/workflows.md +17 -32
  263. package/skills/interface/query/SKILL.md +15 -1
  264. package/skills/interface/retrieve/SKILL.md +32 -65
  265. package/skills/knowledge-base/compile/SKILL.md +59 -83
  266. package/skills/knowledge-base/compile/references/stage-claims.md +1 -1
  267. package/skills/knowledge-base/compile/references/stage-entities.md +2 -2
  268. package/skills/knowledge-base/query/SKILL.md +13 -1
  269. package/skills/knowledge-base/summarize/SKILL.md +54 -24
  270. package/templates/interface/README.md +13 -12
  271. package/templates/interface/interfaces.md +14 -11
  272. package/templates/knowledge-base/README.md +0 -1
  273. package/templates/knowledge-base/registry.md +15 -15
  274. package/templates/workflow-package/README.md +16 -0
  275. package/templates/workflow-package/create/SKILL.md +8 -0
  276. package/templates/workflow-package/interface-query/SKILL.md +29 -0
  277. package/templates/workflow-package/interface-stage/SKILL.md +13 -0
  278. package/templates/workflow-package/knowledge-base-query/SKILL.md +36 -0
  279. package/templates/workflow-package/knowledge-base-stage/SKILL.md +13 -0
  280. package/templates/workflow-starters/interface/interf/README.md +13 -0
  281. package/templates/workflow-starters/interface/interf/create/SKILL.md +15 -0
  282. package/templates/workflow-starters/knowledge-base/interf/README.md +13 -0
  283. package/templates/workflow-starters/knowledge-base/karpathy/README.md +13 -0
@@ -1,24 +1,11 @@
1
1
  import { existsSync, readFileSync, } from "node:fs";
2
- import { basename, extname, join, resolve } from "node:path";
3
- import { countFilesRecursive, listFilesRecursive } from "./filesystem.js";
4
- import { discoverSourceFiles } from "./discovery.js";
5
- import { readInterfConfig, resolveKnowledgeBaseSourcePath } from "./interf.js";
6
- import { parseJsonFrontmatter, safeReadJsonFile } from "./parse.js";
7
- import { getInterfaceWorkflow, resolveInterfaceWorkflowFromConfig, } from "./workflow-definitions.js";
8
- import { InterfaceCoverageSchema, InterfaceRelevantSchema, InterfaceStateSchema, KnowledgeBaseInventorySchema, KnowledgeBaseStateSchema, } from "./schema.js";
9
- function extractFirstSection(text, headings) {
10
- for (const heading of headings) {
11
- const section = extractSection(text, heading);
12
- if (section)
13
- return section;
14
- }
15
- return null;
16
- }
17
- function sectionHasAnyMarker(section, markers) {
18
- if (typeof section !== "string")
19
- return false;
20
- return markers.some((marker) => new RegExp(`(^|\\n)\\s{0,3}#{0,6}\\s*${escapeRegExp(marker)}`, "im").test(section));
21
- }
2
+ import { basename, extname, join } from "node:path";
3
+ import { listFilesRecursive } from "./filesystem.js";
4
+ import { readInterfConfig } from "./interf.js";
5
+ import { parseJsonFrontmatter } from "./parse.js";
6
+ // ---------------------------------------------------------------------------
7
+ // Shared constants
8
+ // ---------------------------------------------------------------------------
22
9
  const REQUIRED_DIGEST_FIELDS = [
23
10
  "source_kind",
24
11
  "evidence_tier",
@@ -27,603 +14,10 @@ const REQUIRED_DIGEST_FIELDS = [
27
14
  ];
28
15
  const MIN_ABSTRACT_WORDS = 5;
29
16
  const WIKILINK_PATTERN = /!?\[\[([^[\]]+)\]\]/g;
30
- export function validateKnowledgeBase(dirPath) {
31
- const config = readKnowledgeBaseConfig(dirPath);
32
- const summaryFiles = listFilesRecursive(join(dirPath, "summaries"), isOutputMarkdownFile);
33
- const summaryValidation = validateSynthFiles(summaryFiles);
34
- const brokenLinks = countBrokenWikilinks(dirPath, [join(dirPath, "summaries"), join(dirPath, "knowledge")], [join(dirPath, "summaries"), join(dirPath, "knowledge")]);
35
- return {
36
- config_present: config.present,
37
- config_valid: config.valid,
38
- config_type_match: config.typeMatch("knowledge-base"),
39
- summary_frontmatter_valid: summaryValidation.invalid_frontmatter === 0,
40
- abstracts_valid: summaryValidation.short_abstracts === 0,
41
- invalid_frontmatter: summaryValidation.invalid_frontmatter,
42
- short_abstracts: summaryValidation.short_abstracts,
43
- broken_links: brokenLinks,
44
- };
45
- }
46
- export function validateInterfaceKnowledgeBase(dirPath) {
47
- const config = readKnowledgeBaseConfig(dirPath);
48
- // Resolve knowledge-base path from config
49
- const configValue = config.value;
50
- const knowledgeBaseConfig = configValue?.knowledge_base;
51
- const knowledgeBasePath = knowledgeBaseConfig?.path;
52
- const resolvedKnowledgeBasePath = knowledgeBasePath ? resolve(dirPath, knowledgeBasePath) : null;
53
- const knowledgeBasePathValid = resolvedKnowledgeBasePath !== null && existsSync(join(resolvedKnowledgeBasePath, "summaries"));
54
- const brokenLinks = countBrokenWikilinks(dirPath, [join(dirPath, "knowledge"), join(dirPath, "briefs"), join(dirPath, "summaries")], [join(dirPath, "knowledge"), join(dirPath, "briefs"), join(dirPath, "summaries")]);
55
- return {
56
- config_present: config.present,
57
- config_valid: config.valid,
58
- config_type_match: config.typeMatch("interface"),
59
- knowledge_base_path_valid: knowledgeBasePathValid,
60
- broken_links: brokenLinks,
61
- };
62
- }
63
- export function validateKnowledgeBaseSummarize(dirPath) {
64
- const config = readKnowledgeBaseConfig(dirPath);
65
- const configValue = config.value;
66
- const sourceConfig = configValue?.source;
67
- const sourcePath = resolveKnowledgeBaseSourcePath(dirPath, sourceConfig && typeof sourceConfig.path === "string"
68
- ? { type: "knowledge-base", name: basename(dirPath), source: { path: sourceConfig.path } }
69
- : null);
70
- const discovery = discoverSourceFiles(sourcePath, dirPath);
71
- const sourceTotal = discovery.totalCount;
72
- const summariesPresent = existsSync(join(dirPath, "summaries"));
73
- const summaryFiles = listFilesRecursive(join(dirPath, "summaries"), isOutputMarkdownFile);
74
- const summaryValidation = validateSynthFiles(summaryFiles);
75
- const summarized = summaryFiles.length;
76
- const sourceCovered = Math.min(sourceTotal, summarized);
77
- const toExtract = Math.max(0, sourceTotal - sourceCovered);
78
- const required = sourceTotal > 0 || summarized > 0;
79
- const checks = {
80
- config_present: config.present,
81
- config_valid: config.valid,
82
- config_type_match: config.typeMatch("knowledge-base"),
83
- summaries_present: summariesPresent,
84
- full_source_coverage: toExtract === 0,
85
- summary_frontmatter_valid: summaryValidation.invalid_frontmatter === 0,
86
- abstracts_valid: summaryValidation.short_abstracts === 0,
87
- };
88
- const errors = [];
89
- if (!checks.config_present)
90
- errors.push("Missing interf.json.");
91
- else if (!checks.config_valid)
92
- errors.push("Could not parse interf.json.");
93
- else if (!checks.config_type_match)
94
- errors.push("Config is not a knowledge base.");
95
- if (required && !checks.summaries_present)
96
- errors.push("Missing summaries/ directory.");
97
- if (!checks.full_source_coverage)
98
- errors.push("Not every source file has a summary yet.");
99
- if (!checks.summary_frontmatter_valid)
100
- errors.push("Some summary files have invalid or incomplete frontmatter.");
101
- if (!checks.abstracts_valid)
102
- errors.push("Some summary files are missing a valid abstract block.");
103
- const ok = errors.length === 0;
104
- const summary = !required
105
- ? "Extract not required yet — no source files discovered."
106
- : ok
107
- ? `Extract verified — ${sourceCovered}/${sourceTotal} source files are covered by valid summaries.`
108
- : `Extract failed — ${errors[0] ?? "incomplete summary coverage."}`;
109
- return {
110
- ok,
111
- required,
112
- summary,
113
- counts: {
114
- source_total: sourceTotal,
115
- source_covered: sourceCovered,
116
- summarized,
117
- to_summarize: toExtract,
118
- invalid_frontmatter: summaryValidation.invalid_frontmatter,
119
- short_abstracts: summaryValidation.short_abstracts,
120
- },
121
- checks,
122
- errors,
123
- };
124
- }
125
- export function validateKnowledgeBaseCompile(dirPath) {
126
- const extractValidation = validateKnowledgeBaseSummarize(dirPath);
127
- const config = readKnowledgeBaseConfig(dirPath);
128
- const statePath = join(dirPath, ".interf", "state.json");
129
- const inventoryPath = join(dirPath, ".interf", "inventory.json");
130
- const statePresent = existsSync(statePath);
131
- const inventoryPresent = existsSync(inventoryPath);
132
- const state = statePresent
133
- ? safeReadJsonFile(statePath, "knowledge-base state")
134
- : null;
135
- const inventory = inventoryPresent
136
- ? safeReadJsonFile(inventoryPath, "knowledge-base inventory")
137
- : null;
138
- const summarized = countFilesRecursive(join(dirPath, "summaries"));
139
- const compiled = asNumber(state?.compiled);
140
- const legacyInventoryFiles = Array.isArray(inventory?.files)
141
- ? inventory.files.filter((item) => !!item && typeof item === "object")
142
- : [];
143
- const summaryInventoryFiles = Array.isArray(inventory?.summaries)
144
- ? inventory.summaries.filter((item) => !!item && typeof item === "object")
145
- : [];
146
- const entryInventoryFiles = Array.isArray(inventory?.entries)
147
- ? inventory.entries.filter((item) => !!item && typeof item === "object")
148
- : [];
149
- const inventoryTotal = typeof inventory?.total === "number"
150
- ? inventory.total
151
- : typeof inventory?.summary_total === "number"
152
- ? inventory.summary_total
153
- : entryInventoryFiles.length;
154
- const inventoryFiles = legacyInventoryFiles.length > 0
155
- ? legacyInventoryFiles
156
- : summaryInventoryFiles.length > 0
157
- ? summaryInventoryFiles
158
- : entryInventoryFiles;
159
- const frontmatterScanned = legacyInventoryFiles.length > 0
160
- ? inventoryFiles.filter((file) => file.frontmatter_scanned === true).length
161
- : summaryInventoryFiles.length > 0
162
- ? inventoryFiles.filter((file) => typeof file.file === "string" && typeof file.source === "string").length
163
- : inventoryFiles.filter((file) => (typeof file.summary === "string" || typeof file.digest === "string") &&
164
- typeof file.source === "string").length;
165
- const abstractsRead = legacyInventoryFiles.length > 0
166
- ? inventoryFiles.filter((file) => file.abstract_read === true).length
167
- : summaryInventoryFiles.length > 0
168
- ? inventoryFiles.filter((file) => typeof file.status === "string").length
169
- : inventoryFiles.filter((file) => typeof file.state === "string").length;
170
- const entryAbstractsRead = entryInventoryFiles.length > 0
171
- ? entryInventoryFiles.filter((file) => typeof file.abstract === "string" && file.abstract.trim().length > 0).length
172
- : 0;
173
- const effectiveAbstractsRead = entryInventoryFiles.length > 0 && summaryInventoryFiles.length === 0 && legacyInventoryFiles.length === 0
174
- ? Math.max(abstractsRead, entryAbstractsRead)
175
- : abstractsRead;
176
- const fullReads = asNumber(state?.full_reads);
177
- const entityFiles = countFilesRecursive(join(dirPath, "knowledge", "entities"));
178
- const claimFiles = countFilesRecursive(join(dirPath, "knowledge", "claims"));
179
- const indexFiles = countFilesRecursive(join(dirPath, "knowledge", "indexes"));
180
- const outputs = entityFiles + claimFiles + indexFiles;
181
- const brokenLinks = countBrokenWikilinks(dirPath, [join(dirPath, "summaries"), join(dirPath, "knowledge")], [join(dirPath, "summaries"), join(dirPath, "knowledge")]);
182
- const required = extractValidation.required || outputs > 0 || compiled > 0 || inventoryPresent;
183
- const checks = {
184
- config_present: config.present,
185
- config_valid: config.valid,
186
- config_type_match: config.typeMatch("knowledge-base"),
187
- summarize_complete: extractValidation.ok && extractValidation.required,
188
- state_present: statePresent,
189
- state_valid: state !== null && KnowledgeBaseStateSchema.safeParse(state).success,
190
- inventory_present: inventoryPresent,
191
- inventory_valid: inventory !== null && KnowledgeBaseInventorySchema.safeParse(inventory).success,
192
- inventory_complete: Boolean(state?.inventory_complete) === true,
193
- inventory_matches_summaries: inventoryTotal === summarized && inventoryFiles.length === summarized,
194
- frontmatter_inventory_complete: frontmatterScanned === summarized,
195
- abstracts_cover_summaries: asNumber(state?.abstracts_read) >= summarized && effectiveAbstractsRead >= summarized,
196
- synced_complete: compiled >= summarized && summarized > 0,
197
- outputs_present: outputs > 0,
198
- links_valid: brokenLinks === 0,
199
- };
200
- const errors = [];
201
- if (!checks.config_present)
202
- errors.push("Missing interf.json.");
203
- else if (!checks.config_valid)
204
- errors.push("Could not parse interf.json.");
205
- else if (!checks.config_type_match)
206
- errors.push("Config is not a knowledge base.");
207
- if (!checks.summarize_complete)
208
- errors.push("Knowledge-base summarize is incomplete or invalid.");
209
- if (!checks.state_present)
210
- errors.push("Missing .interf/state.json.");
211
- else if (!checks.state_valid)
212
- errors.push("Could not parse .interf/state.json.");
213
- if (!checks.inventory_present)
214
- errors.push("Missing .interf/inventory.json.");
215
- else if (!checks.inventory_valid)
216
- errors.push("Could not parse .interf/inventory.json or it has the wrong shape.");
217
- if (checks.inventory_present && checks.inventory_valid && !checks.inventory_complete) {
218
- errors.push("state.json does not mark inventory_complete.");
219
- }
220
- if (checks.inventory_present && checks.inventory_valid && !checks.inventory_matches_summaries) {
221
- errors.push("Inventory does not match the summary set.");
222
- }
223
- if (checks.inventory_present && checks.inventory_valid && !checks.frontmatter_inventory_complete) {
224
- errors.push("Not every summary is marked frontmatter_scanned in inventory.");
225
- }
226
- if (checks.state_present && checks.state_valid && !checks.abstracts_cover_summaries) {
227
- errors.push("Abstract review does not cover the full summary set.");
228
- }
229
- if (checks.state_present && checks.state_valid && !checks.synced_complete) {
230
- errors.push("state.json compiled count does not cover the full summary set.");
231
- }
232
- if (!checks.outputs_present)
233
- errors.push("Knowledge-base compile outputs are missing or empty.");
234
- const ok = required ? errors.length === 0 : true;
235
- const summary = !required
236
- ? "Knowledge-base compile not required yet — no summaries exist yet."
237
- : ok
238
- ? `Knowledge-base compile verified — ${compiled}/${summarized} summaries compiled, ${entityFiles} entities, ${claimFiles} claims.`
239
- : `Knowledge-base compile failed — ${errors[0] ?? "incomplete graph compile."}`;
240
- return {
241
- ok,
242
- required,
243
- summary,
244
- counts: {
245
- source_total: extractValidation.counts.source_total,
246
- summarized,
247
- compiled,
248
- inventory_total: inventoryTotal,
249
- frontmatter_scanned: frontmatterScanned,
250
- abstracts_read: Math.max(asNumber(state?.abstracts_read), effectiveAbstractsRead),
251
- full_reads: fullReads,
252
- entity_files: entityFiles,
253
- claim_files: claimFiles,
254
- index_files: indexFiles,
255
- outputs,
256
- },
257
- checks,
258
- errors,
259
- };
260
- }
261
- export function validateInterfacePlan(dirPath) {
262
- const config = readKnowledgeBaseConfig(dirPath);
263
- const configValue = config.value;
264
- const knowledgeBaseConfig = configValue?.knowledge_base;
265
- const knowledgeBasePath = knowledgeBaseConfig?.path;
266
- const resolvedKnowledgeBasePath = knowledgeBasePath ? resolve(dirPath, knowledgeBasePath) : null;
267
- const knowledgeBasePathValid = resolvedKnowledgeBasePath !== null && existsSync(join(resolvedKnowledgeBasePath, "summaries"));
268
- const compilePlanPath = join(dirPath, "compile-plan.md");
269
- const agentsPath = join(dirPath, "AGENTS.md");
270
- const homePath = join(dirPath, "home.md");
271
- const compilePlanPresent = existsSync(compilePlanPath);
272
- const compilePlanText = compilePlanPresent ? safeReadText(compilePlanPath) : null;
273
- const workflowId = resolveInterfaceWorkflowFromConfig(configValue);
274
- const sourcePath = knowledgeBasePathValid && resolvedKnowledgeBasePath
275
- ? resolveKnowledgeBaseSourcePath(resolvedKnowledgeBasePath)
276
- : undefined;
277
- const workflow = getInterfaceWorkflow(workflowId, { sourcePath, workspacePath: dirPath });
278
- const stageChecks = workflow.stages.map((stage, index) => {
279
- const section = compilePlanText
280
- ? extractFirstSection(compilePlanText, [
281
- `Stage ${index + 1}: ${stage.label}`,
282
- `Stage ${index + 1} — ${stage.label}`,
283
- ])
284
- : null;
285
- return {
286
- id: stage.id,
287
- label: stage.label,
288
- contract_type: stage.contractType,
289
- present: section !== null,
290
- defined: sectionMatchesInterfaceContract(section, stage.contractType),
291
- };
292
- });
293
- const compilePlanSections = stageChecks.every((stage) => stage.present);
294
- const stage1Defined = stageChecks[0]?.defined ?? false;
295
- const stage2Defined = stageChecks[1]?.defined ?? false;
296
- const stage3Defined = stageChecks[2]?.defined ?? false;
297
- const allStageDefinitionsValid = stageChecks.every((stage) => stage.defined);
298
- const agentsPresent = existsSync(agentsPath);
299
- const homePresent = existsSync(homePath);
300
- const outputMentions = countMatches(compilePlanText ?? "", /(?:briefs\/|summaries\/|knowledge\/|home\.md)/gi);
301
- const required = true;
302
- const checks = {
303
- config_present: config.present,
304
- config_valid: config.valid,
305
- config_type_match: config.typeMatch("interface"),
306
- knowledge_base_path_valid: knowledgeBasePathValid,
307
- compile_plan_present: compilePlanPresent,
308
- compile_plan_sections: compilePlanSections,
309
- stage1_defined: stage1Defined,
310
- stage2_defined: stage2Defined,
311
- stage3_defined: stage3Defined,
312
- all_stage_definitions_valid: allStageDefinitionsValid,
313
- stage_checks: stageChecks,
314
- agents_present: agentsPresent,
315
- home_present: homePresent,
316
- };
317
- const errors = [];
318
- if (!checks.config_present)
319
- errors.push("Missing interf.json.");
320
- else if (!checks.config_valid)
321
- errors.push("Could not parse interf.json.");
322
- else if (!checks.config_type_match)
323
- errors.push("Config is not an interface.");
324
- if (!checks.knowledge_base_path_valid)
325
- errors.push("knowledgeBase.path does not resolve to a valid knowledgeBase.");
326
- if (!checks.compile_plan_present)
327
- errors.push("Missing compile-plan.md.");
328
- else if (!checks.compile_plan_sections)
329
- errors.push("compile-plan.md is missing one or more required stage sections.");
330
- if (checks.compile_plan_present) {
331
- for (const [index, stage] of stageChecks.entries()) {
332
- if (!stage.defined) {
333
- errors.push(`Stage ${index + 1} ${stage.label.toLowerCase()} plan is incomplete.`);
334
- }
335
- }
336
- }
337
- if (!checks.agents_present)
338
- errors.push("Missing AGENTS.md.");
339
- if (!checks.home_present)
340
- errors.push("Missing home.md.");
341
- const ok = errors.length === 0;
342
- const summary = ok
343
- ? `Interface plan verified — ${stageChecks.filter((stage) => stage.defined).length}/${stageChecks.length} stages defined with ${outputMentions} output references.`
344
- : `Interface plan failed — ${errors[0] ?? "compile plan is incomplete."}`;
345
- return {
346
- ok,
347
- required,
348
- summary,
349
- counts: {
350
- stage_sections_expected: stageChecks.length,
351
- stage_sections_present: stageChecks.filter((stage) => stage.present).length,
352
- output_mentions: outputMentions,
353
- },
354
- checks,
355
- errors,
356
- };
357
- }
358
- export function validateInterfaceRetrieve(dirPath) {
359
- const config = readKnowledgeBaseConfig(dirPath);
360
- const configValue = config.value;
361
- const knowledgeBaseConfig = configValue?.knowledge_base;
362
- const knowledgeBasePath = knowledgeBaseConfig?.path;
363
- const resolvedKnowledgeBasePath = knowledgeBasePath ? resolve(dirPath, knowledgeBasePath) : null;
364
- const knowledgeBasePathValid = resolvedKnowledgeBasePath !== null && existsSync(join(resolvedKnowledgeBasePath, "summaries"));
365
- const knowledgeBaseTotal = knowledgeBasePathValid ? countFilesRecursive(join(resolvedKnowledgeBasePath, "summaries")) : 0;
366
- const statePath = join(dirPath, ".interf", "state.json");
367
- const relevantPath = join(dirPath, ".interf", "relevant.json");
368
- const coveragePath = join(dirPath, ".interf", "coverage.json");
369
- const statePresent = existsSync(statePath);
370
- const relevantPresent = existsSync(relevantPath);
371
- const coveragePresent = existsSync(coveragePath);
372
- const state = statePresent
373
- ? safeReadJsonFile(statePath, "interface state")
374
- : null;
375
- const relevant = relevantPresent
376
- ? safeReadJsonFile(relevantPath, "interface retrieved set")
377
- : null;
378
- const coverage = coveragePresent
379
- ? safeReadJsonFile(coveragePath, "interface coverage proof")
380
- : null;
381
- const queryStarted = Boolean(state?.retrieve_complete) ||
382
- Boolean(state?.analyze_complete) ||
383
- Boolean(state?.compile_complete) ||
384
- typeof state?.last_retrieve === "string" ||
385
- typeof state?.last_analyze === "string" ||
386
- typeof state?.last_compile === "string" ||
387
- relevantPresent ||
388
- coveragePresent;
389
- const proof = coverage && typeof coverage.proof === "object" && coverage.proof
390
- ? coverage.proof
391
- : null;
392
- const bridgeRelevantEntries = asFileEntries(relevant?.relevant);
393
- const bridgeRejectedEntries = asFileEntries(relevant?.rejected);
394
- const bridgeCoverageShape = coverage !== null &&
395
- typeof coverage?.query_timestamp === "string" &&
396
- typeof coverage?.coverage_complete === "boolean" &&
397
- !!coverage?.entities &&
398
- typeof coverage.entities === "object" &&
399
- !!coverage?.comparators &&
400
- typeof coverage.comparators === "object" &&
401
- Array.isArray(coverage?.gaps);
402
- const derivedRelevantFiles = asStringArray(proof?.retrieved).length > 0
403
- ? asStringArray(proof?.retrieved)
404
- : bridgeRelevantEntries.map((entry) => entry.file);
405
- const relevantFiles = asStringArray(relevant?.relevant_files).length > 0
406
- ? asStringArray(relevant?.relevant_files)
407
- : derivedRelevantFiles;
408
- const deltaFiles = asStringArray(relevant?.delta_files).length > 0
409
- ? asStringArray(relevant?.delta_files)
410
- : asStringArray(state?.delta_files);
411
- const explicitCandidateFiles = asStringArray(coverage?.candidate_files);
412
- const scannedFiles = asStringArray(proof?.scanned).length > 0
413
- ? asStringArray(proof?.scanned)
414
- : dedupeStrings([
415
- ...bridgeRelevantEntries.map((entry) => entry.file),
416
- ...bridgeRejectedEntries.map((entry) => entry.file),
417
- ]);
418
- const candidateFiles = explicitCandidateFiles.length > 0
419
- ? explicitCandidateFiles
420
- : scannedFiles;
421
- const abstractReviewedFiles = asStringArray(coverage?.abstract_reviewed_files).length > 0
422
- ? asStringArray(coverage?.abstract_reviewed_files)
423
- : asStringArray(proof?.reviewed).length > 0
424
- ? asStringArray(proof?.reviewed)
425
- : candidateFiles;
426
- const selectedFiles = asStringArray(coverage?.selected_files).length > 0
427
- ? asStringArray(coverage?.selected_files)
428
- : derivedRelevantFiles;
429
- const rejectedFiles = asRejectedEntries(coverage?.rejected_files);
430
- const rejectedPaths = rejectedFiles.length > 0
431
- ? rejectedFiles.map((entry) => entry.path)
432
- : asStringArray(proof?.excluded).length > 0
433
- ? asStringArray(proof?.excluded)
434
- : bridgeRejectedEntries.map((entry) => entry.file);
435
- const frontmatterScanned = asNumber(coverage?.frontmatter_scanned) ||
436
- asNumber(state?.frontmatter_scanned) ||
437
- asNumber(relevant?.total_scanned);
438
- const candidateCount = asNumber(coverage?.candidates_after_frontmatter) ||
439
- asNumber(state?.candidate_count) ||
440
- candidateFiles.length;
441
- const abstractsRead = asNumber(coverage?.abstracts_reviewed) ||
442
- asNumber(state?.abstracts_read) ||
443
- abstractReviewedFiles.length;
444
- const selectedCount = asNumber(coverage?.relevant_selected) ||
445
- asNumber(state?.relevant_count) ||
446
- asNumber(relevant?.total_relevant) ||
447
- selectedFiles.length;
448
- const rejectedCount = asNumber(coverage?.rejected) ||
449
- asNumber(state?.rejected_count) ||
450
- asNumber(relevant?.total_rejected) ||
451
- rejectedPaths.length;
452
- const deltaCount = asNumber(state?.delta_count) || deltaFiles.length;
453
- const expansionPasses = asNumber(coverage?.expansion_passes) ||
454
- asNumber(state?.expansion_passes);
455
- const relevantValid = (relevant !== null &&
456
- typeof relevant?.generated_at === "string" &&
457
- typeof relevant?.knowledge_base_summary_count === "number" &&
458
- Array.isArray(relevant?.relevant_files) &&
459
- Array.isArray(relevant?.delta_files)) ||
460
- (relevant !== null &&
461
- typeof relevant?.query_timestamp === "string" &&
462
- Array.isArray(relevant?.relevant) &&
463
- Array.isArray(relevant?.rejected) &&
464
- typeof relevant?.total_scanned === "number" &&
465
- typeof relevant?.total_relevant === "number" &&
466
- typeof relevant?.total_rejected === "number") ||
467
- Array.isArray(proof?.retrieved);
468
- const coverageValid = (coverage !== null &&
469
- typeof coverage?.generated_at === "string" &&
470
- typeof coverage?.knowledge_base_summary_count === "number" &&
471
- typeof coverage?.frontmatter_scanned === "number" &&
472
- typeof coverage?.expansion_passes === "number" &&
473
- Array.isArray(coverage?.candidate_files) &&
474
- Array.isArray(coverage?.abstract_reviewed_files) &&
475
- Array.isArray(coverage?.selected_files) &&
476
- Array.isArray(coverage?.rejected_files) &&
477
- rejectedFiles.length === rejectedPaths.length) ||
478
- (coverage !== null &&
479
- typeof coverage?.generated_at === "string" &&
480
- typeof coverage?.knowledge_base_summary_total === "number" &&
481
- typeof coverage?.frontmatter_scanned === "number" &&
482
- typeof coverage?.candidates_after_frontmatter === "number" &&
483
- typeof coverage?.abstracts_reviewed === "number" &&
484
- typeof coverage?.relevant_selected === "number" &&
485
- typeof coverage?.rejected === "number" &&
486
- typeof coverage?.expansion_passes === "number" &&
487
- typeof coverage?.coverage_complete === "boolean" &&
488
- proof !== null &&
489
- Array.isArray(proof.scanned) &&
490
- Array.isArray(proof.reviewed) &&
491
- Array.isArray(proof.retrieved) &&
492
- Array.isArray(proof.excluded)) ||
493
- bridgeCoverageShape;
494
- const selectedSet = new Set(selectedFiles);
495
- const rejectedSet = new Set(rejectedPaths);
496
- const candidateSet = new Set(candidateFiles);
497
- const scannedSet = new Set(scannedFiles);
498
- const abstractSet = new Set(abstractReviewedFiles);
499
- const relevantSet = new Set(relevantFiles);
500
- const selectedRejectedUnion = new Set([...selectedFiles, ...rejectedPaths]);
501
- const usesProofScanCoverage = explicitCandidateFiles.length === 0 &&
502
- Array.isArray(proof?.scanned) &&
503
- typeof coverage?.candidates_after_frontmatter === "number";
504
- const knowledgeBaseCountMatch = knowledgeBasePathValid &&
505
- coverageValid &&
506
- (relevant !== null && typeof relevant?.knowledge_base_summary_count === "number"
507
- ? asNumber(relevant.knowledge_base_summary_count) === knowledgeBaseTotal
508
- : frontmatterScanned === knowledgeBaseTotal || candidateCount <= knowledgeBaseTotal) &&
509
- (typeof coverage?.knowledge_base_summary_count === "number"
510
- ? asNumber(coverage.knowledge_base_summary_count) === knowledgeBaseTotal
511
- : typeof coverage?.knowledge_base_summary_total === "number"
512
- ? asNumber(coverage?.knowledge_base_summary_total) === knowledgeBaseTotal
513
- : frontmatterScanned === knowledgeBaseTotal);
514
- const fullFrontmatterScan = knowledgeBasePathValid && coverageValid && frontmatterScanned === knowledgeBaseTotal;
515
- const candidatePartitionValid = coverageValid &&
516
- setIntersectionSize(selectedSet, rejectedSet) === 0 &&
517
- (usesProofScanCoverage
518
- ? scannedSet.size === selectedRejectedUnion.size &&
519
- isSubset(selectedSet, scannedSet) &&
520
- isSubset(rejectedSet, scannedSet)
521
- : candidateSet.size === selectedRejectedUnion.size &&
522
- isSubset(selectedSet, candidateSet) &&
523
- isSubset(rejectedSet, candidateSet));
524
- const abstractsCoverCandidates = coverageValid &&
525
- (usesProofScanCoverage
526
- ? selectedFiles.every((file) => abstractSet.has(file)) &&
527
- abstractSet.size >= candidateCount &&
528
- abstractsRead >= candidateCount
529
- : isSubset(candidateSet, abstractSet));
530
- const selectedMatchRelevant = coverageValid &&
531
- relevantValid &&
532
- setsEqual(selectedSet, relevantSet);
533
- const deltaSubset = relevantValid &&
534
- isSubset(new Set(deltaFiles), selectedSet);
535
- const stateCountsMatch = state !== null &&
536
- asNumber(state.frontmatter_scanned) === frontmatterScanned &&
537
- asNumber(state.candidate_count) === candidateCount &&
538
- asNumber(state.abstracts_read) === abstractsRead &&
539
- asNumber(state.expansion_passes) === expansionPasses &&
540
- asNumber(state.relevant_count) === selectedCount &&
541
- asNumber(state.rejected_count) === rejectedCount &&
542
- asNumber(state.delta_count) === deltaCount;
543
- const stateFlagsMatch = state !== null &&
544
- state.retrieve_complete === true &&
545
- state.coverage_complete === true;
546
- const checks = {
547
- knowledge_base_path_valid: knowledgeBasePathValid,
548
- state_present: statePresent,
549
- state_valid: state !== null && InterfaceStateSchema.safeParse(state).success,
550
- relevant_present: relevantPresent || Array.isArray(proof?.retrieved),
551
- relevant_valid: relevantValid ||
552
- (relevant !== null && InterfaceRelevantSchema.safeParse(relevant).success),
553
- coverage_present: coveragePresent,
554
- coverage_valid: coverageValid ||
555
- (coverage !== null && InterfaceCoverageSchema.safeParse(coverage).success),
556
- knowledge_base_count_match: knowledgeBaseCountMatch,
557
- full_frontmatter_scan: fullFrontmatterScan,
558
- candidate_partition_valid: candidatePartitionValid,
559
- abstracts_cover_candidates: abstractsCoverCandidates,
560
- selected_match_relevant: selectedMatchRelevant,
561
- delta_subset: deltaSubset,
562
- state_counts_match: stateCountsMatch,
563
- state_flags_match: stateFlagsMatch,
564
- };
565
- const errors = [];
566
- if (queryStarted) {
567
- if (!knowledgeBasePathValid)
568
- errors.push("Knowledge base is missing or invalid.");
569
- if (!statePresent)
570
- errors.push("Missing .interf/state.json.");
571
- else if (state === null)
572
- errors.push("Could not parse .interf/state.json.");
573
- if (!relevantPresent && !Array.isArray(proof?.retrieved))
574
- errors.push("Missing .interf/relevant.json.");
575
- else if (!relevantValid)
576
- errors.push("Could not parse .interf/relevant.json or it has the wrong shape.");
577
- if (!coveragePresent)
578
- errors.push("Missing .interf/coverage.json.");
579
- else if (!coverageValid)
580
- errors.push("Could not parse .interf/coverage.json or it has the wrong shape.");
581
- if (coveragePresent && coverageValid && !knowledgeBaseCountMatch)
582
- errors.push("Coverage proof does not match the current knowledge-base summary count.");
583
- if (coveragePresent && coverageValid && !fullFrontmatterScan)
584
- errors.push("Coverage proof does not show a full frontmatter scan.");
585
- if (coveragePresent && coverageValid && !candidatePartitionValid)
586
- errors.push("Candidate, selected, and rejected sets are inconsistent.");
587
- if (coveragePresent && coverageValid && !abstractsCoverCandidates)
588
- errors.push("Not every candidate file was abstract-reviewed.");
589
- if (relevantPresent && coveragePresent && relevantValid && coverageValid && !selectedMatchRelevant) {
590
- errors.push("Selected files in coverage proof do not match relevant_files.");
591
- }
592
- if (relevantPresent && coveragePresent && relevantValid && coverageValid && !deltaSubset) {
593
- errors.push("delta_files are not a subset of selected relevant files.");
594
- }
595
- if (statePresent && state !== null && coveragePresent && coverageValid && relevantPresent && relevantValid && !stateCountsMatch) {
596
- errors.push("state.json counts do not match the retrieved set and coverage proof.");
597
- }
598
- if (statePresent && state !== null && !stateFlagsMatch) {
599
- errors.push("state.json is missing retrieve_complete or coverage_complete.");
600
- }
601
- }
602
- const ok = queryStarted ? errors.length === 0 : true;
603
- const summary = !queryStarted
604
- ? "Coverage not required yet — interface retrieve has not started."
605
- : ok
606
- ? `Coverage verified — scanned ${frontmatterScanned}/${knowledgeBaseTotal}, reviewed ${abstractsRead} abstracts, selected ${selectedCount}, rejected ${rejectedCount}.`
607
- : `Coverage failed — ${errors[0] ?? "inconsistent retrieval proof."}`;
608
- return {
609
- ok,
610
- required: queryStarted,
611
- summary,
612
- counts: {
613
- knowledge_base_total: knowledgeBaseTotal,
614
- frontmatter_scanned: frontmatterScanned,
615
- candidate_count: candidateCount,
616
- abstracts_read: abstractsRead,
617
- selected_count: selectedCount,
618
- rejected_count: rejectedCount,
619
- delta_count: deltaCount,
620
- expansion_passes: expansionPasses,
621
- },
622
- checks,
623
- errors,
624
- };
625
- }
626
- function readKnowledgeBaseConfig(dirPath) {
17
+ // ---------------------------------------------------------------------------
18
+ // Shared helpers (used by both validate-kb and validate-interface)
19
+ // ---------------------------------------------------------------------------
20
+ export function readKnowledgeBaseConfig(dirPath) {
627
21
  const configPath = join(dirPath, "interf.json");
628
22
  const present = existsSync(configPath);
629
23
  if (!present) {
@@ -644,7 +38,7 @@ function readKnowledgeBaseConfig(dirPath) {
644
38
  },
645
39
  };
646
40
  }
647
- function validateSynthFiles(files) {
41
+ export function validateSynthFiles(files) {
648
42
  let invalidFrontmatter = 0;
649
43
  let shortAbstracts = 0;
650
44
  for (const filePath of files) {
@@ -666,7 +60,7 @@ function validateSynthFiles(files) {
666
60
  short_abstracts: shortAbstracts,
667
61
  };
668
62
  }
669
- function countBrokenWikilinks(knowledgeBaseRoot, noteIndexRoots, linkScanRoots) {
63
+ export function countBrokenWikilinks(knowledgeBaseRoot, noteIndexRoots, linkScanRoots) {
670
64
  const noteIndex = new Set();
671
65
  for (const filePath of dedupeFiles(noteIndexRoots)) {
672
66
  noteIndex.add(noteName(filePath).toLowerCase());
@@ -701,90 +95,23 @@ function countBrokenWikilinks(knowledgeBaseRoot, noteIndexRoots, linkScanRoots)
701
95
  }
702
96
  return brokenLinks;
703
97
  }
704
- function asStringArray(value) {
705
- if (!Array.isArray(value))
706
- return [];
707
- return value.filter((item) => typeof item === "string");
708
- }
709
- function asRejectedEntries(value) {
710
- if (!Array.isArray(value))
711
- return [];
712
- return value.filter((item) => !!item &&
713
- typeof item === "object" &&
714
- typeof item.path === "string" &&
715
- typeof item.reason === "string");
716
- }
717
- function asFileEntries(value) {
718
- if (!Array.isArray(value))
719
- return [];
720
- return value.filter((item) => !!item &&
721
- typeof item === "object" &&
722
- typeof item.file === "string");
723
- }
724
- function asNumber(value) {
98
+ export function asNumber(value) {
725
99
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
726
100
  }
727
- function isSubset(left, right) {
728
- for (const item of left) {
729
- if (!right.has(item))
730
- return false;
731
- }
732
- return true;
733
- }
734
- function setsEqual(left, right) {
735
- return left.size === right.size && isSubset(left, right);
101
+ export function isOutputMarkdownFile(filePath) {
102
+ return extname(filePath) === ".md" && basename(filePath) !== "AGENTS.md";
736
103
  }
737
- function setIntersectionSize(left, right) {
738
- let count = 0;
739
- for (const item of left) {
740
- if (right.has(item))
741
- count += 1;
104
+ export function safeReadText(filePath) {
105
+ try {
106
+ return readFileSync(filePath, "utf-8");
742
107
  }
743
- return count;
744
- }
745
- function dedupeStrings(values) {
746
- return Array.from(new Set(values));
747
- }
748
- function extractSection(content, heading) {
749
- const pattern = new RegExp(`(?:^|\\n)## ${escapeRegExp(heading)}\\s*\\n([\\s\\S]*?)(?=\\n## |\\s*$)`);
750
- const match = content.match(pattern);
751
- return match?.[1]?.trim() ?? null;
752
- }
753
- function sectionMatchesInterfaceContract(section, contractType) {
754
- switch (contractType) {
755
- case "interface-retrieval":
756
- return sectionHasAnyMarker(section, [
757
- "Filter criteria",
758
- "Categories:",
759
- "Priority evidence tiers:",
760
- "Truth modes to prioritize or downweight:",
761
- "Key entities:",
762
- "Expected relevant file count:",
763
- ]);
764
- case "interface-analysis":
765
- return sectionHasAnyMarker(section, [
766
- "Batching:",
767
- "Claims:",
768
- "Entities:",
769
- "extract",
770
- ]);
771
- case "interface-output":
772
- return sectionHasAnyMarker(section, [
773
- "home.md",
774
- "Output files",
775
- "briefs/",
776
- "knowledge/",
777
- ]);
778
- default:
779
- return false;
108
+ catch {
109
+ return null;
780
110
  }
781
111
  }
782
- function countMatches(content, pattern) {
783
- return [...content.matchAll(pattern)].length;
784
- }
785
- function escapeRegExp(value) {
786
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
787
- }
112
+ // ---------------------------------------------------------------------------
113
+ // Private helpers used only by shared functions above
114
+ // ---------------------------------------------------------------------------
788
115
  function dedupeFiles(dirPaths) {
789
116
  const seen = new Set();
790
117
  const files = [];
@@ -810,15 +137,10 @@ function countAbstractWords(frontmatter, body) {
810
137
  const match = body.match(/^## Abstract\s+([\s\S]*?)(?:\n## |\n# |$)/m);
811
138
  if (!match)
812
139
  return 0;
813
- return countWords(match[1]);
814
- }
815
- function safeReadText(filePath) {
816
- try {
817
- return readFileSync(filePath, "utf-8");
818
- }
819
- catch {
820
- return null;
821
- }
140
+ const abstractText = match[1];
141
+ if (!abstractText)
142
+ return 0;
143
+ return countWords(abstractText);
822
144
  }
823
145
  function countWords(text) {
824
146
  return text
@@ -829,7 +151,9 @@ function countWords(text) {
829
151
  function noteName(filePath) {
830
152
  return basename(filePath, extname(filePath));
831
153
  }
832
- function isOutputMarkdownFile(filePath) {
833
- return extname(filePath) === ".md" && basename(filePath) !== "AGENTS.md";
834
- }
154
+ // ---------------------------------------------------------------------------
155
+ // Re-exports: everything from validate-kb and validate-interface
156
+ // ---------------------------------------------------------------------------
157
+ export { validateKnowledgeBase, validateKnowledgeBaseSummarize, validateKnowledgeBaseCompile, } from "./validate-kb.js";
158
+ export { validateInterfaceKnowledgeBase, validateInterfacePlan, validateInterfaceRetrieve, extractInterfacePlannedOutputFiles, } from "./validate-interface.js";
835
159
  //# sourceMappingURL=validate.js.map