@nebulacomponents/citable 1.13.1 → 1.15.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 (359) hide show
  1. package/CHANGELOG.md +312 -0
  2. package/README.md +44 -6
  3. package/dist/universal/.agents/skills/citable/SKILL.md +22 -8
  4. package/dist/universal/.agents/skills/citable/VERSION +1 -1
  5. package/dist/universal/.agents/skills/citable/agents/citable-auditor.md +64 -0
  6. package/dist/universal/.agents/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  7. package/dist/universal/.agents/skills/citable/commands/README.md +4 -1
  8. package/dist/universal/.agents/skills/citable/commands/action-findings.md +9 -1
  9. package/dist/universal/.agents/skills/citable/commands/observe-and-act.md +62 -8
  10. package/dist/universal/.agents/skills/citable/commands/plan-audit.md +35 -0
  11. package/dist/universal/.agents/skills/citable/commands/reporting.md +57 -0
  12. package/dist/universal/.agents/skills/citable/manifest.json +29 -16
  13. package/dist/universal/.agents/skills/citable/schemas/action-plan.schema.json +13 -0
  14. package/dist/universal/.agents/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  15. package/dist/universal/.agents/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  16. package/dist/universal/.agents/skills/citable/schemas/common.defs.json +12 -1
  17. package/dist/universal/.agents/skills/citable/schemas/corroboration-import.schema.json +48 -0
  18. package/dist/universal/.agents/skills/citable/schemas/entity-graph.schema.json +75 -0
  19. package/dist/universal/.agents/skills/citable/schemas/entity.schema.json +13 -0
  20. package/dist/universal/.agents/skills/citable/schemas/observation.schema.json +1 -1
  21. package/dist/universal/.agents/skills/citable/schemas/page.schema.json +3 -0
  22. package/dist/universal/.agents/skills/citable/schemas/regional-network-import.schema.json +55 -0
  23. package/dist/universal/.agents/skills/citable/schemas/schedule.schema.json +1 -1
  24. package/dist/universal/.agents/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  25. package/dist/universal/.agents/skills/citable/scripts/README.md +3 -1
  26. package/dist/universal/.claude/agents/citable/citable-auditor.md +64 -0
  27. package/dist/universal/.claude/agents/citable/citable-semantic-reviewer.md +66 -0
  28. package/dist/universal/.claude/agents/citable/manifest.json +12 -0
  29. package/dist/universal/.claude/skills/citable/SKILL.md +22 -8
  30. package/dist/universal/.claude/skills/citable/VERSION +1 -1
  31. package/dist/universal/.claude/skills/citable/agents/citable-auditor.md +64 -0
  32. package/dist/universal/.claude/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  33. package/dist/universal/.claude/skills/citable/commands/README.md +4 -1
  34. package/dist/universal/.claude/skills/citable/commands/action-findings.md +9 -1
  35. package/dist/universal/.claude/skills/citable/commands/observe-and-act.md +62 -8
  36. package/dist/universal/.claude/skills/citable/commands/plan-audit.md +35 -0
  37. package/dist/universal/.claude/skills/citable/commands/reporting.md +57 -0
  38. package/dist/universal/.claude/skills/citable/manifest.json +34 -16
  39. package/dist/universal/.claude/skills/citable/schemas/action-plan.schema.json +13 -0
  40. package/dist/universal/.claude/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  41. package/dist/universal/.claude/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  42. package/dist/universal/.claude/skills/citable/schemas/common.defs.json +12 -1
  43. package/dist/universal/.claude/skills/citable/schemas/corroboration-import.schema.json +48 -0
  44. package/dist/universal/.claude/skills/citable/schemas/entity-graph.schema.json +75 -0
  45. package/dist/universal/.claude/skills/citable/schemas/entity.schema.json +13 -0
  46. package/dist/universal/.claude/skills/citable/schemas/observation.schema.json +1 -1
  47. package/dist/universal/.claude/skills/citable/schemas/page.schema.json +3 -0
  48. package/dist/universal/.claude/skills/citable/schemas/regional-network-import.schema.json +55 -0
  49. package/dist/universal/.claude/skills/citable/schemas/schedule.schema.json +1 -1
  50. package/dist/universal/.claude/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  51. package/dist/universal/.claude/skills/citable/scripts/README.md +3 -1
  52. package/dist/universal/.cursor/skills/citable/SKILL.md +22 -8
  53. package/dist/universal/.cursor/skills/citable/VERSION +1 -1
  54. package/dist/universal/.cursor/skills/citable/agents/citable-auditor.md +64 -0
  55. package/dist/universal/.cursor/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  56. package/dist/universal/.cursor/skills/citable/commands/README.md +4 -1
  57. package/dist/universal/.cursor/skills/citable/commands/action-findings.md +9 -1
  58. package/dist/universal/.cursor/skills/citable/commands/observe-and-act.md +62 -8
  59. package/dist/universal/.cursor/skills/citable/commands/plan-audit.md +35 -0
  60. package/dist/universal/.cursor/skills/citable/commands/reporting.md +57 -0
  61. package/dist/universal/.cursor/skills/citable/manifest.json +29 -16
  62. package/dist/universal/.cursor/skills/citable/schemas/action-plan.schema.json +13 -0
  63. package/dist/universal/.cursor/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  64. package/dist/universal/.cursor/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  65. package/dist/universal/.cursor/skills/citable/schemas/common.defs.json +12 -1
  66. package/dist/universal/.cursor/skills/citable/schemas/corroboration-import.schema.json +48 -0
  67. package/dist/universal/.cursor/skills/citable/schemas/entity-graph.schema.json +75 -0
  68. package/dist/universal/.cursor/skills/citable/schemas/entity.schema.json +13 -0
  69. package/dist/universal/.cursor/skills/citable/schemas/observation.schema.json +1 -1
  70. package/dist/universal/.cursor/skills/citable/schemas/page.schema.json +3 -0
  71. package/dist/universal/.cursor/skills/citable/schemas/regional-network-import.schema.json +55 -0
  72. package/dist/universal/.cursor/skills/citable/schemas/schedule.schema.json +1 -1
  73. package/dist/universal/.cursor/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  74. package/dist/universal/.cursor/skills/citable/scripts/README.md +3 -1
  75. package/dist/universal/.gemini/skills/citable/SKILL.md +22 -8
  76. package/dist/universal/.gemini/skills/citable/VERSION +1 -1
  77. package/dist/universal/.gemini/skills/citable/agents/citable-auditor.md +64 -0
  78. package/dist/universal/.gemini/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  79. package/dist/universal/.gemini/skills/citable/commands/README.md +4 -1
  80. package/dist/universal/.gemini/skills/citable/commands/action-findings.md +9 -1
  81. package/dist/universal/.gemini/skills/citable/commands/observe-and-act.md +62 -8
  82. package/dist/universal/.gemini/skills/citable/commands/plan-audit.md +35 -0
  83. package/dist/universal/.gemini/skills/citable/commands/reporting.md +57 -0
  84. package/dist/universal/.gemini/skills/citable/manifest.json +29 -16
  85. package/dist/universal/.gemini/skills/citable/schemas/action-plan.schema.json +13 -0
  86. package/dist/universal/.gemini/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  87. package/dist/universal/.gemini/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  88. package/dist/universal/.gemini/skills/citable/schemas/common.defs.json +12 -1
  89. package/dist/universal/.gemini/skills/citable/schemas/corroboration-import.schema.json +48 -0
  90. package/dist/universal/.gemini/skills/citable/schemas/entity-graph.schema.json +75 -0
  91. package/dist/universal/.gemini/skills/citable/schemas/entity.schema.json +13 -0
  92. package/dist/universal/.gemini/skills/citable/schemas/observation.schema.json +1 -1
  93. package/dist/universal/.gemini/skills/citable/schemas/page.schema.json +3 -0
  94. package/dist/universal/.gemini/skills/citable/schemas/regional-network-import.schema.json +55 -0
  95. package/dist/universal/.gemini/skills/citable/schemas/schedule.schema.json +1 -1
  96. package/dist/universal/.gemini/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  97. package/dist/universal/.gemini/skills/citable/scripts/README.md +3 -1
  98. package/dist/universal/.github/skills/citable/SKILL.md +22 -8
  99. package/dist/universal/.github/skills/citable/VERSION +1 -1
  100. package/dist/universal/.github/skills/citable/agents/citable-auditor.md +64 -0
  101. package/dist/universal/.github/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  102. package/dist/universal/.github/skills/citable/commands/README.md +4 -1
  103. package/dist/universal/.github/skills/citable/commands/action-findings.md +9 -1
  104. package/dist/universal/.github/skills/citable/commands/observe-and-act.md +62 -8
  105. package/dist/universal/.github/skills/citable/commands/plan-audit.md +35 -0
  106. package/dist/universal/.github/skills/citable/commands/reporting.md +57 -0
  107. package/dist/universal/.github/skills/citable/manifest.json +29 -16
  108. package/dist/universal/.github/skills/citable/schemas/action-plan.schema.json +13 -0
  109. package/dist/universal/.github/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  110. package/dist/universal/.github/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  111. package/dist/universal/.github/skills/citable/schemas/common.defs.json +12 -1
  112. package/dist/universal/.github/skills/citable/schemas/corroboration-import.schema.json +48 -0
  113. package/dist/universal/.github/skills/citable/schemas/entity-graph.schema.json +75 -0
  114. package/dist/universal/.github/skills/citable/schemas/entity.schema.json +13 -0
  115. package/dist/universal/.github/skills/citable/schemas/observation.schema.json +1 -1
  116. package/dist/universal/.github/skills/citable/schemas/page.schema.json +3 -0
  117. package/dist/universal/.github/skills/citable/schemas/regional-network-import.schema.json +55 -0
  118. package/dist/universal/.github/skills/citable/schemas/schedule.schema.json +1 -1
  119. package/dist/universal/.github/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  120. package/dist/universal/.github/skills/citable/scripts/README.md +3 -1
  121. package/dist/universal/.kiro/skills/citable/SKILL.md +22 -8
  122. package/dist/universal/.kiro/skills/citable/VERSION +1 -1
  123. package/dist/universal/.kiro/skills/citable/agents/citable-auditor.md +64 -0
  124. package/dist/universal/.kiro/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  125. package/dist/universal/.kiro/skills/citable/commands/README.md +4 -1
  126. package/dist/universal/.kiro/skills/citable/commands/action-findings.md +9 -1
  127. package/dist/universal/.kiro/skills/citable/commands/observe-and-act.md +62 -8
  128. package/dist/universal/.kiro/skills/citable/commands/plan-audit.md +35 -0
  129. package/dist/universal/.kiro/skills/citable/commands/reporting.md +57 -0
  130. package/dist/universal/.kiro/skills/citable/manifest.json +29 -16
  131. package/dist/universal/.kiro/skills/citable/schemas/action-plan.schema.json +13 -0
  132. package/dist/universal/.kiro/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  133. package/dist/universal/.kiro/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  134. package/dist/universal/.kiro/skills/citable/schemas/common.defs.json +12 -1
  135. package/dist/universal/.kiro/skills/citable/schemas/corroboration-import.schema.json +48 -0
  136. package/dist/universal/.kiro/skills/citable/schemas/entity-graph.schema.json +75 -0
  137. package/dist/universal/.kiro/skills/citable/schemas/entity.schema.json +13 -0
  138. package/dist/universal/.kiro/skills/citable/schemas/observation.schema.json +1 -1
  139. package/dist/universal/.kiro/skills/citable/schemas/page.schema.json +3 -0
  140. package/dist/universal/.kiro/skills/citable/schemas/regional-network-import.schema.json +55 -0
  141. package/dist/universal/.kiro/skills/citable/schemas/schedule.schema.json +1 -1
  142. package/dist/universal/.kiro/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  143. package/dist/universal/.kiro/skills/citable/scripts/README.md +3 -1
  144. package/dist/universal/.opencode/skills/citable/SKILL.md +22 -8
  145. package/dist/universal/.opencode/skills/citable/VERSION +1 -1
  146. package/dist/universal/.opencode/skills/citable/agents/citable-auditor.md +64 -0
  147. package/dist/universal/.opencode/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  148. package/dist/universal/.opencode/skills/citable/commands/README.md +4 -1
  149. package/dist/universal/.opencode/skills/citable/commands/action-findings.md +9 -1
  150. package/dist/universal/.opencode/skills/citable/commands/observe-and-act.md +62 -8
  151. package/dist/universal/.opencode/skills/citable/commands/plan-audit.md +35 -0
  152. package/dist/universal/.opencode/skills/citable/commands/reporting.md +57 -0
  153. package/dist/universal/.opencode/skills/citable/manifest.json +29 -16
  154. package/dist/universal/.opencode/skills/citable/schemas/action-plan.schema.json +13 -0
  155. package/dist/universal/.opencode/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  156. package/dist/universal/.opencode/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  157. package/dist/universal/.opencode/skills/citable/schemas/common.defs.json +12 -1
  158. package/dist/universal/.opencode/skills/citable/schemas/corroboration-import.schema.json +48 -0
  159. package/dist/universal/.opencode/skills/citable/schemas/entity-graph.schema.json +75 -0
  160. package/dist/universal/.opencode/skills/citable/schemas/entity.schema.json +13 -0
  161. package/dist/universal/.opencode/skills/citable/schemas/observation.schema.json +1 -1
  162. package/dist/universal/.opencode/skills/citable/schemas/page.schema.json +3 -0
  163. package/dist/universal/.opencode/skills/citable/schemas/regional-network-import.schema.json +55 -0
  164. package/dist/universal/.opencode/skills/citable/schemas/schedule.schema.json +1 -1
  165. package/dist/universal/.opencode/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  166. package/dist/universal/.opencode/skills/citable/scripts/README.md +3 -1
  167. package/dist/universal/.pi/agent/skills/citable/SKILL.md +22 -8
  168. package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
  169. package/dist/universal/.pi/agent/skills/citable/agents/citable-auditor.md +64 -0
  170. package/dist/universal/.pi/agent/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  171. package/dist/universal/.pi/agent/skills/citable/commands/README.md +4 -1
  172. package/dist/universal/.pi/agent/skills/citable/commands/action-findings.md +9 -1
  173. package/dist/universal/.pi/agent/skills/citable/commands/observe-and-act.md +62 -8
  174. package/dist/universal/.pi/agent/skills/citable/commands/plan-audit.md +35 -0
  175. package/dist/universal/.pi/agent/skills/citable/commands/reporting.md +57 -0
  176. package/dist/universal/.pi/agent/skills/citable/manifest.json +29 -16
  177. package/dist/universal/.pi/agent/skills/citable/schemas/action-plan.schema.json +13 -0
  178. package/dist/universal/.pi/agent/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  179. package/dist/universal/.pi/agent/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  180. package/dist/universal/.pi/agent/skills/citable/schemas/common.defs.json +12 -1
  181. package/dist/universal/.pi/agent/skills/citable/schemas/corroboration-import.schema.json +48 -0
  182. package/dist/universal/.pi/agent/skills/citable/schemas/entity-graph.schema.json +75 -0
  183. package/dist/universal/.pi/agent/skills/citable/schemas/entity.schema.json +13 -0
  184. package/dist/universal/.pi/agent/skills/citable/schemas/observation.schema.json +1 -1
  185. package/dist/universal/.pi/agent/skills/citable/schemas/page.schema.json +3 -0
  186. package/dist/universal/.pi/agent/skills/citable/schemas/regional-network-import.schema.json +55 -0
  187. package/dist/universal/.pi/agent/skills/citable/schemas/schedule.schema.json +1 -1
  188. package/dist/universal/.pi/agent/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  189. package/dist/universal/.pi/agent/skills/citable/scripts/README.md +3 -1
  190. package/dist/universal/.qoder/skills/citable/SKILL.md +22 -8
  191. package/dist/universal/.qoder/skills/citable/VERSION +1 -1
  192. package/dist/universal/.qoder/skills/citable/agents/citable-auditor.md +64 -0
  193. package/dist/universal/.qoder/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  194. package/dist/universal/.qoder/skills/citable/commands/README.md +4 -1
  195. package/dist/universal/.qoder/skills/citable/commands/action-findings.md +9 -1
  196. package/dist/universal/.qoder/skills/citable/commands/observe-and-act.md +62 -8
  197. package/dist/universal/.qoder/skills/citable/commands/plan-audit.md +35 -0
  198. package/dist/universal/.qoder/skills/citable/commands/reporting.md +57 -0
  199. package/dist/universal/.qoder/skills/citable/manifest.json +29 -16
  200. package/dist/universal/.qoder/skills/citable/schemas/action-plan.schema.json +13 -0
  201. package/dist/universal/.qoder/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  202. package/dist/universal/.qoder/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  203. package/dist/universal/.qoder/skills/citable/schemas/common.defs.json +12 -1
  204. package/dist/universal/.qoder/skills/citable/schemas/corroboration-import.schema.json +48 -0
  205. package/dist/universal/.qoder/skills/citable/schemas/entity-graph.schema.json +75 -0
  206. package/dist/universal/.qoder/skills/citable/schemas/entity.schema.json +13 -0
  207. package/dist/universal/.qoder/skills/citable/schemas/observation.schema.json +1 -1
  208. package/dist/universal/.qoder/skills/citable/schemas/page.schema.json +3 -0
  209. package/dist/universal/.qoder/skills/citable/schemas/regional-network-import.schema.json +55 -0
  210. package/dist/universal/.qoder/skills/citable/schemas/schedule.schema.json +1 -1
  211. package/dist/universal/.qoder/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  212. package/dist/universal/.qoder/skills/citable/scripts/README.md +3 -1
  213. package/dist/universal/.rovodev/skills/citable/SKILL.md +22 -8
  214. package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
  215. package/dist/universal/.rovodev/skills/citable/agents/citable-auditor.md +64 -0
  216. package/dist/universal/.rovodev/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  217. package/dist/universal/.rovodev/skills/citable/commands/README.md +4 -1
  218. package/dist/universal/.rovodev/skills/citable/commands/action-findings.md +9 -1
  219. package/dist/universal/.rovodev/skills/citable/commands/observe-and-act.md +62 -8
  220. package/dist/universal/.rovodev/skills/citable/commands/plan-audit.md +35 -0
  221. package/dist/universal/.rovodev/skills/citable/commands/reporting.md +57 -0
  222. package/dist/universal/.rovodev/skills/citable/manifest.json +29 -16
  223. package/dist/universal/.rovodev/skills/citable/schemas/action-plan.schema.json +13 -0
  224. package/dist/universal/.rovodev/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  225. package/dist/universal/.rovodev/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  226. package/dist/universal/.rovodev/skills/citable/schemas/common.defs.json +12 -1
  227. package/dist/universal/.rovodev/skills/citable/schemas/corroboration-import.schema.json +48 -0
  228. package/dist/universal/.rovodev/skills/citable/schemas/entity-graph.schema.json +75 -0
  229. package/dist/universal/.rovodev/skills/citable/schemas/entity.schema.json +13 -0
  230. package/dist/universal/.rovodev/skills/citable/schemas/observation.schema.json +1 -1
  231. package/dist/universal/.rovodev/skills/citable/schemas/page.schema.json +3 -0
  232. package/dist/universal/.rovodev/skills/citable/schemas/regional-network-import.schema.json +55 -0
  233. package/dist/universal/.rovodev/skills/citable/schemas/schedule.schema.json +1 -1
  234. package/dist/universal/.rovodev/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  235. package/dist/universal/.rovodev/skills/citable/scripts/README.md +3 -1
  236. package/dist/universal/.trae/skills/citable/SKILL.md +22 -8
  237. package/dist/universal/.trae/skills/citable/VERSION +1 -1
  238. package/dist/universal/.trae/skills/citable/agents/citable-auditor.md +64 -0
  239. package/dist/universal/.trae/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  240. package/dist/universal/.trae/skills/citable/commands/README.md +4 -1
  241. package/dist/universal/.trae/skills/citable/commands/action-findings.md +9 -1
  242. package/dist/universal/.trae/skills/citable/commands/observe-and-act.md +62 -8
  243. package/dist/universal/.trae/skills/citable/commands/plan-audit.md +35 -0
  244. package/dist/universal/.trae/skills/citable/commands/reporting.md +57 -0
  245. package/dist/universal/.trae/skills/citable/manifest.json +29 -16
  246. package/dist/universal/.trae/skills/citable/schemas/action-plan.schema.json +13 -0
  247. package/dist/universal/.trae/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  248. package/dist/universal/.trae/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  249. package/dist/universal/.trae/skills/citable/schemas/common.defs.json +12 -1
  250. package/dist/universal/.trae/skills/citable/schemas/corroboration-import.schema.json +48 -0
  251. package/dist/universal/.trae/skills/citable/schemas/entity-graph.schema.json +75 -0
  252. package/dist/universal/.trae/skills/citable/schemas/entity.schema.json +13 -0
  253. package/dist/universal/.trae/skills/citable/schemas/observation.schema.json +1 -1
  254. package/dist/universal/.trae/skills/citable/schemas/page.schema.json +3 -0
  255. package/dist/universal/.trae/skills/citable/schemas/regional-network-import.schema.json +55 -0
  256. package/dist/universal/.trae/skills/citable/schemas/schedule.schema.json +1 -1
  257. package/dist/universal/.trae/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  258. package/dist/universal/.trae/skills/citable/scripts/README.md +3 -1
  259. package/dist/universal/.trae-cn/skills/citable/SKILL.md +22 -8
  260. package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
  261. package/dist/universal/.trae-cn/skills/citable/agents/citable-auditor.md +64 -0
  262. package/dist/universal/.trae-cn/skills/citable/agents/citable-semantic-reviewer.md +66 -0
  263. package/dist/universal/.trae-cn/skills/citable/commands/README.md +4 -1
  264. package/dist/universal/.trae-cn/skills/citable/commands/action-findings.md +9 -1
  265. package/dist/universal/.trae-cn/skills/citable/commands/observe-and-act.md +62 -8
  266. package/dist/universal/.trae-cn/skills/citable/commands/plan-audit.md +35 -0
  267. package/dist/universal/.trae-cn/skills/citable/commands/reporting.md +57 -0
  268. package/dist/universal/.trae-cn/skills/citable/manifest.json +29 -16
  269. package/dist/universal/.trae-cn/skills/citable/schemas/action-plan.schema.json +13 -0
  270. package/dist/universal/.trae-cn/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
  271. package/dist/universal/.trae-cn/skills/citable/schemas/canonical-index-import.schema.json +30 -0
  272. package/dist/universal/.trae-cn/skills/citable/schemas/common.defs.json +12 -1
  273. package/dist/universal/.trae-cn/skills/citable/schemas/corroboration-import.schema.json +48 -0
  274. package/dist/universal/.trae-cn/skills/citable/schemas/entity-graph.schema.json +75 -0
  275. package/dist/universal/.trae-cn/skills/citable/schemas/entity.schema.json +13 -0
  276. package/dist/universal/.trae-cn/skills/citable/schemas/observation.schema.json +1 -1
  277. package/dist/universal/.trae-cn/skills/citable/schemas/page.schema.json +3 -0
  278. package/dist/universal/.trae-cn/skills/citable/schemas/regional-network-import.schema.json +55 -0
  279. package/dist/universal/.trae-cn/skills/citable/schemas/schedule.schema.json +1 -1
  280. package/dist/universal/.trae-cn/skills/citable/schemas/source-identity-chain.schema.json +83 -0
  281. package/dist/universal/.trae-cn/skills/citable/scripts/README.md +3 -1
  282. package/dist/universal/manifest.json +306 -195
  283. package/package.json +1 -1
  284. package/schemas/action-plan.schema.json +13 -0
  285. package/schemas/browser-evidence-plan.schema.json +14 -0
  286. package/schemas/canonical-index-import.schema.json +30 -0
  287. package/schemas/common.defs.json +12 -1
  288. package/schemas/corroboration-import.schema.json +48 -0
  289. package/schemas/entity-graph.schema.json +75 -0
  290. package/schemas/entity.schema.json +13 -0
  291. package/schemas/observation.schema.json +1 -1
  292. package/schemas/page.schema.json +3 -0
  293. package/schemas/regional-network-import.schema.json +55 -0
  294. package/schemas/schedule.schema.json +1 -1
  295. package/schemas/source-identity-chain.schema.json +83 -0
  296. package/scripts/build-dist.js +36 -3
  297. package/skill/SKILL.md +22 -8
  298. package/skill/agents/citable-auditor.md +64 -0
  299. package/skill/agents/citable-semantic-reviewer.md +66 -0
  300. package/skill/commands/README.md +4 -1
  301. package/skill/commands/action-findings.md +9 -1
  302. package/skill/commands/observe-and-act.md +62 -8
  303. package/skill/commands/plan-audit.md +35 -0
  304. package/skill/commands/reporting.md +57 -0
  305. package/src/cli/index.js +115 -15
  306. package/src/commands/actionPlan.js +12 -1
  307. package/src/commands/audit.js +4 -0
  308. package/src/commands/connect.js +29 -0
  309. package/src/commands/context.js +15 -0
  310. package/src/commands/delivery.js +86 -2
  311. package/src/commands/demo.js +36 -0
  312. package/src/commands/fixtures/demo/.citable/claims.yaml +27 -0
  313. package/src/commands/fixtures/demo/.citable/competitors.yaml +3 -0
  314. package/src/commands/fixtures/demo/.citable/config.yaml +18 -0
  315. package/src/commands/fixtures/demo/.citable/crawlers.yaml +30 -0
  316. package/src/commands/fixtures/demo/.citable/entities.yaml +28 -0
  317. package/src/commands/fixtures/demo/.citable/evidence.yaml +19 -0
  318. package/src/commands/fixtures/demo/.citable/experiments.yaml +3 -0
  319. package/src/commands/fixtures/demo/.citable/pages.yaml +54 -0
  320. package/src/commands/fixtures/demo/.citable/prompts.yaml +11 -0
  321. package/src/commands/fixtures/demo/.citable/queries.yaml +10 -0
  322. package/src/commands/fixtures/demo/site/index.html +12 -0
  323. package/src/commands/fixtures/demo/site/learn/execution-governance/index.html +14 -0
  324. package/src/commands/fixtures/demo/site/pricing/index.html +10 -0
  325. package/src/commands/fixtures/demo/site/products/gatekeeper/index.html +15 -0
  326. package/src/commands/fixtures/demo/site/robots.txt +7 -0
  327. package/src/commands/fixtures/demo/site/sitemap.xml +7 -0
  328. package/src/commands/init.js +5 -2
  329. package/src/commands/monitor.js +35 -0
  330. package/src/commands/observe.js +315 -15
  331. package/src/commands/planAudit.js +151 -0
  332. package/src/commands/reportDashboard.js +21 -0
  333. package/src/commands/reportShareOfVoice.js +60 -0
  334. package/src/commands/schemaCmd.js +5 -1
  335. package/src/commands/substantiate.js +22 -10
  336. package/src/connectors/http.js +6 -3
  337. package/src/connectors/index.js +3 -1
  338. package/src/connectors/webflow.js +224 -0
  339. package/src/connectors/wordpress.js +226 -0
  340. package/src/crawler/fetch.js +34 -6
  341. package/src/detectors/geoReco.js +101 -0
  342. package/src/extractor/page.js +17 -0
  343. package/src/installer/index.js +282 -28
  344. package/src/installer/providers.js +11 -0
  345. package/src/monitoring/alertDelivery.js +98 -0
  346. package/src/observations/browserJourney.js +15 -7
  347. package/src/observations/claimDiff.js +83 -0
  348. package/src/observations/common.js +25 -0
  349. package/src/observations/entityGraph.js +89 -0
  350. package/src/observations/renderParity.js +49 -0
  351. package/src/observations/sourceIdentity.js +62 -0
  352. package/src/observations/stance.js +261 -0
  353. package/src/registries/seed.js +95 -0
  354. package/src/registries/seeds/saas-pricing/claims.yaml +18 -0
  355. package/src/registries/seeds/saas-pricing/entities.yaml +20 -0
  356. package/src/registries/seeds/saas-pricing/seed.json +6 -0
  357. package/src/reporting/dashboard.js +254 -0
  358. package/src/reporting/shareOfVoice.js +362 -0
  359. package/src/shared/checklist.js +19 -0
