@nebulacomponents/citable 1.11.0 → 1.13.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 +151 -0
- package/README.md +29 -2
- package/dist/universal/.agents/skills/citable/SKILL.md +14 -1
- package/dist/universal/.agents/skills/citable/VERSION +1 -1
- package/dist/universal/.agents/skills/citable/commands/README.md +2 -1
- package/dist/universal/.agents/skills/citable/commands/artifact-interchange.md +28 -0
- package/dist/universal/.agents/skills/citable/commands/field-validation.md +71 -0
- package/dist/universal/.agents/skills/citable/commands/observe-and-act.md +13 -1
- package/dist/universal/.agents/skills/citable/manifest.json +30 -10
- package/dist/universal/.agents/skills/citable/references/aeo-geo-validation.md +1 -1
- package/dist/universal/.agents/skills/citable/references/capability-boundaries.md +3 -0
- package/dist/universal/.agents/skills/citable/schemas/acceptance-corpus.schema.json +168 -0
- package/dist/universal/.agents/skills/citable/schemas/acceptance-run-context.schema.json +17 -0
- package/dist/universal/.agents/skills/citable/schemas/acceptance-run-receipt.schema.json +88 -0
- package/dist/universal/.agents/skills/citable/schemas/artifact-interchange.schema.json +58 -0
- package/dist/universal/.agents/skills/citable/schemas/assumption.schema.json +119 -0
- package/dist/universal/.agents/skills/citable/schemas/browser-evidence-plan.schema.json +76 -0
- package/dist/universal/.agents/skills/citable/schemas/corpus-publication-receipt.schema.json +24 -0
- package/dist/universal/.agents/skills/citable/schemas/customer-outcome.schema.json +119 -0
- package/dist/universal/.agents/skills/citable/schemas/decision.schema.json +154 -0
- package/dist/universal/.agents/skills/citable/schemas/deployment-receipt.schema.json +51 -0
- package/dist/universal/.agents/skills/citable/schemas/field-validation-metrics.schema.json +15 -0
- package/dist/universal/.agents/skills/citable/schemas/initiative.schema.json +185 -0
- package/dist/universal/.agents/skills/citable/schemas/kpi.schema.json +108 -0
- package/dist/universal/.agents/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.agents/skills/citable/schemas/release-manifest.schema.json +74 -0
- package/dist/universal/.agents/skills/citable/schemas/release-state.schema.json +40 -0
- package/dist/universal/.agents/skills/citable/schemas/risk.schema.json +172 -0
- package/dist/universal/.agents/skills/citable/schemas/scenario.schema.json +165 -0
- package/dist/universal/.agents/skills/citable/schemas/variance.schema.json +112 -0
- package/dist/universal/.claude/skills/citable/SKILL.md +14 -1
- package/dist/universal/.claude/skills/citable/VERSION +1 -1
- package/dist/universal/.claude/skills/citable/commands/README.md +2 -1
- package/dist/universal/.claude/skills/citable/commands/artifact-interchange.md +28 -0
- package/dist/universal/.claude/skills/citable/commands/field-validation.md +71 -0
- package/dist/universal/.claude/skills/citable/commands/observe-and-act.md +13 -1
- package/dist/universal/.claude/skills/citable/manifest.json +30 -10
- package/dist/universal/.claude/skills/citable/references/aeo-geo-validation.md +1 -1
- package/dist/universal/.claude/skills/citable/references/capability-boundaries.md +3 -0
- package/dist/universal/.claude/skills/citable/schemas/acceptance-corpus.schema.json +168 -0
- package/dist/universal/.claude/skills/citable/schemas/acceptance-run-context.schema.json +17 -0
- package/dist/universal/.claude/skills/citable/schemas/acceptance-run-receipt.schema.json +88 -0
- package/dist/universal/.claude/skills/citable/schemas/artifact-interchange.schema.json +58 -0
- package/dist/universal/.claude/skills/citable/schemas/assumption.schema.json +119 -0
- package/dist/universal/.claude/skills/citable/schemas/browser-evidence-plan.schema.json +76 -0
- package/dist/universal/.claude/skills/citable/schemas/corpus-publication-receipt.schema.json +24 -0
- package/dist/universal/.claude/skills/citable/schemas/customer-outcome.schema.json +119 -0
- package/dist/universal/.claude/skills/citable/schemas/decision.schema.json +154 -0
- package/dist/universal/.claude/skills/citable/schemas/deployment-receipt.schema.json +51 -0
- package/dist/universal/.claude/skills/citable/schemas/field-validation-metrics.schema.json +15 -0
- package/dist/universal/.claude/skills/citable/schemas/initiative.schema.json +185 -0
- package/dist/universal/.claude/skills/citable/schemas/kpi.schema.json +108 -0
- package/dist/universal/.claude/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.claude/skills/citable/schemas/release-manifest.schema.json +74 -0
- package/dist/universal/.claude/skills/citable/schemas/release-state.schema.json +40 -0
- package/dist/universal/.claude/skills/citable/schemas/risk.schema.json +172 -0
- package/dist/universal/.claude/skills/citable/schemas/scenario.schema.json +165 -0
- package/dist/universal/.claude/skills/citable/schemas/variance.schema.json +112 -0
- package/dist/universal/.cursor/skills/citable/SKILL.md +14 -1
- package/dist/universal/.cursor/skills/citable/VERSION +1 -1
- package/dist/universal/.cursor/skills/citable/commands/README.md +2 -1
- package/dist/universal/.cursor/skills/citable/commands/artifact-interchange.md +28 -0
- package/dist/universal/.cursor/skills/citable/commands/field-validation.md +71 -0
- package/dist/universal/.cursor/skills/citable/commands/observe-and-act.md +13 -1
- package/dist/universal/.cursor/skills/citable/manifest.json +30 -10
- package/dist/universal/.cursor/skills/citable/references/aeo-geo-validation.md +1 -1
- package/dist/universal/.cursor/skills/citable/references/capability-boundaries.md +3 -0
- package/dist/universal/.cursor/skills/citable/schemas/acceptance-corpus.schema.json +168 -0
- package/dist/universal/.cursor/skills/citable/schemas/acceptance-run-context.schema.json +17 -0
- package/dist/universal/.cursor/skills/citable/schemas/acceptance-run-receipt.schema.json +88 -0
- package/dist/universal/.cursor/skills/citable/schemas/artifact-interchange.schema.json +58 -0
- package/dist/universal/.cursor/skills/citable/schemas/assumption.schema.json +119 -0
- package/dist/universal/.cursor/skills/citable/schemas/browser-evidence-plan.schema.json +76 -0
- package/dist/universal/.cursor/skills/citable/schemas/corpus-publication-receipt.schema.json +24 -0
- package/dist/universal/.cursor/skills/citable/schemas/customer-outcome.schema.json +119 -0
- package/dist/universal/.cursor/skills/citable/schemas/decision.schema.json +154 -0
- package/dist/universal/.cursor/skills/citable/schemas/deployment-receipt.schema.json +51 -0
- package/dist/universal/.cursor/skills/citable/schemas/field-validation-metrics.schema.json +15 -0
- package/dist/universal/.cursor/skills/citable/schemas/initiative.schema.json +185 -0
- package/dist/universal/.cursor/skills/citable/schemas/kpi.schema.json +108 -0
- package/dist/universal/.cursor/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.cursor/skills/citable/schemas/release-manifest.schema.json +74 -0
- package/dist/universal/.cursor/skills/citable/schemas/release-state.schema.json +40 -0
- package/dist/universal/.cursor/skills/citable/schemas/risk.schema.json +172 -0
- package/dist/universal/.cursor/skills/citable/schemas/scenario.schema.json +165 -0
- package/dist/universal/.cursor/skills/citable/schemas/variance.schema.json +112 -0
- package/dist/universal/.gemini/skills/citable/SKILL.md +14 -1
- package/dist/universal/.gemini/skills/citable/VERSION +1 -1
- package/dist/universal/.gemini/skills/citable/commands/README.md +2 -1
- package/dist/universal/.gemini/skills/citable/commands/artifact-interchange.md +28 -0
- package/dist/universal/.gemini/skills/citable/commands/field-validation.md +71 -0
- package/dist/universal/.gemini/skills/citable/commands/observe-and-act.md +13 -1
- package/dist/universal/.gemini/skills/citable/manifest.json +30 -10
- package/dist/universal/.gemini/skills/citable/references/aeo-geo-validation.md +1 -1
- package/dist/universal/.gemini/skills/citable/references/capability-boundaries.md +3 -0
- package/dist/universal/.gemini/skills/citable/schemas/acceptance-corpus.schema.json +168 -0
- package/dist/universal/.gemini/skills/citable/schemas/acceptance-run-context.schema.json +17 -0
- package/dist/universal/.gemini/skills/citable/schemas/acceptance-run-receipt.schema.json +88 -0
- package/dist/universal/.gemini/skills/citable/schemas/artifact-interchange.schema.json +58 -0
- package/dist/universal/.gemini/skills/citable/schemas/assumption.schema.json +119 -0
- package/dist/universal/.gemini/skills/citable/schemas/browser-evidence-plan.schema.json +76 -0
- package/dist/universal/.gemini/skills/citable/schemas/corpus-publication-receipt.schema.json +24 -0
- package/dist/universal/.gemini/skills/citable/schemas/customer-outcome.schema.json +119 -0
- package/dist/universal/.gemini/skills/citable/schemas/decision.schema.json +154 -0
- package/dist/universal/.gemini/skills/citable/schemas/deployment-receipt.schema.json +51 -0
- package/dist/universal/.gemini/skills/citable/schemas/field-validation-metrics.schema.json +15 -0
- package/dist/universal/.gemini/skills/citable/schemas/initiative.schema.json +185 -0
- package/dist/universal/.gemini/skills/citable/schemas/kpi.schema.json +108 -0
- package/dist/universal/.gemini/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.gemini/skills/citable/schemas/release-manifest.schema.json +74 -0
- package/dist/universal/.gemini/skills/citable/schemas/release-state.schema.json +40 -0
- package/dist/universal/.gemini/skills/citable/schemas/risk.schema.json +172 -0
- package/dist/universal/.gemini/skills/citable/schemas/scenario.schema.json +165 -0
- package/dist/universal/.gemini/skills/citable/schemas/variance.schema.json +112 -0
- package/dist/universal/.github/skills/citable/SKILL.md +14 -1
- package/dist/universal/.github/skills/citable/VERSION +1 -1
- package/dist/universal/.github/skills/citable/commands/README.md +2 -1
- package/dist/universal/.github/skills/citable/commands/artifact-interchange.md +28 -0
- package/dist/universal/.github/skills/citable/commands/field-validation.md +71 -0
- package/dist/universal/.github/skills/citable/commands/observe-and-act.md +13 -1
- package/dist/universal/.github/skills/citable/manifest.json +30 -10
- package/dist/universal/.github/skills/citable/references/aeo-geo-validation.md +1 -1
- package/dist/universal/.github/skills/citable/references/capability-boundaries.md +3 -0
- package/dist/universal/.github/skills/citable/schemas/acceptance-corpus.schema.json +168 -0
- package/dist/universal/.github/skills/citable/schemas/acceptance-run-context.schema.json +17 -0
- package/dist/universal/.github/skills/citable/schemas/acceptance-run-receipt.schema.json +88 -0
- package/dist/universal/.github/skills/citable/schemas/artifact-interchange.schema.json +58 -0
- package/dist/universal/.github/skills/citable/schemas/assumption.schema.json +119 -0
- package/dist/universal/.github/skills/citable/schemas/browser-evidence-plan.schema.json +76 -0
- package/dist/universal/.github/skills/citable/schemas/corpus-publication-receipt.schema.json +24 -0
- package/dist/universal/.github/skills/citable/schemas/customer-outcome.schema.json +119 -0
- package/dist/universal/.github/skills/citable/schemas/decision.schema.json +154 -0
- package/dist/universal/.github/skills/citable/schemas/deployment-receipt.schema.json +51 -0
- package/dist/universal/.github/skills/citable/schemas/field-validation-metrics.schema.json +15 -0
- package/dist/universal/.github/skills/citable/schemas/initiative.schema.json +185 -0
- package/dist/universal/.github/skills/citable/schemas/kpi.schema.json +108 -0
- package/dist/universal/.github/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.github/skills/citable/schemas/release-manifest.schema.json +74 -0
- package/dist/universal/.github/skills/citable/schemas/release-state.schema.json +40 -0
- package/dist/universal/.github/skills/citable/schemas/risk.schema.json +172 -0
- package/dist/universal/.github/skills/citable/schemas/scenario.schema.json +165 -0
- package/dist/universal/.github/skills/citable/schemas/variance.schema.json +112 -0
- package/dist/universal/.kiro/skills/citable/SKILL.md +14 -1
- package/dist/universal/.kiro/skills/citable/VERSION +1 -1
- package/dist/universal/.kiro/skills/citable/commands/README.md +2 -1
- package/dist/universal/.kiro/skills/citable/commands/artifact-interchange.md +28 -0
- package/dist/universal/.kiro/skills/citable/commands/field-validation.md +71 -0
- package/dist/universal/.kiro/skills/citable/commands/observe-and-act.md +13 -1
- package/dist/universal/.kiro/skills/citable/manifest.json +30 -10
- package/dist/universal/.kiro/skills/citable/references/aeo-geo-validation.md +1 -1
- package/dist/universal/.kiro/skills/citable/references/capability-boundaries.md +3 -0
- package/dist/universal/.kiro/skills/citable/schemas/acceptance-corpus.schema.json +168 -0
- package/dist/universal/.kiro/skills/citable/schemas/acceptance-run-context.schema.json +17 -0
- package/dist/universal/.kiro/skills/citable/schemas/acceptance-run-receipt.schema.json +88 -0
- package/dist/universal/.kiro/skills/citable/schemas/artifact-interchange.schema.json +58 -0
- package/dist/universal/.kiro/skills/citable/schemas/assumption.schema.json +119 -0
- package/dist/universal/.kiro/skills/citable/schemas/browser-evidence-plan.schema.json +76 -0
- package/dist/universal/.kiro/skills/citable/schemas/corpus-publication-receipt.schema.json +24 -0
- package/dist/universal/.kiro/skills/citable/schemas/customer-outcome.schema.json +119 -0
- package/dist/universal/.kiro/skills/citable/schemas/decision.schema.json +154 -0
- package/dist/universal/.kiro/skills/citable/schemas/deployment-receipt.schema.json +51 -0
- package/dist/universal/.kiro/skills/citable/schemas/field-validation-metrics.schema.json +15 -0
- package/dist/universal/.kiro/skills/citable/schemas/initiative.schema.json +185 -0
- package/dist/universal/.kiro/skills/citable/schemas/kpi.schema.json +108 -0
- package/dist/universal/.kiro/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.kiro/skills/citable/schemas/release-manifest.schema.json +74 -0
- package/dist/universal/.kiro/skills/citable/schemas/release-state.schema.json +40 -0
- package/dist/universal/.kiro/skills/citable/schemas/risk.schema.json +172 -0
- package/dist/universal/.kiro/skills/citable/schemas/scenario.schema.json +165 -0
- package/dist/universal/.kiro/skills/citable/schemas/variance.schema.json +112 -0
- package/dist/universal/.opencode/skills/citable/SKILL.md +14 -1
- package/dist/universal/.opencode/skills/citable/VERSION +1 -1
- package/dist/universal/.opencode/skills/citable/commands/README.md +2 -1
- package/dist/universal/.opencode/skills/citable/commands/artifact-interchange.md +28 -0
- package/dist/universal/.opencode/skills/citable/commands/field-validation.md +71 -0
- package/dist/universal/.opencode/skills/citable/commands/observe-and-act.md +13 -1
- package/dist/universal/.opencode/skills/citable/manifest.json +30 -10
- package/dist/universal/.opencode/skills/citable/references/aeo-geo-validation.md +1 -1
- package/dist/universal/.opencode/skills/citable/references/capability-boundaries.md +3 -0
- package/dist/universal/.opencode/skills/citable/schemas/acceptance-corpus.schema.json +168 -0
- package/dist/universal/.opencode/skills/citable/schemas/acceptance-run-context.schema.json +17 -0
- package/dist/universal/.opencode/skills/citable/schemas/acceptance-run-receipt.schema.json +88 -0
- package/dist/universal/.opencode/skills/citable/schemas/artifact-interchange.schema.json +58 -0
- package/dist/universal/.opencode/skills/citable/schemas/assumption.schema.json +119 -0
- package/dist/universal/.opencode/skills/citable/schemas/browser-evidence-plan.schema.json +76 -0
- package/dist/universal/.opencode/skills/citable/schemas/corpus-publication-receipt.schema.json +24 -0
- package/dist/universal/.opencode/skills/citable/schemas/customer-outcome.schema.json +119 -0
- package/dist/universal/.opencode/skills/citable/schemas/decision.schema.json +154 -0
- package/dist/universal/.opencode/skills/citable/schemas/deployment-receipt.schema.json +51 -0
- package/dist/universal/.opencode/skills/citable/schemas/field-validation-metrics.schema.json +15 -0
- package/dist/universal/.opencode/skills/citable/schemas/initiative.schema.json +185 -0
- package/dist/universal/.opencode/skills/citable/schemas/kpi.schema.json +108 -0
- package/dist/universal/.opencode/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.opencode/skills/citable/schemas/release-manifest.schema.json +74 -0
- package/dist/universal/.opencode/skills/citable/schemas/release-state.schema.json +40 -0
- package/dist/universal/.opencode/skills/citable/schemas/risk.schema.json +172 -0
- package/dist/universal/.opencode/skills/citable/schemas/scenario.schema.json +165 -0
- package/dist/universal/.opencode/skills/citable/schemas/variance.schema.json +112 -0
- package/dist/universal/.pi/agent/skills/citable/SKILL.md +14 -1
- package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
- package/dist/universal/.pi/agent/skills/citable/commands/README.md +2 -1
- package/dist/universal/.pi/agent/skills/citable/commands/artifact-interchange.md +28 -0
- package/dist/universal/.pi/agent/skills/citable/commands/field-validation.md +71 -0
- package/dist/universal/.pi/agent/skills/citable/commands/observe-and-act.md +13 -1
- package/dist/universal/.pi/agent/skills/citable/manifest.json +30 -10
- package/dist/universal/.pi/agent/skills/citable/references/aeo-geo-validation.md +1 -1
- package/dist/universal/.pi/agent/skills/citable/references/capability-boundaries.md +3 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/acceptance-corpus.schema.json +168 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/acceptance-run-context.schema.json +17 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/acceptance-run-receipt.schema.json +88 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/artifact-interchange.schema.json +58 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/assumption.schema.json +119 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/browser-evidence-plan.schema.json +76 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/corpus-publication-receipt.schema.json +24 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/customer-outcome.schema.json +119 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/decision.schema.json +154 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/deployment-receipt.schema.json +51 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/field-validation-metrics.schema.json +15 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/initiative.schema.json +185 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/kpi.schema.json +108 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/release-manifest.schema.json +74 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/release-state.schema.json +40 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/risk.schema.json +172 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/scenario.schema.json +165 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/variance.schema.json +112 -0
- package/dist/universal/.qoder/skills/citable/SKILL.md +14 -1
- package/dist/universal/.qoder/skills/citable/VERSION +1 -1
- package/dist/universal/.qoder/skills/citable/commands/README.md +2 -1
- package/dist/universal/.qoder/skills/citable/commands/artifact-interchange.md +28 -0
- package/dist/universal/.qoder/skills/citable/commands/field-validation.md +71 -0
- package/dist/universal/.qoder/skills/citable/commands/observe-and-act.md +13 -1
- package/dist/universal/.qoder/skills/citable/manifest.json +30 -10
- package/dist/universal/.qoder/skills/citable/references/aeo-geo-validation.md +1 -1
- package/dist/universal/.qoder/skills/citable/references/capability-boundaries.md +3 -0
- package/dist/universal/.qoder/skills/citable/schemas/acceptance-corpus.schema.json +168 -0
- package/dist/universal/.qoder/skills/citable/schemas/acceptance-run-context.schema.json +17 -0
- package/dist/universal/.qoder/skills/citable/schemas/acceptance-run-receipt.schema.json +88 -0
- package/dist/universal/.qoder/skills/citable/schemas/artifact-interchange.schema.json +58 -0
- package/dist/universal/.qoder/skills/citable/schemas/assumption.schema.json +119 -0
- package/dist/universal/.qoder/skills/citable/schemas/browser-evidence-plan.schema.json +76 -0
- package/dist/universal/.qoder/skills/citable/schemas/corpus-publication-receipt.schema.json +24 -0
- package/dist/universal/.qoder/skills/citable/schemas/customer-outcome.schema.json +119 -0
- package/dist/universal/.qoder/skills/citable/schemas/decision.schema.json +154 -0
- package/dist/universal/.qoder/skills/citable/schemas/deployment-receipt.schema.json +51 -0
- package/dist/universal/.qoder/skills/citable/schemas/field-validation-metrics.schema.json +15 -0
- package/dist/universal/.qoder/skills/citable/schemas/initiative.schema.json +185 -0
- package/dist/universal/.qoder/skills/citable/schemas/kpi.schema.json +108 -0
- package/dist/universal/.qoder/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.qoder/skills/citable/schemas/release-manifest.schema.json +74 -0
- package/dist/universal/.qoder/skills/citable/schemas/release-state.schema.json +40 -0
- package/dist/universal/.qoder/skills/citable/schemas/risk.schema.json +172 -0
- package/dist/universal/.qoder/skills/citable/schemas/scenario.schema.json +165 -0
- package/dist/universal/.qoder/skills/citable/schemas/variance.schema.json +112 -0
- package/dist/universal/.rovodev/skills/citable/SKILL.md +14 -1
- package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
- package/dist/universal/.rovodev/skills/citable/commands/README.md +2 -1
- package/dist/universal/.rovodev/skills/citable/commands/artifact-interchange.md +28 -0
- package/dist/universal/.rovodev/skills/citable/commands/field-validation.md +71 -0
- package/dist/universal/.rovodev/skills/citable/commands/observe-and-act.md +13 -1
- package/dist/universal/.rovodev/skills/citable/manifest.json +30 -10
- package/dist/universal/.rovodev/skills/citable/references/aeo-geo-validation.md +1 -1
- package/dist/universal/.rovodev/skills/citable/references/capability-boundaries.md +3 -0
- package/dist/universal/.rovodev/skills/citable/schemas/acceptance-corpus.schema.json +168 -0
- package/dist/universal/.rovodev/skills/citable/schemas/acceptance-run-context.schema.json +17 -0
- package/dist/universal/.rovodev/skills/citable/schemas/acceptance-run-receipt.schema.json +88 -0
- package/dist/universal/.rovodev/skills/citable/schemas/artifact-interchange.schema.json +58 -0
- package/dist/universal/.rovodev/skills/citable/schemas/assumption.schema.json +119 -0
- package/dist/universal/.rovodev/skills/citable/schemas/browser-evidence-plan.schema.json +76 -0
- package/dist/universal/.rovodev/skills/citable/schemas/corpus-publication-receipt.schema.json +24 -0
- package/dist/universal/.rovodev/skills/citable/schemas/customer-outcome.schema.json +119 -0
- package/dist/universal/.rovodev/skills/citable/schemas/decision.schema.json +154 -0
- package/dist/universal/.rovodev/skills/citable/schemas/deployment-receipt.schema.json +51 -0
- package/dist/universal/.rovodev/skills/citable/schemas/field-validation-metrics.schema.json +15 -0
- package/dist/universal/.rovodev/skills/citable/schemas/initiative.schema.json +185 -0
- package/dist/universal/.rovodev/skills/citable/schemas/kpi.schema.json +108 -0
- package/dist/universal/.rovodev/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.rovodev/skills/citable/schemas/release-manifest.schema.json +74 -0
- package/dist/universal/.rovodev/skills/citable/schemas/release-state.schema.json +40 -0
- package/dist/universal/.rovodev/skills/citable/schemas/risk.schema.json +172 -0
- package/dist/universal/.rovodev/skills/citable/schemas/scenario.schema.json +165 -0
- package/dist/universal/.rovodev/skills/citable/schemas/variance.schema.json +112 -0
- package/dist/universal/.trae/skills/citable/SKILL.md +14 -1
- package/dist/universal/.trae/skills/citable/VERSION +1 -1
- package/dist/universal/.trae/skills/citable/commands/README.md +2 -1
- package/dist/universal/.trae/skills/citable/commands/artifact-interchange.md +28 -0
- package/dist/universal/.trae/skills/citable/commands/field-validation.md +71 -0
- package/dist/universal/.trae/skills/citable/commands/observe-and-act.md +13 -1
- package/dist/universal/.trae/skills/citable/manifest.json +30 -10
- package/dist/universal/.trae/skills/citable/references/aeo-geo-validation.md +1 -1
- package/dist/universal/.trae/skills/citable/references/capability-boundaries.md +3 -0
- package/dist/universal/.trae/skills/citable/schemas/acceptance-corpus.schema.json +168 -0
- package/dist/universal/.trae/skills/citable/schemas/acceptance-run-context.schema.json +17 -0
- package/dist/universal/.trae/skills/citable/schemas/acceptance-run-receipt.schema.json +88 -0
- package/dist/universal/.trae/skills/citable/schemas/artifact-interchange.schema.json +58 -0
- package/dist/universal/.trae/skills/citable/schemas/assumption.schema.json +119 -0
- package/dist/universal/.trae/skills/citable/schemas/browser-evidence-plan.schema.json +76 -0
- package/dist/universal/.trae/skills/citable/schemas/corpus-publication-receipt.schema.json +24 -0
- package/dist/universal/.trae/skills/citable/schemas/customer-outcome.schema.json +119 -0
- package/dist/universal/.trae/skills/citable/schemas/decision.schema.json +154 -0
- package/dist/universal/.trae/skills/citable/schemas/deployment-receipt.schema.json +51 -0
- package/dist/universal/.trae/skills/citable/schemas/field-validation-metrics.schema.json +15 -0
- package/dist/universal/.trae/skills/citable/schemas/initiative.schema.json +185 -0
- package/dist/universal/.trae/skills/citable/schemas/kpi.schema.json +108 -0
- package/dist/universal/.trae/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.trae/skills/citable/schemas/release-manifest.schema.json +74 -0
- package/dist/universal/.trae/skills/citable/schemas/release-state.schema.json +40 -0
- package/dist/universal/.trae/skills/citable/schemas/risk.schema.json +172 -0
- package/dist/universal/.trae/skills/citable/schemas/scenario.schema.json +165 -0
- package/dist/universal/.trae/skills/citable/schemas/variance.schema.json +112 -0
- package/dist/universal/.trae-cn/skills/citable/SKILL.md +14 -1
- package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
- package/dist/universal/.trae-cn/skills/citable/commands/README.md +2 -1
- package/dist/universal/.trae-cn/skills/citable/commands/artifact-interchange.md +28 -0
- package/dist/universal/.trae-cn/skills/citable/commands/field-validation.md +71 -0
- package/dist/universal/.trae-cn/skills/citable/commands/observe-and-act.md +13 -1
- package/dist/universal/.trae-cn/skills/citable/manifest.json +30 -10
- package/dist/universal/.trae-cn/skills/citable/references/aeo-geo-validation.md +1 -1
- package/dist/universal/.trae-cn/skills/citable/references/capability-boundaries.md +3 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/acceptance-corpus.schema.json +168 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/acceptance-run-context.schema.json +17 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/acceptance-run-receipt.schema.json +88 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/artifact-interchange.schema.json +58 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/assumption.schema.json +119 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/browser-evidence-plan.schema.json +76 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/corpus-publication-receipt.schema.json +24 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/customer-outcome.schema.json +119 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/decision.schema.json +154 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/deployment-receipt.schema.json +51 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/field-validation-metrics.schema.json +15 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/initiative.schema.json +185 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/kpi.schema.json +108 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/release-manifest.schema.json +74 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/release-state.schema.json +40 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/risk.schema.json +172 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/scenario.schema.json +165 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/variance.schema.json +112 -0
- package/dist/universal/manifest.json +363 -123
- package/package.json +12 -1
- package/release/surfaces.json +21 -0
- package/schemas/acceptance-corpus.schema.json +168 -0
- package/schemas/acceptance-run-context.schema.json +17 -0
- package/schemas/acceptance-run-receipt.schema.json +88 -0
- package/schemas/artifact-interchange.schema.json +58 -0
- package/schemas/assumption.schema.json +119 -0
- package/schemas/browser-evidence-plan.schema.json +76 -0
- package/schemas/corpus-publication-receipt.schema.json +24 -0
- package/schemas/customer-outcome.schema.json +119 -0
- package/schemas/decision.schema.json +154 -0
- package/schemas/deployment-receipt.schema.json +51 -0
- package/schemas/field-validation-metrics.schema.json +15 -0
- package/schemas/initiative.schema.json +185 -0
- package/schemas/kpi.schema.json +108 -0
- package/schemas/observation.schema.json +1 -1
- package/schemas/release-manifest.schema.json +74 -0
- package/schemas/release-state.schema.json +40 -0
- package/schemas/risk.schema.json +172 -0
- package/schemas/scenario.schema.json +165 -0
- package/schemas/variance.schema.json +112 -0
- package/scripts/release-governance.js +123 -0
- package/skill/SKILL.md +14 -1
- package/skill/commands/README.md +2 -1
- package/skill/commands/artifact-interchange.md +28 -0
- package/skill/commands/field-validation.md +71 -0
- package/skill/commands/observe-and-act.md +13 -1
- package/skill/references/aeo-geo-validation.md +1 -1
- package/skill/references/capability-boundaries.md +3 -0
- package/src/acceptance/reproducibility.js +180 -0
- package/src/artifacts/interchange.js +179 -0
- package/src/cli/index.js +149 -4
- package/src/commands/assumptionAudit.js +111 -0
- package/src/commands/audit.js +6 -0
- package/src/commands/boardReport.js +251 -0
- package/src/commands/competitiveIntel.js +124 -0
- package/src/commands/corpus.js +322 -0
- package/src/commands/decisionMemo.js +202 -0
- package/src/commands/executive.js +167 -0
- package/src/commands/executiveReview.js +206 -0
- package/src/commands/init.js +14 -1
- package/src/commands/kpi.js +86 -0
- package/src/commands/monitor.js +9 -0
- package/src/commands/observe.js +202 -30
- package/src/commands/outcomes.js +113 -0
- package/src/commands/prioritize.js +108 -0
- package/src/commands/risk.js +104 -0
- package/src/commands/scenario.js +98 -0
- package/src/commands/variance.js +102 -0
- package/src/detectors/framework.js +37 -0
- package/src/detectors/page.js +10 -10
- package/src/detectors/tech.js +5 -3
- package/src/extractor/site.js +21 -6
- package/src/observations/browserJourney.js +167 -0
- package/src/observations/media.js +8 -1
- package/src/registries/index.js +107 -6
- package/src/release/governance.js +331 -0
- package/src/shared/asOf.js +18 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scenario command — Scenario War Room
|
|
3
|
+
*
|
|
4
|
+
* Models compound risks with cascade analysis and early-warning triggers.
|
|
5
|
+
* Maximum 3 variables per scenario. Requires explicit hedges with cost,
|
|
6
|
+
* impact, owner, and deadline — not just worst-case narratives.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* citable scenario list
|
|
10
|
+
* citable scenario show <scenario_id>
|
|
11
|
+
* citable scenario validate
|
|
12
|
+
* citable scenario triggers — early-warning trigger summary
|
|
13
|
+
*/
|
|
14
|
+
import { contextDir, loadRegistryFile, registryLoadProblems } from '../registries/index.js';
|
|
15
|
+
import { validateAgainst } from '../shared/schemaValidator.js';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
export async function scenarioCommand(args, root = process.cwd()) {
|
|
20
|
+
const [subcommand, ...rest] = args;
|
|
21
|
+
const file = path.join(contextDir(root), 'scenarios.yaml');
|
|
22
|
+
|
|
23
|
+
switch (subcommand) {
|
|
24
|
+
case 'show': return scenarioShow(file, rest[0]);
|
|
25
|
+
case 'validate': return scenarioValidate(file);
|
|
26
|
+
case 'triggers': return scenarioTriggers(file);
|
|
27
|
+
default: return scenarioList(file);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function load(file) {
|
|
32
|
+
return loadRegistryFile(file, 'scenarios');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function scenarioList(file) {
|
|
36
|
+
const data = load(file);
|
|
37
|
+
return {
|
|
38
|
+
scenarios: data.entries.map(s => ({
|
|
39
|
+
scenario_id: s.scenario_id,
|
|
40
|
+
title: s.title,
|
|
41
|
+
variables: (s.variables ?? []).length,
|
|
42
|
+
cascade_steps: (s.cascade_analysis ?? []).length,
|
|
43
|
+
hedges: (s.hedges ?? []).length,
|
|
44
|
+
early_warnings: (s.early_warning_triggers ?? []).length,
|
|
45
|
+
owner: s.owner,
|
|
46
|
+
})),
|
|
47
|
+
total: data.entries.length,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function scenarioShow(file, id) {
|
|
52
|
+
if (!id) return { error: 'scenario_id required' };
|
|
53
|
+
const data = load(file);
|
|
54
|
+
const s = data.entries.find(e => e.scenario_id === id);
|
|
55
|
+
if (!s) return { error: `Scenario not found: ${id}` };
|
|
56
|
+
return { scenario: s };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function scenarioTriggers(file) {
|
|
60
|
+
const data = load(file);
|
|
61
|
+
return {
|
|
62
|
+
triggers: data.entries.flatMap(s =>
|
|
63
|
+
(s.early_warning_triggers ?? []).map(t => ({
|
|
64
|
+
scenario_id: s.scenario_id, title: s.title, trigger: t,
|
|
65
|
+
}))
|
|
66
|
+
),
|
|
67
|
+
total_scenarios: data.entries.length,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function scenarioValidate(file) {
|
|
72
|
+
const data = load(file);
|
|
73
|
+
const problems = [...registryLoadProblems(data)];
|
|
74
|
+
const { valid, errors } = validateAgainst('scenario.schema.json', data);
|
|
75
|
+
if (!valid) problems.push(...errors);
|
|
76
|
+
|
|
77
|
+
for (const s of data.entries) {
|
|
78
|
+
// Variables capped at 3
|
|
79
|
+
if ((s.variables ?? []).length > 3)
|
|
80
|
+
problems.push(`${s.scenario_id}: maximum 3 variables per scenario — combine compound drivers into a single variable`);
|
|
81
|
+
// Cascade analysis required (not just isolated risk statements)
|
|
82
|
+
if (!s.cascade_analysis || s.cascade_analysis.length === 0)
|
|
83
|
+
problems.push(`${s.scenario_id}: cascade_analysis required — scenarios must show cascading effects, not isolated risks`);
|
|
84
|
+
// All three states required
|
|
85
|
+
if (!s.states?.base || !s.states?.stress || !s.states?.severe)
|
|
86
|
+
problems.push(`${s.scenario_id}: all three states (base, stress, severe) required`);
|
|
87
|
+
// Hedges must have all required fields
|
|
88
|
+
for (const h of s.hedges ?? []) {
|
|
89
|
+
if (!h.cost || !h.impact || !h.owner || !h.deadline)
|
|
90
|
+
problems.push(`${s.scenario_id} hedge "${h.action}": cost, impact, owner, and deadline all required`);
|
|
91
|
+
}
|
|
92
|
+
// Early-warning triggers required
|
|
93
|
+
if (!s.early_warning_triggers || s.early_warning_triggers.length === 0)
|
|
94
|
+
problems.push(`${s.scenario_id}: early_warning_triggers required — scenario without triggers is a narrative, not a management tool`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return { valid: problems.length === 0, problems, checked: data.entries.length };
|
|
98
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* variance command — Variance analysis registry
|
|
3
|
+
*
|
|
4
|
+
* Rejects non-causes (market conditions, timing, customer behaviour)
|
|
5
|
+
* unless decomposed into specific observed mechanisms.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* citable variance list [--period <period>]
|
|
9
|
+
* citable variance show <variance_id>
|
|
10
|
+
* citable variance validate
|
|
11
|
+
* citable variance material — list material/critical variances only
|
|
12
|
+
*/
|
|
13
|
+
import { contextDir, loadRegistryFile, registryLoadProblems } from '../registries/index.js';
|
|
14
|
+
import { validateAgainst } from '../shared/schemaValidator.js';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
// Vague causes that must be decomposed before acceptance
|
|
19
|
+
const REJECTED_CAUSE_PATTERNS = [
|
|
20
|
+
/^market conditions?$/i,
|
|
21
|
+
/^timing$/i,
|
|
22
|
+
/^customer behaviour?$/i,
|
|
23
|
+
/^execution issues?$/i,
|
|
24
|
+
/^macro(economic)?$/i,
|
|
25
|
+
/^seasonality$/i,
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export async function varianceCommand(args, root = process.cwd()) {
|
|
29
|
+
const [subcommand, ...rest] = args;
|
|
30
|
+
const file = path.join(contextDir(root), 'variances.yaml');
|
|
31
|
+
|
|
32
|
+
switch (subcommand) {
|
|
33
|
+
case 'show': return varianceShow(file, rest[0]);
|
|
34
|
+
case 'validate': return varianceValidate(file);
|
|
35
|
+
case 'material': return varianceList(file, { materialOnly: true });
|
|
36
|
+
default: {
|
|
37
|
+
const i = rest.indexOf('--period');
|
|
38
|
+
return varianceList(file, { period: i >= 0 ? rest[i + 1] : null });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function load(file) {
|
|
44
|
+
return loadRegistryFile(file, 'variances');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function varianceList(file, { materialOnly = false, period = null } = {}) {
|
|
48
|
+
const data = load(file);
|
|
49
|
+
let entries = data.entries;
|
|
50
|
+
if (materialOnly) entries = entries.filter(v => ['material','critical'].includes(v.materiality));
|
|
51
|
+
if (period) entries = entries.filter(v => v.period === period);
|
|
52
|
+
return {
|
|
53
|
+
variances: entries.map(v => ({
|
|
54
|
+
variance_id: v.variance_id,
|
|
55
|
+
metric_id: v.metric_id,
|
|
56
|
+
period: v.period,
|
|
57
|
+
variance_pct: v.variance_pct,
|
|
58
|
+
materiality: v.materiality,
|
|
59
|
+
primary_driver: v.primary_driver,
|
|
60
|
+
controllable: v.controllable,
|
|
61
|
+
confidence: v.confidence,
|
|
62
|
+
})),
|
|
63
|
+
total: entries.length,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function varianceShow(file, id) {
|
|
68
|
+
if (!id) return { error: 'variance_id required' };
|
|
69
|
+
const data = load(file);
|
|
70
|
+
const v = data.entries.find(e => e.variance_id === id);
|
|
71
|
+
if (!v) return { error: `Variance not found: ${id}` };
|
|
72
|
+
return { variance: v };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function varianceValidate(file) {
|
|
76
|
+
const data = load(file);
|
|
77
|
+
const problems = [...registryLoadProblems(data)];
|
|
78
|
+
const { valid, errors } = validateAgainst('variance.schema.json', data);
|
|
79
|
+
if (!valid) problems.push(...errors);
|
|
80
|
+
|
|
81
|
+
for (const v of data.entries) {
|
|
82
|
+
// Reject vague causes
|
|
83
|
+
for (const pattern of REJECTED_CAUSE_PATTERNS) {
|
|
84
|
+
if (pattern.test((v.primary_driver ?? '').trim())) {
|
|
85
|
+
problems.push(`${v.variance_id}: primary_driver "${v.primary_driver}" is a non-cause — decompose into a specific observed mechanism`);
|
|
86
|
+
}
|
|
87
|
+
for (const d of v.secondary_drivers ?? []) {
|
|
88
|
+
if (pattern.test(d.trim())) {
|
|
89
|
+
problems.push(`${v.variance_id}: secondary_driver "${d}" is a non-cause — decompose into a specific observed mechanism`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Material variances need management response
|
|
94
|
+
if (['material','critical'].includes(v.materiality) && !v.management_response)
|
|
95
|
+
problems.push(`${v.variance_id}: material variance requires management_response`);
|
|
96
|
+
// Evidence required
|
|
97
|
+
if (!v.evidence || v.evidence.length === 0)
|
|
98
|
+
problems.push(`${v.variance_id}: evidence array must not be empty`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return { valid: problems.length === 0, problems, checked: data.entries.length };
|
|
102
|
+
}
|
|
@@ -100,12 +100,49 @@ export function runDetectors(detectors, ctx) {
|
|
|
100
100
|
/** Pages whose declared or default intent is to be indexed. */
|
|
101
101
|
export function indexTargets(ctx) {
|
|
102
102
|
return ctx.site.pages.filter((p) => {
|
|
103
|
+
if (isProviderUtilityUrl(p.url)) return false;
|
|
103
104
|
const reg = registryPageFor(ctx, p);
|
|
104
105
|
if (reg?.indexing_intent === 'noindex') return false;
|
|
105
106
|
return true;
|
|
106
107
|
});
|
|
107
108
|
}
|
|
108
109
|
|
|
110
|
+
/** Index targets whose response is an HTML document. */
|
|
111
|
+
export function htmlIndexTargets(ctx) {
|
|
112
|
+
return indexTargets(ctx).filter(isHtmlDocument);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function isHtmlDocument(page) {
|
|
116
|
+
const type = String(page?.contentType || '').toLowerCase();
|
|
117
|
+
return !type || type.includes('text/html') || type.includes('application/xhtml+xml');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** URLs that conventionally identify HTML documents rather than media/data resources. */
|
|
121
|
+
export function isHtmlDocumentUrl(url) {
|
|
122
|
+
try {
|
|
123
|
+
const pathname = new URL(url).pathname;
|
|
124
|
+
const name = pathname.split('/').pop() || '';
|
|
125
|
+
const match = name.match(/\.([a-z0-9]+)$/i);
|
|
126
|
+
return Boolean(match) && ['html', 'htm', 'xhtml'].includes(match[1].toLowerCase());
|
|
127
|
+
} catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function hasHtmlDocumentMarkup(page) {
|
|
133
|
+
return /^\s*(?:<!doctype\s+html\b|<html\b)/i.test(String(page?.rawHtml || ''));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Provider-owned utility routes are link evidence, not default content pages. */
|
|
137
|
+
export function isProviderUtilityUrl(url) {
|
|
138
|
+
try {
|
|
139
|
+
const { pathname } = new URL(url);
|
|
140
|
+
return pathname === '/cdn-cgi' || pathname.startsWith('/cdn-cgi/');
|
|
141
|
+
} catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
109
146
|
/** Match a live page to its registry entry by URL path. */
|
|
110
147
|
export function registryPageFor(ctx, page) {
|
|
111
148
|
const entries = ctx.registries?.pages?.entries || [];
|
package/src/detectors/page.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineDetector,
|
|
1
|
+
import { defineDetector, htmlIndexTargets, pageSubject } from './framework.js';
|
|
2
2
|
|
|
3
3
|
const D = [];
|
|
4
4
|
|
|
@@ -11,7 +11,7 @@ D.push(defineDetector({
|
|
|
11
11
|
remediation: 'Add a concise title identifying the page subject and distinguishing it from other pages.',
|
|
12
12
|
verification: 'Confirm a non-empty <title> in rendered HTML.',
|
|
13
13
|
check(ctx) {
|
|
14
|
-
return
|
|
14
|
+
return htmlIndexTargets(ctx).filter((p) => p.status === 200 && !p.title).map((p) => ({
|
|
15
15
|
subject: pageSubject(p), summary: 'Page has no title element', evidence: ['<title> absent or empty'],
|
|
16
16
|
}));
|
|
17
17
|
},
|
|
@@ -27,7 +27,7 @@ D.push(defineDetector({
|
|
|
27
27
|
verification: 'Confirm title uniqueness across index targets.',
|
|
28
28
|
check(ctx) {
|
|
29
29
|
const byTitle = new Map();
|
|
30
|
-
for (const p of
|
|
30
|
+
for (const p of htmlIndexTargets(ctx)) {
|
|
31
31
|
if (!p.title || p.status !== 200) continue;
|
|
32
32
|
if (!byTitle.has(p.title)) byTitle.set(p.title, []);
|
|
33
33
|
byTitle.get(p.title).push(p);
|
|
@@ -49,7 +49,7 @@ D.push(defineDetector({
|
|
|
49
49
|
remediation: 'Add an accurate, page-specific description stating the expected value.',
|
|
50
50
|
verification: 'Confirm meta[name=description] present and non-empty.',
|
|
51
51
|
check(ctx) {
|
|
52
|
-
return
|
|
52
|
+
return htmlIndexTargets(ctx).filter((p) => p.status === 200 && !p.metaDescription).map((p) => ({
|
|
53
53
|
subject: pageSubject(p), summary: 'Page has no meta description', evidence: ['meta[name=description] absent'],
|
|
54
54
|
}));
|
|
55
55
|
},
|
|
@@ -65,7 +65,7 @@ D.push(defineDetector({
|
|
|
65
65
|
verification: 'Confirm description uniqueness.',
|
|
66
66
|
check(ctx) {
|
|
67
67
|
const byDesc = new Map();
|
|
68
|
-
for (const p of
|
|
68
|
+
for (const p of htmlIndexTargets(ctx)) {
|
|
69
69
|
if (!p.metaDescription || p.status !== 200) continue;
|
|
70
70
|
if (!byDesc.has(p.metaDescription)) byDesc.set(p.metaDescription, []);
|
|
71
71
|
byDesc.get(p.metaDescription).push(p);
|
|
@@ -87,7 +87,7 @@ D.push(defineDetector({
|
|
|
87
87
|
remediation: 'Use exactly one H1 that identifies the page purpose.',
|
|
88
88
|
verification: 'Count H1 elements.',
|
|
89
89
|
check(ctx) {
|
|
90
|
-
return
|
|
90
|
+
return htmlIndexTargets(ctx).filter((p) => p.status === 200 && p.h1s.length !== 1).map((p) => ({
|
|
91
91
|
subject: pageSubject(p),
|
|
92
92
|
summary: p.h1s.length === 0 ? 'Page has no H1' : `Page has ${p.h1s.length} H1 elements`,
|
|
93
93
|
evidence: p.h1s.length ? p.h1s.map((h) => `H1: ${h.text}`) : ['no <h1> found'],
|
|
@@ -106,7 +106,7 @@ D.push(defineDetector({
|
|
|
106
106
|
verification: 'Walk heading sequence and confirm no level is skipped.',
|
|
107
107
|
check(ctx) {
|
|
108
108
|
const hits = [];
|
|
109
|
-
for (const p of
|
|
109
|
+
for (const p of htmlIndexTargets(ctx)) {
|
|
110
110
|
if (p.status !== 200) continue;
|
|
111
111
|
let prev = 0;
|
|
112
112
|
for (const h of p.headings) {
|
|
@@ -134,7 +134,7 @@ D.push(defineDetector({
|
|
|
134
134
|
remediation: 'Add descriptive alt text (or empty alt for purely decorative images, reviewed case by case).',
|
|
135
135
|
verification: 'Confirm every <img> has an alt attribute.',
|
|
136
136
|
check(ctx) {
|
|
137
|
-
return
|
|
137
|
+
return htmlIndexTargets(ctx)
|
|
138
138
|
.map((p) => ({ p, missing: p.images.filter((i) => i.alt === undefined || i.alt === null) }))
|
|
139
139
|
.filter(({ p, missing }) => p.status === 200 && missing.length > 0)
|
|
140
140
|
.map(({ p, missing }) => ({
|
|
@@ -156,7 +156,7 @@ D.push(defineDetector({
|
|
|
156
156
|
verification: 'Re-measure extracted word count after revision.',
|
|
157
157
|
check(ctx) {
|
|
158
158
|
const min = ctx.config?.audit?.thin_content_words ?? 120;
|
|
159
|
-
return
|
|
159
|
+
return htmlIndexTargets(ctx)
|
|
160
160
|
.filter((p) => p.status === 200 && p.wordCount > 0 && p.wordCount < min)
|
|
161
161
|
.map((p) => ({
|
|
162
162
|
subject: pageSubject(p),
|
|
@@ -178,7 +178,7 @@ D.push(defineDetector({
|
|
|
178
178
|
verification: 'Re-measure dominant-term density.',
|
|
179
179
|
check(ctx) {
|
|
180
180
|
const hits = [];
|
|
181
|
-
for (const p of
|
|
181
|
+
for (const p of htmlIndexTargets(ctx)) {
|
|
182
182
|
if (p.status !== 200 || p.wordCount < 80) continue;
|
|
183
183
|
const words = p.text.toLowerCase().match(/[a-z][a-z-]{3,}/g) || [];
|
|
184
184
|
const freq = new Map();
|
package/src/detectors/tech.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineDetector, indexTargets, registryPageFor, pageSubject, safePath } from './framework.js';
|
|
1
|
+
import { defineDetector, hasHtmlDocumentMarkup, indexTargets, isHtmlDocument, isHtmlDocumentUrl, registryPageFor, pageSubject, safePath } from './framework.js';
|
|
2
2
|
import { isAllowed } from '../crawler/robots.js';
|
|
3
3
|
|
|
4
4
|
const D = [];
|
|
@@ -177,7 +177,9 @@ D.push(defineDetector({
|
|
|
177
177
|
for (const sm of ctx.site.sitemaps) {
|
|
178
178
|
for (const u of sm.parsed.urls) {
|
|
179
179
|
const page = ctx.site.byUrl.get(ctx.site.normalize(u.loc));
|
|
180
|
-
if (!page) {
|
|
180
|
+
if (!page && ctx.site.mode === 'url' && ctx.site.crawl?.truncated) {
|
|
181
|
+
continue;
|
|
182
|
+
} else if (!page) {
|
|
181
183
|
hits.push({
|
|
182
184
|
subject: { type: 'url', identifier: u.loc, url: u.loc, source_location: sm.source },
|
|
183
185
|
summary: 'Sitemap URL not present in audited output',
|
|
@@ -227,7 +229,7 @@ D.push(defineDetector({
|
|
|
227
229
|
verification: 'Inspect the Content-Type response header.',
|
|
228
230
|
check(ctx) {
|
|
229
231
|
return indexTargets(ctx)
|
|
230
|
-
.filter((p) => p.status === 200 && p.contentType &&
|
|
232
|
+
.filter((p) => p.status === 200 && p.contentType && (isHtmlDocumentUrl(p.url) || hasHtmlDocumentMarkup(p)) && !isHtmlDocument(p))
|
|
231
233
|
.map((p) => ({
|
|
232
234
|
subject: pageSubject(p),
|
|
233
235
|
summary: `HTML page served with Content-Type "${p.contentType}"`,
|
package/src/extractor/site.js
CHANGED
|
@@ -59,6 +59,7 @@ export function buildSiteFromDir(dir, { baseUrl = 'https://example.test' } = {})
|
|
|
59
59
|
export async function buildSiteFromUrl(startUrl, {
|
|
60
60
|
maxPages = 50, userAgent, pageMaxBytes = 5 * 1024 * 1024,
|
|
61
61
|
robotsMaxBytes = 512 * 1024, sitemapMaxBytes = 5 * 1024 * 1024,
|
|
62
|
+
fetcher = fetchUrl,
|
|
62
63
|
} = {}) {
|
|
63
64
|
const origin = new URL(startUrl).origin;
|
|
64
65
|
const seen = new Set();
|
|
@@ -71,7 +72,7 @@ export async function buildSiteFromUrl(startUrl, {
|
|
|
71
72
|
if (seen.has(key)) continue;
|
|
72
73
|
seen.add(key);
|
|
73
74
|
try {
|
|
74
|
-
const res = await
|
|
75
|
+
const res = await fetcher(url, { userAgent, maxBodyBytes: pageMaxBytes });
|
|
75
76
|
const page = extractPage({
|
|
76
77
|
url: res.url, html: res.body, status: res.status, headers: res.headers, redirectChain: res.redirectChain,
|
|
77
78
|
});
|
|
@@ -81,7 +82,8 @@ export async function buildSiteFromUrl(startUrl, {
|
|
|
81
82
|
for (const l of page.links) {
|
|
82
83
|
try {
|
|
83
84
|
const u = new URL(l.href, res.url);
|
|
84
|
-
|
|
85
|
+
u.hash = '';
|
|
86
|
+
if (u.origin === origin && !isProviderUtilityPath(u.pathname) && !seen.has(u.href)) queue.push(u.href);
|
|
85
87
|
} catch { /* unresolvable href — surfaced by LINK detectors */ }
|
|
86
88
|
}
|
|
87
89
|
}
|
|
@@ -92,7 +94,7 @@ export async function buildSiteFromUrl(startUrl, {
|
|
|
92
94
|
let robotsText = null;
|
|
93
95
|
const sitemaps = [];
|
|
94
96
|
try {
|
|
95
|
-
const r = await
|
|
97
|
+
const r = await fetcher(new URL('/robots.txt', origin).href, { userAgent, maxBodyBytes: robotsMaxBytes });
|
|
96
98
|
if (r.status === 200) robotsText = r.body;
|
|
97
99
|
} catch { /* recorded as missing robots */ }
|
|
98
100
|
const smUrls = robotsText ? parseRobots(robotsText).sitemaps : [new URL('/sitemap.xml', origin).href];
|
|
@@ -103,16 +105,24 @@ export async function buildSiteFromUrl(startUrl, {
|
|
|
103
105
|
errors.push(`${sitemapUrl.href}: sitemap URL leaves audited origin`);
|
|
104
106
|
continue;
|
|
105
107
|
}
|
|
106
|
-
const r = await
|
|
108
|
+
const r = await fetcher(sitemapUrl.href, { userAgent, maxBodyBytes: sitemapMaxBytes });
|
|
107
109
|
if (r.status === 200) sitemaps.push({ source: sm, parsed: parseSitemap(r.body) });
|
|
108
110
|
} catch { /* absence handled by TECH detectors */ }
|
|
109
111
|
}
|
|
110
|
-
const
|
|
112
|
+
const pendingUrls = [...new Set(queue.map((url) => url.replace(/#.*$/, '')).filter((url) => !seen.has(url)))];
|
|
113
|
+
const crawl = {
|
|
114
|
+
maxPages,
|
|
115
|
+
pagesFetched: pages.length,
|
|
116
|
+
truncated: pages.length >= maxPages && pendingUrls.length > 0,
|
|
117
|
+
pendingUrlCount: pendingUrls.length,
|
|
118
|
+
pendingUrls,
|
|
119
|
+
};
|
|
120
|
+
const site = assembleSite({ baseUrl: origin, pages, robotsText, sitemaps, transport: {}, mode: 'url', location: startUrl, crawl });
|
|
111
121
|
site.fetchErrors = errors;
|
|
112
122
|
return site;
|
|
113
123
|
}
|
|
114
124
|
|
|
115
|
-
function assembleSite({ baseUrl, pages, robotsText, sitemaps, transport, mode, location }) {
|
|
125
|
+
function assembleSite({ baseUrl, pages, robotsText, sitemaps, transport, mode, location, crawl = null }) {
|
|
116
126
|
const byUrl = new Map();
|
|
117
127
|
for (const p of pages) byUrl.set(normalize(p.url), p);
|
|
118
128
|
|
|
@@ -168,9 +178,14 @@ function assembleSite({ baseUrl, pages, robotsText, sitemaps, transport, mode, l
|
|
|
168
178
|
outbound,
|
|
169
179
|
depth,
|
|
170
180
|
normalize,
|
|
181
|
+
crawl,
|
|
171
182
|
};
|
|
172
183
|
}
|
|
173
184
|
|
|
185
|
+
function isProviderUtilityPath(pathname) {
|
|
186
|
+
return pathname === '/cdn-cgi' || pathname.startsWith('/cdn-cgi/');
|
|
187
|
+
}
|
|
188
|
+
|
|
174
189
|
export function normalize(url) {
|
|
175
190
|
try {
|
|
176
191
|
const u = new URL(url);
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { envelope, observationRun, readInput } from './common.js';
|
|
2
|
+
import { fetchUrl } from '../crawler/fetch.js';
|
|
3
|
+
import { sha256 } from '../shared/io.js';
|
|
4
|
+
import { validateAgainst } from '../shared/schemaValidator.js';
|
|
5
|
+
|
|
6
|
+
async function executeStep(page, step) {
|
|
7
|
+
const locator = step.locator ? page.locator(step.locator).first() : null;
|
|
8
|
+
if (step.action === 'navigate') await page.goto(step.url, { waitUntil: 'networkidle' });
|
|
9
|
+
else if (step.action === 'click') await locator.click();
|
|
10
|
+
else if (step.action === 'fill') {
|
|
11
|
+
const value = process.env[step.value_env];
|
|
12
|
+
if (value == null) throw new Error(`environment input ${step.value_env} is unavailable`);
|
|
13
|
+
await locator.fill(value);
|
|
14
|
+
} else if (step.action === 'select_option') {
|
|
15
|
+
const value = process.env[step.value_env];
|
|
16
|
+
if (value == null) throw new Error(`environment input ${step.value_env} is unavailable`);
|
|
17
|
+
await locator.selectOption(value);
|
|
18
|
+
} else if (step.action === 'check') await locator.check();
|
|
19
|
+
else if (step.action === 'press') await locator.press(step.key);
|
|
20
|
+
else if (step.action === 'wait_for') await locator.waitFor({ state: 'visible' });
|
|
21
|
+
else if (step.action === 'assert_visible' && !(await locator.isVisible())) throw new Error(`locator is not visible: ${step.locator}`);
|
|
22
|
+
else if (step.action === 'scroll') await locator.scrollIntoViewIfNeeded();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function playwrightCapture(profile, plan, playwright) {
|
|
26
|
+
if (profile.authentication_state !== 'anonymous') throw new Error(`authentication state ${profile.authentication_state} requires a disclosed custom adapter`);
|
|
27
|
+
const browserType = playwright[profile.browser.engine];
|
|
28
|
+
if (!browserType) throw new Error(`Playwright engine ${profile.browser.engine} is unavailable`);
|
|
29
|
+
const browser = await browserType.launch({ headless: true });
|
|
30
|
+
try {
|
|
31
|
+
const context = await browser.newContext({
|
|
32
|
+
viewport: profile.device.viewport,
|
|
33
|
+
isMobile: profile.device.is_mobile,
|
|
34
|
+
javaScriptEnabled: profile.javascript_enabled,
|
|
35
|
+
locale: profile.locale,
|
|
36
|
+
});
|
|
37
|
+
try {
|
|
38
|
+
const page = await context.newPage();
|
|
39
|
+
const consoleErrors = [], networkFailures = [], steps = [], stepScreenshots = {};
|
|
40
|
+
page.on('console', (message) => { if (message.type() === 'error') consoleErrors.push(message.text()); });
|
|
41
|
+
page.on('requestfailed', (request) => networkFailures.push({ url: request.url(), error: request.failure()?.errorText || 'unknown' }));
|
|
42
|
+
const response = await page.goto(plan.target, { waitUntil: 'networkidle' });
|
|
43
|
+
for (const step of profile.steps) {
|
|
44
|
+
const record = { step_id: step.step_id, action: step.action, status: 'completed', failure: null, screenshot_ref: null };
|
|
45
|
+
try {
|
|
46
|
+
await executeStep(page, step);
|
|
47
|
+
if (step.capture_screenshot) {
|
|
48
|
+
record.screenshot_ref = `journeys/${profile.profile_id}/steps/${step.step_id}.png`;
|
|
49
|
+
stepScreenshots[record.screenshot_ref] = await page.screenshot({ fullPage: true });
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
record.status = 'failed';
|
|
53
|
+
record.failure = error.message;
|
|
54
|
+
steps.push(record);
|
|
55
|
+
if (step.required) throw Object.assign(new Error(`required step ${step.step_id} failed: ${error.message}`), { steps, stepScreenshots });
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
steps.push(record);
|
|
59
|
+
}
|
|
60
|
+
const dom = await page.content();
|
|
61
|
+
const text = await page.locator('body').innerText();
|
|
62
|
+
const accessibilityTree = typeof page.locator('body').ariaSnapshot === 'function' ? await page.locator('body').ariaSnapshot() : null;
|
|
63
|
+
return {
|
|
64
|
+
final_url: page.url(), status: response?.status() ?? null, browser_version: browser.version(),
|
|
65
|
+
dom, text, accessibility_tree: accessibilityTree, screenshot: await page.screenshot({ fullPage: true }),
|
|
66
|
+
console_errors: consoleErrors, network_failures: networkFailures, steps, step_screenshots: stepScreenshots,
|
|
67
|
+
};
|
|
68
|
+
} finally {
|
|
69
|
+
await context.close();
|
|
70
|
+
}
|
|
71
|
+
} finally {
|
|
72
|
+
await browser.close();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function observeBrowserPlan(root, options) {
|
|
77
|
+
const input = readInput(options.input);
|
|
78
|
+
const plan = input.value;
|
|
79
|
+
const check = validateAgainst('browser-evidence-plan.schema.json', plan);
|
|
80
|
+
if (!check.valid) throw new Error(`browser evidence plan violates contract: ${check.errors.join('; ')}`);
|
|
81
|
+
if (options.target && options.target !== plan.target) throw new Error('browser evidence target differs from the plan target');
|
|
82
|
+
const profileIds = plan.profiles.map((item) => item.profile_id);
|
|
83
|
+
if (new Set(profileIds).size !== profileIds.length) throw new Error('browser evidence plan contains duplicate profile ids');
|
|
84
|
+
const targetOrigin = new URL(plan.target).origin;
|
|
85
|
+
for (const profile of plan.profiles) {
|
|
86
|
+
const stepIds = profile.steps.map((item) => item.step_id);
|
|
87
|
+
if (new Set(stepIds).size !== stepIds.length) throw new Error(`${profile.profile_id}: duplicate journey step ids`);
|
|
88
|
+
for (const step of profile.steps) {
|
|
89
|
+
if (step.action === 'navigate') {
|
|
90
|
+
if (!step.url) throw new Error(`${profile.profile_id}/${step.step_id}: navigate requires url`);
|
|
91
|
+
if (new URL(step.url).origin !== targetOrigin) throw new Error(`${profile.profile_id}/${step.step_id}: cross-origin navigation is not allowed`);
|
|
92
|
+
} else if (!step.locator) throw new Error(`${profile.profile_id}/${step.step_id}: ${step.action} requires locator`);
|
|
93
|
+
if (['fill', 'select_option'].includes(step.action) && !step.value_env) throw new Error(`${profile.profile_id}/${step.step_id}: ${step.action} requires value_env`);
|
|
94
|
+
if (step.action === 'press' && !step.key) throw new Error(`${profile.profile_id}/${step.step_id}: press requires key`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let playwright = options.playwright;
|
|
99
|
+
if (!options.captureJourney && !playwright) {
|
|
100
|
+
try { playwright = await import('playwright'); } catch {
|
|
101
|
+
const item = envelope('browser_journey', { plan_id: plan.plan_id, target: plan.target }, { method: 'browser', source: 'playwright', state: 'not_evidenced', confidence: 'unknown', limitations: ['Optional Playwright dependency is not installed.'] });
|
|
102
|
+
return observationRun(root, 'observe render', plan.target, [item], { rawInputs: { browser_evidence_plan: input.raw }, incomplete: ['Cross-browser journey collection unavailable: install Playwright and browser engines.'] });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const initial = await (options.fetchUrl || fetchUrl)(plan.target, { maxRetries: 1 });
|
|
107
|
+
const observations = [], incomplete = [], artifacts = {
|
|
108
|
+
'initial/response.html': initial.body,
|
|
109
|
+
'initial/headers.json': initial.headers || {},
|
|
110
|
+
};
|
|
111
|
+
for (const profile of plan.profiles) {
|
|
112
|
+
const prefix = `journeys/${profile.profile_id}`;
|
|
113
|
+
try {
|
|
114
|
+
const capture = options.captureJourney
|
|
115
|
+
? await options.captureJourney(profile, plan)
|
|
116
|
+
: await playwrightCapture(profile, plan, playwright);
|
|
117
|
+
if (!capture || typeof capture.dom !== 'string' || typeof capture.text !== 'string' || typeof capture.browser_version !== 'string' || capture.screenshot == null) {
|
|
118
|
+
throw new Error('browser adapter returned an incomplete capture contract');
|
|
119
|
+
}
|
|
120
|
+
const refs = {
|
|
121
|
+
raw_response: 'initial/response.html',
|
|
122
|
+
rendered_dom: `${prefix}/dom.html`,
|
|
123
|
+
rendered_text: `${prefix}/text.txt`,
|
|
124
|
+
accessibility_tree: capture.accessibility_tree == null ? null : `${prefix}/accessibility-tree.txt`,
|
|
125
|
+
screenshot: `${prefix}/final.png`,
|
|
126
|
+
failures: `${prefix}/failures.json`,
|
|
127
|
+
interactions: `${prefix}/steps.json`,
|
|
128
|
+
};
|
|
129
|
+
artifacts[refs.rendered_dom] = capture.dom;
|
|
130
|
+
artifacts[refs.rendered_text] = capture.text;
|
|
131
|
+
if (refs.accessibility_tree) artifacts[refs.accessibility_tree] = capture.accessibility_tree;
|
|
132
|
+
artifacts[refs.screenshot] = capture.screenshot;
|
|
133
|
+
artifacts[refs.failures] = { console_errors: capture.console_errors || [], network_failures: capture.network_failures || [] };
|
|
134
|
+
artifacts[refs.interactions] = capture.steps || [];
|
|
135
|
+
for (const [name, value] of Object.entries(capture.step_screenshots || {})) {
|
|
136
|
+
if (!name.startsWith(`${prefix}/steps/`) || name.includes('..')) throw new Error(`browser adapter returned unsafe artifact path: ${name}`);
|
|
137
|
+
artifacts[name] = value;
|
|
138
|
+
}
|
|
139
|
+
const versionMatches = profile.browser.expected_version == null || profile.browser.expected_version === capture.browser_version;
|
|
140
|
+
const data = {
|
|
141
|
+
plan_id: plan.plan_id, target: plan.target, profile_id: profile.profile_id,
|
|
142
|
+
browser: { ...profile.browser, observed_version: capture.browser_version, version_matches_expectation: versionMatches },
|
|
143
|
+
device: profile.device, javascript_enabled: profile.javascript_enabled, locale: profile.locale,
|
|
144
|
+
consent_state: profile.consent_state, authentication_state: profile.authentication_state,
|
|
145
|
+
final_url: capture.final_url, status: capture.status, dom_hash: sha256(capture.dom), text_hash: sha256(capture.text),
|
|
146
|
+
steps: capture.steps || [], artifact_refs: refs,
|
|
147
|
+
interpretation_boundary: 'Observed differences do not establish semantic, retrieval, citation, ranking, or business impact.',
|
|
148
|
+
};
|
|
149
|
+
const limitations = [...plan.limitations, ...profile.limitations];
|
|
150
|
+
if (!versionMatches) limitations.push('Observed browser version differs from the planned version.');
|
|
151
|
+
observations.push(envelope('browser_journey', data, { method: 'browser', source: `playwright/${profile.browser.engine}`, raw: capture.dom, limitations }));
|
|
152
|
+
} catch (error) {
|
|
153
|
+
incomplete.push(`${profile.profile_id}: ${error.message}`);
|
|
154
|
+
const failureRef = `${prefix}/failure.json`;
|
|
155
|
+
artifacts[failureRef] = { profile_id: profile.profile_id, error: error.message, completed_steps: error.steps || [] };
|
|
156
|
+
for (const [name, value] of Object.entries(error.stepScreenshots || {})) artifacts[name] = value;
|
|
157
|
+
observations.push(envelope('browser_journey', {
|
|
158
|
+
plan_id: plan.plan_id, target: plan.target, profile_id: profile.profile_id, browser: profile.browser,
|
|
159
|
+
device: profile.device, javascript_enabled: profile.javascript_enabled, locale: profile.locale,
|
|
160
|
+
consent_state: profile.consent_state, authentication_state: profile.authentication_state,
|
|
161
|
+
steps: error.steps || [], artifact_refs: { failure: failureRef },
|
|
162
|
+
interpretation_boundary: 'Profile failure does not invalidate successful profiles or establish semantic impact.',
|
|
163
|
+
}, { method: 'browser', source: `playwright/${profile.browser.engine}`, state: 'failed', confidence: 'confirmed', raw: error.message, limitations: [...plan.limitations, ...profile.limitations, error.message] }));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return observationRun(root, 'observe render', plan.target, observations, { rawInputs: { browser_evidence_plan: input.raw }, incomplete, artifacts });
|
|
167
|
+
}
|
|
@@ -29,7 +29,14 @@ async function pdfEvidence(item, file, artifacts) {
|
|
|
29
29
|
|
|
30
30
|
function transcriptEvidence(item, file, artifacts) {
|
|
31
31
|
const raw = fs.readFileSync(file, 'utf8');
|
|
32
|
-
const cues = raw.split(/\r?\n\r?\n/).map((block) => {
|
|
32
|
+
const cues = raw.split(/\r?\n\r?\n/).map((block) => {
|
|
33
|
+
const lines = block.split(/\r?\n/).filter(Boolean);
|
|
34
|
+
const timing = lines.find((line) => line.includes('-->')) || null;
|
|
35
|
+
const fragment = parse(lines.filter((line) => line !== 'WEBVTT' && line !== timing && !/^\d+$/.test(line)).join(' '));
|
|
36
|
+
for (const node of fragment.querySelectorAll('script,style')) node.remove();
|
|
37
|
+
const text = fragment.textContent.replace(/\s+/g, ' ').trim();
|
|
38
|
+
return text ? { timing, text } : null;
|
|
39
|
+
}).filter(Boolean);
|
|
33
40
|
const text = cues.map((cue) => cue.text).join(' ');
|
|
34
41
|
artifacts[`media/${item.media_id}/transcript.txt`] = text;
|
|
35
42
|
return envelope('media_transcript', { media_id: item.media_id, source_url: item.source_url, page_url: item.page_url, claim_ids: item.claim_ids, permission_status: item.permission_status, language: item.language || null, published_at: item.published_at || null, cues, word_count: text ? text.split(/\s+/).length : 0 }, { method: 'owner_import', source: file, raw, limitations: ['Speaker identity, transcription accuracy, timing accuracy, and media parity require human verification.', 'Transcript ingestion does not establish that linked claims are supported.'] });
|