@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.
- package/CHANGELOG.md +35 -0
- package/README.md +12 -1
- package/dist/universal/.agents/skills/citable/SKILL.md +11 -2
- package/dist/universal/.agents/skills/citable/VERSION +1 -1
- package/dist/universal/.agents/skills/citable/manifest.json +24 -12
- package/dist/universal/.agents/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.agents/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.agents/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.agents/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.agents/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.agents/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.agents/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.agents/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.agents/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.agents/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.agents/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.agents/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.agents/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.agents/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.agents/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.agents/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.agents/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.agents/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.agents/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.claude/agents/citable/manifest.json +2 -2
- package/dist/universal/.claude/skills/citable/SKILL.md +11 -2
- package/dist/universal/.claude/skills/citable/VERSION +1 -1
- package/dist/universal/.claude/skills/citable/manifest.json +24 -12
- package/dist/universal/.claude/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.claude/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.claude/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.claude/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.claude/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.claude/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.claude/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.claude/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.claude/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.claude/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.claude/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.claude/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.claude/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.claude/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.claude/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.claude/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.claude/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.claude/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.claude/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.cursor/skills/citable/SKILL.md +11 -2
- package/dist/universal/.cursor/skills/citable/VERSION +1 -1
- package/dist/universal/.cursor/skills/citable/manifest.json +24 -12
- package/dist/universal/.cursor/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.cursor/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.cursor/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.cursor/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.cursor/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.cursor/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.cursor/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.cursor/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.cursor/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.cursor/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.cursor/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.cursor/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.cursor/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.cursor/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.cursor/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.cursor/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.cursor/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.cursor/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.cursor/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.gemini/skills/citable/SKILL.md +11 -2
- package/dist/universal/.gemini/skills/citable/VERSION +1 -1
- package/dist/universal/.gemini/skills/citable/manifest.json +24 -12
- package/dist/universal/.gemini/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.gemini/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.gemini/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.gemini/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.gemini/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.gemini/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.gemini/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.gemini/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.gemini/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.gemini/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.gemini/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.gemini/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.gemini/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.gemini/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.gemini/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.gemini/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.gemini/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.gemini/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.gemini/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.github/skills/citable/SKILL.md +11 -2
- package/dist/universal/.github/skills/citable/VERSION +1 -1
- package/dist/universal/.github/skills/citable/manifest.json +24 -12
- package/dist/universal/.github/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.github/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.github/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.github/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.github/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.github/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.github/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.github/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.github/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.github/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.github/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.github/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.github/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.github/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.github/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.github/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.github/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.github/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.github/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.kiro/skills/citable/SKILL.md +11 -2
- package/dist/universal/.kiro/skills/citable/VERSION +1 -1
- package/dist/universal/.kiro/skills/citable/manifest.json +24 -12
- package/dist/universal/.kiro/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.kiro/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.kiro/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.kiro/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.kiro/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.kiro/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.kiro/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.kiro/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.kiro/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.kiro/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.kiro/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.kiro/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.kiro/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.kiro/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.kiro/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.kiro/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.kiro/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.kiro/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.kiro/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.opencode/skills/citable/SKILL.md +11 -2
- package/dist/universal/.opencode/skills/citable/VERSION +1 -1
- package/dist/universal/.opencode/skills/citable/manifest.json +24 -12
- package/dist/universal/.opencode/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.opencode/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.opencode/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.opencode/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.opencode/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.opencode/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.opencode/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.opencode/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.opencode/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.opencode/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.opencode/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.opencode/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.opencode/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.opencode/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.opencode/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.opencode/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.opencode/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.opencode/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.opencode/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.pi/agent/skills/citable/SKILL.md +11 -2
- package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
- package/dist/universal/.pi/agent/skills/citable/manifest.json +24 -12
- package/dist/universal/.pi/agent/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.pi/agent/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.pi/agent/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.qoder/skills/citable/SKILL.md +11 -2
- package/dist/universal/.qoder/skills/citable/VERSION +1 -1
- package/dist/universal/.qoder/skills/citable/manifest.json +24 -12
- package/dist/universal/.qoder/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.qoder/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.qoder/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.qoder/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.qoder/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.qoder/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.qoder/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.qoder/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.qoder/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.qoder/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.qoder/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.qoder/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.qoder/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.qoder/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.qoder/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.qoder/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.qoder/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.qoder/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.qoder/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.rovodev/skills/citable/SKILL.md +11 -2
- package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
- package/dist/universal/.rovodev/skills/citable/manifest.json +24 -12
- package/dist/universal/.rovodev/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.rovodev/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.rovodev/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.rovodev/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.rovodev/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.rovodev/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.rovodev/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.rovodev/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.rovodev/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.rovodev/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.rovodev/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.rovodev/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.rovodev/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.rovodev/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.rovodev/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.rovodev/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.rovodev/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.rovodev/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.rovodev/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.trae/skills/citable/SKILL.md +11 -2
- package/dist/universal/.trae/skills/citable/VERSION +1 -1
- package/dist/universal/.trae/skills/citable/manifest.json +24 -12
- package/dist/universal/.trae/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.trae/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.trae/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.trae/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.trae/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.trae/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.trae/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.trae/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.trae/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.trae/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.trae/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.trae/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.trae/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.trae/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.trae/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.trae/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.trae/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.trae/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.trae/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/.trae-cn/skills/citable/SKILL.md +11 -2
- package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
- package/dist/universal/.trae-cn/skills/citable/manifest.json +24 -12
- package/dist/universal/.trae-cn/skills/citable/schemas/action-plan.schema.json +38 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/audit-coverage.schema.json +118 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/collection-result.schema.json +35 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/config.schema.json +2 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/cro-report.schema.json +8 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/finding.schema.json +105 -2
- package/dist/universal/.trae-cn/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/run.schema.json +15 -2
- package/dist/universal/.trae-cn/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/search-report.schema.json +8 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/summary.schema.json +41 -0
- package/dist/universal/manifest.json +292 -148
- package/package.json +1 -1
- package/schemas/action-plan.schema.json +38 -1
- package/schemas/audit-coverage.schema.json +118 -0
- package/schemas/claim-evidence.schema.json +21 -0
- package/schemas/claim-verification.schema.json +18 -0
- package/schemas/collection-result.schema.json +35 -0
- package/schemas/config.schema.json +2 -0
- package/schemas/cro-report.schema.json +8 -1
- package/schemas/derivation.schema.json +18 -0
- package/schemas/external-comparison.schema.json +16 -0
- package/schemas/external-observation.schema.json +32 -0
- package/schemas/finding.schema.json +105 -2
- package/schemas/prompt-set.schema.json +47 -0
- package/schemas/remediation-verification.schema.json +12 -1
- package/schemas/representation-data.schema.json +21 -0
- package/schemas/research-dataset.schema.json +27 -0
- package/schemas/run.schema.json +15 -2
- package/schemas/search-discovery-data.schema.json +20 -0
- package/schemas/search-report.schema.json +8 -1
- package/schemas/summary.schema.json +41 -0
- package/skill/SKILL.md +11 -2
- package/src/acceptance/reproducibility.js +5 -25
- package/src/analysis/croRoadmap.js +3 -3
- package/src/analysis/strategicRoadmap.js +3 -3
- package/src/artifacts/interchange.js +2 -6
- package/src/cli/index.js +15 -3
- package/src/commands/actionPlan.js +46 -5
- package/src/commands/applyRemediation.js +5 -5
- package/src/commands/audit.js +75 -8
- package/src/commands/ciWorkflow.js +7 -2
- package/src/commands/compareSnapshots.js +155 -47
- package/src/commands/connect.js +24 -4
- package/src/commands/context.js +41 -2
- package/src/commands/croSuite.js +51 -13
- package/src/commands/delivery.js +7 -7
- package/src/commands/governance.js +9 -10
- package/src/commands/implementationKit.js +2 -1
- package/src/commands/init.js +8 -1
- package/src/commands/previewCro.js +1 -1
- package/src/commands/prioritize.js +3 -2
- package/src/commands/reviews.js +31 -5
- package/src/commands/roadmapCmd.js +7 -13
- package/src/commands/sweep.js +15 -2
- package/src/commands/verifyRemediation.js +94 -19
- package/src/connectors/collectionResult.js +80 -0
- package/src/connectors/ga4.js +46 -11
- package/src/connectors/gsc.js +45 -6
- package/src/connectors/index.js +1 -1
- package/src/connectors/webflow.js +140 -10
- package/src/connectors/wordpress.js +92 -8
- package/src/crawler/fetch.js +220 -24
- package/src/crawler/resourceValidity.js +119 -0
- package/src/crawler/sitemap.js +435 -16
- package/src/crawler/sitemapCollector.js +364 -0
- package/src/crawler/urlIdentity.js +97 -0
- package/src/detectors/agent.js +11 -0
- package/src/detectors/ans.js +16 -1
- package/src/detectors/arch.js +6 -0
- package/src/detectors/claim.js +9 -0
- package/src/detectors/crawl.js +7 -0
- package/src/detectors/cro.js +21 -0
- package/src/detectors/cwv.js +4 -0
- package/src/detectors/entity.js +7 -0
- package/src/detectors/evd.js +9 -0
- package/src/detectors/framework.js +28 -2
- package/src/detectors/geoReco.js +16 -0
- package/src/detectors/hreflang.js +3 -0
- package/src/detectors/lifeMeas.js +9 -0
- package/src/detectors/link.js +6 -1
- package/src/detectors/page.js +11 -0
- package/src/detectors/schemaData.js +16 -1
- package/src/detectors/tech.js +34 -3
- package/src/discovery/searchConsole.js +186 -0
- package/src/evidence/coverage.js +448 -0
- package/src/evidence/determination.js +157 -0
- package/src/evidence/downstream.js +110 -0
- package/src/evidence/external.js +251 -0
- package/src/evidence/hashes.js +109 -0
- package/src/evidence/run.js +68 -8
- package/src/extractor/page.js +29 -2
- package/src/extractor/site.js +298 -50
- package/src/reporting/executiveCroReport.js +36 -13
- package/src/reporting/executiveExport.js +45 -16
- package/src/reporting/executiveSearchReport.js +56 -22
- package/src/reporting/report.js +56 -9
- package/src/representation/prompts.js +117 -0
- package/src/representation/representation.js +186 -0
- package/src/shared/epistemicLanguage.js +86 -0
- package/src/shared/evidenceSourceResolver.js +48 -17
- package/src/shared/htmlEscape.js +8 -4
- package/src/shared/runPackageVerifier.js +99 -8
- package/src/shared/verifiedRunLoader.js +129 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import { validateAgainst } from '../shared/schemaValidator.js';
|
|
2
|
+
|
|
3
|
+
export const COVERAGE_SCHEMA_VERSION = 1;
|
|
4
|
+
|
|
5
|
+
const STOP_REASONS = new Set([
|
|
6
|
+
'frontier_exhausted',
|
|
7
|
+
'page_budget_exhausted',
|
|
8
|
+
'time_budget_exhausted',
|
|
9
|
+
'fatal_collection_failure',
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
const DISCOVERY_STATUSES = new Set(['complete', 'truncated', 'indeterminate']);
|
|
13
|
+
const TERMINAL_STATES = new Set([
|
|
14
|
+
'evaluated',
|
|
15
|
+
'valid_but_unevaluated',
|
|
16
|
+
'failed',
|
|
17
|
+
'indeterminate',
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
function assertNonEmptyString(value, name) {
|
|
21
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
22
|
+
throw new TypeError(`${name} must be a non-empty string`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function assertIntegerInRange(value, name, minimum, maximum) {
|
|
27
|
+
if (!Number.isInteger(value) || value < minimum || value > maximum) {
|
|
28
|
+
throw new RangeError(`${name} must be an integer from ${minimum} through ${maximum}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function assertUrl(value, name) {
|
|
33
|
+
assertNonEmptyString(value, name);
|
|
34
|
+
let parsed;
|
|
35
|
+
try {
|
|
36
|
+
parsed = new URL(value);
|
|
37
|
+
} catch {
|
|
38
|
+
throw new TypeError(`${name} must be an absolute URL`);
|
|
39
|
+
}
|
|
40
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
41
|
+
throw new TypeError(`${name} must use http or https`);
|
|
42
|
+
}
|
|
43
|
+
return parsed;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizedUrl(url) {
|
|
47
|
+
assertUrl(url, 'url');
|
|
48
|
+
const fragmentIndex = url.indexOf('#');
|
|
49
|
+
return fragmentIndex === -1 ? url : url.slice(0, fragmentIndex);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function canonicalValue(value, path = 'metadata') {
|
|
53
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;
|
|
54
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
55
|
+
if (Array.isArray(value)) return value.map((item, index) => canonicalValue(item, `${path}[${index}]`));
|
|
56
|
+
if (typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype) {
|
|
57
|
+
return Object.fromEntries(
|
|
58
|
+
Object.keys(value)
|
|
59
|
+
.sort()
|
|
60
|
+
.map((key) => [key, canonicalValue(value[key], `${path}.${key}`)]),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
throw new TypeError(`${path} must contain only JSON-compatible values`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function canonicalMetadata(metadata, name) {
|
|
67
|
+
if (metadata === undefined) return {};
|
|
68
|
+
if (metadata === null || typeof metadata !== 'object' || Array.isArray(metadata)) {
|
|
69
|
+
throw new TypeError(`${name} must be an object`);
|
|
70
|
+
}
|
|
71
|
+
return canonicalValue(metadata, name);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function stableString(value) {
|
|
75
|
+
return JSON.stringify(canonicalValue(value));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function compareStrings(a, b) {
|
|
79
|
+
if (a < b) return -1;
|
|
80
|
+
if (a > b) return 1;
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function sortedUniqueStrings(values, name) {
|
|
85
|
+
if (!Array.isArray(values)) throw new TypeError(`${name} must be an array`);
|
|
86
|
+
for (const value of values) assertNonEmptyString(value, `${name} entry`);
|
|
87
|
+
return [...new Set(values)].sort(compareStrings);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function stateOf(resource) {
|
|
91
|
+
if (resource.exclusion) return 'excluded';
|
|
92
|
+
if (resource.evaluation) return 'evaluated';
|
|
93
|
+
if (resource.validButUnevaluated) return 'valid_but_unevaluated';
|
|
94
|
+
if (resource.failure) return 'failed';
|
|
95
|
+
if (resource.indeterminate) return 'indeterminate';
|
|
96
|
+
if (resource.classification?.state === 'valid_resource') return 'valid_resource';
|
|
97
|
+
if (resource.retrieval) return 'retrieved';
|
|
98
|
+
if (resource.attemptHistory.length > 0) return 'attempted';
|
|
99
|
+
return 'eligible';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function assertNotTerminal(resource, operation) {
|
|
103
|
+
const state = stateOf(resource);
|
|
104
|
+
if (TERMINAL_STATES.has(state) || state === 'excluded') {
|
|
105
|
+
throw new Error(`cannot ${operation} resource in terminal state ${state}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function equationError(label, left, right) {
|
|
110
|
+
return `${label} (${left} != ${right})`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function reconcileCoverage(coverage) {
|
|
114
|
+
const populations = coverage?.populations;
|
|
115
|
+
if (!populations || typeof populations !== 'object') {
|
|
116
|
+
return { valid: false, errors: ['coverage populations are missing'] };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const errors = [];
|
|
120
|
+
const discoveredParts = populations.excluded + populations.eligible;
|
|
121
|
+
if (populations.discovered !== discoveredParts) {
|
|
122
|
+
errors.push(equationError('discovered = excluded + eligible', populations.discovered, discoveredParts));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const eligibleParts = populations.evaluated
|
|
126
|
+
+ populations.valid_but_unevaluated
|
|
127
|
+
+ populations.failed
|
|
128
|
+
+ populations.indeterminate
|
|
129
|
+
+ populations.unvisited;
|
|
130
|
+
if (populations.eligible !== eligibleParts) {
|
|
131
|
+
errors.push(equationError(
|
|
132
|
+
'eligible = evaluated + valid_but_unevaluated + failed + indeterminate + unvisited',
|
|
133
|
+
populations.eligible,
|
|
134
|
+
eligibleParts,
|
|
135
|
+
));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const attemptedParts = populations.retrieved + populations.failed;
|
|
139
|
+
if (populations.attempted !== attemptedParts) {
|
|
140
|
+
errors.push(equationError('attempted = retrieved + failed', populations.attempted, attemptedParts));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const retrievedParts = populations.valid_resource + populations.indeterminate;
|
|
144
|
+
if (populations.retrieved !== retrievedParts) {
|
|
145
|
+
errors.push(equationError('retrieved = valid_resource + indeterminate', populations.retrieved, retrievedParts));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const validResourceParts = populations.evaluated + populations.valid_but_unevaluated;
|
|
149
|
+
if (populations.valid_resource !== validResourceParts) {
|
|
150
|
+
errors.push(equationError(
|
|
151
|
+
'valid_resource = evaluated + valid_but_unevaluated',
|
|
152
|
+
populations.valid_resource,
|
|
153
|
+
validResourceParts,
|
|
154
|
+
));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return { valid: errors.length === 0, errors };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function coverageStatusFor({ stopReason, discoveryStatus = 'complete', populations }) {
|
|
161
|
+
if (!STOP_REASONS.has(stopReason)) throw new Error(`unknown stop reason: ${stopReason}`);
|
|
162
|
+
if (!DISCOVERY_STATUSES.has(discoveryStatus)) {
|
|
163
|
+
throw new Error(`unknown discovery status: ${discoveryStatus}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (stopReason === 'page_budget_exhausted' || stopReason === 'time_budget_exhausted') {
|
|
167
|
+
return 'truncated';
|
|
168
|
+
}
|
|
169
|
+
if (stopReason === 'fatal_collection_failure') return 'indeterminate';
|
|
170
|
+
|
|
171
|
+
const incomplete = populations.failed
|
|
172
|
+
+ populations.indeterminate
|
|
173
|
+
+ populations.valid_but_unevaluated
|
|
174
|
+
+ populations.unvisited;
|
|
175
|
+
return discoveryStatus === 'complete' && incomplete === 0 ? 'complete' : 'indeterminate';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function createCoverageLedger({
|
|
179
|
+
startUrl,
|
|
180
|
+
maxPages,
|
|
181
|
+
timeBudgetSeconds,
|
|
182
|
+
normalizationVersion = 'url-identity-v1',
|
|
183
|
+
}) {
|
|
184
|
+
const parsedStartUrl = assertUrl(startUrl, 'startUrl');
|
|
185
|
+
assertIntegerInRange(maxPages, 'maxPages', 1, 10000);
|
|
186
|
+
assertIntegerInRange(timeBudgetSeconds, 'timeBudgetSeconds', 1, 86400);
|
|
187
|
+
assertNonEmptyString(normalizationVersion, 'normalizationVersion');
|
|
188
|
+
|
|
189
|
+
const resources = new Map();
|
|
190
|
+
let finalized = false;
|
|
191
|
+
|
|
192
|
+
function assertMutable() {
|
|
193
|
+
if (finalized) throw new Error('coverage ledger is already finalized');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function requireResource(url) {
|
|
197
|
+
const normalized = normalizedUrl(url);
|
|
198
|
+
const resource = resources.get(normalized);
|
|
199
|
+
if (!resource) throw new Error(`resource must be discovered before transition: ${url}`);
|
|
200
|
+
return resource;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function discover(url, source, metadata = {}) {
|
|
204
|
+
assertMutable();
|
|
205
|
+
const normalized = normalizedUrl(url);
|
|
206
|
+
assertNonEmptyString(source, 'source');
|
|
207
|
+
const canonical = canonicalMetadata(metadata, 'discovery metadata');
|
|
208
|
+
let resource = resources.get(normalized);
|
|
209
|
+
if (!resource) {
|
|
210
|
+
resource = {
|
|
211
|
+
normalizedUrl: normalized,
|
|
212
|
+
originalUrls: new Set(),
|
|
213
|
+
discoverySources: new Map(),
|
|
214
|
+
exclusion: null,
|
|
215
|
+
attemptHistory: [],
|
|
216
|
+
retrieval: null,
|
|
217
|
+
classification: null,
|
|
218
|
+
evaluation: null,
|
|
219
|
+
failure: null,
|
|
220
|
+
indeterminate: null,
|
|
221
|
+
validButUnevaluated: null,
|
|
222
|
+
};
|
|
223
|
+
resources.set(normalized, resource);
|
|
224
|
+
}
|
|
225
|
+
resource.originalUrls.add(url);
|
|
226
|
+
const discovery = { source, url, ...(Object.keys(canonical).length > 0 ? { metadata: canonical } : {}) };
|
|
227
|
+
resource.discoverySources.set(stableString(discovery), discovery);
|
|
228
|
+
return normalized;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function exclude(url, reason) {
|
|
232
|
+
assertMutable();
|
|
233
|
+
assertNonEmptyString(reason, 'exclusion reason');
|
|
234
|
+
const resource = requireResource(url);
|
|
235
|
+
if (resource.attemptHistory.length > 0) throw new Error('cannot exclude a resource after an attempt');
|
|
236
|
+
if (resource.exclusion) throw new Error('resource is already excluded');
|
|
237
|
+
resource.exclusion = { reason };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function attempt(url, attemptMetadata = {}) {
|
|
241
|
+
assertMutable();
|
|
242
|
+
const resource = requireResource(url);
|
|
243
|
+
assertNotTerminal(resource, 'attempt');
|
|
244
|
+
if (resource.retrieval) throw new Error('cannot attempt a resource after retrieval');
|
|
245
|
+
resource.attemptHistory.push(canonicalMetadata(attemptMetadata, 'attempt metadata'));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function retrieve(url, responseMetadata = {}) {
|
|
249
|
+
assertMutable();
|
|
250
|
+
const resource = requireResource(url);
|
|
251
|
+
assertNotTerminal(resource, 'retrieve');
|
|
252
|
+
if (resource.attemptHistory.length === 0) throw new Error('resource must have an attempt before retrieval');
|
|
253
|
+
if (resource.retrieval) throw new Error('resource is already retrieved');
|
|
254
|
+
resource.retrieval = canonicalMetadata(responseMetadata, 'response metadata');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function classify(url, classification) {
|
|
258
|
+
assertMutable();
|
|
259
|
+
const resource = requireResource(url);
|
|
260
|
+
assertNotTerminal(resource, 'classify');
|
|
261
|
+
if (!resource.retrieval) throw new Error('resource must be retrieved before classification');
|
|
262
|
+
if (resource.classification) throw new Error('resource is already classified');
|
|
263
|
+
|
|
264
|
+
const canonical = typeof classification === 'string'
|
|
265
|
+
? { state: classification }
|
|
266
|
+
: canonicalMetadata(classification, 'classification');
|
|
267
|
+
if (canonical.state !== 'valid_resource' && canonical.state !== 'indeterminate') {
|
|
268
|
+
throw new Error('classification state must be valid_resource or indeterminate');
|
|
269
|
+
}
|
|
270
|
+
if (canonical.state === 'indeterminate') {
|
|
271
|
+
resource.indeterminate = canonical;
|
|
272
|
+
} else {
|
|
273
|
+
resource.classification = canonical;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function evaluate(url, evaluationMetadata = {}) {
|
|
278
|
+
assertMutable();
|
|
279
|
+
const resource = requireResource(url);
|
|
280
|
+
assertNotTerminal(resource, 'evaluate');
|
|
281
|
+
if (resource.classification?.state !== 'valid_resource') {
|
|
282
|
+
throw new Error('resource must be classified as a valid resource before evaluation');
|
|
283
|
+
}
|
|
284
|
+
resource.evaluation = canonicalMetadata(evaluationMetadata, 'evaluation metadata');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function fail(url, failureMetadata = {}) {
|
|
288
|
+
assertMutable();
|
|
289
|
+
const resource = requireResource(url);
|
|
290
|
+
assertNotTerminal(resource, 'fail');
|
|
291
|
+
if (resource.attemptHistory.length === 0) throw new Error('resource must have an attempt before failure');
|
|
292
|
+
if (resource.retrieval) throw new Error('retrieved resources cannot enter retrieval failure');
|
|
293
|
+
resource.failure = canonicalMetadata(failureMetadata, 'failure metadata');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function markIndeterminate(url, metadata = {}) {
|
|
297
|
+
assertMutable();
|
|
298
|
+
const resource = requireResource(url);
|
|
299
|
+
assertNotTerminal(resource, 'mark indeterminate');
|
|
300
|
+
if (!resource.retrieval) throw new Error('resource must be retrieved before it can be indeterminate');
|
|
301
|
+
if (resource.classification?.state === 'valid_resource') {
|
|
302
|
+
throw new Error('a valid resource must be evaluated or marked valid-but-unevaluated');
|
|
303
|
+
}
|
|
304
|
+
resource.indeterminate = { ...canonicalMetadata(metadata, 'indeterminate metadata'), state: 'indeterminate' };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function markValidButUnevaluated(url, metadata = {}) {
|
|
308
|
+
assertMutable();
|
|
309
|
+
const resource = requireResource(url);
|
|
310
|
+
assertNotTerminal(resource, 'mark valid-but-unevaluated');
|
|
311
|
+
if (resource.classification?.state !== 'valid_resource') {
|
|
312
|
+
throw new Error('resource must be classified as a valid resource before evaluator failure');
|
|
313
|
+
}
|
|
314
|
+
resource.validButUnevaluated = canonicalMetadata(metadata, 'evaluator failure metadata');
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function finalizedState(resource) {
|
|
318
|
+
const state = stateOf(resource);
|
|
319
|
+
if (state === 'eligible') return 'unvisited';
|
|
320
|
+
if (state === 'attempted' || state === 'retrieved' || state === 'valid_resource') {
|
|
321
|
+
throw new Error(`resource ${resource.normalizedUrl} has unresolved state ${state}`);
|
|
322
|
+
}
|
|
323
|
+
return state;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function resourceOutput(resource) {
|
|
327
|
+
const state = finalizedState(resource);
|
|
328
|
+
const output = {
|
|
329
|
+
normalized_url: resource.normalizedUrl,
|
|
330
|
+
original_urls: [...resource.originalUrls].sort(compareStrings),
|
|
331
|
+
discovery_sources: [...resource.discoverySources.values()].sort((a, b) => (
|
|
332
|
+
compareStrings(a.source, b.source)
|
|
333
|
+
|| compareStrings(a.url, b.url)
|
|
334
|
+
|| compareStrings(stableString(a), stableString(b))
|
|
335
|
+
)),
|
|
336
|
+
state,
|
|
337
|
+
};
|
|
338
|
+
if (resource.exclusion) output.exclusion = resource.exclusion;
|
|
339
|
+
if (resource.attemptHistory.length > 0) output.attempt_history = resource.attemptHistory;
|
|
340
|
+
if (resource.retrieval) output.retrieval = resource.retrieval;
|
|
341
|
+
if (resource.classification) output.classification = resource.classification;
|
|
342
|
+
if (resource.evaluation) output.evaluation = resource.evaluation;
|
|
343
|
+
if (resource.failure) output.failure = resource.failure;
|
|
344
|
+
if (resource.indeterminate) output.indeterminate = resource.indeterminate;
|
|
345
|
+
if (resource.validButUnevaluated) output.evaluator_failure = resource.validButUnevaluated;
|
|
346
|
+
return output;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function finalize(stopReason, options = {}) {
|
|
350
|
+
assertMutable();
|
|
351
|
+
if (!STOP_REASONS.has(stopReason)) throw new Error(`unknown stop reason: ${stopReason}`);
|
|
352
|
+
const {
|
|
353
|
+
discoveryStatus = 'complete',
|
|
354
|
+
discoveryMethods,
|
|
355
|
+
discoveryLimitations = [],
|
|
356
|
+
limitations = [],
|
|
357
|
+
preview = false,
|
|
358
|
+
} = options;
|
|
359
|
+
if (!DISCOVERY_STATUSES.has(discoveryStatus)) {
|
|
360
|
+
throw new Error(`unknown discovery status: ${discoveryStatus}`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const provisional = [];
|
|
364
|
+
if (preview) {
|
|
365
|
+
for (const resource of resources.values()) {
|
|
366
|
+
if (resource.classification?.state === 'valid_resource' && !resource.evaluation && !resource.validButUnevaluated) {
|
|
367
|
+
resource.validButUnevaluated = { reason: 'evaluation_pending' };
|
|
368
|
+
provisional.push(resource);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
const outputResources = [...resources.values()]
|
|
373
|
+
.map(resourceOutput)
|
|
374
|
+
.sort((a, b) => compareStrings(a.normalized_url, b.normalized_url));
|
|
375
|
+
const stateCount = (state) => outputResources.filter((resource) => resource.state === state).length;
|
|
376
|
+
const populations = {
|
|
377
|
+
discovered: outputResources.length,
|
|
378
|
+
eligible: outputResources.length - stateCount('excluded'),
|
|
379
|
+
excluded: stateCount('excluded'),
|
|
380
|
+
attempted: outputResources.filter((resource) => resource.attempt_history).length,
|
|
381
|
+
retrieved: outputResources.filter((resource) => resource.retrieval).length,
|
|
382
|
+
valid_resource: outputResources.filter((resource) => resource.classification?.state === 'valid_resource').length,
|
|
383
|
+
evaluated: stateCount('evaluated'),
|
|
384
|
+
valid_but_unevaluated: stateCount('valid_but_unevaluated'),
|
|
385
|
+
failed: stateCount('failed'),
|
|
386
|
+
indeterminate: stateCount('indeterminate'),
|
|
387
|
+
unvisited: stateCount('unvisited'),
|
|
388
|
+
};
|
|
389
|
+
const methods = discoveryMethods === undefined
|
|
390
|
+
? [...new Set(outputResources.flatMap((resource) => resource.discovery_sources.map(({ source }) => source)))].sort(compareStrings)
|
|
391
|
+
: sortedUniqueStrings(discoveryMethods, 'discoveryMethods');
|
|
392
|
+
const coverage = {
|
|
393
|
+
schema_version: COVERAGE_SCHEMA_VERSION,
|
|
394
|
+
normalization_version: normalizationVersion,
|
|
395
|
+
requested_scope: {
|
|
396
|
+
start_url: startUrl,
|
|
397
|
+
origin: parsedStartUrl.origin,
|
|
398
|
+
max_pages: maxPages,
|
|
399
|
+
time_budget_seconds: timeBudgetSeconds,
|
|
400
|
+
},
|
|
401
|
+
discovery: {
|
|
402
|
+
status: discoveryStatus,
|
|
403
|
+
methods,
|
|
404
|
+
limitations: sortedUniqueStrings(discoveryLimitations, 'discoveryLimitations'),
|
|
405
|
+
},
|
|
406
|
+
populations,
|
|
407
|
+
resources: outputResources,
|
|
408
|
+
stop_reason: stopReason,
|
|
409
|
+
coverage_status: coverageStatusFor({ stopReason, discoveryStatus, populations }),
|
|
410
|
+
reconciliation: { valid: false, errors: ['not reconciled'] },
|
|
411
|
+
limitations: sortedUniqueStrings(limitations, 'limitations'),
|
|
412
|
+
};
|
|
413
|
+
coverage.reconciliation = reconcileCoverage(coverage);
|
|
414
|
+
if (!coverage.reconciliation.valid) {
|
|
415
|
+
throw new Error(`coverage reconciliation failed: ${coverage.reconciliation.errors.join('; ')}`);
|
|
416
|
+
}
|
|
417
|
+
const schemaValidation = validateAgainst('audit-coverage.schema.json', coverage);
|
|
418
|
+
if (!schemaValidation.valid) {
|
|
419
|
+
throw new Error(`audit coverage invalid: ${schemaValidation.errors.join('; ')}`);
|
|
420
|
+
}
|
|
421
|
+
for (const resource of provisional) resource.validButUnevaluated = null;
|
|
422
|
+
if (!preview) finalized = true;
|
|
423
|
+
return coverage;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Read-only state inspection for detectors that must distinguish an
|
|
427
|
+
// unvisited sitemap URL from an observed retrieval failure. This does not
|
|
428
|
+
// expose mutable ledger internals or alter reconciliation.
|
|
429
|
+
function stateFor(url) {
|
|
430
|
+
const resource = resources.get(normalizedUrl(url));
|
|
431
|
+
return resource ? stateOf(resource) : null;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
discover,
|
|
436
|
+
exclude,
|
|
437
|
+
attempt,
|
|
438
|
+
retrieve,
|
|
439
|
+
classify,
|
|
440
|
+
evaluate,
|
|
441
|
+
fail,
|
|
442
|
+
markIndeterminate,
|
|
443
|
+
markValidButUnevaluated,
|
|
444
|
+
stateFor,
|
|
445
|
+
preview: (stopReason, options = {}) => finalize(stopReason, { ...options, preview: true }),
|
|
446
|
+
finalize,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage requirements for determinations.
|
|
3
|
+
*
|
|
4
|
+
* A requirement describes the population a conclusion is allowed to speak
|
|
5
|
+
* about. It is deliberately separate from the crawler's execution status:
|
|
6
|
+
* observing one page can support a page-local positive finding even when a
|
|
7
|
+
* corpus-wide absence claim is not supportable.
|
|
8
|
+
*/
|
|
9
|
+
export const REQUIREMENTS = Object.freeze({
|
|
10
|
+
PAGE_RESOURCE: 'page_resource',
|
|
11
|
+
EVALUATED_SUBSET: 'evaluated_subset',
|
|
12
|
+
DECLARED_SAMPLE: 'declared_sample',
|
|
13
|
+
EXHAUSTIVE_SCOPE: 'exhaustive_requested_scope',
|
|
14
|
+
RENDERED_RESOURCE: 'rendered_resource',
|
|
15
|
+
PROVIDER_BOUNDED: 'provider_bounded',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const VALID_REQUIREMENTS = new Set(Object.values(REQUIREMENTS));
|
|
19
|
+
|
|
20
|
+
export function isCoverageRequirement(value) {
|
|
21
|
+
return VALID_REQUIREMENTS.has(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function population(coverage) {
|
|
25
|
+
return coverage?.populations && typeof coverage.populations === 'object'
|
|
26
|
+
? coverage.populations
|
|
27
|
+
: {};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function normalizedUrl(value) {
|
|
31
|
+
if (typeof value !== 'string' || !value) return null;
|
|
32
|
+
try {
|
|
33
|
+
const url = new URL(value);
|
|
34
|
+
url.hash = '';
|
|
35
|
+
return url.href;
|
|
36
|
+
} catch {
|
|
37
|
+
return value.split('#', 1)[0];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function resourcesFor(coverage) {
|
|
42
|
+
return Array.isArray(coverage?.resources) ? coverage.resources : [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resourceForSubject(coverage, subject) {
|
|
46
|
+
const candidates = [subject?.url, subject?.identifier].map(normalizedUrl).filter(Boolean);
|
|
47
|
+
return resourcesFor(coverage).find((resource) => {
|
|
48
|
+
const resourceUrl = normalizedUrl(resource?.url || resource?.normalized_url);
|
|
49
|
+
return resourceUrl && candidates.includes(resourceUrl);
|
|
50
|
+
}) || null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function completeCoverage(coverage) {
|
|
54
|
+
const p = population(coverage);
|
|
55
|
+
return coverage?.coverage_status === 'complete'
|
|
56
|
+
&& p.unvisited === 0
|
|
57
|
+
&& p.failed === 0
|
|
58
|
+
&& p.indeterminate === 0
|
|
59
|
+
&& p.valid_but_unevaluated === 0
|
|
60
|
+
&& p.evaluated > 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function result(status, extra = {}) {
|
|
64
|
+
return { status, ...extra };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function evaluatePageResource(coverage, subject, rendered = false) {
|
|
68
|
+
const resource = resourceForSubject(coverage, subject);
|
|
69
|
+
if (!resource) return result('indeterminate', { reason: 'resource_not_observed' });
|
|
70
|
+
const state = resource.state || (resource.evaluation ? 'evaluated' : resource.classification?.state);
|
|
71
|
+
if (state !== 'evaluated') {
|
|
72
|
+
return result('indeterminate', { reason: 'resource_not_evaluated', resource_id: resource.resource_id });
|
|
73
|
+
}
|
|
74
|
+
if (rendered && resource.rendered !== true && resource.rendering_status !== 'complete') {
|
|
75
|
+
return result('indeterminate', { reason: 'rendered_observation_unavailable', resource_id: resource.resource_id });
|
|
76
|
+
}
|
|
77
|
+
return result('supported', {
|
|
78
|
+
resource_id: resource.resource_id,
|
|
79
|
+
population: 'evaluated_resource',
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Evaluate whether a coverage envelope satisfies one determination contract.
|
|
85
|
+
* The returned status is intentionally limited to supported, qualified,
|
|
86
|
+
* indeterminate, and not_applicable, matching the finding/run contracts.
|
|
87
|
+
*/
|
|
88
|
+
export function evaluateRequirement(requirement, coverage, subject = null) {
|
|
89
|
+
if (!VALID_REQUIREMENTS.has(requirement)) throw new TypeError(`unknown coverage requirement: ${requirement}`);
|
|
90
|
+
|
|
91
|
+
if (requirement === REQUIREMENTS.PAGE_RESOURCE) return evaluatePageResource(coverage, subject);
|
|
92
|
+
if (requirement === REQUIREMENTS.RENDERED_RESOURCE) return evaluatePageResource(coverage, subject, true);
|
|
93
|
+
|
|
94
|
+
if (requirement === REQUIREMENTS.DECLARED_SAMPLE) {
|
|
95
|
+
const sample = coverage?.sample;
|
|
96
|
+
const numerator = sample?.numerator;
|
|
97
|
+
const denominator = sample?.denominator;
|
|
98
|
+
if (!Number.isInteger(numerator) || !Number.isInteger(denominator) || denominator < 1 || numerator < 0 || numerator > denominator) {
|
|
99
|
+
return result('indeterminate', { reason: 'sample_denominator_unavailable' });
|
|
100
|
+
}
|
|
101
|
+
return { status: 'qualified', population: 'evaluated_subset', numerator, denominator };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (requirement === REQUIREMENTS.PROVIDER_BOUNDED) {
|
|
105
|
+
const p = population(coverage);
|
|
106
|
+
if (!Number.isInteger(p.evaluated) || p.evaluated < 1) return result('indeterminate', { reason: 'no_evaluated_resources' });
|
|
107
|
+
return result('qualified', { population: 'provider_bounded', denominator: p.evaluated });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (requirement === REQUIREMENTS.EVALUATED_SUBSET) {
|
|
111
|
+
// A subject-bearing determination cannot borrow aggregate subset counts:
|
|
112
|
+
// the named page/URL must itself be an evaluator-completed resource.
|
|
113
|
+
if (subject?.type === 'page' || subject?.type === 'url') {
|
|
114
|
+
return evaluatePageResource(coverage, subject);
|
|
115
|
+
}
|
|
116
|
+
const p = population(coverage);
|
|
117
|
+
if (!Number.isInteger(p.evaluated) || p.evaluated < 1) return result('indeterminate', { reason: 'no_evaluated_resources' });
|
|
118
|
+
return result(coverage?.coverage_status === 'complete' ? 'supported' : 'qualified', {
|
|
119
|
+
population: 'evaluated_subset', denominator: p.evaluated,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Exhaustive scope is the only requirement that may support a corpus-wide
|
|
124
|
+
// absence claim. A completed execution with zero resources is not evidence
|
|
125
|
+
// of absence, so require at least one evaluated resource.
|
|
126
|
+
if (completeCoverage(coverage)) {
|
|
127
|
+
return result('supported', { population: 'requested_scope', denominator: population(coverage).evaluated });
|
|
128
|
+
}
|
|
129
|
+
return result('indeterminate', { reason: 'coverage_incomplete' });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Apply coverage semantics to a determination produced by a detector or
|
|
134
|
+
* downstream consumer. Positive page-local observations are preserved; a
|
|
135
|
+
* negative exhaustive conclusion is downgraded when its corpus is incomplete.
|
|
136
|
+
*/
|
|
137
|
+
export function propagateDetermination(determination, coverage) {
|
|
138
|
+
if (!determination || typeof determination !== 'object') throw new TypeError('determination must be an object');
|
|
139
|
+
const current = determination.status;
|
|
140
|
+
if (!['supported', 'qualified', 'indeterminate', 'not_applicable'].includes(current)) {
|
|
141
|
+
throw new TypeError(`invalid determination status: ${current}`);
|
|
142
|
+
}
|
|
143
|
+
if (current !== 'supported') return { ...determination };
|
|
144
|
+
if (determination.polarity !== 'negative' || !determination.requirement) return { ...determination };
|
|
145
|
+
const evaluated = evaluateRequirement(determination.requirement, coverage, determination.subject);
|
|
146
|
+
if (evaluated.status === 'supported') return { ...determination };
|
|
147
|
+
return { ...determination, status: evaluated.status, reason: evaluated.reason || 'coverage_requirement_unsatisfied' };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function requirementForDetector(detector, hit = null) {
|
|
151
|
+
if (!detector || !isCoverageRequirement(detector.coverage_requirement)) {
|
|
152
|
+
throw new TypeError(`detector ${detector?.id ?? '?'} must declare a valid coverage_requirement`);
|
|
153
|
+
}
|
|
154
|
+
// The detector declaration is authoritative. Subject shape must never
|
|
155
|
+
// silently weaken an exhaustive or rendered-resource contract.
|
|
156
|
+
return detector.coverage_requirement;
|
|
157
|
+
}
|