@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,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 { loadRegistries, saveRegistry } from '../registries/index.js';
4
5
  import { readJson, readYaml, sha256, nowIso } from '../shared/io.js';
5
6
  import { validateAgainst } from '../shared/schemaValidator.js';
@@ -23,7 +24,7 @@ export function queueReviews(root, { runId, policyId, write = false }) {
23
24
  if (!runId || !policyId) throw new Error('reviews queue requires <run-id> <policy-id>');
24
25
  const file = path.join(root,'.citable','runs',runId,'findings.json');
25
26
  if (!fs.existsSync(file)) throw new Error(`source findings not found for run ${runId}`);
26
- const findings = readJson(file);
27
+ const findings = loadVerifiedRun(path.dirname(file), { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false }).findings;
27
28
  const { registries, problems } = loadRegistries(root);
28
29
  if (problems.length) throw new Error(`registry validation failed: ${problems.join('; ')}`);
29
30
  if (!registries.review_policies.entries.some((p) => p.policy_id === policyId && p.status === 'active')) throw new Error(`active review policy not found: ${policyId}`);
@@ -40,7 +41,7 @@ export function queueReviews(root, { runId, policyId, write = false }) {
40
41
  export function prioritizeReviews(root, { write = false } = {}) {
41
42
  const { registries, problems } = loadRegistries(root); if (problems.length) throw new Error(`registry validation failed: ${problems.join('; ')}`);
42
43
  const cache = new Map();
43
- const entries = registries.review_items.entries.map((item) => { const file=path.join(root,'.citable','runs',item.source_run_id,'findings.json'); if(!cache.has(file)) cache.set(file,fs.existsSync(file)?new Map(readJson(file).map(f=>[f.finding_id,f])):new Map()); const ranked=priority(item,cache.get(file).get(item.finding_id)); return {...item,priority_score:ranked.score,priority_tier:ranked.tier,missing_inputs:ranked.missing}; });
44
+ const entries = registries.review_items.entries.map((item) => { const file=path.join(root,'.citable','runs',item.source_run_id,'findings.json'); if(!cache.has(file)) cache.set(file,fs.existsSync(file)?new Map(loadVerifiedRun(path.dirname(file), { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false }).findings.map(f=>[f.finding_id,f])):new Map()); const ranked=priority(item,cache.get(file).get(item.finding_id)); return {...item,priority_score:ranked.score,priority_tier:ranked.tier,missing_inputs:ranked.missing}; });
44
45
  if(write) saveRegistry(root,'review_items',{...registries.review_items,entries});
45
46
  return { items:entries.sort((a,b)=>b.priority_score-a.priority_score), written:write };
46
47
  }
@@ -67,7 +68,32 @@ export function selectSample(root,{planId,write=false}) {
67
68
  }
68
69
 
69
70
  export function evaluateReviews(root) {
70
- const {registries,problems}=loadRegistries(root); const reviewers=new Map(registries.reviewers.entries.map(r=>[r.reviewer_id,r])); const results=[];
71
- for(const item of registries.review_items.entries){const issues=[];const binding=reviewBindingHash(item);const source=path.join(root,'.citable','runs',item.source_run_id,'findings.json');const finding=fs.existsSync(source)?readJson(source).find(f=>f.finding_id===item.finding_id):null;if(!finding||sha256(JSON.stringify(finding))!==item.finding_hash)issues.push('source finding is unavailable or changed');for(const d of item.decisions){const r=reviewers.get(d.reviewer_id);const reviewRole=r?.roles.some(role=>['technical_reviewer','subject_matter_reviewer','independent_reviewer'].includes(role));if(!r||r.status!=='active'||!reviewRole||!item.assigned_reviewer_ids.includes(d.reviewer_id))issues.push(`${d.reviewer_id} is not an active assigned semantic reviewer`);if(d.review_item_hash!==binding)issues.push(`${d.reviewer_id} decision is stale`);}const verdicts=[...new Set(item.decisions.map(d=>d.verdict))];let state=item.decisions.length?'completed':'review_required';if(verdicts.length>1){state='adjudication_required';const a=reviewers.get(item.adjudication?.reviewer_id);if(item.adjudication&&a?.status==='active'&&a.roles.includes('independent_reviewer')&&item.adjudication.review_item_hash===binding)state='completed';else issues.push('reviewer disagreement requires a current decision from an active independent adjudicator');}results.push({review_item_id:item.review_item_id,state,verdict:state==='completed'&&!issues.length?(item.adjudication?.verdict||verdicts[0]):null,issues});}
72
- return {ok:problems.length===0&&results.every(r=>!r.issues.length),problems,results};
71
+ const { registries, problems } = loadRegistries(root);
72
+ const reviewers = new Map(registries.reviewers.entries.map((r) => [r.reviewer_id, r]));
73
+ const results = [];
74
+ for (const item of registries.review_items.entries) {
75
+ const issues = [];
76
+ const binding = reviewBindingHash(item);
77
+ const source = path.join(root, '.citable', 'runs', item.source_run_id, 'findings.json');
78
+ const finding = fs.existsSync(source)
79
+ ? loadVerifiedRun(path.dirname(source), { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false }).findings.find((f) => f.finding_id === item.finding_id)
80
+ : null;
81
+ if (!finding || sha256(JSON.stringify(finding)) !== item.finding_hash) issues.push('source finding is unavailable or changed');
82
+ for (const decision of item.decisions) {
83
+ const reviewer = reviewers.get(decision.reviewer_id);
84
+ const reviewRole = reviewer?.roles.some((role) => ['technical_reviewer', 'subject_matter_reviewer', 'independent_reviewer'].includes(role));
85
+ if (!reviewer || reviewer.status !== 'active' || !reviewRole || !item.assigned_reviewer_ids.includes(decision.reviewer_id)) issues.push(`${decision.reviewer_id} is not an active assigned semantic reviewer`);
86
+ if (decision.review_item_hash !== binding) issues.push(`${decision.reviewer_id} decision is stale`);
87
+ }
88
+ const verdicts = [...new Set(item.decisions.map((d) => d.verdict))];
89
+ let state = item.decisions.length ? 'completed' : 'review_required';
90
+ if (verdicts.length > 1) {
91
+ state = 'adjudication_required';
92
+ const adjudicator = reviewers.get(item.adjudication?.reviewer_id);
93
+ if (item.adjudication && adjudicator?.status === 'active' && adjudicator.roles.includes('independent_reviewer') && item.adjudication.review_item_hash === binding) state = 'completed';
94
+ else issues.push('reviewer disagreement requires a current decision from an active independent adjudicator');
95
+ }
96
+ results.push({ review_item_id: item.review_item_id, state, verdict: state === 'completed' && !issues.length ? (item.adjudication?.verdict || verdicts[0]) : null, issues });
97
+ }
98
+ return { ok: problems.length === 0 && results.every((r) => !r.issues.length), problems, results };
73
99
  }
@@ -1,8 +1,9 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { contextDir, loadRegistryFile } from '../registries/index.js';
4
- import { readJson, writeJson } from '../shared/io.js';
4
+ import { writeJson } from '../shared/io.js';
5
5
  import { buildStrategicRoadmap, formatRoadmapMarkdown } from '../analysis/strategicRoadmap.js';
6
+ import { loadVerifiedRun } from '../shared/verifiedRunLoader.js';
6
7
 
7
8
  /**
8
9
  * `citable roadmap [generate|show]` — generate or view the 30/90/180-day strategic roadmap.
@@ -10,6 +11,7 @@ import { buildStrategicRoadmap, formatRoadmapMarkdown } from '../analysis/strate
10
11
  export async function roadmapCommand(root, { runId, target, write = true } = {}) {
11
12
  let findings = [];
12
13
  let initiatives = [];
14
+ let verifiedRun = null;
13
15
 
14
16
  // 1. Load findings from specified or latest run
15
17
  const runsDir = path.join(root, '.citable', 'runs');
@@ -20,10 +22,8 @@ export async function roadmapCommand(root, { runId, target, write = true } = {})
20
22
  }
21
23
 
22
24
  if (sourceRun) {
23
- const findingsFile = path.join(runsDir, sourceRun, 'findings.json');
24
- if (fs.existsSync(findingsFile)) {
25
- findings = JSON.parse(fs.readFileSync(findingsFile, 'utf8'));
26
- }
25
+ verifiedRun = loadVerifiedRun(path.join(runsDir, sourceRun), { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false });
26
+ findings = verifiedRun.findings;
27
27
  }
28
28
 
29
29
  // 2. Load initiatives if present
@@ -39,14 +39,8 @@ export async function roadmapCommand(root, { runId, target, write = true } = {})
39
39
 
40
40
  // Target domain resolution
41
41
  let domain = target || 'nebulacomponents.com';
42
- if (sourceRun) {
43
- const manifestFile = path.join(runsDir, sourceRun, 'manifest.json');
44
- if (fs.existsSync(manifestFile)) {
45
- try {
46
- const manifest = readJson(manifestFile);
47
- if (manifest.target?.location) domain = manifest.target.location;
48
- } catch {}
49
- }
42
+ if (verifiedRun?.manifest?.target?.location) {
43
+ domain = verifiedRun.manifest.target.location;
50
44
  }
51
45
 
52
46
  const roadmap = buildStrategicRoadmap({ findings, initiatives, targetDomain: domain });
@@ -4,6 +4,7 @@ import { parse as parseHtml } from 'node-html-parser';
4
4
  import { buildContext } from './context.js';
5
5
  import { selectDetectors } from '../detectors/index.js';
6
6
  import { runDetectors, indexTargets, pageSubject, safePath } from '../detectors/framework.js';
7
+ import { downstreamEnvelope, scoreFromCoverage, completeLocalCoverage } from '../evidence/downstream.js';
7
8
 
8
9
  /**
9
10
  * CWV standard thresholds per Google / web.dev guidance
@@ -223,6 +224,9 @@ export async function sweepTechnical(root, { target, baseUrl, refDate, page: pag
223
224
  if (!pages.length) throw new Error(`page not found in audited output: ${pageFilter}`);
224
225
  }
225
226
 
227
+ const coverage = ctx.coverage || (pages.length > 0 ? completeLocalCoverage(pages, ctx.site.baseUrl) : null);
228
+ const evidence = downstreamEnvelope(coverage);
229
+
226
230
  // 3. Aggregate Core Web Vitals across audited pages
227
231
  const pageVitals = [];
228
232
  let totalLcpBlockers = 0;
@@ -269,6 +273,8 @@ export async function sweepTechnical(root, { target, baseUrl, refDate, page: pag
269
273
  } else if (highFindings.length > 0 || totalLcpBlockers > 3 || totalClsRisks > 5) {
270
274
  overallHealth = 'needs_attention';
271
275
  }
276
+ if (evidence.determination_status === 'indeterminate') overallHealth = 'not_established';
277
+ else if (evidence.determination_status === 'qualified' && overallHealth === 'optimal') overallHealth = 'qualified';
272
278
 
273
279
  const result = {
274
280
  target: ctx.site.baseUrl || target,
@@ -291,7 +297,9 @@ export async function sweepTechnical(root, { target, baseUrl, refDate, page: pag
291
297
  total_inp_risks: totalInpRisks,
292
298
  unsized_images: totalUnsizedImages,
293
299
  total_images: totalImagesCount,
294
- image_dimension_coverage_pct: totalImagesCount > 0 ? Math.round(((totalImagesCount - totalUnsizedImages) / totalImagesCount) * 100) : 100,
300
+ image_dimension_coverage_pct: evidence.determination_status === 'supported' && totalImagesCount > 0
301
+ ? scoreFromCoverage(totalImagesCount - totalUnsizedImages, { ...coverage, populations: { ...coverage.populations, evaluated: totalImagesCount } })
302
+ : null,
295
303
  },
296
304
  pages: pageVitals,
297
305
  },
@@ -310,6 +318,10 @@ export async function sweepTechnical(root, { target, baseUrl, refDate, page: pag
310
318
  summary: f.observation.summary,
311
319
  remediation: f.remediation?.preferred,
312
320
  })),
321
+ coverage_status: evidence.coverage_status,
322
+ determination_status: evidence.determination_status,
323
+ evidence_scope: evidence.evidence_scope,
324
+ limitations: evidence.limitations,
313
325
  };
314
326
 
315
327
  return result;
@@ -325,6 +337,7 @@ export function formatSweepOutput(r) {
325
337
  `Target: ${r.target}`,
326
338
  `Pages Audited: ${r.total_pages_audited}`,
327
339
  `Overall Health: ${r.overall_health.toUpperCase()}`,
340
+ `Evidence Determination: ${(r.determination_status || 'not_established').toUpperCase()} (coverage: ${r.coverage_status || 'unknown'})`,
328
341
  ``,
329
342
  `CRAWL & INDEXABILITY:`,
330
343
  ` HTTP 200 OK: ${r.crawl_and_indexability.status_200_count}/${r.total_pages_audited}`,
@@ -335,7 +348,7 @@ export function formatSweepOutput(r) {
335
348
  `CORE WEB VITALS READINESS (Deterministic Static Audit):`,
336
349
  ` LCP Readiness: ${r.core_web_vitals.summary.lcp_readiness.toUpperCase()} (${r.core_web_vitals.summary.total_lcp_blockers} potential render blocker(s))`,
337
350
  ` INP Readiness: ${r.core_web_vitals.summary.inp_readiness.toUpperCase()} (${r.core_web_vitals.summary.total_inp_risks} DOM complexity risk(s))`,
338
- ` CLS Readiness: ${r.core_web_vitals.summary.cls_readiness.toUpperCase()} (${r.core_web_vitals.summary.unsized_images}/${r.core_web_vitals.summary.total_images} images lack dimensions; ${r.core_web_vitals.summary.image_dimension_coverage_pct}% coverage)`,
351
+ ` CLS Readiness: ${r.core_web_vitals.summary.cls_readiness.toUpperCase()} (${r.core_web_vitals.summary.unsized_images}/${r.core_web_vitals.summary.total_images} images lack dimensions; ${r.core_web_vitals.summary.image_dimension_coverage_pct == null ? 'not evidenced' : `${r.core_web_vitals.summary.image_dimension_coverage_pct}%`} coverage)`,
339
352
  ``,
340
353
  `CWV Threshold Standards:`,
341
354
  ` - LCP: <= ${CWV_THRESHOLDS.lcp.good}s (Good), <= ${CWV_THRESHOLDS.lcp.needs_improvement}s (Needs Improvement)`,
@@ -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 crypto from 'node:crypto';
4
5
  import { readJson, writeJson, nowIso } from '../shared/io.js';
5
6
  import { validateAgainst } from '../shared/schemaValidator.js';
@@ -7,6 +8,7 @@ import { buildContext } from './context.js';
7
8
  import { runDetectors } from '../detectors/framework.js';
8
9
  import { selectDetectors } from '../detectors/index.js';
9
10
  import { remediateCommand } from './remediate.js';
11
+ import { assessReobservation, resourceIdentityFor, sameResourceIdentity } from './compareSnapshots.js';
10
12
 
11
13
  const PKG_VERSION = JSON.parse(
12
14
  fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf8')
@@ -20,14 +22,49 @@ function sha256(content) {
20
22
  return crypto.createHash('sha256').update(content, 'utf8').digest('hex');
21
23
  }
22
24
 
23
- function subjectKey(subject) {
24
- return subject?.identifier || subject?.url || JSON.stringify(subject) || 'unknown';
25
+ function findingIdentityKey(finding) {
26
+ const identity = resourceIdentityFor(finding);
27
+ const id = identity.resourceIds.size ? [...identity.resourceIds].sort().join(',')
28
+ : identity.effective || identity.requested || (identity.fallbackUrls.size === 1 ? [...identity.fallbackUrls][0] : JSON.stringify(finding?.subject || {}));
29
+ return `${finding?.detector_id || ''}|${id}`;
25
30
  }
26
31
 
27
32
  function newVerificationId() {
28
33
  return `VR-${nowIso().replace(/\D/g, '')}-${Math.random().toString(36).slice(2, 6)}`;
29
34
  }
30
35
 
36
+ function canonicalJson(value) {
37
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;
38
+ if (value && typeof value === 'object') {
39
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(',')}}`;
40
+ }
41
+ return JSON.stringify(value);
42
+ }
43
+
44
+ export function recheckComparability(manifest, sourceFinding, afterFinding, ctx, detector) {
45
+ const sourceKind = manifest?.target?.kind || null;
46
+ const recheckKind = ctx.site?.mode || null;
47
+ const sourceDetectorVersion = sourceFinding?.provenance?.detector_version ?? 1;
48
+ const recheckDetectorVersion = afterFinding?.provenance?.detector_version ?? detector?.version ?? 1;
49
+ const sourceViewport = sourceFinding?.provenance?.viewport ?? null;
50
+ const recheckViewport = ctx.viewport ?? null;
51
+ const sourceConfig = manifest?.configuration_hash || null;
52
+ const recheckConfig = sha256(JSON.stringify(ctx.config));
53
+ const dimensions = {
54
+ tool_changed: (manifest?.tool_version || null) !== PKG_VERSION,
55
+ evaluator_changed: sourceDetectorVersion !== recheckDetectorVersion,
56
+ observation_method_changed: sourceKind !== recheckKind
57
+ || canonicalJson(sourceViewport) !== canonicalJson(recheckViewport),
58
+ configuration_changed: sourceConfig !== recheckConfig,
59
+ };
60
+ return {
61
+ comparable: !Object.values(dimensions).some(Boolean),
62
+ dimensions,
63
+ source: { tool_version: manifest?.tool_version || null, evaluator_version: sourceDetectorVersion, target_kind: sourceKind, configuration_hash: sourceConfig },
64
+ recheck: { tool_version: PKG_VERSION, evaluator_version: recheckDetectorVersion, target_kind: recheckKind, configuration_hash: recheckConfig },
65
+ };
66
+ }
67
+
31
68
  /**
32
69
  * `citable verify remediation` — closed loop: source-run finding -> patch ->
33
70
  * detector re-run -> before/after evidence bundle.
@@ -37,7 +74,7 @@ function newVerificationId() {
37
74
  * never a silent `verified`.
38
75
  */
39
76
  export async function verifyRemediation(root, options = {}) {
40
- const { run: runId, finding, target, apply = false, subject = null } = options;
77
+ const { run: runId, finding, target, apply = false, subject = null, viewport = null } = options;
41
78
  const runDir = path.join(root, '.citable', 'runs', runId || '');
42
79
  const limitations = [
43
80
  RESOLUTION_DEFINITION,
@@ -55,7 +92,7 @@ export async function verifyRemediation(root, options = {}) {
55
92
  recheck_run_id: null,
56
93
  detector_id: finding ? String(finding).toUpperCase() : null,
57
94
  subject: null,
58
- verdict: { resolved: false, before_finding_ids: [], after_finding_ids: [], new_regressions: [], resolution_definition: RESOLUTION_DEFINITION },
95
+ verdict: { resolved: false, comparison_state: 'indeterminate', before_finding_ids: [], after_finding_ids: [], new_regressions: [], resolution_definition: RESOLUTION_DEFINITION },
59
96
  patch: null,
60
97
  provenance: { command: 'verify remediation', recheck_target: target || null, detectors_run: [], detectors_skipped: [], recheck_errors: [], recheck_warnings: [] },
61
98
  limitations,
@@ -73,23 +110,23 @@ export async function verifyRemediation(root, options = {}) {
73
110
  return finalizeResult(result);
74
111
  }
75
112
 
76
- const beforeFindings = readJson(path.join(runDir, 'findings.json'));
77
- const manifest = fs.existsSync(path.join(runDir, 'manifest.json'))
78
- ? readJson(path.join(runDir, 'manifest.json'))
79
- : null;
113
+ const source = loadVerifiedRun(runDir, { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false });
114
+ const beforeFindings = source.findings;
115
+ const manifest = source.manifest;
80
116
  result.tool_version = manifest?.tool_version || PKG_VERSION;
81
117
  result.repository_commit = manifest?.repository_commit ?? null;
82
118
 
119
+ const requestedSubject = subject ? { subject: { identifier: subject } } : null;
83
120
  const matches = beforeFindings.filter((f) =>
84
121
  (f.detector_id || '').toUpperCase() === result.detector_id
85
- && (!subject || subjectKey(f.subject) === subject));
122
+ && (!requestedSubject || sameResourceIdentity(f, requestedSubject)));
86
123
  if (matches.length === 0) {
87
124
  result.provenance.recheck_errors.push(`finding ${result.detector_id} not present in source run ${runId}`);
88
125
  result.status = 'blocked';
89
126
  return finalizeResult(result);
90
127
  }
91
128
  result.verdict.before_finding_ids = matches.map((f) => f.finding_id);
92
- result.subject = { type: matches[0].subject?.type || 'unknown', identifier: subject || subjectKey(matches[0].subject) };
129
+ result.subject = { type: matches[0].subject?.type || 'unknown', identifier: subject || matches[0].subject?.identifier || matches[0].subject?.url || 'unknown' };
93
130
 
94
131
  // Optional gated patch application (production-safe pipeline from remediate.js)
95
132
  if (target && apply) {
@@ -133,7 +170,7 @@ export async function verifyRemediation(root, options = {}) {
133
170
  const namespace = result.detector_id.split('-')[0];
134
171
  let ctx;
135
172
  try {
136
- ctx = await buildContext(root, { target: recheckTarget, baseUrl: options.baseUrl, refDate: options.refDate });
173
+ ctx = await buildContext(root, { target: recheckTarget, baseUrl: options.baseUrl, refDate: options.refDate, viewport });
137
174
  } catch (err) {
138
175
  result.provenance.recheck_errors.push(`re-check context failed: ${err.message}`);
139
176
  result.limitations.push('required_input: a readable target directory or URL for the detector re-run.');
@@ -155,24 +192,62 @@ export async function verifyRemediation(root, options = {}) {
155
192
 
156
193
  const afterMatches = afterFindings.filter((f) =>
157
194
  (f.detector_id || '').toUpperCase() === result.detector_id
158
- && subjectKey(f.subject) === subjectKey(matches[0].subject));
195
+ && sameResourceIdentity(f, matches[0]));
159
196
  result.verdict.after_finding_ids = afterMatches.map((f) => f.finding_id);
160
197
 
161
- const beforeKeys = new Set(beforeFindings.map((f) => `${f.detector_id}|${subjectKey(f.subject)}|${f.observation?.summary}`));
162
- const afterKeys = new Set(afterFindings.map((f) => `${f.detector_id}|${subjectKey(f.subject)}|${f.observation?.summary}`));
198
+ const beforeKeys = new Set(beforeFindings.map((f) => `${findingIdentityKey(f)}|${f.observation?.summary}`));
199
+ const afterKeys = new Set(afterFindings.map((f) => `${findingIdentityKey(f)}|${f.observation?.summary}`));
163
200
  result.verdict.new_regressions = afterFindings
164
201
  .filter((f) => ['critical', 'high'].includes(f.classification?.severity))
165
- .filter((f) => !beforeKeys.has(`${f.detector_id}|${subjectKey(f.subject)}|${f.observation?.summary}`))
202
+ .filter((f) => !beforeKeys.has(`${findingIdentityKey(f)}|${f.observation?.summary}`))
166
203
  .map((f) => ({ finding_id: f.finding_id, detector_id: f.detector_id, severity: f.classification.severity, summary: f.observation?.summary }));
167
204
 
205
+ const detectorDefinition = selectDetectors({ namespaces: [namespace] })
206
+ .find((candidate) => candidate.id === result.detector_id);
207
+ const comparability = recheckComparability(manifest, matches[0], afterMatches[0], ctx, detectorDefinition);
208
+
209
+ // Finalize the same collection envelope used by audit so detector absence
210
+ // is only resolution when the responsible resource was actually observed.
211
+ let recheckCoverage = null;
212
+ if (ctx.site?.finalizeCoverage) {
213
+ try { recheckCoverage = ctx.site.finalizeCoverage({ evaluated: true }); }
214
+ catch (error) { result.provenance.recheck_errors.push(`coverage finalization failed: ${error.message}`); }
215
+ }
216
+ let reobservation = assessReobservation(matches[0], recheckCoverage);
217
+ // Directory re-checks predate the coverage ledger and intentionally do not
218
+ // manufacture a package artifact. Their concrete page model is still a
219
+ // sufficient page-local observation when the subject is present.
220
+ if (!recheckCoverage && ctx.site?.pages?.some((page) => sameResourceIdentity(matches[0], page))) {
221
+ reobservation = { state: 'resolved', reason: 'page_present_in_recheck_model', resource: null };
222
+ }
223
+ result.comparison = {
224
+ state: comparability.comparable ? reobservation.state : 'not_comparable',
225
+ reason: reobservation.reason,
226
+ coverage_status: recheckCoverage?.coverage_status || 'not_available',
227
+ resource: reobservation.resource?.normalized_url || null,
228
+ comparability,
229
+ };
230
+ result.verdict.comparison_state = comparability.comparable ? reobservation.state : 'not_comparable';
231
+
168
232
  const errorsDuringRecheck = result.provenance.recheck_errors.length > 0;
169
233
  if (errorsDuringRecheck) {
170
- result.status = 'blocked';
234
+ result.status = 'indeterminate';
235
+ result.verdict.resolved = false;
236
+ result.verdict.comparison_state = 'indeterminate';
237
+ result.limitations.push('Detector or collection errors occurred during the re-check; resolution cannot be established from a failed observation.');
238
+ } else if (!comparability.comparable) {
239
+ result.status = 'not_comparable';
171
240
  result.verdict.resolved = false;
172
- result.limitations.push('Detector errors occurred during the re-check; resolution cannot be established from a failed observation.');
241
+ result.limitations.push('The source and re-check evaluator/tool/method/configuration envelopes are not comparable; resolution is not established.');
173
242
  } else if (afterMatches.length === 0) {
174
- result.status = 'verified';
175
- result.verdict.resolved = true;
243
+ if (reobservation.state === 'resolved') {
244
+ result.status = 'verified';
245
+ result.verdict.resolved = true;
246
+ } else {
247
+ result.status = reobservation.state === 'indeterminate' ? 'indeterminate' : 'not_reobserved';
248
+ result.verdict.resolved = false;
249
+ result.limitations.push(`The source subject was not sufficiently reobserved: ${reobservation.reason}.`);
250
+ }
176
251
  } else {
177
252
  result.status = 'not_resolved';
178
253
  result.verdict.resolved = false;
@@ -0,0 +1,80 @@
1
+ import { validateAgainst } from '../shared/schemaValidator.js';
2
+
3
+ export const COLLECTION_SCHEMA_VERSION = 1;
4
+ export const COLLECTION_COVERAGE_STATUSES = new Set([
5
+ 'complete',
6
+ 'truncated',
7
+ 'indeterminate',
8
+ 'provider_bounded',
9
+ ]);
10
+
11
+ function nonNegativeInteger(value, name) {
12
+ if (!Number.isInteger(value) || value < 0) throw new TypeError(`${name} must be a non-negative integer`);
13
+ return value;
14
+ }
15
+
16
+ function stringList(value, name) {
17
+ if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) throw new TypeError(`${name} must be an array of strings`);
18
+ return [...new Set(value)];
19
+ }
20
+
21
+ /**
22
+ * Build the provider-neutral collection envelope. `items` are the items
23
+ * actually observed, never a provider-reported total or an inferred census.
24
+ */
25
+ export function collectionResult({
26
+ items = [],
27
+ paginationState = {},
28
+ providerReportedTotal = null,
29
+ continuationState = null,
30
+ limitations = [],
31
+ errors = [],
32
+ providerCompleteness = 'declared',
33
+ } = {}) {
34
+ if (!Array.isArray(items)) throw new TypeError('items must be an array');
35
+ if (!paginationState || typeof paginationState !== 'object' || Array.isArray(paginationState)) throw new TypeError('paginationState must be an object');
36
+ if (providerReportedTotal !== null) nonNegativeInteger(providerReportedTotal, 'providerReportedTotal');
37
+ stringList(limitations, 'limitations');
38
+ stringList(errors, 'errors');
39
+ if (continuationState !== null && (typeof continuationState !== 'object' || Array.isArray(continuationState))) throw new TypeError('continuationState must be an object or null');
40
+ if (!['declared', 'unknown'].includes(providerCompleteness)) throw new TypeError('providerCompleteness must be declared or unknown');
41
+
42
+ const totalErrors = providerReportedTotal !== null && providerReportedTotal < items.length
43
+ ? [`provider reported total ${providerReportedTotal}, below retrieved item count ${items.length}`]
44
+ : [];
45
+ const cleanErrors = [...new Set([...errors, ...totalErrors])];
46
+ const contradictoryTotal = providerReportedTotal !== null && providerReportedTotal < items.length;
47
+ const unresolvedTotal = providerReportedTotal !== null && providerReportedTotal > items.length && !continuationState;
48
+ const status = (cleanErrors.length || contradictoryTotal)
49
+ ? 'indeterminate'
50
+ : continuationState
51
+ ? 'truncated'
52
+ : (unresolvedTotal || providerCompleteness === 'unknown')
53
+ ? 'provider_bounded'
54
+ : 'complete';
55
+ const result = {
56
+ schema_version: COLLECTION_SCHEMA_VERSION,
57
+ items,
58
+ pagination_state: paginationState,
59
+ provider_reported_total: providerReportedTotal,
60
+ retrieved_total: items.length,
61
+ coverage_status: status,
62
+ continuation_state: continuationState,
63
+ limitations: [...new Set(limitations)],
64
+ errors: cleanErrors,
65
+ };
66
+ const check = validateAgainst('collection-result.schema.json', result);
67
+ if (!check.valid) throw new TypeError(`invalid collection result: ${check.errors.join('; ')}`);
68
+ return result;
69
+ }
70
+
71
+ export function paginationBoundary(context = {}) {
72
+ const value = context.collectionMaxPages ?? context.maxPages ?? 1000;
73
+ if (!Number.isInteger(value) || value < 1 || value > 10000) throw new RangeError('collection max pages must be an integer from 1 to 10000');
74
+ return value;
75
+ }
76
+
77
+ export function errorMessage(error) {
78
+ const message = error instanceof Error ? error.message : String(error);
79
+ return message.replace(/Bearer\s+\S+/gi, 'Bearer [redacted]').slice(0, 300);
80
+ }
@@ -1,4 +1,5 @@
1
1
  import { providerRequest } from './http.js';
2
+ import { collectionResult, errorMessage, paginationBoundary } from './collectionResult.js';
2
3
 
3
4
  const DATA = 'https://analyticsdata.googleapis.com/v1beta';
4
5
  const ADMIN = 'https://analyticsadmin.googleapis.com/v1beta';
@@ -36,24 +37,58 @@ export const ga4Connector = {
36
37
  if (!dimensions.includes('landingPagePlusQueryString')) dimensions.push('landingPagePlusQueryString');
37
38
  const rows = [];
38
39
  const limitations = [];
40
+ const errors = [];
41
+ const items = [];
42
+ const maxPages = paginationBoundary(context);
43
+ let pagesRequested = 0;
44
+ let pagesRetrieved = 0;
45
+ let continuationState = null;
46
+ let providerTotal = null;
47
+ let connectorError = null;
39
48
  let offset = 0;
40
49
  do {
41
- const result = await providerRequest(`${DATA}/properties/${encodeURIComponent(connection.property_id)}:runReport`, {
42
- ...context, method: 'POST', body: {
43
- dateRanges: [{ startDate, endDate }], dimensions: dimensions.map((name) => ({ name })),
44
- metrics: metrics.map((metric) => ({ name: metric.external_name })), limit: '100000', offset: String(offset),
45
- dimensionFilter: { filter: { fieldName: 'sessionDefaultChannelGroup', stringFilter: { matchType: 'EXACT', value: 'Organic Search' } } },
46
- },
47
- });
48
- for (const row of result.rows || []) {
50
+ pagesRequested += 1;
51
+ let result;
52
+ try {
53
+ result = await providerRequest(`${DATA}/properties/${encodeURIComponent(connection.property_id)}:runReport`, {
54
+ ...context, method: 'POST', body: {
55
+ dateRanges: [{ startDate, endDate }], dimensions: dimensions.map((name) => ({ name })),
56
+ metrics: metrics.map((metric) => ({ name: metric.external_name })), limit: '100000', offset: String(offset),
57
+ dimensionFilter: { filter: { fieldName: 'sessionDefaultChannelGroup', stringFilter: { matchType: 'EXACT', value: 'Organic Search' } } },
58
+ },
59
+ });
60
+ } catch (error) {
61
+ connectorError ||= error;
62
+ errors.push(`offset ${offset}: ${errorMessage(error)}`);
63
+ continuationState = { offset };
64
+ break;
65
+ }
66
+ pagesRetrieved += 1;
67
+ const pageRows = result.rows || [];
68
+ items.push(...pageRows);
69
+ if (Number.isInteger(result.rowCount) && result.rowCount >= 0) providerTotal = result.rowCount;
70
+ for (const row of pageRows) {
49
71
  const values = Object.fromEntries(dimensions.map((name, index) => [name === 'landingPagePlusQueryString' ? 'url' : name, row.dimensionValues[index]?.value]));
50
72
  const date = values.date?.replace(/^(\d{4})(\d{2})(\d{2})$/, '$1-$2-$3');
51
73
  rows.push(...metrics.map((metric, index) => ({ metric, value: Number(row.metricValues[index]?.value), dimensions: values, observed_at: `${date}T00:00:00.000Z` })));
52
74
  }
53
75
  if (result.metadata?.dataLossFromOtherRow) limitations.push('Provider reports data loss from the (other) row.');
54
- offset += (result.rows || []).length;
55
- if (!result.rowCount || offset >= result.rowCount || !(result.rows || []).length) break;
76
+ offset += pageRows.length;
77
+ if ((result.rowCount === undefined || result.rowCount === null)
78
+ ? pageRows.length < 100000 || !pageRows.length
79
+ : offset >= result.rowCount || !pageRows.length) break;
80
+ if (pagesRequested >= maxPages) { continuationState = { offset }; break; }
56
81
  } while (true);
57
- return { rows, cursor: endDate, limitations: [...new Set([...limitations, 'GA4 reporting identity, thresholding, attribution, and consent configuration affect results.', 'Sync is filtered to Organic Search sessions.'])] };
82
+ const providerCompleteness = limitations.some((item) => /data loss/i.test(item)) || providerTotal === null ? 'unknown' : 'declared';
83
+ const collection = collectionResult({
84
+ items,
85
+ paginationState: { pages_requested: pagesRequested, pages_retrieved: pagesRetrieved, boundary: { max_pages: maxPages } },
86
+ providerReportedTotal: providerTotal,
87
+ continuationState,
88
+ providerCompleteness,
89
+ limitations: [...new Set([...limitations, 'GA4 reporting identity, thresholding, attribution, and consent configuration affect results.', 'Sync is filtered to Organic Search sessions.'])],
90
+ errors,
91
+ });
92
+ return { rows, cursor: endDate, limitations: collection.limitations, collection, ...(connectorError ? { connectorError } : {}) };
58
93
  },
59
94
  };
@@ -1,4 +1,6 @@
1
1
  import { providerRequest } from './http.js';
2
+ import { collectionResult, errorMessage, paginationBoundary } from './collectionResult.js';
3
+ import { collectSearchConsoleObservations } from '../discovery/searchConsole.js';
2
4
 
3
5
  const BASE = 'https://www.googleapis.com/webmasters/v3';
4
6
  const METRICS = {
@@ -25,17 +27,54 @@ export const gscConnector = {
25
27
  if (!dimensions.includes('date')) dimensions.unshift('date');
26
28
  const rows = [];
27
29
  let startRow = 0;
30
+ const items = [];
31
+ const errors = [];
32
+ const maxPages = paginationBoundary(context);
33
+ let pagesRequested = 0;
34
+ let pagesRetrieved = 0;
35
+ let continuationState = null;
36
+ let providerTotal = null;
37
+ let connectorError = null;
28
38
  do {
29
- const result = await providerRequest(`${BASE}/sites/${encodeURIComponent(connection.property_id)}/searchAnalytics/query`, {
30
- ...context, method: 'POST', body: { startDate, endDate, dimensions, rowLimit: 25000, startRow, dataState: 'final' },
31
- });
32
- for (const row of result.rows || []) {
39
+ pagesRequested += 1;
40
+ let result;
41
+ try {
42
+ result = await providerRequest(`${BASE}/sites/${encodeURIComponent(connection.property_id)}/searchAnalytics/query`, {
43
+ ...context, method: 'POST', body: { startDate, endDate, dimensions, rowLimit: 25000, startRow, dataState: 'final' },
44
+ });
45
+ } catch (error) {
46
+ connectorError ||= error;
47
+ errors.push(`start row ${startRow}: ${errorMessage(error)}`);
48
+ continuationState = { start_row: startRow };
49
+ break;
50
+ }
51
+ pagesRetrieved += 1;
52
+ const pageRows = result.rows || [];
53
+ items.push(...pageRows);
54
+ if (Number.isInteger(result.rowCount) && result.rowCount >= 0) providerTotal = result.rowCount;
55
+ for (const row of pageRows) {
33
56
  const values = Object.fromEntries(dimensions.map((name, index) => [name === 'page' ? 'url' : name, row.keys[index]]));
34
57
  for (const metric of metrics) rows.push({ metric, value: row[metric.external_name], dimensions: values, observed_at: `${values.date}T00:00:00.000Z` });
35
58
  }
36
- if ((result.rows || []).length < 25000) break;
59
+ if (pageRows.length < 25000) break;
37
60
  startRow += 25000;
61
+ if (pagesRequested >= maxPages) {
62
+ continuationState = { start_row: startRow };
63
+ break;
64
+ }
38
65
  } while (true);
39
- return { rows, cursor: endDate, limitations: ['Search Console privacy filtering and aggregation apply.', 'Search Analytics does not guarantee every data row; the API can return top rows only.', 'Final data can still be revised by the provider.'] };
66
+ const collection = collectionResult({
67
+ items,
68
+ paginationState: { pages_requested: pagesRequested, pages_retrieved: pagesRetrieved, boundary: { max_pages: maxPages } },
69
+ providerReportedTotal: providerTotal,
70
+ continuationState,
71
+ providerCompleteness: 'unknown',
72
+ limitations: ['Search Console privacy filtering and aggregation apply.', 'Search Analytics does not guarantee every data row; the API can return top rows only.', 'Final data can still be revised by the provider.'],
73
+ errors,
74
+ });
75
+ return { rows, cursor: endDate, limitations: collection.limitations, collection, ...(connectorError ? { connectorError } : {}) };
76
+ },
77
+ async collectObservations(connection, request, context = {}) {
78
+ return collectSearchConsoleObservations({ connection, request, ...context });
40
79
  },
41
80
  };
@@ -4,6 +4,7 @@ import { wordpressConnector } from './wordpress.js';
4
4
  import { webflowConnector } from './webflow.js';
5
5
  import { indexnowConnector } from './indexnow.js';
6
6
  import { mcpConnector } from './mcp/pilot.js';
7
+ export { collectionResult, paginationBoundary, errorMessage } from './collectionResult.js';
7
8
 
8
9
  const CONNECTORS = new Map([gscConnector, ga4Connector, wordpressConnector, webflowConnector, indexnowConnector, mcpConnector].map((connector) => [connector.provider, connector]));
9
10
 
@@ -18,4 +19,3 @@ export function listConnectors() {
18
19
  }
19
20
 
20
21
  export { McpClient, McpStdioTransport, McpHttpTransport, ALLOWED_MCP_SERVERS, isServerAllowlisted, isToolAllowlisted, assertToolAllowed, collectMcpEvidence, mcpConnector, diagnoseMcpTransports } from './mcp/index.js';
21
-