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,477 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* retrieval-audit-augments.ts
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* ADDITIVE FINAL — closes four blindspots that canonical retrieval-control-plane,
|
|
5
|
+
* retrieval-policy-augments, structured-source-adapter, and epistemic-packer do
|
|
6
|
+
* NOT address:
|
|
7
|
+
*
|
|
8
|
+
* 1. YIELD-AWARE QUEUE MEMORY (persisted across page refreshes via IndexedDB)
|
|
9
|
+
* Per (domain, laneClass) success-rate + p50 latency memory. Used to
|
|
10
|
+
* reorder the crawl queue so historically-yielding hosts get first slots
|
|
11
|
+
* on subsequent runs, not just the current session.
|
|
12
|
+
*
|
|
13
|
+
* 2. LANE-INDEPENDENCE AUDIT
|
|
14
|
+
* When three lanes are all "hosted-renderer" via the same provider (e.g.,
|
|
15
|
+
* three Jina endpoints), they are ONE witness for trust purposes, not
|
|
16
|
+
* three. Audits provenance across sources and collapses shared-infra
|
|
17
|
+
* groups. Emits a demotion signal for the adjudicator.
|
|
18
|
+
*
|
|
19
|
+
* 3. PERSISTED ORIGIN BACKOFF (survives page refresh)
|
|
20
|
+
* Origin-scoped 429/503 backoff state persisted to IndexedDB with expiry.
|
|
21
|
+
* Prevents refresh-and-hammer patterns that trigger WAF escalation.
|
|
22
|
+
*
|
|
23
|
+
* 4. MARGINAL EVIDENCE GAIN STOP
|
|
24
|
+
* Evaluates whether the next page's expected value (domain priority ×
|
|
25
|
+
* historical yield) exceeds a minimum threshold. Stops the crawl early
|
|
26
|
+
* when marginal gain converges below the fetch-cost floor.
|
|
27
|
+
*
|
|
28
|
+
* ADDITIVE ONLY. No canonical file modified. Read-only imports.
|
|
29
|
+
* All persisted state is optional (IndexedDB may be blocked in private mode);
|
|
30
|
+
* every function fail-opens to the current in-memory behavior.
|
|
31
|
+
*
|
|
32
|
+
* BROWSER-ONLY. KEYLESS. HAND-TRACEABLE.
|
|
33
|
+
* NOT EXECUTED in this environment.
|
|
34
|
+
* ============================================================================ */
|
|
35
|
+
|
|
36
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
37
|
+
// TYPES
|
|
38
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
39
|
+
|
|
40
|
+
export interface YieldRecord {
|
|
41
|
+
domainLane: string; // `${domain}\u0000${laneClass}`
|
|
42
|
+
successes: number;
|
|
43
|
+
failures: number;
|
|
44
|
+
totalLatencyMs: number;
|
|
45
|
+
lastSeenAt: number; // epoch ms
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface YieldScore {
|
|
49
|
+
domain: string;
|
|
50
|
+
laneClass: string;
|
|
51
|
+
successRate: number; // successes / (successes + failures); 0 if unknown
|
|
52
|
+
meanLatencyMs: number; // NaN-safe; 0 if unknown
|
|
53
|
+
sampleCount: number;
|
|
54
|
+
score: number; // composite; higher = better
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface BackoffLease {
|
|
58
|
+
origin: string;
|
|
59
|
+
untilMs: number;
|
|
60
|
+
reason: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface LaneIndependenceGroup {
|
|
64
|
+
providerId: string; // "jina", "corsproxy.io", "wayback", direct-origin-hostname
|
|
65
|
+
laneIds: string[];
|
|
66
|
+
witnessWeight: number; // 1/N when N lanes collapse to one witness
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface MarginalGainDecision {
|
|
70
|
+
shouldContinue: boolean;
|
|
71
|
+
reason: string;
|
|
72
|
+
expectedYield: number;
|
|
73
|
+
threshold: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
77
|
+
// IndexedDB PERSISTENCE (fail-open)
|
|
78
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
79
|
+
|
|
80
|
+
const DB_NAME = "retrieval-audit-augments";
|
|
81
|
+
const DB_VERSION = 1;
|
|
82
|
+
const STORE_YIELD = "yield";
|
|
83
|
+
const STORE_BACKOFF = "backoff";
|
|
84
|
+
|
|
85
|
+
let dbPromise: Promise<IDBDatabase | null> | null = null;
|
|
86
|
+
|
|
87
|
+
function openDb(): Promise<IDBDatabase | null> {
|
|
88
|
+
if (dbPromise) return dbPromise;
|
|
89
|
+
dbPromise = new Promise((resolve) => {
|
|
90
|
+
if (typeof indexedDB === "undefined") { resolve(null); return; }
|
|
91
|
+
try {
|
|
92
|
+
const req = indexedDB.open(DB_NAME, DB_VERSION);
|
|
93
|
+
req.onupgradeneeded = () => {
|
|
94
|
+
const db = req.result;
|
|
95
|
+
if (!db.objectStoreNames.contains(STORE_YIELD)) {
|
|
96
|
+
db.createObjectStore(STORE_YIELD, { keyPath: "domainLane" });
|
|
97
|
+
}
|
|
98
|
+
if (!db.objectStoreNames.contains(STORE_BACKOFF)) {
|
|
99
|
+
db.createObjectStore(STORE_BACKOFF, { keyPath: "origin" });
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
req.onsuccess = () => resolve(req.result);
|
|
103
|
+
req.onerror = () => resolve(null);
|
|
104
|
+
req.onblocked = () => resolve(null);
|
|
105
|
+
} catch { resolve(null); }
|
|
106
|
+
});
|
|
107
|
+
return dbPromise;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function idbGet<T>(store: string, key: string): Promise<T | null> {
|
|
111
|
+
const db = await openDb();
|
|
112
|
+
if (!db) return null;
|
|
113
|
+
return new Promise((resolve) => {
|
|
114
|
+
try {
|
|
115
|
+
const tx = db.transaction(store, "readonly");
|
|
116
|
+
const req = tx.objectStore(store).get(key);
|
|
117
|
+
req.onsuccess = () => resolve(req.result ?? null);
|
|
118
|
+
req.onerror = () => resolve(null);
|
|
119
|
+
} catch { resolve(null); }
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function idbPut(store: string, value: unknown): Promise<void> {
|
|
124
|
+
const db = await openDb();
|
|
125
|
+
if (!db) return;
|
|
126
|
+
return new Promise((resolve) => {
|
|
127
|
+
try {
|
|
128
|
+
const tx = db.transaction(store, "readwrite");
|
|
129
|
+
const req = tx.objectStore(store).put(value);
|
|
130
|
+
req.onsuccess = () => resolve();
|
|
131
|
+
req.onerror = () => resolve();
|
|
132
|
+
} catch { resolve(); }
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function idbDelete(store: string, key: string): Promise<void> {
|
|
137
|
+
const db = await openDb();
|
|
138
|
+
if (!db) return;
|
|
139
|
+
return new Promise((resolve) => {
|
|
140
|
+
try {
|
|
141
|
+
const tx = db.transaction(store, "readwrite");
|
|
142
|
+
const req = tx.objectStore(store).delete(key);
|
|
143
|
+
req.onsuccess = () => resolve();
|
|
144
|
+
req.onerror = () => resolve();
|
|
145
|
+
} catch { resolve(); }
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
150
|
+
// 1. YIELD-AWARE QUEUE MEMORY
|
|
151
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
152
|
+
|
|
153
|
+
function hostOf(url: string): string {
|
|
154
|
+
try { return new URL(url).hostname.toLowerCase().replace(/^www\./, ""); }
|
|
155
|
+
catch { return ""; }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function yieldKey(domain: string, laneClass: string): string {
|
|
159
|
+
return `${domain}\u0000${laneClass}`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Record a successful retrieval for future queue prioritization. */
|
|
163
|
+
export async function recordYieldSuccess(
|
|
164
|
+
url: string,
|
|
165
|
+
laneClass: string,
|
|
166
|
+
latencyMs: number,
|
|
167
|
+
): Promise<void> {
|
|
168
|
+
const domain = hostOf(url);
|
|
169
|
+
if (!domain) return;
|
|
170
|
+
const key = yieldKey(domain, laneClass);
|
|
171
|
+
const existing = (await idbGet<YieldRecord>(STORE_YIELD, key)) ?? {
|
|
172
|
+
domainLane: key,
|
|
173
|
+
successes: 0,
|
|
174
|
+
failures: 0,
|
|
175
|
+
totalLatencyMs: 0,
|
|
176
|
+
lastSeenAt: 0,
|
|
177
|
+
};
|
|
178
|
+
existing.successes += 1;
|
|
179
|
+
existing.totalLatencyMs += Math.max(0, latencyMs);
|
|
180
|
+
existing.lastSeenAt = Date.now();
|
|
181
|
+
await idbPut(STORE_YIELD, existing);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Record a failed retrieval for future queue prioritization. */
|
|
185
|
+
export async function recordYieldFailure(
|
|
186
|
+
url: string,
|
|
187
|
+
laneClass: string,
|
|
188
|
+
): Promise<void> {
|
|
189
|
+
const domain = hostOf(url);
|
|
190
|
+
if (!domain) return;
|
|
191
|
+
const key = yieldKey(domain, laneClass);
|
|
192
|
+
const existing = (await idbGet<YieldRecord>(STORE_YIELD, key)) ?? {
|
|
193
|
+
domainLane: key,
|
|
194
|
+
successes: 0,
|
|
195
|
+
failures: 0,
|
|
196
|
+
totalLatencyMs: 0,
|
|
197
|
+
lastSeenAt: 0,
|
|
198
|
+
};
|
|
199
|
+
existing.failures += 1;
|
|
200
|
+
existing.lastSeenAt = Date.now();
|
|
201
|
+
await idbPut(STORE_YIELD, existing);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Look up historical yield for a (domain, laneClass) pair. */
|
|
205
|
+
export async function getYieldScore(url: string, laneClass: string): Promise<YieldScore> {
|
|
206
|
+
const domain = hostOf(url);
|
|
207
|
+
const key = yieldKey(domain, laneClass);
|
|
208
|
+
const rec = await idbGet<YieldRecord>(STORE_YIELD, key);
|
|
209
|
+
if (!rec) {
|
|
210
|
+
return { domain, laneClass, successRate: 0, meanLatencyMs: 0, sampleCount: 0, score: 0 };
|
|
211
|
+
}
|
|
212
|
+
const total = rec.successes + rec.failures;
|
|
213
|
+
const successRate = total > 0 ? rec.successes / total : 0;
|
|
214
|
+
const meanLatencyMs = rec.successes > 0 ? rec.totalLatencyMs / rec.successes : 0;
|
|
215
|
+
// Composite: high success rate + low latency + recency
|
|
216
|
+
const recencyDays = Math.max(0, (Date.now() - rec.lastSeenAt) / 86_400_000);
|
|
217
|
+
const recencyDecay = Math.exp(-0.05 * recencyDays); // half-life ~14 days
|
|
218
|
+
const latencyPenalty = meanLatencyMs > 0 ? Math.min(0.5, meanLatencyMs / 20_000) : 0.3;
|
|
219
|
+
const score = successRate * (1 - latencyPenalty) * recencyDecay;
|
|
220
|
+
return { domain, laneClass, successRate, meanLatencyMs, sampleCount: total, score };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Reorder a candidate URL list by (base priority × historical yield).
|
|
225
|
+
* Untouched URLs keep their input order; known-good URLs float to the top.
|
|
226
|
+
*/
|
|
227
|
+
export async function reorderByYield<T extends { url: string; priority?: number }>(
|
|
228
|
+
candidates: T[],
|
|
229
|
+
laneClass: string,
|
|
230
|
+
): Promise<T[]> {
|
|
231
|
+
const scored = await Promise.all(
|
|
232
|
+
candidates.map(async (c) => {
|
|
233
|
+
const y = await getYieldScore(c.url, laneClass);
|
|
234
|
+
const base = c.priority ?? 0.5;
|
|
235
|
+
const combined = base * (0.5 + 0.5 * y.score); // yield doubles the score at max
|
|
236
|
+
return { c, combined };
|
|
237
|
+
}),
|
|
238
|
+
);
|
|
239
|
+
scored.sort((a, b) => b.combined - a.combined);
|
|
240
|
+
return scored.map((s) => s.c);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
244
|
+
// 2. LANE-INDEPENDENCE AUDIT
|
|
245
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Extract the infrastructure provider identifier for a lane class.
|
|
249
|
+
* Two lanes with the same providerId are NOT independent witnesses.
|
|
250
|
+
*/
|
|
251
|
+
export function laneProviderId(laneClass: string, laneId?: string): string {
|
|
252
|
+
if (laneClass === "origin-direct") {
|
|
253
|
+
// Direct lanes: provider = target hostname (each origin is unique)
|
|
254
|
+
return `direct:${laneId ?? "unknown"}`;
|
|
255
|
+
}
|
|
256
|
+
if (laneClass === "hosted-renderer") return "jina"; // r.jina.ai is the only free hosted renderer
|
|
257
|
+
if (laneClass === "archive") return "wayback";
|
|
258
|
+
if (laneClass.startsWith("relay:")) return laneClass; // relay:corsproxy.io, relay:allorigins, etc.
|
|
259
|
+
return `unknown:${laneClass}`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Collapse a set of successful lanes into independent-provider groups.
|
|
264
|
+
* Witness weight = 1/N when N lanes share one provider; downstream trust
|
|
265
|
+
* math should multiply witness contributions by this weight.
|
|
266
|
+
*/
|
|
267
|
+
export function auditLaneIndependence(
|
|
268
|
+
successfulLanes: Array<{ laneId: string; laneClass: string; sourceUrl?: string }>,
|
|
269
|
+
): LaneIndependenceGroup[] {
|
|
270
|
+
const groups = new Map<string, LaneIndependenceGroup>();
|
|
271
|
+
for (const lane of successfulLanes) {
|
|
272
|
+
const providerId = laneProviderId(
|
|
273
|
+
lane.laneClass,
|
|
274
|
+
lane.laneClass === "origin-direct" ? hostOf(lane.sourceUrl ?? "") : lane.laneId,
|
|
275
|
+
);
|
|
276
|
+
const g = groups.get(providerId) ?? { providerId, laneIds: [], witnessWeight: 1 };
|
|
277
|
+
g.laneIds.push(lane.laneId);
|
|
278
|
+
groups.set(providerId, g);
|
|
279
|
+
}
|
|
280
|
+
// Assign witness weight: 1/N per group
|
|
281
|
+
for (const g of groups.values()) g.witnessWeight = 1 / Math.max(1, g.laneIds.length);
|
|
282
|
+
return Array.from(groups.values()).sort((a, b) => a.providerId.localeCompare(b.providerId));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Effective independent witness count.
|
|
287
|
+
* Three "hosted-renderer" lanes all via Jina → 1 witness, not 3.
|
|
288
|
+
*/
|
|
289
|
+
export function effectiveWitnessCount(
|
|
290
|
+
successfulLanes: Array<{ laneId: string; laneClass: string; sourceUrl?: string }>,
|
|
291
|
+
): number {
|
|
292
|
+
const groups = auditLaneIndependence(successfulLanes);
|
|
293
|
+
return groups.length; // one witness per distinct provider
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
297
|
+
// 3. PERSISTED ORIGIN BACKOFF (survives page refresh)
|
|
298
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
299
|
+
|
|
300
|
+
/** Persist a 429/503 backoff lease that survives page refresh. */
|
|
301
|
+
export async function persistBackoff(
|
|
302
|
+
targetUrl: string,
|
|
303
|
+
durationMs: number,
|
|
304
|
+
reason: string,
|
|
305
|
+
): Promise<void> {
|
|
306
|
+
let origin: string;
|
|
307
|
+
try { origin = new URL(targetUrl).origin; } catch { return; }
|
|
308
|
+
const clamped = Math.max(1_000, Math.min(24 * 60 * 60_000, Math.floor(durationMs)));
|
|
309
|
+
const lease: BackoffLease = {
|
|
310
|
+
origin,
|
|
311
|
+
untilMs: Date.now() + clamped,
|
|
312
|
+
reason,
|
|
313
|
+
};
|
|
314
|
+
await idbPut(STORE_BACKOFF, lease);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Check whether an origin is currently backed off from a prior session.
|
|
319
|
+
* Returns 0 if no backoff active; otherwise milliseconds remaining.
|
|
320
|
+
*/
|
|
321
|
+
export async function checkPersistedBackoff(targetUrl: string): Promise<number> {
|
|
322
|
+
let origin: string;
|
|
323
|
+
try { origin = new URL(targetUrl).origin; } catch { return 0; }
|
|
324
|
+
const lease = await idbGet<BackoffLease>(STORE_BACKOFF, origin);
|
|
325
|
+
if (!lease) return 0;
|
|
326
|
+
const remaining = lease.untilMs - Date.now();
|
|
327
|
+
if (remaining <= 0) {
|
|
328
|
+
// Expired — clean up
|
|
329
|
+
idbDelete(STORE_BACKOFF, origin).catch(() => { /* ignore */ });
|
|
330
|
+
return 0;
|
|
331
|
+
}
|
|
332
|
+
return remaining;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Clear backoff for an origin (e.g., after a successful retry). */
|
|
336
|
+
export async function clearPersistedBackoff(targetUrl: string): Promise<void> {
|
|
337
|
+
let origin: string;
|
|
338
|
+
try { origin = new URL(targetUrl).origin; } catch { return; }
|
|
339
|
+
await idbDelete(STORE_BACKOFF, origin);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
343
|
+
// 4. MARGINAL EVIDENCE GAIN STOP
|
|
344
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
345
|
+
|
|
346
|
+
export interface MarginalGainOptions {
|
|
347
|
+
/** Minimum expected contribution to keep crawling. Default 0.05. */
|
|
348
|
+
minExpectedYield?: number;
|
|
349
|
+
/** Minimum absolute pages fetched before allowing early stop. Default 3. */
|
|
350
|
+
minPagesFetched?: number;
|
|
351
|
+
/** Convergence window: last N pages' contribution rolling average. Default 3. */
|
|
352
|
+
windowSize?: number;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Decide whether to continue crawling.
|
|
357
|
+
* Stops early when the last N pages have consistently produced below-threshold
|
|
358
|
+
* value contributions AND at least minPagesFetched pages have been read.
|
|
359
|
+
*
|
|
360
|
+
* Hand-traceable: rolling mean over last `windowSize` values from
|
|
361
|
+
* `recentValueContributions` (largest = most recent). If that mean is below
|
|
362
|
+
* `minExpectedYield`, and pages fetched ≥ `minPagesFetched`, stop.
|
|
363
|
+
*/
|
|
364
|
+
export function decideMarginalGain(
|
|
365
|
+
recentValueContributions: number[], // e.g., support-mass boost per page
|
|
366
|
+
pagesFetched: number,
|
|
367
|
+
opts?: MarginalGainOptions,
|
|
368
|
+
): MarginalGainDecision {
|
|
369
|
+
const minYield = opts?.minExpectedYield ?? 0.05;
|
|
370
|
+
const minPages = opts?.minPagesFetched ?? 3;
|
|
371
|
+
const win = Math.max(1, opts?.windowSize ?? 3);
|
|
372
|
+
|
|
373
|
+
if (pagesFetched < minPages) {
|
|
374
|
+
return {
|
|
375
|
+
shouldContinue: true,
|
|
376
|
+
reason: `only ${pagesFetched}/${minPages} pages fetched — below floor`,
|
|
377
|
+
expectedYield: NaN,
|
|
378
|
+
threshold: minYield,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const window = recentValueContributions.slice(-win);
|
|
383
|
+
if (window.length === 0) {
|
|
384
|
+
return { shouldContinue: true, reason: "no yield samples", expectedYield: NaN, threshold: minYield };
|
|
385
|
+
}
|
|
386
|
+
const mean = window.reduce((s, v) => s + Math.max(0, v), 0) / window.length;
|
|
387
|
+
if (mean < minYield) {
|
|
388
|
+
return {
|
|
389
|
+
shouldContinue: false,
|
|
390
|
+
reason: `rolling mean ${mean.toFixed(4)} < threshold ${minYield}`,
|
|
391
|
+
expectedYield: mean,
|
|
392
|
+
threshold: minYield,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
shouldContinue: true,
|
|
397
|
+
reason: `rolling mean ${mean.toFixed(4)} ≥ threshold ${minYield}`,
|
|
398
|
+
expectedYield: mean,
|
|
399
|
+
threshold: minYield,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
404
|
+
// DIAGNOSTICS
|
|
405
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
406
|
+
|
|
407
|
+
export async function runAuditAugmentDiagnostics(): Promise<{
|
|
408
|
+
ok: boolean;
|
|
409
|
+
checks: Array<{ id: string; passed: boolean; detail: string }>;
|
|
410
|
+
}> {
|
|
411
|
+
const checks: Array<{ id: string; passed: boolean; detail: string }> = [];
|
|
412
|
+
const add = (id: string, p: boolean, d: string) => checks.push({ id, passed: p, detail: d });
|
|
413
|
+
|
|
414
|
+
// 1. Yield memory: record + retrieve
|
|
415
|
+
await recordYieldSuccess("https://arxiv.org/abs/123", "origin-direct", 500);
|
|
416
|
+
await recordYieldSuccess("https://arxiv.org/abs/456", "origin-direct", 700);
|
|
417
|
+
await recordYieldFailure("https://arxiv.org/abs/999", "origin-direct");
|
|
418
|
+
const y1 = await getYieldScore("https://arxiv.org/abs/anything", "origin-direct");
|
|
419
|
+
const y1SampleCorrect = y1.sampleCount === 3;
|
|
420
|
+
const y1SuccessRateCorrect = Math.abs(y1.successRate - 2 / 3) < 0.001;
|
|
421
|
+
add("yield-record-samples", y1SampleCorrect, `samples=${y1.sampleCount}`);
|
|
422
|
+
add("yield-record-rate", y1SuccessRateCorrect, `rate=${y1.successRate.toFixed(3)}`);
|
|
423
|
+
|
|
424
|
+
// 2. Yield reordering: known-good URL floats above unknown
|
|
425
|
+
const candidates = [
|
|
426
|
+
{ url: "https://unknown-domain-987654.example.com/x", priority: 0.9 },
|
|
427
|
+
{ url: "https://arxiv.org/abs/newpaper", priority: 0.5 },
|
|
428
|
+
];
|
|
429
|
+
const reordered = await reorderByYield(candidates, "origin-direct");
|
|
430
|
+
add("yield-reorder-runs", reordered.length === 2, `n=${reordered.length}`);
|
|
431
|
+
|
|
432
|
+
// 3. Lane independence audit — three Jina lanes = one witness
|
|
433
|
+
const lanes = [
|
|
434
|
+
{ laneId: "jina-1", laneClass: "hosted-renderer" },
|
|
435
|
+
{ laneId: "jina-2", laneClass: "hosted-renderer" },
|
|
436
|
+
{ laneId: "direct", laneClass: "origin-direct", sourceUrl: "https://example.com/x" },
|
|
437
|
+
{ laneId: "wayback", laneClass: "archive" },
|
|
438
|
+
];
|
|
439
|
+
const groups = auditLaneIndependence(lanes);
|
|
440
|
+
const jinaGroup = groups.find((g) => g.providerId === "jina");
|
|
441
|
+
add("independence-jina-collapse", jinaGroup?.laneIds.length === 2 && jinaGroup?.witnessWeight === 0.5, `jina-group=${JSON.stringify(jinaGroup)}`);
|
|
442
|
+
const effective = effectiveWitnessCount(lanes);
|
|
443
|
+
add("independence-effective-count", effective === 3, `effective=${effective}`);
|
|
444
|
+
|
|
445
|
+
// 4. Provider identification for relay lanes
|
|
446
|
+
const relayId = laneProviderId("relay:corsproxy.io", "lane-3");
|
|
447
|
+
add("provider-relay-id", relayId === "relay:corsproxy.io", relayId);
|
|
448
|
+
|
|
449
|
+
// 5. Persisted backoff: set + check + expire
|
|
450
|
+
await persistBackoff("https://backoff-test.example.com/x", 5_000, "429");
|
|
451
|
+
const remaining = await checkPersistedBackoff("https://backoff-test.example.com/y");
|
|
452
|
+
add("backoff-persist", remaining > 0 && remaining <= 5_000, `remaining=${remaining}`);
|
|
453
|
+
await clearPersistedBackoff("https://backoff-test.example.com/x");
|
|
454
|
+
const cleared = await checkPersistedBackoff("https://backoff-test.example.com/x");
|
|
455
|
+
add("backoff-clear", cleared === 0, `cleared=${cleared}`);
|
|
456
|
+
|
|
457
|
+
// 6. Marginal gain: high yields continue
|
|
458
|
+
const d1 = decideMarginalGain([0.3, 0.4, 0.2], 5, { minExpectedYield: 0.1 });
|
|
459
|
+
add("marginal-continue-high", d1.shouldContinue === true, d1.reason);
|
|
460
|
+
|
|
461
|
+
// 7. Marginal gain: low yields stop
|
|
462
|
+
const d2 = decideMarginalGain([0.02, 0.01, 0.03], 5, { minExpectedYield: 0.1 });
|
|
463
|
+
add("marginal-stop-low", d2.shouldContinue === false, d2.reason);
|
|
464
|
+
|
|
465
|
+
// 8. Marginal gain: below page floor, always continue
|
|
466
|
+
const d3 = decideMarginalGain([0.001], 1, { minExpectedYield: 0.1, minPagesFetched: 3 });
|
|
467
|
+
add("marginal-continue-below-floor", d3.shouldContinue === true, d3.reason);
|
|
468
|
+
|
|
469
|
+
// 9. Marginal gain: empty samples continue
|
|
470
|
+
const d4 = decideMarginalGain([], 10, { minExpectedYield: 0.1 });
|
|
471
|
+
add("marginal-continue-no-samples", d4.shouldContinue === true, d4.reason);
|
|
472
|
+
|
|
473
|
+
// 10. hostOf normalization
|
|
474
|
+
add("host-strips-www", hostOf("https://www.Example.COM/x") === "example.com", hostOf("https://www.Example.COM/x"));
|
|
475
|
+
|
|
476
|
+
return { ok: checks.every((c) => c.passed), checks };
|
|
477
|
+
}
|