@@ -1,7 +1,8 @@
1
1
  import { buildContext } from './context.js';
2
2
  import { envelope, observationRun, readInput } from '../observations/common.js';
3
- import { sha256 } from '../shared/io.js';
4
- import { fetchUrl } from '../crawler/fetch.js';
3
+ import { buildClaimDiffContext, diffAssertion } from '../observations/claimDiff.js';
4
+ import { readYaml, sha256 } from '../shared/io.js';
5
+ import { fetchUrl, handlePublicBrowserRoute, validatePublicUrl } from '../crawler/fetch.js';
5
6
  import fs from 'node:fs';
6
7
  import path from 'node:path';
7
8
  import { parse as parseCsv } from 'csv-parse/sync';
@@ -11,6 +12,7 @@ import { observeMedia } from '../observations/media.js';
11
12
  import { parse as parseHtml } from 'node-html-parser';
12
13
  import { verifyManifestIntegrity } from '../release/governance.js';
13
14
  import { observeBrowserPlan } from '../observations/browserJourney.js';
15
+ import { observeStance } from '../observations/stance.js';
14
16
 
15
17
  const originOf = (value) => { try { return new URL(value).origin; } catch { return null; } };
16
18
  const words = (text) => String(text || '').trim().split(/\s+/).filter(Boolean);
