@nebulacomponents/citable 1.18.2 → 1.20.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 +64 -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 +19 -13
- 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/browser-evidence-plan.schema.json +186 -3
- package/dist/universal/.agents/skills/citable/schemas/browser-network-events.schema.json +142 -0
- package/dist/universal/.agents/skills/citable/schemas/browser-runtime-events.schema.json +112 -0
- package/dist/universal/.agents/skills/citable/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/dist/universal/.agents/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.agents/skills/citable/schemas/run.schema.json +15 -2
- 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 +19 -13
- 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/browser-evidence-plan.schema.json +186 -3
- package/dist/universal/.claude/skills/citable/schemas/browser-network-events.schema.json +142 -0
- package/dist/universal/.claude/skills/citable/schemas/browser-runtime-events.schema.json +112 -0
- package/dist/universal/.claude/skills/citable/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/dist/universal/.claude/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.claude/skills/citable/schemas/run.schema.json +15 -2
- 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 +19 -13
- 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/browser-evidence-plan.schema.json +186 -3
- package/dist/universal/.cursor/skills/citable/schemas/browser-network-events.schema.json +142 -0
- package/dist/universal/.cursor/skills/citable/schemas/browser-runtime-events.schema.json +112 -0
- package/dist/universal/.cursor/skills/citable/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/dist/universal/.cursor/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.cursor/skills/citable/schemas/run.schema.json +15 -2
- 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 +19 -13
- 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/browser-evidence-plan.schema.json +186 -3
- package/dist/universal/.gemini/skills/citable/schemas/browser-network-events.schema.json +142 -0
- package/dist/universal/.gemini/skills/citable/schemas/browser-runtime-events.schema.json +112 -0
- package/dist/universal/.gemini/skills/citable/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/dist/universal/.gemini/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.gemini/skills/citable/schemas/run.schema.json +15 -2
- 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 +19 -13
- 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/browser-evidence-plan.schema.json +186 -3
- package/dist/universal/.github/skills/citable/schemas/browser-network-events.schema.json +142 -0
- package/dist/universal/.github/skills/citable/schemas/browser-runtime-events.schema.json +112 -0
- package/dist/universal/.github/skills/citable/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/dist/universal/.github/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.github/skills/citable/schemas/run.schema.json +15 -2
- 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 +19 -13
- 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/browser-evidence-plan.schema.json +186 -3
- package/dist/universal/.kiro/skills/citable/schemas/browser-network-events.schema.json +142 -0
- package/dist/universal/.kiro/skills/citable/schemas/browser-runtime-events.schema.json +112 -0
- package/dist/universal/.kiro/skills/citable/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/dist/universal/.kiro/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.kiro/skills/citable/schemas/run.schema.json +15 -2
- 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 +19 -13
- 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/browser-evidence-plan.schema.json +186 -3
- package/dist/universal/.opencode/skills/citable/schemas/browser-network-events.schema.json +142 -0
- package/dist/universal/.opencode/skills/citable/schemas/browser-runtime-events.schema.json +112 -0
- package/dist/universal/.opencode/skills/citable/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/dist/universal/.opencode/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.opencode/skills/citable/schemas/run.schema.json +15 -2
- 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 +19 -13
- 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/browser-evidence-plan.schema.json +186 -3
- package/dist/universal/.pi/agent/skills/citable/schemas/browser-network-events.schema.json +142 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/browser-runtime-events.schema.json +112 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/dist/universal/.pi/agent/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/run.schema.json +15 -2
- 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 +19 -13
- 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/browser-evidence-plan.schema.json +186 -3
- package/dist/universal/.qoder/skills/citable/schemas/browser-network-events.schema.json +142 -0
- package/dist/universal/.qoder/skills/citable/schemas/browser-runtime-events.schema.json +112 -0
- package/dist/universal/.qoder/skills/citable/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/dist/universal/.qoder/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.qoder/skills/citable/schemas/run.schema.json +15 -2
- 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 +19 -13
- 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/browser-evidence-plan.schema.json +186 -3
- package/dist/universal/.rovodev/skills/citable/schemas/browser-network-events.schema.json +142 -0
- package/dist/universal/.rovodev/skills/citable/schemas/browser-runtime-events.schema.json +112 -0
- package/dist/universal/.rovodev/skills/citable/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/dist/universal/.rovodev/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.rovodev/skills/citable/schemas/run.schema.json +15 -2
- 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 +19 -13
- 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/browser-evidence-plan.schema.json +186 -3
- package/dist/universal/.trae/skills/citable/schemas/browser-network-events.schema.json +142 -0
- package/dist/universal/.trae/skills/citable/schemas/browser-runtime-events.schema.json +112 -0
- package/dist/universal/.trae/skills/citable/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/dist/universal/.trae/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.trae/skills/citable/schemas/run.schema.json +15 -2
- 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 +19 -13
- 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/browser-evidence-plan.schema.json +186 -3
- package/dist/universal/.trae-cn/skills/citable/schemas/browser-network-events.schema.json +142 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/browser-runtime-events.schema.json +112 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/dist/universal/.trae-cn/skills/citable/schemas/remediation-verification.schema.json +12 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/run.schema.json +15 -2
- 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 +232 -160
- package/package.json +1 -1
- package/schemas/action-plan.schema.json +38 -1
- package/schemas/audit-coverage.schema.json +118 -0
- package/schemas/browser-evidence-plan.schema.json +186 -3
- package/schemas/browser-network-events.schema.json +142 -0
- package/schemas/browser-runtime-events.schema.json +112 -0
- package/schemas/browser-state-observations.schema.json +97 -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/finding.schema.json +105 -2
- package/schemas/remediation-verification.schema.json +12 -1
- package/schemas/run.schema.json +15 -2
- 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 +41 -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/evidence/coverage.js +448 -0
- package/src/evidence/determination.js +157 -0
- package/src/evidence/downstream.js +110 -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/observations/browser/correlation.js +83 -0
- package/src/observations/browser/networkCollector.js +413 -0
- package/src/observations/browser/runtimeEventCollector.js +337 -0
- package/src/observations/browser/sanitizer.js +322 -0
- package/src/observations/browser/stateCollector.js +386 -0
- package/src/observations/browserJourney.js +131 -3
- package/src/reporting/executiveCroReport.js +37 -14
- package/src/reporting/executiveExport.js +45 -16
- package/src/reporting/executiveSearchReport.js +57 -23
- package/src/reporting/report.js +56 -9
- 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/sow/generateSow.js +1 -1
package/src/commands/connect.js
CHANGED
|
@@ -79,16 +79,37 @@ export async function syncConnection(root, { connectionId, startDate, endDate, a
|
|
|
79
79
|
}
|
|
80
80
|
throw error;
|
|
81
81
|
}
|
|
82
|
+
// Connector collection may preserve a partial result even when the provider
|
|
83
|
+
// reports an authorization/quota state. Keep the result available to direct
|
|
84
|
+
// connector callers, while retaining the command's legacy state transition.
|
|
85
|
+
if (result.connectorError?.connectorState) {
|
|
86
|
+
const error = result.connectorError;
|
|
87
|
+
const failed = { ...loaded.registries.connections, entries: loaded.registries.connections.entries.map((item) => item.connection_id === connectionId ? { ...item, state: error.connectorState, limitations: [...(result.collection?.errors || []), 'Connector synchronization did not complete; inspect collection errors.'] } : item) };
|
|
88
|
+
saveRegistry(root, 'connections', failed);
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
const collection = result.collection || {
|
|
92
|
+
schema_version: 1,
|
|
93
|
+
items: result.rows,
|
|
94
|
+
pagination_state: { pages_requested: 1, pages_retrieved: 1 },
|
|
95
|
+
provider_reported_total: null,
|
|
96
|
+
retrieved_total: result.rows.length,
|
|
97
|
+
coverage_status: 'indeterminate',
|
|
98
|
+
continuation_state: null,
|
|
99
|
+
limitations: ['Connector did not provide a collection contract.'],
|
|
100
|
+
errors: [],
|
|
101
|
+
};
|
|
102
|
+
const collectionLimitations = [...collection.limitations, ...collection.errors];
|
|
82
103
|
const observations = result.rows.map((row) => envelope('metric', {
|
|
83
104
|
metric_id: row.metric.metric_id, provider: connector.provider, external_name: row.metric.external_name,
|
|
84
105
|
value: Number(row.value), unit: row.metric.unit === 'custom' ? row.metric.custom_unit : row.metric.unit,
|
|
85
106
|
aggregation: row.metric.aggregation, observed_at: row.observed_at, period_start: row.observed_at,
|
|
86
107
|
period_end: row.observed_at, dimensions: row.dimensions,
|
|
87
|
-
}, { method: 'live_api', source: connector.provider, limitations: [...row.metric.limitations, ...
|
|
108
|
+
}, { method: 'live_api', source: connector.provider, limitations: [...row.metric.limitations, ...collectionLimitations] }));
|
|
88
109
|
const run = observationRun(root, 'connect sync', loaded.connection.property_id, observations);
|
|
89
|
-
const updated = { ...loaded.registries.connections, entries: loaded.registries.connections.entries.map((item) => item.connection_id === connectionId ? { ...item, state: 'synchronized', last_synchronized_at: new Date().toISOString(), cursor: result.cursor, limitations:
|
|
110
|
+
const updated = { ...loaded.registries.connections, entries: loaded.registries.connections.entries.map((item) => item.connection_id === connectionId ? { ...item, state: ['truncated', 'indeterminate'].includes(collection.coverage_status) ? 'partial' : 'synchronized', last_synchronized_at: new Date().toISOString(), cursor: result.cursor, limitations: collectionLimitations } : item) };
|
|
90
111
|
saveRegistry(root, 'connections', updated);
|
|
91
|
-
return { ...run, connection_id: connectionId, provider: connector.provider };
|
|
112
|
+
return { ...run, connection_id: connectionId, provider: connector.provider, collection };
|
|
92
113
|
}
|
|
93
114
|
|
|
94
115
|
export async function readCmsContent(root, { connectionId, targetId, accessToken, env, fetchImpl } = {}) {
|
|
@@ -119,4 +140,3 @@ export async function applyCmsRemediation(root, { connectionId, input, write = f
|
|
|
119
140
|
|
|
120
141
|
export { submitIndexNow, verifyHostKey, buildIndexNowPayload, normalizeHost, parseUrlsInput } from '../connectors/indexnow.js';
|
|
121
142
|
export { collectMcpEvidence } from '../connectors/mcp/pilot.js';
|
|
122
|
-
|
package/src/commands/context.js
CHANGED
|
@@ -4,8 +4,44 @@ import { readYaml, readJson, sha256, parseRefDate } from '../shared/io.js';
|
|
|
4
4
|
import { loadRegistries } from '../registries/index.js';
|
|
5
5
|
import { buildSiteFromDir, buildSiteFromUrl } from '../extractor/site.js';
|
|
6
6
|
|
|
7
|
+
const AUDIT_BUDGET_DEFAULTS = Object.freeze({ maxPages: 500, timeBudgetSeconds: 1800 });
|
|
8
|
+
|
|
9
|
+
function validateBudget(value, name, maximum) {
|
|
10
|
+
if (!Number.isInteger(value) || value < 1 || value > maximum) {
|
|
11
|
+
throw new Error(`${name} must be an integer from 1 to ${maximum}; received ${String(value)}`);
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Resolve bounded network collection budgets: command options > config > safe defaults. */
|
|
17
|
+
export function resolveAuditBudgets(config = {}, options = {}) {
|
|
18
|
+
const configured = config.audit ?? {};
|
|
19
|
+
const maxPagesFromOptions = options.maxPages !== undefined;
|
|
20
|
+
const timeBudgetFromOptions = options.timeBudgetSeconds !== undefined;
|
|
21
|
+
const maxPages = maxPagesFromOptions
|
|
22
|
+
? options.maxPages
|
|
23
|
+
: configured.max_pages !== undefined ? configured.max_pages : AUDIT_BUDGET_DEFAULTS.maxPages;
|
|
24
|
+
const timeBudgetSeconds = timeBudgetFromOptions
|
|
25
|
+
? options.timeBudgetSeconds
|
|
26
|
+
: configured.time_budget_seconds !== undefined
|
|
27
|
+
? configured.time_budget_seconds
|
|
28
|
+
: AUDIT_BUDGET_DEFAULTS.timeBudgetSeconds;
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
maxPages: validateBudget(maxPages, maxPagesFromOptions ? '--max-pages' : 'audit.max_pages', 10_000),
|
|
32
|
+
timeBudgetSeconds: validateBudget(
|
|
33
|
+
timeBudgetSeconds,
|
|
34
|
+
timeBudgetFromOptions ? '--time-budget-seconds' : 'audit.time_budget_seconds',
|
|
35
|
+
86_400,
|
|
36
|
+
),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
7
40
|
/** Assemble the shared execution context used by audit-family commands. */
|
|
8
|
-
export async function buildContext(root, {
|
|
41
|
+
export async function buildContext(root, {
|
|
42
|
+
target, baseUrl, refDate, viewport = null,
|
|
43
|
+
maxPages, timeBudgetSeconds, fetcher, concurrency = 1,
|
|
44
|
+
} = {}) {
|
|
9
45
|
const warnings = [];
|
|
10
46
|
const configFile = path.join(root, '.citable', 'config.yaml');
|
|
11
47
|
const config = fs.existsSync(configFile) ? readYaml(configFile) : { version: 1 };
|
|
@@ -13,10 +49,12 @@ export async function buildContext(root, { target, baseUrl, refDate, viewport =
|
|
|
13
49
|
warnings.push(...problems);
|
|
14
50
|
|
|
15
51
|
let site = null;
|
|
52
|
+
let auditBudgets = null;
|
|
16
53
|
const resolvedBase = baseUrl ?? config.site?.base_url ?? 'https://example.test';
|
|
17
54
|
if (target) {
|
|
18
55
|
if (/^https?:\/\//.test(target)) {
|
|
19
|
-
|
|
56
|
+
auditBudgets = resolveAuditBudgets(config, { maxPages, timeBudgetSeconds });
|
|
57
|
+
site = await buildSiteFromUrl(target, { ...auditBudgets, fetcher, concurrency });
|
|
20
58
|
} else if (fs.existsSync(target)) {
|
|
21
59
|
site = buildSiteFromDir(target, { baseUrl: resolvedBase });
|
|
22
60
|
} else {
|
|
@@ -70,6 +108,7 @@ export async function buildContext(root, { target, baseUrl, refDate, viewport =
|
|
|
70
108
|
promptResults: promptResults.length ? promptResults : null,
|
|
71
109
|
observations: observations.length ? observations : null,
|
|
72
110
|
snapshots,
|
|
111
|
+
auditBudgets,
|
|
73
112
|
refDate: parseRefDate(refDate),
|
|
74
113
|
viewport,
|
|
75
114
|
hashPage: (p) => sha256(p.text),
|
package/src/commands/croSuite.js
CHANGED
|
@@ -9,6 +9,7 @@ import { analyzeBehavioralTelemetry } from '../analysis/behavioral.js';
|
|
|
9
9
|
import { generateExperimentBacklog, formatBacklogMarkdown } from './croBacklog.js';
|
|
10
10
|
import { buildIceMatrix, formatIceMatrixOutput } from '../analysis/iceMatrix.js';
|
|
11
11
|
import { buildCroRoadmap, formatCroRoadmapMarkdown } from '../analysis/croRoadmap.js';
|
|
12
|
+
import { downstreamEnvelope, completeLocalCoverage } from '../evidence/downstream.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Full End-to-End CRO Intelligence Suite Audit
|
|
@@ -22,6 +23,8 @@ export async function auditCroSuite(root, { target, baseUrl, refDate, input, fun
|
|
|
22
23
|
const { findings } = runDetectors(croDetectors, ctx);
|
|
23
24
|
|
|
24
25
|
const pages = indexTargets(ctx);
|
|
26
|
+
const coverage = ctx.coverage || (pages.length > 0 ? completeLocalCoverage(pages, ctx.site.baseUrl) : null);
|
|
27
|
+
const evidence = downstreamEnvelope(coverage);
|
|
25
28
|
|
|
26
29
|
// 2. Above-the-fold clarity, trust, cognitive load, offer architecture per page
|
|
27
30
|
const auditedPages = pages.map((page) => auditPageCro(page, ctx));
|
|
@@ -33,21 +36,29 @@ export async function auditCroSuite(root, { target, baseUrl, refDate, input, fun
|
|
|
33
36
|
});
|
|
34
37
|
const targetGroup = commercialPages.length > 0 ? commercialPages : auditedPages;
|
|
35
38
|
|
|
36
|
-
const avgAtfClarity = targetGroup.length > 0
|
|
39
|
+
const avgAtfClarity = targetGroup.length > 0 && evidence.determination_status !== 'indeterminate'
|
|
37
40
|
? Math.round(targetGroup.reduce((acc, p) => acc + p.atf_clarity.score, 0) / targetGroup.length)
|
|
38
|
-
:
|
|
41
|
+
: null;
|
|
39
42
|
|
|
40
|
-
const avgTrustScore = targetGroup.length > 0
|
|
43
|
+
const avgTrustScore = targetGroup.length > 0 && evidence.determination_status !== 'indeterminate'
|
|
41
44
|
? Math.round(targetGroup.reduce((acc, p) => acc + p.trust_and_credibility.score, 0) / targetGroup.length)
|
|
42
|
-
:
|
|
45
|
+
: null;
|
|
43
46
|
|
|
44
|
-
const avgConversionReadiness = targetGroup.length > 0
|
|
47
|
+
const avgConversionReadiness = targetGroup.length > 0 && evidence.determination_status !== 'indeterminate'
|
|
45
48
|
? Math.round(targetGroup.reduce((acc, p) => acc + p.conversion_readiness_score, 0) / targetGroup.length)
|
|
46
|
-
:
|
|
49
|
+
: null;
|
|
47
50
|
|
|
48
51
|
// 3. End-to-end Funnel and Conversion-Path Analysis
|
|
49
52
|
const declaredFunnel = (ctx.registries?.funnels?.entries || []).find((f) => funnelId ? f.funnel_id === funnelId : true) || null;
|
|
50
53
|
const funnelAnalysis = analyzeConversionFunnel(ctx.site.pages, declaredFunnel);
|
|
54
|
+
const qualify = (value) => evidence.determination_status === 'supported' ? value : null;
|
|
55
|
+
const qualifiedFunnel = {
|
|
56
|
+
...funnelAnalysis,
|
|
57
|
+
funnel_health_score: qualify(funnelAnalysis.funnel_health_score),
|
|
58
|
+
determination_status: evidence.determination_status,
|
|
59
|
+
coverage_status: evidence.coverage_status,
|
|
60
|
+
limitations: evidence.limitations,
|
|
61
|
+
};
|
|
51
62
|
|
|
52
63
|
// 4. Behavioral Evidence Analysis (if telemetry file provided)
|
|
53
64
|
let behavioralAnalysis = null;
|
|
@@ -65,9 +76,26 @@ export async function auditCroSuite(root, { target, baseUrl, refDate, input, fun
|
|
|
65
76
|
|
|
66
77
|
// 5. Experiment Backlog with Falsifiable Hypotheses
|
|
67
78
|
const experimentBacklog = generateExperimentBacklog(findings);
|
|
79
|
+
const qualifiedExperimentBacklog = {
|
|
80
|
+
...experimentBacklog,
|
|
81
|
+
total_experiments: qualify(experimentBacklog.total_experiments),
|
|
82
|
+
determination_status: evidence.determination_status,
|
|
83
|
+
coverage_status: evidence.coverage_status,
|
|
84
|
+
limitations: evidence.limitations,
|
|
85
|
+
};
|
|
68
86
|
|
|
69
87
|
// 6. Impact / Effort / Confidence (ICE) Prioritization Matrix
|
|
70
88
|
const iceMatrix = buildIceMatrix(findings, { type: 'findings' });
|
|
89
|
+
const qualifiedIceMatrix = {
|
|
90
|
+
...iceMatrix,
|
|
91
|
+
determination_status: evidence.determination_status,
|
|
92
|
+
coverage_status: evidence.coverage_status,
|
|
93
|
+
limitations: evidence.limitations,
|
|
94
|
+
summary: {
|
|
95
|
+
...iceMatrix.summary,
|
|
96
|
+
quick_wins_count: qualify(iceMatrix.summary.quick_wins_count),
|
|
97
|
+
},
|
|
98
|
+
};
|
|
71
99
|
|
|
72
100
|
// 7. 30 / 90 / 180-Day CRO Strategic Roadmap tied to Measurable Conversion Outcomes
|
|
73
101
|
const targetDomain = ctx.site.baseUrl ? new URL(ctx.site.baseUrl).hostname : 'target-site';
|
|
@@ -75,26 +103,36 @@ export async function auditCroSuite(root, { target, baseUrl, refDate, input, fun
|
|
|
75
103
|
findings,
|
|
76
104
|
targetDomain,
|
|
77
105
|
});
|
|
106
|
+
const qualifiedRoadmap = {
|
|
107
|
+
...croRoadmap,
|
|
108
|
+
determination_status: evidence.determination_status,
|
|
109
|
+
coverage_status: evidence.coverage_status,
|
|
110
|
+
limitations: evidence.limitations,
|
|
111
|
+
};
|
|
78
112
|
|
|
79
113
|
const result = {
|
|
80
114
|
fact_status: 'modeled_cro_evaluation',
|
|
81
115
|
target: ctx.site.baseUrl || target,
|
|
82
116
|
total_pages_audited: pages.length,
|
|
117
|
+
coverage_status: evidence.coverage_status,
|
|
118
|
+
determination_status: evidence.determination_status,
|
|
119
|
+
evidence_scope: evidence.evidence_scope,
|
|
120
|
+
limitations: evidence.limitations,
|
|
83
121
|
summary: {
|
|
84
122
|
conversion_readiness_score: avgConversionReadiness,
|
|
85
123
|
atf_clarity_score: avgAtfClarity,
|
|
86
124
|
trust_credibility_score: avgTrustScore,
|
|
87
|
-
funnel_health_score:
|
|
125
|
+
funnel_health_score: qualifiedFunnel.funnel_health_score,
|
|
88
126
|
total_cro_findings: findings.length,
|
|
89
|
-
planned_experiments:
|
|
90
|
-
quick_wins_count:
|
|
127
|
+
planned_experiments: qualifiedExperimentBacklog.total_experiments,
|
|
128
|
+
quick_wins_count: qualifiedIceMatrix.summary.quick_wins_count,
|
|
91
129
|
},
|
|
92
|
-
funnel_analysis:
|
|
130
|
+
funnel_analysis: qualifiedFunnel,
|
|
93
131
|
pages: auditedPages,
|
|
94
132
|
behavioral_telemetry: behavioralAnalysis,
|
|
95
|
-
experiment_backlog:
|
|
96
|
-
ice_matrix:
|
|
97
|
-
strategic_roadmap:
|
|
133
|
+
experiment_backlog: qualifiedExperimentBacklog,
|
|
134
|
+
ice_matrix: qualifiedIceMatrix,
|
|
135
|
+
strategic_roadmap: qualifiedRoadmap,
|
|
98
136
|
findings: findings.map((f) => ({
|
|
99
137
|
detector_id: f.detector_id,
|
|
100
138
|
severity: f.classification.severity,
|
package/src/commands/delivery.js
CHANGED
|
@@ -6,6 +6,7 @@ import { compareSnapshots } from './compareSnapshots.js';
|
|
|
6
6
|
import { buildAlertPayload, dispatchAlertWebhook, filterAlerts } from '../monitoring/alertDelivery.js';
|
|
7
7
|
import { loadRegistries } from '../registries/index.js';
|
|
8
8
|
import { readJson, sha256, writeJson, nowIso } from '../shared/io.js';
|
|
9
|
+
import { loadVerifiedRun } from '../shared/verifiedRunLoader.js';
|
|
9
10
|
|
|
10
11
|
const PKG = readJson(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../package.json'));
|
|
11
12
|
|
|
@@ -114,12 +115,11 @@ function annotationLevel(severity) {
|
|
|
114
115
|
export function projectGithub(root, { runId } = {}) {
|
|
115
116
|
if (!runId) throw new Error('projection requires a source run id');
|
|
116
117
|
const runDir = path.join(root, '.citable', 'runs', runId);
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
const annotations = JSON.parse(findingsBytes).map((finding) => ({
|
|
118
|
+
const loaded = loadVerifiedRun(runDir, { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false });
|
|
119
|
+
const findingsHash = loaded.artifactHashes?.['findings.json'];
|
|
120
|
+
const manifestHash = loaded.artifactHashes?.['manifest.json'];
|
|
121
|
+
if (!findingsHash || !manifestHash) throw new Error(`run ${runId} has no loader-verified findings or manifest hash`);
|
|
122
|
+
const annotations = loaded.findings.map((finding) => ({
|
|
123
123
|
level: annotationLevel(finding.classification.severity),
|
|
124
124
|
title: `${finding.detector_id}: ${finding.classification.severity}`,
|
|
125
125
|
message: finding.observation.summary,
|
|
@@ -127,7 +127,7 @@ export function projectGithub(root, { runId } = {}) {
|
|
|
127
127
|
line: finding.subject.source_location ? Number(String(finding.subject.source_location).match(/\d+/)?.[0]) || null : null,
|
|
128
128
|
finding_id: finding.finding_id,
|
|
129
129
|
}));
|
|
130
|
-
const projection = { source_run_id: runId, source_manifest_hash:
|
|
130
|
+
const projection = { source_run_id: runId, source_manifest_hash: manifestHash, source_findings_hash: findingsHash, generated_at: nowIso(), projection: 'github-check-annotations', authoritative: false, annotations };
|
|
131
131
|
const dir = path.join(root, '.citable', 'projections', 'github', runId);
|
|
132
132
|
writeJson(path.join(dir, 'annotations.json'), projection);
|
|
133
133
|
return { ...projection, dir };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
1
|
import path from 'node:path';
|
|
2
|
+
import { loadVerifiedRun } from '../shared/verifiedRunLoader.js';
|
|
3
3
|
import { createRun } from '../evidence/run.js';
|
|
4
4
|
import { loadRegistries, checkReferentialIntegrity } from '../registries/index.js';
|
|
5
|
-
import {
|
|
5
|
+
import { sha256, parseRefDate } from '../shared/io.js';
|
|
6
6
|
|
|
7
7
|
function recordHash(value) {
|
|
8
8
|
return sha256(JSON.stringify(value));
|
|
@@ -79,8 +79,8 @@ export function validateGovernance(root, { refDate } = {}) {
|
|
|
79
79
|
const evidence = new Map(registries.evidence.entries.map((item) => [item.evidence_id, item]));
|
|
80
80
|
const activeByFinding = new Map();
|
|
81
81
|
for (const exception of registries.exceptions.entries) {
|
|
82
|
-
const
|
|
83
|
-
const findings = new Map(
|
|
82
|
+
const runDir = path.join(root, '.citable', 'runs', exception.source_run_id);
|
|
83
|
+
const findings = new Map(loadVerifiedRun(runDir, { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false }).findings.map((item) => [item.finding_id, item]));
|
|
84
84
|
const exceptionIssues = exceptionProblems(exception, policies.get(exception.policy_id), reviewers, findings, evidence, reference);
|
|
85
85
|
problems.push(...exceptionIssues.map((problem) => `exceptions/${exception.exception_id}: ${problem}`));
|
|
86
86
|
if (exceptionIssues.length === 0) {
|
|
@@ -98,10 +98,9 @@ export function validateGovernance(root, { refDate } = {}) {
|
|
|
98
98
|
|
|
99
99
|
export function evaluateDispositions(root, { runId, refDate } = {}) {
|
|
100
100
|
if (!runId) throw new Error('source run id is required');
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
const sourceFindings = JSON.parse(sourceBytes);
|
|
101
|
+
const runDir = path.join(root, '.citable', 'runs', runId);
|
|
102
|
+
const sourceRun = loadVerifiedRun(runDir, { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false });
|
|
103
|
+
const sourceFindings = sourceRun.findings;
|
|
105
104
|
const findings = new Map(sourceFindings.map((item) => [item.finding_id, item]));
|
|
106
105
|
const { registries, problems } = loadRegistries(root);
|
|
107
106
|
const integrity = checkReferentialIntegrity(registries);
|
|
@@ -130,8 +129,8 @@ export function evaluateDispositions(root, { runId, refDate } = {}) {
|
|
|
130
129
|
problems: matches.flatMap((item) => item.problems),
|
|
131
130
|
};
|
|
132
131
|
});
|
|
133
|
-
const run = createRun(root, { command: 'governance evaluate', argv: [runId], target: { kind: 'registries', location:
|
|
134
|
-
run.
|
|
132
|
+
const run = createRun(root, { command: 'governance evaluate', argv: [runId], target: { kind: 'registries', location: path.join(runDir, 'findings.json') } });
|
|
133
|
+
run.manifest.input_hashes['source-findings'] = sourceRun.artifactHashes['findings.json'];
|
|
135
134
|
run.addInput('exceptions', registries.exceptions);
|
|
136
135
|
run.addInput('review-policies', registries.review_policies);
|
|
137
136
|
run.writeArtifact('dispositions.json', dispositions);
|
|
@@ -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 { readJson, nowIso } from '../shared/io.js';
|
|
4
5
|
import { remediateCommand } from './remediate.js';
|
|
5
6
|
|
|
@@ -76,7 +77,7 @@ export async function exportImplementationKit(root, options = {}) {
|
|
|
76
77
|
if (!runId || !fs.existsSync(path.join(runDir, 'findings.json'))) {
|
|
77
78
|
throw new Error(`source run not found: .citable/runs/${runId || '(none)'}`);
|
|
78
79
|
}
|
|
79
|
-
const findings =
|
|
80
|
+
const findings = loadVerifiedRun(runDir, { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false }).findings;
|
|
80
81
|
const matches = findings.filter((f) =>
|
|
81
82
|
(f.detector_id || '').toUpperCase() === String(finding || '').toUpperCase()
|
|
82
83
|
&& (!subject || (f.subject?.identifier || f.subject?.url) === subject));
|
package/src/commands/init.js
CHANGED
|
@@ -89,7 +89,14 @@ export function init(root, { force = false, seed = null } = {}) {
|
|
|
89
89
|
package_manager: detected.package_manager,
|
|
90
90
|
build_command: detected.build_commands[0] ?? null, test_command: detected.test_commands[0] ?? null,
|
|
91
91
|
},
|
|
92
|
-
audit: {
|
|
92
|
+
audit: {
|
|
93
|
+
index_target_default: true,
|
|
94
|
+
max_pages: 500,
|
|
95
|
+
time_budget_seconds: 1800,
|
|
96
|
+
max_crawl_depth: 4,
|
|
97
|
+
thin_content_words: 120,
|
|
98
|
+
fetch_external: false,
|
|
99
|
+
},
|
|
93
100
|
};
|
|
94
101
|
const { valid, errors } = validateAgainst('config.schema.json', config);
|
|
95
102
|
if (!valid) throw new Error(`generated config invalid: ${errors.join('; ')}`);
|
|
@@ -167,7 +167,7 @@ export function generateCroPreviewHtml({
|
|
|
167
167
|
</div>
|
|
168
168
|
|
|
169
169
|
<div class="findings-list">
|
|
170
|
-
<h3>
|
|
170
|
+
<h3>Observed CRO Findings (${findingItems.length})</h3>
|
|
171
171
|
${findingItems.map((f) => `
|
|
172
172
|
<div class="finding-row">
|
|
173
173
|
<span><strong>${f.detector_id}</strong>: ${f.summary}</span>
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import fs from 'node:fs';
|
|
15
15
|
import path from 'node:path';
|
|
16
|
+
import { loadVerifiedRun } from '../shared/verifiedRunLoader.js';
|
|
16
17
|
import { contextDir, loadRegistryFile, registryLoadProblems } from '../registries/index.js';
|
|
17
18
|
import { validateAgainst } from '../shared/schemaValidator.js';
|
|
18
19
|
import { buildIceMatrix, formatIceMatrixOutput } from '../analysis/iceMatrix.js';
|
|
@@ -96,7 +97,7 @@ function initiativeMatrix(file, root, rest = []) {
|
|
|
96
97
|
if (runId) {
|
|
97
98
|
const findingsPath = path.join(root, '.citable', 'runs', runId, 'findings.json');
|
|
98
99
|
if (!fs.existsSync(findingsPath)) throw new Error(`run ${runId} findings.json not found`);
|
|
99
|
-
const findings =
|
|
100
|
+
const findings = loadVerifiedRun(path.dirname(findingsPath), { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false }).findings;
|
|
100
101
|
const matrix = buildIceMatrix(findings, { type: 'findings' });
|
|
101
102
|
return { ...matrix, formatted: formatIceMatrixOutput(matrix) };
|
|
102
103
|
}
|
|
@@ -106,7 +107,7 @@ function initiativeMatrix(file, root, rest = []) {
|
|
|
106
107
|
if (fs.existsSync(runsDir)) {
|
|
107
108
|
const runs = fs.readdirSync(runsDir).filter((r) => fs.existsSync(path.join(runsDir, r, 'findings.json'))).sort().reverse();
|
|
108
109
|
if (runs.length > 0 && rest.includes('--findings')) {
|
|
109
|
-
const findings =
|
|
110
|
+
const findings = loadVerifiedRun(path.join(runsDir, runs[0]), { requireCompletedExecution: false, allowLegacy: true, requireCoverage: false }).findings;
|
|
110
111
|
const matrix = buildIceMatrix(findings, { type: 'findings' });
|
|
111
112
|
return { ...matrix, formatted: formatIceMatrixOutput(matrix) };
|
|
112
113
|
}
|
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)`,
|