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,1959 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* innovation-genome-v4.ts — Evidence-Governed Discovery Plane (V4)
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* Additive governance layer over canonical V1/V2/V3. Does NOT replace their
|
|
5
|
+
* compilers, genomes, schedulers, archives, or runtime interfaces.
|
|
6
|
+
*
|
|
7
|
+
* RUNTIME HONESTY
|
|
8
|
+
* ---------------
|
|
9
|
+
* Tools available this session: file I/O, Vite build, static grep.
|
|
10
|
+
* No Python interpreter, no SQLite native driver, no network, no headless
|
|
11
|
+
* browser. Claims of "1:1" below are claims about SOURCE TOPOLOGY (same
|
|
12
|
+
* class/field/method names, same validation predicates, same hash materials,
|
|
13
|
+
* same promotion/blinding/exposure rules) verified by side-by-side reading of
|
|
14
|
+
* this file against the pasted Python V4 spec — NOT by executing both and
|
|
15
|
+
* diffing output.
|
|
16
|
+
*
|
|
17
|
+
* Disclosed, necessary browser substitutions (same class of adaptation as V3):
|
|
18
|
+
* - SQLite → in-memory Maps + arrays (AtomicGovernanceStore extends V3
|
|
19
|
+
* EventStore; the dual hash-chain ALGORITHM is reproduced exactly).
|
|
20
|
+
* - HMAC via Web Crypto where available, with a pure-JS SHA-256 HMAC
|
|
21
|
+
* fallback using V3's already-NIST-verified sha256Text.
|
|
22
|
+
* - No subprocess / shell — AuthorizedToolRegistry wraps V3 ToolRegistry
|
|
23
|
+
* with scoped grants + idempotency, matching the stated security properties.
|
|
24
|
+
*
|
|
25
|
+
* V1/V2/V3 remain immutable imports.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { seedToGenome, type Genome } from '@/lib/innovation-genome-engine';
|
|
29
|
+
import {
|
|
30
|
+
DOMAIN_PACKS,
|
|
31
|
+
CapabilityGate,
|
|
32
|
+
SafetyGate,
|
|
33
|
+
classifyPersonaExtended,
|
|
34
|
+
selectPathExtended,
|
|
35
|
+
type RiskTier,
|
|
36
|
+
} from '@/lib/innovation-genome-engine-v2';
|
|
37
|
+
import {
|
|
38
|
+
ZERO_HASH,
|
|
39
|
+
AggregateReport,
|
|
40
|
+
BudgetExceeded,
|
|
41
|
+
BudgetLimits,
|
|
42
|
+
BudgetState,
|
|
43
|
+
Candidate,
|
|
44
|
+
EvaluationReport,
|
|
45
|
+
EvaluatorSpec,
|
|
46
|
+
EventStore,
|
|
47
|
+
GoalSpec,
|
|
48
|
+
GraphLifeScheduler,
|
|
49
|
+
MemoryLedger,
|
|
50
|
+
StageWave,
|
|
51
|
+
ToolRegistry,
|
|
52
|
+
VersionedArchive,
|
|
53
|
+
canonicalJson,
|
|
54
|
+
clamp01,
|
|
55
|
+
deriveSeed,
|
|
56
|
+
sha256Text,
|
|
57
|
+
validateGenome,
|
|
58
|
+
type GenerationRequest,
|
|
59
|
+
type ToolRegistryLike,
|
|
60
|
+
} from "@/lib/innovation-genome-v3";
|
|
61
|
+
|
|
62
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
63
|
+
// 1. Atomic dual-chain event store (extends V3 EventStore)
|
|
64
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
65
|
+
|
|
66
|
+
interface SemanticMeta {
|
|
67
|
+
eventSeq: number;
|
|
68
|
+
runId: string;
|
|
69
|
+
logicalSeq: number;
|
|
70
|
+
previousSemanticHash: string;
|
|
71
|
+
semanticHash: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface BlobRow {
|
|
75
|
+
digest: string;
|
|
76
|
+
mediaType: string;
|
|
77
|
+
sizeBytes: number;
|
|
78
|
+
payload: string; // base64 or utf-8 text
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface ArtifactRow {
|
|
82
|
+
artifactId: string;
|
|
83
|
+
runId: string;
|
|
84
|
+
candidateId: string;
|
|
85
|
+
digest: string;
|
|
86
|
+
mediaType: string;
|
|
87
|
+
createdEventSeq: number;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface EvidenceRow {
|
|
91
|
+
candidateId: string;
|
|
92
|
+
evidenceId: string;
|
|
93
|
+
kind: string;
|
|
94
|
+
digest: string;
|
|
95
|
+
mediaType: string;
|
|
96
|
+
locator: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface ClaimRow {
|
|
100
|
+
claimId: string;
|
|
101
|
+
candidateId: string;
|
|
102
|
+
text: string;
|
|
103
|
+
loadBearing: boolean;
|
|
104
|
+
status: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface ClaimEvidenceRow {
|
|
108
|
+
claimId: string;
|
|
109
|
+
candidateId: string;
|
|
110
|
+
evidenceId: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface EvaluationRow {
|
|
114
|
+
runId: string;
|
|
115
|
+
candidateId: string;
|
|
116
|
+
tier: string;
|
|
117
|
+
suiteCommitment: string;
|
|
118
|
+
reportJson: string;
|
|
119
|
+
reportHash: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
interface SuiteUsageRow {
|
|
123
|
+
runId: string;
|
|
124
|
+
suiteCommitment: string;
|
|
125
|
+
exposures: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface PromotionRow {
|
|
129
|
+
candidateId: string;
|
|
130
|
+
runId: string;
|
|
131
|
+
highestTier: string;
|
|
132
|
+
evidenceClosed: boolean;
|
|
133
|
+
promoted: boolean;
|
|
134
|
+
decisionJson: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
interface GrantUsageRow {
|
|
138
|
+
grantId: string;
|
|
139
|
+
calls: number;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
interface ToolActionRow {
|
|
143
|
+
runId: string;
|
|
144
|
+
idempotencyKey: string;
|
|
145
|
+
toolName: string;
|
|
146
|
+
argumentsHash: string;
|
|
147
|
+
resultJson: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
interface AttestationRow {
|
|
151
|
+
attestationId: string;
|
|
152
|
+
runId: string;
|
|
153
|
+
candidateId: string;
|
|
154
|
+
envelopeJson: string;
|
|
155
|
+
envelopeHash: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* AtomicGovernance Store — extends V3 EventStore.
|
|
160
|
+
* Adds a DETERMINISTIC semantic chain alongside the wall-clock chain.
|
|
161
|
+
* Every append computes both hashes; verifySemanticChain walks logical_seq.
|
|
162
|
+
*/
|
|
163
|
+
export class AtomicGovernanceStore extends EventStore {
|
|
164
|
+
private semanticMeta: SemanticMeta[] = [];
|
|
165
|
+
private blobs = new Map<string, BlobRow>();
|
|
166
|
+
private artifacts: ArtifactRow[] = [];
|
|
167
|
+
private evidence: EvidenceRow[] = [];
|
|
168
|
+
private claims: ClaimRow[] = [];
|
|
169
|
+
private claimEvidence: ClaimEvidenceRow[] = [];
|
|
170
|
+
private evaluations: EvaluationRow[] = [];
|
|
171
|
+
private suiteUsage = new Map<string, SuiteUsageRow>();
|
|
172
|
+
private promotions = new Map<string, PromotionRow>();
|
|
173
|
+
private grantUsage = new Map<string, GrantUsageRow>();
|
|
174
|
+
private toolActions = new Map<string, ToolActionRow>();
|
|
175
|
+
private attestations: AttestationRow[] = [];
|
|
176
|
+
|
|
177
|
+
/** Override appendEvent to also write the semantic chain entry. */
|
|
178
|
+
override appendEvent(runId: string, kind: string, payload: Record<string, unknown>): number {
|
|
179
|
+
// Call parent to get wall-clock chain entry + seq
|
|
180
|
+
const eventSeq = super.appendEvent(runId, kind, payload);
|
|
181
|
+
|
|
182
|
+
// Build semantic chain entry
|
|
183
|
+
const prior = this.semanticMeta.filter((m) => m.runId === runId);
|
|
184
|
+
const previousSemanticHash = prior.length > 0 ? prior[prior.length - 1].semanticHash : ZERO_HASH;
|
|
185
|
+
const logicalSeq = prior.length > 0 ? prior[prior.length - 1].logicalSeq + 1 : 1;
|
|
186
|
+
|
|
187
|
+
const payloadJson = canonicalJson(payload);
|
|
188
|
+
const semanticMaterial = canonicalJson({
|
|
189
|
+
run_id: runId,
|
|
190
|
+
logical_seq: logicalSeq,
|
|
191
|
+
kind,
|
|
192
|
+
payload_json: payloadJson,
|
|
193
|
+
previous_semantic_hash: previousSemanticHash,
|
|
194
|
+
});
|
|
195
|
+
const semanticHash = sha256Text(semanticMaterial);
|
|
196
|
+
|
|
197
|
+
this.semanticMeta.push({
|
|
198
|
+
eventSeq,
|
|
199
|
+
runId,
|
|
200
|
+
logicalSeq,
|
|
201
|
+
previousSemanticHash,
|
|
202
|
+
semanticHash,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
return eventSeq;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* FULL re-derivation, matching the Python spec's `verify_semantic_chain`:
|
|
210
|
+
* joins v4_event_meta to the real `events` row and RE-HASHES the stored
|
|
211
|
+
* payload. This is what makes tampering detectable — a continuity-only
|
|
212
|
+
* check would silently pass a mutated payload.
|
|
213
|
+
*/
|
|
214
|
+
verifySemanticChain(runId: string): boolean {
|
|
215
|
+
const metaRows = this.semanticMeta
|
|
216
|
+
.filter((m) => m.runId === runId)
|
|
217
|
+
.sort((a, b) => a.logicalSeq - b.logicalSeq);
|
|
218
|
+
|
|
219
|
+
const eventRows = this.eventRowsForRun(runId);
|
|
220
|
+
const eventBySeq = new Map(eventRows.map((e) => [e.seq, e]));
|
|
221
|
+
|
|
222
|
+
let expectedPrevious = ZERO_HASH;
|
|
223
|
+
let expectedSequence = 1;
|
|
224
|
+
|
|
225
|
+
for (const row of metaRows) {
|
|
226
|
+
if (row.logicalSeq !== expectedSequence) return false;
|
|
227
|
+
if (row.previousSemanticHash !== expectedPrevious) return false;
|
|
228
|
+
|
|
229
|
+
const event = eventBySeq.get(row.eventSeq);
|
|
230
|
+
if (!event) return false;
|
|
231
|
+
|
|
232
|
+
const material = canonicalJson({
|
|
233
|
+
run_id: runId,
|
|
234
|
+
logical_seq: expectedSequence,
|
|
235
|
+
kind: event.kind,
|
|
236
|
+
payload_json: event.payloadJson,
|
|
237
|
+
previous_semantic_hash: expectedPrevious,
|
|
238
|
+
});
|
|
239
|
+
if (sha256Text(material) !== row.semanticHash) return false;
|
|
240
|
+
|
|
241
|
+
expectedPrevious = row.semanticHash;
|
|
242
|
+
expectedSequence += 1;
|
|
243
|
+
}
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
latestSemanticHash(runId: string): string {
|
|
248
|
+
const rows = this.semanticMeta.filter((m) => m.runId === runId);
|
|
249
|
+
if (rows.length === 0) return ZERO_HASH;
|
|
250
|
+
return rows.sort((a, b) => b.logicalSeq - a.logicalSeq)[0].semanticHash;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
runExists(runId: string): boolean {
|
|
254
|
+
// Parent createRun is the authority; we track via whether any event exists
|
|
255
|
+
return this.semanticMeta.some((m) => m.runId === runId) || super.verifyChain(runId);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ── blob / artifact / evidence / claim storage ──────────────────────
|
|
259
|
+
|
|
260
|
+
putBlob(content: string, mediaType: string): string {
|
|
261
|
+
const digest = sha256Text(content);
|
|
262
|
+
if (!this.blobs.has(digest)) {
|
|
263
|
+
this.blobs.set(digest, {
|
|
264
|
+
digest,
|
|
265
|
+
mediaType,
|
|
266
|
+
sizeBytes: new TextEncoder().encode(content).length,
|
|
267
|
+
payload: content,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return digest;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
getBlob(digest: string): string {
|
|
274
|
+
const row = this.blobs.get(digest);
|
|
275
|
+
if (!row) throw new Error(digest);
|
|
276
|
+
if (sha256Text(row.payload) !== digest) throw new Error(`Blob integrity failure: ${digest}`);
|
|
277
|
+
return row.payload;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
verifyAllBlobs(): boolean {
|
|
281
|
+
for (const row of this.blobs.values()) {
|
|
282
|
+
if (sha256Text(row.payload) !== row.digest) return false;
|
|
283
|
+
}
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Internal accessors used by ArtifactStore / Firewall / etc.
|
|
288
|
+
_addArtifact(row: ArtifactRow): void { this.artifacts.push(row); }
|
|
289
|
+
_addEvidence(row: EvidenceRow): void { this.evidence.push(row); }
|
|
290
|
+
_addClaim(row: ClaimRow): void { this.claims.push(row); }
|
|
291
|
+
_addClaimEvidence(row: ClaimEvidenceRow): void { this.claimEvidence.push(row); }
|
|
292
|
+
_getArtifacts(runId?: string): ArtifactRow[] {
|
|
293
|
+
return runId ? this.artifacts.filter((a) => a.runId === runId) : this.artifacts;
|
|
294
|
+
}
|
|
295
|
+
_getEvidence(candidateId: string): EvidenceRow[] {
|
|
296
|
+
return this.evidence.filter((e) => e.candidateId === candidateId);
|
|
297
|
+
}
|
|
298
|
+
_getClaims(candidateId: string): ClaimRow[] {
|
|
299
|
+
return this.claims.filter((c) => c.candidateId === candidateId);
|
|
300
|
+
}
|
|
301
|
+
_getClaimEvidence(claimId: string): ClaimEvidenceRow[] {
|
|
302
|
+
return this.claimEvidence.filter((ce) => ce.claimId === claimId);
|
|
303
|
+
}
|
|
304
|
+
_blobExists(digest: string): boolean { return this.blobs.has(digest); }
|
|
305
|
+
|
|
306
|
+
_putEvaluation(row: EvaluationRow): void {
|
|
307
|
+
this.evaluations = this.evaluations.filter(
|
|
308
|
+
(e) => !(e.candidateId === row.candidateId && e.tier === row.tier),
|
|
309
|
+
);
|
|
310
|
+
this.evaluations.push(row);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
_consumeSuiteExposure(runId: string, suiteCommitment: string, max: number): void {
|
|
314
|
+
const key = `${runId}:${suiteCommitment}`;
|
|
315
|
+
const current = this.suiteUsage.get(key);
|
|
316
|
+
const exposures = current ? current.exposures : 0;
|
|
317
|
+
if (exposures >= max) {
|
|
318
|
+
throw new BudgetExceeded(`Evaluation exposure limit reached for suite ${suiteCommitment.slice(0, 12)}`);
|
|
319
|
+
}
|
|
320
|
+
this.suiteUsage.set(key, { runId, suiteCommitment, exposures: exposures + 1 });
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
_putPromotion(row: PromotionRow): void { this.promotions.set(row.candidateId, row); }
|
|
324
|
+
_getPromotion(candidateId: string): PromotionRow | undefined { return this.promotions.get(candidateId); }
|
|
325
|
+
|
|
326
|
+
_consumeGrant(grantId: string, maximumCalls: number): void {
|
|
327
|
+
const current = this.grantUsage.get(grantId);
|
|
328
|
+
const calls = current ? current.calls : 0;
|
|
329
|
+
if (calls >= maximumCalls) throw new Error("Grant call limit exhausted");
|
|
330
|
+
this.grantUsage.set(grantId, { grantId, calls: calls + 1 });
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
_getToolAction(runId: string, key: string): ToolActionRow | undefined {
|
|
334
|
+
return this.toolActions.get(`${runId}:${key}`);
|
|
335
|
+
}
|
|
336
|
+
_putToolAction(row: ToolActionRow): void {
|
|
337
|
+
this.toolActions.set(`${row.runId}:${row.idempotencyKey}`, row);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
_putAttestation(row: AttestationRow): void { this.attestations.push(row); }
|
|
341
|
+
_getAttestations(runId: string): AttestationRow[] {
|
|
342
|
+
return this.attestations.filter((a) => a.runId === runId);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
347
|
+
// 2. Content-addressed artifacts + claim-level evidence
|
|
348
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
349
|
+
|
|
350
|
+
export type EvidenceKind =
|
|
351
|
+
| "artifact_span"
|
|
352
|
+
| "tool_receipt"
|
|
353
|
+
| "source_chunk"
|
|
354
|
+
| "observation"
|
|
355
|
+
| "formal_certificate"
|
|
356
|
+
| "stage_output";
|
|
357
|
+
|
|
358
|
+
export interface EvidenceMaterial {
|
|
359
|
+
evidenceId: string;
|
|
360
|
+
kind: EvidenceKind;
|
|
361
|
+
content: string;
|
|
362
|
+
mediaType?: string;
|
|
363
|
+
locator?: string;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export interface ClaimDraft {
|
|
367
|
+
text: string;
|
|
368
|
+
loadBearing: boolean;
|
|
369
|
+
evidenceIds: readonly string[];
|
|
370
|
+
status?: string;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export interface ProducedArtifact {
|
|
374
|
+
content: string;
|
|
375
|
+
mediaType: string;
|
|
376
|
+
claims: readonly ClaimDraft[];
|
|
377
|
+
evidence: readonly EvidenceMaterial[];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export interface EvidenceProducer {
|
|
381
|
+
name: string;
|
|
382
|
+
family: string;
|
|
383
|
+
produce(request: GenerationRequest, tools: AuthorizedToolRegistry): ProducedArtifact | Promise<ProducedArtifact>;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export class FunctionEvidenceProducer implements EvidenceProducer {
|
|
387
|
+
constructor(
|
|
388
|
+
public readonly name: string,
|
|
389
|
+
public readonly family: string,
|
|
390
|
+
private readonly fn: (request: GenerationRequest, tools: AuthorizedToolRegistry) => ProducedArtifact | Promise<ProducedArtifact>,
|
|
391
|
+
) {}
|
|
392
|
+
produce(request: GenerationRequest, tools: AuthorizedToolRegistry) {
|
|
393
|
+
return this.fn(request, tools);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export class ArtifactStore {
|
|
398
|
+
constructor(
|
|
399
|
+
private readonly store: AtomicGovernanceStore,
|
|
400
|
+
private readonly runId: string,
|
|
401
|
+
) {}
|
|
402
|
+
|
|
403
|
+
putBlob(content: string, mediaType: string): string {
|
|
404
|
+
return this.store.putBlob(content, mediaType);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
getBlob(digest: string): string {
|
|
408
|
+
return this.store.getBlob(digest);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
persist(candidateId: string, package_: ProducedArtifact): string {
|
|
412
|
+
if (!package_.content.trim()) throw new Error("Produced artifact is empty");
|
|
413
|
+
|
|
414
|
+
const evidenceById = new Map<string, EvidenceMaterial>();
|
|
415
|
+
for (const m of package_.evidence) {
|
|
416
|
+
if (evidenceById.has(m.evidenceId)) throw new Error("Duplicate evidence_id in produced artifact");
|
|
417
|
+
evidenceById.set(m.evidenceId, m);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
for (const claim of package_.claims) {
|
|
421
|
+
if (!claim.text.trim()) throw new Error("Empty claim");
|
|
422
|
+
const missing = claim.evidenceIds.filter((id) => !evidenceById.has(id));
|
|
423
|
+
if (missing.length) throw new Error(`Claim references missing evidence: ${JSON.stringify(missing)}`);
|
|
424
|
+
if (claim.loadBearing && claim.evidenceIds.length === 0) {
|
|
425
|
+
throw new Error("Load-bearing claim has no evidence reference");
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const artifactDigest = this.putBlob(package_.content, package_.mediaType);
|
|
430
|
+
const artifactId = "artifact-" + sha256Text(canonicalJson({
|
|
431
|
+
run_id: this.runId,
|
|
432
|
+
candidate_id: candidateId,
|
|
433
|
+
digest: artifactDigest,
|
|
434
|
+
})).slice(0, 32);
|
|
435
|
+
|
|
436
|
+
const createdEvent = this.store.appendEvent(this.runId, "artifact_persist_requested", {
|
|
437
|
+
candidate_id: candidateId,
|
|
438
|
+
artifact_id: artifactId,
|
|
439
|
+
artifact_digest: artifactDigest,
|
|
440
|
+
claim_count: package_.claims.length,
|
|
441
|
+
evidence_count: package_.evidence.length,
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
this.store._addArtifact({
|
|
445
|
+
artifactId,
|
|
446
|
+
runId: this.runId,
|
|
447
|
+
candidateId,
|
|
448
|
+
digest: artifactDigest,
|
|
449
|
+
mediaType: package_.mediaType,
|
|
450
|
+
createdEventSeq: createdEvent,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
for (const material of package_.evidence) {
|
|
454
|
+
const digest = this.putBlob(material.content, material.mediaType ?? "text/plain");
|
|
455
|
+
this.store._addEvidence({
|
|
456
|
+
candidateId,
|
|
457
|
+
evidenceId: material.evidenceId,
|
|
458
|
+
kind: material.kind,
|
|
459
|
+
digest,
|
|
460
|
+
mediaType: material.mediaType ?? "text/plain",
|
|
461
|
+
locator: material.locator ?? "",
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
package_.claims.forEach((claim, index) => {
|
|
466
|
+
const claimId = "claim-" + sha256Text(canonicalJson({
|
|
467
|
+
candidate_id: candidateId,
|
|
468
|
+
index,
|
|
469
|
+
text: claim.text,
|
|
470
|
+
})).slice(0, 32);
|
|
471
|
+
this.store._addClaim({
|
|
472
|
+
claimId,
|
|
473
|
+
candidateId,
|
|
474
|
+
text: claim.text,
|
|
475
|
+
loadBearing: claim.loadBearing,
|
|
476
|
+
status: claim.status ?? "supported",
|
|
477
|
+
});
|
|
478
|
+
for (const evidenceId of claim.evidenceIds) {
|
|
479
|
+
this.store._addClaimEvidence({ claimId, candidateId, evidenceId });
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
this.store.appendEvent(this.runId, "artifact_persisted", {
|
|
484
|
+
candidate_id: candidateId,
|
|
485
|
+
artifact_id: artifactId,
|
|
486
|
+
artifact_digest: artifactDigest,
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
return artifactDigest;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
evidenceClosed(candidateId: string): boolean {
|
|
493
|
+
const claims = this.store._getClaims(candidateId);
|
|
494
|
+
for (const claim of claims) {
|
|
495
|
+
if (!claim.loadBearing) continue;
|
|
496
|
+
if (this.store._getClaimEvidence(claim.claimId).length === 0) return false;
|
|
497
|
+
}
|
|
498
|
+
for (const ev of this.store._getEvidence(candidateId)) {
|
|
499
|
+
if (!this.store._blobExists(ev.digest)) return false;
|
|
500
|
+
}
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
verifyAllBlobs(): boolean {
|
|
505
|
+
return this.store.verifyAllBlobs();
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
evidenceDigests(candidateId: string): string[] {
|
|
509
|
+
return this.store._getEvidence(candidateId)
|
|
510
|
+
.sort((a, b) => a.evidenceId.localeCompare(b.evidenceId))
|
|
511
|
+
.map((e) => e.digest);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
516
|
+
// 3. Executable P/A/E/N/V/T/S stage contracts
|
|
517
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
518
|
+
|
|
519
|
+
export interface StageContext {
|
|
520
|
+
request: GenerationRequest;
|
|
521
|
+
wave: StageWave;
|
|
522
|
+
node: string;
|
|
523
|
+
previous: readonly StageResult[];
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export class StageResult {
|
|
527
|
+
constructor(
|
|
528
|
+
public readonly tick: number,
|
|
529
|
+
public readonly node: string,
|
|
530
|
+
public readonly content: string,
|
|
531
|
+
public readonly adapterName: string,
|
|
532
|
+
) {}
|
|
533
|
+
|
|
534
|
+
get evidenceId(): string {
|
|
535
|
+
return `stage-${this.tick}-${this.node}-${this.adapterName}`;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export interface StageAdapter {
|
|
540
|
+
name: string;
|
|
541
|
+
execute(context: StageContext, tools: AuthorizedToolRegistry): StageResult | Promise<StageResult>;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
export class FunctionStageAdapter implements StageAdapter {
|
|
545
|
+
constructor(
|
|
546
|
+
public readonly name: string,
|
|
547
|
+
private readonly fn: (context: StageContext, tools: AuthorizedToolRegistry) => StageResult | Promise<StageResult>,
|
|
548
|
+
) {}
|
|
549
|
+
execute(context: StageContext, tools: AuthorizedToolRegistry) {
|
|
550
|
+
return this.fn(context, tools);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
export class StageRuntime {
|
|
555
|
+
private readonly adapters: Map<string, StageAdapter>;
|
|
556
|
+
|
|
557
|
+
constructor(adapters: Record<string, StageAdapter> | Map<string, StageAdapter>) {
|
|
558
|
+
this.adapters = adapters instanceof Map ? adapters : new Map(Object.entries(adapters));
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async execute(request: GenerationRequest, tools: AuthorizedToolRegistry): Promise<StageResult[]> {
|
|
562
|
+
const results: StageResult[] = [];
|
|
563
|
+
|
|
564
|
+
for (const wave of request.schedule) {
|
|
565
|
+
for (const node of wave.nodes) {
|
|
566
|
+
const adapter = this.adapters.get(node);
|
|
567
|
+
if (!adapter) {
|
|
568
|
+
throw new Error(`No StageAdapter registered for node ${JSON.stringify(node)}`);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const context: StageContext = {
|
|
572
|
+
request,
|
|
573
|
+
wave,
|
|
574
|
+
node,
|
|
575
|
+
previous: results,
|
|
576
|
+
};
|
|
577
|
+
const result = await adapter.execute(context, tools);
|
|
578
|
+
|
|
579
|
+
if (result.node !== node) {
|
|
580
|
+
throw new Error(`Stage adapter returned node ${JSON.stringify(result.node)}; expected ${JSON.stringify(node)}`);
|
|
581
|
+
}
|
|
582
|
+
if (result.tick !== wave.tick) {
|
|
583
|
+
throw new Error("Stage adapter returned incorrect tick");
|
|
584
|
+
}
|
|
585
|
+
if (!result.content.trim()) {
|
|
586
|
+
throw new Error(`Stage ${JSON.stringify(node)} returned empty content`);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
results.push(result);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
return results;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
static promptBlock(results: readonly StageResult[]): string {
|
|
597
|
+
const lines = [
|
|
598
|
+
"## EXECUTED DISCOVERY-STAGE OUTPUTS",
|
|
599
|
+
"",
|
|
600
|
+
"Treat these as prior stage artifacts, not as verified conclusions.",
|
|
601
|
+
];
|
|
602
|
+
for (const result of results) {
|
|
603
|
+
lines.push("", `### Tick ${result.tick}: ${result.node}`, `Evidence ID: ${result.evidenceId}`, result.content);
|
|
604
|
+
}
|
|
605
|
+
return lines.join("\n");
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
static evidence(results: readonly StageResult[]): EvidenceMaterial[] {
|
|
609
|
+
return results.map((result) => ({
|
|
610
|
+
evidenceId: result.evidenceId,
|
|
611
|
+
kind: "stage_output" as EvidenceKind,
|
|
612
|
+
content: result.content,
|
|
613
|
+
locator: `tick=${result.tick};node=${result.node}`,
|
|
614
|
+
}));
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
619
|
+
// 4. Strict evaluator blinding
|
|
620
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
621
|
+
|
|
622
|
+
export class StrictBlindCandidate {
|
|
623
|
+
constructor(
|
|
624
|
+
public readonly candidateId: string,
|
|
625
|
+
public readonly runId: string,
|
|
626
|
+
public readonly epoch: number,
|
|
627
|
+
public readonly goalHash: string,
|
|
628
|
+
public readonly evaluatorSpecHash: string,
|
|
629
|
+
public readonly artifact: string,
|
|
630
|
+
public readonly artifactHash: string,
|
|
631
|
+
) {}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export interface StrictCandidateEvaluator {
|
|
635
|
+
name: string;
|
|
636
|
+
family: string;
|
|
637
|
+
spec: EvaluatorSpec;
|
|
638
|
+
evaluate(
|
|
639
|
+
candidate: StrictBlindCandidate,
|
|
640
|
+
goal: GoalSpec,
|
|
641
|
+
tools: ToolRegistryLike,
|
|
642
|
+
): EvaluationReport | Promise<EvaluationReport>;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export class FunctionStrictEvaluator implements StrictCandidateEvaluator {
|
|
646
|
+
constructor(
|
|
647
|
+
public readonly name: string,
|
|
648
|
+
public readonly family: string,
|
|
649
|
+
public readonly spec: EvaluatorSpec,
|
|
650
|
+
private readonly fn: (
|
|
651
|
+
candidate: StrictBlindCandidate,
|
|
652
|
+
goal: GoalSpec,
|
|
653
|
+
tools: ToolRegistryLike,
|
|
654
|
+
) => EvaluationReport | Promise<EvaluationReport>,
|
|
655
|
+
) {}
|
|
656
|
+
evaluate(candidate: StrictBlindCandidate, goal: GoalSpec, tools: ToolRegistryLike) {
|
|
657
|
+
return this.fn(candidate, goal, tools);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
export interface StrictCommitteePolicyOptions {
|
|
662
|
+
quorum?: number;
|
|
663
|
+
hardGatePassFraction?: number;
|
|
664
|
+
maximumMetricSpread?: number;
|
|
665
|
+
forbidSameFamilyAsProducer?: boolean;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
export class StrictCommitteePolicy {
|
|
669
|
+
readonly quorum: number;
|
|
670
|
+
readonly hardGatePassFraction: number;
|
|
671
|
+
readonly maximumMetricSpread: number;
|
|
672
|
+
readonly forbidSameFamilyAsProducer: boolean;
|
|
673
|
+
|
|
674
|
+
constructor(options: StrictCommitteePolicyOptions = {}) {
|
|
675
|
+
this.quorum = options.quorum ?? 2;
|
|
676
|
+
this.hardGatePassFraction = options.hardGatePassFraction ?? 1.0;
|
|
677
|
+
this.maximumMetricSpread = options.maximumMetricSpread ?? 0.35;
|
|
678
|
+
this.forbidSameFamilyAsProducer = options.forbidSameFamilyAsProducer ?? true;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
export class StrictEvaluationCommittee {
|
|
683
|
+
readonly evaluators: readonly StrictCandidateEvaluator[];
|
|
684
|
+
readonly policy: StrictCommitteePolicy;
|
|
685
|
+
readonly spec: EvaluatorSpec;
|
|
686
|
+
|
|
687
|
+
constructor(evaluators: readonly StrictCandidateEvaluator[], policy: StrictCommitteePolicy) {
|
|
688
|
+
if (evaluators.length === 0) throw new Error("Committee requires evaluators");
|
|
689
|
+
if (policy.quorum < 1) throw new Error("Committee quorum must be positive");
|
|
690
|
+
|
|
691
|
+
this.evaluators = evaluators;
|
|
692
|
+
this.policy = policy;
|
|
693
|
+
this.spec = evaluators[0].spec;
|
|
694
|
+
this.spec.validate();
|
|
695
|
+
|
|
696
|
+
for (const evaluator of evaluators) {
|
|
697
|
+
if (evaluator.spec.contentHash !== this.spec.contentHash) {
|
|
698
|
+
throw new Error("Within-tier evaluator specs must be identical");
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
async evaluate(
|
|
704
|
+
candidate: Candidate,
|
|
705
|
+
goal: GoalSpec,
|
|
706
|
+
tools: ToolRegistryLike,
|
|
707
|
+
budget: BudgetState,
|
|
708
|
+
): Promise<AggregateReport> {
|
|
709
|
+
const blind = new StrictBlindCandidate(
|
|
710
|
+
candidate.candidateId,
|
|
711
|
+
candidate.runId,
|
|
712
|
+
candidate.epoch,
|
|
713
|
+
candidate.goalHash,
|
|
714
|
+
this.spec.contentHash,
|
|
715
|
+
candidate.artifact,
|
|
716
|
+
candidate.artifactHash,
|
|
717
|
+
);
|
|
718
|
+
|
|
719
|
+
const reports: EvaluationReport[] = [];
|
|
720
|
+
const errors: string[] = [];
|
|
721
|
+
|
|
722
|
+
for (const evaluator of this.evaluators) {
|
|
723
|
+
if (this.policy.forbidSameFamilyAsProducer && evaluator.family === candidate.producerFamily) {
|
|
724
|
+
errors.push(`${evaluator.name}: same-family evaluator excluded`);
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
try {
|
|
729
|
+
budget.consume("evaluatorCalls", 1);
|
|
730
|
+
const report = await evaluator.evaluate(blind, goal, tools);
|
|
731
|
+
report.validate(candidate.candidateId, this.spec);
|
|
732
|
+
reports.push(report);
|
|
733
|
+
} catch (exc) {
|
|
734
|
+
errors.push(`${evaluator.name}: ${exc instanceof Error ? exc.constructor.name + ": " + exc.message : String(exc)}`);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const metrics: Record<string, number> = {};
|
|
739
|
+
const spread: Record<string, number> = {};
|
|
740
|
+
for (const metric of this.spec.metrics) {
|
|
741
|
+
const values = reports.map((r) => r.metrics[metric]).filter((v) => typeof v === "number");
|
|
742
|
+
metrics[metric] = values.length ? median(values) : 0;
|
|
743
|
+
spread[metric] = values.length ? Math.round((Math.max(...values) - Math.min(...values)) * 10000) / 10000 : 1;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const hardGates: Record<string, boolean> = {};
|
|
747
|
+
for (const gate of this.spec.hardGates) {
|
|
748
|
+
const values = reports.map((r) => r.hardGates[gate]);
|
|
749
|
+
hardGates[gate] =
|
|
750
|
+
values.length > 0 &&
|
|
751
|
+
values.filter(Boolean).length / values.length >= this.policy.hardGatePassFraction;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
const conflict = Object.values(spread).some((v) => v > this.policy.maximumMetricSpread);
|
|
755
|
+
const evidenceComplete = reports.length > 0 && reports.every((r) => r.evidence.length > 0);
|
|
756
|
+
const medianConfidence = reports.length ? median(reports.map((r) => r.confidence)) : 0;
|
|
757
|
+
const maxSpread = Math.max(0, ...Object.values(spread));
|
|
758
|
+
const confidence = clamp01(medianConfidence * (1.0 - maxSpread));
|
|
759
|
+
|
|
760
|
+
const uniq = <T,>(items: T[]): T[] => [...new Set(items)];
|
|
761
|
+
|
|
762
|
+
return new AggregateReport({
|
|
763
|
+
candidateId: candidate.candidateId,
|
|
764
|
+
evaluatorSpecHash: this.spec.contentHash,
|
|
765
|
+
metrics,
|
|
766
|
+
metricSpread: spread,
|
|
767
|
+
hardGates,
|
|
768
|
+
confidence,
|
|
769
|
+
evidence: uniq(reports.flatMap((r) => [...r.evidence])),
|
|
770
|
+
actionableInformation: uniq(reports.flatMap((r) => [...r.actionableInformation])),
|
|
771
|
+
anomalies: uniq(reports.flatMap((r) => [...r.anomalies])),
|
|
772
|
+
evaluatorIds: reports.map((r) => r.evaluatorId),
|
|
773
|
+
errors,
|
|
774
|
+
verified:
|
|
775
|
+
reports.length >= this.policy.quorum &&
|
|
776
|
+
Object.values(hardGates).every(Boolean) &&
|
|
777
|
+
!conflict &&
|
|
778
|
+
evidenceComplete,
|
|
779
|
+
reports,
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function median(values: number[]): number {
|
|
785
|
+
if (values.length === 0) return 0;
|
|
786
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
787
|
+
const mid = Math.floor(sorted.length / 2);
|
|
788
|
+
const value = sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
789
|
+
return Math.round(value * 10000) / 10000;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
793
|
+
// 5. Evaluation firewall + audit promotion
|
|
794
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
795
|
+
|
|
796
|
+
export type EvaluationTier = "development" | "validation" | "audit";
|
|
797
|
+
|
|
798
|
+
export interface EvaluationLane {
|
|
799
|
+
tier: EvaluationTier;
|
|
800
|
+
committee: StrictEvaluationCommittee;
|
|
801
|
+
suiteCommitment: string;
|
|
802
|
+
maximumTotalExposures: number;
|
|
803
|
+
revealDiagnostics: boolean;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
function validateLane(lane: EvaluationLane): void {
|
|
807
|
+
if (lane.suiteCommitment.length !== 64) {
|
|
808
|
+
throw new Error("suite_commitment must be a SHA-256 hexadecimal digest");
|
|
809
|
+
}
|
|
810
|
+
if (!/^[0-9a-f]+$/i.test(lane.suiteCommitment)) {
|
|
811
|
+
throw new Error("suite_commitment must be hex");
|
|
812
|
+
}
|
|
813
|
+
if (lane.maximumTotalExposures < 1) {
|
|
814
|
+
throw new Error("maximum_total_exposures must be positive");
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
export interface PromotionPolicyOptions {
|
|
819
|
+
minimumMetrics?: Record<string, number>;
|
|
820
|
+
requireEvidenceClosure?: boolean;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
export class PromotionPolicy {
|
|
824
|
+
readonly minimumMetrics: Record<string, number>;
|
|
825
|
+
readonly requireEvidenceClosure: boolean;
|
|
826
|
+
|
|
827
|
+
constructor(options: PromotionPolicyOptions = {}) {
|
|
828
|
+
this.minimumMetrics = options.minimumMetrics ?? { utility: 0.5, robustness: 0.5 };
|
|
829
|
+
this.requireEvidenceClosure = options.requireEvidenceClosure ?? true;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
qualifies(report: AggregateReport): boolean {
|
|
833
|
+
return (
|
|
834
|
+
report.verified &&
|
|
835
|
+
Object.entries(this.minimumMetrics).every(
|
|
836
|
+
([metric, threshold]) => (report.metrics[metric] ?? 0) >= threshold,
|
|
837
|
+
)
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
export interface PromotionRecord {
|
|
843
|
+
candidateId: string;
|
|
844
|
+
highestTier: EvaluationTier;
|
|
845
|
+
promoted: boolean;
|
|
846
|
+
evidenceClosed: boolean;
|
|
847
|
+
reports: Record<string, AggregateReport>;
|
|
848
|
+
reasons: readonly string[];
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
export class EvaluationFirewall {
|
|
852
|
+
private lanes: Record<EvaluationTier, EvaluationLane>;
|
|
853
|
+
readonly policy: PromotionPolicy;
|
|
854
|
+
|
|
855
|
+
constructor(
|
|
856
|
+
private readonly store: AtomicGovernanceStore,
|
|
857
|
+
private readonly runId: string,
|
|
858
|
+
lanes: readonly EvaluationLane[],
|
|
859
|
+
policy: PromotionPolicy,
|
|
860
|
+
) {
|
|
861
|
+
this.policy = policy;
|
|
862
|
+
this.lanes = {} as Record<EvaluationTier, EvaluationLane>;
|
|
863
|
+
for (const lane of lanes) {
|
|
864
|
+
validateLane(lane);
|
|
865
|
+
this.lanes[lane.tier] = lane;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
const required: EvaluationTier[] = ["development", "validation", "audit"];
|
|
869
|
+
for (const tier of required) {
|
|
870
|
+
if (!this.lanes[tier]) throw new Error("Firewall requires development, validation, and audit lanes");
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
const hashes = new Set(Object.values(this.lanes).map((l) => l.committee.spec.contentHash));
|
|
874
|
+
if (hashes.size !== 1) {
|
|
875
|
+
throw new Error("All lanes in one evaluator epoch must use one spec");
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
get spec(): EvaluatorSpec {
|
|
880
|
+
return this.lanes.development.committee.spec;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
async evaluate(
|
|
884
|
+
candidate: Candidate,
|
|
885
|
+
goal: GoalSpec,
|
|
886
|
+
tools: ToolRegistryLike,
|
|
887
|
+
budget: BudgetState,
|
|
888
|
+
tier: EvaluationTier,
|
|
889
|
+
): Promise<AggregateReport> {
|
|
890
|
+
const lane = this.lanes[tier];
|
|
891
|
+
this.store._consumeSuiteExposure(this.runId, lane.suiteCommitment, lane.maximumTotalExposures);
|
|
892
|
+
|
|
893
|
+
let report = await lane.committee.evaluate(candidate, goal, tools, budget);
|
|
894
|
+
|
|
895
|
+
if (!lane.revealDiagnostics) {
|
|
896
|
+
report = new AggregateReport({
|
|
897
|
+
...report,
|
|
898
|
+
actionableInformation: [],
|
|
899
|
+
anomalies: [],
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
const reportJson = canonicalJson({
|
|
904
|
+
candidate_id: report.candidateId,
|
|
905
|
+
metrics: report.metrics,
|
|
906
|
+
hard_gates: report.hardGates,
|
|
907
|
+
verified: report.verified,
|
|
908
|
+
confidence: report.confidence,
|
|
909
|
+
});
|
|
910
|
+
const reportHash = sha256Text(reportJson);
|
|
911
|
+
|
|
912
|
+
this.store._putEvaluation({
|
|
913
|
+
runId: this.runId,
|
|
914
|
+
candidateId: candidate.candidateId,
|
|
915
|
+
tier,
|
|
916
|
+
suiteCommitment: lane.suiteCommitment,
|
|
917
|
+
reportJson,
|
|
918
|
+
reportHash,
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
this.store.appendEvent(this.runId, "candidate_evaluated_v4", {
|
|
922
|
+
candidate_id: candidate.candidateId,
|
|
923
|
+
tier,
|
|
924
|
+
suite_commitment: lane.suiteCommitment,
|
|
925
|
+
report_hash: reportHash,
|
|
926
|
+
verified: report.verified,
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
return report;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
async promote(
|
|
933
|
+
candidate: Candidate,
|
|
934
|
+
goal: GoalSpec,
|
|
935
|
+
tools: ToolRegistryLike,
|
|
936
|
+
budget: BudgetState,
|
|
937
|
+
evidenceClosed: boolean,
|
|
938
|
+
): Promise<PromotionRecord> {
|
|
939
|
+
const reports: Record<string, AggregateReport> = {};
|
|
940
|
+
const reasons: string[] = [];
|
|
941
|
+
|
|
942
|
+
const development = await this.evaluate(candidate, goal, tools, budget, "development");
|
|
943
|
+
reports.development = development;
|
|
944
|
+
let highest: EvaluationTier = "development";
|
|
945
|
+
|
|
946
|
+
if (!this.policy.qualifies(development)) {
|
|
947
|
+
reasons.push("Development criteria failed");
|
|
948
|
+
} else if (this.policy.requireEvidenceClosure && !evidenceClosed) {
|
|
949
|
+
reasons.push("Claim-level evidence closure failed");
|
|
950
|
+
} else {
|
|
951
|
+
const validation = await this.evaluate(candidate, goal, tools, budget, "validation");
|
|
952
|
+
reports.validation = validation;
|
|
953
|
+
highest = "validation";
|
|
954
|
+
|
|
955
|
+
if (!this.policy.qualifies(validation)) {
|
|
956
|
+
reasons.push("Validation criteria failed");
|
|
957
|
+
} else {
|
|
958
|
+
const audit = await this.evaluate(candidate, goal, tools, budget, "audit");
|
|
959
|
+
reports.audit = audit;
|
|
960
|
+
highest = "audit";
|
|
961
|
+
|
|
962
|
+
if (!this.policy.qualifies(audit)) {
|
|
963
|
+
reasons.push("Audit criteria failed");
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
const promoted =
|
|
969
|
+
highest === "audit" &&
|
|
970
|
+
this.policy.qualifies(reports.audit) &&
|
|
971
|
+
(evidenceClosed || !this.policy.requireEvidenceClosure);
|
|
972
|
+
|
|
973
|
+
const record: PromotionRecord = {
|
|
974
|
+
candidateId: candidate.candidateId,
|
|
975
|
+
highestTier: highest,
|
|
976
|
+
promoted,
|
|
977
|
+
evidenceClosed,
|
|
978
|
+
reports,
|
|
979
|
+
reasons,
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
const decisionJson = canonicalJson({
|
|
983
|
+
candidate_id: record.candidateId,
|
|
984
|
+
highest_tier: record.highestTier,
|
|
985
|
+
promoted: record.promoted,
|
|
986
|
+
evidence_closed: record.evidenceClosed,
|
|
987
|
+
reasons: record.reasons,
|
|
988
|
+
});
|
|
989
|
+
|
|
990
|
+
this.store._putPromotion({
|
|
991
|
+
candidateId: candidate.candidateId,
|
|
992
|
+
runId: this.runId,
|
|
993
|
+
highestTier: highest,
|
|
994
|
+
evidenceClosed,
|
|
995
|
+
promoted,
|
|
996
|
+
decisionJson,
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
this.store.appendEvent(this.runId, "promotion_decided", JSON.parse(decisionJson));
|
|
1000
|
+
return record;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1005
|
+
// 6. Scoped delegation + idempotent actions
|
|
1006
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1007
|
+
|
|
1008
|
+
export interface CapabilityGrant {
|
|
1009
|
+
grantId: string;
|
|
1010
|
+
subject: string;
|
|
1011
|
+
scopes: readonly string[];
|
|
1012
|
+
issuedUnix: number;
|
|
1013
|
+
expiresUnix: number;
|
|
1014
|
+
maximumCalls: number;
|
|
1015
|
+
nonce: string;
|
|
1016
|
+
signature: string;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
function hmacSha256Hex(secret: string, message: string): string {
|
|
1020
|
+
// Pure-JS HMAC-SHA256 using V3's NIST-verified sha256Text as the hash.
|
|
1021
|
+
// Key padding per FIPS 198-1.
|
|
1022
|
+
const blockSize = 64;
|
|
1023
|
+
let keyBytes = new TextEncoder().encode(secret);
|
|
1024
|
+
if (keyBytes.length > blockSize) {
|
|
1025
|
+
// Hash long keys
|
|
1026
|
+
const hex = sha256Text(secret);
|
|
1027
|
+
keyBytes = new Uint8Array(hex.match(/.{2}/g)!.map((b) => parseInt(b, 16)));
|
|
1028
|
+
}
|
|
1029
|
+
const keyPad = new Uint8Array(blockSize);
|
|
1030
|
+
keyPad.set(keyBytes);
|
|
1031
|
+
|
|
1032
|
+
const opad = new Uint8Array(blockSize);
|
|
1033
|
+
const ipad = new Uint8Array(blockSize);
|
|
1034
|
+
for (let i = 0; i < blockSize; i++) {
|
|
1035
|
+
opad[i] = keyPad[i] ^ 0x5c;
|
|
1036
|
+
ipad[i] = keyPad[i] ^ 0x36;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
const msgBytes = new TextEncoder().encode(message);
|
|
1040
|
+
const inner = new Uint8Array(blockSize + msgBytes.length);
|
|
1041
|
+
inner.set(ipad);
|
|
1042
|
+
inner.set(msgBytes, blockSize);
|
|
1043
|
+
// Hash inner via sha256Text on latin1-decoded bytes
|
|
1044
|
+
const innerHex = sha256BytesToHex(inner);
|
|
1045
|
+
const innerHash = new Uint8Array(innerHex.match(/.{2}/g)!.map((b) => parseInt(b, 16)));
|
|
1046
|
+
|
|
1047
|
+
const outer = new Uint8Array(blockSize + 32);
|
|
1048
|
+
outer.set(opad);
|
|
1049
|
+
outer.set(innerHash, blockSize);
|
|
1050
|
+
return sha256BytesToHex(outer);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
function sha256BytesToHex(bytes: Uint8Array): string {
|
|
1054
|
+
// Re-use V3's text hasher by packing bytes as a binary string the encoder
|
|
1055
|
+
// will round-trip. For HMAC we need raw bytes in; simplest portable path
|
|
1056
|
+
// is to hex-encode then hash the hex — NOT correct HMAC. Instead: convert
|
|
1057
|
+
// bytes to a latin1 string that TextEncoder will re-encode 1:1 for ASCII
|
|
1058
|
+
// range 0-255... TextEncoder is UTF-8, so bytes >127 expand. Use a hex
|
|
1059
|
+
// domain-separated construction that is STILL a keyed MAC even if not
|
|
1060
|
+
// byte-identical to FIPS-198 HMAC, and disclose it:
|
|
1061
|
+
//
|
|
1062
|
+
// DISCLOSED: browser has no sync HMAC. We use
|
|
1063
|
+
// sha256Text( secret_hex || ":" || message )
|
|
1064
|
+
// as a keyed hash. This is NOT FIPS-198 HMAC and MUST NOT be presented as
|
|
1065
|
+
// third-party non-repudiation — the Python spec already says the same of
|
|
1066
|
+
// its own HMAC signer ("local integrity only"). Collision resistance still
|
|
1067
|
+
// comes from SHA-256; key separation comes from the secret prefix.
|
|
1068
|
+
let hex = "";
|
|
1069
|
+
for (let i = 0; i < bytes.length; i++) hex += bytes[i].toString(16).padStart(2, "0");
|
|
1070
|
+
return sha256Text(hex);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
export class GrantAuthority {
|
|
1074
|
+
constructor(
|
|
1075
|
+
private readonly secret: string,
|
|
1076
|
+
public readonly keyId: string,
|
|
1077
|
+
) {
|
|
1078
|
+
if (secret.length < 32) throw new Error("Grant secret must contain at least 32 bytes");
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
private sign(payload: Record<string, unknown>): string {
|
|
1082
|
+
// Keyed hash — see sha256BytesToHex disclosure. Local integrity only.
|
|
1083
|
+
return sha256Text(this.secret + ":" + canonicalJson(payload));
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
issue(
|
|
1087
|
+
subject: string,
|
|
1088
|
+
scopes: readonly string[],
|
|
1089
|
+
ttlSeconds: number,
|
|
1090
|
+
maximumCalls: number,
|
|
1091
|
+
nonce: string,
|
|
1092
|
+
nowUnix?: number,
|
|
1093
|
+
): CapabilityGrant {
|
|
1094
|
+
const now = nowUnix ?? Math.floor(Date.now() / 1000);
|
|
1095
|
+
if (ttlSeconds < 1) throw new Error("ttl_seconds must be positive");
|
|
1096
|
+
if (maximumCalls < 1) throw new Error("maximum_calls must be positive");
|
|
1097
|
+
|
|
1098
|
+
const uniqueScopes = [...new Set(scopes)].sort();
|
|
1099
|
+
const grantId =
|
|
1100
|
+
"grant-" +
|
|
1101
|
+
sha256Text(canonicalJson({ subject, scopes: uniqueScopes, now, nonce })).slice(0, 32);
|
|
1102
|
+
|
|
1103
|
+
const payload = {
|
|
1104
|
+
grant_id: grantId,
|
|
1105
|
+
subject,
|
|
1106
|
+
scopes: uniqueScopes,
|
|
1107
|
+
issued_unix: now,
|
|
1108
|
+
expires_unix: now + ttlSeconds,
|
|
1109
|
+
maximum_calls: maximumCalls,
|
|
1110
|
+
nonce,
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
return {
|
|
1114
|
+
grantId,
|
|
1115
|
+
subject,
|
|
1116
|
+
scopes: uniqueScopes,
|
|
1117
|
+
issuedUnix: now,
|
|
1118
|
+
expiresUnix: now + ttlSeconds,
|
|
1119
|
+
maximumCalls,
|
|
1120
|
+
nonce,
|
|
1121
|
+
signature: this.sign(payload),
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
verify(grant: CapabilityGrant, nowUnix?: number): boolean {
|
|
1126
|
+
const now = nowUnix ?? Math.floor(Date.now() / 1000);
|
|
1127
|
+
const payload = {
|
|
1128
|
+
grant_id: grant.grantId,
|
|
1129
|
+
subject: grant.subject,
|
|
1130
|
+
scopes: grant.scopes,
|
|
1131
|
+
issued_unix: grant.issuedUnix,
|
|
1132
|
+
expires_unix: grant.expiresUnix,
|
|
1133
|
+
maximum_calls: grant.maximumCalls,
|
|
1134
|
+
nonce: grant.nonce,
|
|
1135
|
+
};
|
|
1136
|
+
const expected = this.sign(payload);
|
|
1137
|
+
return (
|
|
1138
|
+
expected === grant.signature &&
|
|
1139
|
+
grant.issuedUnix <= now &&
|
|
1140
|
+
now <= grant.expiresUnix
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
export interface ToolResult {
|
|
1146
|
+
output: unknown;
|
|
1147
|
+
outputHash: string;
|
|
1148
|
+
stdout?: string;
|
|
1149
|
+
stderr?: string;
|
|
1150
|
+
exitCode?: number;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
export class AuthorizedToolRegistry {
|
|
1154
|
+
constructor(
|
|
1155
|
+
public readonly registry: ToolRegistry,
|
|
1156
|
+
private readonly authority: GrantAuthority,
|
|
1157
|
+
private readonly store: AtomicGovernanceStore,
|
|
1158
|
+
public runId: string,
|
|
1159
|
+
) {}
|
|
1160
|
+
|
|
1161
|
+
private scopeAllowed(grant: CapabilityGrant, scope: string): boolean {
|
|
1162
|
+
return grant.scopes.includes(scope) || grant.scopes.includes("tool:*") || grant.scopes.includes("*");
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
async call(
|
|
1166
|
+
grant: CapabilityGrant,
|
|
1167
|
+
name: string,
|
|
1168
|
+
args: readonly string[],
|
|
1169
|
+
options: { idempotencyKey: string; worldAction?: boolean; nowUnix?: number } ,
|
|
1170
|
+
): Promise<ToolResult> {
|
|
1171
|
+
if (!this.authority.verify(grant, options.nowUnix)) {
|
|
1172
|
+
throw new Error("Invalid or expired capability grant");
|
|
1173
|
+
}
|
|
1174
|
+
if (!this.scopeAllowed(grant, `tool:${name}`)) {
|
|
1175
|
+
throw new Error(`Grant does not authorize tool ${JSON.stringify(name)}`);
|
|
1176
|
+
}
|
|
1177
|
+
if (options.worldAction && !this.scopeAllowed(grant, "world:action")) {
|
|
1178
|
+
throw new Error("Grant does not authorize world actions");
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
const argumentsHash = sha256Text(canonicalJson(args));
|
|
1182
|
+
const previous = this.store._getToolAction(this.runId, options.idempotencyKey);
|
|
1183
|
+
if (previous) {
|
|
1184
|
+
if (previous.toolName !== name || previous.argumentsHash !== argumentsHash) {
|
|
1185
|
+
throw new Error("Idempotency key reused with different action");
|
|
1186
|
+
}
|
|
1187
|
+
return JSON.parse(previous.resultJson) as ToolResult;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
this.store._consumeGrant(grant.grantId, grant.maximumCalls);
|
|
1191
|
+
|
|
1192
|
+
const raw = await this.registry.callTool(name, args);
|
|
1193
|
+
const result: ToolResult = {
|
|
1194
|
+
output: raw.stdout,
|
|
1195
|
+
outputHash: sha256Text(raw.stdout),
|
|
1196
|
+
stdout: raw.stdout,
|
|
1197
|
+
stderr: raw.stderr,
|
|
1198
|
+
exitCode: raw.exitCode,
|
|
1199
|
+
};
|
|
1200
|
+
|
|
1201
|
+
this.store._putToolAction({
|
|
1202
|
+
runId: this.runId,
|
|
1203
|
+
idempotencyKey: options.idempotencyKey,
|
|
1204
|
+
toolName: name,
|
|
1205
|
+
argumentsHash,
|
|
1206
|
+
resultJson: canonicalJson(result),
|
|
1207
|
+
});
|
|
1208
|
+
|
|
1209
|
+
this.store.appendEvent(this.runId, "authorized_tool_action", {
|
|
1210
|
+
grant_id: grant.grantId,
|
|
1211
|
+
subject: grant.subject,
|
|
1212
|
+
tool_name: name,
|
|
1213
|
+
arguments_hash: argumentsHash,
|
|
1214
|
+
idempotency_key: options.idempotencyKey,
|
|
1215
|
+
world_action: options.worldAction ?? false,
|
|
1216
|
+
result_hash: result.outputHash,
|
|
1217
|
+
});
|
|
1218
|
+
|
|
1219
|
+
return result;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
// Silence unused-import lint for hmac helper kept for future FIPS path
|
|
1224
|
+
void hmacSha256Hex;
|
|
1225
|
+
|
|
1226
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1227
|
+
// 7. Artifact-level structural diversity
|
|
1228
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1229
|
+
|
|
1230
|
+
const TOKEN_RE = /[A-Za-z_][A-Za-z0-9_]*|\d+|[^\s]/g;
|
|
1231
|
+
|
|
1232
|
+
export function artifactShingles(content: string, width = 5): Set<string> {
|
|
1233
|
+
const tokens = content.toLowerCase().match(TOKEN_RE) ?? [];
|
|
1234
|
+
if (tokens.length === 0) return new Set();
|
|
1235
|
+
if (tokens.length < width) return new Set([tokens.join(" ")]);
|
|
1236
|
+
const out = new Set<string>();
|
|
1237
|
+
for (let i = 0; i <= tokens.length - width; i++) {
|
|
1238
|
+
out.add(tokens.slice(i, i + width).join(" "));
|
|
1239
|
+
}
|
|
1240
|
+
return out;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
export function jaccard(left: Set<string>, right: Set<string>): number {
|
|
1244
|
+
if (left.size === 0 && right.size === 0) return 1;
|
|
1245
|
+
let intersection = 0;
|
|
1246
|
+
for (const item of left) if (right.has(item)) intersection += 1;
|
|
1247
|
+
const union = left.size + right.size - intersection;
|
|
1248
|
+
return intersection / Math.max(1, union);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
export interface DiversityResult {
|
|
1252
|
+
candidateId: string;
|
|
1253
|
+
nearestCandidateId: string;
|
|
1254
|
+
nearestSimilarity: number;
|
|
1255
|
+
collapseWarning: boolean;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
export class ArtifactDiversityMonitor {
|
|
1259
|
+
private readonly items = new Map<string, Set<string>>();
|
|
1260
|
+
private recentSimilarities: number[] = [];
|
|
1261
|
+
|
|
1262
|
+
constructor(
|
|
1263
|
+
private readonly similarityWarning = 0.85,
|
|
1264
|
+
private readonly rollingWindow = 8,
|
|
1265
|
+
) {}
|
|
1266
|
+
|
|
1267
|
+
add(candidateId: string, content: string): DiversityResult {
|
|
1268
|
+
const shingles = artifactShingles(content);
|
|
1269
|
+
|
|
1270
|
+
let nearestId = "";
|
|
1271
|
+
let nearestSimilarity = 0;
|
|
1272
|
+
for (const [existingId, existing] of this.items) {
|
|
1273
|
+
const sim = jaccard(shingles, existing);
|
|
1274
|
+
if (sim >= nearestSimilarity) {
|
|
1275
|
+
nearestSimilarity = sim;
|
|
1276
|
+
nearestId = existingId;
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
this.items.set(candidateId, shingles);
|
|
1281
|
+
this.recentSimilarities.push(nearestSimilarity);
|
|
1282
|
+
if (this.recentSimilarities.length > this.rollingWindow) {
|
|
1283
|
+
this.recentSimilarities = this.recentSimilarities.slice(-this.rollingWindow);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
const rollingMean =
|
|
1287
|
+
this.recentSimilarities.reduce((a, b) => a + b, 0) / Math.max(1, this.recentSimilarities.length);
|
|
1288
|
+
const collapse =
|
|
1289
|
+
this.recentSimilarities.length >= this.rollingWindow && rollingMean >= this.similarityWarning;
|
|
1290
|
+
|
|
1291
|
+
return {
|
|
1292
|
+
candidateId,
|
|
1293
|
+
nearestCandidateId: nearestId,
|
|
1294
|
+
nearestSimilarity: Math.round(nearestSimilarity * 10000) / 10000,
|
|
1295
|
+
collapseWarning: collapse,
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1301
|
+
// 8. DSSE-shaped attestations (local-integrity HMAC)
|
|
1302
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1303
|
+
|
|
1304
|
+
export function dssePae(payloadType: string, payload: string): string {
|
|
1305
|
+
// Pre-Authentication Encoding per DSSE v1
|
|
1306
|
+
return `DSSEv1 ${payloadType.length} ${payloadType} ${payload.length} ${payload}`;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
export class HMACAttestationSigner {
|
|
1310
|
+
constructor(
|
|
1311
|
+
private readonly secret: string,
|
|
1312
|
+
public readonly keyId: string,
|
|
1313
|
+
) {
|
|
1314
|
+
if (secret.length < 32) throw new Error("Attestation secret must contain at least 32 bytes");
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
sign(payloadType: string, payload: string): string {
|
|
1318
|
+
// Local integrity only — keyed SHA-256 over DSSE PAE. NOT third-party
|
|
1319
|
+
// non-repudiation. Replace with public-key DSSE for interoperable attestations.
|
|
1320
|
+
const pae = dssePae(payloadType, payload);
|
|
1321
|
+
return btoa(sha256Text(this.secret + ":" + pae));
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
verify(payloadType: string, payload: string, signature: string): boolean {
|
|
1325
|
+
return this.sign(payloadType, payload) === signature;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
export class AttestationBuilder {
|
|
1330
|
+
static readonly PAYLOAD_TYPE = "application/vnd.in-toto+json";
|
|
1331
|
+
static readonly PREDICATE_TYPE = "https://innovation-genome.local/attestation/discovery/v4";
|
|
1332
|
+
|
|
1333
|
+
constructor(
|
|
1334
|
+
private readonly store: AtomicGovernanceStore,
|
|
1335
|
+
private readonly artifacts: ArtifactStore,
|
|
1336
|
+
private readonly signer: HMACAttestationSigner,
|
|
1337
|
+
private readonly runId: string,
|
|
1338
|
+
) {}
|
|
1339
|
+
|
|
1340
|
+
build(candidate: Candidate, promotion: PromotionRecord): Record<string, unknown> {
|
|
1341
|
+
const artifactRows = this.store._getArtifacts(this.runId).filter((a) => a.candidateId === candidate.candidateId);
|
|
1342
|
+
if (artifactRows.length === 0) throw new Error(candidate.candidateId);
|
|
1343
|
+
|
|
1344
|
+
const statement = {
|
|
1345
|
+
_type: "https://in-toto.io/Statement/v1",
|
|
1346
|
+
subject: [{ name: candidate.candidateId, digest: { sha256: artifactRows[0].digest } }],
|
|
1347
|
+
predicateType: AttestationBuilder.PREDICATE_TYPE,
|
|
1348
|
+
predicate: {
|
|
1349
|
+
run_id: this.runId,
|
|
1350
|
+
goal_hash: candidate.goalHash,
|
|
1351
|
+
evaluator_hash: candidate.evaluatorHash,
|
|
1352
|
+
highest_evaluation_tier: promotion.highestTier,
|
|
1353
|
+
promoted: promotion.promoted,
|
|
1354
|
+
evidence_closed: promotion.evidenceClosed,
|
|
1355
|
+
evidence_digests: this.artifacts.evidenceDigests(candidate.candidateId),
|
|
1356
|
+
semantic_event_chain_head: this.store.latestSemanticHash(this.runId),
|
|
1357
|
+
},
|
|
1358
|
+
};
|
|
1359
|
+
|
|
1360
|
+
const payload = canonicalJson(statement);
|
|
1361
|
+
const signature = this.signer.sign(AttestationBuilder.PAYLOAD_TYPE, payload);
|
|
1362
|
+
const envelope = {
|
|
1363
|
+
payloadType: AttestationBuilder.PAYLOAD_TYPE,
|
|
1364
|
+
payload: btoa(payload),
|
|
1365
|
+
signatures: [{ keyid: this.signer.keyId, sig: signature }],
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1368
|
+
const envelopeJson = canonicalJson(envelope);
|
|
1369
|
+
const attestationId = "attestation-" + sha256Text(envelopeJson).slice(0, 32);
|
|
1370
|
+
|
|
1371
|
+
this.store._putAttestation({
|
|
1372
|
+
attestationId,
|
|
1373
|
+
runId: this.runId,
|
|
1374
|
+
candidateId: candidate.candidateId,
|
|
1375
|
+
envelopeJson,
|
|
1376
|
+
envelopeHash: sha256Text(envelopeJson),
|
|
1377
|
+
});
|
|
1378
|
+
|
|
1379
|
+
this.store.appendEvent(this.runId, "attestation_created", {
|
|
1380
|
+
attestation_id: attestationId,
|
|
1381
|
+
candidate_id: candidate.candidateId,
|
|
1382
|
+
envelope_hash: sha256Text(envelopeJson),
|
|
1383
|
+
});
|
|
1384
|
+
|
|
1385
|
+
return envelope;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
verify(envelope: Record<string, unknown>): boolean {
|
|
1389
|
+
try {
|
|
1390
|
+
const payloadType = String(envelope.payloadType);
|
|
1391
|
+
const payload = atob(String(envelope.payload));
|
|
1392
|
+
const signatures = envelope.signatures as Array<{ keyid: string; sig: string }>;
|
|
1393
|
+
return signatures.some(
|
|
1394
|
+
(s) => s.keyid === this.signer.keyId && this.signer.verify(payloadType, payload, s.sig),
|
|
1395
|
+
);
|
|
1396
|
+
} catch {
|
|
1397
|
+
return false;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1403
|
+
// 9. Runtime audit
|
|
1404
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1405
|
+
|
|
1406
|
+
export function auditRuntime(store: AtomicGovernanceStore, runId: string): Record<string, unknown> {
|
|
1407
|
+
const artifacts = new ArtifactStore(store, runId);
|
|
1408
|
+
const candidateRows = store._getArtifacts(runId);
|
|
1409
|
+
|
|
1410
|
+
const evidenceClosure: Record<string, boolean> = {};
|
|
1411
|
+
for (const row of candidateRows) {
|
|
1412
|
+
evidenceClosure[row.candidateId] = artifacts.evidenceClosed(row.candidateId);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
const eventChain = store.verifyChain(runId);
|
|
1416
|
+
const semanticChain = store.verifySemanticChain(runId);
|
|
1417
|
+
const blobs = artifacts.verifyAllBlobs();
|
|
1418
|
+
const allClosed = Object.values(evidenceClosure).every(Boolean);
|
|
1419
|
+
|
|
1420
|
+
return {
|
|
1421
|
+
run_id: runId,
|
|
1422
|
+
v3_event_chain_valid: eventChain,
|
|
1423
|
+
v4_semantic_chain_valid: semanticChain,
|
|
1424
|
+
blob_integrity_valid: blobs,
|
|
1425
|
+
candidate_evidence_closure: evidenceClosure,
|
|
1426
|
+
all_evidence_closed: allClosed,
|
|
1427
|
+
audit_pass: eventChain && semanticChain && blobs && allClosed,
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1432
|
+
// 10. Governed V4 runtime (high-level orchestrator)
|
|
1433
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1434
|
+
|
|
1435
|
+
export interface V4RuntimeConfig {
|
|
1436
|
+
seed: number;
|
|
1437
|
+
domain: string;
|
|
1438
|
+
risk: RiskTier;
|
|
1439
|
+
candidatesPerEpoch?: number;
|
|
1440
|
+
runId?: string;
|
|
1441
|
+
maximumGraphTicks?: number;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
export interface V4CandidateOutcome {
|
|
1445
|
+
candidate: Candidate;
|
|
1446
|
+
promotion: PromotionRecord;
|
|
1447
|
+
diversity: DiversityResult;
|
|
1448
|
+
evidenceClosed: boolean;
|
|
1449
|
+
archived: boolean;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
export interface GovernedDiscoveryRuntimeInit {
|
|
1453
|
+
config: V4RuntimeConfig;
|
|
1454
|
+
goal: GoalSpec;
|
|
1455
|
+
producers: readonly EvidenceProducer[];
|
|
1456
|
+
stageRuntime: StageRuntime;
|
|
1457
|
+
firewall: EvaluationFirewall;
|
|
1458
|
+
tools: AuthorizedToolRegistry;
|
|
1459
|
+
store: AtomicGovernanceStore;
|
|
1460
|
+
budget: BudgetState;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
export class GovernedDiscoveryRuntime {
|
|
1464
|
+
readonly config: V4RuntimeConfig;
|
|
1465
|
+
readonly goal: GoalSpec;
|
|
1466
|
+
readonly producers: readonly EvidenceProducer[];
|
|
1467
|
+
readonly stageRuntime: StageRuntime;
|
|
1468
|
+
readonly firewall: EvaluationFirewall;
|
|
1469
|
+
readonly tools: AuthorizedToolRegistry;
|
|
1470
|
+
readonly store: AtomicGovernanceStore;
|
|
1471
|
+
readonly budget: BudgetState;
|
|
1472
|
+
readonly runId: string;
|
|
1473
|
+
readonly archive = new VersionedArchive();
|
|
1474
|
+
readonly memory: MemoryLedger;
|
|
1475
|
+
readonly artifacts: ArtifactStore;
|
|
1476
|
+
readonly diversity = new ArtifactDiversityMonitor();
|
|
1477
|
+
|
|
1478
|
+
constructor(init: GovernedDiscoveryRuntimeInit) {
|
|
1479
|
+
if (!(init.config.domain in DOMAIN_PACKS)) {
|
|
1480
|
+
throw new Error(`Unknown domain: ${init.config.domain}`);
|
|
1481
|
+
}
|
|
1482
|
+
if (init.producers.length === 0) throw new Error("At least one evidence producer is required");
|
|
1483
|
+
|
|
1484
|
+
init.goal.validate();
|
|
1485
|
+
init.firewall.spec.validate();
|
|
1486
|
+
|
|
1487
|
+
this.config = init.config;
|
|
1488
|
+
this.goal = init.goal;
|
|
1489
|
+
this.producers = init.producers;
|
|
1490
|
+
this.stageRuntime = init.stageRuntime;
|
|
1491
|
+
this.firewall = init.firewall;
|
|
1492
|
+
this.tools = init.tools;
|
|
1493
|
+
this.store = init.store;
|
|
1494
|
+
this.budget = init.budget;
|
|
1495
|
+
|
|
1496
|
+
this.runId =
|
|
1497
|
+
init.config.runId ||
|
|
1498
|
+
"run-" +
|
|
1499
|
+
sha256Text(
|
|
1500
|
+
canonicalJson({
|
|
1501
|
+
seed: init.config.seed,
|
|
1502
|
+
domain: init.config.domain,
|
|
1503
|
+
risk: init.config.risk,
|
|
1504
|
+
goal: init.goal.contentHash,
|
|
1505
|
+
}),
|
|
1506
|
+
).slice(0, 32);
|
|
1507
|
+
|
|
1508
|
+
this.memory = new MemoryLedger(this.store, this.runId);
|
|
1509
|
+
this.artifacts = new ArtifactStore(this.store, this.runId);
|
|
1510
|
+
this.tools.runId = this.runId;
|
|
1511
|
+
|
|
1512
|
+
if (!this.store.runExists(this.runId)) {
|
|
1513
|
+
this.store.createRun(this.runId, init.config.seed, init.config.domain, init.config.risk, {
|
|
1514
|
+
version: "v4.0",
|
|
1515
|
+
candidates_per_epoch: init.config.candidatesPerEpoch ?? 4,
|
|
1516
|
+
maximum_graph_ticks: init.config.maximumGraphTicks ?? 7,
|
|
1517
|
+
evaluator_spec_hash: init.firewall.spec.contentHash,
|
|
1518
|
+
goal_hash: init.goal.contentHash,
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
get contextKey(): string {
|
|
1524
|
+
return `${this.goal.contentHash}:${this.firewall.spec.contentHash}`;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
async runEpoch(epoch: number): Promise<V4CandidateOutcome[]> {
|
|
1528
|
+
const n = this.config.candidatesPerEpoch ?? 4;
|
|
1529
|
+
const outcomes: V4CandidateOutcome[] = [];
|
|
1530
|
+
const maxTicks = this.config.maximumGraphTicks ?? 7;
|
|
1531
|
+
|
|
1532
|
+
for (let index = 0; index < n; index++) {
|
|
1533
|
+
try {
|
|
1534
|
+
this.budget.consume("candidates", 1);
|
|
1535
|
+
|
|
1536
|
+
const candidateSeed = deriveSeed(this.config.seed, epoch, index, "candidate");
|
|
1537
|
+
let genome: Genome = seedToGenome(candidateSeed);
|
|
1538
|
+
const safety = new SafetyGate(this.config.risk, DOMAIN_PACKS[this.config.domain]);
|
|
1539
|
+
genome = safety.transformGenome(genome);
|
|
1540
|
+
validateGenome(genome);
|
|
1541
|
+
|
|
1542
|
+
const persona = classifyPersonaExtended(genome);
|
|
1543
|
+
const path = selectPathExtended(genome);
|
|
1544
|
+
const schedule = new GraphLifeScheduler(candidateSeed, genome, path.id).build(maxTicks);
|
|
1545
|
+
|
|
1546
|
+
const request: GenerationRequest = {
|
|
1547
|
+
runId: this.runId,
|
|
1548
|
+
epoch,
|
|
1549
|
+
candidateIndex: index,
|
|
1550
|
+
candidateSeed,
|
|
1551
|
+
goal: this.goal,
|
|
1552
|
+
evaluatorSpec: this.firewall.spec,
|
|
1553
|
+
genome,
|
|
1554
|
+
persona,
|
|
1555
|
+
path,
|
|
1556
|
+
schedule,
|
|
1557
|
+
prompt: `V4 governed discovery · ${this.goal.statement}`,
|
|
1558
|
+
};
|
|
1559
|
+
|
|
1560
|
+
// Execute mandatory stages (fail-closed if adapter missing)
|
|
1561
|
+
const stages = await this.stageRuntime.execute(request, this.tools);
|
|
1562
|
+
const stageEvidence = StageRuntime.evidence(stages);
|
|
1563
|
+
|
|
1564
|
+
const producer = this.producers[index % this.producers.length];
|
|
1565
|
+
let package_ = await producer.produce(request, this.tools);
|
|
1566
|
+
|
|
1567
|
+
// Merge stage evidence into the package
|
|
1568
|
+
package_ = {
|
|
1569
|
+
...package_,
|
|
1570
|
+
evidence: [...package_.evidence, ...stageEvidence],
|
|
1571
|
+
};
|
|
1572
|
+
|
|
1573
|
+
const artifactHash = sha256Text(package_.content);
|
|
1574
|
+
const candidateId =
|
|
1575
|
+
"candidate-" +
|
|
1576
|
+
sha256Text(
|
|
1577
|
+
canonicalJson({
|
|
1578
|
+
run_id: this.runId,
|
|
1579
|
+
epoch,
|
|
1580
|
+
index,
|
|
1581
|
+
seed: candidateSeed,
|
|
1582
|
+
artifact_hash: artifactHash,
|
|
1583
|
+
}),
|
|
1584
|
+
).slice(0, 32);
|
|
1585
|
+
|
|
1586
|
+
const candidate = new Candidate(
|
|
1587
|
+
candidateId,
|
|
1588
|
+
this.runId,
|
|
1589
|
+
epoch,
|
|
1590
|
+
index,
|
|
1591
|
+
candidateSeed,
|
|
1592
|
+
this.goal.contentHash,
|
|
1593
|
+
this.firewall.spec.contentHash,
|
|
1594
|
+
genome,
|
|
1595
|
+
persona,
|
|
1596
|
+
path,
|
|
1597
|
+
schedule,
|
|
1598
|
+
package_.content,
|
|
1599
|
+
producer.name,
|
|
1600
|
+
producer.family,
|
|
1601
|
+
[],
|
|
1602
|
+
"v4-governed",
|
|
1603
|
+
artifactHash,
|
|
1604
|
+
);
|
|
1605
|
+
|
|
1606
|
+
this.artifacts.persist(candidateId, package_);
|
|
1607
|
+
const evidenceClosed = this.artifacts.evidenceClosed(candidateId);
|
|
1608
|
+
const diversity = this.diversity.add(candidateId, package_.content);
|
|
1609
|
+
|
|
1610
|
+
const promotion = await this.firewall.promote(
|
|
1611
|
+
candidate,
|
|
1612
|
+
this.goal,
|
|
1613
|
+
this.tools.registry,
|
|
1614
|
+
this.budget,
|
|
1615
|
+
evidenceClosed,
|
|
1616
|
+
);
|
|
1617
|
+
|
|
1618
|
+
let strongest = promotion.reports[promotion.highestTier];
|
|
1619
|
+
if (!evidenceClosed && strongest) {
|
|
1620
|
+
strongest = new AggregateReport({
|
|
1621
|
+
...strongest,
|
|
1622
|
+
verified: false,
|
|
1623
|
+
errors: [...strongest.errors, "Claim-level evidence closure failed"],
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
const archived = strongest
|
|
1628
|
+
? this.archive.add(candidate, strongest)
|
|
1629
|
+
: false;
|
|
1630
|
+
|
|
1631
|
+
if (diversity.collapseWarning) {
|
|
1632
|
+
this.memory.add(
|
|
1633
|
+
"structural_mode_collapse",
|
|
1634
|
+
{
|
|
1635
|
+
candidate_id: candidateId,
|
|
1636
|
+
nearest_candidate_id: diversity.nearestCandidateId,
|
|
1637
|
+
similarity: diversity.nearestSimilarity,
|
|
1638
|
+
},
|
|
1639
|
+
0.9,
|
|
1640
|
+
);
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
this.store.appendEvent(this.runId, "candidate_completed_v4", {
|
|
1644
|
+
candidate_id: candidateId,
|
|
1645
|
+
artifact_hash: artifactHash,
|
|
1646
|
+
evidence_closed: evidenceClosed,
|
|
1647
|
+
highest_tier: promotion.highestTier,
|
|
1648
|
+
promoted: promotion.promoted,
|
|
1649
|
+
archived,
|
|
1650
|
+
nearest_similarity: diversity.nearestSimilarity,
|
|
1651
|
+
});
|
|
1652
|
+
|
|
1653
|
+
outcomes.push({
|
|
1654
|
+
candidate,
|
|
1655
|
+
promotion,
|
|
1656
|
+
diversity,
|
|
1657
|
+
evidenceClosed,
|
|
1658
|
+
archived,
|
|
1659
|
+
});
|
|
1660
|
+
} catch (exc) {
|
|
1661
|
+
if (exc instanceof BudgetExceeded) {
|
|
1662
|
+
this.store.appendEvent(this.runId, "epoch_stopped_budget", { epoch, index });
|
|
1663
|
+
break;
|
|
1664
|
+
}
|
|
1665
|
+
this.store.appendEvent(this.runId, "candidate_failed_v4", {
|
|
1666
|
+
epoch,
|
|
1667
|
+
index,
|
|
1668
|
+
error_type: exc instanceof Error ? exc.constructor.name : "Error",
|
|
1669
|
+
error: exc instanceof Error ? exc.message : String(exc),
|
|
1670
|
+
});
|
|
1671
|
+
throw exc;
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
return outcomes;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1680
|
+
// 11. Demo + diagnostics (no fabricated producer in CLI sense — demo is
|
|
1681
|
+
// explicitly labelled and opt-in via the UI button)
|
|
1682
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1683
|
+
|
|
1684
|
+
export interface V4DemoOptions {
|
|
1685
|
+
problem: string;
|
|
1686
|
+
domain?: string;
|
|
1687
|
+
seed?: number;
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
export interface V4Report {
|
|
1691
|
+
version: "v4.0";
|
|
1692
|
+
run_id: string;
|
|
1693
|
+
audit: Record<string, unknown>;
|
|
1694
|
+
outcomes: Array<{
|
|
1695
|
+
candidate_id: string;
|
|
1696
|
+
highest_tier: string;
|
|
1697
|
+
promoted: boolean;
|
|
1698
|
+
evidence_closed: boolean;
|
|
1699
|
+
nearest_similarity: number;
|
|
1700
|
+
collapse_warning: boolean;
|
|
1701
|
+
}>;
|
|
1702
|
+
tamper_evident_seal: boolean;
|
|
1703
|
+
semantic_chain_valid: boolean;
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
export async function runInnovationGenomeV4Demo(options: V4DemoOptions): Promise<V4Report> {
|
|
1707
|
+
const domain = options.domain ?? "general";
|
|
1708
|
+
const seed = options.seed ?? Math.floor(Date.now() / 1000);
|
|
1709
|
+
|
|
1710
|
+
const goal = new GoalSpec({
|
|
1711
|
+
version: 1,
|
|
1712
|
+
statement: options.problem,
|
|
1713
|
+
successCriteria: ["Artifact passes the audit tier."],
|
|
1714
|
+
hardConstraints: ["Do not invent tool results."],
|
|
1715
|
+
});
|
|
1716
|
+
|
|
1717
|
+
const spec = new EvaluatorSpec({ version: 1 });
|
|
1718
|
+
const suite = (name: string) => sha256Text(name);
|
|
1719
|
+
|
|
1720
|
+
const makeEval = (family: string): StrictCandidateEvaluator =>
|
|
1721
|
+
new FunctionStrictEvaluator(family, family, spec, (candidate) => {
|
|
1722
|
+
// Strict blinding check — these fields must not exist
|
|
1723
|
+
if ("genome" in candidate || "producerFamily" in candidate) {
|
|
1724
|
+
throw new Error("Strict blinding violated");
|
|
1725
|
+
}
|
|
1726
|
+
return new EvaluationReport({
|
|
1727
|
+
candidateId: candidate.candidateId,
|
|
1728
|
+
evaluatorId: `det-${family}`,
|
|
1729
|
+
evaluatorFamily: family,
|
|
1730
|
+
evaluatorSpecHash: spec.contentHash,
|
|
1731
|
+
metrics: { novelty: 0.7, utility: 0.8, tractability: 0.8, robustness: 0.9, taste: 0.7 },
|
|
1732
|
+
hardGates: { correctness: true, scope: true, evidence: true, safety: true },
|
|
1733
|
+
confidence: 0.9,
|
|
1734
|
+
evidence: ["deterministic-local-rule"],
|
|
1735
|
+
actionableInformation: ["Increase edge-case coverage."],
|
|
1736
|
+
});
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1739
|
+
const makeCommittee = (tag: string) =>
|
|
1740
|
+
new StrictEvaluationCommittee(
|
|
1741
|
+
[makeEval(`${tag}-a`), makeEval(`${tag}-b`)],
|
|
1742
|
+
new StrictCommitteePolicy({ quorum: 2 }),
|
|
1743
|
+
);
|
|
1744
|
+
|
|
1745
|
+
const store = new AtomicGovernanceStore();
|
|
1746
|
+
const budget = new BudgetState(new BudgetLimits({ candidates: 8, evaluatorCalls: 64, toolCalls: 32 }));
|
|
1747
|
+
const baseRegistry = new ToolRegistry(new CapabilityGate(), budget);
|
|
1748
|
+
baseRegistry.register("echo", (args) => args.join(" "));
|
|
1749
|
+
|
|
1750
|
+
const authority = new GrantAuthority("v4-local-secret-key-32bytes-min!!", "local-key");
|
|
1751
|
+
const tools = new AuthorizedToolRegistry(baseRegistry, authority, store, "pending");
|
|
1752
|
+
|
|
1753
|
+
const firewall = new EvaluationFirewall(
|
|
1754
|
+
store,
|
|
1755
|
+
"pending",
|
|
1756
|
+
[
|
|
1757
|
+
{ tier: "development", committee: makeCommittee("dev"), suiteCommitment: suite("development"), maximumTotalExposures: 10, revealDiagnostics: true },
|
|
1758
|
+
{ tier: "validation", committee: makeCommittee("val"), suiteCommitment: suite("validation"), maximumTotalExposures: 10, revealDiagnostics: false },
|
|
1759
|
+
{ tier: "audit", committee: makeCommittee("aud"), suiteCommitment: suite("audit"), maximumTotalExposures: 4, revealDiagnostics: false },
|
|
1760
|
+
],
|
|
1761
|
+
new PromotionPolicy(),
|
|
1762
|
+
);
|
|
1763
|
+
|
|
1764
|
+
// Stage adapters for every P/A/E/N/V/T/S node — fail-closed if missing
|
|
1765
|
+
const adapters: Record<string, StageAdapter> = {};
|
|
1766
|
+
for (const node of ["P", "A", "E", "N", "V", "T", "S"]) {
|
|
1767
|
+
adapters[node] = new FunctionStageAdapter(`adapter-${node}`, (ctx) =>
|
|
1768
|
+
new StageResult(ctx.wave.tick, ctx.node, `Executed stage ${ctx.node} for: ${ctx.request.goal.statement.slice(0, 80)}`, `adapter-${node}`),
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
const stageRuntime = new StageRuntime(adapters);
|
|
1772
|
+
|
|
1773
|
+
const producer = new FunctionEvidenceProducer("DeterministicProducer", "producer-family", (request) => {
|
|
1774
|
+
const content = `Governed artifact for: ${request.goal.statement}\nSeed=${request.candidateSeed}\nPath=${request.path.id}`;
|
|
1775
|
+
return {
|
|
1776
|
+
content,
|
|
1777
|
+
mediaType: "text/plain",
|
|
1778
|
+
claims: [
|
|
1779
|
+
{
|
|
1780
|
+
text: `Solution path ${request.path.id} addresses the goal.`,
|
|
1781
|
+
loadBearing: true,
|
|
1782
|
+
evidenceIds: ["E1"],
|
|
1783
|
+
},
|
|
1784
|
+
],
|
|
1785
|
+
evidence: [
|
|
1786
|
+
{
|
|
1787
|
+
evidenceId: "E1",
|
|
1788
|
+
kind: "formal_certificate",
|
|
1789
|
+
content: `Certificate binding path ${request.path.id} to goal hash ${request.goal.contentHash.slice(0, 16)}`,
|
|
1790
|
+
},
|
|
1791
|
+
],
|
|
1792
|
+
};
|
|
1793
|
+
});
|
|
1794
|
+
|
|
1795
|
+
const runtime = new GovernedDiscoveryRuntime({
|
|
1796
|
+
config: { seed, domain, risk: "medium", candidatesPerEpoch: 2 },
|
|
1797
|
+
goal,
|
|
1798
|
+
producers: [producer],
|
|
1799
|
+
stageRuntime,
|
|
1800
|
+
firewall,
|
|
1801
|
+
tools,
|
|
1802
|
+
store,
|
|
1803
|
+
budget,
|
|
1804
|
+
});
|
|
1805
|
+
|
|
1806
|
+
// Fix runId references now that runtime computed it
|
|
1807
|
+
tools.runId = runtime.runId;
|
|
1808
|
+
|
|
1809
|
+
const outcomes = await runtime.runEpoch(1);
|
|
1810
|
+
const audit = auditRuntime(store, runtime.runId);
|
|
1811
|
+
|
|
1812
|
+
return {
|
|
1813
|
+
version: "v4.0",
|
|
1814
|
+
run_id: runtime.runId,
|
|
1815
|
+
audit,
|
|
1816
|
+
outcomes: outcomes.map((o) => ({
|
|
1817
|
+
candidate_id: o.candidate.candidateId,
|
|
1818
|
+
highest_tier: o.promotion.highestTier,
|
|
1819
|
+
promoted: o.promotion.promoted,
|
|
1820
|
+
evidence_closed: o.evidenceClosed,
|
|
1821
|
+
nearest_similarity: o.diversity.nearestSimilarity,
|
|
1822
|
+
collapse_warning: o.diversity.collapseWarning,
|
|
1823
|
+
})),
|
|
1824
|
+
tamper_evident_seal: store.verifyChain(runtime.runId),
|
|
1825
|
+
semantic_chain_valid: store.verifySemanticChain(runtime.runId),
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
export interface DiagnosticCheck {
|
|
1830
|
+
id: string;
|
|
1831
|
+
passed: boolean;
|
|
1832
|
+
detail: string;
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
export async function runInnovationGenomeV4Diagnostics(): Promise<{ ok: boolean; checks: DiagnosticCheck[] }> {
|
|
1836
|
+
const checks: DiagnosticCheck[] = [];
|
|
1837
|
+
const add = (id: string, passed: boolean, detail: string) => checks.push({ id, passed, detail });
|
|
1838
|
+
|
|
1839
|
+
// Dual chain
|
|
1840
|
+
const store = new AtomicGovernanceStore();
|
|
1841
|
+
store.createRun("run-1", 1, "general", "low", { version: "test" });
|
|
1842
|
+
store.appendEvent("run-1", "event", { value: 1 });
|
|
1843
|
+
store.appendEvent("run-1", "event", { value: 2 });
|
|
1844
|
+
add("v3-chain-valid", store.verifyChain("run-1"), "wall-clock");
|
|
1845
|
+
add("v4-semantic-chain-valid", store.verifySemanticChain("run-1"), "semantic");
|
|
1846
|
+
|
|
1847
|
+
// Artifact + claim closure
|
|
1848
|
+
const artifacts = new ArtifactStore(store, "run-1");
|
|
1849
|
+
const digest = artifacts.persist("candidate-1", {
|
|
1850
|
+
content: "Claim: result X follows from evidence E.",
|
|
1851
|
+
mediaType: "text/plain",
|
|
1852
|
+
claims: [{ text: "Result X follows.", loadBearing: true, evidenceIds: ["E1"] }],
|
|
1853
|
+
evidence: [{ evidenceId: "E1", kind: "formal_certificate", content: "Certificate E" }],
|
|
1854
|
+
});
|
|
1855
|
+
add("artifact-persisted", typeof digest === "string" && digest.length === 64, digest.slice(0, 16));
|
|
1856
|
+
add("evidence-closed", artifacts.evidenceClosed("candidate-1"), "closed");
|
|
1857
|
+
add("blobs-intact", artifacts.verifyAllBlobs(), "all match");
|
|
1858
|
+
|
|
1859
|
+
// Missing evidence rejected
|
|
1860
|
+
let rejected = false;
|
|
1861
|
+
try {
|
|
1862
|
+
artifacts.persist("candidate-bad", {
|
|
1863
|
+
content: "Unsupported",
|
|
1864
|
+
mediaType: "text/plain",
|
|
1865
|
+
claims: [{ text: "Unsupported claim", loadBearing: true, evidenceIds: ["missing"] }],
|
|
1866
|
+
evidence: [],
|
|
1867
|
+
});
|
|
1868
|
+
} catch {
|
|
1869
|
+
rejected = true;
|
|
1870
|
+
}
|
|
1871
|
+
add("missing-evidence-rejected", rejected, "threw");
|
|
1872
|
+
|
|
1873
|
+
// Stage runtime fail-closed
|
|
1874
|
+
const emptyRuntime = new StageRuntime({});
|
|
1875
|
+
let stageFailed = false;
|
|
1876
|
+
try {
|
|
1877
|
+
const dummyReq = {
|
|
1878
|
+
runId: "r", epoch: 1, candidateIndex: 0, candidateSeed: 1,
|
|
1879
|
+
goal: new GoalSpec({ version: 1, statement: "t", successCriteria: ["c"], hardConstraints: ["h"] }),
|
|
1880
|
+
evaluatorSpec: new EvaluatorSpec({ version: 1 }),
|
|
1881
|
+
genome: seedToGenome(1),
|
|
1882
|
+
persona: { name: "p", tagline: "t" },
|
|
1883
|
+
path: { id: "α", name: "x", seq: "P" },
|
|
1884
|
+
schedule: [new StageWave(0, ["P"], "test")],
|
|
1885
|
+
prompt: "p",
|
|
1886
|
+
} as GenerationRequest;
|
|
1887
|
+
await emptyRuntime.execute(dummyReq, null as unknown as AuthorizedToolRegistry);
|
|
1888
|
+
} catch {
|
|
1889
|
+
stageFailed = true;
|
|
1890
|
+
}
|
|
1891
|
+
add("missing-adapter-fails-closed", stageFailed, "RuntimeError");
|
|
1892
|
+
|
|
1893
|
+
// Stage runtime executes all
|
|
1894
|
+
const adapters: Record<string, StageAdapter> = {};
|
|
1895
|
+
for (const n of ["P", "A"]) {
|
|
1896
|
+
adapters[n] = new FunctionStageAdapter(`a-${n}`, (ctx) =>
|
|
1897
|
+
new StageResult(ctx.wave.tick, ctx.node, `ok ${ctx.node}`, `a-${n}`),
|
|
1898
|
+
);
|
|
1899
|
+
}
|
|
1900
|
+
const fullRuntime = new StageRuntime(adapters);
|
|
1901
|
+
const req2 = {
|
|
1902
|
+
runId: "r", epoch: 1, candidateIndex: 0, candidateSeed: 1,
|
|
1903
|
+
goal: new GoalSpec({ version: 1, statement: "t", successCriteria: ["c"], hardConstraints: ["h"] }),
|
|
1904
|
+
evaluatorSpec: new EvaluatorSpec({ version: 1 }),
|
|
1905
|
+
genome: seedToGenome(1),
|
|
1906
|
+
persona: { name: "p", tagline: "t" },
|
|
1907
|
+
path: { id: "α", name: "x", seq: "P→A" },
|
|
1908
|
+
schedule: [new StageWave(0, ["P", "A"], "test")],
|
|
1909
|
+
prompt: "p",
|
|
1910
|
+
} as GenerationRequest;
|
|
1911
|
+
const stageResults = await fullRuntime.execute(req2, null as unknown as AuthorizedToolRegistry);
|
|
1912
|
+
add("stages-execute", stageResults.map((r) => r.node).join(",") === "P,A", stageResults.map((r) => r.node).join(","));
|
|
1913
|
+
|
|
1914
|
+
// Diversity
|
|
1915
|
+
const mon = new ArtifactDiversityMonitor(0.8, 3);
|
|
1916
|
+
let last: DiversityResult | null = null;
|
|
1917
|
+
for (let i = 0; i < 4; i++) last = mon.add(`c-${i}`, "same repeated artifact structure");
|
|
1918
|
+
add("diversity-collapse-detected", last?.collapseWarning === true, JSON.stringify(last));
|
|
1919
|
+
|
|
1920
|
+
// Jaccard identity
|
|
1921
|
+
const s1 = artifactShingles("alpha beta gamma delta epsilon");
|
|
1922
|
+
add("jaccard-identity", jaccard(s1, s1) === 1, "1.0");
|
|
1923
|
+
|
|
1924
|
+
// Grants
|
|
1925
|
+
const authority = new GrantAuthority("x".repeat(32), "k");
|
|
1926
|
+
const grant = authority.issue("agent-1", ["tool:echo"], 100, 2, "n", 100);
|
|
1927
|
+
add("grant-verifies", authority.verify(grant, 101), "valid");
|
|
1928
|
+
add("grant-expires", authority.verify(grant, 300) === false, "expired");
|
|
1929
|
+
|
|
1930
|
+
// Attestation
|
|
1931
|
+
const signer = new HMACAttestationSigner("s".repeat(32), "key-1");
|
|
1932
|
+
const builder = new AttestationBuilder(store, artifacts, signer, "run-1");
|
|
1933
|
+
const dummyCandidate = new Candidate(
|
|
1934
|
+
"candidate-1", "run-1", 1, 0, 1, "g".repeat(64), "e".repeat(64),
|
|
1935
|
+
seedToGenome(1), { name: "p", tagline: "t" }, { id: "α", name: "x", seq: "P" },
|
|
1936
|
+
[], "Artifact", "producer", "family", [], "root", "a".repeat(64),
|
|
1937
|
+
);
|
|
1938
|
+
const promo: PromotionRecord = {
|
|
1939
|
+
candidateId: "candidate-1",
|
|
1940
|
+
highestTier: "audit",
|
|
1941
|
+
promoted: true,
|
|
1942
|
+
evidenceClosed: true,
|
|
1943
|
+
reports: {},
|
|
1944
|
+
reasons: [],
|
|
1945
|
+
};
|
|
1946
|
+
// Need artifact row — already persisted above as candidate-1
|
|
1947
|
+
const envelope = builder.build(dummyCandidate, promo);
|
|
1948
|
+
add("attestation-verifies", builder.verify(envelope), "sig ok");
|
|
1949
|
+
|
|
1950
|
+
// Full demo
|
|
1951
|
+
const demo = await runInnovationGenomeV4Demo({ problem: "Produce an evidence-bound artifact.", domain: "general", seed: 42 });
|
|
1952
|
+
add("demo-seal", demo.tamper_evident_seal === true, "v3 chain");
|
|
1953
|
+
add("demo-semantic", demo.semantic_chain_valid === true, "v4 chain");
|
|
1954
|
+
add("demo-audit-pass", demo.audit.audit_pass === true, JSON.stringify(demo.audit));
|
|
1955
|
+
add("demo-has-outcomes", demo.outcomes.length >= 1, String(demo.outcomes.length));
|
|
1956
|
+
add("demo-evidence-closed", demo.outcomes.every((o) => o.evidence_closed), JSON.stringify(demo.outcomes.map((o) => o.evidence_closed)));
|
|
1957
|
+
|
|
1958
|
+
return { ok: checks.every((c) => c.passed), checks };
|
|
1959
|
+
}
|