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