@@ -28,6 +30,20 @@ function canonicalReview(raw, targetOrigin) {
28
30
  const url = typeof citation === 'string' ? citation : citation.url;
29
31
  const canonical = typeof citation === 'object' ? citation.canonical_url || url : url;
30
32
  const supports = typeof citation === 'object' ? citation.support_status || 'review_required' : 'review_required';
33
+ const sourceProfile = typeof citation === 'object' ? citation.source_profile || null : null;
34
+ if (sourceProfile) {
35
+ const formats = new Set(['article', 'documentation', 'product', 'landing_page', 'comparison', 'forum', 'video', 'pdf', 'other']);
36
+ const allowedKeys = new Set(['content_format', 'word_count', 'heading_count', 'freshness_date', 'evidence_features']);
37
+ if (Object.keys(sourceProfile).some((key) => !allowedKeys.has(key))
38
+ || !formats.has(sourceProfile.content_format)
39
+ || !Number.isInteger(sourceProfile.word_count) || sourceProfile.word_count < 0
40
+ || !Number.isInteger(sourceProfile.heading_count) || sourceProfile.heading_count < 0
41
+ || !strictDate(sourceProfile.freshness_date)
42
+ || !Array.isArray(sourceProfile.evidence_features)
43
+ || sourceProfile.evidence_features.some((item) => typeof item !== 'string' || !item.trim())) {
44
+ throw new Error(`citation ${index + 1} source_profile violates the declared competitive profile contract`);
45
+ }
46
+ }
31
47
  return {
32
48
  citation_url: url, canonical_url: canonical, citation_order: index + 1,
33
49
  first_party: Boolean(targetOrigin && originOf(canonical) === targetOrigin),
@@ -35,6 +51,7 @@ function canonicalReview(raw, targetOrigin) {
35
51
  answer_claim: typeof citation === 'object' ? citation.answer_claim || null : null,
36
52
  source_passage: typeof citation === 'object' ? citation.source_passage || null : null,
37
53
  reviewer: typeof citation === 'object' ? citation.reviewer || null : null,
54
+ source_profile: sourceProfile,
38
55
  };
39
56
  });
