2sagaco48 1.0.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/diagnostics.mjs +141 -0
- package/dist/compute.worker-j5ahIpF-.js +1 -0
- package/dist/graph.worker-DG6iGCB9.js +1 -0
- package/dist/index.html +2314 -0
- package/flatten-guide.md +248 -0
- package/index.html +12 -0
- package/package.json +38 -0
- package/src/App.orig.tsx +204 -0
- package/src/App.orig1.tsx +27 -0
- package/src/App.orig2.tsx +25 -0
- package/src/App.tsx +3 -0
- package/src/BaseApp.tsx +18 -0
- package/src/INTEGRATION_LEDGER.md +22 -0
- package/src/PERSIST_CANARY.orig.txt +1 -0
- package/src/PERSIST_CANARY.txt +1 -0
- package/src/REGRESSION_LEDGER.md +651 -0
- package/src/TEMPLATE_PIPELINE_AUDIT.md +208 -0
- package/src/WILLIAMS_PERSONA_GUIDE.md +62 -0
- package/src/chat/VeritasChat.tsx +69 -0
- package/src/chat/synthesis.ts +31 -0
- package/src/chat/tier.ts +45 -0
- package/src/components/AdaptersPage.orig.tsx +99 -0
- package/src/components/AdaptersPage.tsx +3 -0
- package/src/components/AdversarialPanel.tsx +136 -0
- package/src/components/AnchorBaselinePanel.tsx +75 -0
- package/src/components/AnswerPanel.tsx +60 -0
- package/src/components/ArtifactPanel.tsx +43 -0
- package/src/components/ChatApp.orig.tsx +1582 -0
- package/src/components/ChatApp.orig1.tsx +722 -0
- package/src/components/ChatApp.tsx +95 -0
- package/src/components/ChatAugmentPanels.tsx +73 -0
- package/src/components/CitationLedgerPanel.tsx +313 -0
- package/src/components/ComputeSandboxPanel.tsx +130 -0
- package/src/components/ConfigPanel.tsx +110 -0
- package/src/components/ControlPlanePage.tsx +195 -0
- package/src/components/CreativeTreeLifePage.tsx +234 -0
- package/src/components/CreativeTreeOfLifePanel.tsx +149 -0
- package/src/components/DebugTracePanel.tsx +137 -0
- package/src/components/DeepReasoningTrace.tsx +48 -0
- package/src/components/EntitySheetPanel.tsx +51 -0
- package/src/components/FailureModesPanel.tsx +104 -0
- package/src/components/GBSDashboard.tsx +239 -0
- package/src/components/GraphView.tsx +116 -0
- package/src/components/HUD.tsx +125 -0
- package/src/components/HypothesisPanel.tsx +67 -0
- package/src/components/InnovationGenomeEngine.tsx +792 -0
- package/src/components/InnovationPersonaGuide.tsx +348 -0
- package/src/components/InnovationPersonaPanel.tsx +289 -0
- package/src/components/LiveResourceHUD.tsx +109 -0
- package/src/components/LongWriterPanel.tsx +112 -0
- package/src/components/MainPipelineV10Bridge.tsx +72 -0
- package/src/components/MarkdownLite.tsx +63 -0
- package/src/components/MemoryInspector.tsx +38 -0
- package/src/components/MemoryMonitor.tsx +52 -0
- package/src/components/MemoryStressPanel.tsx +176 -0
- package/src/components/ModulesPage.tsx +171 -0
- package/src/components/PipelineDebugConsole.orig.tsx +1856 -0
- package/src/components/PipelineDebugConsole.tsx +3 -0
- package/src/components/PreFlightHUD.tsx +67 -0
- package/src/components/PrismaFetchTracePanel.tsx +87 -0
- package/src/components/ReportOSPanel.tsx +165 -0
- package/src/components/ResourceEstimatorPage.tsx +327 -0
- package/src/components/RichText.tsx +181 -0
- package/src/components/SagaOsPanel.tsx +605 -0
- package/src/components/SagaOsReference.tsx +421 -0
- package/src/components/SagaOsV2Panel.tsx +594 -0
- package/src/components/SagaStyleTab.tsx +382 -0
- package/src/components/SageBlueprintPanel.tsx +416 -0
- package/src/components/SharedChatInput.tsx +77 -0
- package/src/components/StatFinancePanel.tsx +190 -0
- package/src/components/StylePersonaPanel.tsx +128 -0
- package/src/components/TemplatesPage.tsx +300 -0
- package/src/components/TestPanel.tsx +63 -0
- package/src/components/TraceLog.tsx +89 -0
- package/src/components/V15CalibrationAugment.tsx +1134 -0
- package/src/components/V15CalibrationDialog.tsx +1871 -0
- package/src/components/V15Overlay.orig.tsx +120 -0
- package/src/components/V15Overlay.tsx +4 -0
- package/src/components/V15OverlayWrapper.tsx +362 -0
- package/src/components/V15Toggle.tsx +34 -0
- package/src/console/DiagnosticRecoveryPanel.tsx +358 -0
- package/src/console/EngineeringConsole.tsx +103 -0
- package/src/console/FictionSystemPanels.tsx +882 -0
- package/src/console/InspectorPanels.tsx +254 -0
- package/src/console/ManuscriptGraderPanel.tsx +182 -0
- package/src/console/RecursiveReviserPanel.tsx +245 -0
- package/src/console/SeedControlPanel.tsx +135 -0
- package/src/console/StoryControllerPanel.tsx +443 -0
- package/src/console/engineBridge.ts +258 -0
- package/src/console/gemini.ts +106 -0
- package/src/console/grading.ts +250 -0
- package/src/console/util.ts +45 -0
- package/src/fiction/continuity.ts +372 -0
- package/src/fiction/crash-protection.ts +111 -0
- package/src/fiction/diagnostics.ts +208 -0
- package/src/fiction/diff.ts +74 -0
- package/src/fiction/entity-state-machine.ts +214 -0
- package/src/fiction/nive-engine.ts +404 -0
- package/src/fiction/pipeline-selftest.ts +132 -0
- package/src/fiction/seed-control.ts +193 -0
- package/src/fiction/story-engine.ts +326 -0
- package/src/fiction/types.ts +68 -0
- package/src/flatten-guide.md +379 -0
- package/src/index.css +14 -0
- package/src/index.orig.css +13 -0
- package/src/lib/academic-sources.base.ts +188 -0
- package/src/lib/academic-sources.orig.ts +28 -0
- package/src/lib/academic-sources.ts +10 -0
- package/src/lib/advanced-math.ts +119 -0
- package/src/lib/adversarial-engine.base.ts +409 -0
- package/src/lib/adversarial-engine.ts +82 -0
- package/src/lib/ai-revision.ts +623 -0
- package/src/lib/app-state.tsx +426 -0
- package/src/lib/artifact-registry.ts +97 -0
- package/src/lib/artifacts.ts +180 -0
- package/src/lib/atlas-dr.ts +167 -0
- package/src/lib/browser-mtls.ts +127 -0
- package/src/lib/browser-queue.ts +221 -0
- package/src/lib/browser-search-scraper.base.ts +382 -0
- package/src/lib/browser-search-scraper.orig.ts +90 -0
- package/src/lib/browser-search-scraper.ts +10 -0
- package/src/lib/calc-interceptor.ts +82 -0
- package/src/lib/citation-lane-tap.ts +78 -0
- package/src/lib/citation-ledger-store.ts +369 -0
- package/src/lib/citation-ledger.ts +203 -0
- package/src/lib/compute-sandbox.ts +215 -0
- package/src/lib/compute.worker.ts +153 -0
- package/src/lib/connectors/gemini.ts +120 -0
- package/src/lib/connectors/jina.ts +233 -0
- package/src/lib/connectors/marketdata.ts +51 -0
- package/src/lib/connectors/prismafetch.base.ts +175 -0
- package/src/lib/connectors/prismafetch.ts +1 -0
- package/src/lib/connectors/serpapi.ts +65 -0
- package/src/lib/connectors/wikidata.ts +44 -0
- package/src/lib/constraints.ts +331 -0
- package/src/lib/continuation-detector.base.ts +153 -0
- package/src/lib/continuation-detector.ts +48 -0
- package/src/lib/contradraft.ts +168 -0
- package/src/lib/cors-proxy.ts +120 -0
- package/src/lib/coverage.ts +81 -0
- package/src/lib/debug/architecture-prescription.ts +556 -0
- package/src/lib/debug/covea-repair.ts +543 -0
- package/src/lib/debug/helios-ground.ts +319 -0
- package/src/lib/debug/intent-decomposer.ts +281 -0
- package/src/lib/debug/intent-lattice.ts +349 -0
- package/src/lib/debug/pipeline-diagnosis.ts +1149 -0
- package/src/lib/debug/pipeline-trace-bus.ts +424 -0
- package/src/lib/debug/prompt-forge.ts +678 -0
- package/src/lib/debug/repair-sites.ts +513 -0
- package/src/lib/debug/research-phase.ts +266 -0
- package/src/lib/debug/retrieval-context.ts +76 -0
- package/src/lib/debug/retrieval-hardener.ts +142 -0
- package/src/lib/debug/scraper-debug-runner.ts +576 -0
- package/src/lib/debug/scraper-forensics.ts +418 -0
- package/src/lib/debug/scraper-lane-roadmap.ts +136 -0
- package/src/lib/debug/self-test.ts +415 -0
- package/src/lib/debug/step-attribution.ts +211 -0
- package/src/lib/debug/template-rubric.ts +301 -0
- package/src/lib/debug/unified-innovation.ts +126 -0
- package/src/lib/debug/veritas-hybrid-scraper.ts +335 -0
- package/src/lib/defense-registry.ts +104 -0
- package/src/lib/defenses.ts +162 -0
- package/src/lib/deterministic-citation-ledger.ts +174 -0
- package/src/lib/elo-registry.base.ts +117 -0
- package/src/lib/elo-registry.ts +1 -0
- package/src/lib/entity-resolver.ts +291 -0
- package/src/lib/failure-modes.ts +267 -0
- package/src/lib/feature-registry.ts +69 -0
- package/src/lib/flaw-registry.ts +143 -0
- package/src/lib/flaws/_template.ts +52 -0
- package/src/lib/flaws/builtins.ts +63 -0
- package/src/lib/flaws/finance.ts +169 -0
- package/src/lib/flaws/fixers.ts +10 -0
- package/src/lib/flaws/index.ts +32 -0
- package/src/lib/flaws/legal.ts +157 -0
- package/src/lib/flaws/medical.ts +188 -0
- package/src/lib/flaws/original-defenses-pack.ts +81 -0
- package/src/lib/flaws/sample-declarative-pack.json +38 -0
- package/src/lib/flaws/selftest.ts +201 -0
- package/src/lib/flaws/software-extended.ts +125 -0
- package/src/lib/flaws/software-rn-webgl.ts +182 -0
- package/src/lib/flaws/statistics-advanced.ts +46 -0
- package/src/lib/flaws/statistics.ts +61 -0
- package/src/lib/gbse/config.ts +20 -0
- package/src/lib/gbse/engine.ts +226 -0
- package/src/lib/gbse/graph.ts +118 -0
- package/src/lib/gbse/graph.worker.ts +21 -0
- package/src/lib/gbse/tests.ts +117 -0
- package/src/lib/gbse/tiers.ts +65 -0
- package/src/lib/gbse/types.ts +94 -0
- package/src/lib/innovation-genome-engine-v2.orig.ts +1001 -0
- package/src/lib/innovation-genome-engine-v2.ts +3 -0
- package/src/lib/innovation-genome-engine.orig.ts +330 -0
- package/src/lib/innovation-genome-engine.ts +3 -0
- package/src/lib/innovation-genome-v10.orig.ts +314 -0
- package/src/lib/innovation-genome-v10.ts +2 -0
- package/src/lib/innovation-genome-v3.orig.ts +2015 -0
- package/src/lib/innovation-genome-v3.ts +2 -0
- package/src/lib/innovation-genome-v4.orig.ts +1959 -0
- package/src/lib/innovation-genome-v4.ts +2 -0
- package/src/lib/innovation-genome-v5.orig.ts +1210 -0
- package/src/lib/innovation-genome-v5.ts +2 -0
- package/src/lib/innovation-genome-v7.orig.ts +2549 -0
- package/src/lib/innovation-genome-v7.ts +2 -0
- package/src/lib/innovation-genome-v8.orig.ts +1485 -0
- package/src/lib/innovation-genome-v8.ts +2 -0
- package/src/lib/innovation-genome-v9.orig.ts +643 -0
- package/src/lib/innovation-genome-v9.ts +2 -0
- package/src/lib/jina.base.ts +293 -0
- package/src/lib/jina.ts +1 -0
- package/src/lib/live-telemetry.ts +61 -0
- package/src/lib/longwriter.ts +84 -0
- package/src/lib/manuscript-grade-v2.ts +298 -0
- package/src/lib/memory-governor.ts +173 -0
- package/src/lib/memory-stress-tests.ts +248 -0
- package/src/lib/model-intelligence.ts +48 -0
- package/src/lib/model-rotator.ts +75 -0
- package/src/lib/models.orig.ts +513 -0
- package/src/lib/models.ts +151 -0
- package/src/lib/n-deep.base.ts +592 -0
- package/src/lib/n-deep.ts +51 -0
- package/src/lib/nih-simulator.ts +56 -0
- package/src/lib/nih-vulnerability-fixes.ts +460 -0
- package/src/lib/omega-templates.base.ts +294 -0
- package/src/lib/omega-templates.ts +225 -0
- package/src/lib/omni-nexus.ts +64 -0
- package/src/lib/oracle-adapters.ts +67 -0
- package/src/lib/oracle-registry.ts +163 -0
- package/src/lib/orchestrator.ts +357 -0
- package/src/lib/output-boundary.ts +235 -0
- package/src/lib/overrides/vite-native-chaos-harness.ts +154 -0
- package/src/lib/overrides/vite-native-contract-plane.ts +245 -0
- package/src/lib/overrides/vite-native-doctor-plane.ts +208 -0
- package/src/lib/overrides/vite-native-knowledge-store.ts +267 -0
- package/src/lib/overrides/vite-native-policy-plane.ts +190 -0
- package/src/lib/overrides/vite-native-replay-plane.ts +197 -0
- package/src/lib/overrides/vite-native-runtime-plane.ts +106 -0
- package/src/lib/overrides/vite-native-scraper.ts +848 -0
- package/src/lib/overrides/vite-native-selftest-functional.ts +217 -0
- package/src/lib/overrides/vite-native-selftest.ts +183 -0
- package/src/lib/overrides/vite-native-snapshot-plane.ts +123 -0
- package/src/lib/philosophy-toolkit.ts +75 -0
- package/src/lib/pipeline.orig.ts +659 -0
- package/src/lib/pipeline.ts +42 -0
- package/src/lib/precache.ts +128 -0
- package/src/lib/py-sandbox.ts +205 -0
- package/src/lib/quality-score.ts +96 -0
- package/src/lib/quant-engine.ts +231 -0
- package/src/lib/quant-lib.ts +80 -0
- package/src/lib/reportos.ts +175 -0
- package/src/lib/research-os.ts +306 -0
- package/src/lib/resource-estimator.ts +114 -0
- package/src/lib/rpm-governor.ts +93 -0
- package/src/lib/runtime-estimator.ts +135 -0
- package/src/lib/sage/empty-space-v6.ts +560 -0
- package/src/lib/sage/empty-space-v7.ts +222 -0
- package/src/lib/sage/empty-space.ts +733 -0
- package/src/lib/sage/engine.ts +786 -0
- package/src/lib/sage/index.ts +17 -0
- package/src/lib/sage/judge.ts +246 -0
- package/src/lib/sage/prng.ts +53 -0
- package/src/lib/sage/reality-grounding.ts +380 -0
- package/src/lib/sage/repetition.ts +151 -0
- package/src/lib/sage/saga-os-v2-engines.ts +1870 -0
- package/src/lib/sage/saga-os-v2.ts +274 -0
- package/src/lib/sage/saga-os-v3-core.ts +1491 -0
- package/src/lib/sage/saga-os-v3-release.ts +739 -0
- package/src/lib/sage/saga-os-v4-aureate.ts +1161 -0
- package/src/lib/sage/saga-os-v4.ts +212 -0
- package/src/lib/sage/saga-os-v5.ts +205 -0
- package/src/lib/sage/saga-os-v6.ts +346 -0
- package/src/lib/sage/saga-os-v7.ts +314 -0
- package/src/lib/sage/saga-os-v8.ts +505 -0
- package/src/lib/sage/saga-os.ts +329 -0
- package/src/lib/sage/taxonomy.ts +403 -0
- package/src/lib/sage/unified-roll.ts +524 -0
- package/src/lib/sage/worked-sketches.ts +44 -0
- package/src/lib/sage.ts +166 -0
- package/src/lib/scraper-debug-bus.ts +53 -0
- package/src/lib/scraper-enhanced.orig.ts +153 -0
- package/src/lib/scraper-enhanced.ts +10 -0
- package/src/lib/scraper-hardener.base.orig.ts +155 -0
- package/src/lib/scraper-hardener.base.ts +317 -0
- package/src/lib/scraper-hardener.ts +1 -0
- package/src/lib/scraper-palisade/palisade-adjudicator.orig.ts +419 -0
- package/src/lib/scraper-palisade/palisade-adjudicator.ts +13 -0
- package/src/lib/scraper-vnext/arbiter-omega.orig.ts +688 -0
- package/src/lib/scraper-vnext/arbiter-omega.ts +13 -0
- package/src/lib/scraper-vnext/canonical-portfolio-augments.ts +479 -0
- package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.orig.ts +1529 -0
- package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.ts +13 -0
- package/src/lib/scraper-vnext/conclave-omega.ts +1830 -0
- package/src/lib/scraper-vnext/content-extractor-v2.ts +226 -0
- package/src/lib/scraper-vnext/diagnostics-suite.ts +36 -0
- package/src/lib/scraper-vnext/epistemic-packer.ts +97 -0
- package/src/lib/scraper-vnext/fusion-v2.ts +65 -0
- package/src/lib/scraper-vnext/hydra-reader.orig.ts +1216 -0
- package/src/lib/scraper-vnext/hydra-reader.ts +13 -0
- package/src/lib/scraper-vnext/native-scraper-browser-vnext.orig.ts +91 -0
- package/src/lib/scraper-vnext/native-scraper-browser-vnext.ts +13 -0
- package/src/lib/scraper-vnext/nexus-consensus.orig.ts +905 -0
- package/src/lib/scraper-vnext/nexus-consensus.ts +13 -0
- package/src/lib/scraper-vnext/portfolio-consensus-adjudicator.ts +804 -0
- package/src/lib/scraper-vnext/portfolio-consensus-memory.ts +377 -0
- package/src/lib/scraper-vnext/portfolio-terminal-governor.ts +548 -0
- package/src/lib/scraper-vnext/query-strategist.orig.ts +224 -0
- package/src/lib/scraper-vnext/query-strategist.ts +423 -0
- package/src/lib/scraper-vnext/retrieval-accelerator.ts +708 -0
- package/src/lib/scraper-vnext/retrieval-audit-augments.ts +477 -0
- package/src/lib/scraper-vnext/retrieval-control-plane.ts +904 -0
- package/src/lib/scraper-vnext/retrieval-policy-augments.ts +358 -0
- package/src/lib/scraper-vnext/safe-fetch-v2.ts +157 -0
- package/src/lib/scraper-vnext/sentinel-omega.ts +56 -0
- package/src/lib/scraper-vnext/sentinel-orchestrator.ts +69 -0
- package/src/lib/scraper-vnext/sibyl-oracle.orig.ts +413 -0
- package/src/lib/scraper-vnext/sibyl-oracle.ts +13 -0
- package/src/lib/scraper-vnext/smart-read-v2.ts +55 -0
- package/src/lib/scraper-vnext/spa-rescue-bridge.ts +608 -0
- package/src/lib/scraper-vnext/strata-engine.orig.ts +2666 -0
- package/src/lib/scraper-vnext/strata-engine.ts +13 -0
- package/src/lib/scraper-vnext/structured-source-adapter.orig.ts +369 -0
- package/src/lib/scraper-vnext/structured-source-adapter.ts +34 -0
- package/src/lib/scraper-vnext/terminal-complete.ts +224 -0
- package/src/lib/scraper-vnext/terminal-final.ts +277 -0
- package/src/lib/scraper-vnext/terminal-saturation.ts +345 -0
- package/src/lib/scraper-vnext/terminal-wire.ts +425 -0
- package/src/lib/scraper-vnext/vanguard-titanium.orig.ts +467 -0
- package/src/lib/scraper-vnext/vanguard-titanium.ts +17 -0
- package/src/lib/search-cache.ts +138 -0
- package/src/lib/sloop-runner.base.ts +223 -0
- package/src/lib/sloop-runner.ts +104 -0
- package/src/lib/sloop.ts +114 -0
- package/src/lib/small-model-adapters.ts +82 -0
- package/src/lib/sscp.ts +86 -0
- package/src/lib/system-map.ts +54 -0
- package/src/lib/uni-node.ts +86 -0
- package/src/lib/universal-rigor-guard.ts +433 -0
- package/src/lib/v15-gate-testbed.ts +274 -0
- package/src/lib/v15-gemini.base.ts +89 -0
- package/src/lib/v15-gemini.ts +1 -0
- package/src/lib/v15-grounding.orig.ts +241 -0
- package/src/lib/v15-grounding.ts +79 -0
- package/src/lib/v15-pipeline.base.ts +769 -0
- package/src/lib/v15-pipeline.orig.ts +1154 -0
- package/src/lib/v15-pipeline.ts +674 -0
- package/src/lib/v15-questions.ts +64 -0
- package/src/lib/v15-rate-limiter.orig.ts +91 -0
- package/src/lib/v15-rate-limiter.ts +48 -0
- package/src/lib/v15-state.base.ts +87 -0
- package/src/lib/v15-state.ts +28 -0
- package/src/lib/visual-table-generator.tsx +207 -0
- package/src/lib/wasm-runtime.ts +133 -0
- package/src/lib/williams-persona-guide.ts +265 -0
- package/src/lib/williams-style.base.ts +464 -0
- package/src/lib/williams-style.ts +59 -0
- package/src/lib/worker-pool.ts +96 -0
- package/src/lib/writing-tiers.ts +125 -0
- package/src/main.orig.tsx +10 -0
- package/src/main.tsx +10 -0
- package/src/utils/cn.orig.ts +6 -0
- package/src/utils/cn.ts +6 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +31 -0
- package/unify.mjs +537 -0
- package/vite.config.ts +18 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* canonical-portfolio-augments.ts
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* ADDITIVE audit companion to the GPT-5.6 canonical portfolio orchestrator.
|
|
5
|
+
*
|
|
6
|
+
* DOES NOT MODIFY:
|
|
7
|
+
* - canonical-portfolio-orchestrator.ts
|
|
8
|
+
* - Any downstream canonical engine (terminal-wire, sentinel-orchestrator,
|
|
9
|
+
* vanguard-titanium, sentinel-omega, omni-nexus, structured-source-adapter)
|
|
10
|
+
* - Any canonical primitive (retrieval-accelerator, retrieval-control-plane,
|
|
11
|
+
* retrieval-policy-augments, epistemic-packer, spa-rescue-bridge,
|
|
12
|
+
* arbiter-omega, palisade-adjudicator, conclave-omega)
|
|
13
|
+
*
|
|
14
|
+
* ADDS:
|
|
15
|
+
* 1. Persisted yield memory (IndexedDB, per (domain, laneClass))
|
|
16
|
+
* 2. Lane-independence audit (N Jina lanes = 1 witness)
|
|
17
|
+
* 3. Persisted origin backoff (survives page refresh; RFC 9309 correct)
|
|
18
|
+
* 4. Marginal evidence gain (rolling-window stop condition)
|
|
19
|
+
* 5. Audit-observing wrapper (groundWithAuditedPortfolio) that records
|
|
20
|
+
* yield/backoff from portfolio LaneExecution results after the fact —
|
|
21
|
+
* the portfolio itself is untouched.
|
|
22
|
+
*
|
|
23
|
+
* HONEST LIMITS (documented, not worked around):
|
|
24
|
+
* - OpenAlex requires an API key as of 2026-02-13; caller must pass one.
|
|
25
|
+
* - RFC 9309: 4xx robots.txt permits crawling; 5xx/unreachable → disallow.
|
|
26
|
+
* The canonical policy-augments makeRobotsDecision() must be checked
|
|
27
|
+
* against this; if it fails-open on 5xx it is BUGGED and must be fixed
|
|
28
|
+
* in that file separately (out of scope here).
|
|
29
|
+
* - IndexedDB may be evicted by Safari after 7 days of no interaction;
|
|
30
|
+
* acceptable because only performance memory (not evidence) is stored.
|
|
31
|
+
* - Web Locks require HTTPS secure context.
|
|
32
|
+
* - No Titanium egress lanes enabled by default (ToS risk).
|
|
33
|
+
*
|
|
34
|
+
* BROWSER-ONLY. KEYLESS BASELINE. STATIC-BUILD COMPATIBLE.
|
|
35
|
+
* NOT EXECUTED in this environment.
|
|
36
|
+
* ============================================================================ */
|
|
37
|
+
|
|
38
|
+
import {
|
|
39
|
+
groundWithCanonicalPortfolio,
|
|
40
|
+
runCanonicalPortfolioDiagnostics,
|
|
41
|
+
type PortfolioOptions,
|
|
42
|
+
type PortfolioGroundingResult,
|
|
43
|
+
} from "./canonical-portfolio-orchestrator";
|
|
44
|
+
|
|
45
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
46
|
+
// SECTION 1 — INDEXEDDB PERSISTENCE (fail-open)
|
|
47
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
48
|
+
|
|
49
|
+
const DB_NAME = "canonical-portfolio-audits-v1";
|
|
50
|
+
const DB_VERSION = 1;
|
|
51
|
+
const STORE_YIELD = "yield";
|
|
52
|
+
const STORE_BACKOFF = "backoff";
|
|
53
|
+
|
|
54
|
+
let dbPromise: Promise<IDBDatabase | null> | null = null;
|
|
55
|
+
|
|
56
|
+
function openDb(): Promise<IDBDatabase | null> {
|
|
57
|
+
if (dbPromise) return dbPromise;
|
|
58
|
+
dbPromise = new Promise((resolve) => {
|
|
59
|
+
if (typeof indexedDB === "undefined") { resolve(null); return; }
|
|
60
|
+
try {
|
|
61
|
+
const req = indexedDB.open(DB_NAME, DB_VERSION);
|
|
62
|
+
req.onupgradeneeded = () => {
|
|
63
|
+
const db = req.result;
|
|
64
|
+
if (!db.objectStoreNames.contains(STORE_YIELD)) db.createObjectStore(STORE_YIELD, { keyPath: "key" });
|
|
65
|
+
if (!db.objectStoreNames.contains(STORE_BACKOFF)) db.createObjectStore(STORE_BACKOFF, { keyPath: "origin" });
|
|
66
|
+
};
|
|
67
|
+
req.onsuccess = () => resolve(req.result);
|
|
68
|
+
req.onerror = () => resolve(null);
|
|
69
|
+
req.onblocked = () => resolve(null);
|
|
70
|
+
} catch { resolve(null); }
|
|
71
|
+
});
|
|
72
|
+
return dbPromise;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function idbGet<T>(store: string, key: string): Promise<T | null> {
|
|
76
|
+
const db = await openDb(); if (!db) return null;
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
try {
|
|
79
|
+
const req = db.transaction(store, "readonly").objectStore(store).get(key);
|
|
80
|
+
req.onsuccess = () => resolve(req.result ?? null);
|
|
81
|
+
req.onerror = () => resolve(null);
|
|
82
|
+
} catch { resolve(null); }
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function idbPut(store: string, value: unknown): Promise<void> {
|
|
87
|
+
const db = await openDb(); if (!db) return;
|
|
88
|
+
return new Promise((resolve) => {
|
|
89
|
+
try {
|
|
90
|
+
const req = db.transaction(store, "readwrite").objectStore(store).put(value);
|
|
91
|
+
req.onsuccess = () => resolve();
|
|
92
|
+
req.onerror = () => resolve();
|
|
93
|
+
} catch { resolve(); }
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function idbDelete(store: string, key: string): Promise<void> {
|
|
98
|
+
const db = await openDb(); if (!db) return;
|
|
99
|
+
return new Promise((resolve) => {
|
|
100
|
+
try {
|
|
101
|
+
const req = db.transaction(store, "readwrite").objectStore(store).delete(key);
|
|
102
|
+
req.onsuccess = () => resolve();
|
|
103
|
+
req.onerror = () => resolve();
|
|
104
|
+
} catch { resolve(); }
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function hostOf(url: string): string {
|
|
109
|
+
try { return new URL(url).hostname.toLowerCase().replace(/^www\./, ""); }
|
|
110
|
+
catch { return ""; }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function originOf(url: string): string {
|
|
114
|
+
try { return new URL(url).origin; }
|
|
115
|
+
catch { return ""; }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
119
|
+
// SECTION 2 — YIELD-AWARE QUEUE MEMORY (persisted per domain+laneClass)
|
|
120
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
121
|
+
|
|
122
|
+
export interface YieldRecord {
|
|
123
|
+
key: string;
|
|
124
|
+
successes: number;
|
|
125
|
+
failures: number;
|
|
126
|
+
totalLatencyMs: number;
|
|
127
|
+
lastSeenAt: number;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function yieldKey(url: string, laneClass: string): string {
|
|
131
|
+
return `${hostOf(url)}\u0000${laneClass}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function recordYieldSuccess(
|
|
135
|
+
url: string,
|
|
136
|
+
laneClass: string,
|
|
137
|
+
latencyMs: number,
|
|
138
|
+
): Promise<void> {
|
|
139
|
+
const key = yieldKey(url, laneClass);
|
|
140
|
+
const existing = (await idbGet<YieldRecord>(STORE_YIELD, key)) ?? {
|
|
141
|
+
key, successes: 0, failures: 0, totalLatencyMs: 0, lastSeenAt: 0,
|
|
142
|
+
};
|
|
143
|
+
existing.successes += 1;
|
|
144
|
+
existing.totalLatencyMs += Math.max(0, latencyMs);
|
|
145
|
+
existing.lastSeenAt = Date.now();
|
|
146
|
+
await idbPut(STORE_YIELD, existing);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export async function recordYieldFailure(
|
|
150
|
+
url: string,
|
|
151
|
+
laneClass: string,
|
|
152
|
+
): Promise<void> {
|
|
153
|
+
const key = yieldKey(url, laneClass);
|
|
154
|
+
const existing = (await idbGet<YieldRecord>(STORE_YIELD, key)) ?? {
|
|
155
|
+
key, successes: 0, failures: 0, totalLatencyMs: 0, lastSeenAt: 0,
|
|
156
|
+
};
|
|
157
|
+
existing.failures += 1;
|
|
158
|
+
existing.lastSeenAt = Date.now();
|
|
159
|
+
await idbPut(STORE_YIELD, existing);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Composite yield score.
|
|
164
|
+
* successRate × (1 - latencyPenalty) × recencyDecay
|
|
165
|
+
* latencyPenalty = clamp(meanLatency / 20s, 0, 0.5)
|
|
166
|
+
* recencyDecay = exp(-0.05 × ageDays) — 14-day half-life
|
|
167
|
+
*/
|
|
168
|
+
export async function getYieldScore(
|
|
169
|
+
url: string,
|
|
170
|
+
laneClass: string,
|
|
171
|
+
): Promise<{ score: number; samples: number }> {
|
|
172
|
+
const key = yieldKey(url, laneClass);
|
|
173
|
+
const rec = await idbGet<YieldRecord>(STORE_YIELD, key);
|
|
174
|
+
if (!rec) return { score: 0, samples: 0 };
|
|
175
|
+
const total = rec.successes + rec.failures;
|
|
176
|
+
const successRate = total > 0 ? rec.successes / total : 0;
|
|
177
|
+
const meanLatencyMs = rec.successes > 0 ? rec.totalLatencyMs / rec.successes : 0;
|
|
178
|
+
const ageDays = Math.max(0, (Date.now() - rec.lastSeenAt) / 86_400_000);
|
|
179
|
+
const latencyPenalty = Math.min(0.5, meanLatencyMs / 20_000);
|
|
180
|
+
const recencyDecay = Math.exp(-0.05 * ageDays);
|
|
181
|
+
const score = successRate * (1 - latencyPenalty) * recencyDecay;
|
|
182
|
+
return { score, samples: total };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export async function reorderByYield(
|
|
186
|
+
urls: string[],
|
|
187
|
+
laneClass: string,
|
|
188
|
+
): Promise<string[]> {
|
|
189
|
+
const scored = await Promise.all(
|
|
190
|
+
urls.map(async (url) => {
|
|
191
|
+
const y = await getYieldScore(url, laneClass);
|
|
192
|
+
return { url, combined: 0.5 * (0.5 + 0.5 * y.score) };
|
|
193
|
+
}),
|
|
194
|
+
);
|
|
195
|
+
scored.sort((a, b) => b.combined - a.combined || a.url.localeCompare(b.url));
|
|
196
|
+
return scored.map((s) => s.url);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
200
|
+
// SECTION 3 — PERSISTED ORIGIN BACKOFF (survives page refresh)
|
|
201
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
202
|
+
|
|
203
|
+
export interface BackoffLease {
|
|
204
|
+
origin: string;
|
|
205
|
+
untilMs: number;
|
|
206
|
+
reason: string;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export async function persistBackoff(
|
|
210
|
+
url: string,
|
|
211
|
+
durationMs: number,
|
|
212
|
+
reason = "rate-limit",
|
|
213
|
+
): Promise<void> {
|
|
214
|
+
const origin = originOf(url);
|
|
215
|
+
if (!origin) return;
|
|
216
|
+
const bounded = Math.max(1_000, Math.min(24 * 3_600_000, Math.floor(durationMs)));
|
|
217
|
+
await idbPut(STORE_BACKOFF, { origin, untilMs: Date.now() + bounded, reason });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export async function checkPersistedBackoff(url: string): Promise<number> {
|
|
221
|
+
const origin = originOf(url);
|
|
222
|
+
if (!origin) return 0;
|
|
223
|
+
const lease = await idbGet<BackoffLease>(STORE_BACKOFF, origin);
|
|
224
|
+
if (!lease) return 0;
|
|
225
|
+
const remaining = lease.untilMs - Date.now();
|
|
226
|
+
if (remaining <= 0) {
|
|
227
|
+
idbDelete(STORE_BACKOFF, origin).catch(() => {});
|
|
228
|
+
return 0;
|
|
229
|
+
}
|
|
230
|
+
return remaining;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export async function clearPersistedBackoff(url: string): Promise<void> {
|
|
234
|
+
const origin = originOf(url);
|
|
235
|
+
if (!origin) return;
|
|
236
|
+
await idbDelete(STORE_BACKOFF, origin);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
240
|
+
// SECTION 4 — LANE-INDEPENDENCE AUDIT
|
|
241
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
242
|
+
|
|
243
|
+
export interface LaneIndependenceReport {
|
|
244
|
+
distinctProviders: number;
|
|
245
|
+
effectiveWitnessCount: number;
|
|
246
|
+
providerGroups: Array<{
|
|
247
|
+
providerId: string;
|
|
248
|
+
laneIds: string[];
|
|
249
|
+
witnessWeight: number;
|
|
250
|
+
}>;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function laneProviderId(
|
|
254
|
+
laneClass: string,
|
|
255
|
+
laneId: string,
|
|
256
|
+
sourceUrl?: string,
|
|
257
|
+
): string {
|
|
258
|
+
if (laneClass === "origin-direct") {
|
|
259
|
+
return `direct:${hostOf(sourceUrl ?? laneId)}`;
|
|
260
|
+
}
|
|
261
|
+
if (laneClass === "hosted-renderer") return "jina";
|
|
262
|
+
if (laneClass === "archive") return "wayback";
|
|
263
|
+
if (laneClass.startsWith("relay:")) return laneClass;
|
|
264
|
+
return `unknown:${laneClass}`;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function auditLaneIndependence(
|
|
268
|
+
successfulLanes: Array<{
|
|
269
|
+
laneId: string;
|
|
270
|
+
laneClass: string;
|
|
271
|
+
sourceUrl?: string;
|
|
272
|
+
}>,
|
|
273
|
+
): LaneIndependenceReport {
|
|
274
|
+
const groups = new Map<
|
|
275
|
+
string,
|
|
276
|
+
{ providerId: string; laneIds: string[]; witnessWeight: number }
|
|
277
|
+
>();
|
|
278
|
+
for (const lane of successfulLanes) {
|
|
279
|
+
const pid = laneProviderId(lane.laneClass, lane.laneId, lane.sourceUrl);
|
|
280
|
+
const g = groups.get(pid) ?? { providerId: pid, laneIds: [], witnessWeight: 1 };
|
|
281
|
+
g.laneIds.push(lane.laneId);
|
|
282
|
+
groups.set(pid, g);
|
|
283
|
+
}
|
|
284
|
+
const providerGroups: LaneIndependenceReport["providerGroups"] = [];
|
|
285
|
+
for (const g of groups.values()) {
|
|
286
|
+
g.witnessWeight = 1 / Math.max(1, g.laneIds.length);
|
|
287
|
+
providerGroups.push({ ...g });
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
distinctProviders: groups.size,
|
|
291
|
+
effectiveWitnessCount: groups.size,
|
|
292
|
+
providerGroups,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
297
|
+
// SECTION 5 — MARGINAL EVIDENCE GAIN STOP
|
|
298
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
299
|
+
|
|
300
|
+
export function decideMarginalGain(
|
|
301
|
+
contributions: number[],
|
|
302
|
+
pagesFetched: number,
|
|
303
|
+
opts?: {
|
|
304
|
+
threshold?: number;
|
|
305
|
+
minPages?: number;
|
|
306
|
+
windowSize?: number;
|
|
307
|
+
},
|
|
308
|
+
): { stop: boolean; mean: number; reason: string } {
|
|
309
|
+
const threshold = opts?.threshold ?? 0.05;
|
|
310
|
+
const minPages = opts?.minPages ?? 3;
|
|
311
|
+
const windowSize = Math.max(1, opts?.windowSize ?? 3);
|
|
312
|
+
if (pagesFetched < minPages) {
|
|
313
|
+
return { stop: false, mean: NaN, reason: `below floor (${pagesFetched}/${minPages})` };
|
|
314
|
+
}
|
|
315
|
+
const window = contributions.slice(-windowSize);
|
|
316
|
+
if (window.length === 0) {
|
|
317
|
+
return { stop: false, mean: NaN, reason: "no samples" };
|
|
318
|
+
}
|
|
319
|
+
const mean = window.reduce((s, v) => s + Math.max(0, v), 0) / window.length;
|
|
320
|
+
if (mean < threshold) {
|
|
321
|
+
return { stop: true, mean, reason: `rolling mean ${mean.toFixed(4)} < threshold ${threshold}` };
|
|
322
|
+
}
|
|
323
|
+
return { stop: false, mean, reason: `rolling mean ${mean.toFixed(4)} ≥ threshold ${threshold}` };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
327
|
+
// SECTION 6 — AUDIT-OBSERVING PORTFOLIO WRAPPER
|
|
328
|
+
// Observes portfolio lane execution results AFTER the fact and records
|
|
329
|
+
// yield/backoff signals. Does not modify the orchestrator or any engine.
|
|
330
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
331
|
+
|
|
332
|
+
export interface AuditedPortfolioResult extends PortfolioGroundingResult {
|
|
333
|
+
laneIndependence: LaneIndependenceReport;
|
|
334
|
+
yieldRecordings: Array<{ url: string; laneClass: string; recorded: "success" | "failure" }>;
|
|
335
|
+
backoffRecordings: Array<{ origin: string; ms: number }>;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* groundWithAuditedPortfolio — runs the canonical portfolio, then observes
|
|
340
|
+
* lane execution results to record yield memory and backoff leases.
|
|
341
|
+
*
|
|
342
|
+
* Observation logic:
|
|
343
|
+
* - Each fulfilled lane result gets a yieldSuccess recording (its provider
|
|
344
|
+
* is the top-level portfolio lane, latency = elapsedMs).
|
|
345
|
+
* - Each rejected/timed-out lane with a 429/503/rate-limit message gets a
|
|
346
|
+
* backoff persistence (60s default).
|
|
347
|
+
* - Each rejected/timed-out lane gets a yieldFailure recording.
|
|
348
|
+
* - Independence audit is computed from lane IDs and classes.
|
|
349
|
+
*
|
|
350
|
+
* The portfolio itself makes all decisions; this wrapper only records.
|
|
351
|
+
*/
|
|
352
|
+
export async function groundWithAuditedPortfolio(
|
|
353
|
+
question: string,
|
|
354
|
+
options: PortfolioOptions = {},
|
|
355
|
+
): Promise<AuditedPortfolioResult> {
|
|
356
|
+
const portfolioResult = await groundWithCanonicalPortfolio(question, options);
|
|
357
|
+
|
|
358
|
+
const yieldRecordings: Array<{ url: string; laneClass: string; recorded: "success" | "failure" }> = [];
|
|
359
|
+
const backoffRecordings: Array<{ origin: string; ms: number }> = [];
|
|
360
|
+
|
|
361
|
+
// Observe lane executions and record yield/backoff.
|
|
362
|
+
// Portfolio lane IDs map to synthetic laneClass "portfolio:{id}".
|
|
363
|
+
for (const lane of portfolioResult.laneResults) {
|
|
364
|
+
const laneClass = `portfolio:${lane.id}`;
|
|
365
|
+
// Synthetic "url" is the lane ID itself (portfolio lanes aren't URL-scoped;
|
|
366
|
+
// we use the lane ID as a stable key for the yield table).
|
|
367
|
+
const syntheticUrl = `https://portfolio.local/${lane.id}`;
|
|
368
|
+
|
|
369
|
+
if (lane.status === "fulfilled") {
|
|
370
|
+
await recordYieldSuccess(syntheticUrl, laneClass, lane.elapsedMs);
|
|
371
|
+
yieldRecordings.push({ url: syntheticUrl, laneClass, recorded: "success" });
|
|
372
|
+
} else {
|
|
373
|
+
await recordYieldFailure(syntheticUrl, laneClass);
|
|
374
|
+
yieldRecordings.push({ url: syntheticUrl, laneClass, recorded: "failure" });
|
|
375
|
+
|
|
376
|
+
// Detect rate-limit-shaped errors and record backoff at the origin level.
|
|
377
|
+
// Since portfolio lanes are synthetic, we can only record backoff at
|
|
378
|
+
// portfolio.local — this is honest: the real host-level backoff must
|
|
379
|
+
// be recorded by the individual engines themselves, not the orchestrator.
|
|
380
|
+
if (lane.error && /429|503|rate.?limit|too many requests/i.test(lane.error)) {
|
|
381
|
+
await persistBackoff(syntheticUrl, 60_000, lane.error.slice(0, 80));
|
|
382
|
+
backoffRecordings.push({ origin: originOf(syntheticUrl), ms: 60_000 });
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Independence audit over the portfolio lane executions.
|
|
388
|
+
// Each portfolio lane is treated as its own provider group (they are
|
|
389
|
+
// architecturally independent engines by construction).
|
|
390
|
+
const successfulLanes = portfolioResult.laneResults
|
|
391
|
+
.filter((l) => l.status === "fulfilled")
|
|
392
|
+
.map((l) => ({
|
|
393
|
+
laneId: l.id,
|
|
394
|
+
laneClass: `portfolio:${l.id}`,
|
|
395
|
+
sourceUrl: `https://portfolio.local/${l.id}`,
|
|
396
|
+
}));
|
|
397
|
+
|
|
398
|
+
const laneIndependence = auditLaneIndependence(successfulLanes);
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
...portfolioResult,
|
|
402
|
+
laneIndependence,
|
|
403
|
+
yieldRecordings,
|
|
404
|
+
backoffRecordings,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
409
|
+
// SECTION 7 — DIAGNOSTICS
|
|
410
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
411
|
+
|
|
412
|
+
export async function runCanonicalPortfolioAugmentDiagnostics(): Promise<{
|
|
413
|
+
ok: boolean;
|
|
414
|
+
checks: Array<{ id: string; passed: boolean; detail: string }>;
|
|
415
|
+
}> {
|
|
416
|
+
const checks: Array<{ id: string; passed: boolean; detail: string }> = [];
|
|
417
|
+
const add = (id: string, passed: boolean, detail: string) => checks.push({ id, passed, detail });
|
|
418
|
+
|
|
419
|
+
// 1. Yield memory: record → retrieve
|
|
420
|
+
const testUrl = `https://yield-test-${Date.now()}.example.com/a`;
|
|
421
|
+
await recordYieldSuccess(testUrl, "origin-direct", 450);
|
|
422
|
+
await recordYieldSuccess(testUrl, "origin-direct", 550);
|
|
423
|
+
await recordYieldFailure(testUrl, "origin-direct");
|
|
424
|
+
const ys = await getYieldScore(testUrl, "origin-direct");
|
|
425
|
+
add("yield-record-samples", ys.samples === 3, `samples=${ys.samples}`);
|
|
426
|
+
add("yield-score-in-range", ys.score >= 0 && ys.score <= 1, `score=${ys.score.toFixed(3)}`);
|
|
427
|
+
add("yield-unknown-zero", (await getYieldScore("https://never-seen.example.com/x", "jina")).score === 0, "zero for unseen");
|
|
428
|
+
|
|
429
|
+
// 2. Reorder by yield: known-good URL floats
|
|
430
|
+
const reordered = await reorderByYield([testUrl, "https://unknown-987.example.com/z"], "origin-direct");
|
|
431
|
+
add("reorder-two-urls", reordered.length === 2, `n=${reordered.length}`);
|
|
432
|
+
|
|
433
|
+
// 3. Persisted backoff: set → check → clear
|
|
434
|
+
const backoffUrl = `https://backoff-test-${Date.now()}.example.com/x`;
|
|
435
|
+
await persistBackoff(backoffUrl, 5_000, "test-429");
|
|
436
|
+
const rem1 = await checkPersistedBackoff(backoffUrl);
|
|
437
|
+
add("backoff-persist", rem1 > 0 && rem1 <= 5_000, `remaining=${rem1}`);
|
|
438
|
+
await clearPersistedBackoff(backoffUrl);
|
|
439
|
+
const rem2 = await checkPersistedBackoff(backoffUrl);
|
|
440
|
+
add("backoff-clear", rem2 === 0, `remaining-after-clear=${rem2}`);
|
|
441
|
+
add("backoff-unseen-zero", (await checkPersistedBackoff("https://no-backoff.example.com/x")) === 0, "zero for unseen");
|
|
442
|
+
|
|
443
|
+
// 4. Lane independence audit: two Jina lanes → one witness
|
|
444
|
+
const laneRep = auditLaneIndependence([
|
|
445
|
+
{ laneId: "jina-1", laneClass: "hosted-renderer" },
|
|
446
|
+
{ laneId: "jina-2", laneClass: "hosted-renderer" },
|
|
447
|
+
{ laneId: "direct", laneClass: "origin-direct", sourceUrl: "https://a.example.com/x" },
|
|
448
|
+
{ laneId: "wayback", laneClass: "archive" },
|
|
449
|
+
]);
|
|
450
|
+
const jinaGroup = laneRep.providerGroups.find((g) => g.providerId === "jina");
|
|
451
|
+
add("lane-jina-collapses", jinaGroup?.laneIds.length === 2 && jinaGroup?.witnessWeight === 0.5, `jina.weight=${jinaGroup?.witnessWeight}`);
|
|
452
|
+
add("lane-effective-count-3", laneRep.effectiveWitnessCount === 3, `effective=${laneRep.effectiveWitnessCount}`);
|
|
453
|
+
|
|
454
|
+
// 5. Provider ID mapping
|
|
455
|
+
add("provider-jina", laneProviderId("hosted-renderer", "x") === "jina", laneProviderId("hosted-renderer", "x"));
|
|
456
|
+
add("provider-archive", laneProviderId("archive", "wb") === "wayback", laneProviderId("archive", "wb"));
|
|
457
|
+
add("provider-relay", laneProviderId("relay:corsproxy.io", "l1") === "relay:corsproxy.io", laneProviderId("relay:corsproxy.io", "l1"));
|
|
458
|
+
|
|
459
|
+
// 6. Marginal gain: high yields continue
|
|
460
|
+
const mg1 = decideMarginalGain([0.4, 0.5, 0.3], 5, { threshold: 0.1 });
|
|
461
|
+
add("marginal-continue-high", !mg1.stop, mg1.reason);
|
|
462
|
+
|
|
463
|
+
// 7. Marginal gain: low yields stop after floor
|
|
464
|
+
const mg2 = decideMarginalGain([0.02, 0.01, 0.03], 5, { threshold: 0.1 });
|
|
465
|
+
add("marginal-stop-low", mg2.stop, mg2.reason);
|
|
466
|
+
|
|
467
|
+
// 8. Marginal gain: below page floor always continues
|
|
468
|
+
const mg3 = decideMarginalGain([0.001], 1, { threshold: 0.1, minPages: 3 });
|
|
469
|
+
add("marginal-continue-below-floor", !mg3.stop, mg3.reason);
|
|
470
|
+
|
|
471
|
+
// 9. hostOf normalization
|
|
472
|
+
add("host-strips-www", hostOf("https://www.Example.COM/x") === "example.com", hostOf("https://www.Example.COM/x"));
|
|
473
|
+
add("origin-of-strips-path", originOf("https://x.example.com/deep/path?q=1") === "https://x.example.com", originOf("https://x.example.com/deep/path?q=1"));
|
|
474
|
+
|
|
475
|
+
return { ok: checks.every((c) => c.passed), checks };
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Re-export the canonical portfolio's own diagnostics for one-stop testing
|
|
479
|
+
export { runCanonicalPortfolioDiagnostics };
|