@nebulacomponents/citable 1.19.0 → 1.21.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 (360) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +12 -1
  3. package/dist/universal/.agents/skills/citable/SKILL.md +11 -2
  4. package/dist/universal/.agents/skills/citable/VERSION +1 -1
  5. package/dist/universal/.agents/skills/citable/manifest.json +24 -12
  6. package/dist/universal/.agents/skills/citable/schemas/action-plan.schema.json +38 -1
  7. package/dist/universal/.agents/skills/citable/schemas/audit-coverage.schema.json +118 -0
  8. package/dist/universal/.agents/skills/citable/schemas/claim-evidence.schema.json +21 -0
  9. package/dist/universal/.agents/skills/citable/schemas/claim-verification.schema.json +18 -0
  10. package/dist/universal/.agents/skills/citable/schemas/collection-result.schema.json +35 -0
  11. package/dist/universal/.agents/skills/citable/schemas/config.schema.json +2 -0
  12. package/dist/universal/.agents/skills/citable/schemas/cro-report.schema.json +8 -1
  13. package/dist/universal/.agents/skills/citable/schemas/derivation.schema.json +18 -0
  14. package/dist/universal/.agents/skills/citable/schemas/external-comparison.schema.json +16 -0
  15. package/dist/universal/.agents/skills/citable/schemas/external-observation.schema.json +32 -0
  16. package/dist/universal/.agents/skills/citable/schemas/finding.schema.json +105 -2
  17. package/dist/universal/.agents/skills/citable/schemas/prompt-set.schema.json +47 -0
  18. package/dist/universal/.agents/skills/citable/schemas/remediation-verification.schema.json +12 -1
  19. package/dist/universal/.agents/skills/citable/schemas/representation-data.schema.json +21 -0
  20. package/dist/universal/.agents/skills/citable/schemas/research-dataset.schema.json +27 -0
  21. package/dist/universal/.agents/skills/citable/schemas/run.schema.json +15 -2
  22. package/dist/universal/.agents/skills/citable/schemas/search-discovery-data.schema.json +20 -0
  23. package/dist/universal/.agents/skills/citable/schemas/search-report.schema.json +8 -1
  24. package/dist/universal/.agents/skills/citable/schemas/summary.schema.json +41 -0
  25. package/dist/universal/.claude/agents/citable/manifest.json +2 -2
  26. package/dist/universal/.claude/skills/citable/SKILL.md +11 -2
  27. package/dist/universal/.claude/skills/citable/VERSION +1 -1
  28. package/dist/universal/.claude/skills/citable/manifest.json +24 -12
  29. package/dist/universal/.claude/skills/citable/schemas/action-plan.schema.json +38 -1
  30. package/dist/universal/.claude/skills/citable/schemas/audit-coverage.schema.json +118 -0
  31. package/dist/universal/.claude/skills/citable/schemas/claim-evidence.schema.json +21 -0
  32. package/dist/universal/.claude/skills/citable/schemas/claim-verification.schema.json +18 -0
  33. package/dist/universal/.claude/skills/citable/schemas/collection-result.schema.json +35 -0
  34. package/dist/universal/.claude/skills/citable/schemas/config.schema.json +2 -0
  35. package/dist/universal/.claude/skills/citable/schemas/cro-report.schema.json +8 -1
  36. package/dist/universal/.claude/skills/citable/schemas/derivation.schema.json +18 -0
  37. package/dist/universal/.claude/skills/citable/schemas/external-comparison.schema.json +16 -0
  38. package/dist/universal/.claude/skills/citable/schemas/external-observation.schema.json +32 -0
  39. package/dist/universal/.claude/skills/citable/schemas/finding.schema.json +105 -2
  40. package/dist/universal/.claude/skills/citable/schemas/prompt-set.schema.json +47 -0
  41. package/dist/universal/.claude/skills/citable/schemas/remediation-verification.schema.json +12 -1
  42. package/dist/universal/.claude/skills/citable/schemas/representation-data.schema.json +21 -0
  43. package/dist/universal/.claude/skills/citable/schemas/research-dataset.schema.json +27 -0
  44. package/dist/universal/.claude/skills/citable/schemas/run.schema.json +15 -2
  45. package/dist/universal/.claude/skills/citable/schemas/search-discovery-data.schema.json +20 -0
  46. package/dist/universal/.claude/skills/citable/schemas/search-report.schema.json +8 -1
  47. package/dist/universal/.claude/skills/citable/schemas/summary.schema.json +41 -0
  48. package/dist/universal/.cursor/skills/citable/SKILL.md +11 -2
  49. package/dist/universal/.cursor/skills/citable/VERSION +1 -1
  50. package/dist/universal/.cursor/skills/citable/manifest.json +24 -12
  51. package/dist/universal/.cursor/skills/citable/schemas/action-plan.schema.json +38 -1
  52. package/dist/universal/.cursor/skills/citable/schemas/audit-coverage.schema.json +118 -0
  53. package/dist/universal/.cursor/skills/citable/schemas/claim-evidence.schema.json +21 -0
  54. package/dist/universal/.cursor/skills/citable/schemas/claim-verification.schema.json +18 -0
  55. package/dist/universal/.cursor/skills/citable/schemas/collection-result.schema.json +35 -0
  56. package/dist/universal/.cursor/skills/citable/schemas/config.schema.json +2 -0
  57. package/dist/universal/.cursor/skills/citable/schemas/cro-report.schema.json +8 -1
  58. package/dist/universal/.cursor/skills/citable/schemas/derivation.schema.json +18 -0
  59. package/dist/universal/.cursor/skills/citable/schemas/external-comparison.schema.json +16 -0
  60. package/dist/universal/.cursor/skills/citable/schemas/external-observation.schema.json +32 -0
  61. package/dist/universal/.cursor/skills/citable/schemas/finding.schema.json +105 -2
  62. package/dist/universal/.cursor/skills/citable/schemas/prompt-set.schema.json +47 -0
  63. package/dist/universal/.cursor/skills/citable/schemas/remediation-verification.schema.json +12 -1
  64. package/dist/universal/.cursor/skills/citable/schemas/representation-data.schema.json +21 -0
  65. package/dist/universal/.cursor/skills/citable/schemas/research-dataset.schema.json +27 -0
  66. package/dist/universal/.cursor/skills/citable/schemas/run.schema.json +15 -2
  67. package/dist/universal/.cursor/skills/citable/schemas/search-discovery-data.schema.json +20 -0
  68. package/dist/universal/.cursor/skills/citable/schemas/search-report.schema.json +8 -1
  69. package/dist/universal/.cursor/skills/citable/schemas/summary.schema.json +41 -0
  70. package/dist/universal/.gemini/skills/citable/SKILL.md +11 -2
  71. package/dist/universal/.gemini/skills/citable/VERSION +1 -1
  72. package/dist/universal/.gemini/skills/citable/manifest.json +24 -12
  73. package/dist/universal/.gemini/skills/citable/schemas/action-plan.schema.json +38 -1
  74. package/dist/universal/.gemini/skills/citable/schemas/audit-coverage.schema.json +118 -0
  75. package/dist/universal/.gemini/skills/citable/schemas/claim-evidence.schema.json +21 -0
  76. package/dist/universal/.gemini/skills/citable/schemas/claim-verification.schema.json +18 -0
  77. package/dist/universal/.gemini/skills/citable/schemas/collection-result.schema.json +35 -0
  78. package/dist/universal/.gemini/skills/citable/schemas/config.schema.json +2 -0
  79. package/dist/universal/.gemini/skills/citable/schemas/cro-report.schema.json +8 -1
  80. package/dist/universal/.gemini/skills/citable/schemas/derivation.schema.json +18 -0
  81. package/dist/universal/.gemini/skills/citable/schemas/external-comparison.schema.json +16 -0
  82. package/dist/universal/.gemini/skills/citable/schemas/external-observation.schema.json +32 -0
  83. package/dist/universal/.gemini/skills/citable/schemas/finding.schema.json +105 -2
  84. package/dist/universal/.gemini/skills/citable/schemas/prompt-set.schema.json +47 -0
  85. package/dist/universal/.gemini/skills/citable/schemas/remediation-verification.schema.json +12 -1
  86. package/dist/universal/.gemini/skills/citable/schemas/representation-data.schema.json +21 -0
  87. package/dist/universal/.gemini/skills/citable/schemas/research-dataset.schema.json +27 -0
  88. package/dist/universal/.gemini/skills/citable/schemas/run.schema.json +15 -2
  89. package/dist/universal/.gemini/skills/citable/schemas/search-discovery-data.schema.json +20 -0
  90. package/dist/universal/.gemini/skills/citable/schemas/search-report.schema.json +8 -1
  91. package/dist/universal/.gemini/skills/citable/schemas/summary.schema.json +41 -0
  92. package/dist/universal/.github/skills/citable/SKILL.md +11 -2
  93. package/dist/universal/.github/skills/citable/VERSION +1 -1
  94. package/dist/universal/.github/skills/citable/manifest.json +24 -12
  95. package/dist/universal/.github/skills/citable/schemas/action-plan.schema.json +38 -1
  96. package/dist/universal/.github/skills/citable/schemas/audit-coverage.schema.json +118 -0
  97. package/dist/universal/.github/skills/citable/schemas/claim-evidence.schema.json +21 -0
  98. package/dist/universal/.github/skills/citable/schemas/claim-verification.schema.json +18 -0
  99. package/dist/universal/.github/skills/citable/schemas/collection-result.schema.json +35 -0
  100. package/dist/universal/.github/skills/citable/schemas/config.schema.json +2 -0
  101. package/dist/universal/.github/skills/citable/schemas/cro-report.schema.json +8 -1
  102. package/dist/universal/.github/skills/citable/schemas/derivation.schema.json +18 -0
  103. package/dist/universal/.github/skills/citable/schemas/external-comparison.schema.json +16 -0
  104. package/dist/universal/.github/skills/citable/schemas/external-observation.schema.json +32 -0
  105. package/dist/universal/.github/skills/citable/schemas/finding.schema.json +105 -2
  106. package/dist/universal/.github/skills/citable/schemas/prompt-set.schema.json +47 -0
  107. package/dist/universal/.github/skills/citable/schemas/remediation-verification.schema.json +12 -1
  108. package/dist/universal/.github/skills/citable/schemas/representation-data.schema.json +21 -0
  109. package/dist/universal/.github/skills/citable/schemas/research-dataset.schema.json +27 -0
  110. package/dist/universal/.github/skills/citable/schemas/run.schema.json +15 -2
  111. package/dist/universal/.github/skills/citable/schemas/search-discovery-data.schema.json +20 -0
  112. package/dist/universal/.github/skills/citable/schemas/search-report.schema.json +8 -1
  113. package/dist/universal/.github/skills/citable/schemas/summary.schema.json +41 -0
  114. package/dist/universal/.kiro/skills/citable/SKILL.md +11 -2
  115. package/dist/universal/.kiro/skills/citable/VERSION +1 -1
  116. package/dist/universal/.kiro/skills/citable/manifest.json +24 -12
  117. package/dist/universal/.kiro/skills/citable/schemas/action-plan.schema.json +38 -1
  118. package/dist/universal/.kiro/skills/citable/schemas/audit-coverage.schema.json +118 -0
  119. package/dist/universal/.kiro/skills/citable/schemas/claim-evidence.schema.json +21 -0
  120. package/dist/universal/.kiro/skills/citable/schemas/claim-verification.schema.json +18 -0
  121. package/dist/universal/.kiro/skills/citable/schemas/collection-result.schema.json +35 -0
  122. package/dist/universal/.kiro/skills/citable/schemas/config.schema.json +2 -0
  123. package/dist/universal/.kiro/skills/citable/schemas/cro-report.schema.json +8 -1
  124. package/dist/universal/.kiro/skills/citable/schemas/derivation.schema.json +18 -0
  125. package/dist/universal/.kiro/skills/citable/schemas/external-comparison.schema.json +16 -0
  126. package/dist/universal/.kiro/skills/citable/schemas/external-observation.schema.json +32 -0
  127. package/dist/universal/.kiro/skills/citable/schemas/finding.schema.json +105 -2
  128. package/dist/universal/.kiro/skills/citable/schemas/prompt-set.schema.json +47 -0
  129. package/dist/universal/.kiro/skills/citable/schemas/remediation-verification.schema.json +12 -1
  130. package/dist/universal/.kiro/skills/citable/schemas/representation-data.schema.json +21 -0
  131. package/dist/universal/.kiro/skills/citable/schemas/research-dataset.schema.json +27 -0
  132. package/dist/universal/.kiro/skills/citable/schemas/run.schema.json +15 -2
  133. package/dist/universal/.kiro/skills/citable/schemas/search-discovery-data.schema.json +20 -0
  134. package/dist/universal/.kiro/skills/citable/schemas/search-report.schema.json +8 -1
  135. package/dist/universal/.kiro/skills/citable/schemas/summary.schema.json +41 -0
  136. package/dist/universal/.opencode/skills/citable/SKILL.md +11 -2
  137. package/dist/universal/.opencode/skills/citable/VERSION +1 -1
  138. package/dist/universal/.opencode/skills/citable/manifest.json +24 -12
  139. package/dist/universal/.opencode/skills/citable/schemas/action-plan.schema.json +38 -1
  140. package/dist/universal/.opencode/skills/citable/schemas/audit-coverage.schema.json +118 -0
  141. package/dist/universal/.opencode/skills/citable/schemas/claim-evidence.schema.json +21 -0
  142. package/dist/universal/.opencode/skills/citable/schemas/claim-verification.schema.json +18 -0
  143. package/dist/universal/.opencode/skills/citable/schemas/collection-result.schema.json +35 -0
  144. package/dist/universal/.opencode/skills/citable/schemas/config.schema.json +2 -0
  145. package/dist/universal/.opencode/skills/citable/schemas/cro-report.schema.json +8 -1
  146. package/dist/universal/.opencode/skills/citable/schemas/derivation.schema.json +18 -0
  147. package/dist/universal/.opencode/skills/citable/schemas/external-comparison.schema.json +16 -0
  148. package/dist/universal/.opencode/skills/citable/schemas/external-observation.schema.json +32 -0
  149. package/dist/universal/.opencode/skills/citable/schemas/finding.schema.json +105 -2
  150. package/dist/universal/.opencode/skills/citable/schemas/prompt-set.schema.json +47 -0
  151. package/dist/universal/.opencode/skills/citable/schemas/remediation-verification.schema.json +12 -1
  152. package/dist/universal/.opencode/skills/citable/schemas/representation-data.schema.json +21 -0
  153. package/dist/universal/.opencode/skills/citable/schemas/research-dataset.schema.json +27 -0
  154. package/dist/universal/.opencode/skills/citable/schemas/run.schema.json +15 -2
  155. package/dist/universal/.opencode/skills/citable/schemas/search-discovery-data.schema.json +20 -0
  156. package/dist/universal/.opencode/skills/citable/schemas/search-report.schema.json +8 -1
  157. package/dist/universal/.opencode/skills/citable/schemas/summary.schema.json +41 -0
  158. package/dist/universal/.pi/agent/skills/citable/SKILL.md +11 -2
  159. package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
  160. package/dist/universal/.pi/agent/skills/citable/manifest.json +24 -12
  161. package/dist/universal/.pi/agent/skills/citable/schemas/action-plan.schema.json +38 -1
  162. package/dist/universal/.pi/agent/skills/citable/schemas/audit-coverage.schema.json +118 -0
  163. package/dist/universal/.pi/agent/skills/citable/schemas/claim-evidence.schema.json +21 -0
  164. package/dist/universal/.pi/agent/skills/citable/schemas/claim-verification.schema.json +18 -0
  165. package/dist/universal/.pi/agent/skills/citable/schemas/collection-result.schema.json +35 -0
  166. package/dist/universal/.pi/agent/skills/citable/schemas/config.schema.json +2 -0
  167. package/dist/universal/.pi/agent/skills/citable/schemas/cro-report.schema.json +8 -1
  168. package/dist/universal/.pi/agent/skills/citable/schemas/derivation.schema.json +18 -0
  169. package/dist/universal/.pi/agent/skills/citable/schemas/external-comparison.schema.json +16 -0
  170. package/dist/universal/.pi/agent/skills/citable/schemas/external-observation.schema.json +32 -0
  171. package/dist/universal/.pi/agent/skills/citable/schemas/finding.schema.json +105 -2
  172. package/dist/universal/.pi/agent/skills/citable/schemas/prompt-set.schema.json +47 -0
  173. package/dist/universal/.pi/agent/skills/citable/schemas/remediation-verification.schema.json +12 -1
  174. package/dist/universal/.pi/agent/skills/citable/schemas/representation-data.schema.json +21 -0
  175. package/dist/universal/.pi/agent/skills/citable/schemas/research-dataset.schema.json +27 -0
  176. package/dist/universal/.pi/agent/skills/citable/schemas/run.schema.json +15 -2
  177. package/dist/universal/.pi/agent/skills/citable/schemas/search-discovery-data.schema.json +20 -0
  178. package/dist/universal/.pi/agent/skills/citable/schemas/search-report.schema.json +8 -1
  179. package/dist/universal/.pi/agent/skills/citable/schemas/summary.schema.json +41 -0
  180. package/dist/universal/.qoder/skills/citable/SKILL.md +11 -2
  181. package/dist/universal/.qoder/skills/citable/VERSION +1 -1
  182. package/dist/universal/.qoder/skills/citable/manifest.json +24 -12
  183. package/dist/universal/.qoder/skills/citable/schemas/action-plan.schema.json +38 -1
  184. package/dist/universal/.qoder/skills/citable/schemas/audit-coverage.schema.json +118 -0
  185. package/dist/universal/.qoder/skills/citable/schemas/claim-evidence.schema.json +21 -0
  186. package/dist/universal/.qoder/skills/citable/schemas/claim-verification.schema.json +18 -0
  187. package/dist/universal/.qoder/skills/citable/schemas/collection-result.schema.json +35 -0
  188. package/dist/universal/.qoder/skills/citable/schemas/config.schema.json +2 -0
  189. package/dist/universal/.qoder/skills/citable/schemas/cro-report.schema.json +8 -1
  190. package/dist/universal/.qoder/skills/citable/schemas/derivation.schema.json +18 -0
  191. package/dist/universal/.qoder/skills/citable/schemas/external-comparison.schema.json +16 -0
  192. package/dist/universal/.qoder/skills/citable/schemas/external-observation.schema.json +32 -0
  193. package/dist/universal/.qoder/skills/citable/schemas/finding.schema.json +105 -2
  194. package/dist/universal/.qoder/skills/citable/schemas/prompt-set.schema.json +47 -0
  195. package/dist/universal/.qoder/skills/citable/schemas/remediation-verification.schema.json +12 -1
  196. package/dist/universal/.qoder/skills/citable/schemas/representation-data.schema.json +21 -0
  197. package/dist/universal/.qoder/skills/citable/schemas/research-dataset.schema.json +27 -0
  198. package/dist/universal/.qoder/skills/citable/schemas/run.schema.json +15 -2
  199. package/dist/universal/.qoder/skills/citable/schemas/search-discovery-data.schema.json +20 -0
  200. package/dist/universal/.qoder/skills/citable/schemas/search-report.schema.json +8 -1
  201. package/dist/universal/.qoder/skills/citable/schemas/summary.schema.json +41 -0
  202. package/dist/universal/.rovodev/skills/citable/SKILL.md +11 -2
  203. package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
  204. package/dist/universal/.rovodev/skills/citable/manifest.json +24 -12
  205. package/dist/universal/.rovodev/skills/citable/schemas/action-plan.schema.json +38 -1
  206. package/dist/universal/.rovodev/skills/citable/schemas/audit-coverage.schema.json +118 -0
  207. package/dist/universal/.rovodev/skills/citable/schemas/claim-evidence.schema.json +21 -0
  208. package/dist/universal/.rovodev/skills/citable/schemas/claim-verification.schema.json +18 -0
  209. package/dist/universal/.rovodev/skills/citable/schemas/collection-result.schema.json +35 -0
  210. package/dist/universal/.rovodev/skills/citable/schemas/config.schema.json +2 -0
  211. package/dist/universal/.rovodev/skills/citable/schemas/cro-report.schema.json +8 -1
  212. package/dist/universal/.rovodev/skills/citable/schemas/derivation.schema.json +18 -0
  213. package/dist/universal/.rovodev/skills/citable/schemas/external-comparison.schema.json +16 -0
  214. package/dist/universal/.rovodev/skills/citable/schemas/external-observation.schema.json +32 -0
  215. package/dist/universal/.rovodev/skills/citable/schemas/finding.schema.json +105 -2
  216. package/dist/universal/.rovodev/skills/citable/schemas/prompt-set.schema.json +47 -0
  217. package/dist/universal/.rovodev/skills/citable/schemas/remediation-verification.schema.json +12 -1
  218. package/dist/universal/.rovodev/skills/citable/schemas/representation-data.schema.json +21 -0
  219. package/dist/universal/.rovodev/skills/citable/schemas/research-dataset.schema.json +27 -0
  220. package/dist/universal/.rovodev/skills/citable/schemas/run.schema.json +15 -2
  221. package/dist/universal/.rovodev/skills/citable/schemas/search-discovery-data.schema.json +20 -0
  222. package/dist/universal/.rovodev/skills/citable/schemas/search-report.schema.json +8 -1
  223. package/dist/universal/.rovodev/skills/citable/schemas/summary.schema.json +41 -0
  224. package/dist/universal/.trae/skills/citable/SKILL.md +11 -2
  225. package/dist/universal/.trae/skills/citable/VERSION +1 -1
  226. package/dist/universal/.trae/skills/citable/manifest.json +24 -12
  227. package/dist/universal/.trae/skills/citable/schemas/action-plan.schema.json +38 -1
  228. package/dist/universal/.trae/skills/citable/schemas/audit-coverage.schema.json +118 -0
  229. package/dist/universal/.trae/skills/citable/schemas/claim-evidence.schema.json +21 -0
  230. package/dist/universal/.trae/skills/citable/schemas/claim-verification.schema.json +18 -0
  231. package/dist/universal/.trae/skills/citable/schemas/collection-result.schema.json +35 -0
  232. package/dist/universal/.trae/skills/citable/schemas/config.schema.json +2 -0
  233. package/dist/universal/.trae/skills/citable/schemas/cro-report.schema.json +8 -1
  234. package/dist/universal/.trae/skills/citable/schemas/derivation.schema.json +18 -0
  235. package/dist/universal/.trae/skills/citable/schemas/external-comparison.schema.json +16 -0
  236. package/dist/universal/.trae/skills/citable/schemas/external-observation.schema.json +32 -0
  237. package/dist/universal/.trae/skills/citable/schemas/finding.schema.json +105 -2
  238. package/dist/universal/.trae/skills/citable/schemas/prompt-set.schema.json +47 -0
  239. package/dist/universal/.trae/skills/citable/schemas/remediation-verification.schema.json +12 -1
  240. package/dist/universal/.trae/skills/citable/schemas/representation-data.schema.json +21 -0
  241. package/dist/universal/.trae/skills/citable/schemas/research-dataset.schema.json +27 -0
  242. package/dist/universal/.trae/skills/citable/schemas/run.schema.json +15 -2
  243. package/dist/universal/.trae/skills/citable/schemas/search-discovery-data.schema.json +20 -0
  244. package/dist/universal/.trae/skills/citable/schemas/search-report.schema.json +8 -1
  245. package/dist/universal/.trae/skills/citable/schemas/summary.schema.json +41 -0
  246. package/dist/universal/.trae-cn/skills/citable/SKILL.md +11 -2
  247. package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
  248. package/dist/universal/.trae-cn/skills/citable/manifest.json +24 -12
  249. package/dist/universal/.trae-cn/skills/citable/schemas/action-plan.schema.json +38 -1
  250. package/dist/universal/.trae-cn/skills/citable/schemas/audit-coverage.schema.json +118 -0
  251. package/dist/universal/.trae-cn/skills/citable/schemas/claim-evidence.schema.json +21 -0
  252. package/dist/universal/.trae-cn/skills/citable/schemas/claim-verification.schema.json +18 -0
  253. package/dist/universal/.trae-cn/skills/citable/schemas/collection-result.schema.json +35 -0
  254. package/dist/universal/.trae-cn/skills/citable/schemas/config.schema.json +2 -0
  255. package/dist/universal/.trae-cn/skills/citable/schemas/cro-report.schema.json +8 -1
  256. package/dist/universal/.trae-cn/skills/citable/schemas/derivation.schema.json +18 -0
  257. package/dist/universal/.trae-cn/skills/citable/schemas/external-comparison.schema.json +16 -0
  258. package/dist/universal/.trae-cn/skills/citable/schemas/external-observation.schema.json +32 -0
  259. package/dist/universal/.trae-cn/skills/citable/schemas/finding.schema.json +105 -2
  260. package/dist/universal/.trae-cn/skills/citable/schemas/prompt-set.schema.json +47 -0
  261. package/dist/universal/.trae-cn/skills/citable/schemas/remediation-verification.schema.json +12 -1
  262. package/dist/universal/.trae-cn/skills/citable/schemas/representation-data.schema.json +21 -0
  263. package/dist/universal/.trae-cn/skills/citable/schemas/research-dataset.schema.json +27 -0
  264. package/dist/universal/.trae-cn/skills/citable/schemas/run.schema.json +15 -2
  265. package/dist/universal/.trae-cn/skills/citable/schemas/search-discovery-data.schema.json +20 -0
  266. package/dist/universal/.trae-cn/skills/citable/schemas/search-report.schema.json +8 -1
  267. package/dist/universal/.trae-cn/skills/citable/schemas/summary.schema.json +41 -0
  268. package/dist/universal/manifest.json +292 -148
  269. package/package.json +1 -1
  270. package/schemas/action-plan.schema.json +38 -1
  271. package/schemas/audit-coverage.schema.json +118 -0
  272. package/schemas/claim-evidence.schema.json +21 -0
  273. package/schemas/claim-verification.schema.json +18 -0
  274. package/schemas/collection-result.schema.json +35 -0
  275. package/schemas/config.schema.json +2 -0
  276. package/schemas/cro-report.schema.json +8 -1
  277. package/schemas/derivation.schema.json +18 -0
  278. package/schemas/external-comparison.schema.json +16 -0
  279. package/schemas/external-observation.schema.json +32 -0
  280. package/schemas/finding.schema.json +105 -2
  281. package/schemas/prompt-set.schema.json +47 -0
  282. package/schemas/remediation-verification.schema.json +12 -1
  283. package/schemas/representation-data.schema.json +21 -0
  284. package/schemas/research-dataset.schema.json +27 -0
  285. package/schemas/run.schema.json +15 -2
  286. package/schemas/search-discovery-data.schema.json +20 -0
  287. package/schemas/search-report.schema.json +8 -1
  288. package/schemas/summary.schema.json +41 -0
  289. package/skill/SKILL.md +11 -2
  290. package/src/acceptance/reproducibility.js +5 -25
  291. package/src/analysis/croRoadmap.js +3 -3
  292. package/src/analysis/strategicRoadmap.js +3 -3
  293. package/src/artifacts/interchange.js +2 -6
  294. package/src/cli/index.js +15 -3
  295. package/src/commands/actionPlan.js +46 -5
  296. package/src/commands/applyRemediation.js +5 -5
  297. package/src/commands/audit.js +75 -8
  298. package/src/commands/ciWorkflow.js +7 -2
  299. package/src/commands/compareSnapshots.js +155 -47
  300. package/src/commands/connect.js +24 -4
  301. package/src/commands/context.js +41 -2
  302. package/src/commands/croSuite.js +51 -13
  303. package/src/commands/delivery.js +7 -7
  304. package/src/commands/governance.js +9 -10
  305. package/src/commands/implementationKit.js +2 -1
  306. package/src/commands/init.js +8 -1
  307. package/src/commands/previewCro.js +1 -1
  308. package/src/commands/prioritize.js +3 -2
  309. package/src/commands/reviews.js +31 -5
  310. package/src/commands/roadmapCmd.js +7 -13
  311. package/src/commands/sweep.js +15 -2
  312. package/src/commands/verifyRemediation.js +94 -19
  313. package/src/connectors/collectionResult.js +80 -0
  314. package/src/connectors/ga4.js +46 -11
  315. package/src/connectors/gsc.js +45 -6
  316. package/src/connectors/index.js +1 -1
  317. package/src/connectors/webflow.js +140 -10
  318. package/src/connectors/wordpress.js +92 -8
  319. package/src/crawler/fetch.js +220 -24
  320. package/src/crawler/resourceValidity.js +119 -0
  321. package/src/crawler/sitemap.js +435 -16
  322. package/src/crawler/sitemapCollector.js +364 -0
  323. package/src/crawler/urlIdentity.js +97 -0
  324. package/src/detectors/agent.js +11 -0
  325. package/src/detectors/ans.js +16 -1
  326. package/src/detectors/arch.js +6 -0
  327. package/src/detectors/claim.js +9 -0
  328. package/src/detectors/crawl.js +7 -0
  329. package/src/detectors/cro.js +21 -0
  330. package/src/detectors/cwv.js +4 -0
  331. package/src/detectors/entity.js +7 -0
  332. package/src/detectors/evd.js +9 -0
  333. package/src/detectors/framework.js +28 -2
  334. package/src/detectors/geoReco.js +16 -0
  335. package/src/detectors/hreflang.js +3 -0
  336. package/src/detectors/lifeMeas.js +9 -0
  337. package/src/detectors/link.js +6 -1
  338. package/src/detectors/page.js +11 -0
  339. package/src/detectors/schemaData.js +16 -1
  340. package/src/detectors/tech.js +34 -3
  341. package/src/discovery/searchConsole.js +186 -0
  342. package/src/evidence/coverage.js +448 -0
  343. package/src/evidence/determination.js +157 -0
  344. package/src/evidence/downstream.js +110 -0
  345. package/src/evidence/external.js +251 -0
  346. package/src/evidence/hashes.js +109 -0
  347. package/src/evidence/run.js +68 -8
  348. package/src/extractor/page.js +29 -2
  349. package/src/extractor/site.js +298 -50
  350. package/src/reporting/executiveCroReport.js +36 -13
  351. package/src/reporting/executiveExport.js +45 -16
  352. package/src/reporting/executiveSearchReport.js +56 -22
  353. package/src/reporting/report.js +56 -9
  354. package/src/representation/prompts.js +117 -0
  355. package/src/representation/representation.js +186 -0
  356. package/src/shared/epistemicLanguage.js +86 -0
  357. package/src/shared/evidenceSourceResolver.js +48 -17
  358. package/src/shared/htmlEscape.js +8 -4
  359. package/src/shared/runPackageVerifier.js +99 -8
  360. package/src/shared/verifiedRunLoader.js +129 -0