40
57
  }
@@ -42,6 +59,7 @@ function canonicalReview(raw, targetOrigin) {
42
59
  async function observeRender(root, options) {
43
60
  if (options.input) return observeBrowserPlan(root, options);
44
61
  if (!options.target || !/^https?:\/\//.test(options.target)) throw new Error('render requires --target <http(s) URL>');
62
+ await validatePublicUrl(options.target, options.lookup ? { lookup: options.lookup } : {});
45
63
  const profileNames = ['desktop', 'mobile', 'javascript_disabled'];
46
64
  let reused = [], previousRaw = null;
47
65
  if (options.resumeRun) {
@@ -66,6 +84,7 @@ async function observeRender(root, options) {
66
84
  capture = async (name, viewport, { isMobile = false, javaScriptEnabled = true } = {}) => {
67
85
  const context = await browser.newContext({ viewport, isMobile, javaScriptEnabled });
68
86
  try {
87
+ await context.route('**/*', (route) => handlePublicBrowserRoute(route, options.lookup ? { lookup: options.lookup } : {}));
69
88
  const page = await context.newPage();
70
89
  const failures = [];
71
90
  page.on('requestfailed', (request) => failures.push({ url: request.url(), error: request.failure()?.errorText || 'unknown' }));
@@ -130,11 +149,12 @@ async function observeCitations(root, options) {
130
149
  if (options.endpoint) {
131
150
  const prompts = input.value.prompts || (Array.isArray(input.value) ? input.value : []);
132
151
  if (!prompts.length) throw new Error('citation runner input must contain a prompts array');
133
- if (!/^https:\/\//.test(options.endpoint) && !/^http:\/\/(127\.0\.0\.1|localhost)(:\d+)?\//.test(options.endpoint)) throw new Error('citation adapter endpoint must use HTTPS (loopback HTTP is allowed for testing)');
152
+ if (!/^https:\/\//.test(options.endpoint)) throw new Error('citation adapter endpoint must use HTTPS');
153
+ await validatePublicUrl(options.endpoint, options.lookup ? { lookup: options.lookup } : {});
134
154
  const repeat = Number.isInteger(options.repeat) && options.repeat > 0 && options.repeat <= 20 ? options.repeat : 3;
135
155
  rows = [];
136
156
  for (const prompt of prompts) for (let runIndex = 1; runIndex <= repeat; runIndex++) {
137
- const response = await fetch(options.endpoint, { method: 'POST', headers: { 'content-type': 'application/json', ...(options.accessToken ? { authorization: `Bearer ${options.accessToken}` } : {}) }, body: JSON.stringify({ prompt_id: prompt.prompt_id, prompt_text: prompt.prompt_text, locale: prompt.locale || input.value.locale || 'en-US', run_index: runIndex, runs_in_series: repeat }) });
157
+ const response = await (options.adapterFetch || globalThis.fetch)(options.endpoint, { method: 'POST', redirect: 'error', headers: { 'content-type': 'application/json', ...(options.accessToken ? { authorization: `Bearer ${options.accessToken}` } : {}) }, body: JSON.stringify({ prompt_id: prompt.prompt_id, prompt_text: prompt.prompt_text, locale: prompt.locale || input.value.locale || 'en-US', run_index: runIndex, runs_in_series: repeat }) });
138
158
  const rawResponse = await response.text();
139
159
  if (!response.ok) throw new Error(`citation adapter returned ${response.status}: ${rawResponse.slice(0, 300)}`);
140
160
  const result = JSON.parse(rawResponse);
@@ -143,14 +163,23 @@ async function observeCitations(root, options) {
143
163
  method = 'live_api';
144
164
  }
145
165
  const targetOrigin = options.target ? originOf(options.target) : input.value.target_origin || null;
166
+ // Registries-only context (no `target` passed — this never fetches or crawls); degrades
167
+ // gracefully to empty registries (and therefore all-`unsupported` diffs) when no
168
+ // `.citable/` exists yet, per loadRegistries()'s missing-file-loads-as-empty contract.
169
+ const diffCtx = buildClaimDiffContext(root, options.refDate);
146
170
  const observations = [];
147
171
  for (const row of rows) {
148
- const reviews = canonicalReview(row, targetOrigin);
172
+ const reviews = canonicalReview(row, targetOrigin).map((review) => ({
173
+ ...review,
174
+ claim_diff: diffAssertion(review.answer_claim, review.canonical_url, diffCtx),
175
+ }));
176
+ const diffCounts = { confirmed: 0, contradicted: 0, stale: 0, unsupported: 0, not_checked: 0 };
177
+ for (const review of reviews) diffCounts[review.claim_diff?.status ?? 'not_checked']++;
149
178
  observations.push(envelope('citation', {
150
179
  prompt_id: row.prompt_id, prompt_text: row.prompt_text, provider: row.provider || row.engine,
151
180
  product_mode: row.product_mode || row.interface || 'unknown', locale: row.locale || 'unknown',
152
181
  answer_text: row.answer_text || '', citations: reviews, run_index: row.run_index || 1,
153
- property_cited: reviews.some((r) => r.first_party),
182
+ property_cited: reviews.some((r) => r.first_party), claim_diff_summary: diffCounts,
154
183
  }, { method, source: options.endpoint || input.file, raw: JSON.stringify(row) }));
155
184
  for (const review of reviews) observations.push(envelope('citation_review', review, {
156
185
  method: review.reviewer ? 'human_review' : 'static_analysis', source: options.endpoint || input.file,
@@ -159,7 +188,8 @@ async function observeCitations(root, options) {
159
188
  limitations: review.reviewer ? [] : ['Material support requires a named human reviewer.'],
160
189
  }));
161
190
  }
162
- return observationRun(root, 'observe citations', input.file, observations, { rawInputs: { citation_results: input.raw } });
191
+ const claimDiffWarnings = diffCtx.problems.map((p) => `claim-diff registry problem (diff degraded to unsupported where affected): ${p}`);
192
+ return observationRun(root, 'observe citations', input.file, observations, { rawInputs: { citation_results: input.raw }, warnings: claimDiffWarnings });
163
193
  }
164
194
 
165
195
  function observeLogs(root, options) {
@@ -248,24 +278,82 @@ async function observePassages(root, options) {
248
278
  const ctx = await buildContext(root, options);
249
279
  if (!ctx.site) throw new Error('passages requires --target <dir|url>');
250
280
  const observations = [];
281
+ const regionPages = new Map();
282
+ for (const page of ctx.site.pages) {
283
+ for (const region of page.structuralRegions || []) {
284
+ const key = sha256(`${region.region_type}\0${region.text}`);
285
+ if (!regionPages.has(key)) regionPages.set(key, new Set());
286
+ regionPages.get(key).add(page.url);
287
+ }
288
+ }
251
289
  for (const page of ctx.site.pages) {
252
290
  const rawWords = words(page.text).length;
291
+ const pageRecord = ctx.registries.pages?.entries?.find((item) => {
292
+ try { return new URL(item.url, page.url).href === page.url; } catch { return item.url === page.url; }
293
+ });
294
+ const promptMap = new Map((ctx.registries.prompts?.entries || []).map((item) => [item.prompt_id, item]));
295
+ const questionContexts = (pageRecord?.target_prompts || []).map((id) => promptMap.get(id)).filter(Boolean).map((prompt) => ({
296
+ prompt_id: prompt.prompt_id,
297
+ question: prompt.canonical_prompt,
298
+ expected_answer_components: prompt.expected_answer_components || [],
299
+ }));
300
+ const extractionMetrics = {
301
+ raw_bytes: Buffer.byteLength(page.rawHtml),
302
+ extracted_bytes: Buffer.byteLength(page.text),
303
+ raw_visible_tokens: page.rawVisibleWordCount,
304
+ extracted_tokens: rawWords,
305
+ extracted_to_raw_byte_ratio: page.rawHtml.length ? Number((Buffer.byteLength(page.text) / Buffer.byteLength(page.rawHtml)).toFixed(3)) : null,
306
+ extracted_to_raw_visible_token_ratio: page.rawVisibleWordCount ? Number((rawWords / page.rawVisibleWordCount).toFixed(3)) : null,
307
+ };
308
+ const repeatedRegions = (page.structuralRegions || []).map((region) => {
309
+ const regionHash = sha256(`${region.region_type}\0${region.text}`);
310
+ const pageOccurrences = regionPages.get(regionHash)?.size || 1;
311
+ return {
312
+ region_type: region.region_type,
313
+ region_hash: regionHash,
314
+ html_bytes: region.html_bytes,
315
+ token_count: region.token_count,
316
+ page_occurrences: pageOccurrences,
317
+ classification: pageOccurrences >= 2 ? 'repeated_site_region' : 'page_local_region',
318
+ };
319
+ });
320
+ const limitations = ['Repeated structural regions and extraction ratios are descriptive cohort evidence; they do not establish content harm, retrieval impact, or ranking effect.'];
253
321
  for (let i = 0; i < page.paragraphs.length; i++) {
254
322
  const passage = page.paragraphs.slice(i, i + 3).join(' ');
255
323
  const count = words(passage).length;
256
324
  if (count < 30) continue;
257
325
  const dependencies = [];
258
326
  if (/\b(this|that|these|those|it|they|above|below|here)\b/i.test(passage.slice(0, 100))) dependencies.push('possible external referent');
259
- observations.push(envelope('passage', { url: page.url, passage_index: i, text: passage, word_count: count, independently_extractable: count >= 100 && count <= 300 && dependencies.length === 0, dependencies, content_to_noise_ratio: page.rawHtml.length ? Number((page.text.length / page.rawHtml.length).toFixed(3)) : null }, { method: 'static_analysis', source: page.sourceFile || page.url, raw: passage, confidence: 'medium' }));
327
+ observations.push(envelope('passage', { url: page.url, passage_contract_version: 1, passage_index: i, text: passage, word_count: count, independently_extractable: count >= 100 && count <= 300 && dependencies.length === 0, dependencies, question_contexts: questionContexts, semantic_review_status: 'review_required', content_to_noise_ratio: extractionMetrics.extracted_to_raw_byte_ratio, extraction_metrics: extractionMetrics, repeated_regions: repeatedRegions }, { method: 'static_analysis', source: page.sourceFile || page.url, raw: passage, confidence: 'medium', limitations: [...limitations, 'Question-to-passage suitability remains review_required and is not inferred from registry linkage.'] }));
260
328
  }
261
- if (!page.paragraphs.length) observations.push(envelope('passage', { url: page.url, word_count: rawWords, independently_extractable: false, dependencies: ['no paragraph passages extracted'] }, { method: 'static_analysis', source: page.sourceFile || page.url, state: 'not_observed', confidence: 'confirmed' }));
329
+ if (!page.paragraphs.length) observations.push(envelope('passage', { url: page.url, passage_contract_version: 1, word_count: rawWords, independently_extractable: false, dependencies: ['no paragraph passages extracted'], question_contexts: questionContexts, semantic_review_status: 'review_required', extraction_metrics: extractionMetrics, repeated_regions: repeatedRegions }, { method: 'static_analysis', source: page.sourceFile || page.url, state: 'not_observed', confidence: 'confirmed', limitations: [...limitations, 'Question-to-passage suitability remains review_required and is not inferred from registry linkage.'] }));
262
330
  }
263
331
  return observationRun(root, 'observe passages', options.target, observations);
264
332
  }
265
333
 
334
+ function normalizeDateSignal(source, raw) {
335
+ if (raw == null || raw === '') return null;
336
+ const text = String(raw).trim();
337
+ const dateOnly = /^\d{4}-\d{2}-\d{2}$/.test(text);
338
+ const date = new Date(dateOnly ? `${text}T00:00:00Z` : text);
339
+ const valid = !Number.isNaN(date.getTime()) && (!dateOnly || date.toISOString().slice(0, 10) === text);
340
+ return {
341
+ source,
342
+ raw: text,
343
+ normalized_date: valid ? date.toISOString().slice(0, 10) : null,
344
+ valid,
345
+ };
346
+ }
347
+
266
348
  async function observeConsensus(root, options) {
267
349
  const ctx = await buildContext(root, options);
268
350
  if (!ctx.site) throw new Error('consensus requires --target <dir|url>');
351
+ let canonicalInput = null;
352
+ if (options.input) {
353
+ canonicalInput = readInput(options.input);
354
+ const check = validateAgainst('canonical-index-import.schema.json', canonicalInput.value);
355
+ if (!check.valid) throw new Error(`canonical index import violates contract: ${check.errors.join('; ')}`);
356
+ }
269
357
  const sitemapRows = ctx.site.sitemaps.flatMap((s) => s.parsed?.urls || []);
270
358
  const sitemapUrls = new Set(sitemapRows.map((u) => u.loc));
271
359
  const observations = ctx.site.pages.map((page) => {
@@ -274,10 +362,59 @@ async function observeConsensus(root, options) {
274
362
  const visibleDate = page.metas['article:modified_time']?.[0] || page.metas['date.modified']?.[0] || page.metas['last-modified']?.[0] || null;
275
363
  const signals = { final_url: page.url, html_canonical: declared, open_graph_url: page.ogUrl, sitemap_present: sitemapUrls.has(page.url), last_modified_header: page.headers['last-modified'] || null, sitemap_lastmod: sitemapDate, visible_or_meta_modified: visibleDate };
276
364
  const urls = [page.url, declared, page.ogUrl].filter(Boolean);
277
- const dates = [signals.last_modified_header, sitemapDate, visibleDate].filter(Boolean).map((value) => String(value).slice(0, 10));
278
- return envelope('canonical_freshness', { url: page.url, signals, canonical_consensus: new Set(urls).size <= 1, date_consensus: dates.length >= 2 ? new Set(dates).size === 1 : null, engine_selected_canonical: null }, { method: 'static_analysis', source: page.sourceFile || page.url, raw: JSON.stringify(signals), confidence: 'high', limitations: ['Engine-selected canonical and content-difference date require external observations.'] });
365
+ const engineCanonicals = canonicalInput?.value.observations.filter((item) => item.url === page.url).map((item) => ({
366
+ engine: item.engine,
367
+ selected_canonical: item.selected_canonical,
368
+ indexed: item.indexed,
369
+ observed_at: item.observed_at,
370
+ collection_authority: canonicalInput.value.collection_authority,
371
+ authenticity_status: canonicalInput.value.authenticity_status,
372
+ })) || [];
373
+ const dateSignals = [
374
+ normalizeDateSignal('http_last_modified', signals.last_modified_header),
375
+ normalizeDateSignal('sitemap_lastmod', sitemapDate),
376
+ normalizeDateSignal('visible_or_meta_modified', visibleDate),
377
+ ].filter(Boolean);
378
+ const validDates = dateSignals.filter((item) => item.valid).map((item) => item.normalized_date);
379
+ const dateConsensus = validDates.length >= 2 ? new Set(validDates).size === 1 : null;
380
+ const prior = ctx.snapshots?.pages?.[page.url] || null;
381
+ const currentHash = ctx.hashPage(page);
382
+ const changed = prior ? prior.contentHash !== currentHash : null;
383
+ const pageRegistry = ctx.registries.pages?.entries?.find((item) => item.url === page.url || item.canonical_url === page.url) || null;
384
+ const contentSnapshot = {
385
+ current_content_hash: currentHash,
386
+ prior_run_id: prior ? ctx.snapshots.run_id || null : null,
387
+ prior_content_hash: prior?.contentHash || null,
388
+ changed_since_snapshot: changed,
389
+ change_observed_after: changed ? ctx.snapshots.taken_at || null : null,
390
+ change_observed_by: changed ? new Date().toISOString() : null,
391
+ exact_change_time_established: false,
392
+ };
393
+ const limitations = ['Engine-selected canonical requires external observations. Snapshot comparison can bound a detected change interval but cannot establish the exact modification time or cause.'];
394
+ if (dateSignals.some((item) => !item.valid)) limitations.push('One or more declared freshness dates could not be parsed and were excluded from consensus.');
395
+ const data = {
396
+ url: page.url,
397
+ signals,
398
+ canonical_consensus: new Set(urls).size <= 1,
399
+ date_signals: dateSignals,
400
+ date_consensus: dateConsensus,
401
+ freshness_assessment: validDates.length < 2 ? 'insufficient_signals' : dateConsensus ? 'aligned_signals' : 'conflicting_signals',
402
+ content_snapshot: contentSnapshot,
403
+ lifecycle_governance: pageRegistry ? {
404
+ page_id: pageRegistry.page_id,
405
+ lifecycle_class: pageRegistry.lifecycle_class || 'unclassified',
406
+ next_review_date: pageRegistry.next_review_date || null,
407
+ } : null,
408
+ engine_selected_canonical: engineCanonicals.length ? engineCanonicals : null,
409
+ canonical_consensus_with_engines: engineCanonicals.length
410
+ ? new Set([...urls, ...engineCanonicals.map((item) => item.selected_canonical).filter(Boolean)]).size <= 1
411
+ : null,
412
+ };
413
+ return envelope('canonical_freshness', data, { method: 'static_analysis', source: page.sourceFile || page.url, raw: JSON.stringify({ signals, dateSignals, contentSnapshot }), confidence: 'high', limitations });
414
+ });
415
+ return observationRun(root, 'observe consensus', options.target, observations, {
416
+ rawInputs: canonicalInput ? { canonical_index_export: canonicalInput.raw } : {},
279
417
  });
280
- return observationRun(root, 'observe consensus', options.target, observations);
281
418
  }
282
419
 
283
420
  const median = (values) => {
@@ -374,11 +511,171 @@ async function observePerformance(root, options) {
374
511
 
375
512
  function observeCorroboration(root, options) {
376
513
  const input = readInput(options.input);
377
- const rows = Array.isArray(input.value) ? input.value : input.value.mentions || [];
378
- const observations = rows.map((row) => envelope('corroboration', row, { method: 'owner_import', source: input.file, raw: JSON.stringify(row), confidence: row.independent === true ? 'high' : 'low', limitations: row.independent === true ? [] : ['Source independence is not established.'] }));
514
+ const document = input.value;
515
+ const check = validateAgainst('corroboration-import.schema.json', document);
516
+ if (!check.valid) throw new Error(`corroboration import violates contract: ${check.errors.join('; ')}`);
517
+ const observations = document.mentions.map((row) => {
518
+ const independent = row.source_control === 'independent' && row.relationship_disclosure === 'none_disclosed';
519
+ const sourceAuthority = independent
520
+ ? 'independently_controlled'
521
+ : row.source_control === 'owner_controlled'
522
+ ? 'owner_controlled'
523
+ : row.source_control === 'affiliated'
524
+ ? 'third_party_commercial'
525
+ : 'unknown';
526
+ const limitations = [
527
+ 'An observed external mention does not establish source authority, claim support, ranking impact, citation eligibility, or representative coverage.',
528
+ ];
529
+ if (!independent) limitations.push('Source independence is not established from the declared control and relationship.');
530
+ const data = {
531
+ ...row,
532
+ import_provider: document.provider,
533
+ import_dataset: document.dataset,
534
+ import_collected_at: document.collected_at,
535
+ coverage: document.coverage,
536
+ };
537
+ return envelope('corroboration', data, {
538
+ method: 'owner_import',
539
+ source: row.source_url,
540
+ raw: JSON.stringify(row),
541
+ confidence: independent ? 'high' : 'low',
542
+ limitations,
543
+ authority: {
544
+ source_authority: sourceAuthority,
545
+ collection_authority: document.collection_authority,
546
+ authenticity_status: document.authenticity_status,
547
+ representativeness: document.representativeness,
548
+ },
549
+ });
550
+ });
379
551
  return observationRun(root, 'observe corroboration', input.file, observations, { rawInputs: { corroboration_export: input.raw } });
380
552
  }
381
553
 
554
+ const PROBE_HEADER_ALLOWLIST = new Set([
555
+ 'age', 'cache-control', 'cf-cache-status', 'cf-mitigated', 'content-type',
556
+ 'date', 'etag', 'retry-after', 'server', 'via', 'x-cache', 'x-robots-tag',
557
+ ]);
558
+
559
+ function safeProbeHeaders(headers = {}) {
560
+ return Object.fromEntries(Object.entries(headers)
561
+ .map(([key, value]) => [key.toLowerCase(), String(value)])
562
+ .filter(([key]) => PROBE_HEADER_ALLOWLIST.has(key)));
563
+ }
564
+
565
+ function classifyProbeResponse(response) {
566
+ const headers = safeProbeHeaders(response.headers);
567
+ const body = String(response.body || '').slice(0, 10000);
568
+ const challengeSignal = headers['cf-mitigated'] === 'challenge'
569
+ || /captcha|verify you are human|attention required|access challenge/i.test(body);
570
+ if ([401, 403, 429].includes(response.status) || challengeSignal) return 'challenged';
571
+ if (response.status >= 500) return 'server_error';
572
+ if (response.status >= 400) return 'blocked';
573
+ if ((response.redirectChain || []).length) return 'redirected';
574
+ return response.status >= 200 && response.status < 400 ? 'accessible' : 'unexpected';
575
+ }
576
+
577
+ async function observeCrawlerProbes(root, options) {
578
+ if (!options.target) throw new Error('probes requires --target <public URL>');
579
+ const registryFile = path.join(root, '.citable', 'crawlers.yaml');
580
+ if (!fs.existsSync(registryFile)) throw new Error('crawlers registry is missing; run citable init first');
581
+ const rawRegistry = fs.readFileSync(registryFile, 'utf8');
582
+ const registry = readYaml(registryFile);
583
+ const check = validateAgainst('crawler.schema.json', registry);
584
+ if (!check.valid) throw new Error(`crawlers registry violates contract: ${check.errors.join('; ')}`);
585
+ const crawlers = registry.entries.filter((item) => item.status === 'active');
586
+ if (!crawlers.length) throw new Error('crawlers registry has no active identities to probe');
587
+ await validatePublicUrl(options.target, options.lookup ? { lookup: options.lookup } : {});
588
+
589
+ const collector = options.fetchUrl || fetchUrl;
590
+ const region = options.region || 'local_unspecified';
591
+ const observations = [], incomplete = [], artifacts = {};
592
+ for (const crawler of crawlers) {
593
+ const artifactBase = `crawler-probes/${crawler.crawler_id}`;
594
+ try {
595
+ const response = await collector(options.target, {
596
+ userAgent: crawler.user_agent,
597
+ maxRetries: 1,
598
+ ...(options.lookup ? { lookup: options.lookup } : {}),
599
+ });
600
+ const headers = safeProbeHeaders(response.headers);
601
+ const edgeClassification = classifyProbeResponse(response);
602
+ const data = {
603
+ crawler_id: crawler.crawler_id,
604
+ vendor: crawler.vendor || null,
605
+ purpose: crawler.purpose,
606
+ declared_policy_decision: crawler.decision,
607
+ user_agent: crawler.user_agent,
608
+ target: options.target,
609
+ final_url: response.url || options.target,
610
+ status: response.status,
611
+ redirect_chain: response.redirectChain || [],
612
+ response_headers: headers,
613
+ response_hash: sha256(response.body || ''),
614
+ edge_classification: edgeClassification,
615
+ region,
616
+ identity_status: 'synthetically_observed',
617
+ production_access_established: false,
618
+ };
619
+ observations.push(envelope('crawler_probe', data, {
620
+ method: 'synthetic_fetch',
621
+ source: options.target,
622
+ raw: response.body || JSON.stringify(data),
623
+ confidence: 'confirmed',
624
+ limitations: ['A spoofed user agent in a synthetic request does not establish crawler identity or production crawler access. One local probe does not represent other regions, networks, times, or edge paths.'],
625
+ authority: { source_authority: 'synthetic', collection_authority: 'synthetic_probe', authenticity_status: 'unverified', representativeness: 'single_observation' },
626
+ }));
627
+ artifacts[`${artifactBase}/response.txt`] = response.body || '';
628
+ artifacts[`${artifactBase}/headers.json`] = headers;
629
+ } catch (error) {
630
+ incomplete.push(`${crawler.crawler_id} synthetic probe failed: ${error.message}`);
631
+ observations.push(envelope('crawler_probe', {
632
+ crawler_id: crawler.crawler_id, vendor: crawler.vendor || null, purpose: crawler.purpose,
633
+ declared_policy_decision: crawler.decision, user_agent: crawler.user_agent,
634
+ target: options.target, region, edge_classification: 'error',
635
+ identity_status: 'synthetically_observed', production_access_established: false,
636
+ }, {
637
+ method: 'synthetic_fetch', source: options.target, state: 'failed',
638
+ confidence: 'confirmed', raw: `${crawler.crawler_id}:${error.message}`,
639
+ limitations: [`Synthetic probe failed: ${error.message}`, 'A spoofed user agent does not establish crawler identity or production crawler access.'],
640
+ authority: { source_authority: 'synthetic', collection_authority: 'synthetic_probe', authenticity_status: 'unverified', representativeness: 'single_observation' },
641
+ }));
642
+ }
643
+ }
644
+ return observationRun(root, 'observe probes', options.target, observations, {
645
+ rawInputs: { crawler_registry: rawRegistry }, incomplete, artifacts,
646
+ warnings: ['Probe outcomes are synthetic retrieval evidence only and must not be interpreted as verified crawler identity or production access.'],
647
+ });
648
+ }
649
+
650
+ function observeRegionalNetwork(root, options) {
651
+ const input = readInput(options.input);
652
+ const document = input.value;
653
+ const check = validateAgainst('regional-network-import.schema.json', document);
654
+ if (!check.valid) throw new Error(`regional network import violates contract: ${check.errors.join('; ')}`);
655
+ const limitations = ['Imported regional observations do not establish managed runner coverage, global representativeness, crawler identity, or future availability.'];
656
+ const observations = document.probes.map((probe) => envelope('regional_network', {
657
+ ...probe,
658
+ provider: document.provider,
659
+ dataset_collected_at: document.collected_at,
660
+ }, {
661
+ method: 'owner_import',
662
+ source: input.file,
663
+ raw: JSON.stringify(probe),
664
+ confidence: 'confirmed',
665
+ limitations,
666
+ authority: {
667
+ source_authority: 'unknown',
668
+ collection_authority: document.collection_authority,
669
+ authenticity_status: document.authenticity_status,
670
+ representativeness: document.representativeness,
671
+ },
672
+ }));
673
+ return observationRun(root, 'observe network', input.file, observations, {
674
+ rawInputs: { regional_network_export: input.raw },
675
+ warnings: ['This release validates regional probe exports but does not operate a managed multi-region runner.'],
676
+ });
677
+ }
678
+
382
679
  async function observeRepresentation(root, options) {
383
680
  const input = readInput(options.input);
384
681
  const manifest = input.value;
@@ -461,8 +758,11 @@ export async function observe(root, mode, options = {}) {
461
758
  case 'consensus': return observeConsensus(root, options);
462
759
  case 'performance': return observePerformance(root, options);
463
760
  case 'corroboration': return observeCorroboration(root, options);
761
+ case 'probes': return observeCrawlerProbes(root, options);
762
+ case 'network': return observeRegionalNetwork(root, options);
464
763
  case 'media': return observeMedia(root, options);
465
764
  case 'representation': return observeRepresentation(root, options);
466
- default: throw new Error('observe mode must be render, index, citations, logs, bing, passages, consensus, performance, corroboration, media, or representation');
765
+ case 'stance': return observeStance(root, options);
766
+ default: throw new Error('observe mode must be render, index, citations, logs, bing, passages, consensus, performance, corroboration, probes, network, media, representation, or stance');
467
767
  }
468
768
  }
@@ -0,0 +1,151 @@
1
+ import { buildContext } from './context.js';
2
+ import { runtimeCapabilities } from '../installer/index.js';
3
+
4
+ const PROFILE_RULES = [
5
+ {
6
+ id: 'saas',
7
+ signals: [
8
+ ['pricing URL discovered', ({ paths }) => paths.some((value) => /\/pricing(?:\/|$)/.test(value))],
9
+ ['product, feature, integration, or documentation URL discovered', ({ paths }) => paths.some((value) => /\/(?:products?|features?|integrations?|docs)(?:\/|$)/.test(value))],
10
+ ['software or platform language observed', ({ text }) => /\b(?:software|saas|platform)\b/i.test(text)],
11
+ ['trial or signup language observed', ({ text }) => /\b(?:free trial|sign up|get started)\b/i.test(text)],
12
+ ],
13
+ emphasis: ['pricing-and-capability-claim-support', 'product-entity-consistency'],
14
+ },
15
+ {
16
+ id: 'ecommerce',
17
+ signals: [
18
+ ['product or collection URL discovered', ({ paths }) => paths.some((value) => /\/(?:products?|collections?|cart)(?:\/|$)/.test(value))],
19
+ ['commerce action language observed', ({ text }) => /\b(?:add to cart|buy now|checkout)\b/i.test(text)],
20
+ ['Product or Offer structured data observed', ({ schemaTypes }) => schemaTypes.has('Product') || schemaTypes.has('Offer')],
21
+ ],
22
+ emphasis: ['product-offer-visible-content-support', 'merchant-claim-governance'],
23
+ },
24
+ {
25
+ id: 'local',
26
+ signals: [
27
+ ['LocalBusiness structured data observed', ({ schemaTypes }) => schemaTypes.has('LocalBusiness')],
28
+ ['telephone link observed', ({ hrefs }) => hrefs.some((value) => value.startsWith('tel:'))],
29
+ ['local-service language observed', ({ text }) => /\b(?:service area|serving [a-z]|visit us|directions)\b/i.test(text)],
30
+ ['map link or embed observed', ({ rawHtml }) => /(?:google\.[^"']*\/maps|maps\.google)/i.test(rawHtml)],
31
+ ],
32
+ emphasis: ['local-entity-and-nap-consistency', 'review-and-rating-evidence'],
33
+ },
34
+ {
35
+ id: 'publisher',
36
+ signals: [
37
+ ['article, blog, or topic URL discovered', ({ paths }) => paths.some((value) => /\/(?:blog|articles?|topics?)(?:\/|$)/.test(value))],
38
+ ['Article-family structured data observed', ({ schemaTypes }) => ['Article', 'BlogPosting', 'NewsArticle'].some((value) => schemaTypes.has(value))],
39
+ ['author or publication-date language observed', ({ text }) => /\b(?:written by|published|updated on)\b/i.test(text)],
40
+ ],
41
+ emphasis: ['authorship-and-source-identity', 'freshness-consensus'],
42
+ },
43
+ {
44
+ id: 'multilingual',
45
+ signals: [
46
+ ['multiple document languages observed', ({ languages }) => languages.size > 1],
47
+ ['hreflang markup observed', ({ rawHtml }) => /hreflang\s*=/i.test(rawHtml)],
48
+ ],
49
+ emphasis: ['hreflang-reciprocity-and-language-parity'],
50
+ },
51
+ {
52
+ id: 'api-docs',
53
+ signals: [
54
+ ['API or documentation URL discovered', ({ paths }) => paths.some((value) => /\/(?:api|docs?|developers?)(?:\/|$)/.test(value))],
55
+ ['API or SDK language observed', ({ text }) => /\b(?:api|sdk|webhook|endpoint)\b/i.test(text)],
56
+ ['machine interface metadata observed', ({ paths }) => paths.some((value) => /\/(?:agent\.json|auth\.md|\.well-known\/)/.test(value))],
57
+ ],
58
+ emphasis: ['machine-interface-discoverability', 'capability-and-authentication-claim-support'],
59
+ },
60
+ ];
61
+
62
+ const COLLECTORS = [
63
+ ['render', 'browser_render', 'citable observe render --target TARGET'],
64
+ ['lighthouse', 'lighthouse_lab', 'citable observe performance --target TARGET --lighthouse --repeat 3'],
65
+ ['crux', 'crux_field', 'citable observe performance --target TARGET'],
66
+ ['gsc-index', 'gsc_index', 'citable observe index --target TARGET --site-url PROPERTY'],
67
+ ['ga4-metrics', 'ga4_metrics', 'citable connect sync --connection-id CONNECTION --start-date DATE --end-date DATE'],
68
+ ];
69
+
70
+ function siteSignals(site) {
71
+ const paths = [];
72
+ const hrefs = [];
73
+ const schemaTypes = new Set();
74
+ const languages = new Set();
75
+ let text = '';
76
+ let rawHtml = '';
77
+ for (const page of site.pages) {
78
+ paths.push(new URL(page.url).pathname);
79
+ hrefs.push(...page.links.map((link) => link.href));
80
+ text += ` ${page.text}`;
81
+ rawHtml += ` ${page.rawHtml}`;
82
+ if (page.lang) languages.add(page.lang.toLowerCase());
83
+ for (const item of page.jsonLd) {
84
+ for (const block of item.blocks) {
85
+ const types = Array.isArray(block?.['@type']) ? block['@type'] : [block?.['@type']];
86
+ for (const type of types.filter(Boolean)) schemaTypes.add(type);
87
+ }
88
+ }
89
+ }
90
+ return { paths, hrefs, schemaTypes, languages, text, rawHtml };
91
+ }
92
+
93
+ function classifyProfiles(site) {
94
+ const signals = siteSignals(site);
95
+ return PROFILE_RULES.map((rule) => {
96
+ const evidence = rule.signals.filter(([, matches]) => matches(signals)).map(([label]) => label);
97
+ return {
98
+ id: rule.id,
99
+ confidence: evidence.length >= 3 ? 'high' : evidence.length === 2 ? 'medium' : 'low',
100
+ evidence,
101
+ emphasis: rule.emphasis,
102
+ };
103
+ }).filter((profile) => profile.evidence.length >= 2);
104
+ }
105
+
106
+ export async function planAudit(root, { target, baseUrl, refDate, capabilities } = {}) {
107
+ if (!target) throw new Error('plan-audit requires --target <dir|url>');
108
+ const ctx = await buildContext(root, { target, baseUrl, refDate });
109
+ if (!ctx.site) throw new Error('plan-audit could not build a site model');
110
+ const profiles = classifyProfiles(ctx.site);
111
+ const runtime = capabilities ?? await runtimeCapabilities();
112
+ const capabilityById = new Map(runtime.map((item) => [item.id, item]));
113
+ const collectors = COLLECTORS.map(([id, capabilityId, command]) => {
114
+ const capability = capabilityById.get(capabilityId);
115
+ const status = capability?.state === 'ready' ? 'available' : 'blocked';
116
+ return {
117
+ id,
118
+ status,
119
+ capability: capabilityId,
120
+ command: command.replace('TARGET', JSON.stringify(target)),
121
+ required_input: status === 'available' ? [] : capability?.requirements ?? [],
122
+ limitations: capability?.limitations ?? ['capability status is unavailable'],
123
+ };
124
+ });
125
+ return {
126
+ target: {
127
+ kind: ctx.site.mode,
128
+ location: ctx.site.location,
129
+ pages_inspected: ctx.site.pages.length,
130
+ },
131
+ classification: {
132
+ statement_type: 'probabilistic_inference',
133
+ profiles,
134
+ unresolved: profiles.length ? [] : ['No profile met the two-signal evidence threshold.'],
135
+ },
136
+ audit: {
137
+ scope: 'all',
138
+ command: `citable audit --target ${JSON.stringify(target)}${baseUrl ? ` --base-url ${JSON.stringify(baseUrl)}` : ''}`,
139
+ rationale: 'The full audit preserves separate SEO, AEO, GEO, governance, and observation boundaries; profile inference changes emphasis, not detector truth.',
140
+ },
141
+ semantic_emphasis: [...new Set(profiles.flatMap((profile) => profile.emphasis))],
142
+ collectors,
143
+ warnings: ctx.warnings,
144
+ boundaries: [
145
+ 'No audit or observation package was created.',
146
+ 'Profile labels are probabilistic inferences from captured page signals, not verified business facts.',
147
+ 'Available collectors establish prerequisite presence only; execution and evidence remain unproven until a collector completes.',
148
+ 'Blocked collectors do not make the proposed audit a success or failure; they identify unavailable follow-up evidence.',
149
+ ],
150
+ };
151
+ }
@@ -0,0 +1,21 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { buildTimeSeries, loadRunHistory, renderDashboardHtml, renderDashboardMarkdown } from '../reporting/dashboard.js';
4
+
5
+ /**
6
+ * `citable report dashboard` — fold the summary already recorded by each audit
7
+ * run into a cross-run Markdown table and a self-contained HTML page. Derived
8
+ * output only: it never reads, edits, or re-derives an immutable run package.
9
+ */
10
+ export function reportDashboard(root, { since, last } = {}) {
11
+ if (last != null && (!Number.isInteger(last) || last < 1)) throw new Error('--last must be a positive integer');
12
+ const { included, skipped } = loadRunHistory(root, { since, last });
13
+ const series = buildTimeSeries(included);
14
+ const dir = path.join(root, '.citable', 'reports');
15
+ const pathMd = path.join(dir, 'dashboard.md');
16
+ const pathHtml = path.join(dir, 'dashboard.html');
17
+ fs.mkdirSync(dir, { recursive: true });
18
+ fs.writeFileSync(pathMd, renderDashboardMarkdown(series, skipped));
19
+ fs.writeFileSync(pathHtml, renderDashboardHtml(series, skipped));
20
+ return { dir, included: included.length, skipped: skipped.length, path_md: pathMd, path_html: pathHtml };
21
+ }