@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
package/src/detectors/tech.js
CHANGED
|
@@ -5,6 +5,7 @@ import { isAllowed } from '../crawler/robots.js';
|
|
|
5
5
|
const D = [];
|
|
6
6
|
|
|
7
7
|
D.push(defineDetector({
|
|
8
|
+
coverage_requirement: 'evaluated_subset',
|
|
8
9
|
id: 'TECH-001', name: 'Non-200 index-target URL', namespace: 'TECH',
|
|
9
10
|
description: 'A page intended for indexing does not return HTTP 200.',
|
|
10
11
|
discipline: ['seo', 'aeo', 'geo'], severity: 'critical', deterministic: true, requires: ['site'],
|
|
@@ -23,6 +24,7 @@ D.push(defineDetector({
|
|
|
23
24
|
}));
|
|
24
25
|
|
|
25
26
|
D.push(defineDetector({
|
|
27
|
+
coverage_requirement: 'evaluated_subset',
|
|
26
28
|
id: 'TECH-002', name: 'Accidental noindex on index-target page', namespace: 'TECH',
|
|
27
29
|
description: 'A page whose registry intent is "index" carries a noindex directive (meta robots or X-Robots-Tag).',
|
|
28
30
|
discipline: ['seo', 'aeo', 'geo'], severity: 'critical', deterministic: true, requires: ['site'],
|
|
@@ -41,6 +43,7 @@ D.push(defineDetector({
|
|
|
41
43
|
}));
|
|
42
44
|
|
|
43
45
|
D.push(defineDetector({
|
|
46
|
+
coverage_requirement: 'evaluated_subset',
|
|
44
47
|
id: 'TECH-003', name: 'robots.txt blocks index-target page', namespace: 'TECH',
|
|
45
48
|
description: 'robots.txt disallows a search crawler from fetching a page that is intended to be indexed and cited.',
|
|
46
49
|
discipline: ['seo', 'aeo', 'geo'], severity: 'critical', deterministic: true, requires: ['site'],
|
|
@@ -67,6 +70,7 @@ D.push(defineDetector({
|
|
|
67
70
|
}));
|
|
68
71
|
|
|
69
72
|
D.push(defineDetector({
|
|
73
|
+
coverage_requirement: 'evaluated_subset',
|
|
70
74
|
id: 'TECH-004', name: 'Multiple conflicting canonical declarations', namespace: 'TECH',
|
|
71
75
|
description: 'A document declares more than one rel=canonical with differing targets.',
|
|
72
76
|
discipline: ['seo', 'aeo'], severity: 'high', deterministic: true, requires: ['site'],
|
|
@@ -85,6 +89,7 @@ D.push(defineDetector({
|
|
|
85
89
|
}));
|
|
86
90
|
|
|
87
91
|
D.push(defineDetector({
|
|
92
|
+
coverage_requirement: 'evaluated_subset',
|
|
88
93
|
id: 'TECH-005', name: 'Canonical points to redirect', namespace: 'TECH',
|
|
89
94
|
description: 'rel=canonical targets a URL that itself redirects.',
|
|
90
95
|
discipline: ['seo', 'aeo'], severity: 'high', deterministic: true, requires: ['site'],
|
|
@@ -98,6 +103,7 @@ D.push(defineDetector({
|
|
|
98
103
|
}));
|
|
99
104
|
|
|
100
105
|
D.push(defineDetector({
|
|
106
|
+
coverage_requirement: 'evaluated_subset',
|
|
101
107
|
id: 'TECH-006', name: 'Canonical points to error page', namespace: 'TECH',
|
|
102
108
|
description: 'rel=canonical targets a URL returning 4xx/5xx.',
|
|
103
109
|
discipline: ['seo', 'aeo'], severity: 'high', deterministic: true, requires: ['site'],
|
|
@@ -111,6 +117,7 @@ D.push(defineDetector({
|
|
|
111
117
|
}));
|
|
112
118
|
|
|
113
119
|
D.push(defineDetector({
|
|
120
|
+
coverage_requirement: 'evaluated_subset',
|
|
114
121
|
id: 'TECH-007', name: 'Canonical points to noindex page', namespace: 'TECH',
|
|
115
122
|
description: 'rel=canonical consolidates signals into a URL that is itself noindexed.',
|
|
116
123
|
discipline: ['seo', 'aeo'], severity: 'high', deterministic: true, requires: ['site'],
|
|
@@ -124,6 +131,7 @@ D.push(defineDetector({
|
|
|
124
131
|
}));
|
|
125
132
|
|
|
126
133
|
D.push(defineDetector({
|
|
134
|
+
coverage_requirement: 'evaluated_subset',
|
|
127
135
|
id: 'TECH-008', name: 'Sitemap contains redirecting URL', namespace: 'TECH',
|
|
128
136
|
description: 'An XML sitemap lists a URL that does not resolve directly with HTTP 200 (redirects).',
|
|
129
137
|
discipline: ['seo', 'aeo'], severity: 'medium', deterministic: true, requires: ['site'],
|
|
@@ -137,6 +145,7 @@ D.push(defineDetector({
|
|
|
137
145
|
}));
|
|
138
146
|
|
|
139
147
|
D.push(defineDetector({
|
|
148
|
+
coverage_requirement: 'evaluated_subset',
|
|
140
149
|
id: 'TECH-009', name: 'Sitemap contains non-canonical URL', namespace: 'TECH',
|
|
141
150
|
description: 'A sitemap lists a URL whose page declares a different canonical.',
|
|
142
151
|
discipline: ['seo', 'aeo'], severity: 'medium', deterministic: true, requires: ['site'],
|
|
@@ -166,6 +175,7 @@ D.push(defineDetector({
|
|
|
166
175
|
}));
|
|
167
176
|
|
|
168
177
|
D.push(defineDetector({
|
|
178
|
+
coverage_requirement: 'evaluated_subset',
|
|
169
179
|
id: 'TECH-010', name: 'Sitemap URL unresolvable or errors', namespace: 'TECH',
|
|
170
180
|
description: 'A sitemap lists a URL that is missing from the built output or returns an error.',
|
|
171
181
|
discipline: ['seo', 'aeo'], severity: 'medium', deterministic: true, requires: ['site'],
|
|
@@ -178,13 +188,19 @@ D.push(defineDetector({
|
|
|
178
188
|
for (const sm of ctx.site.sitemaps) {
|
|
179
189
|
for (const u of sm.parsed.urls) {
|
|
180
190
|
const page = ctx.site.byUrl.get(ctx.site.normalize(u.loc));
|
|
181
|
-
|
|
191
|
+
const crawlState = ctx.site.mode === 'url' ? ctx.site.coverageLedger?.stateFor(u.loc) : null;
|
|
192
|
+
if (!page && ctx.site.mode === 'url' && crawlState !== 'failed') {
|
|
193
|
+
// An unvisited or merely queued sitemap URL is not evidence of an
|
|
194
|
+
// invalid resource. Coverage records it for an aggregate
|
|
195
|
+
// determination instead of manufacturing a TECH-010 absence claim.
|
|
182
196
|
continue;
|
|
183
197
|
} else if (!page) {
|
|
184
198
|
hits.push({
|
|
185
199
|
subject: { type: 'url', identifier: u.loc, url: u.loc, source_location: sm.source },
|
|
186
|
-
summary: 'Sitemap URL not present in audited output',
|
|
187
|
-
evidence: [
|
|
200
|
+
summary: crawlState === 'failed' ? 'Sitemap URL fetch failed' : 'Sitemap URL not present in audited output',
|
|
201
|
+
evidence: [crawlState === 'failed'
|
|
202
|
+
? `sitemap ${sm.source} lists ${u.loc}; retrieval failed after attempted observation`
|
|
203
|
+
: `sitemap ${sm.source} lists ${u.loc}; no matching page found`],
|
|
188
204
|
});
|
|
189
205
|
} else if (page.status >= 400) {
|
|
190
206
|
hits.push({
|
|
@@ -200,6 +216,7 @@ D.push(defineDetector({
|
|
|
200
216
|
}));
|
|
201
217
|
|
|
202
218
|
D.push(defineDetector({
|
|
219
|
+
coverage_requirement: 'evaluated_subset',
|
|
203
220
|
id: 'TECH-011', name: 'Render-dependent primary content', namespace: 'TECH',
|
|
204
221
|
description: 'Initial HTML contains almost no primary text relative to script payload; principal content likely requires client-side rendering.',
|
|
205
222
|
discipline: ['seo', 'aeo', 'geo'], severity: 'high', deterministic: false, requires: ['site'],
|
|
@@ -221,6 +238,7 @@ D.push(defineDetector({
|
|
|
221
238
|
}));
|
|
222
239
|
|
|
223
240
|
D.push(defineDetector({
|
|
241
|
+
coverage_requirement: 'evaluated_subset',
|
|
224
242
|
id: 'TECH-012', name: 'Invalid MIME type for HTML page', namespace: 'TECH',
|
|
225
243
|
description: 'An index-target page is served with a non-HTML content type.',
|
|
226
244
|
discipline: ['seo'], severity: 'high', deterministic: true, requires: ['site'],
|
|
@@ -241,6 +259,7 @@ D.push(defineDetector({
|
|
|
241
259
|
}));
|
|
242
260
|
|
|
243
261
|
D.push(defineDetector({
|
|
262
|
+
coverage_requirement: 'evaluated_subset',
|
|
244
263
|
id: 'TECH-013', name: 'Redirect chain', namespace: 'TECH',
|
|
245
264
|
description: 'Reaching the final URL required more than one redirect hop.',
|
|
246
265
|
discipline: ['seo'], severity: 'medium', deterministic: true, requires: ['site'],
|
|
@@ -258,6 +277,7 @@ D.push(defineDetector({
|
|
|
258
277
|
}));
|
|
259
278
|
|
|
260
279
|
D.push(defineDetector({
|
|
280
|
+
coverage_requirement: 'evaluated_subset',
|
|
261
281
|
id: 'TECH-014', name: 'Soft-404 behaviour', namespace: 'TECH',
|
|
262
282
|
description: 'A page returns 200 but its content indicates the resource does not exist.',
|
|
263
283
|
discipline: ['seo'], severity: 'medium', deterministic: false, requires: ['site'],
|
|
@@ -280,6 +300,7 @@ D.push(defineDetector({
|
|
|
280
300
|
}));
|
|
281
301
|
|
|
282
302
|
D.push(defineDetector({
|
|
303
|
+
coverage_requirement: 'evaluated_subset',
|
|
283
304
|
id: 'TECH-015', name: 'Staging or preview host exposed as indexable', namespace: 'TECH',
|
|
284
305
|
description: 'The audited base URL looks like a staging/preview environment yet pages are indexable.',
|
|
285
306
|
discipline: ['seo'], severity: 'critical', deterministic: false, requires: ['site'],
|
|
@@ -305,6 +326,7 @@ D.push(defineDetector({
|
|
|
305
326
|
}));
|
|
306
327
|
|
|
307
328
|
D.push(defineDetector({
|
|
329
|
+
coverage_requirement: 'exhaustive_requested_scope',
|
|
308
330
|
id: 'TECH-016', name: 'Missing sitemap', namespace: 'TECH',
|
|
309
331
|
description: 'No XML sitemap was found for the audited output.',
|
|
310
332
|
discipline: ['seo', 'aeo'], severity: 'medium', deterministic: true, requires: ['site'],
|
|
@@ -323,6 +345,7 @@ D.push(defineDetector({
|
|
|
323
345
|
}));
|
|
324
346
|
|
|
325
347
|
D.push(defineDetector({
|
|
348
|
+
coverage_requirement: 'evaluated_subset',
|
|
326
349
|
id: 'TECH-017', name: 'Snippet suppression on citation-target page', namespace: 'TECH',
|
|
327
350
|
description: 'A page targeted for answer citation carries nosnippet or max-snippet:0, which removes snippet eligibility that generative search features depend on.',
|
|
328
351
|
discipline: ['aeo', 'geo'], severity: 'high', deterministic: true, requires: ['site'],
|
|
@@ -340,6 +363,7 @@ D.push(defineDetector({
|
|
|
340
363
|
}));
|
|
341
364
|
|
|
342
365
|
D.push(defineDetector({
|
|
366
|
+
coverage_requirement: 'evaluated_subset',
|
|
343
367
|
id: 'TECH-018', name: 'Missing viewport (mobile readiness)', namespace: 'TECH',
|
|
344
368
|
description: 'Page lacks a viewport meta tag, a minimum condition for mobile parity.',
|
|
345
369
|
discipline: ['seo'], severity: 'low', deterministic: true, requires: ['site'],
|
|
@@ -360,6 +384,7 @@ D.push(defineDetector({
|
|
|
360
384
|
}));
|
|
361
385
|
|
|
362
386
|
D.push(defineDetector({
|
|
387
|
+
coverage_requirement: 'evaluated_subset',
|
|
363
388
|
id: 'TECH-019', name: 'Open Graph URL conflicts with canonical', namespace: 'TECH',
|
|
364
389
|
description: 'Open Graph URL (og:url) disagrees with the declared canonical URL, sending conflicting canonicalization and entity identity signals.',
|
|
365
390
|
discipline: ['seo', 'aeo', 'geo'], severity: 'medium', deterministic: true, requires: ['site'],
|
|
@@ -395,6 +420,7 @@ D.push(defineDetector({
|
|
|
395
420
|
}));
|
|
396
421
|
|
|
397
422
|
D.push(defineDetector({
|
|
423
|
+
coverage_requirement: 'evaluated_subset',
|
|
398
424
|
id: 'TECH-020', name: 'Missing reciprocal hreflang return link', namespace: 'TECH',
|
|
399
425
|
description: 'An alternate hreflang URL targets an internal page, but that target page does not reciprocate with a matching hreflang link back to the source page.',
|
|
400
426
|
discipline: ['seo', 'geo'], severity: 'high', deterministic: true, requires: ['site'],
|
|
@@ -445,6 +471,7 @@ D.push(defineDetector({
|
|
|
445
471
|
}));
|
|
446
472
|
|
|
447
473
|
D.push(defineDetector({
|
|
474
|
+
coverage_requirement: 'evaluated_subset',
|
|
448
475
|
id: 'TECH-021', name: 'Invalid or non-standard BCP 47 hreflang syntax', namespace: 'TECH',
|
|
449
476
|
description: 'An hreflang annotation uses an invalid language code, malformed region subtag, deprecated territory, or relative URL.',
|
|
450
477
|
discipline: ['seo', 'geo'], severity: 'high', deterministic: true, requires: ['site'],
|
|
@@ -493,6 +520,7 @@ D.push(defineDetector({
|
|
|
493
520
|
}));
|
|
494
521
|
|
|
495
522
|
D.push(defineDetector({
|
|
523
|
+
coverage_requirement: 'evaluated_subset',
|
|
496
524
|
id: 'TECH-022', name: 'Hreflang alternate target conflicts with rel=canonical', namespace: 'TECH',
|
|
497
525
|
description: 'An alternate hreflang URL points to a page that declares a different rel=canonical target, breaking the alternate cluster.',
|
|
498
526
|
discipline: ['seo', 'geo'], severity: 'critical', deterministic: true, requires: ['site'],
|
|
@@ -539,6 +567,7 @@ D.push(defineDetector({
|
|
|
539
567
|
}));
|
|
540
568
|
|
|
541
569
|
D.push(defineDetector({
|
|
570
|
+
coverage_requirement: 'evaluated_subset',
|
|
542
571
|
id: 'TECH-023', name: 'Hydration gap: critical metadata or schema missing from initial server HTML response', namespace: 'TECH',
|
|
543
572
|
description: 'A single-page application or client-rendered document returns empty content mounting shells with virtually no static text (<20 words) in initial server HTML, breaking crawler and answer engine extraction without full headless JS rendering.',
|
|
544
573
|
discipline: ['seo', 'aeo'], severity: 'high', deterministic: true, requires: ['site'],
|
|
@@ -574,6 +603,7 @@ D.push(defineDetector({
|
|
|
574
603
|
}));
|
|
575
604
|
|
|
576
605
|
D.push(defineDetector({
|
|
606
|
+
coverage_requirement: 'evaluated_subset',
|
|
577
607
|
id: 'TECH-024', name: 'Excessive render-blocking script payload exceeding crawler execution budget', namespace: 'TECH',
|
|
578
608
|
description: 'The page delivers excessive inline/head script payloads (>1.2MB) or an excessive number of render-blocking head scripts (>12 without defer/async), exhausting crawler execution budgets and delaying indexing.',
|
|
579
609
|
discipline: ['seo', 'aeo'], severity: 'medium', deterministic: true, requires: ['site'],
|
|
@@ -619,6 +649,7 @@ D.push(defineDetector({
|
|
|
619
649
|
}));
|
|
620
650
|
|
|
621
651
|
D.push(defineDetector({
|
|
652
|
+
coverage_requirement: 'evaluated_subset',
|
|
622
653
|
id: 'TECH-025', name: 'Oversized uncompressed image asset or heavy inline image payload', namespace: 'TECH',
|
|
623
654
|
description: 'The page embeds an excessively large inline image data URI (>100KB) or references raw uncompressed image payloads exceeding 1MB, delaying Largest Contentful Paint (LCP) and exhausting mobile data budgets.',
|
|
624
655
|
discipline: ['seo', 'aeo'], severity: 'medium', deterministic: true, requires: ['site'],
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { createExternalObservation, verifyExternalObservation } from '../evidence/external.js';
|
|
2
|
+
import { canonicalEvidenceJson } from '../evidence/hashes.js';
|
|
3
|
+
import { sha256, nowIso } from '../shared/io.js';
|
|
4
|
+
import { validateAgainst } from '../shared/schemaValidator.js';
|
|
5
|
+
import { providerRequest } from '../connectors/http.js';
|
|
6
|
+
import { collectionResult, errorMessage } from '../connectors/collectionResult.js';
|
|
7
|
+
|
|
8
|
+
const SEARCH_DATA_SCHEMA = 'search-discovery-data.schema.json';
|
|
9
|
+
const BASE = 'https://www.googleapis.com/webmasters/v3';
|
|
10
|
+
const DIMENSIONS = new Set(['date', 'query', 'page', 'country', 'device', 'searchAppearance']);
|
|
11
|
+
const COLLECTOR = { id: 'citable-search-console-observations', version: '1.0.0' };
|
|
12
|
+
const PARSER = { id: 'google-search-analytics-response', version: '1.0.0' };
|
|
13
|
+
|
|
14
|
+
function assertDate(value, name) {
|
|
15
|
+
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value) || Number.isNaN(Date.parse(`${value}T00:00:00Z`))) {
|
|
16
|
+
throw new TypeError(`${name} must be an ISO date (YYYY-MM-DD)`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function validateRequest(request) {
|
|
21
|
+
if (!request || typeof request !== 'object' || Array.isArray(request)) throw new TypeError('Search Console request is required');
|
|
22
|
+
if (typeof request.property_id !== 'string' || !request.property_id) throw new TypeError('property_id is required');
|
|
23
|
+
assertDate(request.start_date, 'start_date');
|
|
24
|
+
assertDate(request.end_date, 'end_date');
|
|
25
|
+
if (request.start_date > request.end_date) throw new RangeError('start_date must not be after end_date');
|
|
26
|
+
const dimensions = request.dimensions ?? ['query', 'page'];
|
|
27
|
+
if (!Array.isArray(dimensions) || dimensions.length === 0 || dimensions.some((item) => !DIMENSIONS.has(item))) throw new TypeError('dimensions contain an unsupported Search Console dimension');
|
|
28
|
+
const filters = request.filters ?? {};
|
|
29
|
+
if (!filters || typeof filters !== 'object' || Array.isArray(filters)) throw new TypeError('filters must be an object');
|
|
30
|
+
const rowLimit = request.row_limit ?? 25000;
|
|
31
|
+
if (!Number.isInteger(rowLimit) || rowLimit < 1 || rowLimit > 25000) throw new RangeError('row_limit must be an integer from 1 to 25000');
|
|
32
|
+
return { property_id: request.property_id, start_date: request.start_date, end_date: request.end_date, dimensions: [...new Set(dimensions)], filters, row_limit: rowLimit };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function configurationFor(request) {
|
|
36
|
+
const configuration = { property_id: request.property_id, dimensions: request.dimensions, filters: request.filters, row_limit: request.row_limit };
|
|
37
|
+
return { id: `gsc-${sha256(canonicalEvidenceJson(configuration)).slice(0, 16)}`, version: '1.0.0' };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function numberOrNull(value) {
|
|
41
|
+
return value == null || value === '' || !Number.isFinite(Number(value)) ? null : Number(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function requestBody(request, startRow) {
|
|
45
|
+
const body = { startDate: request.start_date, endDate: request.end_date, dimensions: request.dimensions, rowLimit: request.row_limit, startRow, dataState: 'final' };
|
|
46
|
+
if (Object.keys(request.filters).length) body.dimensionFilterGroups = [{ groupType: 'and', filters: Object.entries(request.filters).map(([dimension, expression]) => ({ dimension, operator: 'equals', expression: String(expression) })) }];
|
|
47
|
+
return body;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalizeRow(row, request, dimensions, pageStart, coverageStatus, providerTotal, responseHash, origin) {
|
|
51
|
+
if (!row || !Array.isArray(row.keys)) throw new TypeError('Search Console row is malformed');
|
|
52
|
+
const values = Object.fromEntries(dimensions.map((name, index) => [name, row.keys[index] ?? null]));
|
|
53
|
+
const data = {
|
|
54
|
+
query: values.query ?? null,
|
|
55
|
+
page: values.page ?? null,
|
|
56
|
+
interval: { start_date: request.start_date, end_date: request.end_date },
|
|
57
|
+
metrics: { impressions: numberOrNull(row.impressions), clicks: numberOrNull(row.clicks), ctr: numberOrNull(row.ctr), average_position: numberOrNull(row.position) },
|
|
58
|
+
dimensions: { country: values.country ?? null, device: values.device ?? null, search_appearance: values.searchAppearance ?? null, date: values.date ?? null },
|
|
59
|
+
request: { property_id: request.property_id, dimensions: request.dimensions, filters: request.filters, row_limit: request.row_limit },
|
|
60
|
+
provider_reported: { row_count: providerTotal, page_start: pageStart },
|
|
61
|
+
coverage_status: coverageStatus,
|
|
62
|
+
response_hash: responseHash,
|
|
63
|
+
evidence_origin: origin,
|
|
64
|
+
};
|
|
65
|
+
const check = validateAgainst(SEARCH_DATA_SCHEMA, data);
|
|
66
|
+
if (!check.valid) throw new TypeError(`search discovery data violates contract: ${check.errors.join('; ')}`);
|
|
67
|
+
const subjectId = `query:${data.query ?? ''}|page:${data.page ?? ''}`;
|
|
68
|
+
return createExternalObservation({
|
|
69
|
+
observation_id: `GSC-${sha256(canonicalEvidenceJson({ request, values, row })).slice(0, 24)}`,
|
|
70
|
+
observation_type: 'EXTERNAL_OBSERVATION',
|
|
71
|
+
subject: { type: 'search_query_page', id: subjectId || `row:${pageStart}` },
|
|
72
|
+
source: { provider: 'google_search_console', method: 'search_analytics_api', authority: 'external_provider', account_or_property: request.property_id },
|
|
73
|
+
collector: COLLECTOR,
|
|
74
|
+
parser: PARSER,
|
|
75
|
+
configuration: configurationFor(request),
|
|
76
|
+
observed_at: nowIso(),
|
|
77
|
+
retrieved_at: nowIso(),
|
|
78
|
+
population: { declared: { id: `GSC-${sha256(canonicalEvidenceJson(request)).slice(0, 16)}`, size: providerTotal }, observed: 1, retrieved: 1, evaluated: 1, unavailable: 0, failed: 0 },
|
|
79
|
+
observation_status: coverageStatus === 'complete' ? 'observed' : 'incomplete',
|
|
80
|
+
retrieval_status: coverageStatus === 'complete' ? 'retrieved' : coverageStatus === 'indeterminate' ? 'indeterminate' : 'partial',
|
|
81
|
+
data,
|
|
82
|
+
lineage: { source_evidence: [responseHash] },
|
|
83
|
+
limitations: ['Search Console reports provider-returned search analytics rows, not universal search demand.', 'Provider privacy filtering, aggregation, and reporting revisions may limit representativeness.'],
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Collect normalized Search Console search-analytics observations. This is a
|
|
89
|
+
* provider adapter: credentials are supplied only to the transport and are
|
|
90
|
+
* never written into observations or continuation state.
|
|
91
|
+
*/
|
|
92
|
+
export async function collectSearchConsoleObservations({ connection, request, token, fetchImpl = globalThis.fetch, maxPages = 1000, evidenceOrigin = 'LIVE_PROVIDER' } = {}) {
|
|
93
|
+
const normalized = validateRequest(request);
|
|
94
|
+
if (!['LIVE_PROVIDER', 'RECORDED_FIXTURE', 'SYNTHETIC_TEST'].includes(evidenceOrigin)) throw new TypeError('evidenceOrigin is invalid');
|
|
95
|
+
if (!Number.isInteger(maxPages) || maxPages < 1 || maxPages > 10000) throw new RangeError('maxPages must be an integer from 1 to 10000');
|
|
96
|
+
if (connection?.property_id && connection.property_id !== normalized.property_id) throw new Error('connection property_id does not match request property_id');
|
|
97
|
+
const dimensions = normalized.dimensions;
|
|
98
|
+
const rows = [];
|
|
99
|
+
const observations = [];
|
|
100
|
+
const errors = [];
|
|
101
|
+
let startRow = 0;
|
|
102
|
+
let providerTotal = null;
|
|
103
|
+
let continuationState = null;
|
|
104
|
+
let pagesRequested = 0;
|
|
105
|
+
let pagesRetrieved = 0;
|
|
106
|
+
let lastResponseHash = null;
|
|
107
|
+
let response;
|
|
108
|
+
do {
|
|
109
|
+
pagesRequested += 1;
|
|
110
|
+
try {
|
|
111
|
+
response = await providerRequest(`${BASE}/sites/${encodeURIComponent(normalized.property_id)}/searchAnalytics/query`, {
|
|
112
|
+
token, fetchImpl, method: 'POST', body: requestBody(normalized, startRow),
|
|
113
|
+
});
|
|
114
|
+
if (!response || !Array.isArray(response.rows)) throw new Error('Search Console response rows must be an array');
|
|
115
|
+
} catch (error) {
|
|
116
|
+
errors.push(`start row ${startRow}: ${errorMessage(error)}`);
|
|
117
|
+
continuationState = { start_row: startRow };
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
pagesRetrieved += 1;
|
|
121
|
+
lastResponseHash = sha256(canonicalEvidenceJson(response));
|
|
122
|
+
if (Number.isInteger(response.rowCount) && response.rowCount >= 0) providerTotal = response.rowCount;
|
|
123
|
+
rows.push(...response.rows.map((row) => ({ row, pageStart: startRow, responseHash: lastResponseHash })));
|
|
124
|
+
const moreRows = providerTotal !== null && rows.length < providerTotal;
|
|
125
|
+
if (providerTotal !== null && !moreRows) break;
|
|
126
|
+
if (providerTotal === null && response.rows.length < normalized.row_limit) break;
|
|
127
|
+
startRow += normalized.row_limit;
|
|
128
|
+
if (pagesRequested >= maxPages) {
|
|
129
|
+
continuationState = { start_row: startRow };
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
} while (true);
|
|
133
|
+
|
|
134
|
+
const collection = collectionResult({
|
|
135
|
+
items: rows,
|
|
136
|
+
paginationState: { pages_requested: pagesRequested, pages_retrieved: pagesRetrieved, boundary: { max_pages: maxPages, row_limit: normalized.row_limit } },
|
|
137
|
+
providerReportedTotal: providerTotal,
|
|
138
|
+
continuationState,
|
|
139
|
+
providerCompleteness: providerTotal !== null ? 'declared' : 'unknown',
|
|
140
|
+
limitations: ['Search Console privacy filtering and aggregation apply.', 'A complete result is complete only for the declared API request; it is not a census of all search behavior.'],
|
|
141
|
+
errors,
|
|
142
|
+
});
|
|
143
|
+
const normalizedRows = rows.map(({ row, pageStart, responseHash }) => normalizeRow(row, normalized, dimensions, pageStart, collection.coverage_status, providerTotal, responseHash || sha256(canonicalEvidenceJson({ request: normalized, rows: [] })), evidenceOrigin));
|
|
144
|
+
observations.push(...normalizedRows);
|
|
145
|
+
return { observations, collection, request: normalized, provider: 'google_search_console', response_hash: lastResponseHash, errors };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function compareSearchDiscoveryObservations(left, right) {
|
|
149
|
+
if (!verifyExternalObservation(left).valid || !verifyExternalObservation(right).valid) return { status: 'INDETERMINATE', dimensions: {}, limitations: ['Observation integrity could not be verified.'] };
|
|
150
|
+
const dimensions = {
|
|
151
|
+
provider: left.source.provider === right.source.provider ? 'match' : 'mismatch',
|
|
152
|
+
property: left.source.account_or_property === right.source.account_or_property ? 'match' : 'mismatch',
|
|
153
|
+
query: left.data.query === right.data.query ? 'match' : 'mismatch',
|
|
154
|
+
page: left.data.page === right.data.page ? 'match' : 'mismatch',
|
|
155
|
+
configuration: left.configuration?.id === right.configuration?.id ? 'match' : 'mismatch',
|
|
156
|
+
interval: JSON.stringify(left.data.interval) === JSON.stringify(right.data.interval) ? 'match' : 'mismatch',
|
|
157
|
+
coverage: left.data.coverage_status === right.data.coverage_status ? 'match' : 'mismatch',
|
|
158
|
+
};
|
|
159
|
+
const hardMismatch = ['provider', 'property', 'query', 'page', 'configuration'].some((key) => dimensions[key] === 'mismatch');
|
|
160
|
+
const incomplete = [left.data.coverage_status, right.data.coverage_status].some((status) => !['complete'].includes(status));
|
|
161
|
+
const status = hardMismatch ? 'NOT_COMPARABLE' : incomplete ? 'INDETERMINATE' : dimensions.interval === 'mismatch' ? 'PARTIALLY_COMPARABLE' : 'COMPARABLE';
|
|
162
|
+
return {
|
|
163
|
+
comparison_id: `GSC-CMP-${sha256(canonicalEvidenceJson({ left: left.observation_id, right: right.observation_id, dimensions })).slice(0, 16)}`,
|
|
164
|
+
left_reference: left.observation_id,
|
|
165
|
+
right_reference: right.observation_id,
|
|
166
|
+
status,
|
|
167
|
+
dimensions,
|
|
168
|
+
limitations: status === 'COMPARABLE' ? [] : ['Search Console interval, configuration, and coverage limitations remain attached; no business opportunity or ranking conclusion is inferred.'],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function validSearchObservations(observations) {
|
|
173
|
+
return (Array.isArray(observations) ? observations : []).filter((item) => verifyExternalObservation(item).valid && item.source?.provider === 'google_search_console');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function pagesForQuery(observations, query) {
|
|
177
|
+
return validSearchObservations(observations).filter((item) => item.data.query === query && item.data.page).map((item) => item);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function queriesForPage(observations, page) {
|
|
181
|
+
return validSearchObservations(observations).filter((item) => item.data.page === page && item.data.query).map((item) => item);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function relationshipObservations(observations, { query = null, page = null } = {}) {
|
|
185
|
+
return validSearchObservations(observations).filter((item) => (query == null || item.data.query === query) && (page == null || item.data.page === page));
|
|
186
|
+
}
|