@@ -1,63 +1,171 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { readJson } from '../shared/io.js';
4
+ import { loadVerifiedRun } from '../shared/verifiedRunLoader.js';
5
+ import { normalizeUrlIdentity } from '../crawler/urlIdentity.js';
4
6
 
5
- /**
6
- * `citable compare-snapshots [runA runB]` — regression comparison between two audit runs.
7
- * Defaults to the two most recent runs containing findings.json.
8
- */
9
- export function compareSnapshots(root, { runA, runB } = {}) {
10
- const runsDir = path.join(root, '.citable', 'runs');
11
- if (!fs.existsSync(runsDir)) throw new Error('no runs recorded yet');
12
- const runs = fs.readdirSync(runsDir)
13
- .filter((r) => fs.existsSync(path.join(runsDir, r, 'findings.json')))
14
- .sort();
15
- if (!runA || !runB) {
16
- if (runs.length < 2) throw new Error(`need two runs with findings to compare; found ${runs.length}`);
17
- runB = runB ?? runs[runs.length - 1];
18
- runA = runA ?? runs[runs.length - 2];
7
+ export const COMPARISON_STATES = Object.freeze([
8
+ 'persisted', 'resolved', 'new', 'changed', 'not_comparable', 'not_reobserved', 'indeterminate',
9
+ ]);
10
+
11
+ function normalizeCandidate(value) {
12
+ if (!value) return null;
13
+ try { return normalizeUrlIdentity(String(value)); } catch { return String(value); }
14
+ }
15
+
16
+ /** Keep effective/requested identity distinct; canonical is never fetch identity. */
17
+ export function resourceIdentityFor(findingOrPage) {
18
+ const subject = findingOrPage?.subject || findingOrPage || {};
19
+ const identity = subject.urlIdentity || subject.url_identity || {};
20
+ const ids = new Set([
21
+ subject.resource_id, identity.resource_id,
22
+ subject.classification?.resource_id, subject.evaluation?.resource_id,
23
+ ...(findingOrPage?.evidence_scope?.resource_ids || []),
24
+ ].filter(Boolean).map(String));
25
+ const requested = normalizeCandidate(identity.requested?.normalized_url || identity.requested?.url || subject.requested_url);
26
+ const effective = normalizeCandidate(identity.effective?.normalized_url || identity.effective?.url
27
+ || subject.effective_url || subject.normalized_url || subject.url || subject.identifier);
28
+ const canonical = normalizeCandidate(identity.canonical?.normalized_url || identity.canonical?.url || subject.canonical_url);
29
+ const fallback = new Set([requested, effective, normalizeCandidate(subject.url || subject.identifier)].filter(Boolean));
30
+ return { resourceIds: ids, requested, effective, canonical, fallbackUrls: fallback };
31
+ }
32
+
33
+ export function sameResourceIdentity(left, right) {
34
+ const a = resourceIdentityFor(left); const b = resourceIdentityFor(right);
35
+ if (a.resourceIds.size && b.resourceIds.size) {
36
+ return [...a.resourceIds].some((id) => b.resourceIds.has(id));
19
37
  }
20
- const load = (r) => {
21
- const dir = path.join(runsDir, r);
22
- if (!fs.existsSync(path.join(dir, 'findings.json'))) throw new Error(`run ${r} has no findings.json`);
23
- return {
24
- findings: readJson(path.join(dir, 'findings.json')),
25
- manifest: readJson(path.join(dir, 'manifest.json')),
26
- };
27
- };
28
- const a = load(runA);
29
- const b = load(runB);
30
- const changedKeys = (left = {}, right = {}) => [...new Set([...Object.keys(left), ...Object.keys(right)])].filter((key) => left[key] !== right[key]);
38
+ if (a.effective && b.effective) return a.effective === b.effective;
39
+ if (a.requested && b.requested) return a.requested === b.requested;
40
+ // URL fallback is allowed only when each side has exactly one candidate.
41
+ return a.fallbackUrls.size === 1 && b.fallbackUrls.size === 1
42
+ && [...a.fallbackUrls][0] === [...b.fallbackUrls][0];
43
+ }
44
+
45
+ function findingKey(finding) {
46
+ const identity = resourceIdentityFor(finding);
47
+ const id = identity.resourceIds.size ? [...identity.resourceIds].sort().join(',')
48
+ : identity.effective || identity.requested || (identity.fallbackUrls.size === 1 ? [...identity.fallbackUrls][0] : JSON.stringify(finding?.subject || {}));
49
+ return `${finding?.detector_id || ''}|${id}`;
50
+ }
51
+
52
+ function loadCoverage(dir) {
53
+ const file = path.join(dir, 'coverage.json');
54
+ return fs.existsSync(file) ? readJson(file) : null;
55
+ }
56
+
57
+ function coverageResource(coverage, finding) {
58
+ if (!Array.isArray(coverage?.resources)) return null;
59
+ const target = resourceIdentityFor(finding);
60
+ const byId = coverage.resources.filter((resource) => {
61
+ const ids = resourceIdentityFor(resource).resourceIds;
62
+ return [...target.resourceIds].some((id) => ids.has(id));
63
+ });
64
+ if (target.resourceIds.size) return byId.length === 1 ? byId[0] : null;
65
+ const byUrl = coverage.resources.filter((resource) => sameResourceIdentity(finding, resource));
66
+ return byUrl.length === 1 ? byUrl[0] : null;
67
+ }
68
+
69
+ export function assessReobservation(finding, coverage) {
70
+ if (!coverage) return { state: 'not_reobserved', reason: 'comparison_run_has_no_coverage_artifact' };
71
+ const resource = coverageResource(coverage, finding);
72
+ if (!resource) return { state: 'not_reobserved', reason: 'resource_not_reobserved_in_comparison_run' };
73
+ if (['failed', 'indeterminate', 'unvisited', 'valid_but_unevaluated'].includes(resource.state)) {
74
+ return { state: resource.state === 'indeterminate' ? 'indeterminate' : 'not_reobserved', reason: `resource_state_${resource.state}`, resource };
75
+ }
76
+ if (resource.state !== 'evaluated') return { state: 'not_reobserved', reason: `resource_state_${resource.state || 'unknown'}`, resource };
77
+ if (finding?.evidence_scope?.requirement === 'exhaustive_requested_scope' && coverage.coverage_status !== 'complete') {
78
+ return { state: 'indeterminate', reason: 'exhaustive_scope_coverage_incomplete', resource };
79
+ }
80
+ return { state: 'resolved', reason: 'resource_reobserved_with_sufficient_coverage', resource };
81
+ }
82
+
83
+ function changedKeys(left = {}, right = {}) {
84
+ return [...new Set([...Object.keys(left), ...Object.keys(right)])].filter((key) => left[key] !== right[key]);
85
+ }
86
+
87
+ function comparabilityFor(a, b) {
31
88
  const changeDimensions = {
32
89
  resource_changed: changedKeys(a.manifest.input_hashes, b.manifest.input_hashes),
33
90
  evidence_artifacts_changed: changedKeys(a.manifest.output_hashes, b.manifest.output_hashes),
34
91
  detector_set_changed: JSON.stringify(a.manifest.detectors_run) !== JSON.stringify(b.manifest.detectors_run),
35
92
  configuration_changed: a.manifest.configuration_hash !== b.manifest.configuration_hash,
36
- observation_method_changed: a.manifest.command !== b.manifest.command || JSON.stringify(a.manifest.argv) !== JSON.stringify(b.manifest.argv) || a.manifest.target?.kind !== b.manifest.target?.kind,
93
+ observation_method_changed: a.manifest.command !== b.manifest.command
94
+ || JSON.stringify(a.manifest.argv) !== JSON.stringify(b.manifest.argv)
95
+ || a.manifest.target?.kind !== b.manifest.target?.kind,
37
96
  tool_changed: a.manifest.tool_version !== b.manifest.tool_version,
38
97
  external_system_may_have_changed: a.manifest.target?.kind === 'url' || b.manifest.target?.kind === 'url',
39
98
  };
40
- const key = (f) => `${f.detector_id}|${f.subject.identifier}|${f.observation.summary}`;
41
- const aKeys = new Map(a.findings.map((f) => [key(f), f]));
42
- const bKeys = new Map(b.findings.map((f) => [key(f), f]));
43
- const regressions = [...bKeys.entries()].filter(([k]) => !aKeys.has(k)).map(([, f]) => f);
44
- const resolved = [...aKeys.entries()].filter(([k]) => !bKeys.has(k)).map(([, f]) => f);
45
- const persisting = [...bKeys.entries()].filter(([k]) => aKeys.has(k)).map(([, f]) => f);
46
99
  return {
47
- runA, runB,
48
- baseline_timestamp: a.manifest.timestamp,
49
- comparison_timestamp: b.manifest.timestamp,
50
- regressions, resolved, persisting,
51
- comparability: {
52
- comparable: !changeDimensions.detector_set_changed && !changeDimensions.configuration_changed && !changeDimensions.observation_method_changed && !changeDimensions.tool_changed,
53
- change_dimensions: changeDimensions,
54
- limitation: 'These dimensions identify observed differences between run envelopes; they do not establish what caused a finding change.',
55
- },
56
- summary: {
57
- new_findings: regressions.length,
58
- resolved_findings: resolved.length,
59
- persisting_findings: persisting.length,
60
- regression_critical_or_high: regressions.filter((f) => ['critical', 'high'].includes(f.classification.severity)).length,
61
- },
100
+ comparable: !changeDimensions.detector_set_changed
101
+ && !changeDimensions.configuration_changed
102
+ && !changeDimensions.observation_method_changed
103
+ && !changeDimensions.tool_changed,
104
+ change_dimensions: changeDimensions,
105
+ limitation: 'These dimensions identify observed differences between run envelopes; they do not establish what caused a finding change.',
106
+ };
107
+ }
108
+
109
+ function withState(finding, state, reason, extra = {}) {
110
+ return { ...finding, comparison_state: state, comparison_provenance: { reason, ...extra } };
111
+ }
112
+
113
+ /** Compare findings only after checking that their responsible resource was reobserved. */
114
+ export function compareSnapshots(root, { runA, runB } = {}) {
115
+ const runsDir = path.join(root, '.citable', 'runs');
116
+ if (!fs.existsSync(runsDir)) throw new Error('no runs recorded yet');
117
+ const runs = fs.readdirSync(runsDir).filter((r) => fs.existsSync(path.join(runsDir, r, 'findings.json'))).sort();
118
+ if (!runA || !runB) {
119
+ if (runs.length < 2) throw new Error(`need two runs with findings to compare; found ${runs.length}`);
120
+ runB = runB ?? runs[runs.length - 1];
121
+ runA = runA ?? runs[runs.length - 2];
122
+ }
123
+ const load = (runId) => {
124
+ const dir = path.join(runsDir, runId);
125
+ if (!fs.existsSync(path.join(dir, 'findings.json'))) throw new Error(`run ${runId} has no findings.json`);
126
+ const loaded = loadVerifiedRun(dir, { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false });
127
+ return { findings: loaded.findings, manifest: loaded.manifest, coverage: loaded.coverage };
128
+ };
129
+ const a = load(runA); const b = load(runB);
130
+ const comparability = comparabilityFor(a, b);
131
+ const aFindings = a.findings;
132
+ const bFindings = b.findings;
133
+ const matchedA = new Set();
134
+ const result = {
135
+ runA, runB, baseline_timestamp: a.manifest.timestamp, comparison_timestamp: b.manifest.timestamp,
136
+ regressions: [], resolved: [], persisting: [], new: [], changed: [], not_comparable: [], not_reobserved: [], indeterminate: [],
137
+ comparability,
138
+ };
139
+ for (const finding of bFindings) {
140
+ const priorIndex = aFindings.findIndex((candidate, index) => !matchedA.has(index)
141
+ && candidate.detector_id === finding.detector_id && sameResourceIdentity(candidate, finding));
142
+ const prior = priorIndex >= 0 ? aFindings[priorIndex] : null;
143
+ if (priorIndex >= 0) matchedA.add(priorIndex);
144
+ if (!prior) {
145
+ const item = withState(finding, 'new', 'finding_not_present_in_baseline');
146
+ result.new.push(item); result.regressions.push(item); continue;
147
+ }
148
+ if (!comparability.comparable) result.not_comparable.push(withState(finding, 'not_comparable', 'run_envelopes_not_comparable'));
149
+ else if (JSON.stringify(prior.observation) !== JSON.stringify(finding.observation)) result.changed.push(withState(finding, 'changed', 'finding_observation_changed'));
150
+ else result.persisting.push(withState(finding, 'persisted', 'finding_observation_persisted'));
151
+ }
152
+ for (let index = 0; index < aFindings.length; index += 1) {
153
+ const finding = aFindings[index];
154
+ if (matchedA.has(index)) continue;
155
+ if (!comparability.comparable) { result.not_comparable.push(withState(finding, 'not_comparable', 'run_envelopes_not_comparable')); continue; }
156
+ const state = assessReobservation(finding, b.coverage);
157
+ const item = withState(finding, state.state, state.reason, {
158
+ resource: state.resource?.normalized_url || null, comparison_coverage_status: b.coverage?.coverage_status || 'not_available',
159
+ });
160
+ if (state.state === 'resolved') result.resolved.push(item);
161
+ else if (state.state === 'indeterminate') result.indeterminate.push(item);
162
+ else result.not_reobserved.push(item);
163
+ }
164
+ result.summary = {
165
+ new_findings: result.new.length, changed_findings: result.changed.length, resolved_findings: result.resolved.length,
166
+ persisting_findings: result.persisting.length, not_comparable_findings: result.not_comparable.length,
167
+ not_reobserved_findings: result.not_reobserved.length, indeterminate_findings: result.indeterminate.length,
168
+ regression_critical_or_high: result.regressions.filter((f) => ['critical', 'high'].includes(f.classification?.severity)).length,
62
169
  };
170
+ return result;
63
171
  }
@@ -79,16 +79,37 @@ export async function syncConnection(root, { connectionId, startDate, endDate, a
79
79
  }
80
80
  throw error;
81
81
  }
82
+ // Connector collection may preserve a partial result even when the provider
83
+ // reports an authorization/quota state. Keep the result available to direct
84
+ // connector callers, while retaining the command's legacy state transition.
85
+ if (result.connectorError?.connectorState) {
86
+ const error = result.connectorError;
87
+ const failed = { ...loaded.registries.connections, entries: loaded.registries.connections.entries.map((item) => item.connection_id === connectionId ? { ...item, state: error.connectorState, limitations: [...(result.collection?.errors || []), 'Connector synchronization did not complete; inspect collection errors.'] } : item) };
88
+ saveRegistry(root, 'connections', failed);
89
+ throw error;
90
+ }
91
+ const collection = result.collection || {
92
+ schema_version: 1,
93
+ items: result.rows,
94
+ pagination_state: { pages_requested: 1, pages_retrieved: 1 },
95
+ provider_reported_total: null,
96
+ retrieved_total: result.rows.length,
97
+ coverage_status: 'indeterminate',
98
+ continuation_state: null,
99
+ limitations: ['Connector did not provide a collection contract.'],
100
+ errors: [],
101
+ };
102
+ const collectionLimitations = [...collection.limitations, ...collection.errors];
82
103
  const observations = result.rows.map((row) => envelope('metric', {
83
104
  metric_id: row.metric.metric_id, provider: connector.provider, external_name: row.metric.external_name,
84
105
  value: Number(row.value), unit: row.metric.unit === 'custom' ? row.metric.custom_unit : row.metric.unit,
85
106
  aggregation: row.metric.aggregation, observed_at: row.observed_at, period_start: row.observed_at,
86
107
  period_end: row.observed_at, dimensions: row.dimensions,
87
- }, { method: 'live_api', source: connector.provider, limitations: [...row.metric.limitations, ...result.limitations] }));
108
+ }, { method: 'live_api', source: connector.provider, limitations: [...row.metric.limitations, ...collectionLimitations] }));
88
109
  const run = observationRun(root, 'connect sync', loaded.connection.property_id, observations);
89
- const updated = { ...loaded.registries.connections, entries: loaded.registries.connections.entries.map((item) => item.connection_id === connectionId ? { ...item, state: 'synchronized', last_synchronized_at: new Date().toISOString(), cursor: result.cursor, limitations: result.limitations } : item) };
110
+ const updated = { ...loaded.registries.connections, entries: loaded.registries.connections.entries.map((item) => item.connection_id === connectionId ? { ...item, state: ['truncated', 'indeterminate'].includes(collection.coverage_status) ? 'partial' : 'synchronized', last_synchronized_at: new Date().toISOString(), cursor: result.cursor, limitations: collectionLimitations } : item) };
90
111
  saveRegistry(root, 'connections', updated);
91
- return { ...run, connection_id: connectionId, provider: connector.provider };
112
+ return { ...run, connection_id: connectionId, provider: connector.provider, collection };
92
113
  }
93
114
 
94
115
  export async function readCmsContent(root, { connectionId, targetId, accessToken, env, fetchImpl } = {}) {
@@ -119,4 +140,3 @@ export async function applyCmsRemediation(root, { connectionId, input, write = f
119
140
 
120
141
  export { submitIndexNow, verifyHostKey, buildIndexNowPayload, normalizeHost, parseUrlsInput } from '../connectors/indexnow.js';
121
142
  export { collectMcpEvidence } from '../connectors/mcp/pilot.js';
122
-
@@ -4,8 +4,44 @@ import { readYaml, readJson, sha256, parseRefDate } from '../shared/io.js';
4
4
  import { loadRegistries } from '../registries/index.js';
5
5
  import { buildSiteFromDir, buildSiteFromUrl } from '../extractor/site.js';
6
6
 
7
+ const AUDIT_BUDGET_DEFAULTS = Object.freeze({ maxPages: 500, timeBudgetSeconds: 1800 });
8
+
9
+ function validateBudget(value, name, maximum) {
10
+ if (!Number.isInteger(value) || value < 1 || value > maximum) {
11
+ throw new Error(`${name} must be an integer from 1 to ${maximum}; received ${String(value)}`);
12
+ }
13
+ return value;
14
+ }
15
+
16
+ /** Resolve bounded network collection budgets: command options > config > safe defaults. */
17
+ export function resolveAuditBudgets(config = {}, options = {}) {
18
+ const configured = config.audit ?? {};
19
+ const maxPagesFromOptions = options.maxPages !== undefined;
20
+ const timeBudgetFromOptions = options.timeBudgetSeconds !== undefined;
21
+ const maxPages = maxPagesFromOptions
22
+ ? options.maxPages
23
+ : configured.max_pages !== undefined ? configured.max_pages : AUDIT_BUDGET_DEFAULTS.maxPages;
24
+ const timeBudgetSeconds = timeBudgetFromOptions
25
+ ? options.timeBudgetSeconds
26
+ : configured.time_budget_seconds !== undefined
27
+ ? configured.time_budget_seconds
28
+ : AUDIT_BUDGET_DEFAULTS.timeBudgetSeconds;
29
+
30
+ return {
31
+ maxPages: validateBudget(maxPages, maxPagesFromOptions ? '--max-pages' : 'audit.max_pages', 10_000),
32
+ timeBudgetSeconds: validateBudget(
33
+ timeBudgetSeconds,
34
+ timeBudgetFromOptions ? '--time-budget-seconds' : 'audit.time_budget_seconds',
35
+ 86_400,
36
+ ),
37
+ };
38
+ }
39
+
7
40
  /** Assemble the shared execution context used by audit-family commands. */
8
- export async function buildContext(root, { target, baseUrl, refDate, viewport = null } = {}) {
41
+ export async function buildContext(root, {
42
+ target, baseUrl, refDate, viewport = null,
43
+ maxPages, timeBudgetSeconds, fetcher, concurrency = 1,
44
+ } = {}) {
9
45
  const warnings = [];
10
46
  const configFile = path.join(root, '.citable', 'config.yaml');
11
47
  const config = fs.existsSync(configFile) ? readYaml(configFile) : { version: 1 };
@@ -13,10 +49,12 @@ export async function buildContext(root, { target, baseUrl, refDate, viewport =
13
49
  warnings.push(...problems);
14
50
 
15
51
  let site = null;
52
+ let auditBudgets = null;
16
53
  const resolvedBase = baseUrl ?? config.site?.base_url ?? 'https://example.test';
17
54
  if (target) {
18
55
  if (/^https?:\/\//.test(target)) {
19
- site = await buildSiteFromUrl(target);
56
+ auditBudgets = resolveAuditBudgets(config, { maxPages, timeBudgetSeconds });
57
+ site = await buildSiteFromUrl(target, { ...auditBudgets, fetcher, concurrency });
20
58
  } else if (fs.existsSync(target)) {
21
59
  site = buildSiteFromDir(target, { baseUrl: resolvedBase });
22
60
  } else {
@@ -70,6 +108,7 @@ export async function buildContext(root, { target, baseUrl, refDate, viewport =
70
108
  promptResults: promptResults.length ? promptResults : null,
71
109
  observations: observations.length ? observations : null,
72
110
  snapshots,
111
+ auditBudgets,
73
112
  refDate: parseRefDate(refDate),
74
113
  viewport,
75
114
  hashPage: (p) => sha256(p.text),
@@ -9,6 +9,7 @@ import { analyzeBehavioralTelemetry } from '../analysis/behavioral.js';
9
9
  import { generateExperimentBacklog, formatBacklogMarkdown } from './croBacklog.js';
10
10
  import { buildIceMatrix, formatIceMatrixOutput } from '../analysis/iceMatrix.js';
11
11
  import { buildCroRoadmap, formatCroRoadmapMarkdown } from '../analysis/croRoadmap.js';
12
+ import { downstreamEnvelope, completeLocalCoverage } from '../evidence/downstream.js';
12
13
 
13
14
  /**
14
15
  * Full End-to-End CRO Intelligence Suite Audit
@@ -22,6 +23,8 @@ export async function auditCroSuite(root, { target, baseUrl, refDate, input, fun
22
23
  const { findings } = runDetectors(croDetectors, ctx);
23
24
 
24
25
  const pages = indexTargets(ctx);
26
+ const coverage = ctx.coverage || (pages.length > 0 ? completeLocalCoverage(pages, ctx.site.baseUrl) : null);
27
+ const evidence = downstreamEnvelope(coverage);
25
28
 
26
29
  // 2. Above-the-fold clarity, trust, cognitive load, offer architecture per page
27
30
  const auditedPages = pages.map((page) => auditPageCro(page, ctx));
@@ -33,21 +36,29 @@ export async function auditCroSuite(root, { target, baseUrl, refDate, input, fun
33
36
  });
34
37
  const targetGroup = commercialPages.length > 0 ? commercialPages : auditedPages;
35
38
 
36
- const avgAtfClarity = targetGroup.length > 0
39
+ const avgAtfClarity = targetGroup.length > 0 && evidence.determination_status !== 'indeterminate'
37
40
  ? Math.round(targetGroup.reduce((acc, p) => acc + p.atf_clarity.score, 0) / targetGroup.length)
38
- : 0;
41
+ : null;
39
42
 
40
- const avgTrustScore = targetGroup.length > 0
43
+ const avgTrustScore = targetGroup.length > 0 && evidence.determination_status !== 'indeterminate'
41
44
  ? Math.round(targetGroup.reduce((acc, p) => acc + p.trust_and_credibility.score, 0) / targetGroup.length)
42
- : 0;
45
+ : null;
43
46
 
44
- const avgConversionReadiness = targetGroup.length > 0
47
+ const avgConversionReadiness = targetGroup.length > 0 && evidence.determination_status !== 'indeterminate'
45
48
  ? Math.round(targetGroup.reduce((acc, p) => acc + p.conversion_readiness_score, 0) / targetGroup.length)
46
- : 0;
49
+ : null;
47
50
 
48
51
  // 3. End-to-end Funnel and Conversion-Path Analysis
49
52
  const declaredFunnel = (ctx.registries?.funnels?.entries || []).find((f) => funnelId ? f.funnel_id === funnelId : true) || null;
50
53
  const funnelAnalysis = analyzeConversionFunnel(ctx.site.pages, declaredFunnel);
54
+ const qualify = (value) => evidence.determination_status === 'supported' ? value : null;
55
+ const qualifiedFunnel = {
56
+ ...funnelAnalysis,
57
+ funnel_health_score: qualify(funnelAnalysis.funnel_health_score),
58
+ determination_status: evidence.determination_status,
59
+ coverage_status: evidence.coverage_status,
60
+ limitations: evidence.limitations,
61
+ };
51
62
 
52
63
  // 4. Behavioral Evidence Analysis (if telemetry file provided)
53
64
  let behavioralAnalysis = null;
@@ -65,9 +76,26 @@ export async function auditCroSuite(root, { target, baseUrl, refDate, input, fun
65
76
 
66
77
  // 5. Experiment Backlog with Falsifiable Hypotheses
67
78
  const experimentBacklog = generateExperimentBacklog(findings);
79
+ const qualifiedExperimentBacklog = {
80
+ ...experimentBacklog,
81
+ total_experiments: qualify(experimentBacklog.total_experiments),
82
+ determination_status: evidence.determination_status,
83
+ coverage_status: evidence.coverage_status,
84
+ limitations: evidence.limitations,
85
+ };
68
86
 
69
87
  // 6. Impact / Effort / Confidence (ICE) Prioritization Matrix
70
88
  const iceMatrix = buildIceMatrix(findings, { type: 'findings' });
89
+ const qualifiedIceMatrix = {
90
+ ...iceMatrix,
91
+ determination_status: evidence.determination_status,
92
+ coverage_status: evidence.coverage_status,
93
+ limitations: evidence.limitations,
94
+ summary: {
95
+ ...iceMatrix.summary,
96
+ quick_wins_count: qualify(iceMatrix.summary.quick_wins_count),
97
+ },
98
+ };
71
99
 
72
100
  // 7. 30 / 90 / 180-Day CRO Strategic Roadmap tied to Measurable Conversion Outcomes
73
101
  const targetDomain = ctx.site.baseUrl ? new URL(ctx.site.baseUrl).hostname : 'target-site';
@@ -75,26 +103,36 @@ export async function auditCroSuite(root, { target, baseUrl, refDate, input, fun
75
103
  findings,
76
104
  targetDomain,
77
105
  });
106
+ const qualifiedRoadmap = {
107
+ ...croRoadmap,
108
+ determination_status: evidence.determination_status,
109
+ coverage_status: evidence.coverage_status,
110
+ limitations: evidence.limitations,
111
+ };
78
112
 
79
113
  const result = {
80
114
  fact_status: 'modeled_cro_evaluation',
81
115
  target: ctx.site.baseUrl || target,
82
116
  total_pages_audited: pages.length,
117
+ coverage_status: evidence.coverage_status,
118
+ determination_status: evidence.determination_status,
119
+ evidence_scope: evidence.evidence_scope,
120
+ limitations: evidence.limitations,
83
121
  summary: {
84
122
  conversion_readiness_score: avgConversionReadiness,
85
123
  atf_clarity_score: avgAtfClarity,
86
124
  trust_credibility_score: avgTrustScore,
87
- funnel_health_score: funnelAnalysis.funnel_health_score,
125
+ funnel_health_score: qualifiedFunnel.funnel_health_score,
88
126
  total_cro_findings: findings.length,
89
- planned_experiments: experimentBacklog.total_experiments,
90
- quick_wins_count: iceMatrix.summary.quick_wins_count,
127
+ planned_experiments: qualifiedExperimentBacklog.total_experiments,
128
+ quick_wins_count: qualifiedIceMatrix.summary.quick_wins_count,
91
129
  },
92
- funnel_analysis: funnelAnalysis,
130
+ funnel_analysis: qualifiedFunnel,
93
131
  pages: auditedPages,
94
132
  behavioral_telemetry: behavioralAnalysis,
95
- experiment_backlog: experimentBacklog,
96
- ice_matrix: iceMatrix,
97
- strategic_roadmap: croRoadmap,
133
+ experiment_backlog: qualifiedExperimentBacklog,
134
+ ice_matrix: qualifiedIceMatrix,
135
+ strategic_roadmap: qualifiedRoadmap,
98
136
  findings: findings.map((f) => ({
99
137
  detector_id: f.detector_id,
100
138
  severity: f.classification.severity,
@@ -6,6 +6,7 @@ import { compareSnapshots } from './compareSnapshots.js';
6
6
  import { buildAlertPayload, dispatchAlertWebhook, filterAlerts } from '../monitoring/alertDelivery.js';
7
7
  import { loadRegistries } from '../registries/index.js';
8
8
  import { readJson, sha256, writeJson, nowIso } from '../shared/io.js';
9
+ import { loadVerifiedRun } from '../shared/verifiedRunLoader.js';
9
10
 
10
11
  const PKG = readJson(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../package.json'));
11
12
 
@@ -114,12 +115,11 @@ function annotationLevel(severity) {
114
115
  export function projectGithub(root, { runId } = {}) {
115
116
  if (!runId) throw new Error('projection requires a source run id');
116
117
  const runDir = path.join(root, '.citable', 'runs', runId);
117
- const findingsFile = path.join(runDir, 'findings.json');
118
- const manifestFile = path.join(runDir, 'manifest.json');
119
- if (!fs.existsSync(findingsFile) || !fs.existsSync(manifestFile)) throw new Error(`run ${runId} is missing findings or manifest evidence`);
120
- const findingsBytes = fs.readFileSync(findingsFile);
121
- const manifestBytes = fs.readFileSync(manifestFile);
122
- const annotations = JSON.parse(findingsBytes).map((finding) => ({
118
+ const loaded = loadVerifiedRun(runDir, { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false });
119
+ const findingsHash = loaded.artifactHashes?.['findings.json'];
120
+ const manifestHash = loaded.artifactHashes?.['manifest.json'];
121
+ if (!findingsHash || !manifestHash) throw new Error(`run ${runId} has no loader-verified findings or manifest hash`);
122
+ const annotations = loaded.findings.map((finding) => ({
123
123
  level: annotationLevel(finding.classification.severity),
124
124
  title: `${finding.detector_id}: ${finding.classification.severity}`,
125
125
  message: finding.observation.summary,
@@ -127,7 +127,7 @@ export function projectGithub(root, { runId } = {}) {
127
127
  line: finding.subject.source_location ? Number(String(finding.subject.source_location).match(/\d+/)?.[0]) || null : null,
128
128
  finding_id: finding.finding_id,
129
129
  }));
130
- const projection = { source_run_id: runId, source_manifest_hash: sha256(manifestBytes), source_findings_hash: sha256(findingsBytes), generated_at: nowIso(), projection: 'github-check-annotations', authoritative: false, annotations };
130
+ const projection = { source_run_id: runId, source_manifest_hash: manifestHash, source_findings_hash: findingsHash, generated_at: nowIso(), projection: 'github-check-annotations', authoritative: false, annotations };
131
131
  const dir = path.join(root, '.citable', 'projections', 'github', runId);
132
132
  writeJson(path.join(dir, 'annotations.json'), projection);
133
133
  return { ...projection, dir };
@@ -1,8 +1,8 @@
1
- import fs from 'node:fs';
2
1
  import path from 'node:path';
2
+ import { loadVerifiedRun } from '../shared/verifiedRunLoader.js';
3
3
  import { createRun } from '../evidence/run.js';
4
4
  import { loadRegistries, checkReferentialIntegrity } from '../registries/index.js';
5
- import { readJson, sha256, parseRefDate } from '../shared/io.js';
5
+ import { sha256, parseRefDate } from '../shared/io.js';
6
6
 
7
7
  function recordHash(value) {
8
8
  return sha256(JSON.stringify(value));
@@ -79,8 +79,8 @@ export function validateGovernance(root, { refDate } = {}) {
79
79
  const evidence = new Map(registries.evidence.entries.map((item) => [item.evidence_id, item]));
80
80
  const activeByFinding = new Map();
81
81
  for (const exception of registries.exceptions.entries) {
82
- const file = path.join(root, '.citable', 'runs', exception.source_run_id, 'findings.json');
83
- const findings = new Map(fs.existsSync(file) ? readJson(file).map((item) => [item.finding_id, item]) : []);
82
+ const runDir = path.join(root, '.citable', 'runs', exception.source_run_id);
83
+ const findings = new Map(loadVerifiedRun(runDir, { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false }).findings.map((item) => [item.finding_id, item]));
84
84
  const exceptionIssues = exceptionProblems(exception, policies.get(exception.policy_id), reviewers, findings, evidence, reference);
85
85
  problems.push(...exceptionIssues.map((problem) => `exceptions/${exception.exception_id}: ${problem}`));
86
86
  if (exceptionIssues.length === 0) {
@@ -98,10 +98,9 @@ export function validateGovernance(root, { refDate } = {}) {
98
98
 
99
99
  export function evaluateDispositions(root, { runId, refDate } = {}) {
100
100
  if (!runId) throw new Error('source run id is required');
101
- const findingsFile = path.join(root, '.citable', 'runs', runId, 'findings.json');
102
- if (!fs.existsSync(findingsFile)) throw new Error(`source findings not found for run ${runId}`);
103
- const sourceBytes = fs.readFileSync(findingsFile);
104
- const sourceFindings = JSON.parse(sourceBytes);
101
+ const runDir = path.join(root, '.citable', 'runs', runId);
102
+ const sourceRun = loadVerifiedRun(runDir, { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false });
103
+ const sourceFindings = sourceRun.findings;
105
104
  const findings = new Map(sourceFindings.map((item) => [item.finding_id, item]));
106
105
  const { registries, problems } = loadRegistries(root);
107
106
  const integrity = checkReferentialIntegrity(registries);
@@ -130,8 +129,8 @@ export function evaluateDispositions(root, { runId, refDate } = {}) {
130
129
  problems: matches.flatMap((item) => item.problems),
131
130
  };
132
131
  });
133
- const run = createRun(root, { command: 'governance evaluate', argv: [runId], target: { kind: 'registries', location: findingsFile } });
134
- run.addInput('source-findings', sourceBytes);
132
+ const run = createRun(root, { command: 'governance evaluate', argv: [runId], target: { kind: 'registries', location: path.join(runDir, 'findings.json') } });
133
+ run.manifest.input_hashes['source-findings'] = sourceRun.artifactHashes['findings.json'];
135
134
  run.addInput('exceptions', registries.exceptions);
136
135
  run.addInput('review-policies', registries.review_policies);
137
136
  run.writeArtifact('dispositions.json', dispositions);
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { loadVerifiedRun } from '../shared/verifiedRunLoader.js';
3
4
  import { readJson, nowIso } from '../shared/io.js';
4
5
  import { remediateCommand } from './remediate.js';
5
6
 
@@ -76,7 +77,7 @@ export async function exportImplementationKit(root, options = {}) {
76
77
  if (!runId || !fs.existsSync(path.join(runDir, 'findings.json'))) {
77
78
  throw new Error(`source run not found: .citable/runs/${runId || '(none)'}`);
78
79
  }
79
- const findings = readJson(path.join(runDir, 'findings.json'));
80
+ const findings = loadVerifiedRun(runDir, { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false }).findings;
80
81
  const matches = findings.filter((f) =>
81
82
  (f.detector_id || '').toUpperCase() === String(finding || '').toUpperCase()
82
83
  && (!subject || (f.subject?.identifier || f.subject?.url) === subject));
@@ -89,7 +89,14 @@ export function init(root, { force = false, seed = null } = {}) {
89
89
  package_manager: detected.package_manager,
90
90
  build_command: detected.build_commands[0] ?? null, test_command: detected.test_commands[0] ?? null,
91
91
  },
92
- audit: { index_target_default: true, max_crawl_depth: 4, thin_content_words: 120, fetch_external: false },
92
+ audit: {
93
+ index_target_default: true,
94
+ max_pages: 500,
95
+ time_budget_seconds: 1800,
96
+ max_crawl_depth: 4,
97
+ thin_content_words: 120,
98
+ fetch_external: false,
99
+ },
93
100
  };
94
101
  const { valid, errors } = validateAgainst('config.schema.json', config);
95
102
  if (!valid) throw new Error(`generated config invalid: ${errors.join('; ')}`);
@@ -167,7 +167,7 @@ export function generateCroPreviewHtml({
167
167
  </div>
168
168
 
169
169
  <div class="findings-list">
170
- <h3>Resolved CRO Defects (${findingItems.length})</h3>
170
+ <h3>Observed CRO Findings (${findingItems.length})</h3>
171
171
  ${findingItems.map((f) => `
172
172
  <div class="finding-row">
173
173
  <span><strong>${f.detector_id}</strong>: ${f.summary}</span>
@@ -13,6 +13,7 @@
13
13
  */
14
14
  import fs from 'node:fs';
15
15
  import path from 'node:path';
16
+ import { loadVerifiedRun } from '../shared/verifiedRunLoader.js';
16
17
  import { contextDir, loadRegistryFile, registryLoadProblems } from '../registries/index.js';
17
18
  import { validateAgainst } from '../shared/schemaValidator.js';
18
19
  import { buildIceMatrix, formatIceMatrixOutput } from '../analysis/iceMatrix.js';
@@ -96,7 +97,7 @@ function initiativeMatrix(file, root, rest = []) {
96
97
  if (runId) {
97
98
  const findingsPath = path.join(root, '.citable', 'runs', runId, 'findings.json');
98
99
  if (!fs.existsSync(findingsPath)) throw new Error(`run ${runId} findings.json not found`);
99
- const findings = JSON.parse(fs.readFileSync(findingsPath, 'utf8'));
100
+ const findings = loadVerifiedRun(path.dirname(findingsPath), { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false }).findings;
100
101
  const matrix = buildIceMatrix(findings, { type: 'findings' });
101
102
  return { ...matrix, formatted: formatIceMatrixOutput(matrix) };
102
103
  }
@@ -106,7 +107,7 @@ function initiativeMatrix(file, root, rest = []) {
106
107
  if (fs.existsSync(runsDir)) {
107
108
  const runs = fs.readdirSync(runsDir).filter((r) => fs.existsSync(path.join(runsDir, r, 'findings.json'))).sort().reverse();
108
109
  if (runs.length > 0 && rest.includes('--findings')) {
109
- const findings = JSON.parse(fs.readFileSync(path.join(runsDir, runs[0], 'findings.json'), 'utf8'));
110
+ const findings = loadVerifiedRun(path.join(runsDir, runs[0]), { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false }).findings;
110
111
  const matrix = buildIceMatrix(findings, { type: 'findings' });
111
112
  return { ...matrix, formatted: formatIceMatrixOutput(matrix) };
112
113
  }