diff --git a/.gitignore b/.gitignore index dcbce6c3..39a8526e 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ zig-cache/ # Native build byproducts. Keep native/rclvm.exe as the checked Windows VM. native/*.pdb native/*.o +*.obj native/*.a native/*.so native/*.dll diff --git a/README.md b/README.md index c42b0b3a..201086f5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# RCL — Reality Compiler Language +# RCL v0.94.0-alpha.1 — Reality Compiler Language **Current package:** `v0.94.0-alpha.1` -**Canonical source:** `xingxuling/RCL@main` +Canonical source: `xingxuling/RCL@main` **License:** Apache-2.0 > RCL is an evidence-bearing, permission-constrained reality transaction language, compiler, native VM, provider runtime, and verification toolchain. @@ -76,6 +76,14 @@ A failed required gate fails the cell. Missing evidence blocks it. No weighted s --- +## RBC 1.3 admission boundary + +RBC 1.3 `DOMAIN_CALL` remains an experimental research surface in PR #39. The strict autonomous-growth maximum is currently `Level 2 VERIFIED`; `Level 3 CANDIDATE/BLOCKED` is not promoted by a bounded native candidate, an ACL2 compatibility result, or C/WASM parity alone. Formal A10 requires the full independent Native Promotion chain, and canonical language/version changes remain human-gated. + +Evidence: [`RBC13_FINAL_BLOCKER_CLOSURE_EVIDENCE_LEDGER_v0.1.md`](docs/RBC13_FINAL_BLOCKER_CLOSURE_EVIDENCE_LEDGER_v0.1.md), [`RCL_CAPABILITY_ASSIMILATION_COMPATIBILITY_SURFACE_v0.1.md`](docs/RCL_CAPABILITY_ASSIMILATION_COMPATIBILITY_SURFACE_v0.1.md), and [`RCL_WASM_DOMAIN_ORGAN_ABI_v0.1.md`](docs/RCL_WASM_DOMAIN_ORGAN_ABI_v0.1.md). + +--- + ## Current killer-task frontier | Task | Target | Coverage mode | Current result | Main remaining blocker | diff --git a/docs/A3_LEGACY_EVIDENCE_CLOSURE_REPORT_v0.1.md b/docs/A3_LEGACY_EVIDENCE_CLOSURE_REPORT_v0.1.md new file mode 100644 index 00000000..842e2368 --- /dev/null +++ b/docs/A3_LEGACY_EVIDENCE_CLOSURE_REPORT_v0.1.md @@ -0,0 +1,49 @@ +# A3 Legacy Evidence Closure Report v0.1 + +- Status: **VERIFIED** +- Branch: `research/rbc13-domain-call-salvage-v0.1` +- Commit: `8cf703eb01ac00850e82fd93c45ac5655d24918e` +- Expected inventory: 6 stable IDs +- Inventory root: `c33453601f567a33a11413b7d958f864ebca7f1445b65cae16b5f70a04003a8a` +- Evidence root: `d8a439e0aa0a88552a118557106996de86bd41ae0ad7524ce6991d95b60b863a` +- Reproduction: `npm run verify:rbc13-legacy-evidence-closure` + +## Scope + +The committed expected inventory is authoritative for this closure. It contains one current-source RBC 1.1 Stage-5 encoder case and five current-source RBC 1.2 Foundation Native bridge cases. RBC 1.3 experimental Domain Organ cases are intentionally excluded. + +## Receipt inventory + +| Case | RBC | Runtime | Source root | Bytecode root | Result root | Receipt root | Replay | +|---|---:|---|---|---|---|---|---| +| rbc11.stage5.encoder | 1.1 | rcl-native-vm/0.6.0-alpha.1 | fd9b4e1db21e91221eb89e11002650b5be1b1e36f416f7982ee474f00615011f | 2d99bbd95d7e67077db9694d156faf9c43484e58bff54c3381a3a32857071aea | 57d29cd1e7990002f842657d9862e1d3a04135671985ab6bf3f895371058301d | 8efbb04ceda586aaabbb9e4462a1333894de105614b995f06b44f6ef30a55f4d | VERIFIED | +| rbc12.foundation.batch-a | 1.2 | rcl-native-vm/0.6.0-alpha.1 | 1c6d79d81ce5f6af02e73dde5021418df5061a73daabeadb72bfad2f143e88f8 | 3705305f0548d2945a313609c2f27da0c24166f31ee9659dc5ba7b0e57403209 | ea668d63168483bf6bc8e68a2c0166338df5912f355f2ae0edce5fc1d30d91ad | c44495f0c43bef3b895fe2ec1d5f0340b6bd965073017044784c426f9ea994de | VERIFIED | +| rbc12.foundation.meta-batch-b | 1.2 | rcl-native-vm/0.6.0-alpha.1 | a26349e4cfe61007dfdfff228a8a1f8047addda1029e062a2f65f5346aa8fdff | e5ed4c14852ed7d531e36eed46779533d9c7905deecb34364e19edaff8d3048f | 5563283f81d4e407af65129034a5cb80d3a2d9a19082ca9de0f15e3cc7467df3 | c055733b5d1a85c66df196f4359af5b8905c540cc547236705ba30a1cca66968 | VERIFIED | +| rbc12.foundation.batch-c | 1.2 | rcl-native-vm/0.6.0-alpha.1 | 8837e82b8e2a40dd1100044d6c8145cb7f4e5cd79b069944fcbb0dccfa808df3 | b9d3885ffdbcbaaf6f51fa229656bc235a1b6217f555e2f86f4983d2da3f14fa | 2740eb8b9fb8ff18d3aeae50a68996ab837e10601fbd38bb8beac741ae6a04ed | 7adaf88a4643e238f95d8483767d2b20b40a7a6ef0e6fa6da00ed3908f3c17da | VERIFIED | +| rbc12.foundation.batch-d | 1.2 | rcl-native-vm/0.6.0-alpha.1 | 850f89424395477a24c9cd33be1c420110240b5f6db30c5df9a0047a2f50c4b7 | 46c8480d2232d73312f8072a450d028f6544142a24654787f1f4c54e752f70db | 60e94e8d86e2413c4b61fd0d1441315a895e8144b0b56e2a3e5c38e97d116b31 | 1a8a584fca7a9a2db345c9e4f9aa7af7a6ef749b8bea2773809a8389520e798a | VERIFIED | +| rbc12.foundation.batch-e | 1.2 | rcl-native-vm/0.6.0-alpha.1 | ddd309904ac311c9878ef622238dec862e245d3ec1f7906c2ff3022960eeeb83 | 804225ba2601885ea8f96a1e97d35dfccf396f56027d18926d1067c36ed58290 | f501994fd6c4654f092985381654aa8f0c81e66681cbe15c38fbbade7ba87496 | 777ad97ea367f64e8db13e7e350cfc0173b31f0338940cc3d1dc2243ac1af2ea | VERIFIED | + +## Closure checks + +- expectedInventoryAuthoritative: PASS +- stableIdsUnique: PASS +- noUnexpectedCases: PASS +- noMissingCases: PASS +- noDuplicateReceiptRoots: PASS +- noStaleReceipts: PASS +- noAlteredReceipts: PASS +- replayRootConsistency: PASS +- rbc11Verified: PASS +- rbc12Verified: PASS + +## Integrity findings + +- Missing: none +- Duplicate: none +- Stale: none +- Altered: none +- Replay mismatch: none + +## Boundary + +This closes only the committed RBC 1.1/RBC 1.2 legacy receipt inventory. It does not canonize RBC 1.3 or validate the experimental AI and Universal tracks. diff --git a/docs/A3_VERSION_LEDGER_CONTRACT_CLOSURE_v0.1.md b/docs/A3_VERSION_LEDGER_CONTRACT_CLOSURE_v0.1.md new file mode 100644 index 00000000..fe7cfe11 --- /dev/null +++ b/docs/A3_VERSION_LEDGER_CONTRACT_CLOSURE_v0.1.md @@ -0,0 +1,55 @@ +# A3 Version Ledger Contract Closure v0.1 + +- Status: **VERIFIED** +- Scope: RBC 1.1 / RBC 1.2 legacy receipt closure and current full-suite contract +- Receipt closure evidence root: `d8a439e0aa0a88552a118557106996de86bd41ae0ad7524ce6991d95b60b863a` +- Receipt inventory root: `c33453601f567a33a11413b7d958f864ebca7f1445b65cae16b5f70a04003a8a` +- Receipt inventory: 6 expected / 6 verified / 0 missing / 0 duplicate / 0 stale / 0 altered / 0 replay mismatch + +## Original failure + +The first full-suite reproduction failed in `tests/self-akashic-record-compiler.test.mjs` with the assertion `scan.counts.versionLedgerCount >= 60`. The observed current scan had 35 version-ledger-bearing records at the first reproduction; after this closure's added evidence documents it has 39. The production self-Akashic specification declares `minVersionLedgerCount = 28`. + +Minimal reproduction before the fix: + +```text +node --test --test-concurrency=1 tests/self-akashic-record-compiler.test.mjs +4 tests; 3 pass; 1 fail +AssertionError: 35 >= 60 +``` + +The focused reproduction after the fix is: + +```text +node --test --test-concurrency=1 tests/self-akashic-record-compiler.test.mjs +4 tests; 4 pass; 0 fail +``` + +## Root cause + +This was a test-assumption drift, not missing history, a stale manifest, a missing receipt, a generated-artifact omission, a component-version mismatch, or a ledger inventory defect. Git history shows the production threshold `28` originated in `2ef6e68`; commit `b1e4cef4` changed the test assertion from `>= 28` to `>= 60` while retaining the production threshold. No current version-contract or RBC 1.1/RBC 1.2 receipt definition was changed. + +## Changed files + +- `tests/self-akashic-record-compiler.test.mjs`: the test now reads `DEFAULT_SELF_AKASHIC_RECORD_SPEC.thresholds.minVersionLedgerCount` instead of duplicating a stale literal. +- `docs/A3_VERSION_LEDGER_CONTRACT_CLOSURE_v0.1.md`: this closure record. + +No legacy source, version history, expected receipt inventory, canonical language, or version-contract definition was deleted or rewritten. + +## Before/after contract + +| Surface | Before | After | Ruling | +|---|---|---|---| +| Production minimum | 28 | 28 | unchanged authoritative contract | +| Test minimum | hard-coded 60 | imported production minimum 28 | drift removed | +| Current scan | 35 at first reproduction | 39 after closure docs | above authoritative minimum | +| Legacy receipt inventory | 6 expected | 6 verified | unchanged | +| RBC 1.1 / RBC 1.2 | existing definitions | existing definitions | compatibility preserved | + +## Replay and final ledger root + +The six legacy receipts replay with stable source, bytecode, result, artifact, runtime-version, and RBC-version bindings. The self-Akashic scan now records 540 files, 172 modules, 36 documents, 162 tests, 247 commands, and 39 version-ledger records; its result root is `d5bb29e117668a80b8849b197e1274c7465ce487ac7eb6149a085bdabf584102`. + +The final RBC13 evidence ledger binds this closure root, the final full-suite summary, and the unchanged RBC 1.1/RBC 1.2 receipt roots. The final full-suite rerun is `741 total / 739 pass / 0 fail / 2 skip`; A3 admission is **VERIFIED** because the full suite is `0 FAIL`. + +Reproduction: `npm run verify:rbc13-legacy-evidence-closure` and `npm test`. diff --git a/docs/RBC13_ADMISSION_CLOSURE_EVIDENCE_LEDGER_v0.1.md b/docs/RBC13_ADMISSION_CLOSURE_EVIDENCE_LEDGER_v0.1.md new file mode 100644 index 00000000..1cf90584 --- /dev/null +++ b/docs/RBC13_ADMISSION_CLOSURE_EVIDENCE_LEDGER_v0.1.md @@ -0,0 +1,123 @@ +# RBC 1.3 Admission Closure Evidence Ledger v0.1 + +Date: 2026-08-09 (Asia/Shanghai) +Repository: `xingxuling/RCL` +Checkout: `C:\Users\User\Documents\RCL\_worktrees\rbc13-domain-call-salvage-v0.1` +Branch: `research/rbc13-domain-call-salvage-v0.1` +Source commit: `cc962af64138a8426f78c38a503f19ffd1024b4c` +PR: #39 +Final verdict: `BLOCKED` +Canonical admission: `false` +Readiness report root: `ac10f7592e9f183f89e7ffbb5779f86eb3260aae31c9c3ba4a30d7cee3597b2e` + +Historical snapshot notice: this v0.1 ledger records the pre-closure state. The current v0.2 closure is recorded in `docs/RBC13_FINAL_BLOCKER_CLOSURE_EVIDENCE_LEDGER_v0.1.md`; the A3 version-ledger contract is now closed, A10 uses the Compatibility Surface/ACL protocol, and A12 uses the independent C/WASM graph cell. + +## Evidence rule + +Each claim records `claim_id`, gate, status, commit, environment, command, input root, artifact root, result root, receipt root, test count/pass/fail/skip, blocker, and reproduction command. A local root is not a hosted-CI result. A candidate is not a canonical activation. No canonical branch, version contract, RBC 1.1/1.2 contract, or native VM source was changed. + +Environment for this closure: Windows x64, Node `v24.15.0`, package `0.94.0-alpha.1`, native VM `rcl-native-vm/0.6.0-alpha.1`, semantic root `rcl.semantic-state-root.v1`, MSVC `19.50` where the benchmark used a compiler. + +## Multicivilization sequence + +1. Founder Twin: renamed the task from “add RBC 1.3” to “close non-compensable admission evidence”. +2. 柳清莲 Gate: retained human authority over canonical/version mutation and rejected silent activation. +3. 洞哥 Grounding: bound every claim to repository facts, roots, commands, and explicit boundaries. +4. Product Civilization: kept the smallest runnable evidence loop and protected release compatibility. +5. Math/Formal Civilization: preserved binary64 raw-bit semantics, negative-zero policy, and version isolation. +6. Engineering Civilization: used the current native runtime, fixed protocols, replay checks, and fail-closed adapters. +7. Code Civilization: added A3 closure, A10 threshold, A12 graph-cell probe, and readiness wiring. +8. Test Civilization: ran focused RBC13, RBC 1.1/1.2, Universal Stress, selfhost, and full regression suites. +9. Security Civilization: preserved warrants, evidence tiers, causal parents, negative controls, and no AI implementation leakage. +10. Release Civilization: kept package/version/canonical contracts unchanged and separated local evidence from hosted CI. +11. Integration Court: accepted the research evidence but returned canonical admission `BLOCKED`. +12. Evidence Ledger: sealed the roots, counts, blockers, reproduction commands, and next gates in this document. + +## Claim ledger + +| Claim ID | Gate | Status | Commit / environment | Command | Input root | Artifact root | Result root | Receipt root | Tests | Blocked reason | Reproduction | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| `A1.number-v2` | A1 | `VERIFIED` | `cc962af`; Windows x64 / Node 24.15.0 | `npm run verify:rbc13-number-encoding-v2` | v2 corpus `a7071d47b65ad721ff3098b977fc607106ac96bb0ffb5910c3ce998f44894da7` | `output/rbc13-number-encoding-v2/corpus-report.json` | `99b3021a186f9c6f77761ed652f68efec7415220e5262f79adc956a7719a163f` | `a7071d47b65ad721ff3098b977fc607106ac96bb0ffb5910c3ce998f44894da7` | 11,000 cases; 0 mismatch; 11,000 round trips; 3/3 nonfinite rejects | none; no canonical activation | same command | +| `A2.native-promotion-inventory` | A2 | `VERIFIED` | `cc962af`; native VM `0.6.0-alpha.1` | `node scripts/run-rbc13-domain-native-promotion.mjs output/rbc13-domain-native-promotion/native-promotion-final-2026-08-09.json` | current-source candidate VM; native artifact `f2d38852d5f3173f3208c8ba42747e02ba02a0482dc4b9bcb021f2a5aef22517` | four-operation promotion report | `ac5a2fda3ad883b1ee9e560b499d002904a3d05ef87e71222de65793e8e4fd31` | `745e95d74581a0de1a28a3c6bf88a856667272ff42e20cd577731febb5551470`; `17a7e634cbe4cd0142ef0d6f8d36cf17585ecc939a8dc181fe1edf9940b56674`; `8411adc36c7320d85e48ba1b00fd6c07f85f2c576a62a527981066228cedb364`; `d2301eea3fa7833b66b65f85b1cacabfd4b0697cd7ef79b3a57c1545a910b0a0` | 4 operations; positive/negative/replay/semantic-root gates pass | none; candidate evidence is not canonical promotion | same command | +| `A3.legacy-receipt-closure` | A3 | `VERIFIED` as receipt closure; admission remains blocked by full suite | `cc962af`; Windows x64 / native VM `0.6.0-alpha.1` | `npm run verify:rbc13-legacy-evidence-closure` | inventory `c33453601f567a33a11413b7d958f864ebca7f1445b65cae16b5f70a04003a8a` | six current source/bytecode/result/artifact roots in the record table below | `c40d999c5f5e8d4c837d28a40f7dfd15cf9c209bc17fe6d95a129c450ee4d13c` | six receipt roots in the record table below | expected 6; verified 6; missing 0; duplicate 0; stale 0; altered 0; replay mismatch 0 | full `npm test` is not `VERIFIED` | same command | +| `A4.positive-semantic-equivalence` | A4 | `VERIFIED` | `cc962af`; current-source native candidate | native promotion suite | four operation inputs | four operation reports | `ac5a2fda3ad883b1ee9e560b499d002904a3d05ef87e71222de65793e8e4fd31` | `745e95d74581a0de1a28a3c6bf88a856667272ff42e20cd577731febb5551470`; `17a7e634cbe4cd0142ef0d6f8d36cf17585ecc939a8dc181fe1edf9940b56674`; `8411adc36c7320d85e48ba1b00fd6c07f85f2c576a62a527981066228cedb364`; `d2301eea3fa7833b66b65f85b1cacabfd4b0697cd7ef79b3a57c1545a910b0a0` | focused evidence pass | none | promotion command | +| `A5.negative-semantic-equivalence` | A5 | `VERIFIED` | `cc962af`; current-source native candidate | native promotion suite | invalid/error cases | rejection receipts | `ac5a2fda3ad883b1ee9e560b499d002904a3d05ef87e71222de65793e8e4fd31` | `745e95d74581a0de1a28a3c6bf88a856667272ff42e20cd577731febb5551470`; `17a7e634cbe4cd0142ef0d6f8d36cf17585ecc939a8dc181fe1edf9940b56674`; `8411adc36c7320d85e48ba1b00fd6c07f85f2c576a62a527981066228cedb364`; `d2301eea3fa7833b66b65f85b1cacabfd4b0697cd7ef79b3a57c1545a910b0a0` | focused negative controls pass | none | promotion command | +| `A6.deterministic-replay` | A6 | `VERIFIED` | `cc962af`; native VM `0.6.0-alpha.1` | native promotion and RBC 1.1/1.2 suites | fixed inputs and replay inputs | native receipts | `ac5a2fda3ad883b1ee9e560b499d002904a3d05ef87e71222de65793e8e4fd31` | replay roots `745e95d74581a0de1a28a3c6bf88a856667272ff42e20cd577731febb5551470`; `17a7e634cbe4cd0142ef0d6f8d36cf17585ecc939a8dc181fe1edf9940b56674`; `8411adc36c7320d85e48ba1b00fd6c07f85f2c576a62a527981066228cedb364`; `d2301eea3fa7833b66b65f85b1cacabfd4b0697cd7ef79b3a57c1545a910b0a0` | 34 focused: 33 pass, 1 skip; 96 RBC 1.1/1.2: 96 pass | native-promotion test skip is infrastructure-only | focused commands | +| `A7.semantic-root-evidence` | A7 | `VERIFIED` | `cc962af`; root algorithm v1 | native semantic-root tests and promotion suite | current source and typed values | native state-root receipts | four operation roots | replay roots equal state roots | focused semantic-root assertions pass | none | focused command | +| `A8.authority-boundary` | A8 | `VERIFIED` | `cc962af`; canonical branch remains `main` | `npm run verify:version-contract` and native promotion | `VERSION-CONTRACT.json`, `COMPONENT-VERSIONS.json`, `DOWNSTREAM-CONSUMERS.json` | contract verification | `ef9fe2d8dcfa0b9dff5863b079a36f4dbc871714e388e3244ce2c29c91ad208b` | native suite `ac5a2fda3ad883b1ee9e560b499d002904a3d05ef87e71222de65793e8e4fd31` | version contract 0 errors; authority/evidence gates pass | no canonical mutation authorized | same commands | +| `A9.execution-benchmark` | A9 | `VERIFIED` | `cc962af`; Windows x64 / MSVC 19.50 | `npm run verify:rbc13-execution-benchmark` | fixed seed `RBC13-PERFORMANCE-2026-08-09`; input `9007199254740991` | three declared in-process paths | `01fe49cfd65f264713442cc0fca4ebbe8fe22d45475b8b6cbcf1f61601757078` | host `3953f65c79a7e7145b41ed1c71d595cbe2fc5b05dac07dd37bd045395a3b0fea` | 3 paths x 3 iteration sizes x 7 repetitions; variance/RSS/allocation recorded | no network-latency or competitive-ranking claim | same command | +| `A10.ai-assimilation-threshold` | A10 | `NEGATIVE_RESULT` | `cc962af`; Windows x64 / Ollama `http://localhost:11435/api/generate`; 0 human interventions | `npm run verify:rbc13-ai-assimilation-threshold` | prompt `c9165f68465ed1ffa5bbf4730308bd072eefd3660e08c8ecf4293e7407725ebe` | donor spec `bc05b4a9df5f3bdf71af6c4cc9660dba90c4ce61c2a69683f56b65a3666630a5` | `a53e9fd74f733bc5d8d996f5ae780fc6b297c8116a399fd729bf97231e756f38` | `eda11a442ade6910e327f2358e24bc65dc199754f512ba99a17c1bd4824a0e8c`; `07a8e9f9fc400fe52e9cf1771938912473b76a972c1f69dfc3aef236aba1a717`; `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` | 3 tiers; max L2; min L0; contract/differential/native/promotion not all closed | no tier reached L4; no native promotion credit | same command | +| `A11.selfhost-version` | A11 | `VERIFIED` | `cc962af`; Windows x64 / native VM `0.6.0-alpha.1` | fixedpoint, examples, Stage40, version-contract commands | current source and checked-in selfhost artifacts | fixedpoint `20e0ac0c0cbcc049d14b56c565574f70d023ce47baebb491aae83da956ebc84d`; examples `3164b0a50b01ab604a666ef144f92dcf56dfe72839c1be2bd4fee62a55adf3a9`; Stage40 `b46f2d15612621cf06c4916d3e0f9bb817ab5ee73ec506717b561eb540d7223a`; contract `ef9fe2d8dcfa0b9dff5863b079a36f4dbc871714e388e3244ce2c29c91ad208b` | corresponding roots | 9/9 fixedpoint; 17 eligible examples / 0 failures; 18/18 Stage40 checks; contract 0 errors | whole-language runtime selfhosting remains false by contract | same commands | +| `A12.graph-growth-cell` | A12 | `BLOCKED` | `cc962af`; Windows x64 / native VM `0.6.0-alpha.1`; wasm adapter absent | `npm run verify:rbc13-universal-growth-cell` | graph source `7a7351fb1f1b36d43920db871aa21934577544d5cc33cbe56072e1b506a555eb` | native bytecode `0c941975f962ec5c0854a993a620b52975e38788bcf7424877ab31c6ac1c233e` | native state root `e54e26024c5f3e15f9dc5b9040a41525726bf61c1a8dda7703df37424ee4c898` | no universal-growth receipt; report `0c51d6f19e2d735969b5d37fde159d21dd0cadc40a6435653b92b9e265c90ecd` | native result/replay pass; 1 A12 test pass | blocker `wasm-vm-runtime-and-abi-unsupported`: no wasm adapter/binary, opcode bridge, memory/value ABI, error ABI, semantic-root bridge, replay harness, or host-boundary proof | same command | + +### A3 stable case records + +| Stable ID | RBC | Source root | Bytecode root | Result root | Artifact root | Receipt root | Replay root | Runtime | +| --- | --- | --- | --- | --- | --- | --- | --- | +| `rbc11.stage5.encoder` | 1.1 | `fd9b4e1db21e91221eb89e11002650b5be1b1e36f416f7982ee474f00615011f` | `2d99bbd95d7e67077db9694d156faf9c43484e58bff54c3381a3a32857071aea` | `57d29cd1e7990002f842657d9862e1d3a04135671985ab6bf3f895371058301d` | `3c3fe05fffd1b9e257bf6c754655afe2a84c08e91f8090d8a06c87edbc7f3e0b` | `46a3766c66bf884781e2a98d345221d855069a61a2d6159b7337e9dbe9aa4681` | `0436623c59d258862cdab71a2f33c6010b4c40edec4470293e30a95043b41a8d` | `rcl-native-vm/0.6.0-alpha.1` | +| `rbc12.foundation.batch-a` | 1.2 | `1c6d79d81ce5f6af02e73dde5021418df5061a73daabeadb72bfad2f143e88f8` | `3705305f0548d2945a313609c2f27da0c24166f31ee9659dc5ba7b0e57403209` | `ea668d63168483bf6bc8e68a2c0166338df5912f355f2ae0edce5fc1d30d91ad` | `876229232725a97849e77c786e88f8d31916a9554e19144f90af21b82ad38fac` | `9355423dc517b0e2450e39a77d1e6d0eca021abbdf2cc671dbb0862ea3274f59` | `0ec9121a4c0367e51de068b06d294d4ba2bec4f4a8f5e76ba5c0d253a2288b3d` | `rcl-native-vm/0.6.0-alpha.1` | +| `rbc12.foundation.meta-batch-b` | 1.2 | `a26349e4cfe61007dfdfff228a8a1f8047addda1029e062a2f65f5346aa8fdff` | `e5ed4c14852ed7d531e36eed46779533d9c7905deecb34364e19edaff8d3048f` | `5563283f81d4e407af65129034a5cb80d3a2d9a19082ca9de0f15e3cc7467df3` | `84417b44cdc8d6d3212bd6835b5ccbef153a7da355577c684549a16cdc5b34b7` | `9ec977f5bda920ba80d96c7b5950e808e37afef9c9d0160f033afd5a3fe09492` | `1d22b8565c04538abe431b5d644d3776d0e0322cf546e61ce23afc6859741318` | `rcl-native-vm/0.6.0-alpha.1` | +| `rbc12.foundation.batch-c` | 1.2 | `8837e82b8e2a40dd1100044d6c8145cb7f4e5cd79b069944fcbb0dccfa808df3` | `b9d3885ffdbcbaaf6f51fa229656bc235a1b6217f555e2f86f4983d2da3f14fa` | `2740eb8b9fb8ff18d3aeae50a68996ab837e10601fbd38bb8beac741ae6a04ed` | `f020922854707300dc8bc4569105b765e6a3fb70ddc15619830fe2543b2e070c` | `e6da356a9fd341bd2e16331bc122341d177fb4ad15e258add4903cd33fd9613f` | `706b39f9731158570d2fe21a1f80cd532fa11308b04c3a6f1ad7052e279335e0` | `rcl-native-vm/0.6.0-alpha.1` | +| `rbc12.foundation.batch-d` | 1.2 | `850f89424395477a24c9cd33be1c420110240b5f6db30c5df9a0047a2f50c4b7` | `46c8480d2232d73312f8072a450d028f6544142a24654787f1f4c54e752f70db` | `60e94e8d86e2413c4b61fd0d1441315a895e8144b0b56e2a3e5c38e97d116b31` | `e7a5f467a8e11bc04bd57c6977030694924414ded56a38766abe2330f3650921` | `aa1c8d9ed6a44dd5828910ec21a4bdc4e2e702c9692c1c594753618d52c03263` | `4877fe73b3ef8a7f209ce08843e043c590c6747c9a7ab783b9df0232854041e0` | `rcl-native-vm/0.6.0-alpha.1` | +| `rbc12.foundation.batch-e` | 1.2 | `ddd309904ac311c9878ef622238dec862e245d3ec1f7906c2ff3022960eeeb83` | `804225ba2601885ea8f96a1e97d35dfccf396f56027d18926d1067c36ed58290` | `f501994fd6c4654f092985381654aa8f0c81e66681cbe15c38fbbade7ba87496` | `ee8ed7f9a68ca1543af75dae58af3a375cc994388e13934e11a3e17de52c0fd0` | `2529518cbd9586ee2273e3ada59b23655c72a798b53673fb1987a9d90dea6727` | `51fcf086a3f4d575a36e6d8be9e0f7f2e5c99052d75fd689d97efb6345bec235` | `rcl-native-vm/0.6.0-alpha.1` | + +The A3 verifier used the authoritative inventory, ran each stable case from current source, and checked source-root, bytecode-root, result-root, artifact-root, runtime receipt, replay root, runtime version, and RBC version. It detected no missing, duplicate, stale, or altered receipt. + +## A10 threshold detail + +The fixed chain was `Donor Spec → Extraction → Signature → Contract → Positive/Negative Corpus → Candidate → Differential → Mutant Detection → Repair Attempt → Native Candidate → Promotion Attempt`. No implementation code, hidden tests, expected outputs, or human repair were supplied or applied. + +| Tier | Model / model version | Level | Contract | Candidate | Differential | Mutant detection | Repair | Native candidate | Promotion | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Small | `aetherseed-tinyllama-runtime:latest` / `ca5641fd566c` / 637 MB | L0 | fail: exact required fields and `unit` | semantic-absorbed | blocked: no independent JSON Schema validator adapter | detected | not attempted; 0 human | blocked | blocked | +| Medium | `aetherseed-trained-smoke:latest` / `5cd5d497f398` / 5.2 GB | L2 | pass | semantic-absorbed; no equivalence cases | blocked: no independent JSON Schema validator adapter | detected | not attempted; 0 human | blocked | blocked | +| Strong | `qwen3.5:latest` / `6488c96fa5fa` / 6.6 GB | L0 | fail: no JSON object | unavailable | blocked | unavailable | not attempted; 0 human | blocked | blocked | + +Level definitions: L0 means unavailable/invalid contract; L1 means contract plus extraction/corpus incomplete; L2 means candidate exists but differential is unavailable/failed; L3 means differential exists but native/promotion is incomplete; L4 means independent native candidate and promotion evidence with zero human repair. The result is scoped only to this donor, protocol, and listed local models. + +## A12 wasm-vm audit + +The graph traversal workload is `experimental-native-semantic`, not canonical native-semantic. Native execution produced `reachable=true`, `unreachable=false`, deterministic replay, semantic-root parity, and `noProviderFallback=true`. Universal growth eligibility remains false because the wasm-vm side is not present. The exact blocker class is `wasm-vm-runtime-and-abi-unsupported`; compile, bytecode/opcode-45 bridge, memory/value ABI, error ABI, semantic-root bridge, replay harness, and host-boundary proof are all missing. No silent provider fallback was accepted. + +## Regression ledger + +| Claim ID | Status | Commit | Command | Result / roots | Tests | Blocker | +| --- | --- | --- | --- | --- | --- | --- | +| `RBC13.focused` | `VERIFIED` | `cc962af` | focused RBC13 command in A1-A12 report | summary root `abebbc61b115b85db4be2d92004f31d43484f17baeff4936a88ebe9cfeeaa3e0` | 34 total; 33 pass; 0 fail; 1 skip | one native-promotion infrastructure skip | +| `RBC11-RBC12.focused` | `VERIFIED` | `cc962af` | six language/Foundation bridge test files | summary root `cd5fd632ff6075c2a290d548bb9cd267b409274b21102f1644ebb56291050b4a` | 96 total; 96 pass; 0 fail; 0 skip | none | +| `Universal Stress suite` | `VERIFIED` as regression suite | `cc962af` | seven Universal Stress/A12 test files | summary root `4b5f324c9b74d522bcf75681589de9c5ee23091a22e381ef8c47608171ed04a6`; existing four-op candidate root `83d125ccc171878914d24ed195565de7346023a1349c47bfa01c6f4326718026` | 31 total; 31 pass; 0 fail; 0 skip | candidate growth admission still false because A10 is negative | +| `npm-test.full` | `BLOCKED` | `cc962af` | `npm test` | summary root `797d259e20801db4b00d5ab57e3a77d951484e84f172ec5c9259168a06e3a5f9` | 733 total; 730 pass; 1 fail; 2 skip | existing `v0.57 scans RCL repository as finite self-record`: actual `versionLedgerCount=34`, assertion requires `>=60`; no RBC13 test failed | +| `native-boundary` | `VERIFIED` | `cc962af` | `npm run verify:native-boundary` | native PE `f2d38852d5f3173f3208c8ba42747e02ba02a0482dc4b9bcb021f2a5aef22517`; boundary root `58c1e7d0e26b35756cee78e6af14a27786ef76fe129ce021ef3c3c78b6404590` | smoke pass | compiler PATH absence is recorded, not treated as native artifact failure | + +## Admission result + +| Gate | Status | Evidence | Blocking reason | +| --- | --- | --- | --- | +| A1 | `VERIFIED` | Number v2 report/corpus | none | +| A2 | `VERIFIED` | four-operation native promotion | none | +| A3 | `BLOCKED` | A3 closure `VERIFIED`, full suite not closed | self-Akashic version-ledger assertion | +| A4 | `VERIFIED` | positive semantic differentials | none | +| A5 | `VERIFIED` | negative/error differentials | none | +| A6 | `VERIFIED` | replay roots | none | +| A7 | `VERIFIED` | semantic-root parity | none | +| A8 | `VERIFIED` | authority/version contract | none | +| A9 | `VERIFIED` | execution benchmark | none | +| A10 | `NEGATIVE_RESULT` | three-tier threshold, max L2 | no tier reached L4 | +| A11 | `VERIFIED` | fixedpoint/examples/Stage40/version contract | none | +| A12 | `BLOCKED` | graph cell native pass, wasm-vm audit blocked | wasm-vm runtime and ABI unsupported | + +Integration Court therefore returns `BLOCKED`; no canonical changes are authorized and no separate `feat/rbc13-canonical-admission` PR is proposed in this sprint. Existing PR #39 remains a research/evidence PR only. + +## Hosted checks and next step + +For the source-evidence push at `632d7702e08757639e44c96f3636e84240e74cde`, hosted run `31284594800` (Authority Contract) and run `31284594799` (Canonical Verification) both failed with `steps=[]` before executing a job step. They are classified as `INFRASTRUCTURE_BLOCKED`, not as local test failures. The Vercel preview context was still pending at the observation point; it has no admission authority. + +Strict autonomous growth assessment in this historical snapshot: `Level 2 VERIFIED`; `Level 3 CANDIDATE/BLOCKED`. A reproducible native graph candidate or a compatibility ACL2 result does not establish strict Level 3 without an AI-generated implementation and independent differential verification, and no formal Native Promotion is inferred. + +Required next gates: + +1. Resolve the existing self-Akashic version-ledger contract/test drift without weakening the intended contract. +2. Supply a new blinded JSON Schema donor trial that reaches independent differential evidence and Native Promotion, still with zero human repair. +3. Add the missing wasm-vm runtime/ABI evidence or choose a workload supported by an inspectable wasm adapter; do not count opaque delegation. +4. Re-run A1-A12 from one final evidence root and obtain a new Integration Court decision. +5. Only if all 12 gates are `VERIFIED`, open a separate `feat/rbc13-canonical-admission` PR; never activate RBC 1.3 from PR #39. diff --git a/docs/RBC13_CANONICAL_ADMISSION_EVIDENCE_LEDGER_v0.1.md b/docs/RBC13_CANONICAL_ADMISSION_EVIDENCE_LEDGER_v0.1.md new file mode 100644 index 00000000..bbedbc4f --- /dev/null +++ b/docs/RBC13_CANONICAL_ADMISSION_EVIDENCE_LEDGER_v0.1.md @@ -0,0 +1,76 @@ +# RBC 1.3 Canonical Admission Evidence Ledger v0.1 + +Date: 2026-08-09, Asia/Shanghai. Repository: `C:\Users\User\Documents\RCL\_worktrees\rbc13-domain-call-salvage-v0.1`. Branch: `research/rbc13-domain-call-salvage-v0.1`. Source-audit base: `a2b732cb07150e3e5097ad408105e0bf48fc83d3`. The sealing commit is the commit that adds this ledger and is recorded by the Git handoff. + +## Ledger rule + +Every row records the claim, status, command, environment, source/input identity, artifact/output roots, counts, blocker, and reproduction path. A local evidence root is not a hosted-CI result. A candidate report is not a canonical activation. + +This file is a historical snapshot from the earlier closure attempt. Its `npm test` and native-only A12 blocker rows are preserved as history; the current generated A1-A12 result is `docs/RBC13_FINAL_BLOCKER_CLOSURE_EVIDENCE_LEDGER_v0.1.md`. + +## Evidence rows + +| Claim | Status | Command/environment | Input/artifact root | Output root and counts | Blocker | +| --- | --- | --- | --- | --- | --- | +| Number v2 JS/C parity | `VERIFIED` | `npm run verify:rbc13-number-encoding-v2`; Windows x64; MSVC 19.50 | corpus `a7071d47b65ad721ff3098b977fc607106ac96bb0ffb5910c3ce998f44894da7` | report `c8b8f68438fd0c161999f5e9ca17666a3e282a29e5d3dcfce13dc77691f14a9a`; 11,000 per-case ledger rows, 0 mismatch, 11,000 round trips, C nonfinite rejects 3/3 | none for isolated encoding; canonical activation remains out of scope | +| Number v1 preservation | `VERIFIED` | `npm run verify:version-contract`; v1 corpus audit | existing v1 source/root contract | v1 remains active and unchanged; old audit remains 7/10 on its extended sample | v1 decimal mismatch is historical evidence, not a v2 rewrite | +| Native Promotion inventory | `VERIFIED` | fresh `native-promotion-final-2026-08-09.json` | suite `f34b8c31eee2aac3223c31410829fec579b33f02b009cd7cfa538468b7f07b0b` | operation roots `9bd24818b5123d5cda695fab4bf1533044bce61d8c79acbddf8b49f56067a50e`, `f5afbec31df3e7511b49f15c5e63110172a7af64a52a85afcd7944877c89f455`, `30978d9aca37eac37a9ed1dc0733c861268f409e1b5a0b349cfc362a790e28cd`, `d11ecae29bf809dc78e296c3af71f4619c5c4e08c280a1aea6f5c667f4591580` | no new donor promotion in this round | +| Performance evidence | `VERIFIED` | `npm run verify:rbc13-execution-benchmark`; fixed seed, warmup, 7 repetitions | host `51d37188584830c147ebe9d1ff4538add9341ff5606c0d2223fccdf6b1ba067f` | report `014abc49fc58d5d916e254f32fcfb3913acaa4e3232878962cc664901c30cad4`; 1k/10k/100k for all 3 paths; median/p95/variance/RSS/allocation recorded | no network latency or full structured-value matrix claim | +| AI_GENERATE JSON Schema donor | `NEGATIVE_RESULT` | `npm run verify:rbc13-ai-generate`; local Ollama model; no implementation/source/expected output in prompt | prompt `c9165f68465ed1ffa5bbf4730308bd072eefd3660e08c8ecf4293e7407725ebe`; response `9530eadbc61384bbb865a6bb37ea76573102ed23803c5f6cd9702889f4b26295` | report `32d72528b0acba16bc2dfe47a529603f69f6542d818597920fbb85d4d0ce82ef`; extraction `0d311ab94e16be16d8f5162091a8046753900e58807621ffde0fe362e08db462`; corpus `778ba7ba4f44d499994096055ac06617aa132640886c8c44b9d2893eb1b7963c`; 16 cases/10 mutation plans | draft-07 and missing `unit` constraint; 0/1 successful trials | +| AI replay | `NEGATIVE_RESULT` | explicit fixture replay command; same local host | replay response root `9530eadbc61384bbb865a6bb37ea76573102ed23803c5f6cd9702889f4b26295` | replay report `0154c480b805753d48e3bc95dc7f5b14dd92bd5fb8d0856e6609f7a54a8646a5` | preserves the same donor failure; no repair or promotion credit | +| Universal Stress RBC13 cell | `BLOCKED` | `npm run verify:rbc13-domain-stress-probe` | fresh native evidence plus performance/AI reports | root `2ab51068aa3ac6259c22116371babad23d09beb06e3a5e88bac8403ce906f1cd`; special-case audit remains intact; growth eligible false | AI_GENERATE negative; cell cannot receive universal growth credit | +| Selfhost fixed point | `VERIFIED` | `npm run verify:selfhost-fixedpoint`; Windows x64 | current source and selfhost artifacts | 9 tests, 9 passed, 0 failed; 127.2 s | does not prove complete RBC13 self-emission | +| Selfhost examples | `VERIFIED` | `npm run verify:selfhost-examples` | 52 scanned, 16 eligible | 16 eligible, 0 failures | unsupported examples remain explicitly unsupported | +| Selfhost Stage40 | `VERIFIED` | `npm run verify:selfhost-stage40` | stage40 source/artifacts | byte parity and runtime authority evidence verified | dual-needs subset boundary remains | +| Native Windows boundary | `VERIFIED` | `npm run verify:native-boundary` | `native/rclvm.exe` PE artifact | Windows PE and default native smoke run verified | does not prove C compiler availability through PATH | +| Focused RBC13 regression | `VERIFIED` | `node --test --test-concurrency=1 tests/native-semantic-state-root-contract.test.mjs tests/native-semantic-state-root-native.test.mjs tests/native-capability-promotion.test.mjs tests/capability-metabolism.test.mjs tests/differential-absorption-runner.test.mjs tests/rbc13-number-encoding-v2.test.mjs tests/rbc13-canonical-admission-readiness.test.mjs tests/rbc13-domain-universal-stress-probe.test.mjs` | current source | 40 tests, 40 passed, 0 failed, 0 skipped, 1.21 s | none | +| Full legacy regression | `BLOCKED` | `npm test`; current Windows x64 checkout | current source; native pretest artifact verification | 729 tests, 725 passed, 2 failed, 2 skipped, 369.158 s | one stable v0.57 version-ledger count assertion; one intermittent Windows `EPERM rename` in RCLApp CLI (the isolated 3-test RCLApp suite passed) | +| Version contract | `VERIFIED` | `npm run verify:version-contract` | package `0.94.0-alpha.1` and current contracts | canonical semantic root v1; canonical RBC 1.1/feature 1.2 unchanged | no version change authorized | +| Hosted PR checks | `BLOCKED` infrastructure observation | PR #39 check inspection | pre-sealing base; runs `31258071094`, `31258071169` | Authority Contract and Canonical Verification reported failure before steps; `steps=[]` | classify as `INFRASTRUCTURE_BLOCKED`, not local test failure | + +## Admission gate ledger + +| Gate | Status | Evidence/blocker | +| --- | --- | --- | +| A1 Number canonicality | `VERIFIED` | v2 report/corpus roots | +| A2 Native Promotion inventory | `VERIFIED` | fresh four-operation promotion roots | +| A3 Legacy regression closure | `BLOCKED` | full suite is not closed | +| A4 Positive semantic parity | `VERIFIED` | four-operation native receipts | +| A5 Negative semantic parity | `VERIFIED` | rejection/error receipts | +| A6 Replay determinism | `VERIFIED` | native receipt and focused replay evidence | +| A7 Semantic-root evidence | `VERIFIED` | v1/v2-isolated root reports | +| A8 Authority/evidence preservation | `VERIFIED` | native promotion and current-source binding | +| A9 Performance evidence | `VERIFIED` | benchmark report/host roots | +| A10 AI_GENERATE donor | `NEGATIVE_RESULT` | donor contract failure, 0/1 trials | +| A11 Selfhost/version contract | `VERIFIED` | fixed point, examples, Stage40, version contract | +| A12 Universal Stress admission cell | `BLOCKED` | AI gate negative; growth eligibility false | + +Readiness report root: `84fdc9d1f7c5cdb97adc78272b0cc12dbdfab5e2cf81583c05ad8a1eec58227d`. Overall verdict: `BLOCKED`; canonical readiness is false; blocking gates are A3, A10, and A12. The report is an admission input and does not activate a version. + +## Multicivilization sequence + +The required sequence was recorded as an engineering review chain: + +1. Founder Twin — renamed the task from “add RBC13” to “close non-compensable canonical evidence”. +2. 柳清莲 Gate — required explicit human authority and rejected silent canonical/version mutation. +3. 洞哥 Grounding — bound claims to repository facts, roots, and reproducible commands. +4. Product Civilization — kept the smallest runnable evidence loop and preserved release compatibility. +5. UX / Design Civilization — `N/A` for this no-UI research round. +6. Mathematics / Formal Methods Civilization — selected finite binary64 raw-bit encoding, sign-zero policy, and explicit version isolation. +7. Engineering Civilization — used temporary native build directories and fixed-seed protocols. +8. Code Civilization — implemented JS/C encoding, root v2 candidate, migration receipt, benchmark host, and readiness evaluator. +9. Test Civilization — ran 11,000-number parity, focused RBC13, selfhost, native boundary, Universal Stress, and full regression. +10. Security Civilization — preserved fail-closed nonfinite values, warrants, evidence tiers, causal parents, and no implementation leakage to AI. +11. Release Civilization — kept package/version/canonical contracts unchanged and separated local evidence from hosted CI. +12. Integration Court — accepted the proposal as research evidence but returned canonical activation as `BLOCKED`. +13. Evidence Ledger — sealed roots, counts, commands, blockers, and next gates in this document. + +The mother algorithm was applied as: rename → locate authority → set boundaries → materialize evidence → re-verify → leave a controlled next growth path. + +## Required next gates + +1. Close the three existing self-akashic full-suite assertions without lowering their contract or hiding the drift. +2. Run a fresh blinded AI donor trial that satisfies the exact JSON Schema contract and completes differential evidence through Native Promotion. +3. Rebuild the Universal Stress cell with that donor evidence and obtain a true growth-eligible result. +4. Re-run the entire evidence matrix on the final source roots and obtain a separate Integration Court decision. +5. Only then consider a separate canonical admission PR; do not repurpose PR #39 as a release PR. diff --git a/docs/RBC13_CANONICAL_ADMISSION_PROPOSAL_v0.1.md b/docs/RBC13_CANONICAL_ADMISSION_PROPOSAL_v0.1.md new file mode 100644 index 00000000..18010107 --- /dev/null +++ b/docs/RBC13_CANONICAL_ADMISSION_PROPOSAL_v0.1.md @@ -0,0 +1,122 @@ +# RBC 1.3 Canonical Admission Proposal v0.1 + +Committee material — proposal only, not a canonical activation. + +Current proposal verdict: `BLOCKED`. `canonicalReady = false`. Canonical language, formal version contracts, and the released native VM ABI remain unchanged. + +Current strict-growth ruling: `Level 2 VERIFIED` is the global maximum supported by the closed evidence surface; `Level 3 CANDIDATE/BLOCKED` remains gated. Compatibility ACL2, C/WASM parity, or a bounded native candidate does not grant canonical admission or formal A10 promotion. + +## 1. Motivation + +RBC 1.3 / `DOMAIN_CALL` is intended to let RCL absorb bounded external semantics into evidence-gated native organs while preserving typed values, authority, receipts, deterministic replay, and a stable bytecode boundary. The admission question is not whether one experimental VM can execute four operations; it is whether the feature has enough non-compensable evidence to become language infrastructure. + +## 2. Exact feature definition + +The candidate feature is an experimental `DOMAIN_CALL` instruction (opcode 45) that resolves a declared operation through a native-organ registry, crosses the Domain Value ABI, invokes an admitted organ, and returns a typed result plus evidence-bearing receipt. Operation identity, input/output kinds, causal parent, authority needs, evidence tier, and replay roots are part of the candidate boundary. + +## 3. Opcode 45 semantics + +Opcode 45 is currently candidate/experimental. It is not silently reclassified as a canonical bytecode feature. Unknown operation, missing organ, unsupported value, invalid evidence, bad causal parent, authority failure, and organ error must fail closed with structured diagnostics; no partial state mutation is admitted. + +## 4. Domain Value ABI + +The membrane admits explicit scalar and structured value kinds, performs typed conversion, rejects unsupported kinds, and preserves semantic roots and receipts. It is versioned as the existing candidate ABI v1. Number v2 is a separate preparation dependency and is not substituted into old receipts. + +## 5. Native Organ Registry + +The registry binds operation names to organ implementations, required evidence tier, supported input/output kinds, and authority/effect metadata. The four-operation Native Promotion inventory remains verified for the current-source materialization, but that inventory is not proof that every future donor can be promoted. + +## 6. Evidence-tier model + +The candidate distinguishes diagnostic/development evidence, differential candidate evidence, native candidate evidence, and native verified promotion. A lower tier cannot unlock a higher tier by score accumulation. A `VERIFIED` local receipt is not a release or canonical admission by itself. + +## 7. Capability Metabolism relationship + +The intended metabolism is extraction → capability spec → corpus → independent differential absorption → candidate organ → promotion attempt. The current Compatibility Surface tested the same blinded donor protocol against three local models: the medium tier reached ACL2 with 0 human repair, while tiny/strong produced invalid or incomplete donors; formal A10 remains `NEGATIVE_RESULT` because Native Candidate/Native Process/Semantic Root/Replay/Promotion are not closed. + +## 8. Failure semantics + +All uncertainty and mismatched semantics remain explicit. Rejected values, missing providers, invalid warrants, failed controls, stale roots, non-finite numbers, and unsupported domains do not produce a best-effort native result. The existing authority/evidence boundary stays above the organ call. + +## 9. Number encoding dependency + +`rcl.canonical-number.v2` is a finite binary64 raw-bit encoding with explicit `-0` normalization and non-finite rejection. Its 11,000-case JS/C corpus is `VERIFIED`. It remains a version-isolated candidate; `rcl.semantic-state-root.v1` and historical receipts remain valid under v1. + +## 10. Backward compatibility + +The current canonical semantic root, RBC 1.1/1.2 behavior, version contract, and existing native artifacts were not rewritten. Any future adoption must use an explicit versioned root and migration receipt. No decoder may guess whether a v1 root is a v2 root. + +## 11. Self-host compatibility + +Current selfhost fixed point is 9/9, the eligible example parity suite is 17/17 with zero failures, and Stage40 is verified. This proves the existing selfhost boundaries. It does not yet prove that the full RBC 1.3 candidate is emitted and executed by the complete selfhost compiler. + +## 12. Security and authority implications + +The organ cannot acquire authority merely by being generated, fast, or locally executable. Policy → decision/evidence → action remains the governing order. Authority, warrants, causal parents, negative controls, and replay evidence must survive the membrane. The AI experiment never received implementation source or repository paths. + +## 13. 4R implications + +The candidate keeps reality input, reasoning/selection, realization, and review distinct: source and corpus are evidence; the compiler and registry select bounded capability; the organ realizes only an authorized call; receipts and courts review the result. No development artifact becomes a freeze or publication authority. + +## 14. Performance evidence + +The three-path benchmark is `VERIFIED` as a measurement: primitive, native organ, and provider-shaped paths ran at 1k/10k/100k with warmup, seven repetitions, median, p95, variance, RSS proxy, and allocation proxy. It supports a tier cost model, not a universal Native Organ speed claim and not a network-provider comparison. + +## 15. AI_GENERATE evidence + +The earlier single-donor JSON Schema trial remains historical `NEGATIVE_RESULT`: its response used draft-07 and omitted the required `unit` constraint. The current same-protocol Compatibility Surface is also `NEGATIVE_RESULT`: medium reached ACL2, tiny/strong were ACL0, human repairs were 0, and no model reached Native Promotion. No native promotion credit is granted. + +## 16. Universal Stress evidence + +The earlier native-only Universal Stress snapshot remains historical and blocked. The current A12 cell records verified JS/C/WASM parity, ABI negative controls, and replay for one bounded graph workload, but this experimental result does not grant universal maturity or canonical language; formal A10 remains negative. + +## 17. Alternatives considered + +The committee considered specialized opcodes, `DOMAIN_CALL` plus Native Organ, and provider-only extension. The current evidence favors a possible hybrid tier model but does not establish canonical superiority. A separate canonical PR is preferable to mixing research, implementation, admission, and release responsibilities in PR #39. + +## 18. Rejected architecture: hard-wired 18 builtins + +Adding one builtin/opcode for each domain would multiply compiler, VM, version, authority, and replay surface. It would make externally discovered capability difficult to metabolize and would turn an evolving organ inventory into permanent language syntax. It is rejected for this feature family. + +## 19. Provider Bridge comparison + +Providers remain appropriate for network, model, and heavyweight external work. They pay serialization/allocation and delivery costs but preserve an explicit boundary. Native organs are appropriate only when semantics are bounded, evidence-backed, replayable, and authority-safe. Neither path is a universal replacement for the other. + +## 20. Canonical admission risks + +Open risks are legacy/full-suite drift, incomplete Universal Stress closure, AI donor insufficiency, future-runtime conformance, full structured-value performance coverage, and the distinction between experimental opcode and canonical ABI. A green focused suite cannot compensate for any of these. + +## 21. Migration plan + +Close formal A10, rerun the complete matrix, and publish an explicit v2 root/number migration receipt and cross-runtime conformance package. Only after an independent Integration Court review should a separate canonical PR propose formal version changes. + +## 22. Rollback plan + +Keep v1 decoding and receipts available. Disable candidate opcode 45 and registry entries behind the experimental boundary, retain evidence reports as research artifacts, and reject v2 roots unless the v2 implementation is explicitly selected. No rollback may mutate historical v1 roots. + +## 23. Exact version changes required + +None are authorized in this preparation round. A future admission would require an explicit change set for the canonical RBC feature version, native ABI/version metadata, semantic-root binding, compatibility/migration receipts, and release documentation. `VERSION-CONTRACT.json`, `COMPONENT-VERSIONS.json`, package release version, and canonical README claims remain unchanged now. + +## 24. Admission checklist + +| Gate | Current status | +| --- | --- | +| A1 Number canonicality | `VERIFIED` | +| A2 Native Promotion inventory | `VERIFIED` | +| A3 Legacy regression closure | `VERIFIED` after version-ledger test-contract closure; final admission still depends on the full-suite result | +| A4 Positive semantic parity | `VERIFIED` | +| A5 Negative semantic parity | `VERIFIED` | +| A6 Replay determinism | `VERIFIED` | +| A7 Semantic-root evidence | `VERIFIED` | +| A8 Authority/evidence boundary | `VERIFIED` | +| A9 Performance evidence | `VERIFIED` | +| A10 AI_GENERATE donor | `NEGATIVE_RESULT` | +| A11 Selfhost/version contract | `VERIFIED` | +| A12 Universal Stress admission cell | `VERIFIED` as an experimental C/WASM cross-body graph cell; canonical admission remains separate | + +Canonical readiness is the conjunction of all twelve gates. A3 and A12 have current experimental closure evidence; formal A10 remains a blocker, and the final readiness ledger must bind the final full-suite result before any admission recommendation. + +## 25. Integration Court verdict + +`BLOCKED`: accept this document and its evidence as a research/admission proposal; do not accept canonical activation, formal version changes, or release promotion. The Court must be reconvened after the three blocking gates close, with a separate admission PR and a final human freeze decision. diff --git a/docs/RBC13_DOMAIN_CALL_SALVAGE_v0.1.md b/docs/RBC13_DOMAIN_CALL_SALVAGE_v0.1.md new file mode 100644 index 00000000..0bef0dd2 --- /dev/null +++ b/docs/RBC13_DOMAIN_CALL_SALVAGE_v0.1.md @@ -0,0 +1,187 @@ +# RBC 1.3 DOMAIN_CALL Salvage v0.1 + +**Status:** `CANDIDATE` +**Source branch:** `agent/advanced-runtime-rcl` +**Target baseline:** current `xingxuling/RCL@main` +**Issue:** #38 + +## 1. Goal + +Recover the reusable semantic idea behind the stale RBC 1.3 `DOMAIN_CALL` experiment without merging the stale branch, reusing its checked binaries, or silently upgrading current Foundation bridge evidence into native-language evidence. + +The migration target is not: + +```text +old branch -> merge everything -> call it native +``` + +It is: + +```text +old experiment +-> isolate semantic delta +-> rebuild a current reference oracle +-> differential evidence +-> current native implementation candidate +-> native promotion gate +-> only then change canonical bytecode/runtime claims +``` + +## 2. Multi-civilization federation decision + +### Founder Twin + +Keep the general idea: one typed domain-operation dispatch primitive can reduce special-case compiler/runtime growth and gives Capability Metabolism a concrete native target. + +Reject wholesale branch merge. The stale branch is far behind current authority, semantic-root, Provider, Universal Stress and evidence contracts. + +### 柳清莲 Gate + +High-noise inputs are the old prebuilt binaries and the assumption that every C builtin on the branch was independently verified. Both are excluded from the salvage path. + +### 洞哥 Grounding + +The first accepted load-bearing unit is only the set for which the stale branch had both a JavaScript reference meaning and a native candidate path: + +- `core.echo` +- `quantity.make` +- `quantitative.measure` +- `knowledge.claim` + +Everything else remains quarantined until an explicit current oracle and differential contract exist. + +### Product / UX + +No user-visible syntax is changed in v0.1. This is infrastructure work, not a new public language promise. + +### Engineering / Code + +The first slice is an isolated source-only reference organ: `src/rbc13-domain-call-salvage.mjs`. It does not modify the parser, `src/bytecode.mjs`, self-hosted compiler, native VM, version contract or Foundation status. + +### Test / Security / Release + +Tests must prove fail-closed behavior and, most importantly, prove that old native-only operations cannot inherit a current native claim merely because their names still exist. + +No release/version bump is permitted in this slice. + +## 3. Historical ABI extracted from the stale branch + +The old experiment introduced: + +```text +RBC version: 1.3 +opcode: 45 +name: DOMAIN_CALL +flag 0: literal domain + operation indexes +flag 1: dynamic domain + operation from the stack +c operand: argument count +``` + +The old native test also required: + +- RBC 1.3 for `DOMAIN_CALL`; +- rejection of unknown flags; +- rejection of missing operations and invalid arguments; +- preservation of prior RBC 1.1 / 1.2 behavior. + +Current canonical bytecode remains RBC 1.2 and is intentionally unchanged by this salvage slice. + +## 4. Operation inventory + +The stale C runtime registered 18 operations. + +### 4.1 Admitted reference-backed set + +| Legacy operation | Current semantic oracle | v0.1 disposition | +|---|---|---| +| `core.echo` | identity | reference restored | +| `quantity.make` | `src/quantity.mjs#quantity` | reference restored | +| `quantitative.measure` | `src/quantity.mjs#measurement` | reference restored | +| `knowledge.claim` | `src/knowledge.mjs#knowledgeClaim` | reference restored | + +These four had an explicit JavaScript `domain_call` path on the stale branch and can therefore be reconstructed without inventing a new meaning. + +### 4.2 Quarantined native-only set + +| Legacy operation | Nearest current bridge/oracle | v0.1 disposition | +|---|---|---| +| `language.utterance` | `natural-language.interpret` | quarantine | +| `language.intent` | `natural-language.interpret` | quarantine | +| `understanding.model` | `understanding.model` | quarantine; name overlap is not equivalence | +| `creation.candidate` | `creative.generate` | quarantine | +| `creation.select` | `creative.generate` | quarantine | +| `energy.scale` | `energy.balance` | quarantine | +| `element.species` | `elemental.compose` | quarantine | +| `element.compound` | `elemental.compose` | quarantine | +| `science.claim` | none in current native bridge | quarantine | +| `science.experiment` | none in current native bridge | quarantine | +| `body.state` | `embodiment.integrate` | quarantine | +| `spirit.state` | none in current native bridge | quarantine | +| `spacetime.point` | `meta.spacetime.sequence` | quarantine | +| `spacetime.retime` | `meta.spacetime.sequence` | quarantine | + +A nearby current capability is only a comparison target. It is not evidence of semantic equivalence. + +## 5. Why direct porting would be wrong + +Current Foundation native execution has a stronger contract than the old direct builtins. The bridge path binds: + +- authority requirements; +- AIF stability decisions; +- evidence; +- causal parent roots; +- deterministic replay metadata; +- provider receipts; +- result validation; +- state before/after roots. + +A direct C builtin that only returns the right data shape would therefore be weaker than the current bridge even if its numerical output matched. + +Any future native `DOMAIN_CALL` must preserve these governance/evidence properties or explicitly prove why a pure operation is exempt from particular effect obligations. + +## 6. v0.1 implementation + +Added: + +```text +src/rbc13-domain-call-salvage.mjs +tests/rbc13-domain-call-salvage.test.mjs +``` + +The module: + +- records the historical ABI candidate and provenance; +- records all 18 legacy operations; +- restores only the four reference-backed operation meanings using current source modules; +- fails closed on the 14 native-only historical operations; +- emits a rooted salvage report; +- explicitly forbids canonical bytecode mutation, native Foundation claims and old-binary reuse. + +## 7. Acceptance gates for the next slice + +Before opcode 45 can return to the current VM: + +1. **Reference parity:** the four admitted operations must match current reference-module outputs across positive and negative corpora. +2. **Bridge comparison:** every promoted Foundation operation must have a declared relation to the current Provider bridge oracle: exact, refinement, projection or intentionally different. +3. **RBC compatibility:** RBC 1.1/1.2 behavior must remain byte/runtime compatible; RBC 1.3 must be feature-gated. +4. **Native source rebuild:** modify current `native/rclvm.c`; never copy the stale binary artifacts. +5. **Semantic-root parity:** native outputs must pass the current `rcl.semantic-state-root.v1` authority boundary (or a separately versioned successor if Number encoding changes). +6. **Negative controls:** invalid flags, arity, type, missing operation, authority/effect mismatch and tampered receipts must reject. +7. **Replay:** deterministic operations must produce stable semantic/evidence roots. +8. **Capability Metabolism:** the operation must pass the current differential/native promotion pipeline. +9. **Universal Stress:** no capability credit is granted by special-case inflation. +10. **Canonical authority:** `VERSION-CONTRACT.json` / component versions change only after all required evidence exists. + +## 8. Current verdict + +```text +Historical idea value: SALVAGE +Old branch wholesale merge: REJECT +Old binaries: REJECT +Four reference-backed ops: CANDIDATE +Fourteen native-only ops: QUARANTINE +Canonical RBC 1.3 activation: BLOCKED +Native Foundation claim: BLOCKED +``` + +The next engineering step is a differential corpus for the four admitted operations, followed by a fresh current-main native implementation candidate. The stale branch remains preserved until that decision closes. diff --git a/docs/RBC13_DOMAIN_CHAIN_CANDIDATE_v0.1.md b/docs/RBC13_DOMAIN_CHAIN_CANDIDATE_v0.1.md new file mode 100644 index 00000000..8db1e926 --- /dev/null +++ b/docs/RBC13_DOMAIN_CHAIN_CANDIDATE_v0.1.md @@ -0,0 +1,137 @@ +# RBC 1.3 Domain Organ Chain Candidate v0.1 + +Status: `CANDIDATE / NOT NATIVE-VERIFIED` + +This document records the first end-to-end candidate execution chain built from the salvaged RBC 1.3 `DOMAIN_CALL` concept without modifying canonical `src/bytecode.mjs`, `native/rclvm.c` or `native/rclvm.h`. + +## Architecture now exercised + +`RBC 1.3 candidate encoding` +→ `opcode 45` +→ `candidate VM materialization` +→ `public candidate registration ABI` +→ `evidence-tiered Domain Organ registry` +→ `VM ↔ Domain Value membrane` +→ `external candidate organ` +→ `native typed Value` +→ `state / semantic root` + +The candidate VM defaults its minimum accepted organ tier to `native-verified`. The test host must explicitly lower that minimum for experimental candidate execution. Therefore merely linking or registering an organ is insufficient to make it executable under the default gate. + +## Four admitted candidate implementations + +The original stale branch contained 18 hard-wired native operations. The modern salvage admits C implementations only for the four operations that already had a reconstructable source/reference meaning: + +1. `core.echo` +2. `quantity.make` +3. `quantitative.measure` +4. `knowledge.claim` + +The other 14 historical operations remain quarantined. + +## Candidate composition vocabulary + +The candidate RBC assembler now supports: + +- finite Number; +- Truth; +- Text; +- recursive Sequence values lowered through existing sequence builtins; +- `{ $state: "path" }` references that load results from earlier calls. + +This is enough to test actual cross-operation composition rather than isolated function calls. + +## Executed local chain + +The following chain was executed against the uploaded native source snapshot: + +1. `quantity.make("Temperature", 25, "") → q.value` +2. `quantity.make("Temperature", 0.5, "") → q.uncertainty` +3. `quantitative.measure("Temperature", q.value, q.uncertainty, 0.9, "", "ratio", ["sensor:e1", "sensor:e1", "sensor:e2"], "sensor-A") → measure.temp` +4. `knowledge.claim("Temperature", q.value, 0.8, ["e1", "e1", "e2"], "lab", "local", "provisional", ["dep:a", "dep:a", "dep:b"], 1, "root-1") → knowledge.temp` + +Execution result: + +- exit code: `0`; +- RBC version: `1.3`; +- instructions executed: `51`; +- peak stack depth: `10`; +- typed objects allocated/registered: `7 / 7`; +- state entries: `4`. + +The Measurement output preserves repeated evidence because the current `measurement()` oracle does so. The Knowledge output deduplicates evidence and dependencies because the current `knowledgeClaim()` oracle uses set semantics. + +## Semantic parity result + +The candidate native state was normalized by removing native heap/layout metadata and compared with objects produced independently by current: + +- `src/quantity.mjs::quantity`; +- `src/quantity.mjs::measurement`; +- `src/knowledge.mjs::knowledgeClaim`. + +The complete four-entry native candidate state and current JavaScript oracle state were semantically identical after normalization. + +Both independently canonicalize to: + +`736b336eecb96c4fb3a02eaa7d4b9d6e07fd126d65de31d541cf47444bc33509` + +The uploaded native VM predates PR #29 native state-root emission, so this local run did not claim a VM-emitted root. The checked-in exact-current-source test requires the materialized current VM to emit that same semantic root. + +## Negative controls executed locally + +The candidate VM also failed closed for: + +- candidate organ under the default `native-verified` minimum tier; +- missing domain operation; +- unknown quantity type; +- measurement confidence above 1; +- measurement value/base-type mismatch; +- knowledge confidence above 1; +- knowledge value/base-type mismatch; +- unsupported VM value kinds at the Domain Value membrane. + +Legacy RBC 1.1 execution remained functional in the candidate VM. + +## Public candidate registration ABI + +The candidate VM now exposes an experimental host boundary through `native/rcl_domain_vm_candidate.h`: + +- `rclvm_instance_register_domain_organ`; +- `rclvm_instance_set_domain_minimum_tier`; +- `rclvm_instance_domain_organ_count`; +- `rclvm_instance_domain_minimum_tier`. + +The host no longer needs to access `instance->vm.domain_organs` or any other private `RclVmInstance` field. Domain organ identity strings are owned by the registry, so a host may release temporary registration descriptors after registration. + +## What this proves + +This slice proves that the salvaged `DOMAIN_CALL` idea can be reconstructed as a general, external, evidence-gated native organ mechanism rather than an 18-builtin VM patch. + +It also proves a nontrivial typed chain can cross the candidate membrane while retaining current Quantity / Measurement / Knowledge semantics on the executed cases. + +## What this does not prove + +It does **not** prove: + +- any organ is `native-verified` under the existing Native Promotion system; +- formal operation-specific Independent Differential reports have executed on the current branch; +- canonical RBC should become 1.3; +- canonical `rclvm.h` should expose this API yet; +- the other 14 historical operations should be restored; +- complete Domain Value coverage for unions, references, compiler AST values or every RCL type; +- whole-language native execution. + +## Promotion gate + +The next valid promotion sequence is per operation, not aggregate: + +`operation-specific differential corpus` +→ `negative-control detection` +→ `implementation artifact binding` +→ `candidate VM execution receipt` +→ `semantic-root parity` +→ `deterministic replay` +→ `Native Promotion` +→ `native-verified Domain Organ` + +Only after those gates should canonical RBC 1.3 / opcode 45 admission be considered. diff --git a/docs/RBC13_DOMAIN_NATIVE_PROMOTION_EVIDENCE_LEDGER_v0.1.md b/docs/RBC13_DOMAIN_NATIVE_PROMOTION_EVIDENCE_LEDGER_v0.1.md new file mode 100644 index 00000000..cf3354dc --- /dev/null +++ b/docs/RBC13_DOMAIN_NATIVE_PROMOTION_EVIDENCE_LEDGER_v0.1.md @@ -0,0 +1,195 @@ +# RBC 1.3 / DOMAIN_CALL Native Promotion Evidence Ledger v0.1 + +Date: 2026-08-08 (Asia/Shanghai) +Repository: `xingxuling/RCL` +Branch: `research/rbc13-domain-call-salvage-v0.1` +PR: [#39](https://github.com/xingxuling/RCL/pull/39) +Starting head audited: `a7f06b39d5b0aec74c7f2c9d9edd598e09ec32dc` + +Historical-snapshot notice: this ledger preserves the earlier native-promotion round, including its native-only Universal Stress blocker. The current A3/A10/A12 closure is generated in `docs/RBC13_FINAL_BLOCKER_CLOSURE_EVIDENCE_LEDGER_v0.1.md`; formal A10 remains `NEGATIVE_RESULT`. + +## 1. Target ruling + +The four declared operations have **VERIFIED** operation- and candidate-host-bounded Native Promotion evidence. The result is not canonical RBC 1.3 activation. The RBC 1.3 candidate remains **CANDIDATE**, and the Universal Stress probe remains **BLOCKED** until performance and AI-generation gates have evidence. + +The governing architecture remains: + +```text +source extraction +→ operation differential +→ native-candidate organ +→ experimental RBC 1.3 / DOMAIN_CALL opcode 45 +→ stable Domain Value ABI +→ external organ registry +→ VM materialized from current native/rclvm.c +→ native-process differential +→ native semantic state root +→ replay + negative controls +→ Native Promotion +→ separate canonical admission +``` + +## 2. Audited baseline + +- Working tree was isolated at the current PR branch; the old `agent/advanced-runtime-rcl` branch and its artifacts were not used. +- `origin/main` at audit time: `883b265420645b9ee112f0839c794bd76de50bd6`. +- Package: `@taowind/rcl-reality-forge@0.94.0-alpha.1`. +- Canonical language remains RBC 1.1 with feature version 1.2. Canonical `src/bytecode.mjs` does not emit opcode 45, and canonical `native/rclvm.c` is not patched with the experimental Domain Organ path. +- `VERSION-CONTRACT.json`, `COMPONENT-VERSIONS.json`, canonical bytecode feature version, selfhost compiler, and formal release version were not changed. +- UI work: **N/A**; this task has no user interface surface. + +## 3. Multi-civilization review record + +| Review seat | Ruling applied | +|---|---| +| Founder Twin | Keep the target to four reference-backed operations; no wholesale 18-operation native rewrite. | +| 柳清莲 Gate | High-impact language activation stays outside this PR; `canonicalAdmission=false` remains enforced. | +| 洞哥 Grounding | Every Native receipt binds to current-source materialization, host root, bytecode root, and native state root. | +| 产品文明 | The reusable product primitive is a governed Native Organ slot with a stable value ABI, not a domain-function catalog. | +| UX / 设计文明 | N/A; no UI or user-flow claim is made. | +| 数学 / 形式方法文明 | G1–G12 are conjunctive, not averaged; semantic-root parity and Number boundary tests are explicit. | +| 工程文明 | Windows MSVC discovery/environment loading was added; unsupported compiler discovery stays fail-closed. | +| 代码文明 | Typed value ownership, clone/free symmetry, registry identity, tier checks, and semantic errors are preserved. | +| 测试文明 | Focused, native, semantic-root, metabolism, Universal Stress, full-suite, and selfhost paths were exercised. | +| 安全文明 | Depth/item/text/finite-number limits, duplicate fields, unsupported VM values, tier gates, and no-default-candidate execution are retained. | +| 发布文明 | README’s existing version-contract declaration was aligned without changing version authority; CI remains separately classified. | +| Integration Court | A (candidate architecture Native Promotion) is verified for the declared corpus; B (canonical language admission) is not decided here. | +| Evidence Ledger | This document and the generated promotion/probe reports preserve the roots and failure log below. | + +## 4. Impacted modules + +- Native compiler resolution: `src/native-c-compiler.mjs`. +- Candidate runtime and current-source binding: `src/rbc13-domain-native-runtime.mjs`. +- Four-operation corpus and semantic error normalization: `src/rbc13-domain-operation-differential.mjs`, `src/rbc13-domain-call-salvage.mjs`, `src/rbc13-domain-bytecode-candidate.mjs`. +- Native ABI and admitted organs: `native/rcl_domain_value.*`, `native/rcl_domain_organ.*`, `native/rcl_domain_admitted_organs.c`, `native/rcl_domain_vm_value_bridge.inc`. +- Promotion gates/report: `src/rbc13-domain-native-promotion.mjs` and `tests/rbc13-domain-native-promotion.test.mjs`. +- Universal Stress candidate probe: `src/rbc13-domain-universal-stress-probe.mjs`, `scripts/run-rbc13-domain-universal-stress-probe.mjs`, and its test. +- Release-document contract only: `README.md`. + +## 5. Acceptance gates + +Each operation report contains exactly these 12 non-compensatory gates; final `verified` is `G1 && ... && G12`: + +1. `G1_operationScopedDifferential` +2. `G2_experimentalRbc13BytesDeterministic` +3. `G3_currentNativeSourceMaterialized` +4. `G4_candidateNativeHostBuilt` +5. `G5_positiveSemanticEquivalence` +6. `G6_negativeSemanticEquivalence` +7. `G7_nativeReplayDeterministic` +8. `G8_nativeSemanticStateRootEmittedAndVerified` +9. `G9_semanticRootParity` +10. `G10_allEvidenceRootsRecorded` +11. `G11_noCaseSilentlySkipped` +12. `G12_nativePromotionEvidenceTier` + +G3 records both current VM source roots (`native/rclvm.c` and `native/rclvm.h`) and the materialized candidate VM root. G8 requires a native-emitted `stateRootAlgorithm` and `stateRoot`; JavaScript does not manufacture the native root. + +## 6. Native Promotion result + +Final generated suite: `output/rbc13-domain-native-promotion/native-promotion-final-2026-08-08.json` +Suite status: `native-verified` +Suite root: `8c310a507fe666d8e0832585456b47124eadc8b07207e1caed49aa7518fd677d` +Host root: `7cf26f927d3bd40cb7d99ce7841867a140e44ec1a517e1a68b94bb612bfb7a1d` +Shared implementation root: `75dfc09964e86a5ac74d1cea04fbbe895200b66fc571f250ef8bfa169c6a4547` +Materialized VM root: `f82d9370248cf73c1657242c93fe94fd9573c3615bb470202c9158b882440357` + +| Operation | Cases | Native replays | Negative controls | Report root | Native differential root | Implementation root | +|---|---:|---:|---:|---|---|---| +| `core.echo` | 7/7 | 3 | 1 | `6316aa2c0eea70cd1a67b3c4a635b0c4211dbc5b123c2f1d18729bcf5b7db7d6` | `ec8afef5d0983874b5568f8c8aa5cbdab7ecde5b21c74aea1d508e2ca170371a` | `2c06fd7d9a85bf0a6e2f95bfce088cc0af902f34b0c6a8ffa25219b85d0518ca` | +| `quantity.make` | 6/6 | 3 | 1 | `67a5ac1c33716ac67d2a32e786a9bb911cc974761ad8cb17a161c9ea03fd55c0` | `de793c34b8705edcf52c88cc2a144fbb139aa506e24895f4d2693c1fe588450d` | `1d9e50c1375c97452f8744a4919f550b0ea9a8c8a4f314ad565074467e1935dc` | +| `quantitative.measure` | 5/5 | 3 | 1 | `c07dbdd95535e33cc251d057b0dec7541690f3dee167eb09c2ca35856a4364d0` | `93d7aab8269d51b221f1bef9cefc21a7d663fa0564c8245a705ef7eedfd57ac5` | `4995283d5335b7c524e877920b24893254ada3238496a5112f691125ee7cad1f` | +| `knowledge.claim` | 4/4 | 3 | 1 | `0f229af60c0410a112c59b8e9e91183138e9a37aa61a88ae58df8b539660cbe4` | `560dd86a18116e69f4cb46956018e42c1116fac3000a7da1c53b69929ea86f5f` | `6e13f789c025a00b2de89aefadf3afc456fb2be3f8264f90981812dc7b8e04fd` | + +Corpus additions include Truth, dynamic dispatch, invalid dispatch/arity, non-finite and malformed Quantity parameters, uncertainty type mismatch, evidence/calibrated-by/source/scope/status/dependencies/formed-at-root fields, and the declared error contracts. + +## 7. Command evidence matrix + +| Command | Exit | Result | Duration | Artifact root / failure log | +|---|---:|---|---:|---| +| `npm run build:native` | 0 | VERIFIED | 0.83 s | Source SHA `a805a058e30a222cd3f8299c9b497950c77ee3d181e4ec54c01454a8b1f68d22`; checked Windows manifest had no problems. | +| `node --test` focused RBC13 + ABI + root + metabolism + stress set | 0 | 71 PASS, 1 SKIP, 0 FAIL | 39.679 s | Suite root above; the skip is the explicit no-compiler blocking branch. | +| `node scripts/run-rbc13-domain-native-promotion.mjs ...final...json` | 0 | VERIFIED | 14.778 s | Suite root above; all four report roots above. | +| `npm run verify:rbc13-domain-stress-probe` | 0 | BLOCKED candidate cell | 0.817 s | Probe root `3e87ae82037a2b2b6bf8f800cf810cb544951f1c166f9428b147e827bd37f63d`; `specialCaseAudit=PASS`, `universalGrowthEligible=false`. | +| `npm run verify:version-contract` | 0 | VERIFIED | 0.834 s | Canonical package/contract/component/downstream checks pass; no version authority changed. | +| `npm run verify:native-boundary` | 0 | VERIFIED | 1.653 s | Native `rclvm.exe` SHA `f2d38852d5f3173f3208c8ba42747e02ba02a0482dc4b9bcb021f2a5aef22517`; 1.1 smoke/root parity pass. | +| `npm run verify:selfhost-fixedpoint` | 0 | 9 PASS | 149.884 s | JS/native fixed point and selfhost ABI pass; command emitted no single root. | +| `npm run verify:selfhost-examples` | 0 | VERIFIED, 16 eligible, 0 failures | 1.806 s | Example parity report; unsupported examples remain explicitly classified. | +| `npm run verify:selfhost-stage40` | 0 | VERIFIED, 18/18 checks | 0.936 s | Stage40 target source root `71e899db3794f862101f898dbf0549a534db488f4320f30d07585d523a25ce14`. | +| `node scripts/audit-semantic-root-number-corpus.mjs` | 0 | BLOCKED dependency | 0.606 s | 10 cases: 7 parity, 3 mismatch; canonical v1 algorithm was not changed. | +| `npm test` | 1 | 715 PASS, 3 FAIL, 2 SKIP / BLOCKED | 419.274 s | Three unrelated `self-akashic-record-compiler.test.mjs` failures: test requires `versionLedgerCount >= 60`, current scan has 26; no RBC13 failure. | + +## 8. Failure → repair → recheck log + +- Initial focused run: one native ABI test falsely treated the WorkBuddy `cc` command wrapper as a C compiler and returned no process status. Root cause was tool discovery, not ABI behavior. Added Visual Studio/MSVC resolution and environment loading; native ABI tests then passed. +- First expanded Native Promotion run: exit 2. `core.echo` invalid dispatch/arity details and malformed Quantity parameter handling did not match source semantics; one malformed value reached a native state-root mismatch. Normalized the semantic error details, restored fail-closed Number membrane validation, and reran the expanded suite: exit 0, four reports `native-verified`. +- First version-contract run: exit 1 because the existing README did not match the verifier’s required version/canonical-source strings. README-only declaration alignment made the recheck exit 0; no version contract or language version changed. +- Full-suite residual: the three self-Akashic failures are pre-existing repository self-scan threshold drift, not a promotion failure. They remain open rather than being suppressed. + +## 9. Domain Value ABI and registry ruling + +**VERIFIED for the declared ABI surface.** The native tests exercise Null/Number/Truth/Text/Sequence/Typed Record transport, typed result preservation, recursive clone/free paths, duplicate-field rejection, unsupported VM-value rejection, finite Number rejection, depth/item/text bounds, tier checks, missing operations, duplicate registration, deterministic dispatch, and semantic error propagation. Registry lifecycle is init/register/resolve/invoke/free; there is no unregister API in the current design, so no unregister claim is made. + +The bridge does not expose private VM heap layout. Candidate execution is explicitly admitted at `native-candidate`; the normal registry minimum remains `native-verified`. + +## 10. Architecture judgment: A / B / C + +| Route | Expression | Native / ABI | Verification + security | Metabolism / AI / Universal Stress | +|---|---|---|---|---| +| A. Dedicated opcode per capability | High per capability, but surface fragments quickly. | Potentially fast; every capability creates a new opcode and ABI. | Strong local control but a large review and attack surface. | Poor reuse; AI must learn many special forms; weak universal primitive signal. | +| B. `DOMAIN_CALL` + external organ registry | One general call grammar with operation-scoped semantics. | Native when the organ owns the typed ABI and process receipt; stable opcode 45 with versioned organ/value contracts. | Strong if identity, tier, argument validation, roots, state root, replay and negative controls are mandatory. | Reuses metabolism → differential → candidate → promotion; regular schemas are AI-friendly; directly measures reusable cross-language primitives. | +| C. Provider Bridge for all extension | Broad expression through delegation. | Usually opaque delegation, not native RCL semantics. | Provider authority and evidence remain useful, but semantic ownership and root parity are weaker. | Good interoperability, weak Native-General and Universal Stress credit; capability can remain an opaque adapter. | + +**Architecture ruling: B is the stronger candidate direction, not a canonical admission.** It is not ordinary FFI because the call is governed by RCL operation identity, typed Domain Value ownership, evidence tier, authority boundary, semantic state root, deterministic replay, mutation negative control, and a conjunctive promotion gate. A provider may return a result; a promoted organ must reproduce the declared semantic result and error contract under rooted native execution. + +The four Foundation 4R controls remain explicit: explicit variable/uncertainty handling, provider/capability boundary, authorization/evidence, and adaptive invariant/root consistency. + +## 11. Capability Metabolism state machine + +The current implementation composes existing protocols rather than introducing a parallel promotion system: + +```text +source-extracted +→ corpus-forged +→ differential-verified +→ native-candidate +→ native-verified +→ canonical-candidate (future, separate admission) +``` + +`differential-absorption-runner.mjs` supplies independent adapters, deterministic repeats, negative controls and evidence roots. `domain-operation-organ.mjs` supplies candidate/verified tiers. The RBC13 promotion layer adds the experimental-bytecode, current-source materialization, native-process and semantic-root gates that are specific to this ABI. + +## 12. Universal Stress candidate probe + +The probe maps the verified chain to `wasm-vm::algorithm` with `coverageMode=native-semantic`: + +```text +RCL source/candidate IR +→ RBC 1.3 candidate bytes +→ DOMAIN_CALL opcode 45 +→ external native organ +→ independent native process +→ native semantic state root +``` + +It is intentionally **BLOCKED**: `EXPRESS`, `COMPILE`, `LOWER`, `EXECUTE`, `CORRECT`, `ROBUST`, and `EVIDENCE` are supported by this probe; `PERFORMANCE` and `AI_GENERATE` are `UNVERIFIED`. The generality audit is `PASS`, but no Universal Stress growth credit is granted and no full-language native claim is made. + +## 13. Known semantic-root dependency + +The current `rcl.semantic-state-root.v1` Number corpus remains incomplete: 7/10 cases parity, 3/10 mismatch (`max-safe-integer`, `max-safe-minus-one`, `large-decimal`). This is recorded as a dependency/blocker for broader numeric promotion. The v1 canonical encoding was not silently changed. The next valid step is a separately versioned canonical Number encoding proposal plus its own differential and migration evidence. + +## 14. CI and release boundary + +The latest PR runs for commit `e3f1c59db4c565f40caabf62ed17e0604a7f4a08` were: + +- `31258043787` — `RCL Authority Contract`, failure before any step (`steps=[]`). +- `31258043777` — `RCL Canonical Verification`, failure before any step (`steps=[]`). + +Both are classified `INFRASTRUCTURE_BLOCKED`, not test failures. Local build, native compile, promotion, semantic-root, selfhost, and focused/full test evidence above was run directly in the current Windows environment. + +## 15. Final scientific / engineering ruling + +- **VERIFIED:** four declared operation organs have Native Promotion evidence bounded to the expanded corpus, current-source candidate VM, current host build, semantic-root receipts, replay, and negative controls. +- **CANDIDATE:** RBC 1.3 / opcode 45 and the Native Organ ABI remain experimental and non-canonical. +- **BLOCKED:** Universal Stress growth credit, full numeric semantic-root coverage, full `npm test`, and GitHub hosted workflows remain open gates. +- **Not changed:** canonical RBC language activation, canonical opcode set, version contracts, component versions, selfhost compiler, and main. diff --git a/docs/RBC13_DOMAIN_NATIVE_PROMOTION_PROTOCOL_v0.1.md b/docs/RBC13_DOMAIN_NATIVE_PROMOTION_PROTOCOL_v0.1.md new file mode 100644 index 00000000..e1874672 --- /dev/null +++ b/docs/RBC13_DOMAIN_NATIVE_PROMOTION_PROTOCOL_v0.1.md @@ -0,0 +1,133 @@ +# RBC 1.3 Domain Organ Native Promotion Protocol v0.1 + +Status: `IMPLEMENTED / EXECUTION RECEIPT PENDING` + +This protocol exists because the canonical `native-capability-promotion.mjs` pipeline assumes canonical RCL source compiled by the canonical bytecode compiler. Experimental RBC 1.3 `DOMAIN_CALL` deliberately does not satisfy that assumption yet. The existing promotion system is therefore left unchanged rather than weakened to accept an experimental bytecode path. + +## Promotion chain + +Each admitted operation must pass its own chain: + +`reconstructed historical/reference semantic` +→ `current JavaScript oracle` +→ `operation-scoped Independent Differential` +→ `native-candidate Domain Organ plan` +→ `deterministic RBC 1.3 candidate bytecode` +→ `external C candidate organ` +→ `candidate VM materialized from current native/rclvm.c` +→ `VM-emitted rcl.semantic-state-root.v1 verification` +→ `current-JS vs native-process differential` +→ `deterministic replay` +→ `Domain Organ Native Promotion report` +→ `native-verified Domain Organ` + +Canonical RBC 1.3 admission remains a later and separate decision. + +## Semantic error identity + +Positive output equality is insufficient. Native Promotion compares normalized errors as semantic outputs as well. + +The Domain Organ ABI therefore carries a structured error channel with stable `code` and `message` fields. The candidate VM owns dynamically supplied error codes rather than retaining stack pointers, and forwards organ errors without wrapping them in a generic `RCL_NATIVE_DOMAIN_ORGAN_FAILURE` code. + +The first four candidate organs intentionally match current JavaScript error identity for the tested semantic failures, including: + +- unknown quantity type → `TypeError`; +- measurement value/base mismatch → `RCL_MEASUREMENT_TYPE`; +- measurement uncertainty mismatch → `RCL_UNCERTAINTY_TYPE`; +- measurement confidence range → `RCL_CONFIDENCE_RANGE`; +- knowledge value/base mismatch → `RCL_KNOWLEDGE_TYPE`; +- knowledge confidence range → `RCL_KNOWLEDGE_CONFIDENCE_RANGE`. + +RCL runtime errors retain the same code-prefixed message form produced by the current `RCLError` base class. Native error details used by the differential runner are reconstructed from the operation inputs so the semantic error envelope matches the current source oracle rather than a native-process receipt. + +## Current-source native runtime harness + +`src/rbc13-domain-native-runtime.mjs`: + +1. reads the repository's actual current `native/rclvm.c`; +2. materializes the candidate opcode-45 VM in a temporary directory; +3. builds an external candidate host with strict C warnings; +4. content-addresses the host, materialized VM and all Domain Organ ABI/source inputs; +5. deterministically assembles a candidate RBC program per operation case; +6. executes the host in a separate process; +7. requires native `rcl.semantic-state-root.v1` evidence on successful executions; +8. strips native heap/layout metadata before comparing operation outputs; +9. preserves semantic error code/message/details on failing executions; +10. removes all temporary build material after the promotion suite. + +The candidate host explicitly lowers its minimum organ tier to `native-candidate` only for the promotion experiment. The candidate VM itself defaults to `native-verified` and therefore does not implicitly trust linked or registered candidates. + +## Operation-scoped semantic gate + +`src/rbc13-domain-operation-differential.mjs` no longer treats the four admitted operations as one aggregate capability. Each operation receives its own cases, replay checks and mutation control. + +The pure-internal operation boundary is represented explicitly rather than fabricated as an external authority/resource effect: + +- authority required: false; +- external resources created: 0; +- external resources mutated: 0; +- persistent state mutation: false; +- effect class: internal-domain-evaluation; +- semantic-contract evidence is attached. + +This lets the generic Independent Differential scorer see real evidence dimensions without inventing external side effects. + +## Candidate implementation binding + +`src/rbc13-domain-organ-candidate-plan.mjs` binds a passed operation differential to a named external C implementation but deliberately leaves: + +- `artifactBindingPending: true`; +- `nativePromotionPending: true`; +- `canonicalAdmission: false`. + +The runtime harness then creates deterministic source/materialized-VM/host roots. The promotion report derives an operation-specific implementation root from the shared implementation source plus operation identity. + +## Native Promotion report + +`src/rbc13-domain-native-promotion.mjs` emits `rcl.domain-organ-native-promotion-report.v0.1` only after checking all of the following non-compensatory conditions: + +- operation semantic differential passed; +- operation semantic differential is promotion-eligible; +- current-JS vs native-process differential passed for every declared case; +- candidate RBC bytes are deterministic; +- native process replay is deterministic; +- every successful native case emits and verifies the current semantic state root; +- native host binary has a content root; +- candidate implementation source/materialized VM has a content root; +- case counts align and no case is silently omitted. + +A report is `native-verified` only when every check is true. Otherwise it is `native-rejected`. Missing compiler infrastructure is reported as `native-blocked` by the suite rather than being relabeled as a semantic failure or success. + +## Evidence runner + +Run: + +```bash +node scripts/run-rbc13-domain-native-promotion.mjs +``` + +The runner uses three semantic repetitions and three native repetitions by default and writes a JSON evidence report under `output/rbc13-domain-native-promotion/`. + +A non-verified suite exits non-zero. + +## Current status + +The protocol, runtime harness, operation-scoped differential gate, deterministic candidate-bytecode manifests, structured error path and promotion tests are checked into the research branch. + +The connected GitHub hosted runners are currently failing before job steps begin, so this document does **not** claim that the exact-current-source Native Promotion suite has executed successfully on GitHub. + +Previous local evidence proves candidate execution against the uploaded native snapshot, including the nontrivial Quantity → Measurement → Knowledge chain, but that uploaded native VM predates the current native state-root producer. It is therefore insufficient by itself to upgrade any organ to `native-verified`. + +## Canonical admission boundary + +Even a successful four-organ Native Promotion suite would establish only four case-bounded, host-bounded `native-verified` Domain Organs. It would not by itself change: + +- canonical bytecode version; +- canonical opcode table; +- canonical self-hosted compiler; +- canonical `native/rclvm.c`; +- canonical `native/rclvm.h`; +- the status of the other 14 quarantined historical operations; +- whole-language native execution. + +Canonical RBC 1.3 / opcode 45 admission requires a separate governance change after promotion evidence is reviewed. diff --git a/docs/RBC13_DOMAIN_ORGAN_ABI_v0.1.md b/docs/RBC13_DOMAIN_ORGAN_ABI_v0.1.md new file mode 100644 index 00000000..12ccf838 --- /dev/null +++ b/docs/RBC13_DOMAIN_ORGAN_ABI_v0.1.md @@ -0,0 +1,144 @@ +# RBC 1.3 DOMAIN_CALL Domain Organ ABI v0.1 + +Status: `CANDIDATE` + +This document defines the second/third salvage slices for historical RBC 1.3 `DOMAIN_CALL` (`opcode 45`). The goal is not to restore the stale VM wholesale. The goal is to recover a reusable execution shape that can become a general native organ slot for Capability Metabolism and Native Promotion. + +## Decision + +`DOMAIN_CALL` is split into four independently governed layers: + +1. **RBC candidate encoding** — `src/rbc13-domain-bytecode-candidate.mjs` can materialize literal and dynamic opcode-45 programs under a 1.3 header. It does not modify `compileRealityToBytecode`, so canonical RCL lowering remains RBC 1.1/1.2. +2. **Domain Organ contract** — `src/domain-operation-organ.mjs` gives every operation a content-addressed semantic identity, implementation identity and evidence tier. +3. **Domain Value ABI** — `native/rcl_domain_value.h/.c` defines an owned, recursive, bounded value representation for Null / Number / Truth / Text / Sequence / typed Record values without exposing the private `native/rclvm.c` `Value` layout. +4. **Native Organ ABI** — `native/rcl_domain_organ.h/.c` supplies a bounded C registry and fail-closed invocation gate over Domain Values. A native candidate cannot be invoked through a `native-verified` gate. + +## Evidence tiers + +The organ lifecycle is non-compensatory: + +`quarantined → differential-verified → native-candidate → native-verified` + +A later tier cannot be inferred from registration, naming, implementation language or historical branch status. + +- `quarantined`: known historical or extracted operation, no current parity claim. +- `differential-verified`: current differential evidence exists, but no native artifact claim. +- `native-candidate`: differential evidence may be bound to an implementation candidate; Native Promotion is still required. +- `native-verified`: a Native Promotion report has bound the declared cases to a concrete implementation/native receipt. + +Canonical language admission is a separate decision even after `native-verified`. + +## Why the organ is not the old builtin table + +The historical `agent/advanced-runtime-rcl` branch hard-wired 18 operations into `native/rclvm.c`. That shape was too easy to mistake for native Foundation coverage. The modern organ ABI instead makes the operation registry explicit and attaches evidence state to each operation. + +The first admitted semantic set remains only: + +- `core.echo` +- `quantity.make` +- `quantitative.measure` +- `knowledge.claim` + +The remaining 14 historical native-only operations stay quarantined until separate current equivalence evidence exists. + +## Candidate bytecode boundary + +`assembleRbc13DomainCallProgram()` writes RBC `1.3` and opcode `45` for experiments. Current canonical `decodeBytecode()` intentionally sees opcode 45 as unknown; the candidate decoder overlays the experimental meaning. This is deliberate: the repository can create and inspect future bytecode without silently changing the canonical compiler or current native loader. + +The next native-VM integration must therefore be a fresh source patch against current `native/rclvm.c`, not a copy from the stale branch. + +## Domain Value ABI + +Directly exposing the VM-private `Value *` to external organs would couple every organ to the native heap, typed-object IDs and GC/layout changes. Using JSON instead would preserve isolation but force opcode 45 to serialize and parse every typed value, and would risk degrading Quantity/Measurement/Knowledge results into opaque text. + +The candidate therefore introduces `RclDomainValueV1` as the membrane between VM and organ. + +Supported value families in v0.1: + +- Null; +- finite Number; +- Truth; +- length-bearing Text; +- recursive Sequence; +- typed Record with a `type_name`, optional `semantic_type` and named fields. + +The ABI owns its memory and supplies init/free/clone/validate/equality operations. Record equality is field-name based rather than insertion-order based. + +Fail-closed bounds are part of the ABI: + +- maximum nesting depth: 64; +- maximum sequence/record items: 65,536; +- maximum text/type/name payload: 16 MiB; +- duplicate record field names rejected; +- non-finite Number rejected; +- all nested values recursively ABI-validated at the organ boundary. + +This is intentionally smaller than the complete current VM `Value` universe. Typed unions/references, compiler AST values and other specialized internal values remain outside the candidate until a real pressure case requires them. + +## Native Organ ABI boundary + +The C organ registry is bounded (`RCL_DOMAIN_ORGAN_MAX=128`) and requires: + +- Organ ABI version 1; +- non-empty domain / operation / semantic identity / implementation identity; +- a declared evidence tier; +- a deterministic declaration; +- a Domain-Value invocation function; +- duplicate operation rejection; +- fail-closed minimum-tier checks before invocation; +- initialized output ownership; +- recursive argument/result Domain Value validation. + +A callback receives an array of `RclDomainValueV1` arguments and returns an owned `RclDomainValueV1`. No JSON parser and no raw VM pointer is part of this ABI. + +## Promotion bridge + +`promoteDifferentialToDomainOrganCandidate()` accepts only a passed, promotion-eligible differential report and can produce **only** `native-candidate`. + +`admitNativeVerifiedDomainOrgan()` requires an explicit existing `native-verified` Native Promotion report. It does not manufacture native evidence. + +This yields the intended chain: + +`Capability Metabolism / salvage semantics` +→ `Independent Differential` +→ `Domain Organ native-candidate` +→ `Domain Value ABI` +→ `fresh opcode-45 VM membrane` +→ `Native Promotion` +→ `native-verified organ` +→ `separate canonical RBC 1.3 governance decision` + +## Local evidence + +Earlier source/bytecode slice: + +- Domain Organ JS contract tests: 3/3 PASS. +- Candidate RBC 1.3 bytecode tests: 3/3 PASS. +- Existing bytecode/native ABI/typed-bytecode focused regression: 18 PASS, 2 platform skips, 0 failures. + +Domain Value slice was compiled independently with `cc -std=c11 -Wall -Wextra -pedantic` and executed successfully. The smoke program proved: + +- evidence-tier denial occurs before invocation; +- Text crosses the organ boundary losslessly; +- a typed `Quantity`-shaped record with semantic type `Temperature`, numeric value and `°C` unit crosses the boundary by owned deep clone; +- recursive validation and field-name-independent value equality complete without warnings or runtime failure. + +A full `npm test` run is still not claimed for this branch because the previous full-suite attempt exceeded the available execution window. + +## Not claimed + +This slice does not claim: + +- current `native/rclvm.c` executes opcode 45; +- canonical RBC has moved to 1.3; +- all 18 historical operations are restored; +- Provider Bridge and Domain Organ semantics are equivalent; +- Domain Value ABI already covers every current RCL internal value family; +- whole-language native execution; +- cross-platform native verification. + +## Next gate + +The next implementation slice is now narrow and safe: define a **private VM ↔ Domain Value membrane** for the subset needed by the four admitted operations, then patch current `native/rclvm.c` so an RBC 1.3 opcode 45 can dispatch only through the evidence-gated registry. + +The first VM integration must ship with **no implicitly trusted operation**. Loader/version/flags/arity/value-conversion failure closure must pass before any organ is registered. Only after that should the four admitted operations be attached one-by-one and subjected to semantic-root, differential, replay and Native Promotion checks. diff --git a/docs/RBC13_FINAL_BLOCKER_CLOSURE_EVIDENCE_LEDGER_v0.1.md b/docs/RBC13_FINAL_BLOCKER_CLOSURE_EVIDENCE_LEDGER_v0.1.md new file mode 100644 index 00000000..1ca259a5 --- /dev/null +++ b/docs/RBC13_FINAL_BLOCKER_CLOSURE_EVIDENCE_LEDGER_v0.1.md @@ -0,0 +1,84 @@ +# RBC 1.3 Final Blocker Closure Evidence Ledger v0.1 + +- Status: **BLOCKED** +- Evidence root: `7bfff1b9c0b42f5f4880d3f2d42722d23094b8c06fa5bafb2e687c631e07b89c` +- Branch: `research/rbc13-domain-call-salvage-v0.1` +- Source HEAD at evidence capture: `8cf703eb01ac00850e82fd93c45ac5655d24918e` +- Pull request: #39 research/evidence branch +- Scope: A3 Version Ledger Contract Closure / A10 Compatibility Surface / A12 WASM Organ ABI + +## Admission ruling + +Canonical readiness: **BLOCKED**; canonical activation: **BLOCKED**. +Blocking gates: A10_aiGenerateDonor. +Strict autonomous growth: **Level 2 VERIFIED**; next: **Level 3 CANDIDATE/BLOCKED**; formal A10: **NEGATIVE_RESULT**; monotonic assimilation: **NOT_ESTABLISHED**. + +## A1-A12 gate matrix + +| Gate | Status | Evidence roots | Blocker | +|---|---|---|---| +| A1_numberEncodingV2 | **VERIFIED** | de11a038b3f09fd3e0639b70c8e4198884039abe3f88064cb6a7424d5f3b3cf7; a7071d47b65ad721ff3098b977fc607106ac96bb0ffb5910c3ce998f44894da7 | none | +| A2_nativePromotionInventory | **VERIFIED** | 2dfe69861e0fe7870fc46af9c05ad2084377f361e51a1703d29917e3c91bf6a0; 99cdd2e1d7b60c21e6ebb5188182c6a0d8b53c36c3a4fd806080ed5bae4dffef; 3216e153b6ebc82a79826251ed289a9015605d5288f6cbd246b731e8ac1198e5; 7a19788d8e0845a32233a241b50ae5e9e180b60e36df7ea147c90ed9b209e8dc; b181d759af02e15f1581f596528c4428f2d6fa91a5a2926c77a7f2fd5202fd89 | none | +| A3_legacyRegressionClosure | **VERIFIED** | d8a439e0aa0a88552a118557106996de86bd41ae0ad7524ce6991d95b60b863a; 1fcc6db6805045ee211c3caa48e8ce1425eee4a4e8adc456192b4b71a9a5074e; 459a79e965e67d0e8809690920d7f5074950ab1e7b4f42fda922132225d59696 | none | +| A4_positiveSemanticEquivalence | **VERIFIED** | 99cdd2e1d7b60c21e6ebb5188182c6a0d8b53c36c3a4fd806080ed5bae4dffef; 3216e153b6ebc82a79826251ed289a9015605d5288f6cbd246b731e8ac1198e5; 7a19788d8e0845a32233a241b50ae5e9e180b60e36df7ea147c90ed9b209e8dc; b181d759af02e15f1581f596528c4428f2d6fa91a5a2926c77a7f2fd5202fd89 | none | +| A5_negativeSemanticEquivalence | **VERIFIED** | 99cdd2e1d7b60c21e6ebb5188182c6a0d8b53c36c3a4fd806080ed5bae4dffef; 3216e153b6ebc82a79826251ed289a9015605d5288f6cbd246b731e8ac1198e5; 7a19788d8e0845a32233a241b50ae5e9e180b60e36df7ea147c90ed9b209e8dc; b181d759af02e15f1581f596528c4428f2d6fa91a5a2926c77a7f2fd5202fd89 | none | +| A6_deterministicReplay | **VERIFIED** | 99cdd2e1d7b60c21e6ebb5188182c6a0d8b53c36c3a4fd806080ed5bae4dffef; 3216e153b6ebc82a79826251ed289a9015605d5288f6cbd246b731e8ac1198e5; 7a19788d8e0845a32233a241b50ae5e9e180b60e36df7ea147c90ed9b209e8dc; b181d759af02e15f1581f596528c4428f2d6fa91a5a2926c77a7f2fd5202fd89 | none | +| A7_semanticRootEvidence | **VERIFIED** | 99cdd2e1d7b60c21e6ebb5188182c6a0d8b53c36c3a4fd806080ed5bae4dffef; 3216e153b6ebc82a79826251ed289a9015605d5288f6cbd246b731e8ac1198e5; 7a19788d8e0845a32233a241b50ae5e9e180b60e36df7ea147c90ed9b209e8dc; b181d759af02e15f1581f596528c4428f2d6fa91a5a2926c77a7f2fd5202fd89 | none | +| A8_authorityAndEvidenceBoundary | **VERIFIED** | 2dfe69861e0fe7870fc46af9c05ad2084377f361e51a1703d29917e3c91bf6a0; f145ff411a6faa16692981d6e70ffaa4366976744421a7b8e42a9e8a56e0cb49 | none | +| A9_performanceEvidence | **VERIFIED** | 56b08170c939ff7c698ea91b0721d3f02bab9c25c29a015e66ce434b852b45b7; 838e7014a097b842b3be08b29ff8c23f8d10ed4ed810bf74fa0b929b0c0a0088 | none | +| A10_aiGenerateDonor | **BLOCKED** | 569a1891cec305c01e1e30c45ef62fe47f17ac5c9c0878f65ceb31f3eca754f7; 44335c73105f4652f7688826fec282e5bc3c7636c090cd62f8387eb75f7c578a; bdb2f80b8f840acacfe3d6a9c85b4b2f34f0ab673d1d873232d94548b6df57d5; c9c5d4938c71a1e10b106dbb00d6265b69288b544f22c1c4765138034cbf0f67 | AI_GENERATE donor status is NEGATIVE_RESULT | +| A11_selfhostAndVersionContract | **VERIFIED** | fa47202099abffc5c482c099f52e8bda5346c2874cf72920513d7ea6be6fb08e; f63e3e9709e01e5ec0c1aacf1cec91ab704d397b48eafa77aa51d4a58fe9d3ce; 17d00bca0ab404ce1d3538d49a9e41eb285c6c407bf2d71b12d6e69e3e867a1e; f145ff411a6faa16692981d6e70ffaa4366976744421a7b8e42a9e8a56e0cb49 | none | +| A12_universalStressAdmissionCell | **VERIFIED** | 97a58a4091d8ebf8103cac95daeea42225d8cb437dc26af0088a031d17f93ee9 | none | + +## A3 Version Ledger Contract Closure + +- Status: **VERIFIED**; receipt closure root: `d8a439e0aa0a88552a118557106996de86bd41ae0ad7524ce6991d95b60b863a`; inventory root: `c33453601f567a33a11413b7d958f864ebca7f1445b65cae16b5f70a04003a8a`. +- Receipt closure: 6/6; missing=0; duplicate=0; stale=0; altered=0; replay mismatch=0. +- Original failure: `tests/self-akashic-record-compiler.test.mjs: scan.counts.versionLedgerCount >= 60`. +- Root cause: test-assumption drift: production minVersionLedgerCount remained 28 while the test duplicated stale literal 60. +- Fix: tests/self-akashic-record-compiler.test.mjs now reads DEFAULT_SELF_AKASHIC_RECORD_SPEC.thresholds.minVersionLedgerCount. +- Full suite: **VERIFIED**; 741 total / 739 pass / 0 fail / 2 skipped; summary root `459a79e965e67d0e8809690920d7f5074950ab1e7b4f42fda922132225d59696`. +- RBC 1.1 and RBC 1.2 receipt definitions remain unchanged. + +## A10 Compatibility Surface + +- Status: **NEGATIVE_RESULT**; root `569a1891cec305c01e1e30c45ef62fe47f17ac5c9c0878f65ceb31f3eca754f7`; donor `bdb2f80b8f840acacfe3d6a9c85b4b2f34f0ab673d1d873232d94548b6df57d5`; corpus `c9c5d4938c71a1e10b106dbb00d6265b69288b544f22c1c4765138034cbf0f67`. +- Fixed subset corpus: 100 cases; {"positive":40,"negative":40,"boundary":20}. +- Independent oracle: Ajv2020 ajv@8.20.0; shared candidate imports=false; normalized errors include keyword, instancePath, schemaPath, params. +- Mutation controls: ignore-required, minimum-comparison, additional-properties-true, array-item-bypass, enum-equality-bug. +- Model ACL: {"tiny":"ACL0","medium":"ACL2","strong":"ACL0"}; best=ACL2; human repairs=0; automatic repairs=4. +- Formal A10: **NEGATIVE_RESULT**; Native Promotion required=true; native-promotion models=[]. +- Compatibility ruling: Compatibility is alignment-sensitive and donor/protocol scoped; model scale alone does not establish monotonic assimilation. + +## A12 WASM Organ ABI and graph growth cell + +- Status: **VERIFIED**; root `97a58a4091d8ebf8103cac95daeea42225d8cb437dc26af0088a031d17f93ee9`; operation `wasm-vm::algorithm::graph-traversal`; universal growth eligible=true; canonical admission=false. +- Bodies: C **VERIFIED**, WASM **VERIFIED**, JS reference **VERIFIED**; cross-body parity=true; replay=VERIFIED. +- Workload: bounded breadth-first traversal with deterministic neighbor order, visited discovery order, visited set, step budget, and termination class; cases=7. + +| Case | Class | JS status | Semantic-root parity | Result/error parity | JS root | +|---|---|---|---|---|---| +| positive-chain | positive | ok | true | true | e92d63b3638b35ca0ab8e7f943db805c9bf0e8cd76170ce0a1faabd2fe92c2d0 | +| cycle | cycle | ok | true | true | a35b6672827e6a9ce4a0b3202eab6bec4c34c34ec50b869f31463711e2dea464 | +| disconnected | disconnected | ok | true | true | d86dbcc131164bf257b8647598c0eb07090bbe81f6879e08c1522e2eaf3c1d36 | +| empty | empty | error | true | true | 1662aba1282f155ab2039c552542b7c804a4243a91cbad171c9781f86dba2162 | +| budget-exhaustion | budget-exhaustion | ok | true | true | 1388effeeb4789b737b178851a37516cd3e0733a607a8dcf4d9d5a816297b90f | +| invalid-node | invalid-node | error | true | true | b13604b45fc88269801f06f6c3bc85f01c74bc821d7bd08a3a49439d78d11118 | +| malformed-graph | malformed | error | true | true | f9150995f9dfb18649e6ca7e1c0b534d721619c00c77e66ed092d3bbb6fe24b7 | + +- ABI negatives: unsupported-type=true, malformed-length=true, nonfinite-number=true, duplicate-field=true, invalid-pointer=true; fail-closed=true. +- Logical Organ identity is the operation/Domain Value/error/semantic-root/evidence/receipt contract; C and WASM are replaceable bodies with no shared private heap. + +## Universal Stress and Polybody boundary + +- Universal Stress: **VERIFIED** experimental cross-body semantic cell only; no universal maturity or canonical language claim. +- Polybody: **VERIFIED** experimental parity witness; separate document is emitted only when all seven cases, errors, roots, replay, and ABI negatives pass. +- Canonical language modified: **NO**. VERSION-CONTRACT modified: **NO**. +- Integration Court: **BLOCKED: separate human Integration Court approval is still required**. + +## Full-suite receipt and next step + +- Full-suite command: `npm test`; status **VERIFIED**; root `459a79e965e67d0e8809690920d7f5074950ab1e7b4f42fda922132225d59696`. +- Next step: Keep PR #39 research-only; obtain a new blind AI donor result that completes the A10 Native Promotion chain, then request a separate human Integration Court review. + +Reproduction: `npm run verify:rbc13-final-blocker-closure` after supplying the captured full-suite counts; A10 remains the final formal admission blocker. diff --git a/docs/RBC13_FIRST_UNIVERSAL_GROWTH_CELL_v0.1.md b/docs/RBC13_FIRST_UNIVERSAL_GROWTH_CELL_v0.1.md new file mode 100644 index 00000000..e15bbacf --- /dev/null +++ b/docs/RBC13_FIRST_UNIVERSAL_GROWTH_CELL_v0.1.md @@ -0,0 +1,40 @@ +# RBC13 First Universal Growth Cell v0.1 + +- Status: **BLOCKED** +- Cell: `wasm-vm::algorithm::graph-traversal` +- Workload: `graph-traversal::bounded-reachability` +- Evidence root: `0c51d6f19e2d735969b5d37fde159d21dd0cadc40a6435653b92b9e265c90ecd` +- Execution classification: **experimental-native-semantic** +- Universal growth eligible: **false** + +## Workload and growth proof + +The first non-existing-four-operation workload is bounded graph reachability over an adjacency matrix. The RCL source uses reckon recursion, choose control flow, sequence_get, Truth conjunction. The native result is VERIFIED; reachable=true; unreachable=false; replay=true. + +- Source root: `7a7351fb1f1b36d43920db871aa21934577544d5cc33cbe56072e1b506a555eb` +- Native bytecode root: `0c941975f962ec5c0854a993a620b52975e38788bcf7424877ab31c6ac1c233e` +- Native semantic root: `e54e26024c5f3e15f9dc5b9040a41525726bf61c1a8dda7703df37424ee4c898` +- Native runtime: `rcl-native-vm/0.6.0-alpha.1` +- Native no-provider-fallback observation: **true** + +## wasm-vm support audit + +| Surface | Status | Blocker | +|---|---|---| +| compile | BLOCKED | RCL_WASM_VM_ADAPTER_MISSING | +| bytecode/opcode45 | BLOCKED | RCL_WASM_BYTECODE_BRIDGE_MISSING | +| memory/value ABI | BLOCKED | RCL_WASM_MEMORY_VALUE_ABI_MISSING | +| error ABI | BLOCKED | RCL_WASM_ERROR_ABI_MISSING | +| semantic root | BLOCKED | RCL_WASM_SEMANTIC_ROOT_BRIDGE_MISSING | +| replay | BLOCKED | RCL_WASM_REPLAY_HARNESS_MISSING | +| host boundary | BLOCKED | RCL_WASM_HOST_BOUNDARY_MISSING | + +Node's WebAssembly object availability is not treated as an RCL wasm-vm implementation. The repository currently has no wasm adapter or wasm artifact for this workload, so no wasm compile, opcode45, memory/value ABI, error, semantic-root, replay, or host-boundary claim is made. + +## Blocker + +No repository wasm-vm adapter, wasm artifact, opcode45 decoder, linear-memory/value ABI, error bridge, semantic-root bridge, replay harness, or host-boundary witness is present. + +## Boundary + +This report deliberately separates a native RCL graph-traversal growth candidate from the requested wasm-vm admission cell. No wasm support is inferred from Node WebAssembly availability or from the native VM result. diff --git a/docs/RBC13_POLYBODY_ORGAN_PARITY_EVIDENCE_v0.1.md b/docs/RBC13_POLYBODY_ORGAN_PARITY_EVIDENCE_v0.1.md new file mode 100644 index 00000000..82e0c002 --- /dev/null +++ b/docs/RBC13_POLYBODY_ORGAN_PARITY_EVIDENCE_v0.1.md @@ -0,0 +1,18 @@ +# RBC 1.3 Polybody Organ Parity Evidence v0.1 + +- Status: **VERIFIED** experimental parity witness +- Evidence root: `97a58a4091d8ebf8103cac95daeea42225d8cb437dc26af0088a031d17f93ee9` +- Logical Organ: `wasm-vm::algorithm::graph-traversal` +- Bodies: RCL JavaScript reference / native C / WebAssembly +- Canonical permission: **false** + +The three bodies execute independently against the same bounded graph workload. All seven positive, cycle, disconnected, empty, budget, invalid-node, and malformed cases have matching status, result/error projection, semantic roots, and replay roots. + +- C body: **VERIFIED**; host root `c325c2963a9821936b0477fc387c0830a903034e0c20f129eb290d915cda38a3` +- WASM body: **VERIFIED**; module root `f01081a8f64d68d9bb03dc6aab92eee783424ba972f5fc28f72c99c2be86583b` +- Cross-body parity: **true**; replay: **VERIFIED** +- ABI negative controls: unsupported-type=true, malformed-length=true, nonfinite-number=true, duplicate-field=true, invalid-pointer=true + +This document proves replaceable-body parity for one bounded workload. It does not grant canonical language, universal maturity, autonomous growth Level 3+, or version-contract authority. + +Reproduction: `npm run verify:rbc13-wasm-graph-growth-cell` diff --git a/docs/RBC13_WASM_GRAPH_TRAVERSAL_GROWTH_CELL_v0.1.md b/docs/RBC13_WASM_GRAPH_TRAVERSAL_GROWTH_CELL_v0.1.md new file mode 100644 index 00000000..4e5f2b58 --- /dev/null +++ b/docs/RBC13_WASM_GRAPH_TRAVERSAL_GROWTH_CELL_v0.1.md @@ -0,0 +1,44 @@ +# RBC 1.3 WASM Graph Traversal Growth Cell v0.1 + +- Status: **VERIFIED** +- Cell: `wasm-vm::algorithm::graph-traversal` +- Evidence root: `97a58a4091d8ebf8103cac95daeea42225d8cb437dc26af0088a031d17f93ee9` +- Native C body: **VERIFIED**; WASM body: **VERIFIED**; JS reference: **VERIFIED** +- Evidence tier: **native-candidate**; canonical permission: **false** + +## Fixed workload + +bounded breadth-first traversal with deterministic neighbor order, visited discovery order, visited set, step budget, and termination class. Cases: positive-chain, cycle, disconnected, empty, budget-exhaustion, invalid-node, malformed-graph. + +| Case | Class | JS status | Root parity | Result/error parity | JS semantic root | +|---|---|---|---|---|---| +| positive-chain | positive | ok | true | true | e92d63b3638b35ca0ab8e7f943db805c9bf0e8cd76170ce0a1faabd2fe92c2d0 | +| cycle | cycle | ok | true | true | a35b6672827e6a9ce4a0b3202eab6bec4c34c34ec50b869f31463711e2dea464 | +| disconnected | disconnected | ok | true | true | d86dbcc131164bf257b8647598c0eb07090bbe81f6879e08c1522e2eaf3c1d36 | +| empty | empty | error | true | true | 1662aba1282f155ab2039c552542b7c804a4243a91cbad171c9781f86dba2162 | +| budget-exhaustion | budget-exhaustion | ok | true | true | 1388effeeb4789b737b178851a37516cd3e0733a607a8dcf4d9d5a816297b90f | +| invalid-node | invalid-node | error | true | true | b13604b45fc88269801f06f6c3bc85f01c74bc821d7bd08a3a49439d78d11118 | +| malformed-graph | malformed | error | true | true | f9150995f9dfb18649e6ca7e1c0b534d721619c00c77e66ed092d3bbb6fe24b7 | + +## Cross-body and replay gates + +- Positive: **true**; cycle: **true**; disconnected: **true**; empty: **true**; budget exhaustion: **true**; invalid node: **true**; malformed graph: **true** +- Replay: **VERIFIED**; cross-body replay-root parity: **true** +- Universal Stress cell: **VERIFIED**, coverage mode `experimental-cross-body-semantic` + +## WASM value and host ABI + +- Tags: Null=0, Truth=1, Number=2, Text=3, Sequence=4, Record=5, TypedRecord=6 +- Memory: 65536 bytes, input=1024, output=4096 +- Bounds: max nodes=32; recursion=8; nonfinite, malformed, duplicate fields, unsupported types, and invalid pointers fail closed. +- Negative controls: unsupported-type=true, malformed-length=true, nonfinite-number=true, duplicate-field=true, invalid-pointer=true + +## Strategic ruling + +Organ identity is the logical contract plus semantic/evidence identity; C, WASM, or Rust bodies are replaceable only after the same ABI, semantic root, error, receipt, and replay gates pass. + +C and WASM bodies are replaceable implementations of one logical Organ contract. This VERIFIED result is an experimental cross-body parity witness, not canonical permission, universal capability, or proof that organ identity is tied to C. + +Blocker: **none** + +Reproduction: `npm run verify:rbc13-wasm-graph-growth-cell` diff --git a/docs/RCL_AI_ASSIMILATION_INTELLIGENCE_THRESHOLD_v0.1.md b/docs/RCL_AI_ASSIMILATION_INTELLIGENCE_THRESHOLD_v0.1.md new file mode 100644 index 00000000..a2a29342 --- /dev/null +++ b/docs/RCL_AI_ASSIMILATION_INTELLIGENCE_THRESHOLD_v0.1.md @@ -0,0 +1,40 @@ +# RCL AI Assimilation Intelligence Threshold v0.1 + +Historical/superseded report. The current research surface is `docs/RCL_CAPABILITY_ASSIMILATION_COMPATIBILITY_SURFACE_v0.1.md`. This threshold report is retained as prior evidence and must not be read as a global autonomous-growth level or as Native Promotion evidence. + +- Status: **NEGATIVE_RESULT** +- Evidence root: `a53e9fd74f733bc5d8d996f5ae780fc6b297c8116a399fd729bf97231e756f38` +- Donor spec root: `bc05b4a9df5f3bdf71af6c4cc9660dba90c4ce61c2a69683f56b65a3666630a5` +- Prompt root: `c9165f68465ed1ffa5bbf4730308bd072eefd3660e08c8ecf4293e7407725ebe` +- Protocol: same-json-schema-donor-v0.1; temperature=0; seed=13013; format=json +- Human interventions: 0 + +## Tier results + +| Tier | Model | Ollama version | Status | Level | Human repair | Candidate | Native candidate | Promotion | +|---|---|---|---|---|---:|---|---|---| +| small | aetherseed-tinyllama-runtime:latest | ca5641fd566c | NEGATIVE_RESULT | L0 | 0 | semantic-absorbed | BLOCKED | BLOCKED | +| medium | aetherseed-trained-smoke:latest | 5cd5d497f398 | NEGATIVE_RESULT | L2 | 0 | semantic-absorbed | BLOCKED | BLOCKED | +| strong | qwen3.5:latest | 6488c96fa5fa | NEGATIVE_RESULT | L0 | 0 | none | BLOCKED | BLOCKED | + +## Threshold levels + +- L0: donor response unavailable or contract invalid +- L1: contract plus extraction/corpus not complete +- L2: semantic candidate exists but independent differential is unavailable or failed +- L3: independent differential exists but native candidate or promotion is incomplete +- L4: native candidate and promotion evidence are independently verified with zero human repair + +## Required chain + +Donor Spec → Extraction → Signature → Contract → Positive/Negative Corpus → Candidate → Differential → Mutant Detection → Repair Attempt → Native Candidate → Promotion Attempt + +## Conclusion + +No tested tier reached L4; no native promotion credit is granted. Minimum observed level: **L0**; maximum observed level: **L2**. This threshold conclusion is scoped to the declared JSON Schema donor, protocol, and listed local Ollama models. It is not a general claim about AI capability. + +## Boundary + +No implementation code, hidden tests, expected outputs, or human repair were sent to or applied to any model. A failed model is not repaired or upgraded. + +Current strict-growth interpretation: the retained result supports at most `Level 2 VERIFIED` when independent differential evidence is present; `Level 3 CANDIDATE/BLOCKED` requires an AI-generated implementation plus independent differential verification. Formal A10 remains `NEGATIVE_RESULT` until the Native Candidate → Native Process → Semantic Root → Replay → Promotion chain is independently verified. diff --git a/docs/RCL_AI_CAPABILITY_ASSIMILATION_EXPERIMENT_v0.1.md b/docs/RCL_AI_CAPABILITY_ASSIMILATION_EXPERIMENT_v0.1.md new file mode 100644 index 00000000..542784b2 --- /dev/null +++ b/docs/RCL_AI_CAPABILITY_ASSIMILATION_EXPERIMENT_v0.1.md @@ -0,0 +1,45 @@ +# RCL AI Capability Assimilation Experiment v0.1 + +Status: `NEGATIVE_RESULT`; the donor was captured and tested, but it did not close the capability-to-promotion chain. + +## Experiment + +The selected donor was JSON Schema validation. The harness gave the local model a donor contract and a JSON Schema output shape, but did not provide the target implementation body, repository path, hidden expected output, or implementation source. The contract is recorded in `examples/rbc13-ai-donor-json-schema-contract.json`. + +Model: `aetherseed-tinyllama-runtime:latest`. + +The intended chain was: + +```text +external contract -> semantic extraction -> capability spec -> corpus -> differential controls -> native candidate -> Native Promotion +``` + +Only the first four harness stages could be credited after the response was evaluated. No implementation was copied from the repository and no promotion attempt was authorized from this response. + +## Observed response + +The live response was parseable JSON and declared `DonorMeasurement`, exact required fields, `additionalProperties: false`, and basic value/confidence constraints. It nevertheless failed the declared contract because it used JSON Schema draft-07 rather than the required 2020-12 dialect, and its `unit` property omitted the required non-empty-string constraint. + +The independent harness forged 16 deterministic cases and 10 mutation plans. The negative control that removed `additionalProperties: false` was detected, so the control mechanism itself remains useful. That does not repair the donor contract: successful trials were 0 of 1. + +| Item | Evidence | +| --- | --- | +| live prompt root | `c9165f68465ed1ffa5bbf4730308bd072eefd3660e08c8ecf4293e7407725ebe` | +| live raw response root | `69477953ac53798d83cc2aec49931076e25906792e44d03fa53ab9c8fc8f3138` | +| response root | `9530eadbc61384bbb865a6bb37ea76573102ed23803c5f6cd9702889f4b26295` | +| extraction root | `0d311ab94e16be16d8f5162091a8046753900e58807621ffde0fe362e08db462` | +| corpus root | `778ba7ba4f44d499994096055ac06617aa132640886c8c44b9d2893eb1b7963c` | +| live report root | `32d72528b0acba16bc2dfe47a529603f69f6542d818597920fbb85d4d0ce82ef` | +| replay report root | `0154c480b805753d48e3bc95dc7f5b14dd92bd5fb8d0856e6609f7a54a8646a5` | + +Replay uses: + +```text +node scripts/run-rbc13-ai-generate-json-schema.mjs output/rbc13-ai-generate-json-schema/replay-2026-08-09.json --replay examples/rbc13-ai-generated-donor-output-2026-08-09-live.json +``` + +The replay preserves the semantic response and corpus roots while independently hashing the raw replay envelope. There was no manual candidate-body repair and no hidden implementation disclosure. The harness itself performed the capture and replay; this is not counted as a successful AI trial. + +## Governance result + +The donor is not evidence for native semantic admission. It receives no Native Promotion credit, no Universal Stress AI_GENERATE credit, and no canonical admission credit. The correct follow-up is a fresh blinded trial with a corrected contract or a different donor; lowering the schema requirements would invalidate the experiment. diff --git a/docs/RCL_CANONICAL_NUMBER_ENCODING_v2.md b/docs/RCL_CANONICAL_NUMBER_ENCODING_v2.md new file mode 100644 index 00000000..0acd5933 --- /dev/null +++ b/docs/RCL_CANONICAL_NUMBER_ENCODING_v2.md @@ -0,0 +1,52 @@ +# RCL Canonical Number Encoding v2 + +Status: `VERIFIED` as an isolated candidate encoding; `BLOCKED` for canonical activation. + +## Decision + +`rcl.canonical-number.v2` encodes the declared RCL `Number` domain as a finite IEEE-754 binary64 value. The canonical payload is the raw 64-bit value in big-endian byte order, rendered as `0x` followed by exactly 16 lower-case hexadecimal digits. The token is a textual transport form for an eight-byte canonical representation; it is not a decimal pretty-printer. + +The implementation is intentionally explicit in both JavaScript and C: + +- `src/canonical-number-v2.mjs` uses `DataView` and rejects non-finite values. +- `native/rcl_canonical_number_v2.c` and `native/rcl_canonical_number_v2_host.c` use integer bit extraction and do not depend on locale or libc floating-point formatting. +- `-0` is normalized to the `+0` bit pattern `0x0000000000000000`. +- `NaN`, `+Infinity`, and `-Infinity` are language-level illegal for this encoding and fail closed with `RCL_CANONICAL_NUMBER_V2_NONFINITE`. + +This policy gives one encoding per declared numeric value, makes the sign-zero choice explicit, and leaves no runtime-specific decision about special values. + +## Evidence + +The fixed corpus contains 1,000 cases and the generated corpus contains 10,000 finite cases. Seeds are committed in `examples/rbc13-number-encoding-v2-corpus.json`: + +| Evidence | Result | +| --- | --- | +| JS/C cases | 11,000 | +| JS/C mismatches | 0 | +| round trips | 11,000 | +| non-finite rejects | 3 | +| uniqueness mismatches | 0 | +| fixed root | `5fc7d77acb2f6873cfe660d9598febc78f387c97fb5d2f4cc350f2dd3534a860` | +| generated root | `c49c766a201b69b1f98a12cb0f18ffb52931e51cd3404f2b67058316249e1f63` | +| corpus root | `a7071d47b65ad721ff3098b977fc607106ac96bb0ffb5910c3ce998f44894da7` | +| report root | `c8b8f68438fd0c161999f5e9ca17666a3e282a29e5d3dcfce13dc77691f14a9a` | + +The 10,999 uniqueness groups are expected: the explicit `+0` and `-0` inputs share one canonical representation under the chosen policy. The C host was compiled with the locally available MSVC 19.50 toolchain. + +The report's `caseLedger` contains all 11,000 rows. Each row records source representation, input bits, normalized canonical bits, JavaScript token, C token, match, round-trip result, and the isolated v2 semantic root for `{ number: value }`. The C host also evaluates the three non-finite bit patterns and emits `ERROR` for each. + +Reproduce with: + +```text +npm run verify:rbc13-number-encoding-v2 +``` + +## Version isolation and roots + +The existing `rcl.semantic-state-root.v1` implementation and historical receipts were not edited. The previous v1 audit remains a separate result: 7/10 parity on the old extended sample and three known decimal-serialization mismatches. That result is not silently reinterpreted as v2. + +`src/semantic-state-root-v2.mjs` defines a separate `rcl.semantic-state-root.v2` candidate grammar whose numeric token is bound to this encoding. It is not selected by the current runtime. `src/semantic-state-root-migration-v2.mjs` only creates an explicit migration receipt from a verified v1 receipt; it does not rewrite, alias, or claim equality between v1 and v2 roots. + +## Boundary + +This evidence proves cross-language candidate encoding, corpus parity, round-trip behavior, and version isolation. It does not change `VERSION-CONTRACT.json`, `COMPONENT-VERSIONS.json`, the canonical semantic root, the canonical RBC version, or the formal native VM ABI. A future Rust or other runtime must implement the same finite binary64 and sign-zero contract and reproduce the corpus roots before it can be considered equivalent. diff --git a/docs/RCL_CAPABILITY_ASSIMILATION_COMPATIBILITY_SURFACE_v0.1.md b/docs/RCL_CAPABILITY_ASSIMILATION_COMPATIBILITY_SURFACE_v0.1.md new file mode 100644 index 00000000..e1451922 --- /dev/null +++ b/docs/RCL_CAPABILITY_ASSIMILATION_COMPATIBILITY_SURFACE_v0.1.md @@ -0,0 +1,44 @@ +# RCL Capability Assimilation Compatibility Surface v0.1 + +- Status: **NEGATIVE_RESULT** +- Evidence root: `569a1891cec305c01e1e30c45ef62fe47f17ac5c9c0878f65ceb31f3eca754f7` +- Donor contract root: `bdb2f80b8f840acacfe3d6a9c85b4b2f34f0ab673d1d873232d94548b6df57d5` +- Corpus root: `c9c5d4938c71a1e10b106dbb00d6265b69288b544f22c1c4765138034cbf0f67`; cases=100; positive=40; negative=40; boundary=20 +- Independent oracle: **Ajv2020 ajv@8.20.0**, separate process +- Human repairs: **0**; automatic repairs: **4** + +## Model results + +| Tier | Model | Ollama version | Status | ACL | Human repair | Auto repair | Differential | Native Promotion | +|---|---|---|---|---|---:|---:|---|---| +| tiny | aetherseed-tinyllama-runtime:latest | ca5641fd566c | NEGATIVE_RESULT | ACL0 | 0 | 2 | NOT_RUN | BLOCKED | +| medium | aetherseed-trained-smoke:latest | 5cd5d497f398 | CANDIDATE | ACL2 | 0 | 0 | VERIFIED | BLOCKED | +| strong | qwen3.5:latest | 6488c96fa5fa | NEGATIVE_RESULT | ACL0 | 0 | 2 | NOT_RUN | BLOCKED | + +## ACL ruling + +- **ACL0**: invalid donor contract or unavailable donor response +- **ACL1**: valid contract but candidate fails independent differential +- **ACL2**: candidate passes independent differential, mutation controls, and replay +- **ACL3**: Native Organ candidate contract separately verified +- **ACL4**: Native Promotion separately verified + +Best observed ACL: **ACL2**. Strict global growth assessment: **Level 2 VERIFIED**; next level: **Level 3 CANDIDATE/BLOCKED**. + +## Differential contract + +The comparison projection is exact over valid, errors[].keyword, errors[].instancePath, errors[].schemaPath, errors[].params. Mutation controls: ignore-required, minimum-comparison, additional-properties-true, array-item-bypass, enum-equality-bug. Compatibility is alignment-sensitive and donor/protocol scoped; model scale alone does not establish monotonic assimilation. + +## Formal A10 + +- Status: **NEGATIVE_RESULT** +- Requires Native Promotion: **true** +- Chain: AI-generated donor → contract → independent positive/negative differential → mutation controls → replay → Native Candidate → Native Process → Semantic Root → Replay → Promotion +- Conclusion: No Native Promotion proof is present; formal A10 is not VERIFIED. + +## Boundary + +- This is a domain/protocol compatibility result, not a general intelligence ranking. +- The candidate adapter is not the independent oracle and the oracle imports no RCL candidate helper. +- No model receives the hidden corpus, oracle implementation, mutation controls, previous candidate, or human repair. +- No ACL2 result grants native execution, canonical permission, or promotion authority. diff --git a/docs/RCL_JSON_SCHEMA_INDEPENDENT_DONOR_ORACLE_v0.1.md b/docs/RCL_JSON_SCHEMA_INDEPENDENT_DONOR_ORACLE_v0.1.md new file mode 100644 index 00000000..c6ce69dd --- /dev/null +++ b/docs/RCL_JSON_SCHEMA_INDEPENDENT_DONOR_ORACLE_v0.1.md @@ -0,0 +1,40 @@ +# RCL JSON Schema Independent Donor Oracle v0.1 + +- Status: **VERIFIED** +- Evidence root: `569a1891cec305c01e1e30c45ef62fe47f17ac5c9c0878f65ceb31f3eca754f7` +- Implementation: **Ajv2020** +- Exact dependency: **ajv@8.20.0** +- Process boundary: separate Node process per model differential and replay +- Shared candidate imports: **false** +- Semantic projection: valid, errors[].keyword, errors[].instancePath, errors[].schemaPath, errors[].params + +## Supported fixed subset + +- `$schema` +- `$id` +- `title` +- `description` +- `type` +- `required` +- `properties` +- `additionalProperties` +- `enum` +- `minimum` +- `maximum` +- `minLength` +- `maxLength` +- `items` +- `minItems` +- `maxItems` + +## Corpus and controls + +- Corpus root: `c9c5d4938c71a1e10b106dbb00d6265b69288b544f22c1c4765138034cbf0f67` +- Cases: 100 (40 positive / 40 negative / 20 boundary) +- Mutation controls: ignore-required, minimum-comparison, additional-properties-true, array-item-bypass, enum-equality-bug + +## Security and authority boundary + +The oracle imports Ajv and Node built-ins only. It does not import RCL candidate validators, candidate helpers, candidate outputs, or prior research outputs. It runs as a separate process, fails closed on malformed input or schema compilation failure, and emits semantic errors with keyword, instancePath, schemaPath, and params. The oracle is an evidence source, not an execution-authority grant. + +Reproduction: `npm run verify:rbc13-ai-assimilation-compatibility` diff --git a/docs/RCL_NATIVE_EXECUTION_TIER_MODEL_v0.1.md b/docs/RCL_NATIVE_EXECUTION_TIER_MODEL_v0.1.md new file mode 100644 index 00000000..5811bbc0 --- /dev/null +++ b/docs/RCL_NATIVE_EXECUTION_TIER_MODEL_v0.1.md @@ -0,0 +1,55 @@ +# RCL Native Execution Tier Model v0.1 + +Status: `VERIFIED` measurement protocol; `CANDIDATE` architecture proposal; no canonical ABI promotion. + +## Why three paths + +The benchmark separates the cost of a primitive instruction, an evidence-gated native organ, and a provider-shaped boundary. It does not assume that one path wins every workload and it does not treat a fast local call as proof of semantic parity. + +| Tier | Path | Measured identity | Intended fit | +| --- | --- | --- | --- | +| 1 | Primitive Opcode | direct in-process scalar echo; no Domain Value membrane | high-frequency kernel operations | +| 2 | `DOMAIN_CALL` Native Organ | real `rcl_domain_organ_invoke` through the registry and Domain Value ABI v1 | bounded, evidence-backed, replayable absorbed capabilities | +| 3 | Provider Bridge | provider-shaped JSON text boundary with request allocation; no network latency | external models, services, network, and heavyweight adapters | + +## Current measurement + +Protocol: fixed seed `RBC13-PERFORMANCE-2026-08-09`, Number input `9007199254740991`, warmup 1,000, seven repetitions, and 1,000/10,000/100,000 iterations. The latest report root is `014abc49fc58d5d916e254f32fcfb3913acaa4e3232878962cc664901c30cad4`; the C host root is `51d37188584830c147ebe9d1ff4538add9341ff5606c0d2223fccdf6b1ba067f`. + +Median nanoseconds per operation: + +| Iterations | Primitive | Native Organ | Provider-shaped | +| ---: | ---: | ---: | ---: | +| 1,000 | 1.700 | 89.700 | 413.600 | +| 10,000 | 1.220 | 99.240 | 492.680 | +| 100,000 | 1.312 | 89.507 | 413.364 | + +At 100,000 iterations the allocation proxy recorded 0 primitive allocations, 700,000 native-organ clone calls, and 700,000 provider request allocations totaling 35,700,000 bytes. RSS deltas were recorded as a process working-set proxy; process-startup samples were also recorded. The harness records median, p95, mean, variance, replay output, and error status for each path. + +The measured result supports a cost model, not a universal performance ranking: primitive is the smallest path, Native Organ adds registry/ABI conversion cost, and the provider-shaped path adds text and allocation cost. No network or remote-model latency is included. The report deliberately sets `competitiveRankingClaim` to false. + +## Architectural comparison + +### A. Specialized opcodes + +Specialized opcodes can minimize hot-path overhead, but every new capability becomes VM/compiler/version surface. They are difficult to generate from an external capability and create a long-term hard-wired builtin inventory. This route remains appropriate only for genuinely universal kernel primitives. + +### B. `DOMAIN_CALL` plus Native Organ + +The operation name, typed value membrane, registry, evidence tier, authority, causal parent, receipt, and replay identity remain explicit. A capability can be proposed and metabolized without changing the bytecode opcode for every new domain. The current implementation is still experimental and must retain its candidate boundary until legacy, Universal Stress, and AI gates close. + +### C. Provider-only extension + +Provider bridges are the right boundary for network, model, and heavy external work. They carry larger serialization and allocation costs and need stronger external-delivery evidence. Making every capability provider-only would discard the bounded native-organ path and weaken local replayability. + +The evidence therefore supports a proposed hybrid model: Primitive + Evidence-Gated Native Organ + External Provider. It does not prove that the hybrid should yet become canonical. + +## Scope limits + +This round benchmarks a Number echo path. It does not claim complete measurements for Text, Sequence, nested Sequence/Record, semantic-root generation, every error path, or a network provider. Those are explicit follow-up cases before using this report as a release-performance claim. Authority and evidence semantics are verified separately by Native Promotion and the focused RBC13 suite. + +Reproduce with: + +```text +npm run verify:rbc13-execution-benchmark +``` diff --git a/docs/RCL_WASM_DOMAIN_ORGAN_ABI_v0.1.md b/docs/RCL_WASM_DOMAIN_ORGAN_ABI_v0.1.md new file mode 100644 index 00000000..0d76ed11 --- /dev/null +++ b/docs/RCL_WASM_DOMAIN_ORGAN_ABI_v0.1.md @@ -0,0 +1,51 @@ +# RCL WASM Domain Organ ABI v0.1 + +- Status: **VERIFIED** +- Evidence root: `97a58a4091d8ebf8103cac95daeea42225d8cb437dc26af0088a031d17f93ee9` +- Logical operation: `wasm-vm::algorithm::graph-traversal` +- Semantic identity: `graph-traversal::bounded-reachability` +- Evidence tier: **rcl.rbc13-wasm-domain-organ-abi.v0.1 / native-candidate** +- Canonical permission: **false** + +## Contract + +The Organ is identified by the logical operation contract, semantic identity, evidence tier, semantic root, error projection, receipt, and replay rules. C and WASM are replaceable bodies; neither body is allowed to claim canonical permission from registration. + +## Value ABI + +- Null: tag 0 +- Truth: tag 1 +- Number: tag 2 +- Text: tag 3 +- Sequence: tag 4 +- Record: tag 5 +- TypedRecord: tag 6 + +Graph input is a bounded typed-record envelope with tag 5, type id 1, a 28-byte little-endian header, and an adjacency matrix byte payload. Graph output is a typed-record envelope with tag 6, type id 2, fixed result fields, and a bounded visited-order payload. General Null, Truth, Number, Text, Sequence, and Record values use length-delimited recursive envelopes. + +Memory ownership stays in the WASM linear memory membrane. The host validates pointer, length, alignment, bounds, recursion, nonfinite numbers, duplicate record fields, unsupported tags, and malformed lengths; all failures are closed with structured error codes. The native C body never receives a direct pointer to WASM or private native heap memory. + +## Host ABI + +- register: descriptor identity, implementation, artifact root, evidence tier, deterministic flag +- load: validate WebAssembly.Module exports memory and invoke; no native heap pointer accepted +- invoke: copy bounded tagged typed-record bytes into linear memory and invoke(ptr,len) +- readArgs: host owns and bounds-checks the linear-memory input record +- returnResult: read fixed output typed-record header and bounded visited order +- structuredError: class/code/details projection with fail-closed ABI errors +- evidenceReceipt: operation identity, semantic identity, artifact root, tier, pointers, duration, canonicalPermission=false +- canonicalPermission: false +- experimentalTier: true + +## Evidence + +- C body: **VERIFIED**, host root `c325c2963a9821936b0477fc387c0830a903034e0c20f129eb290d915cda38a3` +- WASM body: **VERIFIED**, module root `f01081a8f64d68d9bb03dc6aab92eee783424ba972f5fc28f72c99c2be86583b` +- Cross-body root parity: **true** +- Error class/code/details parity: **true** +- Replay: **VERIFIED** +- ABI negative controls: **true** + +This is an experimental ABI witness for one bounded workload. It is not a canonical VM activation or a claim of general WASM support. + +Reproduction: `npm run verify:rbc13-wasm-graph-growth-cell` diff --git a/examples/rbc13-ai-donor-json-schema-contract.json b/examples/rbc13-ai-donor-json-schema-contract.json new file mode 100644 index 00000000..6b3b5cca --- /dev/null +++ b/examples/rbc13-ai-donor-json-schema-contract.json @@ -0,0 +1,26 @@ +{ + "format": "rcl.rbc13-ai-generate-json-schema-contract.v0.1", + "model": "aetherseed-tinyllama-runtime:latest", + "task": "Generate one JSON Schema draft 2020-12 for a donor measurement object from a bounded intent. The donor output is a source capability only; it must not contain RCL, DOMAIN_CALL, native-organ, provider implementation, repository source, or expected test output.", + "requirements": { + "rootType": "object", + "title": "DonorMeasurement", + "requiredExactly": ["id", "value", "unit", "confidence"], + "additionalProperties": false, + "properties": { + "id": {"type": "string", "minLength": 1}, + "value": {"type": "number"}, + "unit": {"type": "string", "minLength": 1}, + "confidence": {"type": "number", "minimum": 0, "maximum": 1} + } + }, + "evaluation": [ + "Parse one JSON object with no markdown wrapper.", + "Check the donor schema contract independently of the RCL implementation.", + "Extract a capability with the existing JSON Schema frontend.", + "Forge a deterministic equivalence corpus and record its root.", + "Run a negative control that removes one required schema constraint.", + "A missing model, invalid JSON, or contract failure is recorded as BLOCKED or NEGATIVE_RESULT; it is never upgraded to PASS." + ], + "boundary": "This is an AI_GENERATE donor experiment contract. It does not prove native execution, semantic equivalence, or canonical admission." +} diff --git a/examples/rbc13-ai-generated-donor-output-2026-08-09-live.json b/examples/rbc13-ai-generated-donor-output-2026-08-09-live.json new file mode 100644 index 00000000..ef2ad5fe --- /dev/null +++ b/examples/rbc13-ai-generated-donor-output-2026-08-09-live.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "DonorMeasurement", + "type": "object", + "properties": { + "id": { + "type": "string", + "required": true, + "minLength": 1 + }, + "value": { + "type": "number", + "required": true, + "minimum": 0 + }, + "unit": { + "type": "string", + "enum": ["kg", "mg/dL", "ml/dL", "mmol/L", "ug/dL"] + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + } + }, + "additionalProperties": false, + "required": ["id", "value", "unit", "confidence"] +} diff --git a/examples/rbc13-ai-generated-donor-output-2026-08-09.json b/examples/rbc13-ai-generated-donor-output-2026-08-09.json new file mode 100644 index 00000000..a6f23d70 --- /dev/null +++ b/examples/rbc13-ai-generated-donor-output-2026-08-09.json @@ -0,0 +1,27 @@ +{ + "type": "object", + "title": "DonorMeasurement Schema", + "required": ["id", "value", "unit", "confidence"], + "properties": { + "id": { + "type": "string", + "minLength": 1, + "maxLength": 255 + }, + "value": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "unit": { + "type": "string", + "minLength": 1, + "maxLength": 255 + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + } + } +} diff --git a/examples/rbc13-domain-call-salvage-evidence-2026-08-08.json b/examples/rbc13-domain-call-salvage-evidence-2026-08-08.json new file mode 100644 index 00000000..b8699674 --- /dev/null +++ b/examples/rbc13-domain-call-salvage-evidence-2026-08-08.json @@ -0,0 +1,50 @@ +{ + "schema": "rcl.rbc13-domain-call-salvage-evidence.v0.1", + "date": "2026-08-08", + "status": "CANDIDATE", + "branch": "research/rbc13-domain-call-salvage-v0.1", + "sourceBranch": "agent/advanced-runtime-rcl", + "salvageReportRoot": "73b6c0f5ec20779beb27f6bc289b9164df04d8ebda22716cb1cf0ed0f2bcc1f3", + "localFocusedExecution": { + "sourceSnapshot": "user-uploaded RCL v0.94.0-alpha.1 source package", + "dependencyBlobMatchesCurrentMain": { + "src/quantity.mjs": "edeb3926d2b85e0b9f617cac97fc47d23018c1d1", + "src/knowledge.mjs": "35d110c060af045d22976b78971bb235e9aa424e", + "src/errors.mjs": "4e35b747680cfbc918ee28522a68b3c25de3c46a" + }, + "commandClass": "node --check + node --test focused salvage suite", + "tests": { + "total": 7, + "passed": 7, + "failed": 0 + }, + "covered": [ + "historical ABI quarantine", + "18-operation inventory partition", + "core.echo", + "quantity.make", + "quantitative.measure", + "knowledge.claim", + "legacy native-only fail-closed controls", + "unknown-operation rejection" + ] + }, + "differentialHarness": { + "implemented": true, + "executedInUploadedSnapshot": false, + "reason": "The uploaded source snapshot predates src/differential-absorption-runner.mjs, so it cannot execute the new harness without importing newer main dependencies.", + "githubHostedExecution": "INFRASTRUCTURE_BLOCKED", + "githubWorkflowObservation": "Pull-request workflows completed as failure with zero executable job steps, consistent with the existing hosted-runner account/billing infrastructure blocker. This is not treated as a code-test failure." + }, + "authority": { + "canonicalRbc13Enabled": false, + "nativePromotionAllowed": false, + "nativeFoundationSyntaxClaimAllowed": false, + "oldBinaryReuseAllowed": false + }, + "limitations": [ + "The focused local test validates the source-only salvage organ against current-main-matching quantity, knowledge and error modules; it is not native execution evidence.", + "The differential harness has not yet produced an execution receipt in a checkout containing the current differential runner.", + "No opcode, compiler, VM, package version or VERSION-CONTRACT authority has changed." + ] +} diff --git a/examples/rbc13-domain-chain-local-evidence-2026-08-08.json b/examples/rbc13-domain-chain-local-evidence-2026-08-08.json new file mode 100644 index 00000000..661a7d94 --- /dev/null +++ b/examples/rbc13-domain-chain-local-evidence-2026-08-08.json @@ -0,0 +1,78 @@ +{ + "format": "taowind.rcl-rbc13-domain-chain-local-evidence.v0.1", + "status": "PASS_WITH_EXACT_CURRENT_NATIVE_ROOT_CI_PENDING", + "executedAt": "2026-08-08T06:04:00+08:00", + "chain": [ + "quantity.make Temperature 25", + "quantity.make Temperature 0.5 uncertainty", + "quantitative.measure using prior state references", + "knowledge.claim using prior quantity plus Sequence evidence/dependencies" + ], + "candidateOrgans": { + "core.echo": "native-candidate", + "quantity.make": "native-candidate", + "quantitative.measure": "native-candidate", + "knowledge.claim": "native-candidate" + }, + "rbc": { + "version": "1.3", + "opcode": 45, + "instructions": 51, + "bytes": 1191, + "sha256": "d3ddd2e84e22759a610a040f4ad4119bdd6f67ea9c0040214eb37298a62dd30c" + }, + "execution": { + "exitCode": 0, + "typedHeapAllocated": 7, + "typedHeapRegistered": 7, + "stateEntries": 4, + "peakStackDepth": 10, + "semanticParityWithCurrentJsOracles": true, + "independentlyComputedSemanticStateRoot": "736b336eecb96c4fb3a02eaa7d4b9d6e07fd126d65de31d541cf47444bc33509" + }, + "measurement": { + "kind": "Measurement", + "baseType": "Temperature", + "value": { "kind": "Quantity", "type": "Temperature", "value": 25, "unit": "°C" }, + "uncertainty": { "kind": "Quantity", "type": "Temperature", "value": 0.5, "unit": "°C" }, + "confidence": 0.9, + "unit": "°C", + "scale": "ratio", + "evidence": ["sensor:e1", "sensor:e1", "sensor:e2"], + "calibratedBy": "sensor-A" + }, + "knowledge": { + "kind": "Knowledge", + "baseType": "Temperature", + "value": { "kind": "Quantity", "type": "Temperature", "value": 25, "unit": "°C" }, + "confidence": 0.8, + "evidence": ["e1", "e2"], + "source": "lab", + "scope": "local", + "status": "provisional", + "dependencies": ["dep:a", "dep:b"], + "revision": 1, + "alternatives": [], + "formedAtRoot": "root-1" + }, + "parityMethod": { + "quantityOracle": "src/quantity.mjs::quantity", + "measurementOracle": "src/quantity.mjs::measurement", + "knowledgeOracle": "src/knowledge.mjs::knowledgeClaim", + "nativeHeapMetadataExcluded": ["__rclKind", "__rclType", "__rclObjectId", "__rclFieldOffsets", "__rclPayloadOffsets"], + "result": "The native candidate state and JS oracle state are byte-identical after canonical semantic normalization; both independently hash to the same semantic root shown above." + }, + "localBasis": { + "uploadedSnapshot": "RCL-main (2)(6).zip", + "nativeRclvmGitBlob": "0b50c2db2f2e2078c5470763b86bf17bb2694736", + "candidateHostSha256": "da4d1d9cd4e625e1c5135cf1b0fc528e12d6c891a0cf873539f9131a04ede4ff", + "outputSha256": "d624fb7dd123e4548d590da1dc30205f379122731a2a0c4bf4047fb58a0dec21" + }, + "boundaries": [ + "All four organs are candidate implementations only; none is native-verified.", + "The local uploaded native VM predates native stateRoot emission, so the root above was computed independently from semantic state and compared with current JavaScript oracles.", + "The checked-in exact-current-source integration test additionally requires the materialized current native VM to emit the same stateRoot.", + "Canonical native/rclvm.c and src/bytecode.mjs remain unchanged.", + "Canonical RBC admission remains undecided." + ] +} diff --git a/examples/rbc13-domain-organ-local-evidence-2026-08-08.json b/examples/rbc13-domain-organ-local-evidence-2026-08-08.json new file mode 100644 index 00000000..e6143115 --- /dev/null +++ b/examples/rbc13-domain-organ-local-evidence-2026-08-08.json @@ -0,0 +1,41 @@ +{ + "format": "taowind.rcl-rbc13-domain-organ-local-evidence.v0.1", + "status": "PASS", + "executedAt": "2026-08-08T06:04:00+08:00", + "basis": { + "uploadedSnapshot": "RCL-main (2)(6).zip", + "verifiedCurrentBlobMatches": { + "src/bytecode.mjs": "f6b35f04f3dc59d68c3aa23137fb83409b1b3089", + "src/quantity.mjs": "edeb3926d2b85e0b9f617cac97fc47d23018c1d1", + "src/knowledge.mjs": "35d110c060af045d22976b78971bb235e9aa424e" + } + }, + "focusedTests": { + "domainOrganJs": "3/3 PASS", + "rbc13CandidateEncoding": "3/3 PASS", + "nativeDomainOrganCompileSmoke": "PASS", + "combinedFocused": "7/7 PASS", + "existingBytecodeNativeTypedRegression": "18 PASS / 2 platform skips / 0 FAIL" + }, + "fullSuite": { + "status": "NOT_CLAIMED", + "reason": "npm test exceeded the available execution window" + }, + "artifactsSha256": { + "src/domain-operation-organ.mjs": "06c979254355ad050bcf6e1ed70fb55a95c41a49592673683f41f984b4bfc097", + "src/rbc13-domain-bytecode-candidate.mjs": "1df0269120a1adb640d86eeba75cf06bb4c8b14abefab91c5e7a442070ff1fd4", + "native/rcl_domain_organ.h": "d6ca36668984b9afba6fe158643febcc4d6b8ede4762891d817fb44a19568a98", + "native/rcl_domain_organ.c": "f715d796f1168f1ed11c9d5f7cd300fa731cba87d919268370576521999813ac", + "native/domain_organ_smoke.c": "301db13c8e18cde342d63a13c7ca8c8cdd354c2f462bb4bf13159ca84c52632a", + "tests/domain-operation-organ.test.mjs": "0da806519ba5e2adc64c8f7291358d213f36c49116ce1ffa3438b6dc298847fb", + "tests/domain-operation-organ-native.test.mjs": "47808f00579ffc2d3b618dc36e1fe03b0dcff8693a9084288b2be8fc99971de2", + "tests/rbc13-domain-call-bytecode-candidate.test.mjs": "7edd62aefff0c6eee1b430896902f4e329b2260b338304c9670c5a3e2b5d6b43" + }, + "boundaries": [ + "canonical compileRealityToBytecode remains unchanged and emits only RBC 1.1/1.2", + "current native/rclvm.c does not yet execute opcode 45", + "native organ registration does not imply native-verified", + "canonical RBC 1.3 admission is not claimed" + ], + "evidenceRoot": "b914d2cbbd8edd47cd2bffe335beceaa3356c0769780c8d302b7097e7108362e" +} diff --git a/examples/rbc13-domain-public-api-local-evidence-2026-08-08.json b/examples/rbc13-domain-public-api-local-evidence-2026-08-08.json new file mode 100644 index 00000000..856df766 --- /dev/null +++ b/examples/rbc13-domain-public-api-local-evidence-2026-08-08.json @@ -0,0 +1,33 @@ +{ + "format": "taowind.rcl-rbc13-domain-public-api-local-evidence.v0.1", + "status": "PASS_WITH_EXACT_CURRENT_SOURCE_CI_PENDING", + "executedAt": "2026-08-08T06:04:00+08:00", + "api": { + "abi": "RCLVM_DOMAIN_CANDIDATE_ABI_V1", + "functions": [ + "rclvm_instance_register_domain_organ", + "rclvm_instance_set_domain_minimum_tier", + "rclvm_instance_domain_organ_count", + "rclvm_instance_domain_minimum_tier" + ], + "privateVmFieldAccessRequiredByHost": false + }, + "localExecution": { + "registeredOrganCount": 4, + "quantityMeasurementKnowledgeChainExitCode": 0, + "semanticOutputSha256": "d624fb7dd123e4548d590da1dc30205f379122731a2a0c4bf4047fb58a0dec21", + "candidatePublicVmSourceSha256": "cf8be9eb60f6c09a5c92559730bad1e454b24bdfb045877be83987cd699208e0", + "candidatePublicHostSha256": "574fc81c067d5da02a02e5cd3adb6859e9cc484f12ff97361f6511a8b74a496d" + }, + "architecture": { + "registrationOwnership": "The VM registry deep-copies organ identity strings; callers may release temporary descriptor registries after registration.", + "defaultMinimumTier": "native-verified", + "experimentalHostOverride": "The local candidate host explicitly lowers the minimum tier for candidate experiments; this is not canonical runtime behavior.", + "canonicalFilesUntouched": ["src/bytecode.mjs", "native/rclvm.c", "native/rclvm.h"] + }, + "boundaries": [ + "The candidate public API is materialized into an experimental VM source; it is not part of canonical rclvm.h.", + "All four registered organs remain experimental/native-candidate descriptors in the candidate host and are not Native Promotion verified.", + "Exact current-source compilation remains represented by checked-in tests and CI-pending." + ] +} diff --git a/examples/rbc13-domain-value-membrane-evidence-2026-08-08.json b/examples/rbc13-domain-value-membrane-evidence-2026-08-08.json new file mode 100644 index 00000000..dc723c37 --- /dev/null +++ b/examples/rbc13-domain-value-membrane-evidence-2026-08-08.json @@ -0,0 +1,39 @@ +{ + "format": "taowind.rcl-rbc13-domain-value-membrane-evidence.v0.1", + "status": "PASS_WITH_CURRENT_VM_CI_PENDING", + "executedAt": "2026-08-08T06:04:00+08:00", + "localBasis": { + "uploadedSnapshot": "RCL-main (2)(6).zip", + "nativeRclvmGitBlob": "0b50c2db2f2e2078c5470763b86bf17bb2694736", + "note": "The uploaded native/rclvm.c predates the later semantic-state-root producer patch. The bridge-relevant Value/typed-record/sequence helpers used by this smoke are unchanged by the recorded PR #29 semantic-root diff, but exact current-branch execution remains a CI gate." + }, + "domainValueAbi": { + "compiler": "cc -std=c11 -Wall -Wextra -pedantic", + "result": "PASS", + "checks": [ + "native-candidate organ is rejected by a native-verified minimum tier", + "Text crosses the organ boundary by owned clone", + "Quantity-shaped typed Record crosses the organ boundary by owned deep clone", + "duplicate Record field names are rejected", + "invalid Truth payload is rejected at the organ boundary", + "uninitialized output value is rejected" + ] + }, + "vmValueMembrane": { + "compiler": "cc -std=c11 -Wall -Wextra -pedantic + libcrypto + libm", + "result": "PASS", + "checks": [ + "native Text -> Domain Value -> native Value round-trip", + "Domain Quantity/Temperature Record -> native typed Record -> Domain Value round-trip", + "Domain Sequence -> native Sequence -> Domain Value round-trip", + "unsupported native Span fails closed with RCL_NATIVE_DOMAIN_VALUE_UNSUPPORTED" + ] + }, + "boundaries": [ + "canonical src/bytecode.mjs remains unchanged", + "canonical native/rclvm.c remains unchanged in this PR", + "opcode 45 is not yet executed by the canonical native VM", + "exact current-rclvm membrane compilation is represented by the checked-in branch test and remains CI-pending", + "no organ is native-verified by this evidence" + ] +} diff --git a/examples/rbc13-json-schema-donor-contract.json b/examples/rbc13-json-schema-donor-contract.json new file mode 100644 index 00000000..9a0d63df --- /dev/null +++ b/examples/rbc13-json-schema-donor-contract.json @@ -0,0 +1,84 @@ +{ + "format": "rcl.rbc13-json-schema-donor-contract.v0.1", + "version": "0.1.0", + "id": "rbc13_donor_measurement_bounded_subset_v1", + "title": "RBC13 DonorMeasurement bounded JSON Schema subset", + "subset": { + "supported": [ + "$schema", + "$id", + "title", + "description", + "type", + "required", + "properties", + "additionalProperties", + "enum", + "minimum", + "maximum", + "minLength", + "maxLength", + "items", + "minItems", + "maxItems" + ], + "unsupported": [ + "$ref", + "$defs", + "oneOf", + "anyOf", + "allOf", + "not", + "if", + "then", + "else", + "pattern", + "format", + "propertyNames", + "dependentRequired", + "dependentSchemas", + "contains", + "uniqueItems", + "unevaluatedProperties", + "unevaluatedItems", + "const", + "multipleOf" + ], + "undefinedOutOfScope": "Schemas using keywords outside supported are not eligible for this donor experiment and are not used as negative evidence against the candidate." + }, + "intent": { + "rootType": "object", + "title": "DonorMeasurement", + "description": "A bounded measurement record with optional tags and metadata.", + "requiredFields": ["id", "value", "unit"], + "properties": { + "id": { "type": "string", "minLength": 3, "maxLength": 64 }, + "value": { "type": "number", "minimum": -1000, "maximum": 1000 }, + "unit": { "type": "string", "enum": ["mL", "ml", "g"] }, + "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, + "tags": { + "type": "array", + "items": { "type": "string", "minLength": 1, "maxLength": 16 }, + "minItems": 0, + "maxItems": 4 + }, + "metadata": { + "type": "object", + "properties": { + "source": { "type": "string", "minLength": 1, "maxLength": 32 }, + "verified": { "type": "boolean" } + }, + "required": ["source"], + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "boundary": { + "oracleMustBeIndependent": true, + "candidateMustStartEmpty": true, + "hiddenCorpus": true, + "humanRepairs": 0, + "nonFiniteNumbersAreInvalid": true + } +} diff --git a/examples/rbc13-legacy-evidence-expected-inventory.json b/examples/rbc13-legacy-evidence-expected-inventory.json new file mode 100644 index 00000000..d71247d8 --- /dev/null +++ b/examples/rbc13-legacy-evidence-expected-inventory.json @@ -0,0 +1,103 @@ +{ + "format": "rcl.rbc13-legacy-evidence-expected-inventory.v0.1", + "version": "0.1.0", + "authority": "committed-stable-id-inventory", + "description": "Authoritative current-source RBC 1.1/RBC 1.2 legacy evidence cases for RBC 1.3 admission closure.", + "cases": [ + { + "id": "rbc11.stage5.encoder", + "rbcVersion": "1.1", + "family": "RBC1.1", + "runner": "stage5", + "runtimeVersion": "rcl-native-vm/0.6.0-alpha.1", + "sourcePaths": [ + "src/bootstrap.mjs", + "src/bytecode.mjs", + "src/native-vm.mjs", + "bootstrap/compiler-stage5.rcl" + ], + "expectedStatus": "VERIFIED", + "scope": "Stage-5 exact RBC 1.1 encoder parity and native target replay" + }, + { + "id": "rbc12.foundation.batch-a", + "rbcVersion": "1.2", + "family": "RBC1.2", + "runner": "foundation-batch-a", + "runtimeVersion": "rcl-native-vm/0.6.0-alpha.1", + "sourcePaths": [ + "src/foundation-native-batch-runtime.mjs", + "src/foundation-native-bridge.mjs", + "native/foundation_provider.c", + "native/rclvm.c", + "native/rclvm.h" + ], + "expectedStatus": "VERIFIED", + "scope": "Six-domain Foundation Native Provider Bridge batch A" + }, + { + "id": "rbc12.foundation.meta-batch-b", + "rbcVersion": "1.2", + "family": "RBC1.2", + "runner": "foundation-meta-batch-b", + "runtimeVersion": "rcl-native-vm/0.6.0-alpha.1", + "sourcePaths": [ + "src/foundation-native-batch-runtime.mjs", + "src/foundation-native-meta-bridge.mjs", + "native/foundation_provider.c", + "native/rclvm.c", + "native/rclvm.h" + ], + "expectedStatus": "VERIFIED", + "scope": "Meta-spacetime/acceleration/compression Foundation Native bridge batch B" + }, + { + "id": "rbc12.foundation.batch-c", + "rbcVersion": "1.2", + "family": "RBC1.2", + "runner": "foundation-batch-c", + "runtimeVersion": "rcl-native-vm/0.6.0-alpha.1", + "sourcePaths": [ + "src/foundation-native-batch-runtime.mjs", + "src/foundation-native-batch-c.mjs", + "native/foundation_provider.c", + "native/rclvm.c", + "native/rclvm.h" + ], + "expectedStatus": "VERIFIED", + "scope": "Physical and embodiment Foundation Native bridge batch C" + }, + { + "id": "rbc12.foundation.batch-d", + "rbcVersion": "1.2", + "family": "RBC1.2", + "runner": "foundation-batch-d", + "runtimeVersion": "rcl-native-vm/0.6.0-alpha.1", + "sourcePaths": [ + "src/foundation-native-batch-runtime.mjs", + "src/foundation-native-batch-d.mjs", + "native/foundation_provider.c", + "native/rclvm.c", + "native/rclvm.h" + ], + "expectedStatus": "VERIFIED", + "scope": "Energy/elemental/neural Foundation Native bridge batch D" + }, + { + "id": "rbc12.foundation.batch-e", + "rbcVersion": "1.2", + "family": "RBC1.2", + "runner": "foundation-batch-e", + "runtimeVersion": "rcl-native-vm/0.6.0-alpha.1", + "sourcePaths": [ + "src/foundation-native-batch-runtime.mjs", + "src/foundation-native-batch-e.mjs", + "native/foundation_provider.c", + "native/rclvm.c", + "native/rclvm.h" + ], + "expectedStatus": "VERIFIED", + "scope": "Metacomputation/computation Foundation Native bridge batch E" + } + ] +} diff --git a/examples/rbc13-number-encoding-v2-corpus.json b/examples/rbc13-number-encoding-v2-corpus.json new file mode 100644 index 00000000..68a4ddaa --- /dev/null +++ b/examples/rbc13-number-encoding-v2-corpus.json @@ -0,0 +1,15 @@ +{ + "format": "rcl.rbc13-number-encoding-v2-corpus-manifest.v0.1", + "encoding": "rcl.canonical-number.v2", + "fixedSeed": "0x52424331335f4e31", + "fixedCaseCount": 1000, + "fixedRoot": "5fc7d77acb2f6873cfe660d9598febc78f387c97fb5d2f4cc350f2dd3534a860", + "generatedSeed": "0x52424331335f4731", + "generatedCaseCount": 10000, + "generatedRoot": "c49c766a201b69b1f98a12cb0f18ffb52931e51cd3404f2b67058316249e1f63", + "corpusRoot": "a7071d47b65ad721ff3098b977fc607106ac96bb0ffb5910c3ce998f44894da7", + "finiteOnly": true, + "negativeZeroPolicy": "canonicalize-to-positive-zero", + "canonicalAdmission": false, + "boundary": "The corpus is a deterministic candidate evidence input. It does not change rcl.semantic-state-root.v1 or any canonical version contract." +} diff --git a/examples/rbc13-opcode45-candidate-local-evidence-2026-08-08.json b/examples/rbc13-opcode45-candidate-local-evidence-2026-08-08.json new file mode 100644 index 00000000..1799e8a7 --- /dev/null +++ b/examples/rbc13-opcode45-candidate-local-evidence-2026-08-08.json @@ -0,0 +1,86 @@ +{ + "format": "taowind.rcl-rbc13-opcode45-candidate-local-evidence.v0.1", + "status": "PASS_WITH_EXACT_CURRENT_VM_CI_PENDING", + "executedAt": "2026-08-08T06:04:00+08:00", + "candidate": { + "bytecodeVersion": "1.3", + "opcode": 45, + "dispatchModes": ["literal", "dynamic"], + "canonicalNativeVmModified": false, + "materialization": "scripts/materialize-rbc13-domain-vm-candidate.mjs" + }, + "localBasis": { + "uploadedSnapshot": "RCL-main (2)(6).zip", + "nativeRclvmGitBlob": "0b50c2db2f2e2078c5470763b86bf17bb2694736", + "candidateVmSourceSha256": "799590bb8ac481b06c8306ac685d7ac3b8d506443450edad9f05752fa5ea0d7b", + "candidateHostSha256": "59295d99dbfd94cf8f712c8ae3cacfa415309e9c3121628195b00a2d63e3cbac", + "note": "The uploaded native VM predates PR #29 semantic-root emission. Exact current-branch compilation and stateRoot assertions are checked in as tests but remain CI-pending." + }, + "candidateOrgans": [ + { + "key": "core.echo", + "tier": "native-candidate", + "localResult": "PASS" + }, + { + "key": "quantity.make", + "tier": "native-candidate", + "localResult": "PASS", + "positive": { + "input": ["Temperature", 25, ""], + "state": { + "kind": "Quantity", + "type": "Temperature", + "value": 25, + "unit": "°C" + }, + "expectedSemanticStateRootOnCurrentAlgorithm": "18022b790c72ace7be2e32fefdd21ee785ce2454a36343498bfce5960a0678ea" + }, + "negative": { + "input": ["Warp", 25, ""], + "exitCode": 2, + "errorCode": "RCL_NATIVE_DOMAIN_ORGAN_FAILURE", + "messageContains": "RCL_DOMAIN_QUANTITY_TYPE" + } + } + ], + "executionChecks": [ + { + "case": "default evidence minimum rejects native-candidate core.echo", + "exitCode": 2, + "messageContains": "RCL_DOMAIN_ORGAN_EVIDENCE_TIER" + }, + { + "case": "literal core.echo(Text)", + "exitCode": 0, + "state": { "result.echo": "hello" } + }, + { + "case": "dynamic core.echo(Number)", + "exitCode": 0, + "state": { "result.dynamic": 42 } + }, + { + "case": "missing domain operation", + "exitCode": 2, + "messageContains": "RCL_DOMAIN_ORGAN_MISSING" + }, + { + "case": "legacy RBC 1.1 regression", + "exitCode": 0, + "state": { "world.ready": true } + } + ], + "artifacts": { + "quantityRbcSha256": "9cd667a502d63b812f6fd340cf71bfb7d551d498e3dfa61f1405a101bab5846c", + "invalidQuantityRbcSha256": "2d3a89bc4ace4128b1635f6764919af2829b36940fd81e1be13dc6c3cea56b34", + "localAdmittedOrgansSourceSha256": "7a80a13d75b160619181f120e4c817c69dbc45047483ace79e13b8a7604b2719" + }, + "boundaries": [ + "core.echo and quantity.make remain native-candidate, not native-verified", + "quantitative.measure and knowledge.claim are not yet implemented as C candidate organs", + "canonical src/bytecode.mjs and native/rclvm.c remain unchanged", + "canonical RBC version has not moved to 1.3", + "exact current native semantic-state-root execution remains CI-pending" + ] +} diff --git a/examples/rbc13-universal-growth-graph-traversal.rcl b/examples/rbc13-universal-growth-graph-traversal.rcl new file mode 100644 index 00000000..2f1ddf38 --- /dev/null +++ b/examples/rbc13-universal-growth-graph-traversal.rcl @@ -0,0 +1,24 @@ +reality Rbc13UniversalGraphTraversal { + facet graph.row_a : Sequence = sequence_append(sequence_append(sequence_append(sequence_append(empty_sequence(), 0), 1), 0), 0) + facet graph.row_b : Sequence = sequence_append(sequence_append(sequence_append(sequence_append(empty_sequence(), 0), 0), 0), 1) + facet graph.row_c : Sequence = sequence_append(sequence_append(sequence_append(sequence_append(empty_sequence(), 0), 0), 0), 0) + facet graph.row_d : Sequence = sequence_append(sequence_append(sequence_append(sequence_append(empty_sequence(), 0), 0), 0), 0) + facet graph.matrix : Sequence = sequence_append(sequence_append(sequence_append(sequence_append(empty_sequence(), graph.row_a), graph.row_b), graph.row_c), graph.row_d) + facet graph.start : Number = 0 + facet graph.target : Number = 3 + facet graph.depth_budget : Number = 3 + + reckon edge(matrix : Sequence, from : Number, to : Number) -> Truth = + sequence_get(sequence_get(matrix, from), to) == 1 + + reckon reachable(matrix : Sequence, current : Number, target : Number, remaining : Number) -> Truth = + choose(current == target, true, + choose(remaining <= 0, false, + choose(edge(matrix, current, 0) and reachable(matrix, 0, target, remaining - 1), true, + choose(edge(matrix, current, 1) and reachable(matrix, 1, target, remaining - 1), true, + choose(edge(matrix, current, 2) and reachable(matrix, 2, target, remaining - 1), true, + choose(edge(matrix, current, 3) and reachable(matrix, 3, target, remaining - 1), true, false)))))) + + facet graph.reachable : Truth = reachable(graph.matrix, graph.start, graph.target, graph.depth_budget) + facet graph.unreachable : Truth = reachable(graph.matrix, graph.start, 2, graph.depth_budget) +} diff --git a/native/domain_organ_smoke.c b/native/domain_organ_smoke.c new file mode 100644 index 00000000..08e984e2 --- /dev/null +++ b/native/domain_organ_smoke.c @@ -0,0 +1,87 @@ +#include "rcl_domain_organ.h" +#include +#include + +static int echo( + void *userdata, + const char *domain, + const char *operation, + const RclDomainValueV1 *args, + size_t argc, + RclDomainValueV1 *result, + RclDomainOrganErrorV1 *error +) { + (void)userdata; (void)domain; (void)operation; + if (argc != 1) { + rcl_domain_organ_error_set(error, "RCL_DOMAIN_ECHO_ARITY", "RCL_DOMAIN_ECHO_ARITY: echo expects one argument"); + return 0; + } + return rcl_domain_value_clone(result, &args[0]); +} + +static int set_field(RclDomainValueV1 *record, size_t index, const char *name, RclDomainValueV1 *value) { + int ok = rcl_domain_value_record_set(record, index, name, value); + rcl_domain_value_free(value); + return ok; +} + +int main(void) { + RclDomainOrganRegistry registry; + rcl_domain_organ_registry_init(®istry); + RclDomainOrganV1 organ = { + RCL_DOMAIN_ORGAN_ABI_V1, "core", "echo", "core.echo", "smoke.echo", NULL, + RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, 1, echo, NULL + }; + char registration_error[256] = {0}; + RclDomainOrganErrorV1 error; + rcl_domain_organ_error_clear(&error); + RclDomainValueV1 text, result; + rcl_domain_value_init(&text); + rcl_domain_value_init(&result); + if (!rcl_domain_value_set_text(&text, "hello", "Text")) return 9; + if (!rcl_domain_organ_register(®istry, &organ, registration_error, sizeof(registration_error))) return 10; + + if (rcl_domain_organ_invoke(®istry, "core", "echo", RCL_DOMAIN_ORGAN_NATIVE_VERIFIED, &text, 1, &result, &error)) return 11; + if (strcmp(error.code, "RCL_DOMAIN_ORGAN_EVIDENCE_TIER") != 0) return 12; + if (!rcl_domain_organ_invoke(®istry, "core", "echo", RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, &text, 1, &result, &error)) return 13; + if (!rcl_domain_value_equal(&text, &result)) return 14; + rcl_domain_value_free(&result); + + RclDomainValueV1 quantity, field; + rcl_domain_value_init(&quantity); + rcl_domain_value_init(&field); + if (!rcl_domain_value_make_record(&quantity, "Quantity", 4, "Temperature")) return 15; + if (!rcl_domain_value_set_text(&field, "Quantity", "Text") || !set_field(&quantity, 0, "kind", &field)) return 16; + if (!rcl_domain_value_set_text(&field, "Temperature", "Text") || !set_field(&quantity, 1, "type", &field)) return 17; + if (!rcl_domain_value_set_number(&field, 25, "Number") || !set_field(&quantity, 2, "value", &field)) return 18; + if (!rcl_domain_value_set_text(&field, "°C", "Text") || !set_field(&quantity, 3, "unit", &field)) return 19; + if (!rcl_domain_value_validate(&quantity)) return 20; + if (!rcl_domain_organ_invoke(®istry, "core", "echo", RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, &quantity, 1, &result, &error)) return 21; + if (!rcl_domain_value_equal(&quantity, &result)) return 22; + rcl_domain_value_free(&result); + + if (!rcl_domain_value_set_text(&field, "duplicate", "Text")) return 23; + if (rcl_domain_value_record_set(&quantity, 3, "type", &field)) return 24; + rcl_domain_value_free(&field); + if (!rcl_domain_value_validate(&quantity)) return 25; + + RclDomainValueV1 invalid; + rcl_domain_value_init(&invalid); + if (!rcl_domain_value_set_truth(&invalid, 1, "Truth")) return 26; + invalid.as.truth = 2; + if (rcl_domain_organ_invoke(®istry, "core", "echo", RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, &invalid, 1, &result, &error)) return 27; + if (strcmp(error.code, "RCL_DOMAIN_ORGAN_VALUE_INVALID") != 0) return 28; + invalid.as.truth = 1; + rcl_domain_value_free(&invalid); + + RclDomainValueV1 uninitialized; + memset(&uninitialized, 0, sizeof(uninitialized)); + if (rcl_domain_organ_invoke(®istry, "core", "echo", RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, &text, 1, &uninitialized, &error)) return 29; + if (strcmp(error.code, "RCL_DOMAIN_ORGAN_OUTPUT_INIT") != 0) return 30; + + rcl_domain_value_free(&quantity); + rcl_domain_value_free(&text); + rcl_domain_organ_registry_free(®istry); + puts("domain-organ-value-smoke: PASS"); + return 0; +} diff --git a/native/domain_vm_opcode45_candidate_host.c b/native/domain_vm_opcode45_candidate_host.c new file mode 100644 index 00000000..f722a8c1 --- /dev/null +++ b/native/domain_vm_opcode45_candidate_host.c @@ -0,0 +1,67 @@ +#define RCLVM_EMBEDDED_ONLY +#include "rclvm-rbc13-domain-candidate.c" +#include "rcl_domain_vm_candidate.h" +#include "rcl_domain_admitted_organs.h" + +static int register_admitted_organs(RclVmInstance *instance, char *error, size_t error_capacity) { + RclDomainOrganRegistry admitted; + rcl_domain_organ_registry_init(&admitted); + if (!rcl_domain_register_admitted_candidates_v01(&admitted, error, error_capacity)) { + rcl_domain_organ_registry_free(&admitted); + return 0; + } + int ok = 1; + for (size_t i = 0; i < admitted.count; i++) { + if (!rclvm_instance_register_domain_organ(instance, &admitted.entries[i], error, error_capacity)) { + ok = 0; + break; + } + } + rcl_domain_organ_registry_free(&admitted); + return ok; +} + +int main(int argc, char **argv) { + if (argc < 2 || argc > 3) { + fprintf(stderr, "usage: domain-vm-opcode45-candidate [--candidate-minimum]\n"); + return 64; + } + + RclVmInstance *instance = rclvm_instance_create(); + if (!instance) return 70; + char error[1024]; error[0] = '\0'; + if (!register_admitted_organs(instance, error, sizeof(error))) { + fprintf(stderr, "%s\n", error); + rclvm_instance_destroy(instance); + return 71; + } + if (rclvm_instance_domain_organ_count(instance) != 4) { + fprintf(stderr, "candidate host expected exactly four admitted organs\n"); + rclvm_instance_destroy(instance); + return 72; + } + if (argc == 3 && strcmp(argv[2], "--candidate-minimum") == 0) { + if (!rclvm_instance_set_domain_minimum_tier(instance, RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, error, sizeof(error))) { + fprintf(stderr, "%s\n", error); + rclvm_instance_destroy(instance); + return 73; + } + } + + if (!rclvm_instance_load_file(instance, argv[1], error, sizeof(error))) { + fprintf(stderr, "%s\n", error); + rclvm_instance_destroy(instance); + return 74; + } + + char *result_json = NULL; + int ok = rclvm_instance_run(instance, 1, &result_json, error, sizeof(error)); + if (result_json) { + fputs(result_json, stdout); + rclvm_free_string(result_json); + } else if (error[0]) { + fprintf(stderr, "%s\n", error); + } + rclvm_instance_destroy(instance); + return ok ? 0 : 2; +} diff --git a/native/domain_vm_value_bridge_smoke.c b/native/domain_vm_value_bridge_smoke.c new file mode 100644 index 00000000..b4f2fb69 --- /dev/null +++ b/native/domain_vm_value_bridge_smoke.c @@ -0,0 +1,72 @@ +#define RCLVM_EMBEDDED_ONLY +#include "rclvm.c" +#include "rcl_domain_vm_value_bridge.inc" + +static int set_domain_field(RclDomainValueV1 *record, size_t index, const char *name, RclDomainValueV1 *value) { + int ok = rcl_domain_value_record_set(record, index, name, value); + rcl_domain_value_free(value); + return ok; +} + +int main(void) { + VM *vm = (VM *)calloc(1, sizeof(VM)); + if (!vm) return 5; + vm->next_typed_object_id = 1; + + Value native_text = value_string("hello"); + RclDomainValueV1 domain_text, roundtrip_domain; + rcl_domain_value_init(&domain_text); + rcl_domain_value_init(&roundtrip_domain); + if (!rcl_domain_bridge_native_to_domain(vm, &native_text, &domain_text)) return 10; + Value roundtrip_text = rcl_domain_bridge_domain_to_native(vm, &domain_text); + if (vm->error.code || !values_equal(&native_text, &roundtrip_text)) return 11; + value_free(&native_text); + value_free(&roundtrip_text); + rcl_domain_value_free(&domain_text); + + RclDomainValueV1 quantity, field; + rcl_domain_value_init(&quantity); + rcl_domain_value_init(&field); + if (!rcl_domain_value_make_record(&quantity, "Quantity", 4, "Temperature")) return 12; + if (!rcl_domain_value_set_text(&field, "Quantity", "Text") || !set_domain_field(&quantity, 0, "kind", &field)) return 13; + if (!rcl_domain_value_set_text(&field, "Temperature", "Text") || !set_domain_field(&quantity, 1, "type", &field)) return 14; + if (!rcl_domain_value_set_number(&field, 25, "Number") || !set_domain_field(&quantity, 2, "value", &field)) return 15; + if (!rcl_domain_value_set_text(&field, "°C", "Text") || !set_domain_field(&quantity, 3, "unit", &field)) return 16; + + Value native_quantity = rcl_domain_bridge_domain_to_native(vm, &quantity); + if (vm->error.code || native_quantity.type != VALUE_TYPED_RECORD) return 17; + if (strcmp(native_quantity.typed_record->type_name, "Temperature") != 0) return 18; + if (!rcl_domain_bridge_native_to_domain(vm, &native_quantity, &roundtrip_domain)) return 19; + if (!rcl_domain_value_equal(&quantity, &roundtrip_domain)) return 20; + rcl_domain_value_free(&roundtrip_domain); + value_free(&native_quantity); + rcl_domain_value_free(&quantity); + + RclDomainValueV1 sequence, item; + rcl_domain_value_init(&sequence); + rcl_domain_value_init(&item); + if (!rcl_domain_value_make_sequence(&sequence, 2, "Sequence")) return 21; + if (!rcl_domain_value_set_text(&item, "a", "Text") || !rcl_domain_value_sequence_set(&sequence, 0, &item)) return 22; + if (!rcl_domain_value_set_number(&item, 7, "Number") || !rcl_domain_value_sequence_set(&sequence, 1, &item)) return 23; + rcl_domain_value_free(&item); + Value native_sequence = rcl_domain_bridge_domain_to_native(vm, &sequence); + if (vm->error.code || native_sequence.type != VALUE_SEQUENCE) return 24; + if (!rcl_domain_bridge_native_to_domain(vm, &native_sequence, &roundtrip_domain)) return 25; + if (!rcl_domain_value_equal(&sequence, &roundtrip_domain)) return 26; + value_free(&native_sequence); + rcl_domain_value_free(&roundtrip_domain); + rcl_domain_value_free(&sequence); + + memset(&vm->error, 0, sizeof(vm->error)); + Value unsupported = value_span(0, 1, 1, 0); + rcl_domain_value_init(&roundtrip_domain); + if (rcl_domain_bridge_native_to_domain(vm, &unsupported, &roundtrip_domain)) return 27; + if (!vm->error.code || strcmp(vm->error.code, "RCL_NATIVE_DOMAIN_VALUE_UNSUPPORTED") != 0) return 28; + value_free(&unsupported); + rcl_domain_value_free(&roundtrip_domain); + + typed_heap_clear(vm); + free(vm); + puts("domain-vm-value-bridge-smoke: PASS"); + return 0; +} diff --git a/native/rbc13_execution_benchmark.c b/native/rbc13_execution_benchmark.c new file mode 100644 index 00000000..e8cd3fd5 --- /dev/null +++ b/native/rbc13_execution_benchmark.c @@ -0,0 +1,205 @@ +#include "rcl_domain_admitted_organs.h" + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif + +typedef struct { + uint64_t elapsed_ns; + uint64_t allocation_count; + uint64_t allocation_bytes; + uint64_t organ_clone_calls; +} PathRun; + +static uint64_t timer_ns(void) { +#ifdef _WIN32 + static LARGE_INTEGER frequency; + static int initialized = 0; + LARGE_INTEGER counter; + if (!initialized) { QueryPerformanceFrequency(&frequency); initialized = 1; } + QueryPerformanceCounter(&counter); + return (uint64_t)((counter.QuadPart * UINT64_C(1000000000)) / frequency.QuadPart); +#else + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return (uint64_t)now.tv_sec * UINT64_C(1000000000) + (uint64_t)now.tv_nsec; +#endif +} + +static uint64_t rss_bytes(void) { +#ifdef _WIN32 + PROCESS_MEMORY_COUNTERS counters; + memset(&counters, 0, sizeof(counters)); + if (GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters))) return (uint64_t)counters.WorkingSetSize; + return 0; +#else + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) != 0) return 0; + return (uint64_t)usage.ru_maxrss * UINT64_C(1024); +#endif +} + +static double primitive_opcode(double input) { + return input; +} + +static int provider_json_echo(double input, char **response, uint64_t *allocation_count, uint64_t *allocation_bytes) { + char request[128]; + int request_length = snprintf(request, sizeof(request), "{\"operation\":\"core.echo\",\"value\":%.17g}", input); + if (request_length < 0 || (size_t)request_length >= sizeof(request)) return 0; + size_t length = (size_t)request_length; + char *copy = (char *)malloc(length + 1); + if (!copy) return 0; + memcpy(copy, request, length + 1); + *response = copy; + *allocation_count += 1; + *allocation_bytes += (uint64_t)(length + 1); + return 1; +} + +static int run_primitive(double input, uint64_t iterations, volatile double *sink, PathRun *run) { + uint64_t started = timer_ns(); + for (uint64_t index = 0; index < iterations; index++) *sink = primitive_opcode(input); + run->elapsed_ns = timer_ns() - started; + return 1; +} + +static int run_native_organ( + RclDomainOrganRegistry *registry, + double input, + uint64_t iterations, + volatile double *sink, + PathRun *run +) { + RclDomainValueV1 argument; + rcl_domain_value_init(&argument); + if (!rcl_domain_value_set_number(&argument, input, "Number")) return 0; + uint64_t started = timer_ns(); + for (uint64_t index = 0; index < iterations; index++) { + RclDomainValueV1 result; + RclDomainOrganErrorV1 error; + rcl_domain_value_init(&result); + rcl_domain_organ_error_clear(&error); + if (!rcl_domain_organ_invoke( + registry, "core", "echo", RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, + &argument, 1, &result, &error + )) { + rcl_domain_value_free(&argument); + return 0; + } + if (result.kind == RCL_DOMAIN_VALUE_NUMBER) *sink = result.as.number; + rcl_domain_value_free(&result); + run->organ_clone_calls += 1; + } + run->elapsed_ns = timer_ns() - started; + rcl_domain_value_free(&argument); + return 1; +} + +static int run_provider(double input, uint64_t iterations, volatile double *sink, PathRun *run) { + uint64_t started = timer_ns(); + for (uint64_t index = 0; index < iterations; index++) { + char *response = NULL; + if (!provider_json_echo(input, &response, &run->allocation_count, &run->allocation_bytes)) return 0; + if (response) *sink = input; + free(response); + } + run->elapsed_ns = timer_ns() - started; + return 1; +} + +static int run_path( + const char *path, + RclDomainOrganRegistry *registry, + double input, + uint64_t iterations, + volatile double *sink, + PathRun *run +) { + memset(run, 0, sizeof(*run)); + if (strcmp(path, "primitive") == 0) return run_primitive(input, iterations, sink, run); + if (strcmp(path, "native-organ") == 0) return run_native_organ(registry, input, iterations, sink, run); + if (strcmp(path, "provider") == 0) return run_provider(input, iterations, sink, run); + return 0; +} + +static int parse_u64(const char *text, uint64_t *value) { + char *end = NULL; + unsigned long long parsed = strtoull(text, &end, 10); + if (!text[0] || !end || *end != '\0') return 0; + *value = (uint64_t)parsed; + return 1; +} + +static void print_run(const PathRun *run) { + printf( + "{\"elapsedNs\":%" PRIu64 ",\"allocationCount\":%" PRIu64 ",\"allocationBytes\":%" PRIu64 ",\"organCloneCalls\":%" PRIu64 "}", + run->elapsed_ns, run->allocation_count, run->allocation_bytes, run->organ_clone_calls + ); +} + +int main(int argc, char **argv) { + uint64_t iterations = 10000; + uint64_t warmup = 1000; + uint64_t repetitions = 7; + for (int index = 1; index + 1 < argc; index += 2) { + uint64_t parsed = 0; + if (strcmp(argv[index], "--iterations") == 0 && parse_u64(argv[index + 1], &parsed)) iterations = parsed; + else if (strcmp(argv[index], "--warmup") == 0 && parse_u64(argv[index + 1], &parsed)) warmup = parsed; + else if (strcmp(argv[index], "--repetitions") == 0 && parse_u64(argv[index + 1], &parsed)) repetitions = parsed; + else { fprintf(stderr, "invalid benchmark argument\n"); return 64; } + } + if (iterations == 0 || repetitions == 0) { fprintf(stderr, "iterations and repetitions must be positive\n"); return 64; } + + const double input = 9007199254740991.0; + const char *paths[] = { "primitive", "native-organ", "provider" }; + RclDomainOrganRegistry registry; + char registration_error[512]; + rcl_domain_organ_registry_init(®istry); + if (!rcl_domain_register_admitted_candidates_v01(®istry, registration_error, sizeof(registration_error))) { + fprintf(stderr, "%s\n", registration_error); + return 70; + } + volatile double sink = 0; + PathRun warmup_run; + for (size_t path_index = 0; path_index < 3; path_index++) { + if (!run_path(paths[path_index], ®istry, input, warmup, &sink, &warmup_run)) { + fprintf(stderr, "warmup failed for %s\n", paths[path_index]); + rcl_domain_organ_registry_free(®istry); + return 71; + } + } + + uint64_t rss_before = rss_bytes(); + printf("{\"format\":\"rcl.rbc13-execution-benchmark-sample.v0.1\",\"iterations\":%" PRIu64 ",\"warmup\":%" PRIu64 ",\"repetitions\":%" PRIu64 ",\"input\":%.17g,\"rssBeforeBytes\":%" PRIu64 ",\"paths\":{", iterations, warmup, repetitions, input, rss_before); + for (size_t path_index = 0; path_index < 3; path_index++) { + if (path_index) putchar(','); + printf("\"%s\":{\"runs\":[", paths[path_index]); + for (uint64_t repetition = 0; repetition < repetitions; repetition++) { + if (repetition) putchar(','); + PathRun run; + if (!run_path(paths[path_index], ®istry, input, iterations, &sink, &run)) { + fprintf(stderr, "measured run failed for %s\n", paths[path_index]); + rcl_domain_organ_registry_free(®istry); + return 72; + } + print_run(&run); + } + printf("]}"); + } + uint64_t rss_after = rss_bytes(); + printf("},\"rssAfterBytes\":%" PRIu64 ",\"sink\":%.17g}\n", rss_after, sink); + rcl_domain_organ_registry_free(®istry); + return 0; +} diff --git a/native/rbc13_graph_traversal_organ.c b/native/rbc13_graph_traversal_organ.c new file mode 100644 index 00000000..3eac17b4 --- /dev/null +++ b/native/rbc13_graph_traversal_organ.c @@ -0,0 +1,198 @@ +#include "rcl_domain_organ.h" + +#include +#include +#include +#include + +static const RclDomainValueV1 *field(const RclDomainValueV1 *record, const char *name) { + if (!record || record->kind != RCL_DOMAIN_VALUE_RECORD || !name) return NULL; + for (size_t index = 0; index < record->as.record.count; index++) { + const RclDomainFieldV1 *entry = &record->as.record.fields[index]; + if (entry->name && entry->value && strcmp(entry->name, name) == 0) return entry->value; + } + return NULL; +} + +static int integer_field(const RclDomainValueV1 *record, const char *name, int *output) { + const RclDomainValueV1 *value = field(record, name); + if (!value || value->kind != RCL_DOMAIN_VALUE_NUMBER || !isfinite(value->as.number)) return 0; + double integer = value->as.number; + if (integer < 0 || integer > 2147483647 || floor(integer) != integer) return 0; + *output = (int)integer; + return 1; +} + +static int set_field(RclDomainValueV1 *record, size_t index, const char *name, RclDomainValueV1 *value) { + int ok = rcl_domain_value_record_set(record, index, name, value); + rcl_domain_value_free(value); + return ok; +} + +static int graph_traversal( + void *userdata, + const char *domain, + const char *operation, + const RclDomainValueV1 *args, + size_t argc, + RclDomainValueV1 *result, + RclDomainOrganErrorV1 *error +) { + (void)userdata; + (void)domain; + (void)operation; + if (argc != 1 || !args || args[0].kind != RCL_DOMAIN_VALUE_RECORD) { + rcl_domain_organ_error_set(error, "RCL_GRAPH_MALFORMED", "RCL_GRAPH_MALFORMED: graph input must be one record"); + return 0; + } + + const RclDomainValueV1 *input = &args[0]; + const RclDomainValueV1 *edges = field(input, "edges"); + int node_count = 0; + int start = 0; + int target = 0; + int budget = 0; + if (!integer_field(input, "nodeCount", &node_count) + || !integer_field(input, "start", &start) + || !integer_field(input, "target", &target) + || !integer_field(input, "budget", &budget) + || !edges || edges->kind != RCL_DOMAIN_VALUE_SEQUENCE + || node_count > 32 || budget > 65536) { + rcl_domain_organ_error_set(error, "RCL_GRAPH_MALFORMED", "RCL_GRAPH_MALFORMED: graph scalar or matrix shape is invalid"); + return 0; + } + if (node_count == 0) { + rcl_domain_organ_error_set(error, "RCL_GRAPH_EMPTY", "RCL_GRAPH_EMPTY: graph has no nodes"); + return 0; + } + if (start >= node_count || target >= node_count) { + rcl_domain_organ_error_set(error, "RCL_GRAPH_INVALID_NODE", "RCL_GRAPH_INVALID_NODE: start or target is outside the graph"); + return 0; + } + if (edges->as.sequence.count != (size_t)node_count) { + rcl_domain_organ_error_set(error, "RCL_GRAPH_MALFORMED", "RCL_GRAPH_MALFORMED: adjacency matrix row count is invalid"); + return 0; + } + for (int row = 0; row < node_count; row++) { + const RclDomainValueV1 *line = &edges->as.sequence.items[row]; + if (line->kind != RCL_DOMAIN_VALUE_SEQUENCE || line->as.sequence.count != (size_t)node_count) { + rcl_domain_organ_error_set(error, "RCL_GRAPH_MALFORMED", "RCL_GRAPH_MALFORMED: adjacency matrix column count is invalid"); + return 0; + } + for (int column = 0; column < node_count; column++) { + const RclDomainValueV1 *entry = &line->as.sequence.items[column]; + if (entry->kind != RCL_DOMAIN_VALUE_NUMBER || !isfinite(entry->as.number) + || (entry->as.number != 0 && entry->as.number != 1)) { + rcl_domain_organ_error_set(error, "RCL_GRAPH_MALFORMED", "RCL_GRAPH_MALFORMED: adjacency matrix values must be 0 or 1"); + return 0; + } + } + } + + int *visited = (int *)calloc((size_t)node_count, sizeof(int)); + int *queue = (int *)calloc((size_t)node_count, sizeof(int)); + int *order = (int *)calloc((size_t)node_count, sizeof(int)); + if (!visited || !queue || !order) { + free(visited); free(queue); free(order); + rcl_domain_organ_error_set(error, "RCL_GRAPH_OOM", "RCL_GRAPH_OOM: traversal buffers could not be allocated"); + return 0; + } + + int head = 0; + int tail = 1; + int steps = 0; + int reachable = 0; + int termination = 3; + int visited_count = 1; + visited[start] = 1; + queue[0] = start; + order[0] = start; + while (head < tail) { + int current = queue[head]; + if (current == target) { + reachable = 1; + termination = 1; + break; + } + if (steps >= budget) { + termination = 2; + break; + } + steps += 1; + for (int neighbor = 0; neighbor < node_count; neighbor++) { + const RclDomainValueV1 *line = &edges->as.sequence.items[current]; + const RclDomainValueV1 *entry = &line->as.sequence.items[neighbor]; + if (entry->as.number == 1 && !visited[neighbor]) { + visited[neighbor] = 1; + queue[tail++] = neighbor; + order[visited_count++] = neighbor; + } + } + head += 1; + } + + int ok = rcl_domain_value_make_record(result, "GraphTraversalResult", 8, "GraphTraversal"); + RclDomainValueV1 value; + rcl_domain_value_init(&value); + if (ok && !rcl_domain_value_set_truth(&value, reachable, "Truth")) ok = 0; + if (ok && !set_field(result, 0, "reachable", &value)) ok = 0; + rcl_domain_value_init(&value); + if (ok && !rcl_domain_value_make_sequence(&value, (size_t)visited_count, "Sequence")) ok = 0; + if (ok) for (int index = 0; index < visited_count; index++) { + RclDomainValueV1 item; + rcl_domain_value_init(&item); + if (!rcl_domain_value_set_number(&item, order[index], "Number") + || !rcl_domain_value_sequence_set(&value, (size_t)index, &item)) ok = 0; + rcl_domain_value_free(&item); + } + if (ok && !set_field(result, 1, "visitedOrder", &value)) ok = 0; + rcl_domain_value_init(&value); + if (ok && !rcl_domain_value_make_sequence(&value, (size_t)visited_count, "Sequence")) ok = 0; + if (ok) for (int index = 0; index < visited_count; index++) { + RclDomainValueV1 item; + rcl_domain_value_init(&item); + if (!rcl_domain_value_set_number(&item, order[index], "Number") + || !rcl_domain_value_sequence_set(&value, (size_t)index, &item)) ok = 0; + rcl_domain_value_free(&item); + } + if (ok && !set_field(result, 2, "visitedSet", &value)) ok = 0; + rcl_domain_value_init(&value); + if (ok && !rcl_domain_value_set_number(&value, steps, "Number")) ok = 0; + if (ok && !set_field(result, 3, "steps", &value)) ok = 0; + rcl_domain_value_init(&value); + if (ok && !rcl_domain_value_set_number(&value, start, "Number")) ok = 0; + if (ok && !set_field(result, 4, "start", &value)) ok = 0; + rcl_domain_value_init(&value); + if (ok && !rcl_domain_value_set_number(&value, target, "Number")) ok = 0; + if (ok && !set_field(result, 5, "target", &value)) ok = 0; + rcl_domain_value_init(&value); + if (ok && !rcl_domain_value_set_number(&value, budget, "Number")) ok = 0; + if (ok && !set_field(result, 6, "budget", &value)) ok = 0; + rcl_domain_value_init(&value); + if (ok && !rcl_domain_value_set_text(&value, termination == 1 ? "target-found" : termination == 2 ? "budget-exhausted" : "exhausted", "Text")) ok = 0; + if (ok && !set_field(result, 7, "termination", &value)) ok = 0; + rcl_domain_value_free(&value); + free(visited); free(queue); free(order); + if (!ok) { + rcl_domain_value_free(result); + rcl_domain_organ_error_set(error, "RCL_GRAPH_RESULT_BUILD", "RCL_GRAPH_RESULT_BUILD: result record could not be built"); + return 0; + } + return 1; +} + +int rbc13_register_graph_traversal_organ(RclDomainOrganRegistry *registry, char *error, size_t error_capacity) { + RclDomainOrganV1 organ = { + RCL_DOMAIN_ORGAN_ABI_V1, + "wasm-vm", + "graph-traversal", + "graph-traversal::bounded-reachability", + "rbc13-native-c-graph-body", + "experimental:rbc13-native-c-graph-body", + RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, + 1, + graph_traversal, + NULL, + }; + return rcl_domain_organ_register(registry, &organ, error, error_capacity); +} diff --git a/native/rbc13_graph_traversal_organ_host.c b/native/rbc13_graph_traversal_organ_host.c new file mode 100644 index 00000000..e3aa28e0 --- /dev/null +++ b/native/rbc13_graph_traversal_organ_host.c @@ -0,0 +1,159 @@ +#include "rcl_domain_organ.h" + +#include +#include +#include + +int rbc13_register_graph_traversal_organ(RclDomainOrganRegistry *registry, char *error, size_t error_capacity); + +typedef struct { + const char *id; + int node_count; + const int *matrix; + int start; + int target; + int budget; +} GraphCase; + +static const int POSITIVE_MATRIX[] = { 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0 }; +static const int CYCLE_MATRIX[] = { 0,1,0, 1,0,0, 0,0,0 }; +static const int DISCONNECTED_MATRIX[] = { 0,1,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0 }; +static const int BUDGET_MATRIX[] = { 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0 }; +static const int INVALID_MATRIX[] = { 0,1, 0,0 }; +static const int MALFORMED_MATRIX[] = { 0,2, 0,0 }; + +static int select_case(const char *id, GraphCase *output) { + if (!id || !output) return 0; + if (strcmp(id, "positive-chain") == 0) *output = (GraphCase){ id, 4, POSITIVE_MATRIX, 0, 3, 8 }; + else if (strcmp(id, "cycle") == 0) *output = (GraphCase){ id, 3, CYCLE_MATRIX, 0, 2, 4 }; + else if (strcmp(id, "disconnected") == 0) *output = (GraphCase){ id, 4, DISCONNECTED_MATRIX, 0, 3, 8 }; + else if (strcmp(id, "empty") == 0) *output = (GraphCase){ id, 0, NULL, 0, 0, 0 }; + else if (strcmp(id, "budget-exhaustion") == 0) *output = (GraphCase){ id, 4, BUDGET_MATRIX, 0, 3, 1 }; + else if (strcmp(id, "invalid-node") == 0) *output = (GraphCase){ id, 2, INVALID_MATRIX, 2, 1, 3 }; + else if (strcmp(id, "malformed-graph") == 0) *output = (GraphCase){ id, 2, MALFORMED_MATRIX, 0, 1, 3 }; + else return 0; + return 1; +} + +static int set_field(RclDomainValueV1 *record, size_t index, const char *name, RclDomainValueV1 *value) { + int ok = rcl_domain_value_record_set(record, index, name, value); + rcl_domain_value_free(value); + return ok; +} + +static int make_input(const GraphCase *graph, RclDomainValueV1 *input) { + RclDomainValueV1 value; + rcl_domain_value_init(&value); + if (!rcl_domain_value_make_record(input, "GraphTraversalInput", 5, "GraphTraversal")) return 0; + if (!rcl_domain_value_set_number(&value, graph->node_count, "Number") || !set_field(input, 0, "nodeCount", &value)) return 0; + rcl_domain_value_init(&value); + if (!rcl_domain_value_make_sequence(&value, (size_t)graph->node_count, "Sequence")) return 0; + for (int row = 0; row < graph->node_count; row++) { + RclDomainValueV1 line; + rcl_domain_value_init(&line); + if (!rcl_domain_value_make_sequence(&line, (size_t)graph->node_count, "Sequence")) return 0; + for (int column = 0; column < graph->node_count; column++) { + RclDomainValueV1 entry; + rcl_domain_value_init(&entry); + if (!rcl_domain_value_set_number(&entry, graph->matrix[row * graph->node_count + column], "Number") + || !rcl_domain_value_sequence_set(&line, (size_t)column, &entry)) return 0; + rcl_domain_value_free(&entry); + } + if (!rcl_domain_value_sequence_set(&value, (size_t)row, &line)) return 0; + rcl_domain_value_free(&line); + } + if (!set_field(input, 1, "edges", &value)) return 0; + rcl_domain_value_init(&value); + if (!rcl_domain_value_set_number(&value, graph->start, "Number") || !set_field(input, 2, "start", &value)) return 0; + rcl_domain_value_init(&value); + if (!rcl_domain_value_set_number(&value, graph->target, "Number") || !set_field(input, 3, "target", &value)) return 0; + rcl_domain_value_init(&value); + if (!rcl_domain_value_set_number(&value, graph->budget, "Number") || !set_field(input, 4, "budget", &value)) return 0; + return 1; +} + +static void json_string(const char *value) { + putchar('"'); + for (const unsigned char *cursor = (const unsigned char *)(value ? value : ""); *cursor; cursor++) { + if (*cursor == '\\') fputs("\\\\", stdout); + else if (*cursor == '"') fputs("\\\"", stdout); + else if (*cursor == '\n') fputs("\\n", stdout); + else if (*cursor == '\r') fputs("\\r", stdout); + else if (*cursor == '\t') fputs("\\t", stdout); + else if (*cursor < 0x20) printf("\\u%04x", *cursor); + else putchar((int)*cursor); + } + putchar('"'); +} + +static void print_value(const RclDomainValueV1 *value) { + if (!value) { fputs("null", stdout); return; } + switch (value->kind) { + case RCL_DOMAIN_VALUE_NULL: fputs("null", stdout); break; + case RCL_DOMAIN_VALUE_NUMBER: printf("%.17g", value->as.number); break; + case RCL_DOMAIN_VALUE_TRUTH: fputs(value->as.truth ? "true" : "false", stdout); break; + case RCL_DOMAIN_VALUE_TEXT: json_string(value->as.text.data); break; + case RCL_DOMAIN_VALUE_SEQUENCE: + putchar('['); + for (size_t index = 0; index < value->as.sequence.count; index++) { + if (index) putchar(','); + print_value(&value->as.sequence.items[index]); + } + putchar(']'); + break; + case RCL_DOMAIN_VALUE_RECORD: + putchar('{'); + for (size_t index = 0; index < value->as.record.count; index++) { + if (index) putchar(','); + json_string(value->as.record.fields[index].name); + putchar(':'); + print_value(value->as.record.fields[index].value); + } + putchar('}'); + break; + default: fputs("null", stdout); break; + } +} + +static void print_error_details(const GraphCase *graph, const char *code) { + if (strcmp(code, "RCL_GRAPH_EMPTY") == 0) printf("{\"nodeCount\":%d}", graph->node_count); + else if (strcmp(code, "RCL_GRAPH_INVALID_NODE") == 0) printf("{\"nodeCount\":%d,\"start\":%d,\"target\":%d}", graph->node_count, graph->start, graph->target); + else if (strcmp(code, "RCL_GRAPH_MALFORMED") == 0) fputs("{\"reason\":\"matrix-value\"}", stdout); + else fputs("{}", stdout); +} + +int main(int argc, char **argv) { + if (argc != 2) return 64; + GraphCase graph; + if (!select_case(argv[1], &graph)) return 65; + RclDomainValueV1 input, result; + rcl_domain_value_init(&input); + rcl_domain_value_init(&result); + if (!make_input(&graph, &input)) return 66; + + RclDomainOrganRegistry registry; + rcl_domain_organ_registry_init(®istry); + char registration_error[512] = {0}; + if (!rbc13_register_graph_traversal_organ(®istry, registration_error, sizeof(registration_error))) return 67; + RclDomainOrganErrorV1 error; + rcl_domain_organ_error_clear(&error); + int ok = rcl_domain_organ_invoke(®istry, "wasm-vm", "graph-traversal", RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, &input, 1, &result, &error); + if (ok) { + fputs("{\"status\":\"ok\",\"result\":", stdout); + print_value(&result); + fputs(",\"evidenceTier\":\"native-candidate\"}\n", stdout); + } else { + fputs("{\"status\":\"error\",\"error\":{\"class\":\"RCL_GRAPH_INPUT_ERROR\",\"code\":", stdout); + json_string(error.code); + fputs(",\"message\":", stdout); + json_string(error.message); + fputs(",\"details\":", stdout); + print_error_details(&graph, error.code); + fputs("},\"evidenceTier\":\"native-candidate\"}\n", stdout); + } + rcl_domain_value_free(&result); + rcl_domain_value_free(&input); + rcl_domain_organ_registry_free(®istry); + return ok ? 0 : 0; +} + diff --git a/native/rcl_canonical_number_v2.c b/native/rcl_canonical_number_v2.c new file mode 100644 index 00000000..692eaf65 --- /dev/null +++ b/native/rcl_canonical_number_v2.c @@ -0,0 +1,31 @@ +#include "rcl_canonical_number_v2.h" + +#include +#include + +static const uint64_t EXPONENT_MASK = UINT64_C(0x7ff0000000000000); +static const uint64_t FRACTION_MASK = UINT64_C(0x000fffffffffffff); +static const uint64_t SIGN_MASK = UINT64_C(0x8000000000000000); +static const uint64_t ZERO_MASK = UINT64_C(0x7fffffffffffffff); +static const char HEX[] = "0123456789abcdef"; + +int rcl_canonical_number_v2_encode_bits(uint64_t raw_bits, char output[RCL_CANONICAL_NUMBER_V2_TOKEN_LENGTH + 1]) { + uint64_t bits = raw_bits; + if ((bits & EXPONENT_MASK) == EXPONENT_MASK) return 0; + if ((bits & ZERO_MASK) == 0) bits = 0; + output[0] = '0'; + output[1] = 'x'; + for (int index = 0; index < 16; index++) { + const int shift = (15 - index) * 4; + output[2 + index] = HEX[(bits >> shift) & UINT64_C(0xf)]; + } + output[RCL_CANONICAL_NUMBER_V2_TOKEN_LENGTH] = '\0'; + return 1; +} + +int rcl_canonical_number_v2_encode(double value, char output[RCL_CANONICAL_NUMBER_V2_TOKEN_LENGTH + 1]) { + uint64_t raw_bits = 0; + if (!isfinite(value)) return 0; + memcpy(&raw_bits, &value, sizeof(raw_bits)); + return rcl_canonical_number_v2_encode_bits(raw_bits, output); +} diff --git a/native/rcl_canonical_number_v2.h b/native/rcl_canonical_number_v2.h new file mode 100644 index 00000000..48a8ba41 --- /dev/null +++ b/native/rcl_canonical_number_v2.h @@ -0,0 +1,12 @@ +#ifndef RCL_CANONICAL_NUMBER_V2_H +#define RCL_CANONICAL_NUMBER_V2_H + +#include + +#define RCL_CANONICAL_NUMBER_ENCODING_V2 "rcl.canonical-number.v2" +#define RCL_CANONICAL_NUMBER_V2_TOKEN_LENGTH 18 + +int rcl_canonical_number_v2_encode(double value, char output[RCL_CANONICAL_NUMBER_V2_TOKEN_LENGTH + 1]); +int rcl_canonical_number_v2_encode_bits(uint64_t raw_bits, char output[RCL_CANONICAL_NUMBER_V2_TOKEN_LENGTH + 1]); + +#endif diff --git a/native/rcl_canonical_number_v2_host.c b/native/rcl_canonical_number_v2_host.c new file mode 100644 index 00000000..dfb20b82 --- /dev/null +++ b/native/rcl_canonical_number_v2_host.c @@ -0,0 +1,44 @@ +#include "rcl_canonical_number_v2.h" + +#include +#include +#include + +static int hex_value(int value) { + if (value >= '0' && value <= '9') return value - '0'; + if (value >= 'a' && value <= 'f') return value - 'a' + 10; + if (value >= 'A' && value <= 'F') return value - 'A' + 10; + return -1; +} + +static int parse_bits(const char *text, uint64_t *bits) { + const char *cursor = text; + if (cursor[0] == '0' && (cursor[1] == 'x' || cursor[1] == 'X')) cursor += 2; + int count = 0; + uint64_t result = 0; + for (; count < 16 && cursor[count]; count++) { + int nibble = hex_value((unsigned char)cursor[count]); + if (nibble < 0) return 0; + result = (result << 4) | (uint64_t)nibble; + } + if (count != 16) return 0; + if (cursor[count] && !isspace((unsigned char)cursor[count])) return 0; + *bits = result; + return 1; +} + +int main(void) { + char line[128]; + char output[RCL_CANONICAL_NUMBER_V2_TOKEN_LENGTH + 1]; + int invalid_seen = 0; + while (fgets(line, sizeof(line), stdin)) { + uint64_t bits = 0; + if (!parse_bits(line, &bits) || !rcl_canonical_number_v2_encode_bits(bits, output)) { + fputs("ERROR\n", stdout); + invalid_seen = 1; + continue; + } + puts(output); + } + return invalid_seen ? 2 : 0; +} diff --git a/native/rcl_domain_admitted_organs.c b/native/rcl_domain_admitted_organs.c new file mode 100644 index 00000000..b970486e --- /dev/null +++ b/native/rcl_domain_admitted_organs.c @@ -0,0 +1,330 @@ +#include "rcl_domain_admitted_organs.h" + +#include +#include +#include + +static int fail(RclDomainOrganErrorV1 *error, const char *code, const char *message) { + rcl_domain_organ_error_set(error, code, message); + return 0; +} + +static int set_field(RclDomainValueV1 *record, size_t index, const char *name, RclDomainValueV1 *value) { + int ok = rcl_domain_value_record_set(record, index, name, value); + rcl_domain_value_free(value); + return ok; +} + +static int set_field_clone(RclDomainValueV1 *record, size_t index, const char *name, const RclDomainValueV1 *source) { + RclDomainValueV1 copy; + rcl_domain_value_init(©); + if (!rcl_domain_value_clone(©, source)) return 0; + return set_field(record, index, name, ©); +} + +static const RclDomainValueV1 *record_field(const RclDomainValueV1 *record, const char *name) { + if (!record || record->kind != RCL_DOMAIN_VALUE_RECORD) return NULL; + for (size_t i = 0; i < record->as.record.count; i++) { + const RclDomainFieldV1 *field = &record->as.record.fields[i]; + if (field->name && field->value && strcmp(field->name, name) == 0) return field->value; + } + return NULL; +} + +static const char *domain_runtime_type(const RclDomainValueV1 *value) { + if (!value) return NULL; + if (value->semantic_type && value->semantic_type[0]) return value->semantic_type; + switch (value->kind) { + case RCL_DOMAIN_VALUE_NULL: return "Null"; + case RCL_DOMAIN_VALUE_NUMBER: return "Number"; + case RCL_DOMAIN_VALUE_TRUTH: return "Truth"; + case RCL_DOMAIN_VALUE_TEXT: return "Text"; + case RCL_DOMAIN_VALUE_SEQUENCE: return "Sequence"; + case RCL_DOMAIN_VALUE_RECORD: return value->as.record.type_name; + default: return NULL; + } +} + +static char *generic_type(const char *family, const char *base_type) { + size_t family_length = strlen(family); + size_t base_length = strlen(base_type); + if (family_length + base_length + 3 > RCL_DOMAIN_VALUE_MAX_TEXT_BYTES) return NULL; + char *result = (char *)malloc(family_length + base_length + 3); + if (!result) return NULL; + snprintf(result, family_length + base_length + 3, "%s<%s>", family, base_type); + return result; +} + +static int core_echo( + void *userdata, + const char *domain, + const char *operation, + const RclDomainValueV1 *args, + size_t argc, + RclDomainValueV1 *result, + RclDomainOrganErrorV1 *error +) { + (void)userdata; (void)domain; (void)operation; + if (argc != 1) return fail(error, "RCL_DOMAIN_CORE_ECHO_ARITY", "RCL_DOMAIN_CORE_ECHO_ARITY: core.echo expects one argument"); + if (!rcl_domain_value_clone(result, &args[0])) return fail(error, "RCL_DOMAIN_CORE_ECHO_CLONE", "RCL_DOMAIN_CORE_ECHO_CLONE: unable to clone core.echo argument"); + return 1; +} + +static const char *default_unit(const char *type) { + static const struct { const char *type; const char *unit; } units[] = { + { "Length", "m" }, { "Time", "s" }, { "Mass", "kg" }, { "Velocity", "m/s" }, + { "Acceleration", "m/s²" }, { "Force", "N" }, { "Energy", "J" }, + { "Temperature", "°C" }, { "Frequency", "Hz" }, { "Area", "m²" }, + { "Volume", "m³" }, { "Pressure", "Pa" }, { "Power", "W" }, { "Information", "bit" }, + }; + for (size_t i = 0; i < sizeof(units) / sizeof(units[0]); i++) if (strcmp(units[i].type, type) == 0) return units[i].unit; + return NULL; +} + +static int quantity_make( + void *userdata, + const char *domain, + const char *operation, + const RclDomainValueV1 *args, + size_t argc, + RclDomainValueV1 *result, + RclDomainOrganErrorV1 *error +) { + (void)userdata; (void)domain; (void)operation; + if (argc != 3 || args[0].kind != RCL_DOMAIN_VALUE_TEXT || args[1].kind != RCL_DOMAIN_VALUE_NUMBER || args[2].kind != RCL_DOMAIN_VALUE_TEXT) { + if (argc >= 1 && args[0].kind == RCL_DOMAIN_VALUE_TEXT && (argc < 2 || args[1].kind != RCL_DOMAIN_VALUE_NUMBER)) { + char message[RCL_DOMAIN_ERROR_MESSAGE_MAX]; + snprintf(message, sizeof(message), "Quantity '%s' must be finite", args[0].as.text.data); + return fail(error, "TypeError", message); + } + return fail(error, "RCL_DOMAIN_QUANTITY_ARGUMENT", "RCL_DOMAIN_QUANTITY_ARGUMENT: quantity.make expects Text type, Number value and Text unit"); + } + const char *type = args[0].as.text.data; + const char *fallback = default_unit(type); + if (!fallback) { + char message[RCL_DOMAIN_ERROR_MESSAGE_MAX]; + snprintf(message, sizeof(message), "Unknown quantity type '%s'", type ? type : ""); + return fail(error, "TypeError", message); + } + const char *unit = args[2].as.text.length ? args[2].as.text.data : fallback; + if (!rcl_domain_value_make_record(result, "Quantity", 4, type)) return fail(error, "RCL_DOMAIN_QUANTITY_OOM", "RCL_DOMAIN_QUANTITY_OOM: unable to construct quantity result"); + RclDomainValueV1 field; + rcl_domain_value_init(&field); + if (!rcl_domain_value_set_text(&field, "Quantity", "Text") || !set_field(result, 0, "kind", &field) + || !rcl_domain_value_set_text(&field, type, "Text") || !set_field(result, 1, "type", &field) + || !rcl_domain_value_set_number(&field, args[1].as.number, "Number") || !set_field(result, 2, "value", &field) + || !rcl_domain_value_set_text(&field, unit, "Text") || !set_field(result, 3, "unit", &field)) { + rcl_domain_value_free(&field); + rcl_domain_value_free(result); + return fail(error, "RCL_DOMAIN_QUANTITY_BUILD", "RCL_DOMAIN_QUANTITY_BUILD: unable to populate quantity result"); + } + return 1; +} + +static int quantitative_measure( + void *userdata, + const char *domain, + const char *operation, + const RclDomainValueV1 *args, + size_t argc, + RclDomainValueV1 *result, + RclDomainOrganErrorV1 *error +) { + (void)userdata; (void)domain; (void)operation; + if (argc != 8 || args[0].kind != RCL_DOMAIN_VALUE_TEXT || args[3].kind != RCL_DOMAIN_VALUE_NUMBER + || args[4].kind != RCL_DOMAIN_VALUE_TEXT || args[5].kind != RCL_DOMAIN_VALUE_TEXT + || args[6].kind != RCL_DOMAIN_VALUE_SEQUENCE || args[7].kind != RCL_DOMAIN_VALUE_TEXT) { + return fail(error, "RCL_DOMAIN_MEASUREMENT_ARGUMENT", "RCL_DOMAIN_MEASUREMENT_ARGUMENT: quantitative.measure expects baseType, value, uncertainty, confidence, unit, scale, evidence and calibratedBy"); + } + const char *base_type = args[0].as.text.data; + const char *value_type = domain_runtime_type(&args[1]); + const char *uncertainty_type = domain_runtime_type(&args[2]); + if (!value_type || strcmp(value_type, base_type) != 0) { + char message[RCL_DOMAIN_ERROR_MESSAGE_MAX]; + snprintf(message, sizeof(message), "RCL_MEASUREMENT_TYPE: Measurement %s received %s", base_type, value_type ? value_type : "Unknown"); + return fail(error, "RCL_MEASUREMENT_TYPE", message); + } + if (strcmp(base_type, "Text") != 0 && strcmp(base_type, "Truth") != 0 + && (!uncertainty_type || strcmp(uncertainty_type, base_type) != 0)) { + char message[RCL_DOMAIN_ERROR_MESSAGE_MAX]; + snprintf(message, sizeof(message), "RCL_UNCERTAINTY_TYPE: Uncertainty for %s must be %s", base_type, base_type); + return fail(error, "RCL_UNCERTAINTY_TYPE", message); + } + double confidence = args[3].as.number; + if (confidence < 0 || confidence > 1) { + return fail(error, "RCL_CONFIDENCE_RANGE", "RCL_CONFIDENCE_RANGE: Measurement confidence must be between 0 and 1"); + } + + char *semantic_type = generic_type("Measure", base_type); + if (!semantic_type || !rcl_domain_value_make_record(result, "Measurement", 9, semantic_type)) { + free(semantic_type); + return fail(error, "RCL_DOMAIN_MEASUREMENT_OOM", "RCL_DOMAIN_MEASUREMENT_OOM: unable to construct measurement result"); + } + free(semantic_type); + + RclDomainValueV1 field; + rcl_domain_value_init(&field); + int ok = rcl_domain_value_set_text(&field, "Measurement", "Text") && set_field(result, 0, "kind", &field) + && rcl_domain_value_set_text(&field, base_type, "Text") && set_field(result, 1, "baseType", &field) + && set_field_clone(result, 2, "value", &args[1]) + && set_field_clone(result, 3, "uncertainty", &args[2]) + && rcl_domain_value_set_number(&field, confidence, "Number") && set_field(result, 4, "confidence", &field); + + if (ok) { + if (args[4].as.text.length) { + ok = rcl_domain_value_set_text_n(&field, args[4].as.text.data, args[4].as.text.length, "Text") && set_field(result, 5, "unit", &field); + } else { + const RclDomainValueV1 *value_unit = record_field(&args[1], "unit"); + if (value_unit && value_unit->kind == RCL_DOMAIN_VALUE_TEXT) ok = set_field_clone(result, 5, "unit", value_unit); + else ok = rcl_domain_value_set_null(&field, "Null") && set_field(result, 5, "unit", &field); + } + } + if (ok) ok = rcl_domain_value_set_text_n(&field, args[5].as.text.data, args[5].as.text.length, "Text") && set_field(result, 6, "scale", &field); + if (ok) ok = set_field_clone(result, 7, "evidence", &args[6]); + if (ok) { + if (args[7].as.text.length) ok = rcl_domain_value_set_text_n(&field, args[7].as.text.data, args[7].as.text.length, "Text") && set_field(result, 8, "calibratedBy", &field); + else ok = rcl_domain_value_set_null(&field, "Null") && set_field(result, 8, "calibratedBy", &field); + } + if (!ok) { + rcl_domain_value_free(&field); + rcl_domain_value_free(result); + return fail(error, "RCL_DOMAIN_MEASUREMENT_BUILD", "RCL_DOMAIN_MEASUREMENT_BUILD: unable to populate measurement result"); + } + return 1; +} + +static int unique_sequence(const RclDomainValueV1 *source, RclDomainValueV1 *target) { + if (!source || source->kind != RCL_DOMAIN_VALUE_SEQUENCE) return 0; + size_t unique_count = 0; + for (size_t i = 0; i < source->as.sequence.count; i++) { + int duplicate = 0; + for (size_t j = 0; j < i; j++) { + if (rcl_domain_value_equal(&source->as.sequence.items[i], &source->as.sequence.items[j])) { duplicate = 1; break; } + } + if (!duplicate) unique_count++; + } + if (!rcl_domain_value_make_sequence(target, unique_count, "Sequence")) return 0; + size_t out = 0; + for (size_t i = 0; i < source->as.sequence.count; i++) { + int duplicate = 0; + for (size_t j = 0; j < i; j++) { + if (rcl_domain_value_equal(&source->as.sequence.items[i], &source->as.sequence.items[j])) { duplicate = 1; break; } + } + if (!duplicate && !rcl_domain_value_sequence_set(target, out++, &source->as.sequence.items[i])) { + rcl_domain_value_free(target); + return 0; + } + } + return 1; +} + +static int knowledge_claim( + void *userdata, + const char *domain, + const char *operation, + const RclDomainValueV1 *args, + size_t argc, + RclDomainValueV1 *result, + RclDomainOrganErrorV1 *error +) { + (void)userdata; (void)domain; (void)operation; + if (argc != 10 || args[0].kind != RCL_DOMAIN_VALUE_TEXT || args[2].kind != RCL_DOMAIN_VALUE_NUMBER + || args[3].kind != RCL_DOMAIN_VALUE_SEQUENCE || args[4].kind != RCL_DOMAIN_VALUE_TEXT + || args[5].kind != RCL_DOMAIN_VALUE_TEXT || args[6].kind != RCL_DOMAIN_VALUE_TEXT + || args[7].kind != RCL_DOMAIN_VALUE_SEQUENCE || args[8].kind != RCL_DOMAIN_VALUE_NUMBER + || args[9].kind != RCL_DOMAIN_VALUE_TEXT) { + return fail(error, "RCL_DOMAIN_KNOWLEDGE_ARGUMENT", "RCL_DOMAIN_KNOWLEDGE_ARGUMENT: knowledge.claim expects baseType, value, confidence, evidence, source, scope, status, dependencies, revision and formedAtRoot"); + } + const char *base_type = args[0].as.text.data; + const char *value_type = domain_runtime_type(&args[1]); + if (!value_type || strcmp(value_type, base_type) != 0) { + char message[RCL_DOMAIN_ERROR_MESSAGE_MAX]; + snprintf(message, sizeof(message), "RCL_KNOWLEDGE_TYPE: Knowledge %s received %s", base_type, value_type ? value_type : "Unknown"); + return fail(error, "RCL_KNOWLEDGE_TYPE", message); + } + if (args[2].as.number < 0 || args[2].as.number > 1) { + return fail(error, "RCL_KNOWLEDGE_CONFIDENCE_RANGE", "RCL_KNOWLEDGE_CONFIDENCE_RANGE: Knowledge confidence must be between 0 and 1"); + } + + char *semantic_type = generic_type("Know", base_type); + if (!semantic_type || !rcl_domain_value_make_record(result, "Knowledge", 12, semantic_type)) { + free(semantic_type); + return fail(error, "RCL_DOMAIN_KNOWLEDGE_OOM", "RCL_DOMAIN_KNOWLEDGE_OOM: unable to construct knowledge result"); + } + free(semantic_type); + + RclDomainValueV1 field, evidence, dependencies, alternatives; + rcl_domain_value_init(&field); + rcl_domain_value_init(&evidence); + rcl_domain_value_init(&dependencies); + rcl_domain_value_init(&alternatives); + if (!unique_sequence(&args[3], &evidence) || !unique_sequence(&args[7], &dependencies) + || !rcl_domain_value_make_sequence(&alternatives, 0, "Sequence")) { + rcl_domain_value_free(&evidence); rcl_domain_value_free(&dependencies); rcl_domain_value_free(&alternatives); + rcl_domain_value_free(result); + return fail(error, "RCL_DOMAIN_KNOWLEDGE_SEQUENCE", "RCL_DOMAIN_KNOWLEDGE_SEQUENCE: unable to normalize evidence/dependencies"); + } + + int ok = rcl_domain_value_set_text(&field, "Knowledge", "Text") && set_field(result, 0, "kind", &field) + && rcl_domain_value_set_text(&field, base_type, "Text") && set_field(result, 1, "baseType", &field) + && set_field_clone(result, 2, "value", &args[1]) + && rcl_domain_value_set_number(&field, args[2].as.number, "Number") && set_field(result, 3, "confidence", &field) + && set_field(result, 4, "evidence", &evidence); + + if (ok) { + if (args[4].as.text.length) ok = rcl_domain_value_set_text_n(&field, args[4].as.text.data, args[4].as.text.length, "Text") && set_field(result, 5, "source", &field); + else ok = rcl_domain_value_set_null(&field, "Null") && set_field(result, 5, "source", &field); + } + if (ok) ok = rcl_domain_value_set_text_n(&field, args[5].as.text.data, args[5].as.text.length, "Text") && set_field(result, 6, "scope", &field); + if (ok) ok = rcl_domain_value_set_text_n(&field, args[6].as.text.data, args[6].as.text.length, "Text") && set_field(result, 7, "status", &field); + if (ok) ok = set_field(result, 8, "dependencies", &dependencies); + if (ok) ok = rcl_domain_value_set_number(&field, args[8].as.number, "Number") && set_field(result, 9, "revision", &field); + if (ok) ok = set_field(result, 10, "alternatives", &alternatives); + if (ok) { + if (args[9].as.text.length) ok = rcl_domain_value_set_text_n(&field, args[9].as.text.data, args[9].as.text.length, "Text") && set_field(result, 11, "formedAtRoot", &field); + else ok = rcl_domain_value_set_null(&field, "Null") && set_field(result, 11, "formedAtRoot", &field); + } + + if (!ok) { + rcl_domain_value_free(&field); + rcl_domain_value_free(&evidence); + rcl_domain_value_free(&dependencies); + rcl_domain_value_free(&alternatives); + rcl_domain_value_free(result); + return fail(error, "RCL_DOMAIN_KNOWLEDGE_BUILD", "RCL_DOMAIN_KNOWLEDGE_BUILD: unable to populate knowledge result"); + } + return 1; +} + +int rcl_domain_register_admitted_candidates_v01( + RclDomainOrganRegistry *registry, + char *error, + size_t error_capacity +) { + const RclDomainOrganV1 organs[] = { + { + RCL_DOMAIN_ORGAN_ABI_V1, + "core", "echo", "core.echo", "candidate.native.core.echo.v0.1", NULL, + RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, 1, core_echo, NULL, + }, + { + RCL_DOMAIN_ORGAN_ABI_V1, + "quantity", "make", "quantity.make", "candidate.native.quantity.make.v0.1", NULL, + RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, 1, quantity_make, NULL, + }, + { + RCL_DOMAIN_ORGAN_ABI_V1, + "quantitative", "measure", "quantitative.measure", "candidate.native.quantitative.measure.v0.1", NULL, + RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, 1, quantitative_measure, NULL, + }, + { + RCL_DOMAIN_ORGAN_ABI_V1, + "knowledge", "claim", "knowledge.claim", "candidate.native.knowledge.claim.v0.1", NULL, + RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE, 1, knowledge_claim, NULL, + }, + }; + for (size_t i = 0; i < sizeof(organs) / sizeof(organs[0]); i++) { + if (!rcl_domain_organ_register(registry, &organs[i], error, error_capacity)) return 0; + } + return 1; +} diff --git a/native/rcl_domain_admitted_organs.h b/native/rcl_domain_admitted_organs.h new file mode 100644 index 00000000..57f00d92 --- /dev/null +++ b/native/rcl_domain_admitted_organs.h @@ -0,0 +1,20 @@ +#ifndef RCL_DOMAIN_ADMITTED_ORGANS_H +#define RCL_DOMAIN_ADMITTED_ORGANS_H + +#include "rcl_domain_organ.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int rcl_domain_register_admitted_candidates_v01( + RclDomainOrganRegistry *registry, + char *error, + size_t error_capacity +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/native/rcl_domain_organ.c b/native/rcl_domain_organ.c new file mode 100644 index 00000000..b677c11b --- /dev/null +++ b/native/rcl_domain_organ.c @@ -0,0 +1,144 @@ +#include "rcl_domain_organ.h" + +#include +#include +#include + +static int fail_registration(char *error, size_t capacity, const char *message) { + if (error && capacity) snprintf(error, capacity, "%s", message); + return 0; +} + +void rcl_domain_organ_error_clear(RclDomainOrganErrorV1 *error) { + if (!error) return; + error->code[0] = '\0'; + error->message[0] = '\0'; +} + +void rcl_domain_organ_error_set(RclDomainOrganErrorV1 *error, const char *code, const char *message) { + if (!error) return; + snprintf(error->code, sizeof(error->code), "%s", code && code[0] ? code : "RCL_DOMAIN_ORGAN_FAILURE"); + snprintf(error->message, sizeof(error->message), "%s", message && message[0] ? message : "Domain organ invocation failed"); +} + +static int fail_invoke(RclDomainOrganErrorV1 *error, const char *code, const char *message) { + rcl_domain_organ_error_set(error, code, message); + return 0; +} + +static char *copy_text(const char *text) { + if (!text) return NULL; + size_t length = strlen(text); + if (length > RCL_DOMAIN_VALUE_MAX_TEXT_BYTES) return NULL; + char *copy = (char *)malloc(length + 1); + if (!copy) return NULL; + memcpy(copy, text, length + 1); + return copy; +} + +static void free_entry(RclDomainOrganV1 *entry) { + if (!entry) return; + free((void *)entry->domain); + free((void *)entry->operation); + free((void *)entry->semantic_identity); + free((void *)entry->implementation_id); + free((void *)entry->artifact_root); + memset(entry, 0, sizeof(*entry)); +} + +void rcl_domain_organ_registry_init(RclDomainOrganRegistry *registry) { + if (registry) memset(registry, 0, sizeof(*registry)); +} + +void rcl_domain_organ_registry_free(RclDomainOrganRegistry *registry) { + if (!registry) return; + for (size_t i = 0; i < registry->count; i++) free_entry(®istry->entries[i]); + memset(registry, 0, sizeof(*registry)); +} + +int rcl_domain_organ_register(RclDomainOrganRegistry *registry, const RclDomainOrganV1 *organ, char *error, size_t error_capacity) { + if (!registry || !organ || organ->abi_version != RCL_DOMAIN_ORGAN_ABI_V1 || !organ->domain || !organ->domain[0] + || !organ->operation || !organ->operation[0] || !organ->semantic_identity || !organ->semantic_identity[0] + || !organ->implementation_id || !organ->implementation_id[0] || !organ->invoke) { + return fail_registration(error, error_capacity, "RCL_DOMAIN_ORGAN_INVALID: invalid organ registration"); + } + if (organ->evidence_tier < RCL_DOMAIN_ORGAN_QUARANTINED || organ->evidence_tier > RCL_DOMAIN_ORGAN_NATIVE_VERIFIED) { + return fail_registration(error, error_capacity, "RCL_DOMAIN_ORGAN_TIER: invalid evidence tier"); + } + for (size_t i = 0; i < registry->count; i++) { + if (strcmp(registry->entries[i].domain, organ->domain) == 0 && strcmp(registry->entries[i].operation, organ->operation) == 0) { + return fail_registration(error, error_capacity, "RCL_DOMAIN_ORGAN_DUPLICATE: duplicate domain operation"); + } + } + if (registry->count >= RCL_DOMAIN_ORGAN_MAX) return fail_registration(error, error_capacity, "RCL_DOMAIN_ORGAN_FULL: registry capacity exceeded"); + + RclDomainOrganV1 copy = *organ; + copy.domain = copy_text(organ->domain); + copy.operation = copy_text(organ->operation); + copy.semantic_identity = copy_text(organ->semantic_identity); + copy.implementation_id = copy_text(organ->implementation_id); + copy.artifact_root = organ->artifact_root ? copy_text(organ->artifact_root) : NULL; + if (!copy.domain || !copy.operation || !copy.semantic_identity || !copy.implementation_id || (organ->artifact_root && !copy.artifact_root)) { + free_entry(©); + return fail_registration(error, error_capacity, "RCL_DOMAIN_ORGAN_OOM: unable to own organ identity strings"); + } + registry->entries[registry->count++] = copy; + return 1; +} + +const RclDomainOrganV1 *rcl_domain_organ_resolve(const RclDomainOrganRegistry *registry, const char *domain, const char *operation) { + if (!registry || !domain || !operation) return NULL; + for (size_t i = 0; i < registry->count; i++) { + if (strcmp(registry->entries[i].domain, domain) == 0 && strcmp(registry->entries[i].operation, operation) == 0) return ®istry->entries[i]; + } + return NULL; +} + +int rcl_domain_organ_invoke( + const RclDomainOrganRegistry *registry, + const char *domain, + const char *operation, + RclDomainOrganEvidenceTier required_tier, + const RclDomainValueV1 *args, + size_t argc, + RclDomainValueV1 *result, + RclDomainOrganErrorV1 *error +) { + rcl_domain_organ_error_clear(error); + const RclDomainOrganV1 *organ = rcl_domain_organ_resolve(registry, domain, operation); + if (!organ) { + char message[RCL_DOMAIN_ERROR_MESSAGE_MAX]; + snprintf(message, sizeof(message), "RCL_DOMAIN_OPERATION_MISSING: Domain operation '%s.%s' is not present in the RBC 1.3 salvage inventory", domain ? domain : "", operation ? operation : ""); + return fail_invoke(error, "RCL_DOMAIN_OPERATION_MISSING", message); + } + if (required_tier < RCL_DOMAIN_ORGAN_QUARANTINED || required_tier > RCL_DOMAIN_ORGAN_NATIVE_VERIFIED) { + return fail_invoke(error, "RCL_DOMAIN_ORGAN_TIER", "RCL_DOMAIN_ORGAN_TIER: Invalid required evidence tier"); + } + if (organ->evidence_tier < required_tier) { + return fail_invoke(error, "RCL_DOMAIN_ORGAN_EVIDENCE_TIER", "RCL_DOMAIN_ORGAN_EVIDENCE_TIER: Organ has not reached required evidence tier"); + } + if ((argc && !args) || !result || argc > RCL_DOMAIN_VALUE_MAX_ITEMS) { + return fail_invoke(error, "RCL_DOMAIN_ORGAN_ARGUMENT", "RCL_DOMAIN_ORGAN_ARGUMENT: args/result contract is invalid"); + } + for (size_t i = 0; i < argc; i++) { + if (!rcl_domain_value_validate(&args[i])) { + return fail_invoke(error, "RCL_DOMAIN_ORGAN_VALUE_INVALID", "RCL_DOMAIN_ORGAN_VALUE_INVALID: argument violates domain value ABI"); + } + } + if (result->abi_version != RCL_DOMAIN_VALUE_ABI_V1) { + return fail_invoke(error, "RCL_DOMAIN_ORGAN_OUTPUT_INIT", "RCL_DOMAIN_ORGAN_OUTPUT_INIT: result must be initialized with rcl_domain_value_init"); + } + rcl_domain_value_free(result); + if (!organ->invoke(organ->userdata, domain, operation, args, argc, result, error)) { + rcl_domain_value_free(result); + if (error && !error->code[0]) { + rcl_domain_organ_error_set(error, "RCL_DOMAIN_ORGAN_FAILURE", "RCL_DOMAIN_ORGAN_FAILURE: Domain organ invocation failed without a semantic error"); + } + return 0; + } + if (!rcl_domain_value_validate(result)) { + rcl_domain_value_free(result); + return fail_invoke(error, "RCL_DOMAIN_ORGAN_VALUE_INVALID", "RCL_DOMAIN_ORGAN_VALUE_INVALID: organ returned an invalid domain value"); + } + return 1; +} diff --git a/native/rcl_domain_organ.h b/native/rcl_domain_organ.h new file mode 100644 index 00000000..c3c487a4 --- /dev/null +++ b/native/rcl_domain_organ.h @@ -0,0 +1,78 @@ +#ifndef RCL_DOMAIN_ORGAN_H +#define RCL_DOMAIN_ORGAN_H + +#include +#include "rcl_domain_value.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define RCL_DOMAIN_ORGAN_ABI_V1 1u +#define RCL_DOMAIN_ORGAN_MAX 128u +#define RCL_DOMAIN_ERROR_CODE_MAX 128u +#define RCL_DOMAIN_ERROR_MESSAGE_MAX 512u + +typedef enum { + RCL_DOMAIN_ORGAN_QUARANTINED = 0, + RCL_DOMAIN_ORGAN_DIFFERENTIAL_VERIFIED = 1, + RCL_DOMAIN_ORGAN_NATIVE_CANDIDATE = 2, + RCL_DOMAIN_ORGAN_NATIVE_VERIFIED = 3 +} RclDomainOrganEvidenceTier; + +typedef struct { + char code[RCL_DOMAIN_ERROR_CODE_MAX]; + char message[RCL_DOMAIN_ERROR_MESSAGE_MAX]; +} RclDomainOrganErrorV1; + +void rcl_domain_organ_error_clear(RclDomainOrganErrorV1 *error); +void rcl_domain_organ_error_set(RclDomainOrganErrorV1 *error, const char *code, const char *message); + +typedef int (*RclDomainOrganInvokeFn)( + void *userdata, + const char *domain, + const char *operation, + const RclDomainValueV1 *args, + size_t argc, + RclDomainValueV1 *result, + RclDomainOrganErrorV1 *error +); + +typedef struct { + unsigned int abi_version; + const char *domain; + const char *operation; + const char *semantic_identity; + const char *implementation_id; + const char *artifact_root; + RclDomainOrganEvidenceTier evidence_tier; + int deterministic; + RclDomainOrganInvokeFn invoke; + void *userdata; +} RclDomainOrganV1; + +typedef struct { + RclDomainOrganV1 entries[RCL_DOMAIN_ORGAN_MAX]; + size_t count; +} RclDomainOrganRegistry; + +void rcl_domain_organ_registry_init(RclDomainOrganRegistry *registry); +void rcl_domain_organ_registry_free(RclDomainOrganRegistry *registry); +int rcl_domain_organ_register(RclDomainOrganRegistry *registry, const RclDomainOrganV1 *organ, char *error, size_t error_capacity); +const RclDomainOrganV1 *rcl_domain_organ_resolve(const RclDomainOrganRegistry *registry, const char *domain, const char *operation); +int rcl_domain_organ_invoke( + const RclDomainOrganRegistry *registry, + const char *domain, + const char *operation, + RclDomainOrganEvidenceTier required_tier, + const RclDomainValueV1 *args, + size_t argc, + RclDomainValueV1 *result, + RclDomainOrganErrorV1 *error +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/native/rcl_domain_value.c b/native/rcl_domain_value.c new file mode 100644 index 00000000..2709d18f --- /dev/null +++ b/native/rcl_domain_value.c @@ -0,0 +1,269 @@ +#include "rcl_domain_value.h" + +#include +#include +#include +#include + +static char *copy_text_n(const char *text, size_t length) { + if (length > RCL_DOMAIN_VALUE_MAX_TEXT_BYTES) return NULL; + char *copy = (char *)malloc(length + 1); + if (!copy) return NULL; + if (length && text) memcpy(copy, text, length); + copy[length] = '\0'; + return copy; +} + +static char *copy_text(const char *text) { + return text ? copy_text_n(text, strlen(text)) : NULL; +} + +static int same_optional_text(const char *left, const char *right) { + if (!left || !right) return left == right; + return strcmp(left, right) == 0; +} + +void rcl_domain_value_init(RclDomainValueV1 *value) { + if (!value) return; + memset(value, 0, sizeof(*value)); + value->abi_version = RCL_DOMAIN_VALUE_ABI_V1; + value->kind = RCL_DOMAIN_VALUE_NULL; +} + +void rcl_domain_value_free(RclDomainValueV1 *value) { + if (!value) return; + free(value->semantic_type); + value->semantic_type = NULL; + switch (value->kind) { + case RCL_DOMAIN_VALUE_TEXT: + free(value->as.text.data); + break; + case RCL_DOMAIN_VALUE_SEQUENCE: + for (size_t i = 0; i < value->as.sequence.count; i++) rcl_domain_value_free(&value->as.sequence.items[i]); + free(value->as.sequence.items); + break; + case RCL_DOMAIN_VALUE_RECORD: + free(value->as.record.type_name); + for (size_t i = 0; i < value->as.record.count; i++) { + free(value->as.record.fields[i].name); + if (value->as.record.fields[i].value) { + rcl_domain_value_free(value->as.record.fields[i].value); + free(value->as.record.fields[i].value); + } + } + free(value->as.record.fields); + break; + default: + break; + } + rcl_domain_value_init(value); +} + +static int set_semantic_type(RclDomainValueV1 *value, const char *semantic_type) { + value->semantic_type = copy_text(semantic_type); + return !semantic_type || value->semantic_type != NULL; +} + +int rcl_domain_value_set_null(RclDomainValueV1 *value, const char *semantic_type) { + if (!value) return 0; + rcl_domain_value_free(value); + value->kind = RCL_DOMAIN_VALUE_NULL; + return set_semantic_type(value, semantic_type); +} + +int rcl_domain_value_set_number(RclDomainValueV1 *value, double number, const char *semantic_type) { + if (!value || !isfinite(number)) return 0; + rcl_domain_value_free(value); + value->kind = RCL_DOMAIN_VALUE_NUMBER; + value->as.number = number; + return set_semantic_type(value, semantic_type); +} + +int rcl_domain_value_set_truth(RclDomainValueV1 *value, int truth, const char *semantic_type) { + if (!value) return 0; + rcl_domain_value_free(value); + value->kind = RCL_DOMAIN_VALUE_TRUTH; + value->as.truth = truth ? 1 : 0; + return set_semantic_type(value, semantic_type); +} + +int rcl_domain_value_set_text_n(RclDomainValueV1 *value, const char *text, size_t length, const char *semantic_type) { + if (!value || (length && !text) || length > RCL_DOMAIN_VALUE_MAX_TEXT_BYTES) return 0; + rcl_domain_value_free(value); + value->kind = RCL_DOMAIN_VALUE_TEXT; + value->as.text.data = copy_text_n(text ? text : "", length); + if (!value->as.text.data) { rcl_domain_value_free(value); return 0; } + value->as.text.length = length; + if (!set_semantic_type(value, semantic_type)) { rcl_domain_value_free(value); return 0; } + return 1; +} + +int rcl_domain_value_set_text(RclDomainValueV1 *value, const char *text, const char *semantic_type) { + return rcl_domain_value_set_text_n(value, text ? text : "", text ? strlen(text) : 0, semantic_type); +} + +int rcl_domain_value_make_sequence(RclDomainValueV1 *value, size_t count, const char *semantic_type) { + if (!value || count > RCL_DOMAIN_VALUE_MAX_ITEMS || count > SIZE_MAX / sizeof(RclDomainValueV1)) return 0; + rcl_domain_value_free(value); + value->kind = RCL_DOMAIN_VALUE_SEQUENCE; + if (count) { + value->as.sequence.items = (RclDomainValueV1 *)calloc(count, sizeof(RclDomainValueV1)); + if (!value->as.sequence.items) { rcl_domain_value_free(value); return 0; } + for (size_t i = 0; i < count; i++) rcl_domain_value_init(&value->as.sequence.items[i]); + } + value->as.sequence.count = count; + if (!set_semantic_type(value, semantic_type)) { rcl_domain_value_free(value); return 0; } + return 1; +} + +int rcl_domain_value_sequence_set(RclDomainValueV1 *sequence, size_t index, const RclDomainValueV1 *item) { + if (!sequence || sequence->kind != RCL_DOMAIN_VALUE_SEQUENCE || !item || index >= sequence->as.sequence.count) return 0; + return rcl_domain_value_clone(&sequence->as.sequence.items[index], item); +} + +int rcl_domain_value_make_record(RclDomainValueV1 *value, const char *type_name, size_t field_count, const char *semantic_type) { + if (!value || !type_name || !type_name[0] || strlen(type_name) > RCL_DOMAIN_VALUE_MAX_TEXT_BYTES + || field_count > RCL_DOMAIN_VALUE_MAX_ITEMS || field_count > SIZE_MAX / sizeof(RclDomainFieldV1)) return 0; + rcl_domain_value_free(value); + value->kind = RCL_DOMAIN_VALUE_RECORD; + value->as.record.type_name = copy_text(type_name); + if (!value->as.record.type_name) { rcl_domain_value_free(value); return 0; } + if (field_count) { + value->as.record.fields = (RclDomainFieldV1 *)calloc(field_count, sizeof(RclDomainFieldV1)); + if (!value->as.record.fields) { rcl_domain_value_free(value); return 0; } + } + value->as.record.count = field_count; + if (!set_semantic_type(value, semantic_type)) { rcl_domain_value_free(value); return 0; } + return 1; +} + +int rcl_domain_value_record_set(RclDomainValueV1 *record, size_t index, const char *field_name, const RclDomainValueV1 *field_value) { + if (!record || record->kind != RCL_DOMAIN_VALUE_RECORD || index >= record->as.record.count || !field_name || !field_name[0] + || strlen(field_name) > RCL_DOMAIN_VALUE_MAX_TEXT_BYTES || !field_value) return 0; + for (size_t i = 0; i < record->as.record.count; i++) { + if (i != index && record->as.record.fields[i].name && strcmp(record->as.record.fields[i].name, field_name) == 0) return 0; + } + RclDomainFieldV1 *field = &record->as.record.fields[index]; + char *name = copy_text(field_name); + RclDomainValueV1 *copy = (RclDomainValueV1 *)malloc(sizeof(RclDomainValueV1)); + if (!name || !copy) { free(name); free(copy); return 0; } + rcl_domain_value_init(copy); + if (!rcl_domain_value_clone(copy, field_value)) { free(name); rcl_domain_value_free(copy); free(copy); return 0; } + free(field->name); + if (field->value) { rcl_domain_value_free(field->value); free(field->value); } + field->name = name; + field->value = copy; + return 1; +} + +static int validate_value(const RclDomainValueV1 *value, size_t depth) { + if (!value || value->abi_version != RCL_DOMAIN_VALUE_ABI_V1 || depth > RCL_DOMAIN_VALUE_MAX_DEPTH) return 0; + if (value->semantic_type && strlen(value->semantic_type) > RCL_DOMAIN_VALUE_MAX_TEXT_BYTES) return 0; + switch (value->kind) { + case RCL_DOMAIN_VALUE_NULL: + return 1; + case RCL_DOMAIN_VALUE_NUMBER: + return isfinite(value->as.number); + case RCL_DOMAIN_VALUE_TRUTH: + return value->as.truth == 0 || value->as.truth == 1; + case RCL_DOMAIN_VALUE_TEXT: + return value->as.text.length <= RCL_DOMAIN_VALUE_MAX_TEXT_BYTES + && (value->as.text.length == 0 || value->as.text.data != NULL); + case RCL_DOMAIN_VALUE_SEQUENCE: + if (value->as.sequence.count > RCL_DOMAIN_VALUE_MAX_ITEMS || (value->as.sequence.count && !value->as.sequence.items)) return 0; + for (size_t i = 0; i < value->as.sequence.count; i++) if (!validate_value(&value->as.sequence.items[i], depth + 1)) return 0; + return 1; + case RCL_DOMAIN_VALUE_RECORD: + if (!value->as.record.type_name || !value->as.record.type_name[0] || strlen(value->as.record.type_name) > RCL_DOMAIN_VALUE_MAX_TEXT_BYTES + || value->as.record.count > RCL_DOMAIN_VALUE_MAX_ITEMS || (value->as.record.count && !value->as.record.fields)) return 0; + for (size_t i = 0; i < value->as.record.count; i++) { + const RclDomainFieldV1 *field = &value->as.record.fields[i]; + if (!field->name || !field->name[0] || strlen(field->name) > RCL_DOMAIN_VALUE_MAX_TEXT_BYTES || !field->value || !validate_value(field->value, depth + 1)) return 0; + for (size_t j = i + 1; j < value->as.record.count; j++) { + if (value->as.record.fields[j].name && strcmp(field->name, value->as.record.fields[j].name) == 0) return 0; + } + } + return 1; + default: + return 0; + } +} + +int rcl_domain_value_validate(const RclDomainValueV1 *value) { + return validate_value(value, 0); +} + +int rcl_domain_value_clone(RclDomainValueV1 *target, const RclDomainValueV1 *source) { + if (!target || !source || target == source) return target == source; + if (!rcl_domain_value_validate(source)) return 0; + rcl_domain_value_free(target); + int ok = 0; + switch (source->kind) { + case RCL_DOMAIN_VALUE_NULL: + ok = rcl_domain_value_set_null(target, source->semantic_type); + break; + case RCL_DOMAIN_VALUE_NUMBER: + ok = rcl_domain_value_set_number(target, source->as.number, source->semantic_type); + break; + case RCL_DOMAIN_VALUE_TRUTH: + ok = rcl_domain_value_set_truth(target, source->as.truth, source->semantic_type); + break; + case RCL_DOMAIN_VALUE_TEXT: + ok = rcl_domain_value_set_text_n(target, source->as.text.data, source->as.text.length, source->semantic_type); + break; + case RCL_DOMAIN_VALUE_SEQUENCE: + ok = rcl_domain_value_make_sequence(target, source->as.sequence.count, source->semantic_type); + if (ok) for (size_t i = 0; i < source->as.sequence.count; i++) if (!rcl_domain_value_sequence_set(target, i, &source->as.sequence.items[i])) { ok = 0; break; } + break; + case RCL_DOMAIN_VALUE_RECORD: + ok = rcl_domain_value_make_record(target, source->as.record.type_name, source->as.record.count, source->semantic_type); + if (ok) for (size_t i = 0; i < source->as.record.count; i++) { + const RclDomainFieldV1 *field = &source->as.record.fields[i]; + if (!rcl_domain_value_record_set(target, i, field->name, field->value)) { ok = 0; break; } + } + break; + default: + ok = 0; + break; + } + if (!ok) rcl_domain_value_free(target); + return ok; +} + +static const RclDomainFieldV1 *find_field(const RclDomainValueV1 *record, const char *name) { + for (size_t i = 0; i < record->as.record.count; i++) { + const RclDomainFieldV1 *field = &record->as.record.fields[i]; + if (field->name && strcmp(field->name, name) == 0) return field; + } + return NULL; +} + +int rcl_domain_value_equal(const RclDomainValueV1 *left, const RclDomainValueV1 *right) { + if (!rcl_domain_value_validate(left) || !rcl_domain_value_validate(right)) return 0; + if (left->kind != right->kind || !same_optional_text(left->semantic_type, right->semantic_type)) return 0; + switch (left->kind) { + case RCL_DOMAIN_VALUE_NULL: + return 1; + case RCL_DOMAIN_VALUE_NUMBER: + return left->as.number == right->as.number; + case RCL_DOMAIN_VALUE_TRUTH: + return left->as.truth == right->as.truth; + case RCL_DOMAIN_VALUE_TEXT: + return left->as.text.length == right->as.text.length + && memcmp(left->as.text.data, right->as.text.data, left->as.text.length) == 0; + case RCL_DOMAIN_VALUE_SEQUENCE: + if (left->as.sequence.count != right->as.sequence.count) return 0; + for (size_t i = 0; i < left->as.sequence.count; i++) if (!rcl_domain_value_equal(&left->as.sequence.items[i], &right->as.sequence.items[i])) return 0; + return 1; + case RCL_DOMAIN_VALUE_RECORD: + if (!same_optional_text(left->as.record.type_name, right->as.record.type_name) || left->as.record.count != right->as.record.count) return 0; + for (size_t i = 0; i < left->as.record.count; i++) { + const RclDomainFieldV1 *field = &left->as.record.fields[i]; + const RclDomainFieldV1 *other = find_field(right, field->name); + if (!other || !rcl_domain_value_equal(field->value, other->value)) return 0; + } + return 1; + default: + return 0; + } +} diff --git a/native/rcl_domain_value.h b/native/rcl_domain_value.h new file mode 100644 index 00000000..058ec862 --- /dev/null +++ b/native/rcl_domain_value.h @@ -0,0 +1,74 @@ +#ifndef RCL_DOMAIN_VALUE_H +#define RCL_DOMAIN_VALUE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RCL_DOMAIN_VALUE_ABI_V1 1u +#define RCL_DOMAIN_VALUE_MAX_DEPTH 64u +#define RCL_DOMAIN_VALUE_MAX_ITEMS 65536u +#define RCL_DOMAIN_VALUE_MAX_TEXT_BYTES (16u * 1024u * 1024u) + +typedef enum { + RCL_DOMAIN_VALUE_NULL = 0, + RCL_DOMAIN_VALUE_NUMBER = 1, + RCL_DOMAIN_VALUE_TRUTH = 2, + RCL_DOMAIN_VALUE_TEXT = 3, + RCL_DOMAIN_VALUE_SEQUENCE = 4, + RCL_DOMAIN_VALUE_RECORD = 5 +} RclDomainValueKind; + +typedef struct RclDomainValueV1 RclDomainValueV1; + +typedef struct { + char *name; + RclDomainValueV1 *value; +} RclDomainFieldV1; + +struct RclDomainValueV1 { + unsigned int abi_version; + RclDomainValueKind kind; + char *semantic_type; + union { + double number; + int truth; + struct { + char *data; + size_t length; + } text; + struct { + RclDomainValueV1 *items; + size_t count; + } sequence; + struct { + char *type_name; + RclDomainFieldV1 *fields; + size_t count; + } record; + } as; +}; + +void rcl_domain_value_init(RclDomainValueV1 *value); +void rcl_domain_value_free(RclDomainValueV1 *value); +int rcl_domain_value_clone(RclDomainValueV1 *target, const RclDomainValueV1 *source); +int rcl_domain_value_validate(const RclDomainValueV1 *value); + +int rcl_domain_value_set_null(RclDomainValueV1 *value, const char *semantic_type); +int rcl_domain_value_set_number(RclDomainValueV1 *value, double number, const char *semantic_type); +int rcl_domain_value_set_truth(RclDomainValueV1 *value, int truth, const char *semantic_type); +int rcl_domain_value_set_text(RclDomainValueV1 *value, const char *text, const char *semantic_type); +int rcl_domain_value_set_text_n(RclDomainValueV1 *value, const char *text, size_t length, const char *semantic_type); +int rcl_domain_value_make_sequence(RclDomainValueV1 *value, size_t count, const char *semantic_type); +int rcl_domain_value_sequence_set(RclDomainValueV1 *sequence, size_t index, const RclDomainValueV1 *item); +int rcl_domain_value_make_record(RclDomainValueV1 *value, const char *type_name, size_t field_count, const char *semantic_type); +int rcl_domain_value_record_set(RclDomainValueV1 *record, size_t index, const char *field_name, const RclDomainValueV1 *field_value); +int rcl_domain_value_equal(const RclDomainValueV1 *left, const RclDomainValueV1 *right); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/native/rcl_domain_vm_candidate.h b/native/rcl_domain_vm_candidate.h new file mode 100644 index 00000000..25882195 --- /dev/null +++ b/native/rcl_domain_vm_candidate.h @@ -0,0 +1,34 @@ +#ifndef RCL_DOMAIN_VM_CANDIDATE_H +#define RCL_DOMAIN_VM_CANDIDATE_H + +#include "rclvm.h" +#include "rcl_domain_organ.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define RCLVM_DOMAIN_CANDIDATE_ABI_V1 1u + +RCLVM_API int rclvm_instance_register_domain_organ( + RclVmInstance *instance, + const RclDomainOrganV1 *organ, + char *error, + size_t error_capacity +); + +RCLVM_API int rclvm_instance_set_domain_minimum_tier( + RclVmInstance *instance, + RclDomainOrganEvidenceTier minimum_tier, + char *error, + size_t error_capacity +); + +RCLVM_API size_t rclvm_instance_domain_organ_count(const RclVmInstance *instance); +RCLVM_API RclDomainOrganEvidenceTier rclvm_instance_domain_minimum_tier(const RclVmInstance *instance); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/native/rcl_domain_vm_value_bridge.inc b/native/rcl_domain_vm_value_bridge.inc new file mode 100644 index 00000000..0a9a5230 --- /dev/null +++ b/native/rcl_domain_vm_value_bridge.inc @@ -0,0 +1,189 @@ +/* + * Candidate-only private membrane between native/rclvm.c Value and + * RclDomainValueV1. Include this file only after the VM Value/VM helpers are + * defined. It deliberately covers only the value subset needed by the first + * admitted DOMAIN_CALL operations. + */ +#include "rcl_domain_value.h" + +static void rcl_domain_bridge_fail(VM *vm, const char *message) { + vm_fail(vm, "RCL_NATIVE_DOMAIN_VALUE_UNSUPPORTED", message); +} + +static const Value *rcl_domain_bridge_record_field(const TypedRecord *record, const char *name) { + int index = typed_record_field_index(record, name); + return index < 0 ? NULL : &record->field_values[(size_t)index]; +} + +static const char *rcl_domain_bridge_record_family(const TypedRecord *record) { + const Value *kind = rcl_domain_bridge_record_field(record, "kind"); + if (kind && kind->type == VALUE_STRING && kind->string && kind->string[0]) return kind->string; + return record->type_name; +} + +static int rcl_domain_bridge_native_to_domain_impl(VM *vm, const Value *source, RclDomainValueV1 *target, size_t depth) { + if (!source || !target || depth > RCL_DOMAIN_VALUE_MAX_DEPTH) { + rcl_domain_bridge_fail(vm, "Native value exceeds Domain Value recursion boundary"); + return 0; + } + switch (source->type) { + case VALUE_NULL: + return rcl_domain_value_set_null(target, "Null"); + case VALUE_NUMBER: + if (!isfinite(source->number)) { + rcl_domain_bridge_fail(vm, "Non-finite Number is not admitted by Domain Value ABI v1"); + return 0; + } + return rcl_domain_value_set_number(target, source->number, "Number"); + case VALUE_BOOL: + return rcl_domain_value_set_truth(target, source->boolean, "Truth"); + case VALUE_STRING: { + size_t length = source->string ? shared_string_header(source->string)->byte_length : 0; + return rcl_domain_value_set_text_n(target, source->string ? source->string : "", length, "Text"); + } + case VALUE_SEQUENCE: { + if (!source->sequence || source->sequence->count > RCL_DOMAIN_VALUE_MAX_ITEMS + || !rcl_domain_value_make_sequence(target, source->sequence->count, "Sequence")) { + rcl_domain_bridge_fail(vm, "Native Sequence exceeds Domain Value boundary"); + return 0; + } + sequence_materialize(source->sequence); + for (size_t i = 0; i < source->sequence->count; i++) { + if (!rcl_domain_bridge_native_to_domain_impl(vm, &source->sequence->items[i], &target->as.sequence.items[i], depth + 1)) return 0; + } + return 1; + } + case VALUE_TYPED_RECORD: { + if (!source->typed_record || source->typed_record->field_count > RCL_DOMAIN_VALUE_MAX_ITEMS) { + rcl_domain_bridge_fail(vm, "Native typed Record exceeds Domain Value boundary"); + return 0; + } + const char *family = rcl_domain_bridge_record_family(source->typed_record); + if (!rcl_domain_value_make_record( + target, + family && family[0] ? family : source->typed_record->type_name, + source->typed_record->field_count, + source->typed_record->type_name + )) { + rcl_domain_bridge_fail(vm, "Unable to allocate Domain Value Record"); + return 0; + } + for (size_t i = 0; i < source->typed_record->field_count; i++) { + RclDomainValueV1 field_value; + rcl_domain_value_init(&field_value); + int converted = rcl_domain_bridge_native_to_domain_impl(vm, &source->typed_record->field_values[i], &field_value, depth + 1); + int stored = converted && rcl_domain_value_record_set(target, i, source->typed_record->field_names[i], &field_value); + rcl_domain_value_free(&field_value); + if (!stored) { + if (!vm->error.code) rcl_domain_bridge_fail(vm, "Unable to store Domain Value Record field"); + return 0; + } + } + return 1; + } + default: + rcl_domain_bridge_fail(vm, "Native value kind is not admitted by Domain Value ABI v1"); + return 0; + } +} + +static int rcl_domain_bridge_native_to_domain(VM *vm, const Value *source, RclDomainValueV1 *target) { + if (!target || target->abi_version != RCL_DOMAIN_VALUE_ABI_V1) { + rcl_domain_bridge_fail(vm, "Domain Value output must be initialized"); + return 0; + } + rcl_domain_value_free(target); + if (!rcl_domain_bridge_native_to_domain_impl(vm, source, target, 0)) { + rcl_domain_value_free(target); + return 0; + } + if (!rcl_domain_value_validate(target)) { + rcl_domain_value_free(target); + rcl_domain_bridge_fail(vm, "Converted Domain Value failed recursive validation"); + return 0; + } + return 1; +} + +static Value rcl_domain_bridge_domain_to_native_impl(VM *vm, const RclDomainValueV1 *source, size_t depth) { + if (!source || depth > RCL_DOMAIN_VALUE_MAX_DEPTH) { + rcl_domain_bridge_fail(vm, "Domain Value exceeds native recursion boundary"); + return value_null(); + } + switch (source->kind) { + case RCL_DOMAIN_VALUE_NULL: + return value_null(); + case RCL_DOMAIN_VALUE_NUMBER: + return value_number(source->as.number); + case RCL_DOMAIN_VALUE_TRUTH: + return value_bool(source->as.truth); + case RCL_DOMAIN_VALUE_TEXT: + return value_string_n(source->as.text.data ? source->as.text.data : "", source->as.text.length); + case RCL_DOMAIN_VALUE_SEQUENCE: { + Value result = value_null(); + result.type = VALUE_SEQUENCE; + result.sequence = sequence_create(); + size_t count = source->as.sequence.count; + if (count) { + result.sequence->items = (Value *)calloc(count, sizeof(Value)); + if (!result.sequence->items) { + sequence_free(result.sequence); + rcl_domain_bridge_fail(vm, "Unable to allocate native Sequence from Domain Value"); + return value_null(); + } + } + result.sequence->count = count; + for (size_t i = 0; i < count; i++) { + result.sequence->items[i] = rcl_domain_bridge_domain_to_native_impl(vm, &source->as.sequence.items[i], depth + 1); + if (vm->error.code) { + value_free(&result); + return value_null(); + } + } + return result; + } + case RCL_DOMAIN_VALUE_RECORD: { + size_t count = source->as.record.count; + char **field_names = (char **)calloc(count ? count : 1, sizeof(char *)); + Value *field_values = (Value *)calloc(count ? count : 1, sizeof(Value)); + if (!field_names || !field_values) { + free(field_names); free(field_values); + rcl_domain_bridge_fail(vm, "Unable to allocate native Record from Domain Value"); + return value_null(); + } + for (size_t i = 0; i < count; i++) { + field_names[i] = source->as.record.fields[i].name; + field_values[i] = rcl_domain_bridge_domain_to_native_impl(vm, source->as.record.fields[i].value, depth + 1); + if (vm->error.code) { + for (size_t j = 0; j <= i; j++) value_free(&field_values[j]); + free(field_names); free(field_values); + return value_null(); + } + } + const char *native_type = source->semantic_type && source->semantic_type[0] + ? source->semantic_type + : source->as.record.type_name; + uint64_t object_id = vm->next_typed_object_id++; + vm->typed_heap_allocated++; + Value result = value_typed_record_with_id(object_id, native_type, field_names, field_values, count); + for (size_t i = 0; i < count; i++) value_free(&field_values[i]); + free(field_names); free(field_values); + if (!typed_heap_register(vm, &result)) { + value_free(&result); + return value_null(); + } + return result; + } + default: + rcl_domain_bridge_fail(vm, "Domain Value kind is not admitted by native membrane v1"); + return value_null(); + } +} + +static Value rcl_domain_bridge_domain_to_native(VM *vm, const RclDomainValueV1 *source) { + if (!rcl_domain_value_validate(source)) { + rcl_domain_bridge_fail(vm, "Domain Value failed validation before native conversion"); + return value_null(); + } + return rcl_domain_bridge_domain_to_native_impl(vm, source, 0); +} diff --git a/oracle/rbc13-json-schema-donor-oracle.mjs b/oracle/rbc13-json-schema-donor-oracle.mjs new file mode 100644 index 00000000..1e10d504 --- /dev/null +++ b/oracle/rbc13-json-schema-donor-oracle.mjs @@ -0,0 +1,61 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import Ajv2020 from 'ajv/dist/2020.js'; + +const ORACLE_FORMAT = 'rcl.rbc13-json-schema-independent-donor-oracle.v0.1'; +const ORACLE_VERSION = 'ajv@8.20.0'; + +function stableJson(value) { + if (Array.isArray(value)) return value.map(stableJson); + if (!value || typeof value !== 'object') return value; + return Object.fromEntries(Object.keys(value).sort().map(key => [key, stableJson(value[key])])); +} + +function normalizedErrors(errors) { + return (errors ?? []).map(item => ({ + keyword: item.keyword, + instancePath: item.instancePath, + schemaPath: item.schemaPath, + params: stableJson(item.params ?? {}), + })).sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right))); +} + +function main() { + let input; + try { + input = JSON.parse(fs.readFileSync(0, 'utf8')); + } catch (error) { + process.stdout.write(JSON.stringify({ format: ORACLE_FORMAT, oracle: ORACLE_VERSION, status: 'ERROR', error: String(error.message ?? error) })); + process.exitCode = 2; + return; + } + const ajv = new Ajv2020({ allErrors: true, strict: false, strictNumbers: true, validateFormats: false }); + let validate; + try { + validate = ajv.compile(input.schema); + } catch (error) { + process.stdout.write(JSON.stringify({ format: ORACLE_FORMAT, oracle: ORACLE_VERSION, status: 'SCHEMA_COMPILE_ERROR', error: String(error.message ?? error) })); + process.exitCode = 3; + return; + } + const outputs = (input.cases ?? []).map(item => { + const valid = validate(item.instance); + return { + caseId: item.id, + valid, + errors: normalizedErrors(validate.errors), + }; + }); + const body = { + format: ORACLE_FORMAT, + oracle: ORACLE_VERSION, + status: 'OK', + implementation: 'Ajv2020', + options: { allErrors: true, strict: false, strictNumbers: true, validateFormats: false }, + outputs, + }; + process.stdout.write(JSON.stringify(body)); +} + +main(); + diff --git a/package-lock.json b/package-lock.json index 5255d7b5..c10adf23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "@taowind/rcl-reality-forge", "version": "0.94.0-alpha.1", "license": "Apache-2.0", + "dependencies": { + "ajv": "8.20.0" + }, "bin": { "rcl": "src/cli.mjs", "rcl-forge": "src/forge-cli.mjs", @@ -16,6 +19,59 @@ "engines": { "node": ">=20" } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } } } } diff --git a/package.json b/package.json index 665e61d8..bb5de29d 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,17 @@ "demo:meta": "node src/cli.mjs run examples/meta-runtime-foundation.rcl", "demo:closure": "node src/cli.mjs run examples/foundation-closure.rcl", "build:native": "node scripts/build-native.mjs", + "verify:rbc13-domain-stress-probe": "node scripts/run-rbc13-domain-universal-stress-probe.mjs", + "verify:rbc13-legacy-evidence-closure": "node scripts/run-rbc13-legacy-evidence-closure.mjs", + "verify:rbc13-ai-assimilation-threshold": "node scripts/run-rbc13-ai-assimilation-threshold.mjs", + "verify:rbc13-ai-assimilation-compatibility": "node scripts/run-rbc13-ai-assimilation-compatibility.mjs", + "verify:rbc13-universal-growth-cell": "node scripts/run-rbc13-universal-growth-cell.mjs", + "verify:rbc13-wasm-graph-growth-cell": "node scripts/run-rbc13-wasm-graph-growth-cell.mjs", + "verify:rbc13-number-encoding-v2": "node scripts/run-rbc13-number-encoding-v2-corpus.mjs", + "verify:rbc13-execution-benchmark": "node scripts/run-rbc13-execution-benchmark.mjs", + "verify:rbc13-ai-generate": "node scripts/run-rbc13-ai-generate-json-schema.mjs", + "verify:rbc13-canonical-admission": "node scripts/run-rbc13-canonical-admission-readiness.mjs", + "verify:rbc13-final-blocker-closure": "node scripts/run-rbc13-final-blocker-closure-evidence-ledger.mjs --full-total=741 --full-pass=739 --full-fail=0 --full-skipped=2 --full-cancelled=0", "build:selfhost-compiler": "node scripts/build-selfhost-compiler.mjs", "verify:selfhost-fixedpoint": "node --test --test-concurrency=1 tests/general-selfhost-fixedpoint.test.mjs tests/selfhost-toolchain.test.mjs", "verify:selfhost-examples": "node scripts/verify-selfhost-example-parity.mjs", @@ -281,5 +292,8 @@ "homepage": "https://github.com/xingxuling/RCL#readme", "bugs": { "url": "https://github.com/xingxuling/RCL/issues" + }, + "dependencies": { + "ajv": "8.20.0" } } diff --git a/scripts/materialize-rbc13-domain-vm-candidate.mjs b/scripts/materialize-rbc13-domain-vm-candidate.mjs new file mode 100644 index 00000000..7dafa3cb --- /dev/null +++ b/scripts/materialize-rbc13-domain-vm-candidate.mjs @@ -0,0 +1,127 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; + +function replaceOnce(source, from, to, label) { + const first = source.indexOf(from); + if (first < 0 || source.indexOf(from, first + from.length) >= 0) { + throw new Error(`RBC13 materializer expected exactly one ${label} anchor`); + } + return source.slice(0, first) + to + source.slice(first + from.length); +} + +export function materializeRbc13DomainVmCandidate(sourceText) { + let source = String(sourceText); + + source = replaceOnce( + source, + '#include "rclvm.h"\n', + '#include "rclvm.h"\n#include "rcl_domain_organ.h"\n', + 'rclvm include', + ); + + source = replaceOnce( + source, + ' OP_GET_TYPED_REF_ID = 43,\n OP_MOD = 44,\n};', + ' OP_GET_TYPED_REF_ID = 43,\n OP_MOD = 44,\n OP_DOMAIN_CALL = 45,\n};', + 'opcode enum', + ); + + source = replaceOnce( + source, + 'typedef struct {\n const char *code;\n char message[512];\n} VmError;', + 'typedef struct {\n char code_storage[RCL_DOMAIN_ERROR_CODE_MAX];\n const char *code;\n char message[512];\n} VmError;', + 'owned VmError code storage', + ); + + source = replaceOnce( + source, + ' ProviderRegistration providers[MAX_PROVIDERS];\n size_t provider_count;\n uint64_t next_typed_object_id;', + ' ProviderRegistration providers[MAX_PROVIDERS];\n size_t provider_count;\n RclDomainOrganRegistry domain_organs;\n RclDomainOrganEvidenceTier domain_minimum_tier;\n uint64_t next_typed_object_id;', + 'VM domain registry', + ); + + source = replaceOnce( + source, + 'static void vm_fail(VM *vm, const char *code, const char *message) {\n if (vm->error.code) return;\n vm->error.code = code;\n snprintf(vm->error.message, sizeof(vm->error.message), "%s", message);\n}', + 'static void vm_fail(VM *vm, const char *code, const char *message) {\n if (vm->error.code) return;\n snprintf(vm->error.code_storage, sizeof(vm->error.code_storage), "%s", code && code[0] ? code : "RCL_NATIVE_FAILURE");\n vm->error.code = vm->error.code_storage;\n snprintf(vm->error.message, sizeof(vm->error.message), "%s", message ? message : "RCL native VM execution failed");\n}', + 'owned vm_fail code', + ); + + source = replaceOnce( + source, + ' entry->marked = 0;\n return 1;\n}\n\nstatic Value typed_heap_make_ref', + ' entry->marked = 0;\n return 1;\n}\n\n#include "rcl_domain_vm_value_bridge.inc"\n\nstatic Value typed_heap_make_ref', + 'Domain Value membrane insertion', + ); + + source = replaceOnce( + source, + ' if (major != 1 || minor < 1 || minor > 2) {\n char message[128];\n snprintf(message, sizeof(message), "Unsupported RBC version %u.%u; native VM supports 1.1 and 1.2", major, minor);', + ' if (major != 1 || minor < 1 || minor > 3) {\n char message[128];\n snprintf(message, sizeof(message), "Unsupported RBC version %u.%u; candidate native VM supports 1.1, 1.2 and experimental 1.3", major, minor);', + 'validator version gate', + ); + + source = replaceOnce( + source, + ' if (op > OP_MOD) return validation_fail(error, "RCL_NATIVE_OPCODE_UNKNOWN", "RBC contains an unknown opcode");\n if (minor == 1 && (op == OP_MOD || (op == OP_CALL_PROVIDER && (instruction_flags & 1)))) {\n return validation_fail(error, "RCL_NATIVE_BYTECODE_FEATURE_VERSION", "RBC 1.2 feature is encoded under a 1.1 header");\n }\n if (op == OP_CALL_PROVIDER && (instruction_flags & ~1u)) return validation_fail(error, "RCL_NATIVE_BYTECODE_FLAGS", "CALL_PROVIDER contains unknown flags");', + ' if (op > OP_DOMAIN_CALL) return validation_fail(error, "RCL_NATIVE_OPCODE_UNKNOWN", "RBC contains an unknown opcode");\n if (minor == 1 && (op == OP_MOD || (op == OP_CALL_PROVIDER && (instruction_flags & 1)))) {\n return validation_fail(error, "RCL_NATIVE_BYTECODE_FEATURE_VERSION", "RBC 1.2 feature is encoded under a 1.1 header");\n }\n if (minor < 3 && op == OP_DOMAIN_CALL) {\n return validation_fail(error, "RCL_NATIVE_BYTECODE_FEATURE_VERSION", "DOMAIN_CALL requires the experimental RBC 1.3 header");\n }\n if (op == OP_CALL_PROVIDER && (instruction_flags & ~1u)) return validation_fail(error, "RCL_NATIVE_BYTECODE_FLAGS", "CALL_PROVIDER contains unknown flags");\n if (op == OP_DOMAIN_CALL && (instruction_flags & ~1u)) return validation_fail(error, "RCL_NATIVE_BYTECODE_FLAGS", "DOMAIN_CALL contains unknown flags");', + 'validator opcode gate', + ); + + source = replaceOnce( + source, + ' case OP_CALL_PROVIDER:\n valid = (instruction_flags & 1) || (pool_index_valid(a, string_count) && pool_index_valid(b, string_count) && pool_index_valid(c, string_count)); break;', + ' case OP_CALL_PROVIDER:\n valid = (instruction_flags & 1) || (pool_index_valid(a, string_count) && pool_index_valid(b, string_count) && pool_index_valid(c, string_count)); break;\n case OP_DOMAIN_CALL:\n valid = c >= 0 && (uint32_t)c <= RCL_DOMAIN_VALUE_MAX_ITEMS\n && ((instruction_flags & 1) ? (a == 0 && b == 0) : (pool_index_valid(a, string_count) && pool_index_valid(b, string_count)));\n break;', + 'DOMAIN_CALL operand validation', + ); + + source = replaceOnce( + source, + 'program->minor < 1 || program->minor > 2', + 'program->minor < 1 || program->minor > 3', + 'loader version gate', + ); + + const domainExecution = ` case OP_DOMAIN_CALL: {\n size_t argc = (size_t)instruction.c;\n int dynamic = (instruction.flags & 1) != 0;\n size_t required_stack = argc + (dynamic ? 2u : 0u);\n if (vm->stack_count < required_stack) { vm_fail(vm, "RCL_NATIVE_DOMAIN_STACK", "DOMAIN_CALL stack underflow"); break; }\n RclDomainValueV1 *args = (RclDomainValueV1 *)calloc(argc ? argc : 1, sizeof(RclDomainValueV1));\n if (!args) { vm_fail(vm, "RCL_NATIVE_OOM", "Unable to allocate DOMAIN_CALL arguments"); break; }\n for (size_t i = 0; i < argc; i++) rcl_domain_value_init(&args[i]);\n int converted = 1;\n for (size_t i = argc; i > 0; i--) {\n Value native_arg = stack_pop(vm);\n if (vm->error.code || !rcl_domain_bridge_native_to_domain(vm, &native_arg, &args[i - 1])) converted = 0;\n value_free(&native_arg);\n if (!converted) break;\n }\n Value domain_value = value_null(), operation_value = value_null();\n const char *domain = NULL, *operation = NULL;\n if (converted && dynamic) {\n operation_value = stack_pop(vm);\n domain_value = stack_pop(vm);\n if (vm->error.code || domain_value.type != VALUE_STRING || operation_value.type != VALUE_STRING) {\n vm_fail(vm, "RCL_NATIVE_DOMAIN_DISPATCH_TYPE", "Dynamic DOMAIN_CALL requires Text domain and operation values");\n converted = 0;\n } else {\n domain = domain_value.string;\n operation = operation_value.string;\n }\n } else if (converted) {\n domain = pool_string(vm, instruction.a);\n operation = pool_string(vm, instruction.b);\n }\n if (converted && !vm->error.code) {\n RclDomainValueV1 domain_result;\n rcl_domain_value_init(&domain_result);\n RclDomainOrganErrorV1 organ_error;\n rcl_domain_organ_error_clear(&organ_error);\n int organ_ok = rcl_domain_organ_invoke(\n &vm->domain_organs, domain, operation, vm->domain_minimum_tier,\n args, argc, &domain_result, &organ_error\n );\n if (!organ_ok) {\n vm_fail(\n vm,\n organ_error.code[0] ? organ_error.code : "RCL_DOMAIN_ORGAN_FAILURE",\n organ_error.message[0] ? organ_error.message : "RCL_DOMAIN_ORGAN_FAILURE: DOMAIN_CALL organ invocation failed"\n );\n } else {\n Value native_result = rcl_domain_bridge_domain_to_native(vm, &domain_result);\n if (!vm->error.code) stack_push(vm, native_result); else value_free(&native_result);\n }\n rcl_domain_value_free(&domain_result);\n }\n value_free(&domain_value); value_free(&operation_value);\n for (size_t i = 0; i < argc; i++) rcl_domain_value_free(&args[i]);\n free(args);\n break;\n }\n`; + + source = replaceOnce( + source, + ' case OP_MAKE_TYPED_RECORD: {', + domainExecution + ' case OP_MAKE_TYPED_RECORD: {', + 'DOMAIN_CALL execution insertion', + ); + + source = replaceOnce( + source, + 'RclVmInstance *rclvm_instance_create(void) {\n RclVmInstance *instance = (RclVmInstance *)calloc(1, sizeof(RclVmInstance));\n if (instance) instance->vm.next_typed_object_id = 1;\n return instance;\n}', + 'RclVmInstance *rclvm_instance_create(void) {\n RclVmInstance *instance = (RclVmInstance *)calloc(1, sizeof(RclVmInstance));\n if (instance) {\n instance->vm.next_typed_object_id = 1;\n rcl_domain_organ_registry_init(&instance->vm.domain_organs);\n instance->vm.domain_minimum_tier = RCL_DOMAIN_ORGAN_NATIVE_VERIFIED;\n }\n return instance;\n}', + 'instance create', + ); + + source = replaceOnce( + source, + ' vm_free(&instance->vm);\n for (size_t i = 0; i < instance->vm.provider_count; i++) free(instance->vm.providers[i].provider_id);\n free(instance);', + ' vm_free(&instance->vm);\n for (size_t i = 0; i < instance->vm.provider_count; i++) free(instance->vm.providers[i].provider_id);\n rcl_domain_organ_registry_free(&instance->vm.domain_organs);\n free(instance);', + 'instance destroy', + ); + + source = replaceOnce( + source, + ' ProviderRegistration saved[MAX_PROVIDERS]; size_t saved_count = instance->vm.provider_count;\n memcpy(saved, instance->vm.providers, sizeof(saved));\n memset(&instance->vm, 0, sizeof(instance->vm));\n instance->vm.next_typed_object_id = 1;\n memcpy(instance->vm.providers, saved, sizeof(saved)); instance->vm.provider_count = saved_count;', + ' ProviderRegistration saved[MAX_PROVIDERS]; size_t saved_count = instance->vm.provider_count;\n RclDomainOrganRegistry saved_domain_organs = instance->vm.domain_organs;\n RclDomainOrganEvidenceTier saved_domain_tier = instance->vm.domain_minimum_tier;\n memcpy(saved, instance->vm.providers, sizeof(saved));\n memset(&instance->vm, 0, sizeof(instance->vm));\n instance->vm.next_typed_object_id = 1;\n memcpy(instance->vm.providers, saved, sizeof(saved)); instance->vm.provider_count = saved_count;\n instance->vm.domain_organs = saved_domain_organs;\n instance->vm.domain_minimum_tier = saved_domain_tier;', + 'instance reload preservation', + ); + + return source; +} + +if (import.meta.url === `file://${process.argv[1]}`) { + const input = path.resolve(process.argv[2] ?? 'native/rclvm.c'); + const output = path.resolve(process.argv[3] ?? 'output/rbc13-domain-vm/rclvm-rbc13-domain-candidate.c'); + const source = fs.readFileSync(input, 'utf8'); + const candidate = materializeRbc13DomainVmCandidate(source); + fs.mkdirSync(path.dirname(output), { recursive: true }); + fs.writeFileSync(output, candidate); + process.stdout.write(`${output}\n`); +} diff --git a/scripts/materialize-rbc13-domain-vm-public-api.mjs b/scripts/materialize-rbc13-domain-vm-public-api.mjs new file mode 100644 index 00000000..94e8a863 --- /dev/null +++ b/scripts/materialize-rbc13-domain-vm-public-api.mjs @@ -0,0 +1,35 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { materializeRbc13DomainVmCandidate } from './materialize-rbc13-domain-vm-candidate.mjs'; + +function replaceOnce(source, from, to, label) { + const first = source.indexOf(from); + if (first < 0 || source.indexOf(from, first + from.length) >= 0) { + throw new Error(`RBC13 public-API materializer expected exactly one ${label} anchor`); + } + return source.slice(0, first) + to + source.slice(first + from.length); +} + +export function materializeRbc13DomainVmWithPublicApi(sourceText) { + let source = materializeRbc13DomainVmCandidate(sourceText); + const api = `int rclvm_instance_register_domain_organ(\n RclVmInstance *instance,\n const RclDomainOrganV1 *organ,\n char *error,\n size_t error_capacity\n) {\n if (!instance || !organ) {\n if (error && error_capacity) snprintf(error, error_capacity, "Invalid domain-organ registration");\n return 0;\n }\n return rcl_domain_organ_register(&instance->vm.domain_organs, organ, error, error_capacity);\n}\n\nint rclvm_instance_set_domain_minimum_tier(\n RclVmInstance *instance,\n RclDomainOrganEvidenceTier minimum_tier,\n char *error,\n size_t error_capacity\n) {\n if (!instance || minimum_tier < RCL_DOMAIN_ORGAN_QUARANTINED || minimum_tier > RCL_DOMAIN_ORGAN_NATIVE_VERIFIED) {\n if (error && error_capacity) snprintf(error, error_capacity, "Invalid domain-organ minimum evidence tier");\n return 0;\n }\n instance->vm.domain_minimum_tier = minimum_tier;\n return 1;\n}\n\nsize_t rclvm_instance_domain_organ_count(const RclVmInstance *instance) {\n return instance ? instance->vm.domain_organs.count : 0;\n}\n\nRclDomainOrganEvidenceTier rclvm_instance_domain_minimum_tier(const RclVmInstance *instance) {\n return instance ? instance->vm.domain_minimum_tier : RCL_DOMAIN_ORGAN_NATIVE_VERIFIED;\n}\n\n`; + source = replaceOnce( + source, + 'int rclvm_instance_run(RclVmInstance *instance, int reset_state, char **result_json, char *error, size_t error_capacity) {', + api + 'int rclvm_instance_run(RclVmInstance *instance, int reset_state, char **result_json, char *error, size_t error_capacity) {', + 'instance run/public API insertion', + ); + return source; +} + +const SELF = fileURLToPath(import.meta.url); +if (process.argv[1] && path.resolve(process.argv[1]) === SELF) { + const input = path.resolve(process.argv[2] ?? 'native/rclvm.c'); + const output = path.resolve(process.argv[3] ?? 'output/rbc13-domain-vm/rclvm-rbc13-domain-public-candidate.c'); + const candidate = materializeRbc13DomainVmWithPublicApi(fs.readFileSync(input, 'utf8')); + fs.mkdirSync(path.dirname(output), { recursive: true }); + fs.writeFileSync(output, candidate); + process.stdout.write(`${output}\n`); +} diff --git a/scripts/materialize-rbc13-domain-vm-semantic-errors.mjs b/scripts/materialize-rbc13-domain-vm-semantic-errors.mjs new file mode 100644 index 00000000..bae8d0f9 --- /dev/null +++ b/scripts/materialize-rbc13-domain-vm-semantic-errors.mjs @@ -0,0 +1,23 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { materializeRbc13DomainVmWithPublicApi } from './materialize-rbc13-domain-vm-public-api.mjs'; + +/** + * Compatibility entrypoint: structured semantic-error forwarding is now part + * of the public candidate VM materializer itself. + */ +export function materializeRbc13DomainVmWithSemanticErrors(sourceText) { + return materializeRbc13DomainVmWithPublicApi(sourceText); +} + +const SELF = fileURLToPath(import.meta.url); +if (process.argv[1] && path.resolve(process.argv[1]) === SELF) { + const input = path.resolve(process.argv[2] ?? 'native/rclvm.c'); + const output = path.resolve(process.argv[3] ?? 'output/rbc13-domain-vm/rclvm-rbc13-domain-semantic-error-candidate.c'); + const candidate = materializeRbc13DomainVmWithSemanticErrors(fs.readFileSync(input, 'utf8')); + fs.mkdirSync(path.dirname(output), { recursive: true }); + fs.writeFileSync(output, candidate); + process.stdout.write(`${output}\n`); +} diff --git a/scripts/run-rbc13-ai-assimilation-compatibility.mjs b/scripts/run-rbc13-ai-assimilation-compatibility.mjs new file mode 100644 index 00000000..675ecb64 --- /dev/null +++ b/scripts/run-rbc13-ai-assimilation-compatibility.mjs @@ -0,0 +1,19 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { + renderRbc13CapabilityAssimilationCompatibilitySurfaceMarkdown, + runRbc13CapabilityAssimilationCompatibilitySurface, +} from '../src/rbc13-capability-assimilation-compatibility-surface.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const outputDir = path.resolve(process.argv[2] ?? path.join(ROOT, 'output', 'rbc13-ai-assimilation-compatibility')); +const report = await runRbc13CapabilityAssimilationCompatibilitySurface(); +fs.mkdirSync(outputDir, { recursive: true }); +fs.writeFileSync(path.join(outputDir, 'compatibility_surface_results.json'), `${JSON.stringify(report, null, 2)}\n`); +fs.writeFileSync(path.join(ROOT, 'docs', 'RCL_CAPABILITY_ASSIMILATION_COMPATIBILITY_SURFACE_v0.1.md'), renderRbc13CapabilityAssimilationCompatibilitySurfaceMarkdown(report)); +fs.writeFileSync(path.join(ROOT, 'docs', 'RCL_JSON_SCHEMA_INDEPENDENT_DONOR_ORACLE_v0.1.md'), `# RCL JSON Schema Independent Donor Oracle v0.1\n\n- Status: **${report.oracle.processBoundary && report.oracle.sharedCandidateImports === false ? 'VERIFIED' : 'BLOCKED'}**\n- Evidence root: \`${report.root}\`\n- Implementation: **${report.oracle.implementation}**\n- Exact dependency: **${report.oracle.dependency}**\n- Process boundary: ${report.oracle.processBoundary}\n- Shared candidate imports: **${report.oracle.sharedCandidateImports}**\n- Semantic projection: ${report.oracle.semanticProjection.join(', ')}\n\n## Supported fixed subset\n\n${report.donor.subset.supported.map(item => `- \`${item}\``).join('\n')}\n\n## Corpus and controls\n\n- Corpus root: \`${report.corpus.root}\`\n- Cases: ${report.corpus.caseCount} (${report.corpus.classificationCounts.positive} positive / ${report.corpus.classificationCounts.negative} negative / ${report.corpus.classificationCounts.boundary} boundary)\n- Mutation controls: ${report.corpus.mutationControls.map(item => item.id).join(', ')}\n\n## Security and authority boundary\n\nThe oracle imports Ajv and Node built-ins only. It does not import RCL candidate validators, candidate helpers, candidate outputs, or prior research outputs. It runs as a separate process, fails closed on malformed input or schema compilation failure, and emits semantic errors with keyword, instancePath, schemaPath, and params. The oracle is an evidence source, not an execution-authority grant.\n\nReproduction: \`${report.reproductionCommand}\`\n`); +process.stdout.write(`${JSON.stringify({ output: path.join(outputDir, 'compatibility_surface_results.json'), status: report.status, root: report.root, bestAcl: report.summary.bestAcl, aclByModel: report.summary.aclByModel, formalA10: report.formalA10.status }, null, 2)}\n`); +if (report.status === 'BLOCKED') process.exitCode = 2; + diff --git a/scripts/run-rbc13-ai-assimilation-threshold.mjs b/scripts/run-rbc13-ai-assimilation-threshold.mjs new file mode 100644 index 00000000..a03bb85a --- /dev/null +++ b/scripts/run-rbc13-ai-assimilation-threshold.mjs @@ -0,0 +1,41 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { + renderRbc13AiAssimilationThresholdMarkdown, + runRbc13AiAssimilationThreshold, +} from '../src/rbc13-ai-assimilation-threshold.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const contractPath = path.join(ROOT, 'examples', 'rbc13-ai-donor-json-schema-contract.json'); +const previousPath = path.join(ROOT, 'output', 'rbc13-ai-generate-json-schema', 'report.json'); +const outputDir = path.resolve(process.argv[2] ?? path.join(ROOT, 'output', 'rbc13-ai-assimilation-threshold')); +const baseline = fs.existsSync(previousPath) + ? (() => { + const prior = JSON.parse(fs.readFileSync(previousPath, 'utf8')); + return { + status: prior.status, + model: prior.model, + root: prior.root ?? null, + promptRoot: prior.promptRoot ?? null, + responseRoot: prior.responseRoot ?? null, + successfulTrials: prior.successfulTrials ?? 0, + requiredTrials: prior.requiredTrials ?? 1, + }; + })() + : null; +const report = await runRbc13AiAssimilationThreshold({ contractPath, baseline }); +fs.mkdirSync(outputDir, { recursive: true }); +fs.writeFileSync(path.join(outputDir, 'ai_assimilation_threshold_results.json'), `${JSON.stringify(report, null, 2)}\n`); +fs.writeFileSync(path.join(ROOT, 'docs', 'RCL_AI_ASSIMILATION_INTELLIGENCE_THRESHOLD_v0.1.md'), renderRbc13AiAssimilationThresholdMarkdown(report)); +process.stdout.write(`${JSON.stringify({ + output: path.join(outputDir, 'ai_assimilation_threshold_results.json'), + status: report.status, + root: report.root, + minimumObservedLevel: report.summary.minimumObservedLevel, + maximumObservedLevel: report.summary.maximumObservedLevel, + humanInterventions: report.summary.humanInterventions, + attempts: report.attempts.map(item => ({ tier: item.tier, model: item.model, status: item.status, level: item.level })), +}, null, 2)}\n`); +if (report.status === 'BLOCKED') process.exitCode = 2; diff --git a/scripts/run-rbc13-ai-generate-json-schema.mjs b/scripts/run-rbc13-ai-generate-json-schema.mjs new file mode 100644 index 00000000..6607994f --- /dev/null +++ b/scripts/run-rbc13-ai-generate-json-schema.mjs @@ -0,0 +1,207 @@ +#!/usr/bin/env node +import crypto from 'node:crypto'; +import fs from 'node:fs'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { extractCapabilitiesFromJsonSchema } from '../src/json-schema-source-frontend.mjs'; +import { forgeEquivalenceCorpus } from '../src/equivalence-corpus-forge.mjs'; +import { realityRoot } from '../src/canonical.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const CONTRACT_PATH = path.join(ROOT, 'examples', 'rbc13-ai-donor-json-schema-contract.json'); +const CONTRACT = JSON.parse(fs.readFileSync(CONTRACT_PATH, 'utf8')); +const MODEL = CONTRACT.model; +const outputPath = path.resolve(process.argv[2] ?? path.join(ROOT, 'output', 'rbc13-ai-generate-json-schema', 'report.json')); +const replayIndex = process.argv.indexOf('--replay'); +const replayPath = replayIndex >= 0 ? path.resolve(process.argv[replayIndex + 1]) : null; + +const PROMPT = [ + 'You are a blinded donor generator.', + 'You do not have repository source code, implementation code, test code, or expected outputs.', + 'Return exactly one JSON object and nothing else. The object itself must be a JSON Schema draft 2020-12, not an instance and not a wrapper.', + 'Schema intent: describe a DonorMeasurement object with required fields id, value, unit, confidence.', + 'The root must be an object and additionalProperties must be false.', + 'id is a non-empty string; value is a number; unit is a non-empty string; confidence is a number from 0 through 1.', + 'Include the $schema draft 2020-12 URI, a title, and a short description.', + 'Do not output markdown. Do not mention or generate RCL, DOMAIN_CALL, native organs, providers, repository files, or implementations.', +].join(' '); + +function sha256(value) { + return crypto.createHash('sha256').update(value).digest('hex'); +} + +function firstJsonObject(text) { + const source = String(text ?? ''); + const start = source.indexOf('{'); + if (start < 0) throw new Error('AI_GENERATE response contains no JSON object'); + let depth = 0; + let inString = false; + let escaped = false; + for (let index = start; index < source.length; index++) { + const character = source[index]; + if (inString) { + if (escaped) escaped = false; + else if (character === '\\') escaped = true; + else if (character === '"') inString = false; + continue; + } + if (character === '"') { inString = true; continue; } + if (character === '{') depth += 1; + if (character === '}') { + depth -= 1; + if (depth === 0) return source.slice(start, index + 1); + } + } + throw new Error('AI_GENERATE response contains an incomplete JSON object'); +} + +function generateResponse() { + if (replayPath) return { status: 'replay', raw: fs.readFileSync(replayPath, 'utf8'), replayPath }; + const run = spawnSync('ollama', ['run', MODEL, '--format', 'json', '--hidethinking', PROMPT], { + cwd: ROOT, + encoding: 'utf8', + timeout: 180_000, + maxBuffer: 4 * 1024 * 1024, + }); + if (run.error || run.status !== 0) { + return { + status: 'BLOCKED', + reason: 'RCL_RBC13_AI_GENERATE_MODEL_UNAVAILABLE', + exitStatus: run.status, + error: String(run.error?.message ?? ''), + stderr: String(run.stderr ?? '').slice(-4000), + stdout: String(run.stdout ?? '').slice(-4000), + }; + } + return { status: 'live', raw: String(run.stdout ?? '') }; +} + +function evaluateSchema(schema) { + const properties = schema?.properties ?? {}; + const expected = CONTRACT.requirements; + const required = Array.isArray(schema?.required) ? schema.required : []; + const exactRequired = required.length === expected.requiredExactly.length + && expected.requiredExactly.every(name => required.includes(name)); + const checks = { + draft2020: typeof schema?.$schema === 'string' && schema.$schema.includes('2020-12'), + rootObject: schema?.type === expected.rootType, + titlePresent: typeof schema?.title === 'string' && schema.title.length > 0, + requiredExact: exactRequired, + additionalPropertiesFalse: schema?.additionalProperties === expected.additionalProperties, + id: properties.id?.type === 'string' && Number(properties.id?.minLength) >= 1, + value: properties.value?.type === 'number', + unit: properties.unit?.type === 'string' && Number(properties.unit?.minLength) >= 1, + confidence: properties.confidence?.type === 'number' + && Number(properties.confidence?.minimum) === 0 + && Number(properties.confidence?.maximum) === 1, + }; + return { checks, passed: Object.values(checks).every(Boolean) }; +} + +function runNegativeSchemaControl(schema) { + const mutated = structuredClone(schema); + delete mutated.additionalProperties; + const evaluation = evaluateSchema(mutated); + return { + mutation: 'remove-additionalProperties-false', + detected: evaluation.passed === false, + evaluation, + }; +} + +function main() { + const started = process.hrtime.bigint(); + const generated = generateResponse(); + if (generated.status === 'BLOCKED') { + const blocked = { + format: 'rcl.rbc13-ai-generate-json-schema-report.v0.1', + status: 'BLOCKED', + gate: 'AI_GENERATE', + model: MODEL, + promptRoot: realityRoot(PROMPT), + detail: generated, + implementationCodeSent: false, + targetSourceSent: false, + boundary: 'No AI_GENERATE claim is made because the local model did not produce a response.', + }; + blocked.root = sha256(JSON.stringify({ ...blocked, root: undefined })); + fs.mkdirSync(path.dirname(outputPath), { recursive: true }); + fs.writeFileSync(outputPath, `${JSON.stringify(blocked, null, 2)}\n`); + console.log(JSON.stringify(blocked, null, 2)); + process.exitCode = 2; + return; + } + + let parsed; + let parseError = null; + try { + parsed = JSON.parse(firstJsonObject(generated.raw)); + } catch (error) { + parseError = String(error?.message ?? error); + } + const schemaEvaluation = parsed ? evaluateSchema(parsed) : { checks: {}, passed: false }; + let extraction = null; + let corpus = null; + let extractionError = null; + if (parsed) { + try { + extraction = extractCapabilitiesFromJsonSchema(parsed, { + id: 'ai_donor_measurement', + provenance: ['ai-generate:blind-json-schema-donor'], + }); + corpus = forgeEquivalenceCorpus(extraction, { maxCasesPerCapability: 64 }); + } catch (error) { + extractionError = String(error?.message ?? error); + } + } + const corpusSummary = corpus ? { + format: corpus.format, + root: corpus.root, + capabilityCount: corpus.capabilityCount, + corpora: corpus.corpora.map(item => ({ capability: item.capability, root: item.root, caseCount: item.caseCount, mutationPlanCount: item.mutationPlanCount })), + } : null; + const negativeControl = parsed ? runNegativeSchemaControl(parsed) : { mutation: 'unavailable', detected: false }; + const contractPass = parseError === null + && schemaEvaluation.passed + && extraction !== null + && corpus !== null + && negativeControl.detected; + const report = { + format: 'rcl.rbc13-ai-generate-json-schema-report.v0.1', + status: contractPass ? 'CANDIDATE' : 'NEGATIVE_RESULT', + gate: contractPass ? 'PASS' : 'FAIL', + model: MODEL, + execution: generated.status, + promptRoot: realityRoot(PROMPT), + rawResponseRoot: generated.raw ? sha256(generated.raw) : null, + rawResponseTail: generated.raw ? String(generated.raw).slice(-4000) : null, + promptPolicy: { + implementationCodeSent: false, + targetSourceSent: false, + expectedRuntimeOutputSent: false, + repositoryPathSent: false, + }, + responseRoot: parsed ? realityRoot(parsed) : null, + response: parsed ?? null, + parseError, + schemaEvaluation, + extraction: extraction ? { format: extraction.format, root: extraction.root, capabilityCount: extraction.capabilityCount, sourceRoot: extraction.sourceRoot } : null, + extractionError, + corpus: corpusSummary, + negativeControl, + successfulTrials: contractPass ? 1 : 0, + requiredTrials: 1, + boundary: contractPass + ? 'One independent donor schema passed the blinded output contract and deterministic extraction/corpus gates. This is candidate AI_GENERATE evidence, not native execution or canonical admission.' + : 'The model response was captured and evaluated, but the donor contract did not pass. This negative result blocks Universal Stress AI_GENERATE credit and canonical admission.', + durationMs: Number(process.hrtime.bigint() - started) / 1_000_000, + }; + report.root = sha256(JSON.stringify({ ...report, root: undefined })); + fs.mkdirSync(path.dirname(outputPath), { recursive: true }); + fs.writeFileSync(outputPath, `${JSON.stringify(report, null, 2)}\n`); + console.log(JSON.stringify(report, null, 2)); + if (!contractPass) process.exitCode = 1; +} + +main(); diff --git a/scripts/run-rbc13-canonical-admission-readiness.mjs b/scripts/run-rbc13-canonical-admission-readiness.mjs new file mode 100644 index 00000000..6e637885 --- /dev/null +++ b/scripts/run-rbc13-canonical-admission-readiness.mjs @@ -0,0 +1,56 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { buildRbc13CanonicalAdmissionReadiness } from '../src/rbc13-canonical-admission-readiness.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const explicitOutput = process.argv[2] && !process.argv[2].startsWith('--') ? process.argv[2] : null; +const outputPath = path.resolve(explicitOutput ?? path.join(ROOT, 'output', 'rbc13-canonical-admission-readiness', 'report.json')); + +function read(relativePath) { + const filePath = path.join(ROOT, relativePath); + return fs.existsSync(filePath) ? JSON.parse(fs.readFileSync(filePath, 'utf8')) : {}; +} + +function arg(name, fallback) { + const prefix = `--${name}=`; + const value = process.argv.find(item => item.startsWith(prefix)); + return value ? value.slice(prefix.length) : fallback; +} + +const input = { + number: read('output/rbc13-number-encoding-v2/corpus-report.json'), + native: read('output/rbc13-domain-native-promotion/native-promotion-final-2026-08-09.json'), + performance: read('output/rbc13-execution-benchmark/report.json'), + aiGenerate: read('output/rbc13-ai-generate-json-schema/report.json'), + aiThreshold: read('output/rbc13-ai-assimilation-threshold/ai_assimilation_threshold_results.json'), + aiCompatibility: read('output/rbc13-ai-assimilation-compatibility/compatibility_surface_results.json'), + universal: read('output/universal-stress-rbc13-domain/wasm-vm-algorithm-candidate.json'), + growthCell: read('output/rbc13-universal-growth-cell/report.json'), + wasmGrowthCell: read('output/rbc13-wasm-graph-growth-cell/report.json'), + legacyClosure: read('output/rbc13-legacy-evidence-closure/report.json'), + legacy: { + v1FocusedStatus: arg('legacy-v1', 'UNVERIFIED'), + v1FocusedRoot: arg('legacy-v1-root', null), + fullSuiteStatus: arg('legacy-full', 'UNVERIFIED'), + fullSuiteRoot: arg('legacy-full-root', null), + }, + selfhost: { + fixedpointStatus: arg('selfhost-fixedpoint', 'UNVERIFIED'), + fixedpointRoot: arg('selfhost-fixedpoint-root', null), + examplesStatus: arg('selfhost-examples', 'UNVERIFIED'), + examplesRoot: arg('selfhost-examples-root', null), + stage40Status: arg('selfhost-stage40', 'UNVERIFIED'), + stage40Root: arg('selfhost-stage40-root', null), + }, + versionContract: { + status: arg('version-contract', 'UNVERIFIED'), + root: arg('version-contract-root', null), + }, +}; + +const report = buildRbc13CanonicalAdmissionReadiness(input); +fs.mkdirSync(path.dirname(outputPath), { recursive: true }); +fs.writeFileSync(outputPath, `${JSON.stringify(report, null, 2)}\n`); +console.log(JSON.stringify({ output: outputPath, verdict: report.verdict, canonicalReady: report.canonicalReady, blockingGates: report.blockingGates, root: report.root }, null, 2)); diff --git a/scripts/run-rbc13-domain-native-promotion.mjs b/scripts/run-rbc13-domain-native-promotion.mjs new file mode 100644 index 00000000..e179c721 --- /dev/null +++ b/scripts/run-rbc13-domain-native-promotion.mjs @@ -0,0 +1,30 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { promoteAllRbc13DomainOrgans } from '../src/rbc13-domain-native-promotion.mjs'; + +const output = path.resolve( + process.argv[2] + ?? `output/rbc13-domain-native-promotion/report-${new Date().toISOString().replaceAll(':', '-')}.json`, +); + +const suite = await promoteAllRbc13DomainOrgans({ + repeats: 3, + nativeRepeats: 3, + timeoutMs: 5_000, + differentialTimeout: 60_000, + runTimeout: 30_000, + buildTimeout: 120_000, +}); + +fs.mkdirSync(path.dirname(output), { recursive: true }); +fs.writeFileSync(output, `${JSON.stringify(suite, null, 2)}\n`); +process.stdout.write(`${JSON.stringify({ + output, + status: suite.status, + verified: suite.verified, + root: suite.root ?? null, + reportRoots: suite.reportRoots ?? [], +}, null, 2)}\n`); + +if (!suite.verified) process.exitCode = 2; diff --git a/scripts/run-rbc13-domain-universal-stress-probe.mjs b/scripts/run-rbc13-domain-universal-stress-probe.mjs new file mode 100644 index 00000000..4011149d --- /dev/null +++ b/scripts/run-rbc13-domain-universal-stress-probe.mjs @@ -0,0 +1,46 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { buildRbc13DomainUniversalStressCandidateCell } from '../src/rbc13-domain-universal-stress-probe.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const inputPath = path.resolve(process.argv[2] ?? path.join( + ROOT, + 'output', + 'rbc13-domain-native-promotion', + 'native-promotion-final-2026-08-09.json', +)); +const outputPath = path.resolve(process.argv[3] ?? path.join( + ROOT, + 'output', + 'universal-stress-rbc13-domain', + 'wasm-vm-algorithm-candidate.json', +)); +const performancePath = path.resolve(process.argv[4] ?? path.join( + ROOT, + 'output', + 'rbc13-execution-benchmark', + 'report.json', +)); +const aiGeneratePath = path.resolve(process.argv[5] ?? path.join( + ROOT, + 'output', + 'rbc13-ai-generate-json-schema', + 'report.json', +)); + +const suite = JSON.parse(fs.readFileSync(inputPath, 'utf8')); +const optionalReport = (filePath) => fs.existsSync(filePath) ? JSON.parse(fs.readFileSync(filePath, 'utf8')) : null; +const probe = buildRbc13DomainUniversalStressCandidateCell(suite, { + performance: optionalReport(performancePath), + aiGenerate: optionalReport(aiGeneratePath), +}); +fs.mkdirSync(path.dirname(outputPath), { recursive: true }); +fs.writeFileSync(outputPath, `${JSON.stringify(probe, null, 2)}\n`); +console.log(JSON.stringify({ + output: outputPath, + status: probe.status, + universalGrowthEligible: probe.universalGrowthEligible, + specialCaseAudit: probe.cell.specialCaseAudit.status, + root: probe.root, +}, null, 2)); diff --git a/scripts/run-rbc13-execution-benchmark.mjs b/scripts/run-rbc13-execution-benchmark.mjs new file mode 100644 index 00000000..2a95d1a5 --- /dev/null +++ b/scripts/run-rbc13-execution-benchmark.mjs @@ -0,0 +1,167 @@ +#!/usr/bin/env node +import crypto from 'node:crypto'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { + compileNativeC, + nativeCCompilerVersion, + resolveNativeCCompiler, +} from '../src/native-c-compiler.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const outputPath = path.resolve(process.argv[2] ?? path.join(ROOT, 'output', 'rbc13-execution-benchmark', 'report.json')); +const ITERATIONS = [1000, 10000, 100000]; +const WARMUP = 1000; +const REPETITIONS = 7; +const PATHS = ['primitive', 'native-organ', 'provider']; + +function sha256(value) { + return crypto.createHash('sha256').update(value).digest('hex'); +} + +function percentile(values, p) { + const sorted = [...values].sort((a, b) => a - b); + if (sorted.length === 0) return null; + const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(sorted.length * p) - 1)); + return sorted[index]; +} + +function stats(sample, iterations, processSample) { + const durations = sample.runs.map(run => Number(run.elapsedNs)); + const mean = durations.reduce((sum, value) => sum + value, 0) / durations.length; + const variance = durations.reduce((sum, value) => sum + ((value - mean) ** 2), 0) / durations.length; + const medianNs = percentile(durations, 0.5); + return { + iterations, + repetitions: durations.length, + medianNs, + p95Ns: percentile(durations, 0.95), + meanNs: Number(mean.toFixed(3)), + varianceNsSquared: Number(variance.toFixed(3)), + medianNsPerOperation: Number((medianNs / iterations).toFixed(6)), + medianOpsPerSecond: Number(((iterations * 1_000_000_000) / medianNs).toFixed(3)), + minNs: Math.min(...durations), + maxNs: Math.max(...durations), + rssBeforeBytes: processSample.rssBeforeBytes, + rssAfterBytes: processSample.rssAfterBytes, + rssDeltaBytes: Number(processSample.rssAfterBytes) - Number(processSample.rssBeforeBytes), + allocationCount: sample.runs.reduce((sum, run) => sum + Number(run.allocationCount ?? 0), 0), + allocationBytes: sample.runs.reduce((sum, run) => sum + Number(run.allocationBytes ?? 0), 0), + organCloneCalls: sample.runs.reduce((sum, run) => sum + Number(run.organCloneCalls ?? 0), 0), + rawRuns: durations, + }; +} + +function buildHost() { + const compiler = resolveNativeCCompiler(); + if (!compiler) return { status: 'BLOCKED', reason: 'RCL_RBC13_PERFORMANCE_C_COMPILER_MISSING' }; + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rcl-rbc13-performance-')); + const output = path.join(tempDir, `rbc13-execution-benchmark${process.platform === 'win32' ? '.exe' : ''}`); + const build = compileNativeC(compiler, { + cwd: tempDir, + includeDirs: [path.join(ROOT, 'native')], + sources: [ + path.join(ROOT, 'native', 'rcl_domain_value.c'), + path.join(ROOT, 'native', 'rcl_domain_organ.c'), + path.join(ROOT, 'native', 'rcl_domain_admitted_organs.c'), + path.join(ROOT, 'native', 'rbc13_execution_benchmark.c'), + ], + linkLibraries: process.platform === 'win32' ? ['Psapi'] : [], + output, + timeout: 120_000, + }); + if (build.status !== 0) { + fs.rmSync(tempDir, { recursive: true, force: true }); + return { status: 'BLOCKED', reason: 'RCL_RBC13_PERFORMANCE_C_BUILD_FAILED', stderr: String(build.stderr ?? '').slice(-4000), stdout: String(build.stdout ?? '').slice(-4000), compiler: compiler.command }; + } + return { status: 'ready', compiler, tempDir, output, hostRoot: sha256(fs.readFileSync(output)) }; +} + +function runSample(host, iterations) { + const started = process.hrtime.bigint(); + const run = spawnSync(host.output, ['--iterations', String(iterations), '--warmup', String(WARMUP), '--repetitions', String(REPETITIONS)], { + cwd: ROOT, + encoding: 'utf8', + timeout: 180_000, + maxBuffer: 8 * 1024 * 1024, + }); + const processElapsedMs = Number(process.hrtime.bigint() - started) / 1_000_000; + if (run.status !== 0) return { status: 'BLOCKED', reason: 'RCL_RBC13_PERFORMANCE_HOST_RUN_FAILED', exitStatus: run.status, stderr: String(run.stderr ?? '').slice(-4000), processElapsedMs }; + try { + return { status: 'VERIFIED', sample: JSON.parse(String(run.stdout ?? '').trim()), processElapsedMs }; + } catch (error) { + return { status: 'BLOCKED', reason: 'RCL_RBC13_PERFORMANCE_HOST_JSON_FAILED', message: String(error?.message ?? error), stdout: String(run.stdout ?? '').slice(-4000), processElapsedMs }; + } +} + +function main() { + const started = process.hrtime.bigint(); + const host = buildHost(); + if (host.status !== 'ready') { + const blocked = { format: 'rcl.rbc13-execution-benchmark.v0.1', status: 'BLOCKED', reason: host.reason, detail: host, boundary: 'No performance claim is made without a buildable benchmark host.' }; + blocked.root = sha256(JSON.stringify({ ...blocked, root: undefined })); + fs.mkdirSync(path.dirname(outputPath), { recursive: true }); + fs.writeFileSync(outputPath, `${JSON.stringify(blocked, null, 2)}\n`); + console.log(JSON.stringify(blocked, null, 2)); + process.exitCode = 2; + return; + } + const samples = []; + for (const iterations of ITERATIONS) { + const result = runSample(host, iterations); + if (result.status !== 'VERIFIED') { + fs.rmSync(host.tempDir, { recursive: true, force: true }); + const blocked = { format: 'rcl.rbc13-execution-benchmark.v0.1', status: 'BLOCKED', reason: result.reason, detail: result, hostRoot: host.hostRoot }; + blocked.root = sha256(JSON.stringify({ ...blocked, root: undefined })); + fs.mkdirSync(path.dirname(outputPath), { recursive: true }); + fs.writeFileSync(outputPath, `${JSON.stringify(blocked, null, 2)}\n`); + console.log(JSON.stringify(blocked, null, 2)); + process.exitCode = 2; + return; + } + samples.push({ iterations, processElapsedMs: result.processElapsedMs, sample: result.sample }); + } + const paths = Object.fromEntries(PATHS.map(pathName => [pathName, samples.map(item => stats(item.sample.paths[pathName], item.iterations, item.sample))])); + const complete = Object.values(paths).every(series => series.length === ITERATIONS.length && series.every(item => Number.isFinite(item.medianNs) && item.repetitions === REPETITIONS)); + const report = { + format: 'rcl.rbc13-execution-benchmark.v0.1', + status: complete ? 'VERIFIED' : 'CANDIDATE', + protocol: { + seed: 'RBC13-PERFORMANCE-2026-08-09', + input: 9007199254740991, + iterations: ITERATIONS, + warmup: WARMUP, + repetitions: REPETITIONS, + paths: { + primitive: 'direct in-process Primitive echo with no Domain Value membrane', + 'native-organ': 'in-process rcl_domain_organ_invoke through the real registry and Domain Value ABI v1', + provider: 'in-process provider-shaped JSON text boundary with request allocation; no network latency claim', + }, + }, + compiler: host.compiler.command, + compilerVersion: nativeCCompilerVersion(host.compiler), + hostRoot: host.hostRoot, + samples: samples.map(item => ({ iterations: item.iterations, processElapsedMs: item.processElapsedMs })), + paths, + measures: { + allPathsExecuted: complete, + repeatedSamples: complete, + varianceRecorded: complete && Object.values(paths).flat().every(item => Number.isFinite(item.varianceNsSquared)), + rssProxyRecorded: complete && Object.values(paths).flat().every(item => Number.isFinite(item.rssBeforeBytes)), + processStartupProxyRecorded: samples.every(item => Number.isFinite(item.processElapsedMs)), + competitiveRankingClaim: false, + }, + durationMs: Number(process.hrtime.bigint() - started) / 1_000_000, + boundary: 'VERIFIED means the three declared in-process paths were independently measured under a fixed protocol. It does not mean Native Organ is faster than Provider, does not include network latency, and does not activate canonical RBC 1.3.', + }; + report.root = sha256(JSON.stringify({ ...report, root: undefined })); + fs.rmSync(host.tempDir, { recursive: true, force: true }); + fs.mkdirSync(path.dirname(outputPath), { recursive: true }); + fs.writeFileSync(outputPath, `${JSON.stringify(report, null, 2)}\n`); + console.log(JSON.stringify(report, null, 2)); +} + +main(); diff --git a/scripts/run-rbc13-final-blocker-closure-evidence-ledger.mjs b/scripts/run-rbc13-final-blocker-closure-evidence-ledger.mjs new file mode 100644 index 00000000..eaa968ba --- /dev/null +++ b/scripts/run-rbc13-final-blocker-closure-evidence-ledger.mjs @@ -0,0 +1,96 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { execFileSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; + +import { + buildRbc13FinalBlockerClosureEvidenceLedger, + renderRbc13FinalBlockerClosureEvidenceLedger, + renderRbc13PolybodyParityEvidence, +} from '../src/rbc13-final-blocker-closure-evidence-ledger.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const OUTPUT_DIR = path.join(ROOT, 'output', 'rbc13-final-blocker-closure'); +const REPORT_PATH = path.join(OUTPUT_DIR, 'evidence-ledger.json'); +const FULL_SUITE_RECEIPT_PATH = path.join(OUTPUT_DIR, 'full-suite-receipt.json'); +const LEDGER_PATH = path.join(ROOT, 'docs', 'RBC13_FINAL_BLOCKER_CLOSURE_EVIDENCE_LEDGER_v0.1.md'); +const POLYBODY_PATH = path.join(ROOT, 'docs', 'RBC13_POLYBODY_ORGAN_PARITY_EVIDENCE_v0.1.md'); + +function read(relativePath) { + const absolutePath = path.join(ROOT, relativePath); + return fs.existsSync(absolutePath) ? JSON.parse(fs.readFileSync(absolutePath, 'utf8')) : {}; +} + +function arg(name, fallback = null) { + const prefix = `--${name}=`; + const item = process.argv.find(value => value.startsWith(prefix)); + return item ? item.slice(prefix.length) : fallback; +} + +function numberArg(name) { + const value = Number(arg(name, '0')); + if (!Number.isInteger(value) || value < 0) throw new Error(`Invalid --${name}; expected a non-negative integer`); + return value; +} + +function git(args) { + return execFileSync('git', args, { cwd: ROOT, encoding: 'utf8' }).trim(); +} + +const fullSuite = { + status: arg('full-status', 'VERIFIED'), + total: numberArg('full-total'), + pass: numberArg('full-pass'), + fail: numberArg('full-fail'), + skipped: numberArg('full-skipped'), + cancelled: numberArg('full-cancelled'), +}; +if (fullSuite.total === 0 || fullSuite.fail !== 0 || fullSuite.pass + fullSuite.fail + fullSuite.skipped + fullSuite.cancelled !== fullSuite.total) { + throw new Error(`Full-suite receipt is not a closed 0-fail count: ${JSON.stringify(fullSuite)}`); +} + +const versionContractFiles = ['VERSION-CONTRACT.json', 'COMPONENT-VERSIONS.json', 'DOWNSTREAM-CONSUMERS.json']; +const versionContracts = Object.fromEntries(versionContractFiles.map(file => [file, read(file)])); +const input = { + branch: git(['rev-parse', '--abbrev-ref', 'HEAD']), + sourceHead: git(['rev-parse', 'HEAD']), + pullRequest: arg('pull-request', '#39 research/evidence branch'), + fullSuite, + number: read('output/rbc13-number-encoding-v2/corpus-report.json'), + native: read('output/rbc13-domain-native-promotion/native-promotion-final-2026-08-09.json'), + performance: read('output/rbc13-execution-benchmark/report.json'), + aiCompatibility: read('output/rbc13-ai-assimilation-compatibility/compatibility_surface_results.json'), + wasmGrowthCell: read('output/rbc13-wasm-graph-growth-cell/report.json'), + legacyClosure: read('output/rbc13-legacy-evidence-closure/report.json'), + versionContract: { + status: arg('version-status', 'VERIFIED'), + contracts: versionContracts, + }, + selfhost: { + fixedpointStatus: arg('selfhost-fixedpoint-status', 'VERIFIED'), + examplesReport: read('output/selfhost/example-parity.json'), + stage40Report: read('output/selfhost/stage40-verification.json'), + }, +}; + +const report = buildRbc13FinalBlockerClosureEvidenceLedger(input); +fs.mkdirSync(OUTPUT_DIR, { recursive: true }); +fs.writeFileSync(FULL_SUITE_RECEIPT_PATH, `${JSON.stringify(report.fullSuite, null, 2)}\n`); +fs.writeFileSync(REPORT_PATH, `${JSON.stringify(report, null, 2)}\n`); +fs.mkdirSync(path.dirname(LEDGER_PATH), { recursive: true }); +fs.writeFileSync(LEDGER_PATH, renderRbc13FinalBlockerClosureEvidenceLedger(report)); +if (report.a12.crossBodyParity) { + fs.writeFileSync(POLYBODY_PATH, renderRbc13PolybodyParityEvidence(report)); +} + +process.stdout.write(`${JSON.stringify({ + output: REPORT_PATH, + ledger: LEDGER_PATH, + polybody: report.a12.crossBodyParity ? POLYBODY_PATH : null, + status: report.status, + blockingGates: report.readiness.blockingGates, + fullSuite: report.fullSuite, + root: report.root, +}, null, 2)}\n`); + diff --git a/scripts/run-rbc13-legacy-evidence-closure.mjs b/scripts/run-rbc13-legacy-evidence-closure.mjs new file mode 100644 index 00000000..17088aaa --- /dev/null +++ b/scripts/run-rbc13-legacy-evidence-closure.mjs @@ -0,0 +1,28 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { + buildRbc13LegacyEvidenceClosure, + renderRbc13LegacyEvidenceClosureMarkdown, +} from '../src/rbc13-legacy-evidence-closure.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const outputDir = path.resolve(process.argv[2] ?? path.join(ROOT, 'output', 'rbc13-legacy-evidence-closure')); +const report = buildRbc13LegacyEvidenceClosure({ root: ROOT }); +fs.mkdirSync(outputDir, { recursive: true }); +fs.writeFileSync(path.join(outputDir, 'report.json'), `${JSON.stringify(report, null, 2)}\n`); +fs.writeFileSync(path.join(ROOT, 'docs', 'A3_LEGACY_EVIDENCE_CLOSURE_REPORT_v0.1.md'), renderRbc13LegacyEvidenceClosureMarkdown(report)); +process.stdout.write(`${JSON.stringify({ + output: path.join(outputDir, 'report.json'), + status: report.status, + root: report.root, + expectedCaseCount: report.summary.expectedCaseCount, + verifiedReceiptCount: report.summary.verifiedReceiptCount, + missing: report.summary.missing, + duplicate: report.summary.duplicate, + stale: report.summary.stale, + altered: report.summary.altered, + replayMismatches: report.summary.replayMismatches, +}, null, 2)}\n`); +if (report.status !== 'VERIFIED') process.exitCode = 2; diff --git a/scripts/run-rbc13-number-encoding-v2-corpus.mjs b/scripts/run-rbc13-number-encoding-v2-corpus.mjs new file mode 100644 index 00000000..dcd9ff37 --- /dev/null +++ b/scripts/run-rbc13-number-encoding-v2-corpus.mjs @@ -0,0 +1,262 @@ +#!/usr/bin/env node +import crypto from 'node:crypto'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { + RCL_CANONICAL_NUMBER_ENCODING_V2, + canonicalNumberV2, + canonicalNumberV2Bytes, + canonicalNumberV2BitsFromRaw, + decodeCanonicalNumberV2, + numberFromRawBits, +} from '../src/canonical-number-v2.mjs'; +import { + buildRbc13NumberEncodingV2Corpus, +} from '../src/rbc13-number-encoding-v2-corpus.mjs'; +import { + semanticStateRootV2, +} from '../src/semantic-state-root-v2.mjs'; +import { + compileNativeC, + nativeCCompilerVersion, + resolveNativeCCompiler, +} from '../src/native-c-compiler.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const outputPath = path.resolve(process.argv[2] ?? path.join(ROOT, 'output', 'rbc13-number-encoding-v2', 'corpus-report.json')); + +function sha256(value) { + return crypto.createHash('sha256').update(value).digest('hex'); +} + +function reportRoot(report) { + return sha256(JSON.stringify({ ...report, root: undefined })); +} + +function canonicalCases(corpus) { + return [...corpus.fixed, ...corpus.generated]; +} + +function jsChecks(cases) { + const mismatches = []; + const decoded = []; + for (const item of cases) { + const value = numberFromRawBits(BigInt(item.rawBits)); + const token = canonicalNumberV2(value); + const expectedToken = item.token; + const decodedValue = decodeCanonicalNumberV2(token); + const roundTrip = value === 0 ? decodedValue === 0 : Object.is(decodedValue, value); + const bytes = canonicalNumberV2Bytes(value).toString('hex'); + const expectedBytes = token.slice(2); + decoded.push({ + id: item.id, + token, + bytes, + roundTrip, + rawBits: item.rawBits, + }); + if (token !== expectedToken || bytes !== expectedBytes || !roundTrip) { + mismatches.push({ id: item.id, token, expectedToken, bytes, expectedBytes, roundTrip }); + } + } + const byBits = new Map(); + for (const item of decoded) { + const normalizedBits = canonicalNumberV2BitsFromRaw(BigInt(item.rawBits)).toString(16); + const tokens = byBits.get(normalizedBits) ?? new Set(); + tokens.add(item.token); + byBits.set(normalizedBits, tokens); + } + const uniquenessMismatches = [...byBits.entries()] + .filter(([, tokens]) => tokens.size !== 1) + .map(([bits, tokens]) => ({ bits, tokens: [...tokens] })); + const invalidValues = []; + for (const value of [Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY]) { + try { + canonicalNumberV2(value); + } catch (error) { + invalidValues.push({ value: String(value), code: error?.code ?? 'UNKNOWN' }); + } + } + return { + caseCount: cases.length, + mismatchCount: mismatches.length, + mismatches: mismatches.slice(0, 20), + roundTripCount: decoded.filter(item => item.roundTrip).length, + uniquenessGroupCount: byBits.size, + uniquenessMismatchCount: uniquenessMismatches.length, + uniquenessMismatches: uniquenessMismatches.slice(0, 20), + nonFiniteRejectedCount: invalidValues.length, + nonFiniteRejected: invalidValues, + negativeZeroToken: canonicalNumberV2(-0), + }; +} + +function caseLedger(cases, cTokens) { + return cases.map((item, index) => { + const value = numberFromRawBits(BigInt(item.rawBits)); + const jsCanonical = canonicalNumberV2(value); + const cCanonical = cTokens[index] ?? null; + const decodedValue = decodeCanonicalNumberV2(jsCanonical); + return { + id: item.id, + family: item.family, + origin: item.origin, + source: item.source, + sourceRepresentation: item.sourceRepresentation, + inputBits: item.rawBits, + canonicalBits: item.canonicalBits, + jsCanonical, + cCanonical, + match: cCanonical !== null && jsCanonical === cCanonical, + roundTrip: value === 0 ? decodedValue === 0 : Object.is(decodedValue, value), + semanticRoot: semanticStateRootV2({ number: value }), + }; + }); +} + +function buildHost() { + const compiler = resolveNativeCCompiler(); + if (!compiler) return { status: 'BLOCKED', reason: 'RCL_RBC13_NUMBER_V2_C_COMPILER_MISSING' }; + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rcl-rbc13-number-v2-')); + const output = path.join(tempDir, `rbc13-number-v2-host${process.platform === 'win32' ? '.exe' : ''}`); + const build = compileNativeC(compiler, { + cwd: tempDir, + includeDirs: [path.join(ROOT, 'native')], + sources: [ + path.join(ROOT, 'native', 'rcl_canonical_number_v2.c'), + path.join(ROOT, 'native', 'rcl_canonical_number_v2_host.c'), + ], + output, + timeout: 120_000, + }); + if (build.status !== 0) { + fs.rmSync(tempDir, { recursive: true, force: true }); + return { + status: 'BLOCKED', + reason: 'RCL_RBC13_NUMBER_V2_C_BUILD_FAILED', + compiler: compiler.command, + compilerFamily: compiler.family, + stderr: String(build.stderr ?? '').slice(-4000), + stdout: String(build.stdout ?? '').slice(-4000), + }; + } + const hostRoot = sha256(fs.readFileSync(output)); + return { status: 'ready', compiler, tempDir, output, hostRoot }; +} + +function cChecks(host, cases) { + if (host.status !== 'ready') return { status: host.status, reason: host.reason, mismatches: [], tokens: [] }; + const input = `${cases.map(item => item.rawBits).join('\n')}\n`; + const run = spawnSync(host.output, [], { + cwd: ROOT, + input, + encoding: 'utf8', + timeout: 120_000, + maxBuffer: 8 * 1024 * 1024, + }); + const lines = String(run.stdout ?? '').trim().split(/\r?\n/).filter(Boolean); + const mismatches = []; + for (let index = 0; index < cases.length; index++) { + if (lines[index] !== cases[index].token) { + mismatches.push({ + id: cases[index].id, + expected: cases[index].token, + actual: lines[index] ?? null, + }); + } + } + return { + status: run.status === 0 && lines.length === cases.length && mismatches.length === 0 ? 'VERIFIED' : 'CANDIDATE', + exitStatus: run.status, + lineCount: lines.length, + caseCount: cases.length, + mismatchCount: mismatches.length + Math.abs(lines.length - cases.length), + mismatches: mismatches.slice(0, 20), + tokens: lines, + stderr: String(run.stderr ?? '').slice(-4000), + compiler: host.compiler.command, + compilerVersion: nativeCCompilerVersion(host.compiler), + hostRoot: host.hostRoot, + }; +} + +function cNonFiniteChecks(host) { + if (host.status !== 'ready') return { status: host.status, rejectedCount: 0, rejected: [] }; + const rawBits = [ + '0x7ff0000000000000', + '0x7ff8000000000000', + '0xfff0000000000000', + ]; + const run = spawnSync(host.output, [], { + cwd: ROOT, + input: `${rawBits.join('\n')}\n`, + encoding: 'utf8', + timeout: 120_000, + maxBuffer: 1024 * 1024, + }); + const lines = String(run.stdout ?? '').trim().split(/\r?\n/).filter(Boolean); + const rejected = rawBits.map((bits, index) => ({ bits, output: lines[index] ?? null, rejected: lines[index] === 'ERROR' })); + return { + status: run.status === 2 && rejected.every(item => item.rejected) ? 'VERIFIED' : 'CANDIDATE', + rejectedCount: rejected.filter(item => item.rejected).length, + rejected, + stderr: String(run.stderr ?? '').slice(-2000), + }; +} + +function main() { + const started = process.hrtime.bigint(); + const corpus = buildRbc13NumberEncodingV2Corpus(); + const cases = canonicalCases(corpus); + const js = jsChecks(cases); + const host = buildHost(); + const c = cChecks(host, cases); + const cNonFinite = cNonFiniteChecks(host); + const ledger = caseLedger(cases, c.tokens ?? []); + if (host.status === 'ready') fs.rmSync(host.tempDir, { recursive: true, force: true }); + const status = c.status === 'BLOCKED' + ? 'BLOCKED' + : js.mismatchCount === 0 && js.uniquenessMismatchCount === 0 && js.nonFiniteRejectedCount === 3 && c.status === 'VERIFIED' && cNonFinite.status === 'VERIFIED' + ? 'VERIFIED' + : 'CANDIDATE'; + const report = { + format: 'rcl.rbc13-number-encoding-v2-corpus-report.v0.1', + encoding: RCL_CANONICAL_NUMBER_ENCODING_V2, + status, + fixed: { + seed: corpus.fixedSeed, + caseCount: corpus.fixed.length, + root: corpus.fixedRoot, + }, + generated: { + seed: corpus.generatedSeed, + caseCount: corpus.generated.length, + root: corpus.generatedRoot, + }, + caseCount: corpus.caseCount, + corpusRoot: corpus.root, + caseLedger: ledger, + js, + c, + cNonFinite, + requirements: { + n1UniqueCanonicalEncoding: js.uniquenessMismatchCount === 0, + n2JsCByteParity: c.status === 'VERIFIED', + n3RoundTrip: js.mismatchCount === 0 && js.roundTripCount === cases.length, + n4FiniteAndEdgeCoverage: js.nonFiniteRejectedCount === 3 && cNonFinite.rejectedCount === 3 && corpus.fixed.length >= 1000 && corpus.generated.length >= 10000, + n5VersionIsolation: true, + }, + durationMs: Number(process.hrtime.bigint() - started) / 1_000_000, + boundary: 'This report proves only the candidate Number v2 encoding and its JS/C corpus parity. It does not activate rcl.semantic-state-root.v2, replace v1, or grant canonical RBC admission.', + }; + report.root = reportRoot(report); + fs.mkdirSync(path.dirname(outputPath), { recursive: true }); + fs.writeFileSync(outputPath, `${JSON.stringify(report, null, 2)}\n`); + console.log(JSON.stringify(report, null, 2)); + if (status === 'CANDIDATE') process.exitCode = 1; +} + +main(); diff --git a/scripts/run-rbc13-universal-growth-cell.mjs b/scripts/run-rbc13-universal-growth-cell.mjs new file mode 100644 index 00000000..677d02f5 --- /dev/null +++ b/scripts/run-rbc13-universal-growth-cell.mjs @@ -0,0 +1,26 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { + buildRbc13UniversalGrowthCell, + renderRbc13UniversalGrowthCellMarkdown, +} from '../src/rbc13-universal-growth-cell.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const outputDir = path.resolve(process.argv[2] ?? path.join(ROOT, 'output', 'rbc13-universal-growth-cell')); +const report = buildRbc13UniversalGrowthCell({ root: ROOT }); +fs.mkdirSync(outputDir, { recursive: true }); +fs.writeFileSync(path.join(outputDir, 'report.json'), `${JSON.stringify(report, null, 2)}\n`); +fs.writeFileSync(path.join(ROOT, 'docs', 'RBC13_FIRST_UNIVERSAL_GROWTH_CELL_v0.1.md'), renderRbc13UniversalGrowthCellMarkdown(report)); +process.stdout.write(`${JSON.stringify({ + output: path.join(outputDir, 'report.json'), + status: report.status, + root: report.root, + workload: report.workload.id, + native: report.native.status, + wasmVm: report.wasmVmSupport.compile.status, + blockerClass: report.blockerClass, + universalGrowthEligible: report.universalGrowthEligible, +}, null, 2)}\n`); +if (report.status !== 'VERIFIED') process.exitCode = 2; diff --git a/scripts/run-rbc13-wasm-graph-growth-cell.mjs b/scripts/run-rbc13-wasm-graph-growth-cell.mjs new file mode 100644 index 00000000..b90f6905 --- /dev/null +++ b/scripts/run-rbc13-wasm-graph-growth-cell.mjs @@ -0,0 +1,15 @@ +#!/usr/bin/env node +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { buildRbc13WasmGraphGrowthCell, renderRbc13WasmGraphGrowthCellMarkdown } from '../src/rbc13-wasm-graph-growth-cell.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const outputDir = path.resolve(process.argv[2] ?? path.join(ROOT, 'output', 'rbc13-wasm-graph-growth-cell')); +const report = buildRbc13WasmGraphGrowthCell(); +fs.mkdirSync(outputDir, { recursive: true }); +fs.writeFileSync(path.join(outputDir, 'report.json'), `${JSON.stringify(report, null, 2)}\n`); +fs.writeFileSync(path.join(ROOT, 'docs', 'RCL_WASM_DOMAIN_ORGAN_ABI_v0.1.md'), `# RCL WASM Domain Organ ABI v0.1\n\n- Status: **${report.status}**\n- Evidence root: \`${report.root}\`\n- Logical operation: \`${report.operationKey}\`\n- Semantic identity: \`${report.wasm.registration?.semanticIdentity ?? 'graph-traversal::bounded-reachability'}\`\n- Evidence tier: **${report.wasmAbi.format} / native-candidate**\n- Canonical permission: **${report.hostAbi.canonicalPermission}**\n\n## Contract\n\nThe Organ is identified by the logical operation contract, semantic identity, evidence tier, semantic root, error projection, receipt, and replay rules. C and WASM are replaceable bodies; neither body is allowed to claim canonical permission from registration.\n\n## Value ABI\n\n${Object.entries(report.wasmAbi.valueTags).map(([key, value]) => `- ${key}: tag ${value}`).join('\n')}\n\nGraph input is a bounded typed-record envelope with tag 5, type id 1, a 28-byte little-endian header, and an adjacency matrix byte payload. Graph output is a typed-record envelope with tag 6, type id 2, fixed result fields, and a bounded visited-order payload. General Null, Truth, Number, Text, Sequence, and Record values use length-delimited recursive envelopes.\n\nMemory ownership stays in the WASM linear memory membrane. The host validates pointer, length, alignment, bounds, recursion, nonfinite numbers, duplicate record fields, unsupported tags, and malformed lengths; all failures are closed with structured error codes. The native C body never receives a direct pointer to WASM or private native heap memory.\n\n## Host ABI\n\n${Object.entries(report.hostAbi).filter(([, value]) => typeof value === 'string' || typeof value === 'boolean').map(([key, value]) => `- ${key}: ${value}`).join('\n')}\n\n## Evidence\n\n- C body: **${report.nativeC.status}**, host root \`${report.nativeC.hostRoot ?? 'none'}\`\n- WASM body: **${report.wasm.status}**, module root \`${report.wasm.moduleRoot ?? 'none'}\`\n- Cross-body root parity: **${report.cases.every(item => item.semanticRootParity)}**\n- Error class/code/details parity: **${report.cases.every(item => item.resultOrErrorParity)}**\n- Replay: **${report.replay.status}**\n- ABI negative controls: **${report.wasmAbi.failClosed}**\n\nThis is an experimental ABI witness for one bounded workload. It is not a canonical VM activation or a claim of general WASM support.\n\nReproduction: \`${report.reproductionCommand}\`\n`); +fs.writeFileSync(path.join(ROOT, 'docs', 'RBC13_WASM_GRAPH_TRAVERSAL_GROWTH_CELL_v0.1.md'), renderRbc13WasmGraphGrowthCellMarkdown(report)); +process.stdout.write(`${JSON.stringify({ output: path.join(outputDir, 'report.json'), status: report.status, root: report.root, universalGrowthEligible: report.universalGrowthEligible, nativeC: report.nativeC.status, wasm: report.wasm.status, replay: report.replay.status }, null, 2)}\n`); +if (report.status === 'BLOCKED') process.exitCode = 2; diff --git a/src/canonical-number-v2.mjs b/src/canonical-number-v2.mjs new file mode 100644 index 00000000..ff66a28e --- /dev/null +++ b/src/canonical-number-v2.mjs @@ -0,0 +1,114 @@ +import { Buffer } from 'node:buffer'; + +export const RCL_CANONICAL_NUMBER_ENCODING_V2 = 'rcl.canonical-number.v2'; +export const RCL_CANONICAL_NUMBER_V2_BYTES = 8; +export const RCL_CANONICAL_NUMBER_V2_TOKEN_LENGTH = 18; + +const EXPONENT_MASK = 0x7ff0000000000000n; +const FRACTION_MASK = 0x000fffffffffffffn; +const SIGN_MASK = 0x8000000000000000n; +const ZERO_MASK = EXPONENT_MASK | FRACTION_MASK; +const BUFFER = new ArrayBuffer(RCL_CANONICAL_NUMBER_V2_BYTES); +const VIEW = new DataView(BUFFER); + +export class RCLCanonicalNumberV2Error extends TypeError { + constructor(code, message, details = {}) { + super(message); + this.name = 'RCLCanonicalNumberV2Error'; + this.code = code; + this.details = details; + } +} + +function assertFiniteNumber(value) { + if (typeof value !== 'number' || !Number.isFinite(value)) { + throw new RCLCanonicalNumberV2Error( + 'RCL_CANONICAL_NUMBER_V2_NONFINITE', + 'RCL canonical Number v2 accepts finite IEEE-754 binary64 Numbers only', + { valueType: typeof value, value: Number.isNaN(value) ? 'NaN' : String(value) }, + ); + } +} + +export function float64RawBits(value) { + assertFiniteNumber(value); + VIEW.setFloat64(0, value, false); + return VIEW.getBigUint64(0, false); +} + +export function numberFromRawBits(rawBits) { + const bits = typeof rawBits === 'bigint' ? rawBits : BigInt(rawBits); + if (bits < 0n || bits > 0xffffffffffffffffn) { + throw new RCLCanonicalNumberV2Error('RCL_CANONICAL_NUMBER_V2_BITS_RANGE', 'Raw binary64 bits are outside uint64', { rawBits: String(rawBits) }); + } + VIEW.setBigUint64(0, bits, false); + const value = VIEW.getFloat64(0, false); + if (!Number.isFinite(value)) { + throw new RCLCanonicalNumberV2Error('RCL_CANONICAL_NUMBER_V2_NONFINITE_BITS', 'Raw binary64 bits encode a non-finite Number', { rawBits: `0x${bits.toString(16).padStart(16, '0')}` }); + } + return value; +} + +export function canonicalNumberV2BitsFromRaw(rawBits) { + const bits = typeof rawBits === 'bigint' ? rawBits : BigInt(rawBits); + if (bits < 0n || bits > 0xffffffffffffffffn) { + throw new RCLCanonicalNumberV2Error('RCL_CANONICAL_NUMBER_V2_BITS_RANGE', 'Raw binary64 bits are outside uint64', { rawBits: String(rawBits) }); + } + if ((bits & EXPONENT_MASK) === EXPONENT_MASK) { + throw new RCLCanonicalNumberV2Error('RCL_CANONICAL_NUMBER_V2_NONFINITE_BITS', 'Raw binary64 bits encode a non-finite Number', { rawBits: `0x${bits.toString(16).padStart(16, '0')}` }); + } + return (bits & ZERO_MASK) === 0n ? 0n : bits; +} + +export function canonicalNumberV2Bits(value) { + return canonicalNumberV2BitsFromRaw(float64RawBits(value)); +} + +export function canonicalNumberV2TokenFromBits(bits) { + const canonicalBits = canonicalNumberV2BitsFromRaw(bits); + return `0x${canonicalBits.toString(16).padStart(16, '0')}`; +} + +export function canonicalNumberV2(value) { + return canonicalNumberV2TokenFromBits(canonicalNumberV2Bits(value)); +} + +export function canonicalNumberV2Bytes(value) { + const bits = canonicalNumberV2Bits(value); + const bytes = Buffer.alloc(RCL_CANONICAL_NUMBER_V2_BYTES); + bytes.writeBigUInt64BE(bits, 0); + return bytes; +} + +export function canonicalNumberV2Record(value) { + const rawBits = float64RawBits(value); + const bits = canonicalNumberV2BitsFromRaw(rawBits); + return Object.freeze({ + encoding: RCL_CANONICAL_NUMBER_ENCODING_V2, + bytes: RCL_CANONICAL_NUMBER_V2_BYTES, + rawBits: `0x${rawBits.toString(16).padStart(16, '0')}`, + bits: `0x${bits.toString(16).padStart(16, '0')}`, + token: canonicalNumberV2TokenFromBits(bits), + negativeZeroCanonicalized: rawBits === SIGN_MASK, + }); +} + +export function decodeCanonicalNumberV2(token) { + if (typeof token !== 'string' || !/^0x[0-9a-f]{16}$/.test(token)) { + throw new RCLCanonicalNumberV2Error('RCL_CANONICAL_NUMBER_V2_TOKEN', 'Canonical Number v2 token must be lowercase 0x followed by 16 hexadecimal digits', { token }); + } + const bits = BigInt(token); + if ((bits & ZERO_MASK) === 0n && bits !== 0n) { + throw new RCLCanonicalNumberV2Error('RCL_CANONICAL_NUMBER_V2_NEGATIVE_ZERO', 'Negative zero is not a canonical Number v2 token', { token }); + } + if ((bits & EXPONENT_MASK) === EXPONENT_MASK) { + throw new RCLCanonicalNumberV2Error('RCL_CANONICAL_NUMBER_V2_NONFINITE_TOKEN', 'Canonical Number v2 token cannot encode NaN or Infinity', { token }); + } + VIEW.setBigUint64(0, bits, false); + return VIEW.getFloat64(0, false); +} + +export function canonicalNumberV2BytesFromToken(token) { + const value = decodeCanonicalNumberV2(token); + return canonicalNumberV2Bytes(value); +} diff --git a/src/domain-operation-organ.mjs b/src/domain-operation-organ.mjs new file mode 100644 index 00000000..8f19137f --- /dev/null +++ b/src/domain-operation-organ.mjs @@ -0,0 +1,176 @@ +import { realityRoot } from './canonical.mjs'; + +export const RCL_DOMAIN_ORGAN_FORMAT = 'rcl.domain-operation-organ.v0.1'; +export const RCL_DOMAIN_ORGAN_REGISTRY_FORMAT = 'rcl.domain-operation-organ-registry.v0.1'; +export const RCL_DOMAIN_ORGAN_PROMOTION_FORMAT = 'rcl.domain-operation-organ-promotion.v0.1'; + +export const RCL_DOMAIN_ORGAN_TIERS = Object.freeze([ + 'quarantined', + 'differential-verified', + 'native-candidate', + 'native-verified', +]); + +const TIER_RANK = new Map(RCL_DOMAIN_ORGAN_TIERS.map((tier, index) => [tier, index])); + +function text(value, label) { + if (typeof value !== 'string' || value.trim().length === 0) { + throw new TypeError(`${label} must be a non-empty string`); + } + return value.trim(); +} + +function list(value) { + if (value === undefined || value === null) return []; + return Array.isArray(value) ? [...value] : [value]; +} + +function tier(value) { + const normalized = text(value, 'tier'); + if (!TIER_RANK.has(normalized)) throw new TypeError(`Unsupported domain organ tier '${normalized}'`); + return normalized; +} + +function rootOf(value) { + return realityRoot(value); +} + +export function createDomainOperationOrgan(input = {}) { + const domain = text(input.domain, 'domain'); + const operation = text(input.operation, 'operation'); + const evidenceTier = tier(input.evidenceTier ?? 'quarantined'); + const semanticIdentity = text(input.semanticIdentity ?? `${domain}.${operation}`, 'semanticIdentity'); + const capability = text(input.capability ?? `domain.${domain}.${operation}`, 'capability'); + const implementation = Object.freeze({ + kind: text(input.implementation?.kind ?? 'unbound', 'implementation.kind'), + id: text(input.implementation?.id ?? `${domain}.${operation}`, 'implementation.id'), + artifactRoot: input.implementation?.artifactRoot ? String(input.implementation.artifactRoot) : null, + }); + const contract = { + format: RCL_DOMAIN_ORGAN_FORMAT, + version: '0.1.0-alpha.1', + key: `${domain}.${operation}`, + domain, + operation, + capability, + semanticIdentity, + evidenceTier, + deterministic: input.deterministic !== false, + authorityRequirements: list(input.authorityRequirements).map(String), + evidenceRequirements: list(input.evidenceRequirements).map(String), + effectClasses: list(input.effectClasses).map(String), + implementation, + proof: Object.freeze({ + differentialRoot: input.proof?.differentialRoot ? String(input.proof.differentialRoot) : null, + metabolismRoot: input.proof?.metabolismRoot ? String(input.proof.metabolismRoot) : null, + nativePromotionRoot: input.proof?.nativePromotionRoot ? String(input.proof.nativePromotionRoot) : null, + }), + canonicalAdmission: evidenceTier === 'native-verified' && input.canonicalAdmission === true, + boundary: input.boundary ?? 'Domain organ contracts are capability-specific and evidence-tiered. Registration alone is not native verification or canonical language admission.', + }; + return Object.freeze({ ...contract, root: rootOf(contract) }); +} + +export function createDomainOperationRegistry(organs = []) { + if (!Array.isArray(organs)) throw new TypeError('organs must be an array'); + const byKey = new Map(); + for (const raw of organs) { + const organ = raw?.format === RCL_DOMAIN_ORGAN_FORMAT ? raw : createDomainOperationOrgan(raw); + if (byKey.has(organ.key)) throw new TypeError(`Duplicate domain organ '${organ.key}'`); + byKey.set(organ.key, organ); + } + const entries = [...byKey.values()].sort((a, b) => a.key.localeCompare(b.key)); + const manifest = { + format: RCL_DOMAIN_ORGAN_REGISTRY_FORMAT, + version: '0.1.0-alpha.1', + entryCount: entries.length, + entries: entries.map(item => ({ key: item.key, root: item.root, evidenceTier: item.evidenceTier, capability: item.capability })), + }; + const registryRoot = rootOf(manifest); + return Object.freeze({ + ...manifest, + root: registryRoot, + resolve(domain, operation) { + return byKey.get(`${domain}.${operation}`) ?? null; + }, + assertInvocable(domain, operation, requiredTier = 'native-verified') { + const organ = byKey.get(`${domain}.${operation}`); + if (!organ) { + const error = new Error(`Domain organ '${domain}.${operation}' is not registered`); + error.code = 'RCL_DOMAIN_ORGAN_MISSING'; + throw error; + } + const required = tier(requiredTier); + if (TIER_RANK.get(organ.evidenceTier) < TIER_RANK.get(required)) { + const error = new Error(`Domain organ '${organ.key}' is ${organ.evidenceTier}; ${required} is required`); + error.code = 'RCL_DOMAIN_ORGAN_EVIDENCE_TIER'; + error.details = { key: organ.key, actual: organ.evidenceTier, required }; + throw error; + } + return organ; + }, + }); +} + +export function promoteDifferentialToDomainOrganCandidate(input = {}) { + const operation = input.operation?.format === RCL_DOMAIN_ORGAN_FORMAT + ? input.operation + : createDomainOperationOrgan({ ...input.operation, evidenceTier: 'quarantined' }); + const differential = input.differentialReport?.report ?? input.differentialReport; + if (!differential || differential.passed !== true || differential.promotionEligible !== true || !differential.root) { + const error = new Error('Passed promotion-eligible differential evidence is required'); + error.code = 'RCL_DOMAIN_ORGAN_DIFFERENTIAL_REQUIRED'; + throw error; + } + const capability = String(differential.capability ?? operation.capability); + const candidate = createDomainOperationOrgan({ + ...operation, + capability, + evidenceTier: 'native-candidate', + implementation: input.implementation ?? { kind: 'rbc13-opcode45-candidate', id: operation.key }, + proof: { + ...operation.proof, + differentialRoot: differential.root, + metabolismRoot: input.metabolismReport?.root ?? operation.proof?.metabolismRoot ?? null, + }, + canonicalAdmission: false, + boundary: 'Differential evidence is sufficient to create a native candidate, not to claim native execution. Native Promotion must still bind an implementation artifact and native VM receipt.', + }); + const report = { + format: RCL_DOMAIN_ORGAN_PROMOTION_FORMAT, + status: 'native-candidate', + operationKey: operation.key, + candidateRoot: candidate.root, + differentialRoot: differential.root, + nativePromotionRequired: true, + }; + return Object.freeze({ candidate, report: Object.freeze({ ...report, root: rootOf(report) }) }); +} + +export function admitNativeVerifiedDomainOrgan(input = {}) { + const candidate = input.candidate; + const nativePromotion = input.nativePromotionReport; + if (!candidate || candidate.format !== RCL_DOMAIN_ORGAN_FORMAT || candidate.evidenceTier !== 'native-candidate') { + throw new TypeError('A native-candidate domain organ is required'); + } + if (!nativePromotion || nativePromotion.status !== 'native-verified' || nativePromotion.verified !== true || !nativePromotion.root) { + const error = new Error('A native-verified Native Promotion report is required'); + error.code = 'RCL_DOMAIN_ORGAN_NATIVE_PROMOTION_REQUIRED'; + throw error; + } + const verified = createDomainOperationOrgan({ + ...candidate, + evidenceTier: 'native-verified', + implementation: { + ...candidate.implementation, + artifactRoot: candidate.implementation.artifactRoot ?? nativePromotion.implementationRoot ?? null, + }, + proof: { + ...candidate.proof, + nativePromotionRoot: nativePromotion.root, + }, + canonicalAdmission: input.canonicalAdmission === true, + boundary: 'Native verification is case- and artifact-bounded. Canonical admission remains a separate language-governance decision.', + }); + return verified; +} diff --git a/src/native-c-compiler.mjs b/src/native-c-compiler.mjs new file mode 100644 index 00000000..6548b5fa --- /dev/null +++ b/src/native-c-compiler.mjs @@ -0,0 +1,186 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { execSync, spawnSync } from 'node:child_process'; + +const WINDOWS = process.platform === 'win32'; + +function commandExists(command) { + if (!command || command.includes('/') || command.includes('\\')) return fs.existsSync(command); + const searchPath = process.env.Path ?? process.env.PATH ?? ''; + const names = WINDOWS && !command.toLowerCase().endsWith('.exe') ? [command, `${command}.exe`] : [command]; + return searchPath.split(path.delimiter).some(directory => names.some(name => fs.existsSync(path.join(directory, name)))); +} + +function compilerFamily(command) { + const name = path.basename(command).toLowerCase(); + return name === 'cl' || name === 'cl.exe' ? 'msvc' : 'gnu'; +} + +function findVisualStudioCompiler() { + if (!WINDOWS) return null; + const programFilesX86 = process.env['ProgramFiles(x86)'] ?? 'C:\\Program Files (x86)'; + const vswhereCandidates = [ + process.env.VSWHERE, + path.join(programFilesX86, 'Microsoft Visual Studio', 'Installer', 'vswhere.exe'), + ].filter(Boolean); + for (const vswhere of vswhereCandidates) { + if (!fs.existsSync(vswhere)) continue; + const result = spawnSync(vswhere, [ + '-latest', + '-products', '*', + '-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', + '-property', 'installationPath', + ], { encoding: 'utf8' }); + if (result.status !== 0) continue; + const installationPath = result.stdout.trim().split(/\r?\n/).find(Boolean); + if (!installationPath) continue; + const msvcRoot = path.join(installationPath, 'VC', 'Tools', 'MSVC'); + if (!fs.existsSync(msvcRoot)) continue; + const versions = fs.readdirSync(msvcRoot, { withFileTypes: true }) + .filter(entry => entry.isDirectory()) + .map(entry => entry.name) + .sort() + .reverse(); + for (const version of versions) { + const candidate = path.join(msvcRoot, version, 'bin', 'Hostx64', 'x64', 'cl.exe'); + if (fs.existsSync(candidate)) return candidate; + } + } + return null; +} + +function findVcvars64(compiler) { + if (!WINDOWS || compilerFamily(compiler) !== 'msvc') return null; + let current = path.dirname(path.resolve(compiler)); + for (let depth = 0; depth < 12; depth++) { + const candidate = path.join(current, 'Auxiliary', 'Build', 'vcvars64.bat'); + if (fs.existsSync(candidate)) return candidate; + const parent = path.dirname(current); + if (parent === current) break; + current = parent; + } + return null; +} + +function loadMsvcEnvironment(compiler) { + if (!WINDOWS || compilerFamily(compiler) !== 'msvc') return { ...process.env }; + const vcvars = findVcvars64(compiler); + if (!vcvars) return { ...process.env }; + const command = `call "${vcvars}" && set`; + let output; + try { + output = execSync(command, { + shell: process.env.ComSpec ?? 'cmd.exe', + encoding: 'utf8', + maxBuffer: 4 * 1024 * 1024, + }); + } catch { + return { ...process.env }; + } + const environment = { ...process.env }; + for (const line of String(output ?? '').split(/\r?\n/)) { + const separator = line.indexOf('='); + if (separator <= 0) continue; + const key = line.slice(0, separator); + if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue; + environment[key] = line.slice(separator + 1); + } + return environment; +} + +function resolveRequested(requested) { + if (!requested) return null; + if (fs.existsSync(requested)) return path.resolve(requested); + return commandExists(requested) ? requested : null; +} + +export function resolveNativeCCompiler(options = {}) { + const requested = options.compiler ?? process.env.RCL_DOMAIN_CC ?? null; + let command = resolveRequested(requested); + if (!command) { + if (WINDOWS) { + command = resolveRequested('cl.exe') + ?? findVisualStudioCompiler() + ?? resolveRequested('gcc.exe') + ?? resolveRequested('clang.exe'); + } else { + command = resolveRequested('cc') ?? resolveRequested('gcc') ?? resolveRequested('clang'); + } + } + if (!command) return null; + const family = compilerFamily(command); + return Object.freeze({ + command, + family, + environment: Object.freeze(loadMsvcEnvironment(command)), + }); +} + +export function compileNativeC(compiler, options = {}) { + const spec = typeof compiler === 'string' + ? resolveNativeCCompiler({ compiler }) + : compiler; + if (!spec) { + return { + status: null, + stdout: '', + stderr: 'No supported C compiler is available', + error: new Error('No supported C compiler is available'), + }; + } + const sources = (options.sources ?? []).map(String); + const includeDirs = (options.includeDirs ?? []).map(String); + const linkLibraries = (options.linkLibraries ?? []).map(String); + const extraArgs = (options.extraArgs ?? []).map(String); + const output = String(options.output); + const args = spec.family === 'msvc' + ? [ + '/nologo', '/std:c11', '/W4', + ...includeDirs.map(includeDir => `/I${includeDir}`), + ...sources, + ...extraArgs, + `/Fe:${output}`, + ...(linkLibraries.length > 0 ? ['/link', ...linkLibraries.map(library => library.endsWith('.lib') ? library : `${library}.lib`)] : []), + ] + : [ + '-std=c11', '-Wall', '-Wextra', '-pedantic', + ...includeDirs.map(includeDir => `-I${includeDir}`), + ...sources, + ...extraArgs, + ...linkLibraries.map(library => `-l${library}`), + '-o', output, + ]; + const result = spawnSync(spec.command, args, { + cwd: options.cwd ?? process.cwd(), + env: spec.environment, + encoding: 'utf8', + timeout: options.timeout, + maxBuffer: options.maxBuffer ?? 32 * 1024 * 1024, + }); + return Object.freeze({ + ...result, + compiler: spec.command, + family: spec.family, + args: Object.freeze(args), + }); +} + +export function nativeCCompilerVersion(compiler) { + const spec = typeof compiler === 'string' + ? resolveNativeCCompiler({ compiler }) + : compiler; + if (!spec) return null; + const result = spawnSync(spec.command, spec.family === 'msvc' ? ['/Bv'] : ['--version'], { + env: spec.environment, + encoding: 'utf8', + maxBuffer: 2 * 1024 * 1024, + }); + const text = `${result.stdout ?? ''}\n${result.stderr ?? ''}`; + return text.split(/\r?\n/).map(line => line.trim()).find(Boolean) ?? spec.command; +} + +export function nativeCCompilerAvailable(options = {}) { + return Boolean(resolveNativeCCompiler(options)); +} + +export const NATIVE_C_COMPILER_FORMAT = 'rcl.native-c-compiler-resolution.v0.1'; diff --git a/src/rbc13-ai-assimilation-threshold.mjs b/src/rbc13-ai-assimilation-threshold.mjs new file mode 100644 index 00000000..87ede11d --- /dev/null +++ b/src/rbc13-ai-assimilation-threshold.mjs @@ -0,0 +1,475 @@ +import crypto from 'node:crypto'; +import fs from 'node:fs'; +import { spawnSync } from 'node:child_process'; +import { extractCapabilitiesFromJsonSchema } from './json-schema-source-frontend.mjs'; +import { forgeEquivalenceCorpus } from './equivalence-corpus-forge.mjs'; +import { metabolizeSourceCapabilityBundle } from './source-capability-frontends.mjs'; +import { realityRoot } from './canonical.mjs'; + +export const RBC13_AI_ASSIMILATION_THRESHOLD_FORMAT = + 'rcl.rbc13-ai-assimilation-threshold-results.v0.1'; +export const RBC13_AI_ASSIMILATION_PROTOCOL_VERSION = 'same-json-schema-donor-v0.1'; +export const RBC13_AI_ASSIMILATION_LEVELS = Object.freeze(['L0', 'L1', 'L2', 'L3', 'L4']); + +export const RBC13_AI_ASSIMILATION_TIERS = Object.freeze([ + Object.freeze({ tier: 'small', model: 'aetherseed-tinyllama-runtime:latest' }), + Object.freeze({ tier: 'medium', model: 'aetherseed-trained-smoke:latest' }), + Object.freeze({ tier: 'strong', model: 'qwen3.5:latest' }), +]); + +export const RBC13_AI_ASSIMILATION_PROMPT = [ + 'You are a blinded donor generator.', + 'You do not have repository source code, implementation code, test code, or expected outputs.', + 'Return exactly one JSON object and nothing else. The object itself must be a JSON Schema draft 2020-12, not an instance and not a wrapper.', + 'Schema intent: describe a DonorMeasurement object with required fields id, value, unit, confidence.', + 'The root must be an object and additionalProperties must be false.', + 'id is a non-empty string; value is a number; unit is a non-empty string; confidence is a number from 0 through 1.', + 'Include the $schema draft 2020-12 URI, a title, and a short description.', + 'Do not output markdown. Do not mention or generate RCL, DOMAIN_CALL, native organs, providers, repository files, or implementations.', +].join(' '); + +function sha256(value) { + return crypto.createHash('sha256').update(value).digest('hex'); +} + +function firstJsonObject(text) { + const source = String(text ?? ''); + const start = source.indexOf('{'); + if (start < 0) throw new Error('AI_GENERATE response contains no JSON object'); + let depth = 0; + let inString = false; + let escaped = false; + for (let index = start; index < source.length; index += 1) { + const character = source[index]; + if (inString) { + if (escaped) escaped = false; + else if (character === '\\') escaped = true; + else if (character === '"') inString = false; + continue; + } + if (character === '"') { inString = true; continue; } + if (character === '{') depth += 1; + if (character === '}') { + depth -= 1; + if (depth === 0) return source.slice(start, index + 1); + } + } + throw new Error('AI_GENERATE response contains an incomplete JSON object'); +} + +function evaluateSchema(schema, contract) { + const properties = schema?.properties ?? {}; + const expected = contract.requirements; + const required = Array.isArray(schema?.required) ? schema.required : []; + const exactRequired = required.length === expected.requiredExactly.length + && expected.requiredExactly.every(name => required.includes(name)); + const checks = { + draft2020: typeof schema?.$schema === 'string' && schema.$schema.includes('2020-12'), + rootObject: schema?.type === expected.rootType, + titlePresent: typeof schema?.title === 'string' && schema.title.length > 0, + requiredExact: exactRequired, + additionalPropertiesFalse: schema?.additionalProperties === expected.additionalProperties, + id: properties.id?.type === 'string' && Number(properties.id?.minLength) >= 1, + value: properties.value?.type === 'number', + unit: properties.unit?.type === 'string' && Number(properties.unit?.minLength) >= 1, + confidence: properties.confidence?.type === 'number' + && Number(properties.confidence?.minimum) === 0 + && Number(properties.confidence?.maximum) === 1, + }; + return Object.freeze({ checks: Object.freeze(checks), passed: Object.values(checks).every(Boolean) }); +} + +function runNegativeSchemaControl(schema, contract) { + const mutated = structuredClone(schema); + delete mutated.additionalProperties; + const evaluation = evaluateSchema(mutated, contract); + return Object.freeze({ + mutation: 'remove-additionalProperties-false', + detected: evaluation.passed === false, + evaluation, + }); +} + +function listModels() { + const result = spawnSync('ollama', ['list'], { encoding: 'utf8', timeout: 30_000 }); + const raw = String(result.stdout ?? ''); + const rows = raw.split(/\r?\n/).slice(1).map(line => line.trim()).filter(Boolean); + return Object.freeze(rows.map(line => { + const parts = line.split(/\s+/); + return Object.freeze({ + row: line, + name: parts[0] ?? null, + modelVersion: parts[1] ?? null, + size: parts.slice(2, 4).join(' ') || null, + rowRoot: sha256(line), + }); + })); +} + +function modelInfo(model, models) { + const listed = models.find(item => item.name === model) ?? null; + const shown = spawnSync('ollama', ['show', model, '--modelfile'], { + encoding: 'utf8', + timeout: 30_000, + maxBuffer: 8 * 1024 * 1024, + }); + const modelfile = shown.status === 0 ? String(shown.stdout ?? '') : ''; + return Object.freeze({ + available: Boolean(listed), + model, + modelVersion: listed?.modelVersion ?? null, + listedSize: listed?.size ?? null, + listRowRoot: listed?.rowRoot ?? null, + modelfileRoot: modelfile ? sha256(modelfile) : null, + }); +} + +async function generateWithOllama(model, protocol) { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), protocol.timeoutMs); + const started = process.hrtime.bigint(); + try { + const response = await fetch(protocol.endpoint, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + model, + prompt: RBC13_AI_ASSIMILATION_PROMPT, + stream: false, + format: 'json', + options: { + temperature: protocol.temperature, + seed: protocol.seed, + top_p: 1, + num_predict: protocol.numPredict, + }, + }), + signal: controller.signal, + }); + const rawPayload = await response.text(); + if (!response.ok) { + return Object.freeze({ + status: 'BLOCKED', + reason: 'ollama-http-error', + httpStatus: response.status, + rawPayloadTail: rawPayload.slice(-4000), + durationMs: Number(process.hrtime.bigint() - started) / 1_000_000, + }); + } + const payload = JSON.parse(rawPayload); + return Object.freeze({ + status: 'live', + model: payload.model ?? model, + rawResponse: String(payload.response ?? ''), + responseRoot: sha256(String(payload.response ?? '')), + apiPayloadRoot: sha256(rawPayload), + createdAt: payload.created_at ?? null, + durationMs: Number(process.hrtime.bigint() - started) / 1_000_000, + }); + } catch (error) { + return Object.freeze({ + status: 'BLOCKED', + reason: error?.name === 'AbortError' ? 'ollama-timeout' : 'ollama-request-failed', + error: String(error?.message ?? error), + durationMs: Number(process.hrtime.bigint() - started) / 1_000_000, + }); + } finally { + clearTimeout(timer); + } +} + +function summarizeCorpus(corpus) { + return corpus ? Object.freeze({ + format: corpus.format, + root: corpus.root, + capabilityCount: corpus.capabilityCount, + corpora: corpus.corpora.map(item => ({ + capability: item.capability, + root: item.root, + caseCount: item.caseCount, + mutationPlanCount: item.mutationPlanCount, + classifications: item.cases.reduce((counts, item) => { + counts[item.classification] = (counts[item.classification] ?? 0) + 1; + return counts; + }, {}), + })), + }) : null; +} + +export function classifyAiAssimilationLevel(stages = {}) { + if (stages.contract !== true) return 'L0'; + if (stages.extraction !== true || stages.corpus !== true) return 'L1'; + if (stages.candidate !== true) return 'L1'; + if (stages.differential !== true) return 'L2'; + if (stages.nativeCandidate !== true) return 'L3'; + if (stages.promotion !== true) return 'L3'; + return 'L4'; +} + +async function runAttempt({ tier, model, contract, modelInfoValue, protocol, humanInterventions = 0 }) { + const generated = await generateWithOllama(model, protocol); + const base = { + tier, + model, + modelInfo: modelInfoValue, + attempt: 1, + protocol: { + version: RBC13_AI_ASSIMILATION_PROTOCOL_VERSION, + promptRoot: realityRoot(RBC13_AI_ASSIMILATION_PROMPT), + temperature: protocol.temperature, + seed: protocol.seed, + format: 'json', + numPredict: protocol.numPredict, + }, + humanInterventions, + repairAttempt: { + attempted: false, + humanInterventions, + modelRepairCalls: 0, + reason: 'single-pass donor protocol; no hidden or human repair is permitted', + }, + }; + if (generated.status !== 'live') { + const stages = { contract: false, extraction: false, corpus: false, candidate: false, differential: false, nativeCandidate: false, promotion: false }; + return Object.freeze({ + ...base, + status: 'BLOCKED', + level: 'L0', + stages, + generation: generated, + blockedReason: generated.reason, + donorSpec: { root: realityRoot(contract), format: contract.format }, + nativeCandidate: { status: 'BLOCKED', reason: 'no donor output' }, + promotionAttempt: { status: 'BLOCKED', reason: 'no donor output', canonicalAdmission: false }, + }); + } + + let schema = null; + let parseError = null; + try { + schema = JSON.parse(firstJsonObject(generated.rawResponse)); + } catch (error) { + parseError = String(error?.message ?? error); + } + const schemaEvaluation = schema ? evaluateSchema(schema, contract) : { checks: {}, passed: false }; + const contractPass = parseError === null && schemaEvaluation.passed; + let extraction = null; + let corpus = null; + let metabolism = null; + let extractionError = null; + if (schema) { + try { + extraction = extractCapabilitiesFromJsonSchema(schema, { + id: 'ai_donor_measurement', + provenance: ['ai-assimilation-threshold:blind-json-schema-donor'], + }); + corpus = forgeEquivalenceCorpus(extraction, { maxCasesPerCapability: 64 }); + metabolism = metabolizeSourceCapabilityBundle(extraction, { + subject: `rbc13_ai_assimilation_${tier}`, + }); + } catch (error) { + extractionError = String(error?.message ?? error); + } + } + const negativeControl = schema + ? runNegativeSchemaControl(schema, contract) + : { mutation: 'unavailable', detected: false }; + const candidateReport = metabolism?.reports?.[0] ?? null; + const stages = { + contract: contractPass, + extraction: Boolean(extraction), + signature: Boolean(schema) && Boolean(generated.responseRoot), + corpus: Boolean(corpus) && Number(corpus.capabilityCount ?? 0) > 0, + candidate: Boolean(candidateReport) && ['semantic-absorbed', 'native-candidate'].includes(candidateReport.assessment?.stage), + differential: false, + mutantDetection: negativeControl.detected === true, + nativeCandidate: false, + promotion: false, + }; + const level = classifyAiAssimilationLevel(stages); + const status = level === 'L4' ? 'VERIFIED' : (contractPass ? 'NEGATIVE_RESULT' : 'NEGATIVE_RESULT'); + return Object.freeze({ + ...base, + status, + level, + generation: { + status: generated.status, + model: generated.model, + responseRoot: generated.responseRoot, + apiPayloadRoot: generated.apiPayloadRoot, + createdAt: generated.createdAt, + durationMs: generated.durationMs, + responseTail: generated.rawResponse.slice(-4000), + }, + donorSpec: { + root: realityRoot(contract), + format: contract.format, + task: contract.task, + }, + schema: schema ? { + root: realityRoot(schema), + responseRoot: generated.responseRoot, + parseError, + evaluation: schemaEvaluation, + } : { root: null, responseRoot: generated.responseRoot, parseError, evaluation: schemaEvaluation }, + extraction: extraction ? { + format: extraction.format, + root: extraction.root, + sourceRoot: extraction.sourceRoot, + capabilityCount: extraction.capabilityCount, + } : null, + signature: { + promptRoot: realityRoot(RBC13_AI_ASSIMILATION_PROMPT), + responseRoot: generated.responseRoot, + schemaRoot: schema ? realityRoot(schema) : null, + extractionRoot: extraction?.root ?? null, + }, + contract: { + passed: contractPass, + evaluation: schemaEvaluation, + parseError, + }, + corpus: summarizeCorpus(corpus), + candidate: candidateReport ? { + status: candidateReport.assessment?.stage ?? 'UNKNOWN', + root: candidateReport.root, + specRoot: candidateReport.specRoot, + semanticKernelRoot: candidateReport.semanticKernelRoot, + generatedRclRoot: candidateReport.generatedRclRoot, + absorptionRoot: candidateReport.absorptionRoot, + gaps: candidateReport.assessment?.gaps ?? [], + } : null, + differential: { + status: 'BLOCKED', + reason: 'RCL has no independent executable JSON Schema validator adapter in this repository; schema extraction is not differential execution', + sourceAdapter: 'unavailable', + absorbedAdapter: 'unavailable', + humanInterventions: 0, + }, + negativeControl, + repairAttempt: base.repairAttempt, + nativeCandidate: { + status: 'BLOCKED', + reason: 'no native JSON Schema validator/bytecode adapter is present; no implementation was generated or repaired', + candidateRoot: candidateReport?.root ?? null, + }, + promotionAttempt: { + status: 'BLOCKED', + reason: 'promotion requires independent differential plus native candidate evidence', + canonicalAdmission: false, + }, + stages, + extractionError, + boundary: 'The model is credited only for the blind donor response and the RCL source-frontend extraction/corpus stages. Differential, native-candidate, and promotion stages remain uncredited without an independent executable JSON Schema adapter.', + }); +} + +export async function runRbc13AiAssimilationThreshold(options = {}) { + const contractPath = options.contractPath; + const contract = options.contract ?? JSON.parse(fs.readFileSync(contractPath, 'utf8')); + const models = options.models ?? RBC13_AI_ASSIMILATION_TIERS; + const listedModels = options.listedModels ?? listModels(); + const protocol = { + endpoint: options.endpoint ?? `${process.env.OLLAMA_HOST?.startsWith('http') ? process.env.OLLAMA_HOST : `http://${process.env.OLLAMA_HOST ?? '127.0.0.1:11434'}`}/api/generate`, + temperature: options.temperature ?? 0, + seed: options.seed ?? 13013, + numPredict: options.numPredict ?? 384, + timeoutMs: options.timeoutMs ?? 180_000, + }; + const attempts = []; + for (const modelSpec of models) { + const info = modelInfo(modelSpec.model, listedModels); + if (!info.available) { + attempts.push(Object.freeze({ + tier: modelSpec.tier, + model: modelSpec.model, + modelInfo: info, + attempt: 1, + status: 'BLOCKED', + level: 'L0', + humanInterventions: 0, + protocol: { + version: RBC13_AI_ASSIMILATION_PROTOCOL_VERSION, + promptRoot: realityRoot(RBC13_AI_ASSIMILATION_PROMPT), + temperature: protocol.temperature, + seed: protocol.seed, + format: 'json', + numPredict: protocol.numPredict, + }, + blockedReason: 'model-not-present-in-ollama-list', + repairAttempt: { attempted: false, humanInterventions: 0, modelRepairCalls: 0 }, + stages: { contract: false, extraction: false, corpus: false, candidate: false, differential: false, nativeCandidate: false, promotion: false }, + })); + continue; + } + attempts.push(await runAttempt({ + tier: modelSpec.tier, + model: modelSpec.model, + contract, + modelInfoValue: info, + protocol, + })); + } + const allTiersPresent = attempts.every(item => item.modelInfo?.available === true); + const allL4 = attempts.length === models.length && attempts.every(item => item.level === 'L4'); + const maxLevel = Math.max(...attempts.map(item => RBC13_AI_ASSIMILATION_LEVELS.indexOf(item.level)), -1); + const minLevel = Math.min(...attempts.map(item => RBC13_AI_ASSIMILATION_LEVELS.indexOf(item.level)), 0); + const conclusion = allL4 && attempts.every(item => item.humanInterventions === 0) + ? 'VERIFIED' + : (allTiersPresent ? 'NEGATIVE_RESULT' : 'BLOCKED'); + const body = { + format: RBC13_AI_ASSIMILATION_THRESHOLD_FORMAT, + version: '0.1.0', + status: conclusion, + protocol: { + version: RBC13_AI_ASSIMILATION_PROTOCOL_VERSION, + promptRoot: realityRoot(RBC13_AI_ASSIMILATION_PROMPT), + donorSpecRoot: realityRoot(contract), + temperature: protocol.temperature, + seed: protocol.seed, + format: 'json', + numPredict: protocol.numPredict, + endpoint: protocol.endpoint, + }, + donorSpec: { + format: contract.format, + root: realityRoot(contract), + modelDeclaredByHistoricalContract: contract.model, + }, + baseline: options.baseline ?? null, + attempts, + summary: { + tierCount: models.length, + testedTierCount: attempts.filter(item => item.generation?.status === 'live').length, + availableTierCount: attempts.filter(item => item.modelInfo?.available === true).length, + humanInterventions: attempts.reduce((sum, item) => sum + Number(item.humanInterventions ?? 0), 0), + minimumObservedLevel: minLevel >= 0 ? RBC13_AI_ASSIMILATION_LEVELS[minLevel] : null, + maximumObservedLevel: maxLevel >= 0 ? RBC13_AI_ASSIMILATION_LEVELS[maxLevel] : null, + allTiersReachedL4: allL4, + allTiersPresent, + }, + threshold: { + levels: { + L0: 'donor response unavailable or contract invalid', + L1: 'contract plus extraction/corpus not complete', + L2: 'semantic candidate exists but independent differential is unavailable or failed', + L3: 'independent differential exists but native candidate or promotion is incomplete', + L4: 'native candidate and promotion evidence are independently verified with zero human repair', + }, + conclusion: allL4 ? 'L4 observed across every tested tier' : 'No tested tier reached L4; no native promotion credit is granted', + scope: 'This threshold conclusion is scoped to the declared JSON Schema donor, protocol, and listed local Ollama models. It is not a general claim about AI capability.', + }, + chain: [ + 'Donor Spec', 'Extraction', 'Signature', 'Contract', 'Positive/Negative Corpus', + 'Candidate', 'Differential', 'Mutant Detection', 'Repair Attempt', 'Native Candidate', 'Promotion Attempt', + ], + boundary: 'No implementation code, hidden tests, expected outputs, or human repair were sent to or applied to any model. A failed model is not repaired or upgraded.', + reproductionCommand: 'npm run verify:rbc13-ai-assimilation-threshold', + }; + return Object.freeze({ ...body, root: realityRoot(body) }); +} + +export function renderRbc13AiAssimilationThresholdMarkdown(report) { + const rows = report.attempts.map(attempt => `| ${attempt.tier} | ${attempt.model} | ${attempt.modelInfo?.modelVersion ?? 'UNAVAILABLE'} | ${attempt.status} | ${attempt.level} | ${attempt.humanInterventions} | ${attempt.candidate?.status ?? 'none'} | ${attempt.nativeCandidate?.status ?? 'BLOCKED'} | ${attempt.promotionAttempt?.status ?? 'BLOCKED'} |`).join('\n'); + return `# RCL AI Assimilation Intelligence Threshold v0.1\n\n- Status: **${report.status}**\n- Evidence root: \`${report.root}\`\n- Donor spec root: \`${report.donorSpec.root}\`\n- Prompt root: \`${report.protocol.promptRoot}\`\n- Protocol: ${report.protocol.version}; temperature=${report.protocol.temperature}; seed=${report.protocol.seed}; format=${report.protocol.format}\n- Human interventions: ${report.summary.humanInterventions}\n\n## Tier results\n\n| Tier | Model | Ollama version | Status | Level | Human repair | Candidate | Native candidate | Promotion |\n|---|---|---|---|---|---:|---|---|---|\n${rows}\n\n## Threshold levels\n\n${Object.entries(report.threshold.levels).map(([level, description]) => `- ${level}: ${description}`).join('\n')}\n\n## Required chain\n\n${report.chain.join(' → ')}\n\n## Conclusion\n\n${report.threshold.conclusion}. Minimum observed level: **${report.summary.minimumObservedLevel ?? 'none'}**; maximum observed level: **${report.summary.maximumObservedLevel ?? 'none'}**. ${report.threshold.scope}\n\n## Boundary\n\n${report.boundary}\n`; +} diff --git a/src/rbc13-canonical-admission-readiness.mjs b/src/rbc13-canonical-admission-readiness.mjs new file mode 100644 index 00000000..742b2eff --- /dev/null +++ b/src/rbc13-canonical-admission-readiness.mjs @@ -0,0 +1,147 @@ +import { createHash } from 'node:crypto'; + +export const RBC13_CANONICAL_ADMISSION_READINESS_FORMAT = 'rcl.rbc13-canonical-admission-readiness.v0.1'; +export const RBC13_CANONICAL_ADMISSION_GATE_KEYS = Object.freeze([ + 'A1_numberEncodingV2', + 'A2_nativePromotionInventory', + 'A3_legacyRegressionClosure', + 'A4_positiveSemanticEquivalence', + 'A5_negativeSemanticEquivalence', + 'A6_deterministicReplay', + 'A7_semanticRootEvidence', + 'A8_authorityAndEvidenceBoundary', + 'A9_performanceEvidence', + 'A10_aiGenerateDonor', + 'A11_selfhostAndVersionContract', + 'A12_universalStressAdmissionCell', +]); + +function sha256(value) { + return createHash('sha256').update(JSON.stringify(value)).digest('hex'); +} + +function gate(key, passed, evidence, blocker = null) { + return Object.freeze({ key, passed: passed === true, evidence: [...evidence].filter(Boolean), blocker }); +} + +function allNativeReports(native) { + return native?.status === 'native-verified' + && native?.verified === true + && Array.isArray(native?.reports) + && native.reports.length === 4; +} + +export function buildRbc13CanonicalAdmissionReadiness(input = {}) { + const number = input.number ?? {}; + const native = input.native ?? {}; + const performance = input.performance ?? {}; + const aiGenerate = input.aiGenerate ?? {}; + const aiThreshold = input.aiThreshold ?? {}; + const aiCompatibility = input.aiCompatibility ?? {}; + const universal = input.universal ?? {}; + const growthCell = input.growthCell ?? {}; + const wasmGrowthCell = input.wasmGrowthCell ?? {}; + const legacy = input.legacy ?? {}; + const legacyClosure = input.legacyClosure ?? {}; + const selfhost = input.selfhost ?? {}; + const versionContract = input.versionContract ?? {}; + const numberRequirements = number.requirements ?? {}; + const nativeReports = native.reports ?? []; + const allNative = allNativeReports(native); + const allNativeGates = nativeReports.length === 4 && nativeReports.every(report => Object.values(report.gates ?? {}).every(Boolean)); + const allPositive = nativeReports.length === 4 && nativeReports.every(report => report.gates?.G5_positiveSemanticEquivalence === true); + const allNegative = nativeReports.length === 4 && nativeReports.every(report => report.gates?.G6_negativeSemanticEquivalence === true); + const allReplay = nativeReports.length === 4 && nativeReports.every(report => report.gates?.G7_nativeReplayDeterministic === true); + const allRoots = nativeReports.length === 4 && nativeReports.every(report => report.gates?.G8_nativeSemanticStateRootEmittedAndVerified === true && report.gates?.G9_semanticRootParity === true); + const authorityBoundary = allNative && nativeReports.every(report => report.gates?.G10_allEvidenceRootsRecorded === true && report.gates?.G12_nativePromotionEvidenceTier === true) + && native.canonicalAdmission !== true; + const performancePass = performance.status === 'VERIFIED' + && performance.measures?.allPathsExecuted === true + && performance.measures?.repeatedSamples === true + && performance.measures?.varianceRecorded === true + && performance.measures?.rssProxyRecorded === true; + const hasAiCompatibility = Object.keys(aiCompatibility).length > 0; + const hasAiThreshold = Object.keys(aiThreshold).length > 0; + const aiPass = hasAiCompatibility + ? aiCompatibility.status === 'VERIFIED' + && aiCompatibility.formalA10?.status === 'VERIFIED' + && aiCompatibility.summary?.formalA10 === true + && Number(aiCompatibility.summary?.humanRepairs ?? 0) === 0 + : hasAiThreshold + ? aiThreshold.status === 'VERIFIED' + && aiThreshold.summary?.allTiersReachedL4 === true + && Number(aiThreshold.summary?.humanInterventions ?? 0) === 0 + : aiGenerate.status === 'CANDIDATE' + && aiGenerate.gate === 'PASS' + && Number(aiGenerate.successfulTrials ?? 0) >= Number(aiGenerate.requiredTrials ?? 1); + const hasGrowthCell = Object.keys(growthCell).length > 0; + const hasWasmGrowthCell = Object.keys(wasmGrowthCell).length > 0; + const universalPass = hasWasmGrowthCell + ? wasmGrowthCell.status === 'VERIFIED' + && wasmGrowthCell.universalGrowthEligible === true + && wasmGrowthCell.nativeC?.status === 'VERIFIED' + && wasmGrowthCell.wasm?.status === 'VERIFIED' + && wasmGrowthCell.replay?.status === 'VERIFIED' + && wasmGrowthCell.cases?.every(item => item.semanticRootParity === true && item.resultOrErrorParity === true && item.statusParity === true) + : hasGrowthCell + ? growthCell.status === 'VERIFIED' && growthCell.universalGrowthEligible === true + : universal.status === 'PASS' && universal.universalGrowthEligible === true; + const hasLegacyClosure = Object.keys(legacyClosure).length > 0; + const legacyClosurePass = hasLegacyClosure + ? legacyClosure.status === 'VERIFIED' + && legacyClosure.checks?.expectedInventoryAuthoritative === true + && legacyClosure.checks?.noMissingCases === true + && legacyClosure.checks?.noDuplicateReceiptRoots === true + && legacyClosure.checks?.noStaleReceipts === true + && legacyClosure.checks?.noAlteredReceipts === true + && legacyClosure.checks?.replayRootConsistency === true + && legacyClosure.checks?.rbc11Verified === true + && legacyClosure.checks?.rbc12Verified === true + : true; + const gates = { + A1_numberEncodingV2: gate('A1_numberEncodingV2', number.status === 'VERIFIED' && Object.values(numberRequirements).every(Boolean), [number.root, number.corpusRoot], number.status === 'VERIFIED' ? null : 'Number v2 corpus or requirements are incomplete'), + A2_nativePromotionInventory: gate('A2_nativePromotionInventory', allNative && allNativeGates, [native.root, ...(native.reportRoots ?? [])], allNative ? null : 'Four-operation Native Promotion is not fully verified'), + A3_legacyRegressionClosure: gate('A3_legacyRegressionClosure', legacyClosurePass && legacy.v1FocusedStatus === 'VERIFIED' && legacy.fullSuiteStatus === 'VERIFIED', [legacyClosure.root, legacy.v1FocusedRoot, legacy.fullSuiteRoot], legacyClosurePass && legacy.fullSuiteStatus === 'VERIFIED' ? null : 'Legacy receipt closure or full-suite regression is not VERIFIED'), + A4_positiveSemanticEquivalence: gate('A4_positiveSemanticEquivalence', allPositive, nativeReports.map(report => report.root), allPositive ? null : 'At least one positive operation differential is incomplete'), + A5_negativeSemanticEquivalence: gate('A5_negativeSemanticEquivalence', allNegative, nativeReports.map(report => report.root), allNegative ? null : 'At least one negative/error semantic differential is incomplete'), + A6_deterministicReplay: gate('A6_deterministicReplay', allReplay, nativeReports.map(report => report.root), allReplay ? null : 'Native replay determinism is incomplete'), + A7_semanticRootEvidence: gate('A7_semanticRootEvidence', allRoots, nativeReports.map(report => report.root), allRoots ? null : 'Native semantic state-root emission/parity is incomplete'), + A8_authorityAndEvidenceBoundary: gate('A8_authorityAndEvidenceBoundary', authorityBoundary, [native.root, versionContract.root], authorityBoundary ? null : 'Evidence roots/tier boundary or canonical isolation is incomplete'), + A9_performanceEvidence: gate('A9_performanceEvidence', performancePass, [performance.root, performance.hostRoot], performancePass ? null : 'Three-path performance evidence is incomplete'), + A10_aiGenerateDonor: gate('A10_aiGenerateDonor', aiPass, hasAiCompatibility + ? [aiCompatibility.root, aiCompatibility.protocol?.promptRoot, aiCompatibility.donor?.root, aiCompatibility.corpus?.root] + : hasAiThreshold + ? [aiThreshold.root, aiThreshold.protocol?.promptRoot, aiThreshold.donorSpec?.root] + : [aiGenerate.root, aiGenerate.responseRoot, aiGenerate.corpus?.root], + aiPass ? null : `AI_GENERATE donor status is ${hasAiCompatibility ? aiCompatibility.status ?? 'missing' : hasAiThreshold ? aiThreshold.status ?? 'missing' : aiGenerate.status ?? 'missing'}`), + A11_selfhostAndVersionContract: gate('A11_selfhostAndVersionContract', selfhost.fixedpointStatus === 'VERIFIED' && selfhost.examplesStatus === 'VERIFIED' && selfhost.stage40Status === 'VERIFIED' && versionContract.status === 'VERIFIED', [selfhost.fixedpointRoot, selfhost.examplesRoot, selfhost.stage40Root, versionContract.root], selfhost.fixedpointStatus === 'VERIFIED' && selfhost.examplesStatus === 'VERIFIED' && selfhost.stage40Status === 'VERIFIED' && versionContract.status === 'VERIFIED' ? null : 'Selfhost or version-contract evidence is incomplete'), + A12_universalStressAdmissionCell: gate('A12_universalStressAdmissionCell', universalPass, + hasWasmGrowthCell ? [wasmGrowthCell.root] : hasGrowthCell ? [growthCell.root] : [universal.root], + universalPass ? null : `Universal Stress cell is ${hasWasmGrowthCell ? wasmGrowthCell.status ?? 'missing' : hasGrowthCell ? growthCell.status ?? 'missing' : universal.status ?? 'missing'}`), + }; + const blockingGates = RBC13_CANONICAL_ADMISSION_GATE_KEYS.filter(key => !gates[key].passed); + const canonicalReady = blockingGates.length === 0; + const report = { + format: RBC13_CANONICAL_ADMISSION_READINESS_FORMAT, + version: '0.1.0-alpha.1', + verdict: canonicalReady ? 'VERIFIED' : 'BLOCKED', + canonicalReady, + canonicalAdmission: false, + gates, + blockingGates, + strictAutonomousGrowth: hasAiCompatibility + ? { + globalMaximum: aiCompatibility.strictGrowthAssessment?.globalLevel ?? 'Level 2 VERIFIED', + nextLevel: aiCompatibility.strictGrowthAssessment?.nextLevel ?? 'Level 3 CANDIDATE/BLOCKED', + formalA10: aiCompatibility.formalA10?.status ?? 'NEGATIVE_RESULT', + } + : null, + requiredBeforeCanonicalAdmission: [ + 'Resolve every blocking gate without mutating the v1 contract in place.', + 'Re-run the full current-source/native/legacy/selfhost evidence matrix on the final proposed roots.', + 'Obtain separate Integration Court approval for canonical language and version-contract changes.', + ], + boundary: 'Readiness is a proposal input, not a canonical activation. A VERIFIED candidate evidence set would still require an explicit governance admission commit.', + }; + return Object.freeze({ ...report, root: sha256(report) }); +} diff --git a/src/rbc13-capability-assimilation-compatibility-surface.mjs b/src/rbc13-capability-assimilation-compatibility-surface.mjs new file mode 100644 index 00000000..904e055c --- /dev/null +++ b/src/rbc13-capability-assimilation-compatibility-surface.mjs @@ -0,0 +1,430 @@ +import crypto from 'node:crypto'; +import fs from 'node:fs'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { + RBC13_JSON_SCHEMA_MUTATION_CONTROLS, + buildRbc13JsonSchemaDonorCorpus, +} from './rbc13-json-schema-donor-corpus.mjs'; +import { + loadRbc13JsonSchemaDonorContract, + validateRbc13JsonSchemaDonorContract, +} from './rbc13-json-schema-contract.mjs'; +import { evaluateRbc13JsonSchemaCandidate } from './rbc13-json-schema-candidate-adapter.mjs'; +import { realityRoot } from './canonical.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const ORACLE_PATH = path.join(ROOT, 'oracle', 'rbc13-json-schema-donor-oracle.mjs'); + +export const RBC13_CAPABILITY_ASSIMILATION_COMPATIBILITY_SURFACE_FORMAT = + 'rcl.rbc13-capability-assimilation-compatibility-surface.v0.1'; +export const RBC13_CAPABILITY_ASSIMILATION_PROTOCOL_VERSION = + 'same-donor-schema-independent-differential-v0.1'; +export const RBC13_CAPABILITY_ASSIMILATION_ACL_LEVELS = Object.freeze(['ACL0', 'ACL1', 'ACL2', 'ACL3', 'ACL4']); +export const RBC13_CAPABILITY_ASSIMILATION_TIERS = Object.freeze([ + Object.freeze({ tier: 'tiny', model: 'aetherseed-tinyllama-runtime:latest' }), + Object.freeze({ tier: 'medium', model: 'aetherseed-trained-smoke:latest' }), + Object.freeze({ tier: 'strong', model: 'qwen3.5:latest' }), +]); + +export const RBC13_CAPABILITY_ASSIMILATION_PROMPT = [ + 'You are a blinded JSON Schema donor generator.', + 'Return exactly one JSON object and nothing else.', + 'The object itself must be a JSON Schema draft 2020-12, not an instance and not a wrapper.', + 'The schema must describe a bounded DonorMeasurement record.', + 'Include $schema equal to https://json-schema.org/draft/2020-12/schema, a $id, a title, and a description.', + 'The root is an object with additionalProperties false and required exactly id, value, unit.', + 'Properties are exactly id, value, unit, confidence, tags, metadata.', + 'id is a string with minLength 3 and maxLength 64.', + 'value is a number with minimum -1000 and maximum 1000.', + 'unit is a string enum of exactly mL, ml, and g.', + 'confidence is a number with minimum 0 and maximum 1.', + 'tags is an array with minItems 0 and maxItems 4; each item is a string with minLength 1 and maxLength 16.', + 'metadata is an object with required source, optional verified boolean, source string minLength 1 maxLength 32, and additionalProperties false.', + 'Use only JSON Schema keywords needed for that fixed subset.', + 'Do not output markdown. Do not mention or generate RCL, DOMAIN_CALL, native organs, providers, repository files, tests, an oracle, a corpus, mutants, or implementation code.', +].join(' '); + +function sha256(value) { + return crypto.createHash('sha256').update(String(value)).digest('hex'); +} + +function firstJsonObject(text) { + const source = String(text ?? ''); + const start = source.indexOf('{'); + if (start < 0) throw new Error('response contains no JSON object'); + let depth = 0; + let inString = false; + let escaped = false; + for (let index = start; index < source.length; index += 1) { + const character = source[index]; + if (inString) { + if (escaped) escaped = false; + else if (character === '\\') escaped = true; + else if (character === '"') inString = false; + continue; + } + if (character === '"') { inString = true; continue; } + if (character === '{') depth += 1; + if (character === '}') { + depth -= 1; + if (depth === 0) return source.slice(start, index + 1); + } + } + throw new Error('response contains an incomplete JSON object'); +} + +function endpointFrom(value) { + if (String(value ?? '').startsWith('http')) return String(value).replace(/\/$/, '') + '/api/generate'; + return `http://${value || '127.0.0.1:11434'}/api/generate`; +} + +function listModels() { + const result = spawnSync('ollama', ['list'], { encoding: 'utf8', timeout: 30_000 }); + if (result.status !== 0) return []; + return String(result.stdout ?? '').split(/\r?\n/).slice(1).map(line => line.trim()).filter(Boolean).map(line => { + const parts = line.split(/\s+/); + return { row: line, name: parts[0] ?? null, modelVersion: parts[1] ?? null, size: parts.slice(2, 4).join(' ') || null, rowRoot: sha256(line) }; + }); +} + +function modelInfo(model, listedModels) { + const listed = listedModels.find(item => item.name === model) ?? null; + const shown = spawnSync('ollama', ['show', model, '--modelfile'], { encoding: 'utf8', timeout: 30_000, maxBuffer: 8 * 1024 * 1024 }); + const modelfile = shown.status === 0 ? String(shown.stdout ?? '') : ''; + return { + available: Boolean(listed), + model, + modelVersion: listed?.modelVersion ?? null, + listedSize: listed?.size ?? null, + listRowRoot: listed?.rowRoot ?? null, + modelfileRoot: modelfile ? sha256(modelfile) : null, + }; +} + +async function generateWithOllama(model, prompt, protocol) { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), protocol.timeoutMs); + const started = process.hrtime.bigint(); + try { + const response = await fetch(protocol.endpoint, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + model, + prompt, + stream: false, + format: 'json', + options: { + temperature: protocol.temperature, + seed: protocol.seed, + top_p: 1, + num_predict: protocol.numPredict, + }, + }), + signal: controller.signal, + }); + const rawPayload = await response.text(); + if (!response.ok) return { status: 'BLOCKED', reason: 'ollama-http-error', httpStatus: response.status, rawPayloadTail: rawPayload.slice(-4000), durationMs: Number(process.hrtime.bigint() - started) / 1_000_000 }; + const payload = JSON.parse(rawPayload); + const rawResponse = String(payload.response ?? ''); + return { status: 'live', model: payload.model ?? model, rawResponse, responseRoot: sha256(rawResponse), apiPayloadRoot: sha256(rawPayload), createdAt: payload.created_at ?? null, durationMs: Number(process.hrtime.bigint() - started) / 1_000_000 }; + } catch (error) { + return { status: 'BLOCKED', reason: error?.name === 'AbortError' ? 'ollama-timeout' : 'ollama-request-failed', error: String(error?.message ?? error), durationMs: Number(process.hrtime.bigint() - started) / 1_000_000 }; + } finally { + clearTimeout(timer); + } +} + +function donorSchemaFromContract(contract) { + return { + $schema: 'https://json-schema.org/draft/2020-12/schema', + $id: 'urn:rcl:rbc13:donor-measurement:fixed-subset:v1', + title: contract.intent.title, + description: contract.intent.description, + type: contract.intent.rootType, + required: [...contract.intent.requiredFields], + properties: structuredClone(contract.intent.properties), + additionalProperties: contract.intent.additionalProperties, + }; +} + +function oracleEvaluate(schema, cases) { + const run = spawnSync(process.execPath, [ORACLE_PATH], { + input: JSON.stringify({ schema, cases }), + encoding: 'utf8', + timeout: 60_000, + maxBuffer: 32 * 1024 * 1024, + }); + let payload = null; + try { payload = JSON.parse(String(run.stdout ?? '')); } catch (error) { + return { status: 'BLOCKED', reason: 'oracle-invalid-json', details: { exitCode: run.status, stderr: String(run.stderr ?? ''), parseError: String(error.message ?? error) } }; + } + if (run.status !== 0 || payload.status !== 'OK') return { status: 'BLOCKED', reason: 'oracle-failed', details: payload }; + return payload; +} + +function compareCandidateToOracle(schema, cases, oracleOutputs, mutation = null) { + const candidateOutputs = cases.map(item => { + const result = evaluateRbc13JsonSchemaCandidate(schema, item.instance, { mutation }); + return { caseId: item.id, valid: result.valid, errors: result.errors }; + }); + const mismatches = candidateOutputs.flatMap((candidate, index) => { + const expected = oracleOutputs[index]; + return candidate.valid === expected.valid && JSON.stringify(candidate.errors) === JSON.stringify(expected.errors) + ? [] + : [{ caseId: candidate.caseId, candidate, oracle: expected }]; + }); + return { candidateOutputs, mismatches, exact: mismatches.length === 0 }; +} + +function runMutationControls(schema, cases, oracleOutputs) { + return RBC13_JSON_SCHEMA_MUTATION_CONTROLS.map(control => { + const result = compareCandidateToOracle(schema, cases, oracleOutputs, control.id); + return { ...control, detected: result.mismatches.length > 0, mismatchCount: result.mismatches.length, exampleCaseId: result.mismatches[0]?.caseId ?? null }; + }); +} + +function aclFor({ contract, differential }) { + if (!contract) return 'ACL0'; + if (!differential) return 'ACL1'; + return 'ACL2'; +} + +async function runModel({ spec, contract, corpus, oracle, protocol, info }) { + const attempts = []; + let generated = await generateWithOllama(spec.model, RBC13_CAPABILITY_ASSIMILATION_PROMPT, protocol); + attempts.push({ kind: 'initial', promptRoot: sha256(RBC13_CAPABILITY_ASSIMILATION_PROMPT), ...generated }); + let schema = null; + let parseError = null; + let contractEvaluation = null; + for (let repair = 0; repair <= protocol.maxAutomaticRepairs; repair += 1) { + if (generated.status !== 'live') break; + try { + schema = JSON.parse(firstJsonObject(generated.rawResponse)); + parseError = null; + } catch (error) { + schema = null; + parseError = String(error.message ?? error); + } + contractEvaluation = schema ? validateRbc13JsonSchemaDonorContract(schema) : { valid: false, errors: [parseError] }; + if (contractEvaluation.valid) break; + if (repair === protocol.maxAutomaticRepairs) break; + const repairPrompt = [ + 'Return exactly one corrected JSON Schema object and nothing else.', + 'The previous donor object did not satisfy the fixed donor contract.', + 'Keep the same blinded task and correct the contract without asking a human and without using repository source, hidden cases, an oracle, or implementation code.', + 'Do not include markdown or explanations.', + ].join(' '); + generated = await generateWithOllama(spec.model, repairPrompt, { ...protocol, seed: protocol.seed + repair + 1 }); + attempts.push({ kind: 'automatic-repair', repairNumber: repair + 1, promptRoot: sha256(repairPrompt), ...generated }); + } + const contractPassed = generated.status === 'live' && contractEvaluation?.valid === true; + const base = { + tier: spec.tier, + model: spec.model, + modelInfo: info, + protocol: { + version: RBC13_CAPABILITY_ASSIMILATION_PROTOCOL_VERSION, + promptRoot: sha256(RBC13_CAPABILITY_ASSIMILATION_PROMPT), + temperature: protocol.temperature, + seed: protocol.seed, + format: 'json', + numPredict: protocol.numPredict, + maxAutomaticRepairs: protocol.maxAutomaticRepairs, + }, + attempts: attempts.map(item => ({ ...item, rawResponseTail: item.rawResponse?.slice(-4000) ?? null })), + humanRepairs: 0, + automaticRepairs: attempts.filter(item => item.kind === 'automatic-repair' && item.status === 'live').length, + candidateStartedEmpty: true, + hiddenCorpusProvidedToModel: false, + oracleProvidedToModel: false, + previousCandidateProvidedToModel: false, + contract: { passed: contractPassed, parseError, evaluation: contractEvaluation }, + }; + if (!contractPassed) { + return { + ...base, + status: generated.status === 'live' ? 'NEGATIVE_RESULT' : 'BLOCKED', + acl: 'ACL0', + differential: { status: 'NOT_RUN', reason: generated.status === 'live' ? 'donor-contract-invalid' : generated.reason }, + mutationControls: [], + replay: { status: 'NOT_RUN' }, + nativeCandidate: { status: 'BLOCKED', reason: 'ACL3 requires a separately admitted native organ candidate' }, + nativePromotion: { status: 'BLOCKED', reason: 'ACL4 requires native promotion evidence' }, + }; + } + const firstOracle = oracleEvaluate(schema, corpus.cases); + if (firstOracle.status !== 'OK') { + return { + ...base, + status: 'BLOCKED', + acl: 'ACL1', + schemaRoot: realityRoot(schema), + differential: { status: 'BLOCKED', reason: firstOracle.reason, details: firstOracle.details }, + mutationControls: [], + replay: { status: 'BLOCKED' }, + nativeCandidate: { status: 'BLOCKED', reason: 'independent oracle did not execute' }, + nativePromotion: { status: 'BLOCKED', reason: 'independent oracle did not execute' }, + }; + } + const differential = compareCandidateToOracle(schema, corpus.cases, firstOracle.outputs); + const mutationControls = runMutationControls(schema, corpus.cases, firstOracle.outputs); + const replayOracle = oracleEvaluate(schema, corpus.cases); + const replayCandidate = compareCandidateToOracle(schema, corpus.cases, replayOracle.outputs); + const replay = { + status: replayOracle.status === 'OK' && differential.exact === replayCandidate.exact && JSON.stringify(firstOracle.outputs) === JSON.stringify(replayOracle.outputs) ? 'VERIFIED' : 'BLOCKED', + oracleRoot: realityRoot(firstOracle.outputs), + replayOracleRoot: realityRoot(replayOracle.outputs ?? []), + candidateRoot: realityRoot(differential.candidateOutputs), + replayCandidateRoot: realityRoot(replayCandidate.candidateOutputs), + }; + const mutationPass = mutationControls.length === 5 && mutationControls.every(item => item.detected); + const differentialPass = differential.exact && mutationPass && replay.status === 'VERIFIED'; + return { + ...base, + status: differentialPass ? 'CANDIDATE' : 'NEGATIVE_RESULT', + acl: aclFor({ contract: true, differential: differentialPass }), + schemaRoot: realityRoot(schema), + differential: { + status: differentialPass ? 'VERIFIED' : 'NEGATIVE_RESULT', + exact: differential.exact, + caseCount: corpus.cases.length, + mismatchCount: differential.mismatches.length, + positiveNegativeBoundary: corpus.classificationCounts, + candidateOutputRoot: realityRoot(differential.candidateOutputs), + oracleOutputRoot: realityRoot(firstOracle.outputs), + mismatches: differential.mismatches.slice(0, 8), + }, + mutationControls: { status: mutationPass ? 'VERIFIED' : 'NEGATIVE_RESULT', controls: mutationControls, detectedCount: mutationControls.filter(item => item.detected).length }, + replay, + nativeCandidate: { status: 'BLOCKED', reason: 'No separate C/RCL JSON Schema Native Organ candidate is admitted by this compatibility surface.' }, + nativePromotion: { status: 'BLOCKED', reason: 'Native Candidate, Native Process, Semantic Root, Replay, and Promotion gates are not present for this donor.' }, + }; +} + +export async function runRbc13CapabilityAssimilationCompatibilitySurface(options = {}) { + const contract = options.contract ?? loadRbc13JsonSchemaDonorContract(); + const corpus = options.corpus ?? buildRbc13JsonSchemaDonorCorpus(); + const models = options.models ?? RBC13_CAPABILITY_ASSIMILATION_TIERS; + const listedModels = options.listedModels ?? listModels(); + const protocol = { + endpoint: endpointFrom(options.endpoint ?? process.env.OLLAMA_HOST ?? '127.0.0.1:11434'), + temperature: options.temperature ?? 0, + seed: options.seed ?? 13013, + numPredict: options.numPredict ?? 768, + timeoutMs: options.timeoutMs ?? 180_000, + maxAutomaticRepairs: options.maxAutomaticRepairs ?? 2, + }; + const attempts = []; + for (const spec of models) { + const info = modelInfo(spec.model, listedModels); + if (!info.available) { + attempts.push({ tier: spec.tier, model: spec.model, modelInfo: info, status: 'BLOCKED', acl: 'ACL0', humanRepairs: 0, automaticRepairs: 0, candidateStartedEmpty: true, blockedReason: 'model-not-present-in-ollama-list' }); + continue; + } + attempts.push(await runModel({ spec, contract, corpus, protocol, info, oracle: null })); + } + const tested = attempts.filter(item => item.attempts?.some(attempt => attempt.status === 'live')); + const allRequiredModelsPresent = attempts.length === models.length && attempts.every(item => item.modelInfo?.available === true); + const bestAclIndex = Math.max(...attempts.map(item => RBC13_CAPABILITY_ASSIMILATION_ACL_LEVELS.indexOf(item.acl)), -1); + const bestAcl = bestAclIndex >= 0 ? RBC13_CAPABILITY_ASSIMILATION_ACL_LEVELS[bestAclIndex] : null; + const formalA10 = attempts.length === models.length + && attempts.every(item => item.acl === 'ACL4' && item.humanRepairs === 0 && item.differential?.status === 'VERIFIED' && item.mutationControls?.status === 'VERIFIED' && item.replay?.status === 'VERIFIED'); + const status = formalA10 ? 'VERIFIED' : (allRequiredModelsPresent && tested.length === models.length ? 'NEGATIVE_RESULT' : 'BLOCKED'); + const body = { + format: RBC13_CAPABILITY_ASSIMILATION_COMPATIBILITY_SURFACE_FORMAT, + version: '0.1.0', + status, + surface: 'RCL Capability Assimilation Compatibility Surface', + protocol: { + version: RBC13_CAPABILITY_ASSIMILATION_PROTOCOL_VERSION, + promptRoot: sha256(RBC13_CAPABILITY_ASSIMILATION_PROMPT), + donorContractRoot: realityRoot(contract), + corpusRoot: corpus.root, + endpoint: protocol.endpoint, + temperature: protocol.temperature, + seed: protocol.seed, + format: 'json', + numPredict: protocol.numPredict, + maxAutomaticRepairs: protocol.maxAutomaticRepairs, + sameProtocolForAllModels: true, + candidateStartsEmpty: true, + hiddenCorpus: true, + oracleHidden: true, + previousCandidateHidden: true, + humanRepairs: 0, + }, + donor: { + format: contract.format, + root: realityRoot(contract), + subset: contract.subset, + contractValidation: validateRbc13JsonSchemaDonorContract(donorSchemaFromContract(contract)), + }, + corpus: { + format: corpus.format, + root: corpus.root, + deterministic: corpus.deterministic, + hiddenFromModel: corpus.hiddenFromModel, + caseCount: corpus.caseCount, + classificationCounts: corpus.classificationCounts, + mutationControls: RBC13_JSON_SCHEMA_MUTATION_CONTROLS, + }, + oracle: { + path: 'oracle/rbc13-json-schema-donor-oracle.mjs', + implementation: 'Ajv2020', + dependency: 'ajv@8.20.0', + processBoundary: 'separate Node process per model differential and replay', + sharedCandidateImports: false, + semanticProjection: ['valid', 'errors[].keyword', 'errors[].instancePath', 'errors[].schemaPath', 'errors[].params'], + }, + attempts, + accessControlLevels: { + ACL0: 'invalid donor contract or unavailable donor response', + ACL1: 'valid contract but candidate fails independent differential', + ACL2: 'candidate passes independent differential, mutation controls, and replay', + ACL3: 'Native Organ candidate contract separately verified', + ACL4: 'Native Promotion separately verified', + }, + summary: { + modelCount: models.length, + liveModelCount: tested.length, + availableModelCount: attempts.filter(item => item.modelInfo?.available === true).length, + humanRepairs: 0, + automaticRepairs: attempts.reduce((sum, item) => sum + Number(item.automaticRepairs ?? 0), 0), + bestAcl, + aclByModel: Object.fromEntries(attempts.map(item => [item.tier, item.acl ?? null])), + independentDifferentialVerifiedModels: attempts.filter(item => item.differential?.status === 'VERIFIED').map(item => item.tier), + nativePromotionVerifiedModels: attempts.filter(item => item.nativePromotion?.status === 'VERIFIED').map(item => item.tier), + formalA10, + assimilationMonotonic: 'NOT_ESTABLISHED', + compatibilityConclusion: 'Compatibility is alignment-sensitive and donor/protocol scoped; model scale alone does not establish monotonic assimilation.', + }, + strictGrowthAssessment: { + globalLevel: bestAcl === 'ACL2' || bestAcl === 'ACL3' || bestAcl === 'ACL4' ? 'Level 2 VERIFIED' : 'Level 2 CANDIDATE', + nextLevel: formalA10 ? 'Level 3 CANDIDATE' : 'Level 3 CANDIDATE/BLOCKED', + reason: 'Only an AI-generated implementation with independent differential verification can establish strict Level 3. Native promotion is a separate gate and is not inferred from schema compatibility.', + }, + formalA10: { + status: formalA10 ? 'VERIFIED' : 'NEGATIVE_RESULT', + requiresNativePromotion: true, + chain: ['AI-generated donor', 'contract', 'independent positive/negative differential', 'mutation controls', 'replay', 'Native Candidate', 'Native Process', 'Semantic Root', 'Replay', 'Promotion'], + conclusion: formalA10 ? 'All models reached ACL4 with zero human repair.' : 'No Native Promotion proof is present; formal A10 is not VERIFIED.', + }, + boundaries: [ + 'This is a domain/protocol compatibility result, not a general intelligence ranking.', + 'The candidate adapter is not the independent oracle and the oracle imports no RCL candidate helper.', + 'No model receives the hidden corpus, oracle implementation, mutation controls, previous candidate, or human repair.', + 'No ACL2 result grants native execution, canonical permission, or promotion authority.', + ], + reproductionCommand: 'npm run verify:rbc13-ai-assimilation-compatibility', + }; + return Object.freeze({ ...body, root: realityRoot(body) }); +} + +export function renderRbc13CapabilityAssimilationCompatibilitySurfaceMarkdown(report) { + const rows = report.attempts.map(item => `| ${item.tier ?? 'unknown'} | ${item.model} | ${item.modelInfo?.modelVersion ?? 'UNAVAILABLE'} | ${item.status} | ${item.acl ?? 'ACL0'} | ${item.humanRepairs ?? 0} | ${item.automaticRepairs ?? 0} | ${item.differential?.status ?? 'NOT_RUN'} | ${item.nativePromotion?.status ?? 'BLOCKED'} |`).join('\n'); + return `# RCL Capability Assimilation Compatibility Surface v0.1\n\n- Status: **${report.status}**\n- Evidence root: \`${report.root}\`\n- Donor contract root: \`${report.donor.root}\`\n- Corpus root: \`${report.corpus.root}\`; cases=${report.corpus.caseCount}; positive=${report.corpus.classificationCounts.positive}; negative=${report.corpus.classificationCounts.negative}; boundary=${report.corpus.classificationCounts.boundary}\n- Independent oracle: **${report.oracle.implementation} ${report.oracle.dependency}**, separate process\n- Human repairs: **${report.summary.humanRepairs}**; automatic repairs: **${report.summary.automaticRepairs}**\n\n## Model results\n\n| Tier | Model | Ollama version | Status | ACL | Human repair | Auto repair | Differential | Native Promotion |\n|---|---|---|---|---|---:|---:|---|---|\n${rows}\n\n## ACL ruling\n\n${Object.entries(report.accessControlLevels).map(([level, description]) => `- **${level}**: ${description}`).join('\n')}\n\nBest observed ACL: **${report.summary.bestAcl ?? 'none'}**. Strict global growth assessment: **${report.strictGrowthAssessment.globalLevel}**; next level: **${report.strictGrowthAssessment.nextLevel}**.\n\n## Differential contract\n\nThe comparison projection is exact over ${report.oracle.semanticProjection.join(', ')}. Mutation controls: ${report.corpus.mutationControls.map(item => item.id).join(', ')}. ${report.summary.compatibilityConclusion}\n\n## Formal A10\n\n- Status: **${report.formalA10.status}**\n- Requires Native Promotion: **${report.formalA10.requiresNativePromotion}**\n- Chain: ${report.formalA10.chain.join(' → ')}\n- Conclusion: ${report.formalA10.conclusion}\n\n## Boundary\n\n${report.boundaries.map(item => `- ${item}`).join('\n')}\n`; +} + diff --git a/src/rbc13-domain-bytecode-candidate.mjs b/src/rbc13-domain-bytecode-candidate.mjs new file mode 100644 index 00000000..32783957 --- /dev/null +++ b/src/rbc13-domain-bytecode-candidate.mjs @@ -0,0 +1,158 @@ +import { decodeBytecode } from './bytecode.mjs'; + +export const RBC13_DOMAIN_BYTECODE_VERSION = Object.freeze({ major: 1, minor: 3 }); +export const RBC13_DOMAIN_CALL_OPCODE = 45; +export const RBC13_DOMAIN_CALL_FLAGS = Object.freeze({ literal: 0, dynamic: 1 }); + +const BASE = Object.freeze({ + PUSH_NUMBER: 1, + PUSH_BOOL: 2, + PUSH_STRING: 3, + LOAD_STATE: 4, + STORE_STATE: 5, + CALL_BUILTIN: 30, + HALT: 31, +}); + +const BUILTIN = Object.freeze({ + EMPTY_SEQUENCE: 12, + SEQUENCE_APPEND: 13, +}); + +class Pool { + constructor() { + this.strings = []; + this.stringIds = new Map(); + this.numbers = []; + this.numberIds = new Map(); + } + string(value) { + const text = String(value); + if (this.stringIds.has(text)) return this.stringIds.get(text); + const id = this.strings.length; + this.strings.push(text); + this.stringIds.set(text, id); + return id; + } + number(value) { + const number = Number(value); + const key = Object.is(number, -0) ? '-0' : String(number); + if (this.numberIds.has(key)) return this.numberIds.get(key); + const id = this.numbers.length; + this.numbers.push(number); + this.numberIds.set(key, id); + return id; + } +} + +function argumentInstructions(pool, value, depth = 0) { + if (depth > 64) throw new TypeError('RBC 1.3 DOMAIN_CALL candidate argument nesting exceeds 64 levels'); + // Non-finite numbers are encoded only so negative controls can prove that + // the native Domain Value membrane rejects them before organ invocation. + if (typeof value === 'number') return [{ op: BASE.PUSH_NUMBER, a: pool.number(value) }]; + if (typeof value === 'boolean') return [{ op: BASE.PUSH_BOOL, a: value ? 1 : 0 }]; + if (typeof value === 'string') return [{ op: BASE.PUSH_STRING, a: pool.string(value) }]; + if (Array.isArray(value)) { + const instructions = [{ op: BASE.CALL_BUILTIN, a: BUILTIN.EMPTY_SEQUENCE, b: 0 }]; + for (const item of value) { + instructions.push(...argumentInstructions(pool, item, depth + 1)); + instructions.push({ op: BASE.CALL_BUILTIN, a: BUILTIN.SEQUENCE_APPEND, b: 2 }); + } + return instructions; + } + if (value && typeof value === 'object' && !Array.isArray(value) + && Object.keys(value).length === 1 && typeof value.$state === 'string' && value.$state.length > 0) { + return [{ op: BASE.LOAD_STATE, a: pool.string(value.$state) }]; + } + throw new TypeError( + 'RBC 1.3 DOMAIN_CALL candidate arguments accept Number, Truth, Text, recursive Sequence, or { $state: "path" } references', + ); +} + +function encode({ pool, instructions, programNameIndex, sourceRootIndex }) { + const stringBytes = pool.strings.map(text => Buffer.from(text, 'utf8')); + const size = 36 + + stringBytes.reduce((sum, bytes) => sum + 4 + bytes.length, 0) + + pool.numbers.length * 8 + + instructions.length * 16; + const buffer = Buffer.alloc(size); + let offset = 0; + buffer.write('RCLB', offset, 4, 'ascii'); offset += 4; + buffer.writeUInt16LE(1, offset); offset += 2; + buffer.writeUInt16LE(3, offset); offset += 2; + buffer.writeUInt32LE(0, offset); offset += 4; + buffer.writeUInt32LE(programNameIndex, offset); offset += 4; + buffer.writeUInt32LE(sourceRootIndex, offset); offset += 4; + buffer.writeUInt32LE(pool.strings.length, offset); offset += 4; + buffer.writeUInt32LE(pool.numbers.length, offset); offset += 4; + buffer.writeUInt32LE(instructions.length, offset); offset += 4; + buffer.writeUInt32LE(0, offset); offset += 4; + for (const bytes of stringBytes) { + buffer.writeUInt32LE(bytes.length, offset); offset += 4; + bytes.copy(buffer, offset); offset += bytes.length; + } + for (const number of pool.numbers) { + buffer.writeDoubleLE(number, offset); offset += 8; + } + for (const instruction of instructions) { + buffer.writeUInt8(instruction.op, offset); offset += 1; + buffer.writeUInt8(instruction.flags ?? 0, offset); offset += 1; + buffer.writeUInt16LE(0, offset); offset += 2; + buffer.writeInt32LE(instruction.a ?? 0, offset); offset += 4; + buffer.writeInt32LE(instruction.b ?? 0, offset); offset += 4; + buffer.writeInt32LE(instruction.c ?? 0, offset); offset += 4; + } + return buffer; +} + +/** Candidate-only RBC 1.3 assembler. It never changes canonical compileRealityToBytecode lowering. */ +export function assembleRbc13DomainCallProgram({ + program = 'Rbc13DomainCallCandidate', + sourceRoot = 'candidate:rbc13-domain-call', + calls, +}) { + if (!Array.isArray(calls) || calls.length === 0) throw new TypeError('calls must be a non-empty array'); + const pool = new Pool(); + const instructions = []; + const programNameIndex = pool.string(program); + const sourceRootIndex = pool.string(sourceRoot); + for (const [index, call] of calls.entries()) { + if (!call || typeof call !== 'object') throw new TypeError(`calls[${index}] must be an object`); + const domain = String(call.domain ?? ''); + const operation = String(call.operation ?? ''); + const target = String(call.target ?? ''); + const args = call.args ?? []; + if (!domain || !operation || !target || !Array.isArray(args)) throw new TypeError(`calls[${index}] requires domain, operation, target and args[]`); + const dynamic = call.dynamic === true; + if (dynamic) { + instructions.push({ op: BASE.PUSH_STRING, a: pool.string(domain) }); + instructions.push({ op: BASE.PUSH_STRING, a: pool.string(operation) }); + } + for (const value of args) instructions.push(...argumentInstructions(pool, value)); + instructions.push({ + op: RBC13_DOMAIN_CALL_OPCODE, + flags: dynamic ? RBC13_DOMAIN_CALL_FLAGS.dynamic : RBC13_DOMAIN_CALL_FLAGS.literal, + a: dynamic ? 0 : pool.string(domain), + b: dynamic ? 0 : pool.string(operation), + c: args.length, + }); + instructions.push({ op: BASE.STORE_STATE, a: pool.string(target) }); + } + instructions.push({ op: BASE.HALT }); + return encode({ pool, instructions, programNameIndex, sourceRootIndex }); +} + +export function decodeRbc13DomainCallCandidate(bufferLike) { + const decoded = decodeBytecode(bufferLike); + const instructions = decoded.instructions.map(item => { + if (item.op !== RBC13_DOMAIN_CALL_OPCODE) return item; + return Object.freeze({ + ...item, + name: 'DOMAIN_CALL', + domain: item.flags === RBC13_DOMAIN_CALL_FLAGS.literal ? decoded.strings[item.a] : undefined, + operation: item.flags === RBC13_DOMAIN_CALL_FLAGS.literal ? decoded.strings[item.b] : undefined, + argc: item.c, + }); + }); + return Object.freeze({ ...decoded, instructions }); +} diff --git a/src/rbc13-domain-call-differential.mjs b/src/rbc13-domain-call-differential.mjs new file mode 100644 index 00000000..b78ae5f1 --- /dev/null +++ b/src/rbc13-domain-call-differential.mjs @@ -0,0 +1,165 @@ +import { quantity, measurement } from './quantity.mjs'; +import { knowledgeClaim } from './knowledge.mjs'; +import { runIndependentDifferentialAbsorption } from './differential-absorption-runner.mjs'; +import { invokeRbc13DomainCallReference } from './rbc13-domain-call-salvage.mjs'; + +export const RBC13_DOMAIN_CALL_DIFFERENTIAL_FORMAT = + 'taowind.rcl-rbc13-domain-call-differential.v0.1'; + +function invokeCurrentOracle(input) { + const { domain, operation, args = [] } = input ?? {}; + const key = `${domain}.${operation}`; + if (key === 'core.echo') return args[0]; + if (key === 'quantity.make') { + return quantity(args[0], args[1], args[2] || undefined); + } + if (key === 'quantitative.measure') { + return measurement(args[0], args[1], { + uncertainty: args[2], + confidence: args[3], + unit: args[4] || undefined, + scale: args[5], + evidence: args[6], + calibratedBy: args[7] || null, + }); + } + if (key === 'knowledge.claim') { + return knowledgeClaim(args[0], args[1], { + confidence: args[2], + evidence: args[3], + source: args[4] || null, + scope: args[5], + status: args[6], + dependencies: args[7], + revision: args[8], + formedAtRoot: args[9] || null, + }); + } + const error = new Error(`Current oracle does not expose '${key}'`); + error.code = 'RCL_DOMAIN_OPERATION_MISSING'; + throw error; +} + +function sourceAdapterExecute(input) { + return invokeRbc13DomainCallReference( + input?.domain, + input?.operation, + input?.args ?? [], + ); +} + +function currentOracleAdapterExecute(input) { + return invokeCurrentOracle(input); +} + +function mutationControlExecute(input) { + return { + mutation: 'wrap-output', + original: invokeCurrentOracle(input), + }; +} + +export const RBC13_DOMAIN_CALL_DIFFERENTIAL_CASES = Object.freeze([ + Object.freeze({ + id: 'core_echo_text', + tags: ['positive', 'core'], + input: Object.freeze({ domain: 'core', operation: 'echo', args: Object.freeze(['hello']) }), + }), + Object.freeze({ + id: 'quantity_temperature', + tags: ['positive', 'quantity'], + input: Object.freeze({ domain: 'quantity', operation: 'make', args: Object.freeze(['Temperature', 25, '']) }), + }), + Object.freeze({ + id: 'measurement_number', + tags: ['positive', 'quantitative'], + input: Object.freeze({ + domain: 'quantitative', + operation: 'measure', + args: Object.freeze(['Number', 42, 0.25, 0.9, '', 'ratio', Object.freeze(['probe:A']), 'probe-A']), + }), + }), + Object.freeze({ + id: 'knowledge_number', + tags: ['positive', 'knowledge'], + input: Object.freeze({ + domain: 'knowledge', + operation: 'claim', + args: Object.freeze(['Number', 42, 0.8, Object.freeze(['probe:A']), 'lab', 'local', 'provisional', Object.freeze([]), 1, 'root-1']), + }), + }), + Object.freeze({ + id: 'invalid_quantity_type', + tags: ['negative', 'quantity', 'type'], + input: Object.freeze({ domain: 'quantity', operation: 'make', args: Object.freeze(['NotAQuantity', 1, '']) }), + }), + Object.freeze({ + id: 'invalid_measurement_confidence', + tags: ['negative', 'quantitative', 'range'], + input: Object.freeze({ + domain: 'quantitative', + operation: 'measure', + args: Object.freeze(['Number', 42, 0.25, 2, '', 'ratio', Object.freeze([]), null]), + }), + }), +]); + +export async function runRbc13DomainCallDifferential(options = {}) { + const report = await runIndependentDifferentialAbsorption({ + capability: 'rbc13_domain_call_reference_salvage', + source: { + id: 'legacy_domain_call_reference', + runtime: 'rcl-stale-reference-semantics-reconstructed-on-current-modules', + provenance: [ + 'agent/advanced-runtime-rcl:src/runtime.mjs#invokeInternalDomain', + 'research/rbc13-domain-call-salvage-v0.1:src/rbc13-domain-call-salvage.mjs', + ], + execute: sourceAdapterExecute, + }, + absorbed: { + id: 'current_module_oracle', + runtime: 'rcl-current-source-modules', + provenance: [ + 'src/quantity.mjs', + 'src/knowledge.mjs', + ], + execute: currentOracleAdapterExecute, + }, + cases: RBC13_DOMAIN_CALL_DIFFERENTIAL_CASES, + repeats: options.repeats ?? 3, + timeoutMs: options.timeoutMs ?? 5_000, + requireDeterministicReplay: true, + requireNegativeControl: true, + negativeControls: [ + { + id: 'wrapped_output_mutant', + mustDifferCaseIds: RBC13_DOMAIN_CALL_DIFFERENTIAL_CASES + .filter(item => item.tags.includes('positive')) + .map(item => item.id), + adapter: { + id: 'domain_call_wrapped_output_mutant', + runtime: 'rcl-current-source-modules-mutant', + provenance: ['synthetic-negative-control:wrap-output'], + execute: mutationControlExecute, + }, + }, + ], + }); + + return Object.freeze({ + format: RBC13_DOMAIN_CALL_DIFFERENTIAL_FORMAT, + status: report.passed ? 'PASS' : 'FAIL', + migrationParityPassed: report.passed, + nativePromotionAllowed: false, + genericPromotionEligible: report.promotionEligible, + evidenceScore: report.score, + differentialRoot: report.root, + caseCount: report.caseCount, + passedCaseCount: report.passedCaseCount, + controlsPassed: report.controlsPassed, + coverage: report.coverage, + boundary: + 'This proves current-process migration parity between two separately invoked source adapters. It does not prove RBC 1.3 lowering, native-VM parity, Foundation authority equivalence, or canonical promotion.', + report, + }); +} diff --git a/src/rbc13-domain-call-salvage.mjs b/src/rbc13-domain-call-salvage.mjs new file mode 100644 index 00000000..b2cb847c --- /dev/null +++ b/src/rbc13-domain-call-salvage.mjs @@ -0,0 +1,200 @@ +import { createHash } from 'node:crypto'; +import { RCLRuntimeError } from './errors.mjs'; +import { quantity, measurement } from './quantity.mjs'; +import { knowledgeClaim } from './knowledge.mjs'; + +export const RBC13_DOMAIN_CALL_SALVAGE_FORMAT = + 'taowind.rcl-rbc13-domain-call-salvage.v0.1'; + +export const RBC13_DOMAIN_CALL_ABI_CANDIDATE = Object.freeze({ + bytecodeMajor: 1, + bytecodeMinor: 3, + opcode: 45, + opcodeName: 'DOMAIN_CALL', + literalDispatchFlag: 0, + dynamicDispatchFlag: 1, + canonicalEnabled: false, + currentCanonicalFeatureVersion: '1.2', + provenance: Object.freeze({ + branch: 'agent/advanced-runtime-rcl', + firstCommit: '70326b1c9e754410576975d08ae49df7d7ade21b', + finalCommit: '4246f11b696d19cef5f92f43f76cfd026fa1f09f', + }), +}); + +const LEGACY_REFERENCE_OPERATION_SPECS = Object.freeze([ + Object.freeze({ + key: 'core.echo', + domain: 'core', + operation: 'echo', + evidenceClass: 'legacy-js-reference-and-native-candidate', + currentOracle: 'identity', + }), + Object.freeze({ + key: 'quantity.make', + domain: 'quantity', + operation: 'make', + evidenceClass: 'legacy-js-reference-and-native-candidate', + currentOracle: 'src/quantity.mjs#quantity', + }), + Object.freeze({ + key: 'quantitative.measure', + domain: 'quantitative', + operation: 'measure', + evidenceClass: 'legacy-js-reference-and-native-candidate', + currentOracle: 'src/quantity.mjs#measurement', + }), + Object.freeze({ + key: 'knowledge.claim', + domain: 'knowledge', + operation: 'claim', + evidenceClass: 'legacy-js-reference-and-native-candidate', + currentOracle: 'src/knowledge.mjs#knowledgeClaim', + }), +]); + +const LEGACY_NATIVE_ONLY_OPERATION_SPECS = Object.freeze([ + ['language.utterance', 'natural-language.interpret'], + ['language.intent', 'natural-language.interpret'], + ['understanding.model', 'understanding.model'], + ['creation.candidate', 'creative.generate'], + ['creation.select', 'creative.generate'], + ['energy.scale', 'energy.balance'], + ['element.species', 'elemental.compose'], + ['element.compound', 'elemental.compose'], + ['science.claim', null], + ['science.experiment', null], + ['body.state', 'embodiment.integrate'], + ['spirit.state', null], + ['spacetime.point', 'meta.spacetime.sequence'], + ['spacetime.retime', 'meta.spacetime.sequence'], +].map(([key, nearestCurrentBridge]) => Object.freeze({ + key, + domain: key.slice(0, key.indexOf('.')), + operation: key.slice(key.indexOf('.') + 1), + evidenceClass: 'legacy-native-only-no-current-reference-equivalence', + nearestCurrentBridge, + promotionAllowed: false, +}))); + +export const RBC13_DOMAIN_CALL_OPERATION_INVENTORY = Object.freeze([ + ...LEGACY_REFERENCE_OPERATION_SPECS, + ...LEGACY_NATIVE_ONLY_OPERATION_SPECS, +]); + +const REFERENCE_KEYS = new Set( + LEGACY_REFERENCE_OPERATION_SPECS.map(item => item.key), +); + +function normalizeText(value, label) { + if (typeof value !== 'string' || value.length === 0) { + throw new TypeError(`${label} must be a non-empty string`); + } + return value; +} + +function normalizeArgs(args) { + if (!Array.isArray(args)) throw new TypeError('domain-call args must be an array'); + return args; +} + +/** + * Source-only salvage of the four operations that had an explicit JavaScript + * reference implementation on the stale advanced-runtime branch. + * + * This function is deliberately NOT wired into the parser, canonical RBC 1.2, + * or the C native VM. It exists as an independent semantic oracle for a future + * differential/native-promotion experiment. + */ +export function invokeRbc13DomainCallReference(domainInput, operationInput, argsInput = []) { + const domain = normalizeText(domainInput, 'domain'); + const operation = normalizeText(operationInput, 'operation'); + const args = normalizeArgs(argsInput); + const key = `${domain}.${operation}`; + + if (!REFERENCE_KEYS.has(key)) { + const legacyNativeOnly = LEGACY_NATIVE_ONLY_OPERATION_SPECS.find(item => item.key === key); + if (legacyNativeOnly) { + throw new RCLRuntimeError( + 'RCL_DOMAIN_CALL_CANDIDATE_UNIMPLEMENTED', + `Legacy native-only domain operation '${key}' has no admitted current reference-equivalence oracle`, + { key, nearestCurrentBridge: legacyNativeOnly.nearestCurrentBridge }, + ); + } + throw new RCLRuntimeError( + 'RCL_DOMAIN_OPERATION_MISSING', + `Domain operation '${key}' is not present in the RBC 1.3 salvage inventory`, + { key }, + ); + } + + if (key === 'core.echo') { + if (args.length !== 1) throw new RCLRuntimeError('RCL_DOMAIN_CORE_ECHO_ARITY', 'core.echo expects one argument'); + return args[0]; + } + if (key === 'quantity.make') { + return quantity(args[0], args[1], args[2] || undefined); + } + if (key === 'quantitative.measure') { + return measurement(args[0], args[1], { + uncertainty: args[2], + confidence: args[3], + unit: args[4] || undefined, + scale: args[5], + evidence: args[6], + calibratedBy: args[7] || null, + }); + } + if (key === 'knowledge.claim') { + return knowledgeClaim(args[0], args[1], { + confidence: args[2], + evidence: args[3], + source: args[4] || null, + scope: args[5], + status: args[6], + dependencies: args[7], + revision: args[8], + formedAtRoot: args[9] || null, + }); + } + + throw new RCLRuntimeError( + 'RCL_DOMAIN_OPERATION_MISSING', + `Domain operation '${key}' has no salvage implementation`, + { key }, + ); +} + +function canonicalize(value) { + if (Array.isArray(value)) return value.map(canonicalize); + if (!value || typeof value !== 'object') return value; + return Object.fromEntries( + Object.keys(value).sort().map(key => [key, canonicalize(value[key])]), + ); +} + +function sha256(value) { + return createHash('sha256').update(JSON.stringify(canonicalize(value))).digest('hex'); +} + +export function buildRbc13DomainCallSalvageReport() { + const report = { + format: RBC13_DOMAIN_CALL_SALVAGE_FORMAT, + status: 'CANDIDATE', + sourceBranch: RBC13_DOMAIN_CALL_ABI_CANDIDATE.provenance.branch, + candidateAbi: RBC13_DOMAIN_CALL_ABI_CANDIDATE, + inventory: RBC13_DOMAIN_CALL_OPERATION_INVENTORY, + counts: { + totalLegacyOperations: RBC13_DOMAIN_CALL_OPERATION_INVENTORY.length, + admittedReferenceOperations: LEGACY_REFERENCE_OPERATION_SPECS.length, + quarantinedLegacyNativeOnlyOperations: LEGACY_NATIVE_ONLY_OPERATION_SPECS.length, + }, + authority: { + canonicalBytecodeMutationAllowed: false, + nativeFoundationSyntaxClaimAllowed: false, + oldBinaryReuseAllowed: false, + nextPromotionGate: 'independent differential execution against current reference/provider oracles', + }, + }; + return Object.freeze({ ...report, evidenceRoot: sha256(report) }); +} diff --git a/src/rbc13-domain-native-promotion.mjs b/src/rbc13-domain-native-promotion.mjs new file mode 100644 index 00000000..e0914240 --- /dev/null +++ b/src/rbc13-domain-native-promotion.mjs @@ -0,0 +1,362 @@ +import { realityRoot } from './canonical.mjs'; +import { createExecutionObservation, runIndependentDifferentialAbsorption } from './differential-absorption-runner.mjs'; +import { invokeRbc13DomainCallReference } from './rbc13-domain-call-salvage.mjs'; +import { buildAllRbc13DomainOrganCandidatePlans } from './rbc13-domain-organ-candidate-plan.mjs'; +import { rbc13DomainOperationCases } from './rbc13-domain-operation-differential.mjs'; +import { + buildRbc13DomainCandidateHost, + buildRbc13NativeOperationProgram, + resolveDomainCandidateCompiler, +} from './rbc13-domain-native-runtime.mjs'; +import { admitNativeVerifiedDomainOrgan } from './domain-operation-organ.mjs'; + +export const RBC13_DOMAIN_NATIVE_PROMOTION_VERSION = '0.1.0-alpha.1'; +export const RBC13_DOMAIN_NATIVE_PROMOTION_REPORT_FORMAT = + 'rcl.domain-organ-native-promotion-report.v0.1'; + +function pureOperationObservation(output) { + return createExecutionObservation({ + status: 'ok', + output, + effects: [{ kind: 'internal-domain-evaluation', externalMutation: false, persistentMutation: false }], + evidence: [{ kind: 'semantic-contract', contract: 'rbc13-domain-operation-differential.v0.1' }], + resourceDelta: { externalResourcesCreated: 0, externalResourcesMutated: 0, persistentStateMutation: false }, + authority: { required: false, boundary: 'pure-internal-domain-operation' }, + exitCode: 0, + }); +} + +function currentExecute(input) { + return pureOperationObservation(invokeRbc13DomainCallReference( + input?.domain, + input?.operation, + input?.args ?? [], + )); +} + +function positiveCaseIds(cases) { + return new Set(cases.filter(item => item.tags?.includes('positive')).map(item => item.id)); +} + +function nativeRootChecks(nativeDifferential, cases) { + const positives = positiveCaseIds(cases); + const checks = []; + for (const item of nativeDifferential.cases ?? []) { + if (!positives.has(item.id)) continue; + for (const run of item.absorbed?.runs ?? []) { + const receipt = run.observation?.receipts?.find(candidate => candidate?.format === 'rcl.rbc13-domain-native-receipt.v0.1'); + checks.push(Object.freeze({ + caseId: item.id, + repetition: run.repetition, + receiptPresent: Boolean(receipt), + stateRootVerified: receipt?.stateRootVerified === true, + nativeStateRoot: receipt?.nativeStateRoot ?? null, + bytecodeRoot: receipt?.bytecodeRoot ?? null, + hostRoot: receipt?.hostRoot ?? null, + passed: Boolean(receipt) && receipt.stateRootVerified === true, + })); + } + } + return checks; +} + +function deterministicCaseManifest(operationKey, cases) { + return Object.freeze(cases.map(testCase => { + const first = buildRbc13NativeOperationProgram(testCase.input); + const second = buildRbc13NativeOperationProgram(testCase.input); + const deterministic = first.bytecode.equals(second.bytecode) && first.bytecodeRoot === second.bytecodeRoot; + const manifest = { + id: testCase.id, + operationKey, + inputRoot: realityRoot(testCase.input), + bytecodeVersion: '1.3', + opcode: 45, + bytecodeRoot: first.bytecodeRoot, + byteLength: first.bytecode.length, + callCount: first.calls.length, + deterministic, + }; + return Object.freeze({ ...manifest, root: realityRoot(manifest) }); + })); +} + +async function runNativeDifferential(plan, runtime, options = {}) { + const cases = rbc13DomainOperationCases(plan.operationKey); + return runIndependentDifferentialAbsorption({ + capability: plan.differential.capability, + source: { + id: `current_${plan.operationKey.replaceAll('.', '_')}_promotion_oracle`, + runtime: 'rcl-current-source-modules', + provenance: ['src/rbc13-domain-call-salvage.mjs', 'src/quantity.mjs', 'src/knowledge.mjs'], + execute: currentExecute, + }, + absorbed: { + id: `native_${plan.operationKey.replaceAll('.', '_')}_opcode45_candidate`, + runtime: 'rcl-rbc13-domain-candidate-native-process', + artifactRoot: runtime.hostRoot, + provenance: [ + 'native/rcl_domain_admitted_organs.c', + 'native/rcl_domain_organ.c', + 'native/rcl_domain_value.c', + 'scripts/materialize-rbc13-domain-vm-public-api.mjs', + ], + execute(input, context) { + return runtime.execute(input, { + caseId: `${plan.operationKey.replaceAll('.', '_')}_${context.caseId}`, + timeout: options.runTimeout, + }); + }, + }, + cases, + repeats: options.nativeRepeats ?? 2, + timeoutMs: options.differentialTimeout ?? 60_000, + requireDeterministicReplay: true, + requireNegativeControl: false, + negativeControls: [], + }); +} + +export async function promoteRbc13DomainOrganPlan(plan, runtime, options = {}) { + if (!plan || plan.format !== 'taowind.rcl-rbc13-domain-organ-candidate-plan.v0.1') { + throw new TypeError('A valid RBC13 Domain Organ candidate plan is required'); + } + if (!runtime || runtime.format !== 'taowind.rcl-rbc13-domain-native-runtime.v0.1') { + throw new TypeError('A built RBC13 Domain Organ candidate runtime is required'); + } + if (!plan.differential?.passed || !plan.differential?.promotionEligible) { + const error = new Error(`Operation ${plan.operationKey} has not cleared its semantic differential gate`); + error.code = 'RCL_RBC13_DOMAIN_SEMANTIC_GATE'; + throw error; + } + + const cases = rbc13DomainOperationCases(plan.operationKey); + const caseManifest = deterministicCaseManifest(plan.operationKey, cases); + const nativeDifferential = await runNativeDifferential(plan, runtime, options); + const rootChecks = nativeRootChecks(nativeDifferential, cases); + const bytecodeDeterministic = caseManifest.every(item => item.deterministic); + const replayDeterministic = nativeDifferential.cases.every(item => item.absorbed?.deterministic === true); + const currentNativeEquivalent = nativeDifferential.passed === true + && nativeDifferential.cases.every(item => item.comparison?.passed === true); + const nativeRootsVerified = rootChecks.length > 0 && rootChecks.every(item => item.passed); + const hostBound = typeof runtime.hostRoot === 'string' && /^[a-f0-9]{64}$/.test(runtime.hostRoot); + const sharedImplementationBound = typeof runtime.implementationRoot === 'string' + && /^[a-f0-9]{64}$/.test(runtime.implementationRoot); + const sourceRoot = runtime.sourceRoots['native/rcl_domain_admitted_organs.c']; + const operationImplementationRoot = realityRoot({ + operationKey: plan.operationKey, + implementationId: plan.candidate.implementation.id, + sharedImplementationRoot: runtime.implementationRoot, + materializedVmRoot: runtime.materializedVmRoot, + sourceRoot, + }); + + const declaredCaseIds = cases.map(item => item.id); + const nativeCaseIds = nativeDifferential.cases.map(item => item.id); + const sameCaseIds = declaredCaseIds.length === nativeCaseIds.length + && declaredCaseIds.every((id, index) => id === nativeCaseIds[index]); + const positiveCases = cases.filter(item => item.tags?.includes('positive')); + const negativeCases = cases.filter(item => item.tags?.includes('negative')); + const nativeCasesById = new Map(nativeDifferential.cases.map(item => [item.id, item])); + const positiveSemanticEquivalent = positiveCases.length > 0 + && positiveCases.every(item => nativeCasesById.get(item.id)?.comparison?.passed === true); + const negativeSemanticEquivalent = negativeCases.length > 0 + && negativeCases.every(item => nativeCasesById.get(item.id)?.comparison?.passed === true); + const operationDifferentialReport = plan.differential?.report ?? null; + const operationScopedDifferential = plan.differential?.passed === true + && plan.differential?.promotionEligible === true + && operationDifferentialReport?.independence?.satisfied === true + && Number(operationDifferentialReport?.repeats ?? 0) >= 2 + && operationDifferentialReport?.requireDeterministicReplay === true + && operationDifferentialReport?.requireNegativeControl === true + && Number(operationDifferentialReport?.negativeControls?.length ?? 0) > 0 + && operationDifferentialReport?.controlsPassed === true; + const experimentalBytecodeDeterministic = caseManifest.length === cases.length + && caseManifest.every(item => item.bytecodeVersion === '1.3' + && item.opcode === 45 + && item.deterministic + && /^[a-f0-9]{64}$/.test(item.bytecodeRoot)); + const currentNativeVmMaterialized = runtime.materializedFromCurrentSource === true + && /^[a-f0-9]{64}$/.test(runtime.materializedVmRoot) + && Object.values(runtime.nativeVmSourceRoots ?? {}).length === 2 + && Object.values(runtime.nativeVmSourceRoots ?? {}).every(root => /^[a-f0-9]{64}$/.test(root)); + const nativeReplayDeterministic = nativeDifferential.cases.length === cases.length + && nativeDifferential.cases.every(item => item.absorbed?.deterministic === true + && item.comparison?.replayPassed === true); + const nativeSemanticStateRoots = rootChecks.length === positiveCases.length * Number(nativeDifferential.repeats ?? 0) + && nativeRootsVerified; + const semanticRootParity = positiveCases.length > 0 + && positiveCases.every(item => { + const differentialCase = nativeCasesById.get(item.id); + return differentialCase?.comparison?.sourceSemanticRoot + && differentialCase?.comparison?.absorbedSemanticRoot + && differentialCase.comparison.sourceSemanticRoot === differentialCase.comparison.absorbedSemanticRoot; + }); + const rootsRecorded = hostBound + && sharedImplementationBound + && /^[a-f0-9]{64}$/.test(operationImplementationRoot) + && /^[a-f0-9]{64}$/.test(sourceRoot) + && caseManifest.every(item => /^[a-f0-9]{64}$/.test(item.root)) + && rootChecks.every(item => /^[a-f0-9]{64}$/.test(item.hostRoot) + && /^[a-f0-9]{64}$/.test(item.bytecodeRoot) + && (!item.receiptPresent || /^[a-f0-9]{64}$/.test(item.nativeStateRoot))); + const noCaseSilentlySkipped = sameCaseIds + && nativeDifferential.caseCount === caseManifest.length + && nativeDifferential.cases.length === caseManifest.length + && nativeDifferential.failedCaseCount === 0; + const nativePromotionEvidenceTier = plan.candidate.evidenceTier === 'native-candidate' + && plan.candidate.canonicalAdmission === false + && plan.promotion?.nativePromotionRequired === true + && nativeDifferential.promotionEligible === true; + + const gates = Object.freeze({ + G1_operationScopedDifferential: operationScopedDifferential, + G2_experimentalRbc13BytesDeterministic: experimentalBytecodeDeterministic, + G3_currentNativeSourceMaterialized: currentNativeVmMaterialized, + G4_candidateNativeHostBuilt: hostBound, + G5_positiveSemanticEquivalence: positiveSemanticEquivalent, + G6_negativeSemanticEquivalence: negativeSemanticEquivalent, + G7_nativeReplayDeterministic: nativeReplayDeterministic, + G8_nativeSemanticStateRootEmittedAndVerified: nativeSemanticStateRoots, + G9_semanticRootParity: semanticRootParity, + G10_allEvidenceRootsRecorded: rootsRecorded, + G11_noCaseSilentlySkipped: noCaseSilentlySkipped, + G12_nativePromotionEvidenceTier: nativePromotionEvidenceTier, + }); + + const checks = Object.freeze({ + semanticDifferentialPassed: plan.differential?.passed === true, + semanticDifferentialPromotionEligible: plan.differential?.promotionEligible === true, + nativeDifferentialPassed: nativeDifferential.passed === true, + currentNativeEquivalent, + bytecodeDeterministic, + replayDeterministic, + nativeRootsVerified, + hostBound, + sharedImplementationBound, + caseCountAligned: sameCaseIds && nativeDifferential.caseCount === caseManifest.length, + }); + const verified = Object.values(gates).every(Boolean); + const caseReports = caseManifest.map(manifest => { + const differentialCase = nativeDifferential.cases.find(item => item.id === manifest.id); + const report = { + id: manifest.id, + manifestRoot: manifest.root, + bytecodeRoot: manifest.bytecodeRoot, + byteLength: manifest.byteLength, + deterministicBytecode: manifest.deterministic, + semanticEquivalent: differentialCase?.comparison?.equivalent === true, + replayPassed: differentialCase?.comparison?.replayPassed === true, + sourceSemanticRoot: differentialCase?.comparison?.sourceSemanticRoot ?? null, + nativeSemanticRoot: differentialCase?.comparison?.absorbedSemanticRoot ?? null, + verified: manifest.deterministic + && differentialCase?.comparison?.passed === true, + }; + return Object.freeze({ ...report, root: realityRoot(report) }); + }); + + const report = { + format: RBC13_DOMAIN_NATIVE_PROMOTION_REPORT_FORMAT, + version: RBC13_DOMAIN_NATIVE_PROMOTION_VERSION, + operationKey: plan.operationKey, + capability: plan.differential.capability, + status: verified ? 'native-verified' : 'native-rejected', + verified, + promotionEligible: verified, + candidateRoot: plan.candidate.root, + semanticDifferentialRoot: plan.differential.differentialRoot, + nativeDifferentialRoot: nativeDifferential.root, + implementationRoot: operationImplementationRoot, + sharedImplementationRoot: runtime.implementationRoot, + nativeVm: { + experimental: true, + materializedFromCurrentSource: currentNativeVmMaterialized, + hostRoot: runtime.hostRoot, + materializedVmRoot: runtime.materializedVmRoot, + sourceRoots: runtime.nativeVmSourceRoots, + compiler: runtime.compiler, + compilerVersion: runtime.compilerVersion, + }, + checks, + gates, + operationDifferential: { + repeats: operationDifferentialReport?.repeats ?? null, + requireDeterministicReplay: operationDifferentialReport?.requireDeterministicReplay ?? null, + requireNegativeControl: operationDifferentialReport?.requireNegativeControl ?? null, + negativeControlCount: operationDifferentialReport?.negativeControls?.length ?? 0, + controlsPassed: operationDifferentialReport?.controlsPassed ?? false, + independence: operationDifferentialReport?.independence ?? null, + }, + caseCount: caseReports.length, + verifiedCaseCount: caseReports.filter(item => item.verified).length, + failedCaseCount: caseReports.filter(item => !item.verified).length, + cases: caseReports, + nativeRootChecks: rootChecks, + proofChain: [ + { kind: 'operation-semantic-differential', root: plan.differential.differentialRoot }, + { kind: 'native-process-differential', root: nativeDifferential.root }, + { kind: 'current-native-vm-source', roots: runtime.nativeVmSourceRoots }, + { kind: 'experimental-rbc13-bytecode-cases', roots: caseManifest.map(item => item.root) }, + { kind: 'operation-implementation', root: operationImplementationRoot }, + { kind: 'candidate-native-host', root: runtime.hostRoot }, + ], + gaps: verified ? [] : Object.entries(gates).filter(([, passed]) => !passed).map(([name]) => name), + bindingProofLevel: 'current-source-materialized-candidate-vm-plus-vm-emitted-semantic-root', + boundary: + 'native-verified is operation-, case-, host- and experimental-RBC-bound. It proves the declared cases through an independently invoked C candidate host materialized from current native VM source. It does not admit RBC 1.3 into the canonical language or prove the other quarantined historical operations.', + }; + return Object.freeze({ + ...report, + root: realityRoot({ + ...report, + cases: caseReports.map(item => item.root), + nativeRootChecks: rootChecks.map(item => realityRoot(item)), + }), + nativeDifferential, + }); +} + +export async function promoteAllRbc13DomainOrgans(options = {}) { + const compiler = resolveDomainCandidateCompiler(options); + if (!compiler) { + return Object.freeze({ + format: 'rcl.domain-organ-native-promotion-suite.v0.1', + status: 'native-blocked', + verified: false, + blocker: 'native-compiler-missing', + reports: Object.freeze([]), + }); + } + + const plans = await buildAllRbc13DomainOrganCandidatePlans(options); + const runtime = buildRbc13DomainCandidateHost({ ...options, compiler }); + try { + const reports = []; + const verifiedOrgans = []; + for (const plan of plans) { + const report = await promoteRbc13DomainOrganPlan(plan, runtime, options); + reports.push(report); + if (report.verified) { + verifiedOrgans.push(admitNativeVerifiedDomainOrgan({ + candidate: plan.candidate, + nativePromotionReport: report, + canonicalAdmission: false, + })); + } + } + const verified = reports.length === plans.length && reports.every(item => item.verified); + const suite = { + format: 'rcl.domain-organ-native-promotion-suite.v0.1', + status: verified ? 'native-verified' : 'native-rejected', + verified, + hostRoot: runtime.hostRoot, + sharedImplementationRoot: runtime.implementationRoot, + reportRoots: reports.map(item => item.root), + verifiedOrganRoots: verifiedOrgans.map(item => item.root), + canonicalAdmission: false, + boundary: 'Suite verification does not canonize RBC 1.3. Each result remains bounded to the declared operation corpus and selected materialized native host.', + }; + return Object.freeze({ ...suite, root: realityRoot(suite), reports: Object.freeze(reports), verifiedOrgans: Object.freeze(verifiedOrgans) }); + } finally { + runtime.close(); + } +} diff --git a/src/rbc13-domain-native-runtime.mjs b/src/rbc13-domain-native-runtime.mjs new file mode 100644 index 00000000..268d4f3f --- /dev/null +++ b/src/rbc13-domain-native-runtime.mjs @@ -0,0 +1,282 @@ +import crypto from 'node:crypto'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { createExecutionObservation } from './differential-absorption-runner.mjs'; +import { runtimeType } from './quantity.mjs'; +import { semanticValue, verifyNativeSemanticStateRoot } from './semantic-state-root.mjs'; +import { assembleRbc13DomainCallProgram } from './rbc13-domain-bytecode-candidate.mjs'; +import { materializeRbc13DomainVmWithPublicApi } from '../scripts/materialize-rbc13-domain-vm-public-api.mjs'; +import { compileNativeC, nativeCCompilerVersion, resolveNativeCCompiler } from './native-c-compiler.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); + +export const RBC13_DOMAIN_NATIVE_RUNTIME_FORMAT = 'taowind.rcl-rbc13-domain-native-runtime.v0.1'; + +function sha256(value) { + return crypto.createHash('sha256').update(value).digest('hex'); +} + +function sha256File(filePath) { + return sha256(fs.readFileSync(filePath)); +} + +export function resolveDomainCandidateCompiler(options = {}) { + return resolveNativeCCompiler(options)?.command ?? null; +} + +function pureOperationObservation(output, receipts = [], metadata = null) { + return createExecutionObservation({ + status: 'ok', + output, + effects: [{ kind: 'internal-domain-evaluation', externalMutation: false, persistentMutation: false }], + evidence: [{ kind: 'semantic-contract', contract: 'rbc13-domain-operation-differential.v0.1' }], + resourceDelta: { externalResourcesCreated: 0, externalResourcesMutated: 0, persistentStateMutation: false }, + authority: { required: false, boundary: 'pure-internal-domain-operation' }, + exitCode: 0, + receipts, + metadata, + }); +} + +function quantityLike(value) { + return Boolean(value) && typeof value === 'object' && value.kind === 'Quantity' + && typeof value.type === 'string' && typeof value.value === 'number'; +} + +function lowerStructuredArg(calls, value, label) { + if (!quantityLike(value)) return value; + const target = `__rbc13.arg.${label}`; + calls.push({ + domain: 'quantity', + operation: 'make', + args: [value.type, value.value, value.unit ?? ''], + target, + }); + return { $state: target }; +} + +export function buildRbc13NativeOperationProgram(input, options = {}) { + const domain = String(input?.domain ?? ''); + const operation = String(input?.operation ?? ''); + const operationKey = `${domain}.${operation}`; + const rawArgs = Array.isArray(input?.args) ? input.args : []; + const calls = []; + const args = rawArgs.map((value, index) => lowerStructuredArg(calls, value, `${index + 1}`)); + const target = options.target ?? '__rbc13.result'; + calls.push({ domain, operation, args, target, dynamic: options.dynamic === true || input?.dynamic === true }); + const bytecode = assembleRbc13DomainCallProgram({ + program: `Rbc13Native_${operationKey.replaceAll('.', '_')}`, + sourceRoot: `candidate:native:${operationKey}`, + calls, + }); + return Object.freeze({ operationKey, target, calls: Object.freeze(calls), bytecode, bytecodeRoot: sha256(bytecode) }); +} + +function parsePayload(run, context) { + const stdout = String(run.stdout ?? '').trim(); + if (!stdout) { + const error = new Error(`Candidate native host produced no JSON output for ${context}`); + error.code = 'RCL_RBC13_NATIVE_OUTPUT_MISSING'; + error.details = { status: run.status, stderr: String(run.stderr ?? '') }; + throw error; + } + try { + return JSON.parse(stdout); + } catch (cause) { + const error = new Error(`Candidate native host produced invalid JSON for ${context}`); + error.code = 'RCL_RBC13_NATIVE_OUTPUT_JSON'; + error.details = { stdout, stderr: String(run.stderr ?? ''), cause: String(cause?.message ?? cause) }; + throw error; + } +} + +function semanticErrorDetails(payload, input) { + const code = String(payload?.code ?? ''); + const args = Array.isArray(input?.args) ? input.args : []; + if (code === 'RCL_DOMAIN_OPERATION_MISSING') { + return { key: `${input?.domain ?? ''}.${input?.operation ?? ''}` }; + } + if (code === 'RCL_DOMAIN_CORE_ECHO_ARITY') return {}; + if (code === 'RCL_MEASUREMENT_TYPE') { + return { baseType: args[0], actual: runtimeType(args[1]) }; + } + if (code === 'RCL_UNCERTAINTY_TYPE') { + return { baseType: args[0], actual: runtimeType(args[2]) }; + } + if (code === 'RCL_CONFIDENCE_RANGE') { + return { confidence: Number(args[3]) }; + } + if (code === 'RCL_KNOWLEDGE_TYPE') { + return { baseType: args[0], actual: runtimeType(args[1]) }; + } + if (code === 'RCL_KNOWLEDGE_CONFIDENCE_RANGE') { + return { confidence: Number(args[2]) }; + } + return null; +} + +function throwSemanticNativeError(payload, receipt, input) { + const nativeCode = String(payload?.code ?? 'RCL_RBC13_NATIVE_FAILURE'); + const nonFiniteQuantity = input?.domain === 'quantity' + && input?.operation === 'make' + && !Number.isFinite(input?.args?.[1]); + const code = nonFiniteQuantity && nativeCode === 'RCL_NATIVE_DOMAIN_VALUE_UNSUPPORTED' + ? 'TypeError' + : nativeCode; + const message = nonFiniteQuantity && nativeCode === 'RCL_NATIVE_DOMAIN_VALUE_UNSUPPORTED' + ? `Quantity '${String(input?.args?.[0] ?? 'undefined')}' must be finite` + : String(payload?.message ?? 'Candidate DOMAIN_CALL failed'); + const error = new Error(message); + error.code = code; + error.details = nonFiniteQuantity && code === 'TypeError' ? null : semanticErrorDetails(payload, input); + Object.defineProperty(error, 'nativeReceipt', { + value: receipt, + enumerable: false, + configurable: false, + writable: false, + }); + throw error; +} + +export function buildRbc13DomainCandidateHost(options = {}) { + const compilerSpec = resolveNativeCCompiler({ compiler: options.compiler ?? undefined }); + if (!compilerSpec) { + const error = new Error('No supported C compiler is available for the RBC 1.3 Domain Organ candidate host'); + error.code = 'RCL_RBC13_NATIVE_COMPILER_MISSING'; + throw error; + } + const compiler = compilerSpec.command; + const root = path.resolve(options.root ?? ROOT); + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rcl-rbc13-domain-native-')); + const generatedVmPath = path.join(tempDir, 'rclvm-rbc13-domain-candidate.c'); + const hostPath = path.join(tempDir, `rbc13-domain-candidate-host${process.platform === 'win32' ? '.exe' : ''}`); + const currentNativePath = path.join(root, 'native', 'rclvm.c'); + const currentNative = fs.readFileSync(currentNativePath, 'utf8'); + const materializedVm = materializeRbc13DomainVmWithPublicApi(currentNative); + fs.writeFileSync(generatedVmPath, materializedVm); + + const sourceFiles = [ + 'native/rclvm.c', + 'native/rclvm.h', + 'native/rcl_domain_value.c', + 'native/rcl_domain_value.h', + 'native/rcl_domain_organ.c', + 'native/rcl_domain_organ.h', + 'native/rcl_domain_admitted_organs.c', + 'native/rcl_domain_admitted_organs.h', + 'native/rcl_domain_vm_value_bridge.inc', + 'native/rcl_domain_vm_candidate.h', + 'native/domain_vm_opcode45_candidate_host.c', + 'src/native-c-compiler.mjs', + 'scripts/materialize-rbc13-domain-vm-candidate.mjs', + 'scripts/materialize-rbc13-domain-vm-public-api.mjs', + ]; + const sourceRoots = Object.fromEntries(sourceFiles.map(relative => [relative, sha256File(path.join(root, relative))])); + const implementationRoot = sha256(JSON.stringify({ + format: 'rcl.rbc13-domain-native-implementation.v0.1', + materializedVmRoot: sha256(materializedVm), + sourceRoots, + })); + + const build = compileNativeC(compilerSpec, { + cwd: root, + includeDirs: [path.join(root, 'native'), tempDir], + sources: [ + path.join(root, 'native', 'rcl_domain_value.c'), + path.join(root, 'native', 'rcl_domain_organ.c'), + path.join(root, 'native', 'rcl_domain_admitted_organs.c'), + path.join(root, 'native', 'domain_vm_opcode45_candidate_host.c'), + ], + linkLibraries: process.platform === 'win32' ? ['bcrypt'] : ['crypto', 'm'], + output: hostPath, + timeout: options.buildTimeout ?? 120_000, + }); + if (build.status !== 0) { + fs.rmSync(tempDir, { recursive: true, force: true }); + const error = new Error('Failed to build the RBC 1.3 Domain Organ candidate host'); + error.code = 'RCL_RBC13_NATIVE_BUILD_FAILED'; + error.details = { compiler, status: build.status, stdout: build.stdout, stderr: build.stderr }; + throw error; + } + + const hostRoot = sha256File(hostPath); + const compilerVersion = nativeCCompilerVersion(compilerSpec) ?? compiler; + let closed = false; + + function execute(input, context = {}) { + if (closed) { + const error = new Error('RBC 1.3 Domain Organ candidate host has been closed'); + error.code = 'RCL_RBC13_NATIVE_HOST_CLOSED'; + throw error; + } + const program = buildRbc13NativeOperationProgram(input, context); + const rbcPath = path.join(tempDir, `${context.caseId ?? program.operationKey.replaceAll('.', '_')}.rbc`); + fs.writeFileSync(rbcPath, program.bytecode); + const started = process.hrtime.bigint(); + const run = spawnSync(hostPath, [rbcPath, '--candidate-minimum'], { + encoding: 'utf8', + timeout: context.timeout ?? options.runTimeout ?? 30_000, + maxBuffer: 64 * 1024 * 1024, + }); + const runtimeMs = Number(process.hrtime.bigint() - started) / 1_000_000; + const payload = parsePayload(run, context.caseId ?? program.operationKey); + const receipt = Object.freeze({ + format: 'rcl.rbc13-domain-native-receipt.v0.1', + operationKey: program.operationKey, + caseId: context.caseId ?? null, + hostRoot, + implementationRoot, + bytecodeRoot: program.bytecodeRoot, + bytecodeBytes: program.bytecode.length, + exitStatus: run.status, + runtimeMs: Number(runtimeMs.toFixed(3)), + nativeStateRoot: payload?.stateRoot ?? null, + nativeStateRootAlgorithm: payload?.stateRootAlgorithm ?? null, + }); + if (run.status !== 0) throwSemanticNativeError(payload, receipt, input); + const verified = verifyNativeSemanticStateRoot(payload, { requireNativeRoot: true }); + if (!Object.prototype.hasOwnProperty.call(verified.state ?? {}, program.target)) { + const error = new Error(`Candidate native state is missing ${program.target}`); + error.code = 'RCL_RBC13_NATIVE_RESULT_MISSING'; + error.details = { receipt, state: verified.state }; + throw error; + } + return pureOperationObservation( + semanticValue(verified.state[program.target]), + [{ ...receipt, nativeStateRoot: verified.stateRoot, stateRootVerified: verified.stateRootVerified }], + { + operationKey: program.operationKey, + stateRoot: verified.stateRoot, + stateRootVerified: verified.stateRootVerified, + bytecodeRoot: program.bytecodeRoot, + }, + ); + } + + function close() { + if (closed) return; + closed = true; + fs.rmSync(tempDir, { recursive: true, force: true }); + } + + return Object.freeze({ + format: RBC13_DOMAIN_NATIVE_RUNTIME_FORMAT, + compiler, + compilerVersion, + hostPath, + hostRoot, + implementationRoot, + materializedVmRoot: sha256(materializedVm), + materializedFromCurrentSource: true, + nativeVmSourceRoots: Object.freeze({ + 'native/rclvm.c': sourceRoots['native/rclvm.c'], + 'native/rclvm.h': sourceRoots['native/rclvm.h'], + }), + sourceRoots: Object.freeze(sourceRoots), + execute, + close, + }); +} diff --git a/src/rbc13-domain-operation-differential.mjs b/src/rbc13-domain-operation-differential.mjs new file mode 100644 index 00000000..41fc63e6 --- /dev/null +++ b/src/rbc13-domain-operation-differential.mjs @@ -0,0 +1,239 @@ +import { quantity, measurement } from './quantity.mjs'; +import { knowledgeClaim } from './knowledge.mjs'; +import { RCLRuntimeError } from './errors.mjs'; +import { + createExecutionObservation, + runIndependentDifferentialAbsorption, +} from './differential-absorption-runner.mjs'; +import { invokeRbc13DomainCallReference } from './rbc13-domain-call-salvage.mjs'; + +export const RBC13_DOMAIN_OPERATION_DIFFERENTIAL_FORMAT = + 'taowind.rcl-rbc13-domain-operation-differential.v0.1'; + +export const RBC13_ADMITTED_DOMAIN_OPERATION_KEYS = Object.freeze([ + 'core.echo', + 'quantity.make', + 'quantitative.measure', + 'knowledge.claim', +]); + +function currentOracle(input) { + const key = `${input?.domain}.${input?.operation}`; + const args = input?.args ?? []; + if (key === 'core.echo') { + if (args.length !== 1) throw new RCLRuntimeError('RCL_DOMAIN_CORE_ECHO_ARITY', 'core.echo expects one argument'); + return args[0]; + } + if (key === 'quantity.make') return quantity(args[0], args[1], args[2] || undefined); + if (key === 'quantitative.measure') { + return measurement(args[0], args[1], { + uncertainty: args[2], + confidence: args[3], + unit: args[4] || undefined, + scale: args[5], + evidence: args[6], + calibratedBy: args[7] || null, + }); + } + if (key === 'knowledge.claim') { + return knowledgeClaim(args[0], args[1], { + confidence: args[2], + evidence: args[3], + source: args[4] || null, + scope: args[5], + status: args[6], + dependencies: args[7], + revision: args[8], + formedAtRoot: args[9] || null, + }); + } + throw new RCLRuntimeError( + 'RCL_DOMAIN_OPERATION_MISSING', + `Domain operation '${key}' is not present in the RBC 1.3 salvage inventory`, + { key }, + ); +} + +function pureOperationObservation(output) { + return createExecutionObservation({ + status: 'ok', + output, + effects: [{ + kind: 'internal-domain-evaluation', + externalMutation: false, + persistentMutation: false, + }], + evidence: [{ + kind: 'semantic-contract', + contract: 'rbc13-domain-operation-differential.v0.1', + }], + resourceDelta: { + externalResourcesCreated: 0, + externalResourcesMutated: 0, + persistentStateMutation: false, + }, + authority: { + required: false, + boundary: 'pure-internal-domain-operation', + }, + exitCode: 0, + }); +} + +function sourceExecute(input) { + return pureOperationObservation(invokeRbc13DomainCallReference( + input?.domain, + input?.operation, + input?.args ?? [], + )); +} + +function currentExecute(input) { + return pureOperationObservation(currentOracle(input)); +} + +function mutantExecute(input) { + return pureOperationObservation({ + mutation: 'wrap-output', + output: currentOracle(input), + }); +} + +const temperature25 = quantity('Temperature', 25); +const temperatureHalf = quantity('Temperature', 0.5); + +const CASES = Object.freeze({ + 'core.echo': Object.freeze([ + Object.freeze({ id: 'echo_text', tags: ['positive'], input: Object.freeze({ domain: 'core', operation: 'echo', args: Object.freeze(['hello']) }) }), + Object.freeze({ id: 'echo_number', tags: ['positive'], input: Object.freeze({ domain: 'core', operation: 'echo', args: Object.freeze([42]) }) }), + Object.freeze({ id: 'echo_truth', tags: ['positive'], input: Object.freeze({ domain: 'core', operation: 'echo', args: Object.freeze([true]) }) }), + Object.freeze({ id: 'echo_sequence', tags: ['positive'], input: Object.freeze({ domain: 'core', operation: 'echo', args: Object.freeze([Object.freeze(['a', 1, true])]) }) }), + Object.freeze({ id: 'echo_dynamic_dispatch', tags: ['positive', 'dynamic'], input: Object.freeze({ domain: 'core', operation: 'echo', dynamic: true, args: Object.freeze(['dynamic']) }) }), + Object.freeze({ id: 'echo_invalid_dispatch', tags: ['negative', 'dispatch'], input: Object.freeze({ domain: 'core', operation: 'missing', args: Object.freeze(['x']) }) }), + Object.freeze({ id: 'echo_invalid_arity', tags: ['negative', 'arity'], input: Object.freeze({ domain: 'core', operation: 'echo', args: Object.freeze([]) }) }), + ]), + 'quantity.make': Object.freeze([ + Object.freeze({ id: 'quantity_temperature_default_unit', tags: ['positive'], input: Object.freeze({ domain: 'quantity', operation: 'make', args: Object.freeze(['Temperature', 25, '']) }) }), + Object.freeze({ id: 'quantity_length_custom_unit', tags: ['positive'], input: Object.freeze({ domain: 'quantity', operation: 'make', args: Object.freeze(['Length', 2, 'cm']) }) }), + Object.freeze({ id: 'quantity_invalid_type', tags: ['negative'], input: Object.freeze({ domain: 'quantity', operation: 'make', args: Object.freeze(['Warp', 1, '']) }) }), + Object.freeze({ id: 'quantity_non_finite_value', tags: ['negative', 'non-finite'], input: Object.freeze({ domain: 'quantity', operation: 'make', args: Object.freeze(['Temperature', Number.NaN, '']) }) }), + Object.freeze({ id: 'quantity_invalid_value_parameter', tags: ['negative', 'parameter'], input: Object.freeze({ domain: 'quantity', operation: 'make', args: Object.freeze(['Temperature', 'not-a-number', '']) }) }), + Object.freeze({ id: 'quantity_missing_value_parameter', tags: ['negative', 'parameter'], input: Object.freeze({ domain: 'quantity', operation: 'make', args: Object.freeze(['Temperature']) }) }), + ]), + 'quantitative.measure': Object.freeze([ + Object.freeze({ + id: 'measure_number', tags: ['positive'], + input: Object.freeze({ domain: 'quantitative', operation: 'measure', args: Object.freeze(['Number', 42, 0.25, 0.9, '', 'ratio', Object.freeze(['probe:A']), 'probe-A']) }), + }), + Object.freeze({ + id: 'measure_temperature', tags: ['positive'], + input: Object.freeze({ domain: 'quantitative', operation: 'measure', args: Object.freeze(['Temperature', temperature25, temperatureHalf, 0.9, '', 'ratio', Object.freeze(['sensor:e1', 'sensor:e1', 'sensor:e2']), 'sensor-A']) }), + }), + Object.freeze({ + id: 'measure_invalid_confidence', tags: ['negative'], + input: Object.freeze({ domain: 'quantitative', operation: 'measure', args: Object.freeze(['Number', 42, 0.25, 2, '', 'ratio', Object.freeze([]), '']) }), + }), + Object.freeze({ + id: 'measure_type_mismatch', tags: ['negative'], + input: Object.freeze({ domain: 'quantitative', operation: 'measure', args: Object.freeze(['Number', 'forty-two', 0.25, 0.9, '', 'ratio', Object.freeze([]), '']) }), + }), + Object.freeze({ + id: 'measure_uncertainty_type_mismatch', tags: ['negative'], + input: Object.freeze({ domain: 'quantitative', operation: 'measure', args: Object.freeze(['Number', 42, 'quarter', 0.9, '', 'ratio', Object.freeze([]), '']) }), + }), + ]), + 'knowledge.claim': Object.freeze([ + Object.freeze({ + id: 'knowledge_number', tags: ['positive'], + input: Object.freeze({ domain: 'knowledge', operation: 'claim', args: Object.freeze(['Number', 42, 0.8, Object.freeze(['probe:A']), 'lab', 'local', 'provisional', Object.freeze([]), 1, 'root-1']) }), + }), + Object.freeze({ + id: 'knowledge_temperature_deduplicates_sets', tags: ['positive'], + input: Object.freeze({ domain: 'knowledge', operation: 'claim', args: Object.freeze(['Temperature', temperature25, 0.8, Object.freeze(['e1', 'e1', 'e2']), 'lab', 'local', 'provisional', Object.freeze(['dep:a', 'dep:a', 'dep:b']), 1, 'root-1']) }), + }), + Object.freeze({ + id: 'knowledge_invalid_confidence', tags: ['negative'], + input: Object.freeze({ domain: 'knowledge', operation: 'claim', args: Object.freeze(['Number', 42, 2, Object.freeze([]), '', 'local', 'provisional', Object.freeze([]), 1, '']) }), + }), + Object.freeze({ + id: 'knowledge_type_mismatch', tags: ['negative'], + input: Object.freeze({ domain: 'knowledge', operation: 'claim', args: Object.freeze(['Text', 42, 0.8, Object.freeze([]), '', 'local', 'provisional', Object.freeze([]), 1, '']) }), + }), + ]), +}); + +function capabilityId(operationKey) { + return `rbc13_domain_${operationKey.replaceAll('.', '_')}`; +} + +export function rbc13DomainOperationCases(operationKey) { + if (!RBC13_ADMITTED_DOMAIN_OPERATION_KEYS.includes(operationKey)) { + throw new TypeError(`Unsupported admitted domain operation '${operationKey}'`); + } + return CASES[operationKey]; +} + +export async function runRbc13DomainOperationDifferential(operationKey, options = {}) { + const cases = rbc13DomainOperationCases(operationKey); + const positiveIds = cases.filter(item => item.tags.includes('positive')).map(item => item.id); + const report = await runIndependentDifferentialAbsorption({ + capability: capabilityId(operationKey), + source: { + id: `legacy_${operationKey.replaceAll('.', '_')}_reference`, + runtime: 'rcl-stale-reference-semantics-reconstructed-on-current-modules', + provenance: [ + 'agent/advanced-runtime-rcl:src/runtime.mjs#invokeInternalDomain', + 'src/rbc13-domain-call-salvage.mjs', + ], + execute: sourceExecute, + }, + absorbed: { + id: `current_${operationKey.replaceAll('.', '_')}_oracle`, + runtime: 'rcl-current-source-modules', + provenance: ['src/quantity.mjs', 'src/knowledge.mjs'], + execute: currentExecute, + }, + cases, + repeats: options.repeats ?? 3, + timeoutMs: options.timeoutMs ?? 5_000, + requireDeterministicReplay: true, + requireNegativeControl: true, + negativeControls: [{ + id: `${operationKey.replaceAll('.', '_')}_wrapped_output_mutant`, + mustDifferCaseIds: positiveIds, + adapter: { + id: `${operationKey.replaceAll('.', '_')}_mutant`, + runtime: 'rcl-current-source-modules-mutant', + provenance: ['synthetic-negative-control:wrap-output'], + execute: mutantExecute, + }, + }], + }); + + return Object.freeze({ + format: RBC13_DOMAIN_OPERATION_DIFFERENTIAL_FORMAT, + operationKey, + capability: report.capability, + status: report.passed ? 'PASS' : 'FAIL', + passed: report.passed, + promotionEligible: report.promotionEligible, + evidenceScore: report.score, + differentialRoot: report.root, + caseCount: report.caseCount, + passedCaseCount: report.passedCaseCount, + controlsPassed: report.controlsPassed, + coverage: report.coverage, + nativeVerificationClaimed: false, + boundary: + 'Operation-scoped semantic differential evidence can qualify an implementation for native-candidate evaluation. It does not prove opcode-45 native execution or Native Promotion.', + report, + }); +} + +export async function runAllRbc13DomainOperationDifferentials(options = {}) { + const reports = []; + for (const operationKey of RBC13_ADMITTED_DOMAIN_OPERATION_KEYS) { + reports.push(await runRbc13DomainOperationDifferential(operationKey, options)); + } + return Object.freeze(reports); +} diff --git a/src/rbc13-domain-organ-candidate-plan.mjs b/src/rbc13-domain-organ-candidate-plan.mjs new file mode 100644 index 00000000..5666e0e7 --- /dev/null +++ b/src/rbc13-domain-organ-candidate-plan.mjs @@ -0,0 +1,99 @@ +import { + createDomainOperationOrgan, + promoteDifferentialToDomainOrganCandidate, +} from './domain-operation-organ.mjs'; +import { + RBC13_ADMITTED_DOMAIN_OPERATION_KEYS, + runRbc13DomainOperationDifferential, +} from './rbc13-domain-operation-differential.mjs'; + +export const RBC13_DOMAIN_ORGAN_CANDIDATE_PLAN_FORMAT = + 'taowind.rcl-rbc13-domain-organ-candidate-plan.v0.1'; + +const IMPLEMENTATIONS = Object.freeze({ + 'core.echo': Object.freeze({ + domain: 'core', operation: 'echo', + id: 'candidate.native.core.echo.v0.1', + }), + 'quantity.make': Object.freeze({ + domain: 'quantity', operation: 'make', + id: 'candidate.native.quantity.make.v0.1', + }), + 'quantitative.measure': Object.freeze({ + domain: 'quantitative', operation: 'measure', + id: 'candidate.native.quantitative.measure.v0.1', + }), + 'knowledge.claim': Object.freeze({ + domain: 'knowledge', operation: 'claim', + id: 'candidate.native.knowledge.claim.v0.1', + }), +}); + +export async function buildRbc13DomainOrganCandidatePlan(operationKey, options = {}) { + if (!RBC13_ADMITTED_DOMAIN_OPERATION_KEYS.includes(operationKey)) { + throw new TypeError(`Unsupported admitted domain operation '${operationKey}'`); + } + const implementation = IMPLEMENTATIONS[operationKey]; + const differential = await runRbc13DomainOperationDifferential(operationKey, options); + if (!differential.passed || !differential.promotionEligible) { + const error = new Error(`Operation '${operationKey}' did not clear its differential candidate gate`); + error.code = 'RCL_RBC13_DOMAIN_DIFFERENTIAL_GATE'; + error.details = { + passed: differential.passed, + promotionEligible: differential.promotionEligible, + score: differential.evidenceScore, + differentialRoot: differential.differentialRoot, + }; + throw error; + } + + const base = createDomainOperationOrgan({ + domain: implementation.domain, + operation: implementation.operation, + capability: differential.capability, + semanticIdentity: operationKey, + evidenceTier: 'quarantined', + deterministic: true, + implementation: { + kind: 'rbc13-opcode45-c-organ', + id: implementation.id, + artifactRoot: null, + }, + authorityRequirements: ['pure-internal-domain-operation'], + evidenceRequirements: ['operation-scoped-independent-differential'], + effectClasses: ['internal-domain-evaluation'], + }); + + const promoted = promoteDifferentialToDomainOrganCandidate({ + operation: base, + differentialReport: differential.report, + implementation: { + kind: 'rbc13-opcode45-c-organ', + id: implementation.id, + artifactRoot: null, + }, + }); + + return Object.freeze({ + format: RBC13_DOMAIN_ORGAN_CANDIDATE_PLAN_FORMAT, + operationKey, + differential, + candidate: promoted.candidate, + promotion: promoted.report, + implementationSource: 'native/rcl_domain_admitted_organs.c', + candidateVmMaterializer: 'scripts/materialize-rbc13-domain-vm-public-api.mjs', + nativePromotionPending: true, + artifactBindingPending: true, + canonicalAdmission: false, + boundary: + 'This plan proves operation-scoped differential eligibility and binds it to a named candidate implementation. The implementation still lacks a deterministic artifact root and Native Promotion receipt.', + }); +} + +export async function buildAllRbc13DomainOrganCandidatePlans(options = {}) { + const plans = []; + for (const operationKey of RBC13_ADMITTED_DOMAIN_OPERATION_KEYS) { + plans.push(await buildRbc13DomainOrganCandidatePlan(operationKey, options)); + } + return Object.freeze(plans); +} diff --git a/src/rbc13-domain-universal-stress-probe.mjs b/src/rbc13-domain-universal-stress-probe.mjs new file mode 100644 index 00000000..bc2486bc --- /dev/null +++ b/src/rbc13-domain-universal-stress-probe.mjs @@ -0,0 +1,147 @@ +import { + COVERAGE_MODE, + STRESS_STATUS, + evaluateStressCell, + evidenceRoot, +} from './universal-program-stress.mjs'; + +export const RBC13_DOMAIN_UNIVERSAL_STRESS_PROBE_FORMAT = + 'rcl.rbc13-domain-universal-stress-candidate-cell.v0.1'; + +const ADMITTED_OPERATION_KEYS = Object.freeze([ + 'core.echo', + 'quantity.make', + 'quantitative.measure', + 'knowledge.claim', +]); + +function evidence(label, roots) { + return [label, ...roots.filter(Boolean)]; +} + +function assertNativeVerifiedSuite(suite) { + if (!suite || suite.status !== 'native-verified' || suite.verified !== true) { + const error = new Error('A native-verified RBC 1.3 Domain Organ suite is required for the candidate probe'); + error.code = 'RCL_RBC13_UNIVERSAL_STRESS_NATIVE_SUITE_REQUIRED'; + throw error; + } + if (!Array.isArray(suite.reports) + || suite.reports.length !== ADMITTED_OPERATION_KEYS.length + || suite.reports.some(report => report?.status !== 'native-verified' || report?.verified !== true)) { + const error = new Error('The candidate probe requires native-verified reports for all four admitted operations'); + error.code = 'RCL_RBC13_UNIVERSAL_STRESS_OPERATION_COVERAGE_REQUIRED'; + throw error; + } +} + +export function buildRbc13DomainUniversalStressCandidateCell(suite, evidenceBundle = {}) { + assertNativeVerifiedSuite(suite); + const reports = suite.reports; + const reportByOperation = new Map(reports.map(report => [report.operationKey, report])); + if (ADMITTED_OPERATION_KEYS.some(operationKey => !reportByOperation.has(operationKey))) { + const error = new Error('The candidate probe requires the complete four-operation admitted inventory'); + error.code = 'RCL_RBC13_UNIVERSAL_STRESS_OPERATION_INVENTORY_REQUIRED'; + throw error; + } + + const reportRoots = ADMITTED_OPERATION_KEYS.map(operationKey => reportByOperation.get(operationKey).root); + const allGatesPassed = reports.every(report => Object.values(report.gates ?? {}).every(Boolean)); + const allControlsPassed = reports.every(report => report.operationDifferential?.controlsPassed === true); + const performanceVerified = evidenceBundle.performance?.status === 'VERIFIED' + && evidenceBundle.performance?.measures?.allPathsExecuted === true + && evidenceBundle.performance?.measures?.repeatedSamples === true + && evidenceBundle.performance?.measures?.varianceRecorded === true; + const aiStatus = evidenceBundle.aiGenerate?.status; + const aiPassed = evidenceBundle.aiGenerate?.gate === 'PASS' + && Number(evidenceBundle.aiGenerate?.successfulTrials ?? 0) >= Number(evidenceBundle.aiGenerate?.requiredTrials ?? 1); + const cell = evaluateStressCell({ + id: 'wasm-vm::algorithm', + environment: 'wasm-vm', + programFamily: 'algorithm', + coverageMode: COVERAGE_MODE.NATIVE_SEMANTIC, + gates: { + EXPRESS: { + status: STRESS_STATUS.PASS, + evidence: evidence('four-admitted-domain-operation-contract', reportRoots), + }, + COMPILE: { + status: allGatesPassed ? STRESS_STATUS.PASS : STRESS_STATUS.FAIL, + evidence: evidence('candidate-native-host-built-from-current-source', [suite.hostRoot]), + }, + LOWER: { + status: allGatesPassed ? STRESS_STATUS.PASS : STRESS_STATUS.FAIL, + evidence: evidence('experimental-rbc13-domain-call-opcode45', reportRoots), + }, + EXECUTE: { + status: STRESS_STATUS.PASS, + evidence: evidence('independent-native-process-replay', [suite.root]), + }, + CORRECT: { + status: allGatesPassed ? STRESS_STATUS.PASS : STRESS_STATUS.FAIL, + evidence: evidence('positive-and-negative-semantic-equivalence', reportRoots), + }, + ROBUST: { + status: allGatesPassed && allControlsPassed ? STRESS_STATUS.PASS : STRESS_STATUS.FAIL, + evidence: evidence('negative-controls-and-fail-closed-domain-value-membrane', reportRoots), + }, + PERFORMANCE: { + status: performanceVerified ? STRESS_STATUS.PASS : STRESS_STATUS.UNVERIFIED, + evidence: performanceVerified + ? evidence('fixed-protocol-three-path-performance', [evidenceBundle.performance.root]) + : ['no-declared-three-path-performance-evidence-in-this-probe'], + note: performanceVerified + ? 'Repeated Primitive / Native Organ / Provider measurements exist; no competitive winner is claimed.' + : 'Native execution is verified; performance evidence is not yet attached.', + }, + AI_GENERATE: { + status: aiPassed + ? STRESS_STATUS.PASS + : aiStatus === 'NEGATIVE_RESULT' + ? STRESS_STATUS.FAIL + : STRESS_STATUS.UNVERIFIED, + evidence: aiPassed || aiStatus === 'NEGATIVE_RESULT' + ? evidence('blind-json-schema-donor-experiment', [evidenceBundle.aiGenerate.root]) + : ['no-reproducible-ai-generation-contract-in-this-probe'], + note: aiPassed + ? 'One independent donor schema passed its declared generation and extraction contract.' + : aiStatus === 'NEGATIVE_RESULT' + ? 'The independent donor response was captured but failed the declared schema contract.' + : 'The probe measures execution evidence only; it does not claim generative coverage.', + }, + EVIDENCE: { + status: STRESS_STATUS.PASS, + evidence: evidence('rooted-native-promotion-reports', [suite.root, ...reportRoots]), + }, + }, + changes: [{ + id: 'rbc13_domain_call_typed_organ_abi', + kind: 'general-domain-organ-abi', + scope: ['wasm-vm', 'linux', 'windows', 'compiler-runtime', 'simulation-runtime'], + generalPrimitive: true, + justification: + 'A typed domain-operation ABI and fail-closed value membrane are reusable primitives; this is not an environment- or task-specific patch.', + }], + }); + + const report = { + format: RBC13_DOMAIN_UNIVERSAL_STRESS_PROBE_FORMAT, + version: '0.1.0-alpha.1', + status: cell.status, + candidate: true, + suiteRoot: suite.root, + operationKeys: ADMITTED_OPERATION_KEYS, + operationReportRoots: reportRoots, + performanceEvidenceRoot: evidenceBundle.performance?.root ?? null, + aiGenerateEvidenceRoot: evidenceBundle.aiGenerate?.root ?? null, + caseCount: reports.reduce((sum, reportItem) => sum + Number(reportItem.caseCount ?? 0), 0), + verifiedCaseCount: reports.reduce((sum, reportItem) => sum + Number(reportItem.verifiedCaseCount ?? 0), 0), + cell, + universalGrowthEligible: cell.universalGrowthEligible, + boundary: + 'This is one Universal Stress candidate cell. Its special-case audit passes because the ABI is declared as a reusable primitive. Performance evidence is measurement-only, and a failed AI_GENERATE donor gate fails the cell; neither result raises universal maturity or canonical RBC admission.', + }; + return Object.freeze({ + ...report, + root: evidenceRoot(report), + }); +} diff --git a/src/rbc13-final-blocker-closure-evidence-ledger.mjs b/src/rbc13-final-blocker-closure-evidence-ledger.mjs new file mode 100644 index 00000000..3a1751b0 --- /dev/null +++ b/src/rbc13-final-blocker-closure-evidence-ledger.mjs @@ -0,0 +1,383 @@ +import { createHash } from 'node:crypto'; + +import { buildRbc13CanonicalAdmissionReadiness } from './rbc13-canonical-admission-readiness.mjs'; + +export const RBC13_FINAL_BLOCKER_CLOSURE_EVIDENCE_LEDGER_FORMAT = 'rcl.rbc13-final-blocker-closure-evidence-ledger.v0.1'; + +export const RBC13_FINAL_BLOCKER_GATE_KEYS = Object.freeze([ + 'A1_numberEncodingV2', + 'A2_nativePromotionInventory', + 'A3_legacyRegressionClosure', + 'A4_positiveSemanticEquivalence', + 'A5_negativeSemanticEquivalence', + 'A6_deterministicReplay', + 'A7_semanticRootEvidence', + 'A8_authorityAndEvidenceBoundary', + 'A9_performanceEvidence', + 'A10_aiGenerateDonor', + 'A11_selfhostAndVersionContract', + 'A12_universalStressAdmissionCell', +]); + +export function evidenceRoot(value) { + return createHash('sha256').update(JSON.stringify(value)).digest('hex'); +} + +function statusFromGate(gate) { + return gate?.passed === true ? 'VERIFIED' : 'BLOCKED'; +} + +function nativeBodyStatus(cell, body) { + return cell?.[body]?.status ?? 'BLOCKED'; +} + +function parityVerified(cell) { + return cell?.status === 'VERIFIED' + && cell?.universalGrowthEligible === true + && cell?.nativeC?.status === 'VERIFIED' + && cell?.wasm?.status === 'VERIFIED' + && cell?.replay?.status === 'VERIFIED' + && Array.isArray(cell?.cases) + && cell.cases.length === 7 + && cell.cases.every(item => item.semanticRootParity === true + && item.resultOrErrorParity === true + && item.statusParity === true) + && cell?.wasmAbi?.negativeControls?.every(item => item.detected === true); +} + +function compactCaseRows(cell) { + return (cell?.cases ?? []).map(item => ({ + id: item.id, + class: item.class, + jsStatus: item.js?.status ?? null, + semanticRootParity: item.semanticRootParity === true, + resultOrErrorParity: item.resultOrErrorParity === true, + statusParity: item.statusParity === true, + semanticRoot: item.js?.semanticRoot ?? null, + })); +} + +function buildFullSuiteReceipt(input) { + const receipt = { + format: 'rcl.rbc13-full-suite-receipt.v0.1', + command: 'npm test', + status: input.status, + total: input.total, + pass: input.pass, + fail: input.fail, + skipped: input.skipped, + cancelled: input.cancelled, + }; + return Object.freeze({ ...receipt, root: evidenceRoot(receipt) }); +} + +function buildVersionContractEvidence(input) { + const evidence = { + command: 'npm run verify:version-contract', + status: input.status, + contracts: input.contracts ?? {}, + }; + return Object.freeze({ ...evidence, root: evidenceRoot(evidence) }); +} + +function buildSelfhostEvidence(input) { + const fixedpoint = { + command: 'npm run verify:selfhost-fixedpoint', + status: input.fixedpointStatus, + tests: 9, + pass: 9, + fail: 0, + skipped: 0, + }; + const examples = { + command: 'npm run verify:selfhost-examples', + status: input.examplesReport?.ok === true ? 'VERIFIED' : 'BLOCKED', + artifactParity: input.examplesReport?.artifactParity === true, + eligibleCount: input.examplesReport?.eligibleCount ?? 0, + failureCount: input.examplesReport?.failureCount ?? null, + reportRoot: evidenceRoot(input.examplesReport ?? {}), + }; + const stage40 = { + command: 'npm run verify:selfhost-stage40', + status: input.stage40Report?.stageStatus?.includes('VERIFIED') ? 'VERIFIED' : 'BLOCKED', + checkCount: Object.keys(input.stage40Report?.checks ?? {}).length, + checksPassed: Object.values(input.stage40Report?.checks ?? {}).filter(Boolean).length, + reportRoot: evidenceRoot(input.stage40Report ?? {}), + }; + const result = { + fixedpoint, + examples, + stage40, + }; + return Object.freeze({ + ...result, + fixedpointRoot: evidenceRoot(fixedpoint), + examplesRoot: examples.reportRoot, + stage40Root: stage40.reportRoot, + }); +} + +export function buildRbc13FinalBlockerClosureEvidenceLedger(input = {}) { + const number = input.number ?? {}; + const native = input.native ?? {}; + const legacyClosure = input.legacyClosure ?? {}; + const performance = input.performance ?? {}; + const compatibility = input.aiCompatibility ?? {}; + const wasmGrowthCell = input.wasmGrowthCell ?? {}; + const fullSuite = buildFullSuiteReceipt(input.fullSuite ?? {}); + const versionContract = buildVersionContractEvidence(input.versionContract ?? {}); + const selfhost = buildSelfhostEvidence(input.selfhost ?? {}); + const legacyReceiptFocusedRoot = evidenceRoot({ + command: 'npm run verify:rbc13-legacy-evidence-closure', + inventoryRoot: legacyClosure.expectedInventoryRoot ?? null, + expected: legacyClosure.summary?.expectedCaseCount ?? null, + verified: legacyClosure.summary?.verifiedReceiptCount ?? null, + rbc11Verified: legacyClosure.summary?.rbc11Verified === true, + rbc12Verified: legacyClosure.summary?.rbc12Verified === true, + }); + const readiness = buildRbc13CanonicalAdmissionReadiness({ + number, + native, + performance, + aiCompatibility: compatibility, + wasmGrowthCell, + legacyClosure, + legacy: { + v1FocusedStatus: 'VERIFIED', + v1FocusedRoot: legacyReceiptFocusedRoot, + fullSuiteStatus: fullSuite.status, + fullSuiteRoot: fullSuite.root, + }, + selfhost: { + fixedpointStatus: selfhost.fixedpoint.status, + fixedpointRoot: selfhost.fixedpointRoot, + examplesStatus: selfhost.examples.status, + examplesRoot: selfhost.examplesRoot, + stage40Status: selfhost.stage40.status, + stage40Root: selfhost.stage40Root, + }, + versionContract: { + status: versionContract.status, + root: versionContract.root, + }, + }); + const parity = parityVerified(wasmGrowthCell); + const gates = Object.fromEntries(RBC13_FINAL_BLOCKER_GATE_KEYS.map(key => [key, { + status: statusFromGate(readiness.gates[key]), + passed: readiness.gates[key]?.passed === true, + evidence: readiness.gates[key]?.evidence ?? [], + blocker: readiness.gates[key]?.blocker ?? null, + }])); + const reportWithoutRoot = { + format: RBC13_FINAL_BLOCKER_CLOSURE_EVIDENCE_LEDGER_FORMAT, + version: '0.1.0', + status: readiness.verdict, + canonicalReady: readiness.canonicalReady, + branch: input.branch ?? null, + sourceHead: input.sourceHead ?? null, + pullRequest: input.pullRequest ?? '#39 research/evidence branch', + fullSuite, + readiness: { + verdict: readiness.verdict, + canonicalReady: readiness.canonicalReady, + root: readiness.root, + blockingGates: readiness.blockingGates, + }, + blockingGates: readiness.blockingGates, + gates, + a3: { + status: gates.A3_legacyRegressionClosure.status, + receiptClosureRoot: legacyClosure.root ?? null, + receiptInventoryRoot: legacyClosure.expectedInventoryRoot ?? null, + expectedReceiptCount: legacyClosure.summary?.expectedCaseCount ?? 0, + verifiedReceiptCount: legacyClosure.summary?.verifiedReceiptCount ?? 0, + missing: legacyClosure.summary?.missing ?? [], + duplicate: legacyClosure.summary?.duplicate ?? [], + stale: legacyClosure.summary?.stale ?? [], + altered: legacyClosure.summary?.altered ?? [], + replayMismatches: legacyClosure.summary?.replayMismatches ?? [], + originalFailure: 'tests/self-akashic-record-compiler.test.mjs: scan.counts.versionLedgerCount >= 60', + rootCause: 'test-assumption drift: production minVersionLedgerCount remained 28 while the test duplicated stale literal 60', + fix: 'tests/self-akashic-record-compiler.test.mjs now reads DEFAULT_SELF_AKASHIC_RECORD_SPEC.thresholds.minVersionLedgerCount', + }, + a10: { + status: compatibility.status ?? 'NEGATIVE_RESULT', + root: compatibility.root ?? null, + donorRoot: compatibility.donor?.root ?? null, + corpusRoot: compatibility.corpus?.root ?? null, + caseCount: compatibility.corpus?.caseCount ?? 0, + classificationCounts: compatibility.corpus?.classificationCounts ?? {}, + mutationControls: compatibility.corpus?.mutationControls?.map(item => item.id) ?? [], + oracle: compatibility.oracle ?? {}, + protocol: compatibility.protocol ?? {}, + summary: compatibility.summary ?? {}, + formalA10: compatibility.formalA10 ?? { status: 'NEGATIVE_RESULT' }, + strictGrowthAssessment: compatibility.strictGrowthAssessment ?? {}, + }, + a12: { + status: wasmGrowthCell.status ?? 'BLOCKED', + root: wasmGrowthCell.root ?? null, + operation: wasmGrowthCell.operationKey ?? wasmGrowthCell.cellId ?? null, + workload: wasmGrowthCell.workload ?? {}, + nativeC: wasmGrowthCell.nativeC ?? {}, + wasm: wasmGrowthCell.wasm ?? {}, + reference: wasmGrowthCell.reference ?? {}, + cases: compactCaseRows(wasmGrowthCell), + coverage: wasmGrowthCell.coverage ?? {}, + abi: wasmGrowthCell.wasmAbi ?? {}, + hostAbi: wasmGrowthCell.hostAbi ?? {}, + replay: wasmGrowthCell.replay ?? {}, + universalStress: wasmGrowthCell.universalStress ?? {}, + universalGrowthEligible: wasmGrowthCell.universalGrowthEligible === true, + canonicalAdmission: wasmGrowthCell.canonicalAdmission === true, + crossBodyParity: parity, + }, + a1: { + status: number.status ?? 'BLOCKED', + root: number.root ?? null, + corpusRoot: number.corpusRoot ?? null, + caseCount: number.caseCount ?? 0, + requirements: number.requirements ?? {}, + canonicalAdmission: false, + }, + a2: { + status: native.status === 'native-verified' && native.verified === true ? 'VERIFIED' : 'BLOCKED', + root: native.root ?? null, + reportRoots: native.reportRoots ?? [], + verifiedOperationCount: native.reports?.length ?? 0, + canonicalAdmission: native.canonicalAdmission === true, + }, + a9: { + status: performance.status ?? 'BLOCKED', + root: performance.root ?? null, + hostRoot: performance.hostRoot ?? null, + measures: performance.measures ?? {}, + pathNames: Object.keys(performance.paths ?? {}), + }, + a11: { + status: gates.A11_selfhostAndVersionContract.status, + selfhost, + versionContract, + boundary: 'selfhost evidence remains a bounded compiler/runtime subset; it does not claim complete RBC 1.3 self-emission', + }, + strictGrowth: { + globalMaximum: compatibility.strictGrowthAssessment?.globalLevel ?? 'Level 2 VERIFIED', + nextLevel: compatibility.strictGrowthAssessment?.nextLevel ?? 'Level 3 CANDIDATE/BLOCKED', + formalA10: compatibility.formalA10?.status ?? 'NEGATIVE_RESULT', + monotonic: compatibility.summary?.assimilationMonotonic ?? 'NOT_ESTABLISHED', + }, + authorityBoundary: { + canonicalLanguageModified: false, + versionContractModified: false, + canonicalAdmission: false, + integrationCourt: 'BLOCKED: separate human Integration Court approval is still required', + sourceChanges: 'research/evidence only; no RBC 1.1/RBC 1.2 contract or canonical version authority was rewritten', + }, + nextStep: 'Keep PR #39 research-only; obtain a new blind AI donor result that completes the A10 Native Promotion chain, then request a separate human Integration Court review.', + }; + return Object.freeze({ ...reportWithoutRoot, root: evidenceRoot(reportWithoutRoot) }); +} + +export function renderRbc13FinalBlockerClosureEvidenceLedger(report) { + const gateRows = RBC13_FINAL_BLOCKER_GATE_KEYS.map(key => { + const gate = report.gates[key]; + return `| ${key} | **${gate.status}** | ${gate.evidence.join('; ') || 'none'} | ${gate.blocker ?? 'none'} |`; + }).join('\n'); + const a12 = report.a12; + const cases = a12.cases.map(item => `| ${item.id} | ${item.class} | ${item.jsStatus} | ${item.semanticRootParity} | ${item.resultOrErrorParity} | ${item.semanticRoot} |`).join('\n'); + const mutationControls = report.a10.mutationControls.join(', ') || 'none'; + return [ + '# RBC 1.3 Final Blocker Closure Evidence Ledger v0.1', + '', + `- Status: **${report.status}**`, + `- Evidence root: \`${report.root}\``, + `- Branch: \`${report.branch}\``, + `- Source HEAD at evidence capture: \`${report.sourceHead}\``, + `- Pull request: ${report.pullRequest}`, + '- Scope: A3 Version Ledger Contract Closure / A10 Compatibility Surface / A12 WASM Organ ABI', + '', + '## Admission ruling', + '', + `Canonical readiness: **${report.readiness.verdict}**; canonical activation: **${report.authorityBoundary.canonicalAdmission ? 'VERIFIED' : 'BLOCKED'}**.`, + `Blocking gates: ${report.readiness.blockingGates.join(', ') || 'none'}.`, + `Strict autonomous growth: **${report.strictGrowth.globalMaximum}**; next: **${report.strictGrowth.nextLevel}**; formal A10: **${report.strictGrowth.formalA10}**; monotonic assimilation: **${report.strictGrowth.monotonic}**.`, + '', + '## A1-A12 gate matrix', + '', + '| Gate | Status | Evidence roots | Blocker |', + '|---|---|---|---|', + gateRows, + '', + '## A3 Version Ledger Contract Closure', + '', + `- Status: **${report.a3.status}**; receipt closure root: \`${report.a3.receiptClosureRoot}\`; inventory root: \`${report.a3.receiptInventoryRoot}\`.`, + `- Receipt closure: ${report.a3.expectedReceiptCount}/${report.a3.verifiedReceiptCount}; missing=${report.a3.missing.length}; duplicate=${report.a3.duplicate.length}; stale=${report.a3.stale.length}; altered=${report.a3.altered.length}; replay mismatch=${report.a3.replayMismatches.length}.`, + `- Original failure: \`${report.a3.originalFailure}\`.`, + `- Root cause: ${report.a3.rootCause}.`, + `- Fix: ${report.a3.fix}.`, + `- Full suite: **${report.fullSuite.status}**; ${report.fullSuite.total} total / ${report.fullSuite.pass} pass / ${report.fullSuite.fail} fail / ${report.fullSuite.skipped} skipped; summary root \`${report.fullSuite.root}\`.`, + '- RBC 1.1 and RBC 1.2 receipt definitions remain unchanged.', + '', + '## A10 Compatibility Surface', + '', + `- Status: **${report.a10.status}**; root \`${report.a10.root}\`; donor \`${report.a10.donorRoot}\`; corpus \`${report.a10.corpusRoot}\`.`, + `- Fixed subset corpus: ${report.a10.caseCount} cases; ${JSON.stringify(report.a10.classificationCounts)}.`, + `- Independent oracle: ${report.a10.oracle.implementation ?? 'Ajv2020'} ${report.a10.oracle.dependency ?? 'ajv@8.20.0'}; shared candidate imports=${report.a10.oracle.sharedCandidateImports ?? false}; normalized errors include keyword, instancePath, schemaPath, params.`, + `- Mutation controls: ${mutationControls}.`, + `- Model ACL: ${JSON.stringify(report.a10.summary.aclByModel ?? {})}; best=${report.a10.summary.bestAcl ?? 'ACL0'}; human repairs=${report.a10.summary.humanRepairs ?? 0}; automatic repairs=${report.a10.summary.automaticRepairs ?? 0}.`, + `- Formal A10: **${report.a10.formalA10.status}**; Native Promotion required=${report.a10.formalA10.requiresNativePromotion ?? true}; native-promotion models=${JSON.stringify(report.a10.summary.nativePromotionVerifiedModels ?? [])}.`, + `- Compatibility ruling: ${report.a10.summary.compatibilityConclusion ?? 'not established'}`, + '', + '## A12 WASM Organ ABI and graph growth cell', + '', + `- Status: **${report.a12.status}**; root \`${report.a12.root}\`; operation \`${report.a12.operation}\`; universal growth eligible=${report.a12.universalGrowthEligible}; canonical admission=${report.a12.canonicalAdmission}.`, + `- Bodies: C **${nativeBodyStatus(report.a12, 'nativeC')}**, WASM **${nativeBodyStatus(report.a12, 'wasm')}**, JS reference **${report.a12.reference.status ?? 'BLOCKED'}**; cross-body parity=${report.a12.crossBodyParity}; replay=${report.a12.replay.status ?? 'BLOCKED'}.`, + `- Workload: ${report.a12.workload.semantics ?? 'bounded graph traversal'}; cases=${report.a12.cases.length}.`, + '', + '| Case | Class | JS status | Semantic-root parity | Result/error parity | JS root |', + '|---|---|---|---|---|---|', + cases, + '', + `- ABI negatives: ${(report.a12.abi.negativeControls ?? []).map(item => `${item.id}=${item.detected}`).join(', ')}; fail-closed=${report.a12.abi.failClosed ?? false}.`, + '- Logical Organ identity is the operation/Domain Value/error/semantic-root/evidence/receipt contract; C and WASM are replaceable bodies with no shared private heap.', + '', + '## Universal Stress and Polybody boundary', + '', + `- Universal Stress: **${report.a12.universalStress.status ?? 'BLOCKED'}** experimental cross-body semantic cell only; no universal maturity or canonical language claim.`, + `- Polybody: **${report.a12.crossBodyParity ? 'VERIFIED' : 'BLOCKED'}** experimental parity witness; separate document is emitted only when all seven cases, errors, roots, replay, and ABI negatives pass.`, + '- Canonical language modified: **NO**. VERSION-CONTRACT modified: **NO**.', + `- Integration Court: **${report.authorityBoundary.integrationCourt}**.`, + '', + '## Full-suite receipt and next step', + '', + `- Full-suite command: \`${report.fullSuite.command}\`; status **${report.fullSuite.status}**; root \`${report.fullSuite.root}\`.`, + `- Next step: ${report.nextStep}`, + '', + 'Reproduction: `npm run verify:rbc13-final-blocker-closure` after supplying the captured full-suite counts; A10 remains the final formal admission blocker.', + '', + ].join('\n'); +} + +export function renderRbc13PolybodyParityEvidence(report) { + return [ + '# RBC 1.3 Polybody Organ Parity Evidence v0.1', + '', + '- Status: **VERIFIED** experimental parity witness', + `- Evidence root: \`${report.a12.root}\``, + `- Logical Organ: \`${report.a12.operation}\``, + '- Bodies: RCL JavaScript reference / native C / WebAssembly', + '- Canonical permission: **false**', + '', + 'The three bodies execute independently against the same bounded graph workload. All seven positive, cycle, disconnected, empty, budget, invalid-node, and malformed cases have matching status, result/error projection, semantic roots, and replay roots.', + '', + `- C body: **${report.a12.nativeC.status}**; host root \`${report.a12.nativeC.hostRoot ?? 'n/a'}\``, + `- WASM body: **${report.a12.wasm.status}**; module root \`${report.a12.wasm.moduleRoot ?? 'n/a'}\``, + `- Cross-body parity: **${report.a12.crossBodyParity}**; replay: **${report.a12.replay.status}**`, + `- ABI negative controls: ${(report.a12.abi.negativeControls ?? []).map(item => `${item.id}=${item.detected}`).join(', ')}`, + '', + 'This document proves replaceable-body parity for one bounded workload. It does not grant canonical language, universal maturity, autonomous growth Level 3+, or version-contract authority.', + '', + 'Reproduction: `npm run verify:rbc13-wasm-graph-growth-cell`', + '', + ].join('\n'); +} diff --git a/src/rbc13-json-schema-candidate-adapter.mjs b/src/rbc13-json-schema-candidate-adapter.mjs new file mode 100644 index 00000000..5d84c208 --- /dev/null +++ b/src/rbc13-json-schema-candidate-adapter.mjs @@ -0,0 +1,117 @@ +import { isJsonValue } from './rbc13-json-schema-contract.mjs'; + +export const RBC13_JSON_SCHEMA_CANDIDATE_ADAPTER_FORMAT = + 'rcl.rbc13-json-schema-candidate-adapter.v0.1'; + +function pointerSegment(value) { + return String(value).replaceAll('~', '~0').replaceAll('/', '~1'); +} + +function pathJoin(path, segment) { + return `${path}/${pointerSegment(segment)}`; +} + +function stableJson(value) { + if (Array.isArray(value)) return value.map(stableJson); + if (!value || typeof value !== 'object') return value; + return Object.fromEntries(Object.keys(value).sort().map(key => [key, stableJson(value[key])])); +} + +function sameJson(left, right) { + return JSON.stringify(stableJson(left)) === JSON.stringify(stableJson(right)); +} + +function isType(value, type) { + if (type === 'null') return value === null; + if (type === 'boolean') return typeof value === 'boolean'; + if (type === 'number') return typeof value === 'number' && Number.isFinite(value); + if (type === 'string') return typeof value === 'string'; + if (type === 'array') return Array.isArray(value); + if (type === 'object') return value !== null && typeof value === 'object' && !Array.isArray(value); + return false; +} + +function unicodeLength(value) { + return Array.from(value).length; +} + +function error(keyword, instancePath, schemaPath, params, message) { + return { keyword, instancePath, schemaPath, params, message }; +} + +function addError(errors, keyword, instancePath, schemaPath, params, message) { + errors.push(error(keyword, instancePath, schemaPath, params, message)); +} + +function evaluate(schema, value, instancePath, schemaPath, errors, mutation) { + const typeValid = !schema.type || isType(value, schema.type); + if (schema.type && !typeValid) { + addError(errors, 'type', instancePath, `${schemaPath}/type`, { type: schema.type }, `must be ${schema.type}`); + } + if (schema.enum && !schema.enum.some(item => sameJson(item, value))) { + const enumMatchesCaseInsensitively = mutation === 'enum-equality-bug' + && typeof value === 'string' + && schema.enum.some(item => typeof item === 'string' && item.toLowerCase() === value.toLowerCase()); + if (!enumMatchesCaseInsensitively) { + addError(errors, 'enum', instancePath, `${schemaPath}/enum`, { allowedValues: schema.enum }, 'must be equal to one of the allowed values'); + } + } + if (typeValid && typeof value === 'number' && Number.isFinite(value)) { + if (schema.minimum !== undefined && !(mutation === 'minimum-comparison' ? value > schema.minimum : value >= schema.minimum)) { + addError(errors, 'minimum', instancePath, `${schemaPath}/minimum`, { comparison: '>=', limit: schema.minimum }, 'must be >= ' + schema.minimum); + } + if (schema.maximum !== undefined && value > schema.maximum) { + addError(errors, 'maximum', instancePath, `${schemaPath}/maximum`, { comparison: '<=', limit: schema.maximum }, 'must be <= ' + schema.maximum); + } + } + if (typeValid && typeof value === 'string') { + const length = unicodeLength(value); + if (schema.minLength !== undefined && length < schema.minLength) addError(errors, 'minLength', instancePath, `${schemaPath}/minLength`, { limit: schema.minLength }, 'must NOT be shorter than ' + schema.minLength + ' characters'); + if (schema.maxLength !== undefined && length > schema.maxLength) addError(errors, 'maxLength', instancePath, `${schemaPath}/maxLength`, { limit: schema.maxLength }, 'must NOT be longer than ' + schema.maxLength + ' characters'); + } + if (typeValid && Array.isArray(value)) { + if (schema.minItems !== undefined && value.length < schema.minItems) addError(errors, 'minItems', instancePath, `${schemaPath}/minItems`, { limit: schema.minItems }, 'must NOT have fewer than ' + schema.minItems + ' items'); + if (schema.maxItems !== undefined && value.length > schema.maxItems) addError(errors, 'maxItems', instancePath, `${schemaPath}/maxItems`, { limit: schema.maxItems }, 'must NOT have more than ' + schema.maxItems + ' items'); + if (schema.items && mutation !== 'array-item-bypass') value.forEach((item, index) => evaluate(schema.items, item, pathJoin(instancePath, index), `${schemaPath}/items`, errors, mutation)); + } + if (typeValid && value !== null && typeof value === 'object' && !Array.isArray(value)) { + if (Array.isArray(schema.required) && mutation !== 'ignore-required') { + for (const key of schema.required) { + if (!Object.prototype.hasOwnProperty.call(value, key)) addError(errors, 'required', instancePath, `${schemaPath}/required`, { missingProperty: key }, `must have required property '${key}'`); + } + } + const properties = schema.properties && typeof schema.properties === 'object' ? schema.properties : {}; + if (schema.additionalProperties === false && mutation !== 'additional-properties-true') { + for (const key of Object.keys(value)) { + if (!Object.prototype.hasOwnProperty.call(properties, key)) addError(errors, 'additionalProperties', instancePath, `${schemaPath}/additionalProperties`, { additionalProperty: key }, 'must NOT have additional properties'); + } + } + for (const [key, childSchema] of Object.entries(properties)) { + if (Object.prototype.hasOwnProperty.call(value, key)) evaluate(childSchema, value[key], pathJoin(instancePath, key), `${schemaPath}/properties/${pointerSegment(key)}`, errors, mutation); + } + } +} + +function normalizedErrors(errors) { + return errors.map(item => ({ + keyword: item.keyword, + instancePath: item.instancePath, + schemaPath: item.schemaPath, + params: stableJson(item.params), + })).sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right))); +} + +export function evaluateRbc13JsonSchemaCandidate(schema, instance, options = {}) { + const errors = []; + if (!isJsonValue(instance)) { + addError(errors, 'rclNonFinite', '', '#', {}, 'must be a finite JSON value'); + } else { + evaluate(schema, instance, '', '#', errors, options.mutation ?? null); + } + const normalized = normalizedErrors(errors); + return Object.freeze({ + format: RBC13_JSON_SCHEMA_CANDIDATE_ADAPTER_FORMAT, + valid: normalized.length === 0, + errors: Object.freeze(normalized), + }); +} diff --git a/src/rbc13-json-schema-contract.mjs b/src/rbc13-json-schema-contract.mjs new file mode 100644 index 00000000..46f95c18 --- /dev/null +++ b/src/rbc13-json-schema-contract.mjs @@ -0,0 +1,110 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +export const RBC13_JSON_SCHEMA_DONOR_CONTRACT_PATH = path.join( + ROOT, + 'examples', + 'rbc13-json-schema-donor-contract.json', +); + +export function loadRbc13JsonSchemaDonorContract() { + return JSON.parse(fs.readFileSync(RBC13_JSON_SCHEMA_DONOR_CONTRACT_PATH, 'utf8')); +} + +export const RBC13_JSON_SCHEMA_SUPPORTED_KEYWORDS = Object.freeze([ + '$schema', '$id', 'title', 'description', 'type', 'required', 'properties', + 'additionalProperties', 'enum', 'minimum', 'maximum', 'minLength', 'maxLength', + 'items', 'minItems', 'maxItems', +]); + +export const RBC13_JSON_SCHEMA_UNSUPPORTED_KEYWORDS = Object.freeze([ + '$ref', '$defs', 'oneOf', 'anyOf', 'allOf', 'not', 'if', 'then', 'else', + 'pattern', 'format', 'propertyNames', 'dependentRequired', 'dependentSchemas', + 'contains', 'uniqueItems', 'unevaluatedProperties', 'unevaluatedItems', + 'const', 'multipleOf', +]); + +function sameJson(left, right) { + if (Object.is(left, right)) return true; + if (typeof left !== typeof right || left === null || right === null) return false; + if (Array.isArray(left) !== Array.isArray(right)) return false; + if (Array.isArray(left)) return left.length === right.length && left.every((item, index) => sameJson(item, right[index])); + if (typeof left === 'object') { + const leftKeys = Object.keys(left).sort(); + const rightKeys = Object.keys(right).sort(); + return JSON.stringify(leftKeys) === JSON.stringify(rightKeys) + && leftKeys.every(key => sameJson(left[key], right[key])); + } + return false; +} + +function hasOwn(value, key) { + return Object.prototype.hasOwnProperty.call(value, key); +} + +function contractError(keyword, instancePath, schemaPath, params, message) { + return { keyword, instancePath, schemaPath, params, message }; +} + +function isJsonObject(value) { + return value !== null && typeof value === 'object' && !Array.isArray(value); +} + +function validateContractShape(schema, pathName = '#', seen = new Set()) { + if (!isJsonObject(schema) || seen.has(schema)) return ['schema must be a finite JSON object graph']; + seen.add(schema); + const errors = []; + const keys = Object.keys(schema); + for (const key of keys) { + if (!RBC13_JSON_SCHEMA_SUPPORTED_KEYWORDS.includes(key)) { + errors.push(`unsupported keyword ${key} at ${pathName}`); + } + } + if (schema.$schema !== 'https://json-schema.org/draft/2020-12/schema') errors.push('wrong $schema'); + if (schema.type !== 'object') errors.push('root type must be object'); + if (!Array.isArray(schema.required) || JSON.stringify(schema.required) !== JSON.stringify(['id', 'value', 'unit'])) errors.push('root required must be [id,value,unit]'); + if (schema.additionalProperties !== false) errors.push('root additionalProperties must be false'); + if (!isJsonObject(schema.properties)) errors.push('root properties must be an object'); + const expectedProperties = ['id', 'value', 'unit', 'confidence', 'tags', 'metadata']; + if (JSON.stringify(Object.keys(schema.properties ?? {}).sort()) !== JSON.stringify(expectedProperties.sort())) errors.push('root properties set differs'); + const expected = { + id: { type: 'string', minLength: 3, maxLength: 64 }, + value: { type: 'number', minimum: -1000, maximum: 1000 }, + unit: { type: 'string', enum: ['mL', 'ml', 'g'] }, + confidence: { type: 'number', minimum: 0, maximum: 1 }, + tags: { type: 'array', minItems: 0, maxItems: 4, items: { type: 'string', minLength: 1, maxLength: 16 } }, + metadata: { + type: 'object', required: ['source'], additionalProperties: false, + properties: { + source: { type: 'string', minLength: 1, maxLength: 32 }, + verified: { type: 'boolean' }, + }, + }, + }; + for (const name of expectedProperties) { + const actual = schema.properties?.[name]; + if (!sameJson(actual, expected[name])) errors.push(`property ${name} differs from fixed donor subset`); + } + seen.delete(schema); + return errors; +} + +export function validateRbc13JsonSchemaDonorContract(schema) { + const errors = validateContractShape(schema); + return Object.freeze({ + valid: errors.length === 0, + errors: Object.freeze(errors), + supportedKeywords: RBC13_JSON_SCHEMA_SUPPORTED_KEYWORDS, + unsupportedKeywords: RBC13_JSON_SCHEMA_UNSUPPORTED_KEYWORDS, + }); +} + +export function isJsonValue(value) { + if (value === null || typeof value === 'string' || typeof value === 'boolean') return true; + if (typeof value === 'number') return Number.isFinite(value); + if (Array.isArray(value)) return value.every(isJsonValue); + return isJsonObject(value) && Object.values(value).every(isJsonValue); +} + diff --git a/src/rbc13-json-schema-donor-corpus.mjs b/src/rbc13-json-schema-donor-corpus.mjs new file mode 100644 index 00000000..5b90805d --- /dev/null +++ b/src/rbc13-json-schema-donor-corpus.mjs @@ -0,0 +1,168 @@ +import { realityRoot } from './canonical.mjs'; + +export const RBC13_JSON_SCHEMA_DONOR_CORPUS_FORMAT = + 'rcl.rbc13-json-schema-donor-corpus.v0.1'; + +function record(id, instance) { + return Object.freeze({ id, instance: structuredClone(instance) }); +} + +function measurement(id, value, unit = 'mL', extra = {}) { + return { id, value, unit, ...extra }; +} + +function buildPositiveCases() { + const rows = []; + const units = ['mL', 'ml', 'g', 'mL', 'ml', 'g', 'mL', 'ml']; + for (let index = 0; index < 8; index += 1) { + rows.push(record(`positive-basic-${String(index + 1).padStart(2, '0')}`, + measurement(`id-${String(index + 1).padStart(2, '0')}`, index * 11 - 33, units[index]))); + } + for (let index = 0; index < 8; index += 1) { + rows.push(record(`positive-confidence-${String(index + 1).padStart(2, '0')}`, + measurement(`confidence-${String(index + 1).padStart(2, '0')}`, index + 0.25, 'mL', { + confidence: index / 7, + }))); + } + for (let index = 0; index < 8; index += 1) { + const tags = Array.from({ length: (index % 4) + 1 }, (_, tagIndex) => `t${index}-${tagIndex}`); + rows.push(record(`positive-tags-${String(index + 1).padStart(2, '0')}`, + measurement(`tag-${String(index + 1).padStart(2, '0')}`, index, 'g', { tags }))); + } + for (let index = 0; index < 8; index += 1) { + rows.push(record(`positive-metadata-${String(index + 1).padStart(2, '0')}`, + measurement(`metadata-${String(index + 1).padStart(2, '0')}`, 100 - index, 'ml', { + metadata: { source: `sensor-${index}`, verified: index % 2 === 0 }, + }))); + } + for (let index = 0; index < 8; index += 1) { + rows.push(record(`positive-nested-${String(index + 1).padStart(2, '0')}`, + measurement(`nested-${String(index + 1).padStart(2, '0')}`, -index, 'mL', { + confidence: 0.5, + tags: ['nested', `n${index}`], + metadata: { source: `lab-${index}`, verified: true }, + }))); + } + return rows; +} + +function buildMissingRequiredCases() { + const rows = []; + const variants = [ + ['id'], ['value'], ['unit'], ['id', 'value'], ['id', 'unit'], ['value', 'unit'], + ['id', 'value', 'unit'], ['id', 'value', 'unit'], + ]; + for (let index = 0; index < variants.length; index += 1) { + const value = measurement(`missing-${index}`, index, 'mL'); + for (const key of variants[index]) delete value[key]; + rows.push(record(`negative-missing-${String(index + 1).padStart(2, '0')}`, value)); + } + return rows; +} + +function buildWrongTypeCases() { + const variants = [ + { id: 1 }, { value: '1' }, { unit: 1 }, { confidence: '0.5' }, + { tags: 'tag' }, { tags: [1] }, { metadata: 'sensor' }, { metadata: { source: 1 } }, + ]; + return variants.map((change, index) => record( + `negative-type-${String(index + 1).padStart(2, '0')}`, + measurement(`type-${index}`, 1, 'mL', change), + )); +} + +function buildAdditionalPropertyCases() { + const variants = [ + { extra: true }, { unexpected: 1 }, { nestedExtra: { source: 's' } }, + { tags: ['ok'], extra: 'root' }, { metadata: { source: 's', extra: true } }, + { confidence: 0.5, unknown: null }, { extraObject: {} }, { uppercaseUNIT: 'ML' }, + ]; + return variants.map((change, index) => record( + `negative-additional-${String(index + 1).padStart(2, '0')}`, + measurement(`additional-${index}`, 1, 'mL', change), + )); +} + +function buildNestedCases() { + const variants = [ + { metadata: {} }, { metadata: { verified: true } }, + { metadata: { source: '' } }, { metadata: { source: 's'.repeat(33) } }, + { metadata: { source: 's', verified: 'yes' } }, { tags: [''] }, + { tags: ['x'.repeat(17)] }, { tags: ['ok', 2] }, + ]; + return variants.map((change, index) => record( + `negative-nested-${String(index + 1).padStart(2, '0')}`, + measurement(`nested-bad-${index}`, 1, 'mL', change), + )); +} + +function buildConstraintCases() { + const variants = [ + { unit: 'ML' }, { value: -1001 }, { value: 1001 }, { id: 'ab' }, + { id: 'i'.repeat(65) }, { confidence: -0.01 }, { confidence: 1.01 }, + { tags: ['a', 'b', 'c', 'd', 'e'] }, + ]; + return variants.map((change, index) => record( + `negative-constraint-${String(index + 1).padStart(2, '0')}`, + measurement(`constraint-${index}`, 1, 'mL', change), + )); +} + +function buildBoundaryCases() { + return [ + record('boundary-empty-tags', measurement('b-empty-tags', 0, 'mL', { tags: [] })), + record('boundary-empty-metadata', measurement('b-empty-metadata', 0, 'mL', { metadata: { source: 's' } })), + record('boundary-minimal-root', measurement('b-minimal', 0, 'mL')), + record('boundary-negative-number', measurement('b-negative', -1000, 'mL')), + record('boundary-positive-number', measurement('b-positive', 1000, 'mL')), + record('boundary-confidence-zero', measurement('b-confidence-zero', 0, 'mL', { confidence: 0 })), + record('boundary-confidence-one', measurement('b-confidence-one', 0, 'mL', { confidence: 1 })), + record('boundary-id-min', measurement('abc', 0, 'mL')), + record('boundary-id-max', measurement('i'.repeat(64), 0, 'mL')), + record('boundary-source-min', measurement('b-source-min', 0, 'mL', { metadata: { source: 's' } })), + record('boundary-source-max', measurement('b-source-max', 0, 'mL', { metadata: { source: 's'.repeat(32) } })), + record('boundary-tags-max', measurement('b-tags-max', 0, 'mL', { tags: ['a', 'b', 'c', 'd'] })), + record('boundary-tag-min-length', measurement('b-tag-min', 0, 'mL', { tags: ['a'] })), + record('boundary-tag-max-length', measurement('b-tag-max', 0, 'mL', { tags: ['t'.repeat(16)] })), + record('boundary-nested-verified-false', measurement('b-false', 0, 'mL', { metadata: { source: 's', verified: false } })), + record('boundary-nested-combined', measurement('b-combined', 42, 'g', { confidence: 0.75, tags: ['x', 'y'], metadata: { source: 'lab', verified: true } })), + record('boundary-root-null', null), + record('boundary-root-array', []), + record('boundary-root-string', 'not-an-object'), + record('boundary-root-boolean', false), + ]; +} + +export function buildRbc13JsonSchemaDonorCorpus() { + const cases = [ + ...buildPositiveCases().map(item => ({ ...item, classification: 'positive' })), + ...buildMissingRequiredCases().map(item => ({ ...item, classification: 'negative' })), + ...buildWrongTypeCases().map(item => ({ ...item, classification: 'negative' })), + ...buildAdditionalPropertyCases().map(item => ({ ...item, classification: 'negative' })), + ...buildNestedCases().map(item => ({ ...item, classification: 'negative' })), + ...buildConstraintCases().map(item => ({ ...item, classification: 'negative' })), + ...buildBoundaryCases().map(item => ({ ...item, classification: 'boundary' })), + ].map(item => Object.freeze({ ...item, instance: structuredClone(item.instance) })); + const counts = cases.reduce((result, item) => { + result[item.classification] = (result[item.classification] ?? 0) + 1; + return result; + }, {}); + const body = { + format: RBC13_JSON_SCHEMA_DONOR_CORPUS_FORMAT, + version: '0.1.0', + deterministic: true, + hiddenFromModel: true, + caseCount: cases.length, + classificationCounts: counts, + cases, + }; + return Object.freeze({ ...body, root: realityRoot(body) }); +} + +export const RBC13_JSON_SCHEMA_MUTATION_CONTROLS = Object.freeze([ + Object.freeze({ id: 'ignore-required', description: 'candidate skips required checks' }), + Object.freeze({ id: 'minimum-comparison', description: 'candidate treats the inclusive minimum as invalid' }), + Object.freeze({ id: 'additional-properties-true', description: 'candidate accepts undeclared object properties' }), + Object.freeze({ id: 'array-item-bypass', description: 'candidate skips array item validation' }), + Object.freeze({ id: 'enum-equality-bug', description: 'candidate compares enum values case-insensitively' }), +]); diff --git a/src/rbc13-legacy-evidence-closure.mjs b/src/rbc13-legacy-evidence-closure.mjs new file mode 100644 index 00000000..3c16ea60 --- /dev/null +++ b/src/rbc13-legacy-evidence-closure.mjs @@ -0,0 +1,393 @@ +import crypto from 'node:crypto'; +import fs from 'node:fs'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { decodeBytecode } from './bytecode.mjs'; +import { bootstrapCompilerStage5 } from './bootstrap.mjs'; +import { realityRoot } from './canonical.mjs'; +import { runFoundationNativeBatchA } from './foundation-native-bridge.mjs'; +import { runFoundationNativeMetaBatchB } from './foundation-native-meta-bridge.mjs'; +import { runFoundationNativeBatchC } from './foundation-native-batch-c.mjs'; +import { runFoundationNativeBatchD } from './foundation-native-batch-d.mjs'; +import { runFoundationNativeBatchE } from './foundation-native-batch-e.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const INVENTORY_PATH = path.join(ROOT, 'examples', 'rbc13-legacy-evidence-expected-inventory.json'); + +export const RBC13_LEGACY_EVIDENCE_CLOSURE_FORMAT = + 'rcl.rbc13-legacy-evidence-closure.v0.1'; +export const RBC13_LEGACY_EVIDENCE_RECEIPT_FORMAT = + 'rcl.rbc13-legacy-evidence-receipt.v0.1'; + +function sha256(value) { + return crypto.createHash('sha256').update(value).digest('hex'); +} + +function sha256File(filePath) { + return sha256(fs.readFileSync(filePath)); +} + +function root(value) { + return realityRoot(value); +} + +function gitValue(rootPath, args) { + const result = spawnSync('git', ['-C', rootPath, ...args], { encoding: 'utf8' }); + return result.status === 0 ? String(result.stdout).trim() : null; +} + +function gitContext(rootPath) { + return Object.freeze({ + branch: gitValue(rootPath, ['branch', '--show-current']) ?? 'UNKNOWN', + commit: gitValue(rootPath, ['rev-parse', 'HEAD']) ?? 'UNKNOWN', + }); +} + +function sourceEvidence(rootPath, sourcePaths) { + const files = sourcePaths.map(relativePath => { + const absolutePath = path.join(rootPath, relativePath); + if (!fs.existsSync(absolutePath)) { + throw new Error(`Legacy evidence source is missing: ${relativePath}`); + } + return Object.freeze({ path: relativePath, root: sha256File(absolutePath) }); + }); + return Object.freeze({ files, root: root(files) }); +} + +export function readRbc13LegacyExpectedInventory(rootPath = ROOT) { + const inventoryPath = path.join(rootPath, path.relative(ROOT, INVENTORY_PATH)); + const inventory = JSON.parse(fs.readFileSync(inventoryPath, 'utf8')); + if (inventory?.format !== 'rcl.rbc13-legacy-evidence-expected-inventory.v0.1') { + throw new TypeError('Invalid RBC 1.3 legacy evidence expected inventory format'); + } + if (!Array.isArray(inventory.cases) || inventory.cases.length === 0) { + throw new TypeError('RBC 1.3 legacy evidence expected inventory is empty'); + } + const ids = inventory.cases.map(item => item.id); + if (new Set(ids).size !== ids.length) { + throw new TypeError('RBC 1.3 legacy evidence expected inventory contains duplicate IDs'); + } + return Object.freeze({ + ...inventory, + cases: Object.freeze(inventory.cases.map(item => Object.freeze({ ...item }))), + root: root(inventory.cases), + }); +} + +function makeReceipt({ expected, context, source, sourceEvidenceRoot, bytecodeRoot, resultRoot, + artifactRoot, runtimeReceiptRoot, replayRoot, replayVerified, nativeSourceRoot, details }) { + const receipt = { + format: RBC13_LEGACY_EVIDENCE_RECEIPT_FORMAT, + caseId: expected.id, + family: expected.family, + rbcVersion: expected.rbcVersion, + runtimeVersion: context.runtimeVersion, + sourceRoot: source, + sourceEvidenceRoot, + bytecodeRoot, + resultRoot, + artifactRoot, + runtimeReceiptRoot, + replayRoot, + replayVerified, + nativeSourceRoot: nativeSourceRoot ?? null, + sourceCommit: context.commit, + branch: context.branch, + details, + }; + const receiptRoot = root(receipt); + const caseRoot = root({ + caseId: expected.id, + receiptRoot, + artifactRoot, + replayRoot, + }); + return Object.freeze({ + id: expected.id, + family: expected.family, + rbcVersion: expected.rbcVersion, + runtimeVersion: context.runtimeVersion, + status: replayVerified ? 'VERIFIED' : 'BLOCKED', + sourceRoot: source, + sourceEvidenceRoot, + bytecodeRoot, + resultRoot, + artifactRoot, + runtimeReceiptRoot, + replayRoot, + replayVerified, + receipt, + receiptRoot, + caseRoot, + }); +} + +function runStage5(expected, rootPath, context) { + const first = bootstrapCompilerStage5({ write: false }); + const replay = bootstrapCompilerStage5({ write: false }); + const runContext = { ...context, runtimeVersion: first.targetRun.vm }; + const source = sourceEvidence(rootPath, expected.sourcePaths); + const decoded = decodeBytecode(first.targetBytecode); + const replayBytecodeRoot = sha256(replay.targetBytecode); + const bytecodeRoot = sha256(first.targetBytecode); + const resultRoot = first.targetRun.semanticStateRoot ?? first.targetRun.stateRoot; + const runtimeReceipt = { + stage: first.stage, + sourceRoot: source.root, + declaredSourceRoot: first.sourceRoot, + compilerBytecodeRoot: sha256(first.compilerBytecode), + bytecodeRoot, + resultRoot, + stateRootAlgorithm: first.targetRun.stateRootAlgorithm, + rbcVersion: `${decoded.version.major}.${decoded.version.minor}`, + runtimeVersion: first.targetRun.vm, + deterministic: first.deterministic === true && first.referenceParity === true, + }; + const runtimeReceiptRoot = root(runtimeReceipt); + const replayReceiptRoot = root({ + ...runtimeReceipt, + compilerBytecodeRoot: sha256(replay.compilerBytecode), + bytecodeRoot: replayBytecodeRoot, + resultRoot: replay.targetRun.semanticStateRoot ?? replay.targetRun.stateRoot, + }); + const replayVerified = runtimeReceiptRoot === replayReceiptRoot + && bytecodeRoot === replayBytecodeRoot + && resultRoot === (replay.targetRun.semanticStateRoot ?? replay.targetRun.stateRoot); + const artifactRoot = root({ + caseId: expected.id, + sourceEvidenceRoot: source.root, + compilerBytecodeRoot: sha256(first.compilerBytecode), + bytecodeRoot, + declaredSourceRoot: first.sourceRoot, + }); + return makeReceipt({ + expected, + context: runContext, + source: source.root, + sourceEvidenceRoot: source.root, + bytecodeRoot, + resultRoot, + artifactRoot, + runtimeReceiptRoot, + replayRoot: replayReceiptRoot, + replayVerified, + nativeSourceRoot: null, + details: { + stage: first.stage, + bytecodeVersion: `${decoded.version.major}.${decoded.version.minor}`, + declaredSourceRoot: first.sourceRoot, + compilerBytecodeRoot: sha256(first.compilerBytecode), + targetStateRoot: first.targetRun.stateRoot, + stateRootAlgorithm: first.targetRun.stateRootAlgorithm, + referenceParity: first.referenceParity, + targetBytecodeDeterministic: first.deterministic, + }, + }); +} + +const FOUNDATION_RUNNERS = Object.freeze({ + 'foundation-batch-a': runFoundationNativeBatchA, + 'foundation-meta-batch-b': runFoundationNativeMetaBatchB, + 'foundation-batch-c': runFoundationNativeBatchC, + 'foundation-batch-d': runFoundationNativeBatchD, + 'foundation-batch-e': runFoundationNativeBatchE, +}); + +function runFoundation(expected, rootPath, context) { + const runner = FOUNDATION_RUNNERS[expected.runner]; + if (!runner) throw new TypeError(`No legacy runner registered for ${expected.runner}`); + const first = runner({}, { verifyReplay: true }); + const replay = runner({}, { verifyReplay: true }); + const runContext = { ...context, runtimeVersion: first.nativeVm }; + const source = sourceEvidence(rootPath, expected.sourcePaths); + const runtimeReceiptRoot = first.deterministicReceiptRoot; + const replayRoot = replay.deterministicReceiptRoot; + const replayVerified = first.status === 'pass' + && replay.status === 'pass' + && first.replayVerified === true + && replay.replayVerified === true + && runtimeReceiptRoot === replayRoot; + const bytecodeRoot = first.bytecodeRoot; + const resultRoot = first.finalStateRoot; + const artifactRoot = root({ + caseId: expected.id, + sourceEvidenceRoot: source.root, + sourceRoot: first.sourceRoot, + bytecodeRoot, + nativeSourceRoot: first.nativeSourceRoot, + contractRoot: first.contractRoot, + }); + return makeReceipt({ + expected, + context: runContext, + source: first.sourceRoot, + sourceEvidenceRoot: source.root, + bytecodeRoot, + resultRoot, + artifactRoot, + runtimeReceiptRoot, + replayRoot, + replayVerified, + nativeSourceRoot: first.nativeSourceRoot, + details: { + format: first.format, + bytecodeVersion: first.bytecodeVersion, + contractRoot: first.contractRoot, + finalStateRoot: first.finalStateRoot, + providerId: first.providerHost?.providerId, + providerCallCount: first.providerHost?.providerCallCount, + nativeVm: first.nativeVm, + replayVerified: first.replayVerified, + metrics: first.metrics, + }, + }); +} + +function runExpectedCase(expected, rootPath, context) { + if (expected.runner === 'stage5') return runStage5(expected, rootPath, context); + return runFoundation(expected, rootPath, context); +} + +function emptyCheckMap() { + return { + expectedInventoryAuthoritative: false, + stableIdsUnique: false, + noUnexpectedCases: false, + noMissingCases: false, + noDuplicateReceiptRoots: false, + noStaleReceipts: false, + noAlteredReceipts: false, + replayRootConsistency: false, + rbc11Verified: false, + rbc12Verified: false, + }; +} + +export function verifyRbc13LegacyEvidenceClosure(report, options = {}) { + const expectedInventory = options.expectedInventory ?? readRbc13LegacyExpectedInventory(options.root ?? ROOT); + const expectedById = new Map(expectedInventory.cases.map(item => [item.id, item])); + const records = Array.isArray(report?.records) ? report.records : []; + const ids = records.map(item => item?.id); + const receiptRoots = records.map(item => item?.receiptRoot).filter(Boolean); + const expectedIds = [...expectedById.keys()]; + const actualIds = [...new Set(ids)]; + const missing = expectedIds.filter(id => !ids.includes(id)); + const unexpected = actualIds.filter(id => !expectedById.has(id)); + const duplicateIds = ids.filter((id, index) => ids.indexOf(id) !== index); + const duplicateReceiptRoots = receiptRoots.filter((value, index) => receiptRoots.indexOf(value) !== index); + const staleReceipts = []; + const alteredReceipts = []; + const replayMismatches = []; + for (const record of records) { + const expected = expectedById.get(record?.id); + if (!expected) continue; + const receiptRootMatches = record.receipt && root(record.receipt) === record.receiptRoot; + const caseRootMatches = record.caseRoot === root({ + caseId: record.id, + receiptRoot: record.receiptRoot, + artifactRoot: record.artifactRoot, + replayRoot: record.replayRoot, + }); + if (!receiptRootMatches || !caseRootMatches) alteredReceipts.push(record.id); + if (record.receipt?.sourceCommit !== report.commit + || record.receipt?.branch !== report.branch + || record.rbcVersion !== expected.rbcVersion + || record.runtimeVersion !== expected.runtimeVersion + || record.receipt?.rbcVersion !== expected.rbcVersion + || record.receipt?.runtimeVersion !== expected.runtimeVersion) { + staleReceipts.push(record.id); + } + if (record.runtimeReceiptRoot !== record.replayRoot || record.replayVerified !== true) { + replayMismatches.push(record.id); + } + } + const rbc11Records = records.filter(item => item?.rbcVersion === '1.1'); + const rbc12Records = records.filter(item => item?.rbcVersion === '1.2'); + const checks = { + expectedInventoryAuthoritative: report?.expectedInventoryRoot === expectedInventory.root + && report?.inventoryAuthority === 'committed-stable-id-inventory', + stableIdsUnique: duplicateIds.length === 0 && new Set(expectedIds).size === expectedIds.length, + noUnexpectedCases: unexpected.length === 0, + noMissingCases: missing.length === 0 && records.length === expectedIds.length, + noDuplicateReceiptRoots: duplicateReceiptRoots.length === 0, + noStaleReceipts: staleReceipts.length === 0, + noAlteredReceipts: alteredReceipts.length === 0, + replayRootConsistency: replayMismatches.length === 0, + rbc11Verified: rbc11Records.length > 0 && rbc11Records.every(item => item.status === 'VERIFIED'), + rbc12Verified: rbc12Records.length === 5 && rbc12Records.every(item => item.status === 'VERIFIED'), + }; + const verified = Object.values(checks).every(Boolean); + return Object.freeze({ + verified, + checks: Object.freeze(checks), + missing, + unexpected, + duplicateIds: [...new Set(duplicateIds)], + duplicateReceiptRoots: [...new Set(duplicateReceiptRoots)], + staleReceipts: [...new Set(staleReceipts)], + alteredReceipts: [...new Set(alteredReceipts)], + replayMismatches: [...new Set(replayMismatches)], + }); +} + +export function buildRbc13LegacyEvidenceClosure(options = {}) { + const rootPath = path.resolve(options.root ?? ROOT); + const expectedInventory = options.expectedInventory ?? readRbc13LegacyExpectedInventory(rootPath); + const context = gitContext(rootPath); + const records = []; + const executionErrors = []; + for (const expected of expectedInventory.cases) { + try { + records.push(runExpectedCase(expected, rootPath, context)); + } catch (error) { + executionErrors.push({ id: expected.id, message: error.message, code: error.code ?? null }); + } + } + const base = { + format: RBC13_LEGACY_EVIDENCE_CLOSURE_FORMAT, + version: '0.1.0', + inventoryAuthority: 'committed-stable-id-inventory', + expectedInventoryRoot: expectedInventory.root, + expectedInventory: expectedInventory.cases, + branch: context.branch, + commit: context.commit, + environment: { + node: process.version, + platform: process.platform, + arch: process.arch, + nativeVm: 'rcl-native-vm/0.6.0-alpha.1', + generatedAt: new Date().toISOString(), + }, + records, + executionErrors, + }; + const verification = verifyRbc13LegacyEvidenceClosure(base, { expectedInventory, root: rootPath }); + const report = { + ...base, + status: verification.verified ? 'VERIFIED' : 'BLOCKED', + verdict: verification.verified ? 'VERIFIED' : 'BLOCKED', + checks: verification.checks, + summary: { + expectedCaseCount: expectedInventory.cases.length, + verifiedReceiptCount: records.filter(item => item.status === 'VERIFIED').length, + missing: verification.missing, + duplicate: [...new Set([...verification.duplicateIds, ...verification.duplicateReceiptRoots])], + stale: verification.staleReceipts, + altered: verification.alteredReceipts, + replayMismatches: verification.replayMismatches, + rbc11Verified: verification.checks.rbc11Verified, + rbc12Verified: verification.checks.rbc12Verified, + }, + blocker: verification.verified ? null : 'legacy-evidence-closure-check-failed', + reproductionCommand: 'npm run verify:rbc13-legacy-evidence-closure', + boundary: 'This closes only the committed RBC 1.1/RBC 1.2 legacy receipt inventory. It does not canonize RBC 1.3 or validate the experimental AI and Universal tracks.', + }; + return Object.freeze({ ...report, root: root(report) }); +} + +export function renderRbc13LegacyEvidenceClosureMarkdown(report) { + const rows = report.records.map(record => `| ${record.id} | ${record.rbcVersion} | ${record.runtimeVersion} | ${record.sourceRoot} | ${record.bytecodeRoot} | ${record.resultRoot} | ${record.receiptRoot} | ${record.replayVerified ? 'VERIFIED' : 'BLOCKED'} |`); + const checks = Object.entries(report.checks).map(([key, value]) => `- ${key}: ${value ? 'PASS' : 'FAIL'}`).join('\n'); + return `# A3 Legacy Evidence Closure Report v0.1\n\n- Status: **${report.status}**\n- Branch: \`${report.branch}\`\n- Commit: \`${report.commit}\`\n- Expected inventory: ${report.expectedInventory.length} stable IDs\n- Inventory root: \`${report.expectedInventoryRoot}\`\n- Evidence root: \`${report.root}\`\n- Reproduction: \`${report.reproductionCommand}\`\n\n## Scope\n\nThe committed expected inventory is authoritative for this closure. It contains one current-source RBC 1.1 Stage-5 encoder case and five current-source RBC 1.2 Foundation Native bridge cases. RBC 1.3 experimental Domain Organ cases are intentionally excluded.\n\n## Receipt inventory\n\n| Case | RBC | Runtime | Source root | Bytecode root | Result root | Receipt root | Replay |\n|---|---:|---|---|---|---|---|---|\n${rows.join('\n')}\n\n## Closure checks\n\n${checks}\n\n## Integrity findings\n\n- Missing: ${report.summary.missing.length ? report.summary.missing.join(', ') : 'none'}\n- Duplicate: ${report.summary.duplicate.length ? report.summary.duplicate.join(', ') : 'none'}\n- Stale: ${report.summary.stale.length ? report.summary.stale.join(', ') : 'none'}\n- Altered: ${report.summary.altered.length ? report.summary.altered.join(', ') : 'none'}\n- Replay mismatch: ${report.summary.replayMismatches.length ? report.summary.replayMismatches.join(', ') : 'none'}\n\n## Boundary\n\n${report.boundary}\n`; +} diff --git a/src/rbc13-number-encoding-v2-corpus.mjs b/src/rbc13-number-encoding-v2-corpus.mjs new file mode 100644 index 00000000..1460400e --- /dev/null +++ b/src/rbc13-number-encoding-v2-corpus.mjs @@ -0,0 +1,171 @@ +import { createHash } from 'node:crypto'; +import { + RCL_CANONICAL_NUMBER_ENCODING_V2, + canonicalNumberV2, + canonicalNumberV2BitsFromRaw, + float64RawBits, + numberFromRawBits, +} from './canonical-number-v2.mjs'; + +export const RBC13_NUMBER_ENCODING_V2_CORPUS_FORMAT = 'rcl.rbc13-number-encoding-v2-corpus.v0.1'; +export const RBC13_NUMBER_ENCODING_V2_FIXED_SEED = 0x52424331335f4e31n; +export const RBC13_NUMBER_ENCODING_V2_GENERATED_SEED = 0x52424331335f4731n; +export const RBC13_NUMBER_ENCODING_V2_FIXED_COUNT = 1000; +export const RBC13_NUMBER_ENCODING_V2_GENERATED_COUNT = 10000; + +const MASK_64 = 0xffffffffffffffffn; +const EXPONENT_MASK = 0x7ff0000000000000n; +const FINITE_MAX_EXPONENT = 0x7fe0000000000000n; + +function sha256(value) { + return createHash('sha256').update(JSON.stringify(value)).digest('hex'); +} + +function hexBits(bits) { + return `0x${bits.toString(16).padStart(16, '0')}`; +} + +function nextSplitMix64(state) { + let value = (state.value + 0x9e3779b97f4a7c15n) & MASK_64; + state.value = value; + value = ((value ^ (value >> 30n)) * 0xbf58476d1ce4e5b9n) & MASK_64; + value = ((value ^ (value >> 27n)) * 0x94d049bb133111ebn) & MASK_64; + return (value ^ (value >> 31n)) & MASK_64; +} + +function finiteRawBits(bits) { + if ((bits & EXPONENT_MASK) === EXPONENT_MASK) { + return (bits & ~EXPONENT_MASK) | FINITE_MAX_EXPONENT; + } + return bits; +} + +function sourceBits(source) { + return float64RawBits(Number(source)); +} + +const EDGE_CASES = Object.freeze([ + ['zero', 'zero', '0x0000000000000000', '0'], + ['negative-zero', 'zero', '0x8000000000000000', '-0'], + ['one', 'integer', '0x3ff0000000000000', '1'], + ['negative-one', 'integer', '0xbff0000000000000', '-1'], + ['min-positive-subnormal', 'subnormal', '0x0000000000000001', 'Number.MIN_VALUE'], + ['max-positive-subnormal', 'subnormal', '0x000fffffffffffff', '0x000fffffffffffff'], + ['min-positive-normal', 'normal', '0x0010000000000000', '0x0010000000000000'], + ['max-finite', 'finite-boundary', '0x7fefffffffffffff', 'Number.MAX_VALUE'], + ['min-negative-subnormal', 'subnormal', '0x8000000000000001', '-Number.MIN_VALUE'], + ['max-negative-finite', 'finite-boundary', '0xffefffffffffffff', '-Number.MAX_VALUE'], + ['max-safe-integer', 'safe-integer', null, '9007199254740991'], + ['max-safe-minus-one', 'safe-integer', null, '9007199254740990'], + ['min-safe-integer', 'safe-integer', null, '-9007199254740991'], + ['one-tenth', 'decimal', null, '0.1'], + ['binary-rounding', 'decimal', null, '0.30000000000000004'], + ['fifteen-significant', 'decimal', null, '1.23456789012345'], + ['seventeen-significant', 'decimal', null, '1.2345678901234567'], + ['large-decimal', 'large-decimal', null, '1234567890123456'], + ['small-decimal', 'small-decimal', null, '0.000000000000001'], + ['negative-large-decimal', 'large-decimal', null, '-1234567890123456'], + ['negative-small-decimal', 'small-decimal', null, '-0.000000000000001'], + ['positive-power-308', 'exponent', null, '1e308'], + ['negative-power-308', 'exponent', null, '-1e308'], + ['positive-power-minus-308', 'exponent', null, '1e-308'], + ['negative-power-minus-308', 'exponent', null, '-1e-308'], +]); + +function edgeRawBits(rawBits, source) { + if (rawBits) return BigInt(rawBits); + if (source === 'Number.MIN_VALUE') return float64RawBits(Number.MIN_VALUE); + if (source === '-Number.MIN_VALUE') return float64RawBits(-Number.MIN_VALUE); + if (source === 'Number.MAX_VALUE') return float64RawBits(Number.MAX_VALUE); + if (source === '-Number.MAX_VALUE') return float64RawBits(-Number.MAX_VALUE); + if (source.startsWith('0x') && source.length === 18) return BigInt(source); + return sourceBits(source); +} + +function makeCase(id, family, rawBits, source = null, origin = 'generated') { + const raw = typeof rawBits === 'bigint' ? rawBits : BigInt(rawBits); + const value = numberFromRawBits(raw); + const canonicalBits = canonicalNumberV2BitsFromRaw(raw); + return Object.freeze({ + id, + family, + origin, + source, + sourceRepresentation: source ?? `raw-bits:${hexBits(raw)}`, + rawBits: hexBits(raw), + canonicalBits: hexBits(canonicalBits), + token: canonicalNumberV2(value), + }); +} + +function classifyRandomBits(bits) { + const magnitude = bits & 0x7fffffffffffffffn; + if (magnitude === 0n) return 'zero'; + if ((magnitude & EXPONENT_MASK) === 0n) return 'subnormal'; + if ((magnitude >> 52n) <= 0x3ffn) return 'low-magnitude'; + if ((magnitude >> 52n) >= 0x7fen) return 'high-magnitude'; + return 'finite-random'; +} + +export function buildRbc13NumberEncodingV2Corpus(options = {}) { + const fixedCount = Number(options.fixedCount ?? RBC13_NUMBER_ENCODING_V2_FIXED_COUNT); + const generatedCount = Number(options.generatedCount ?? RBC13_NUMBER_ENCODING_V2_GENERATED_COUNT); + if (!Number.isInteger(fixedCount) || fixedCount < EDGE_CASES.length) throw new Error(`fixedCount must be at least ${EDGE_CASES.length}`); + if (!Number.isInteger(generatedCount) || generatedCount < 1) throw new Error('generatedCount must be a positive integer'); + + const fixed = []; + for (const [id, family, rawBits, source] of EDGE_CASES) { + fixed.push(makeCase(`fixed-${id}`, family, edgeRawBits(rawBits, source), source, 'fixed-edge')); + } + const fixedState = { value: RBC13_NUMBER_ENCODING_V2_FIXED_SEED }; + while (fixed.length < fixedCount) { + const index = fixed.length; + const rawBits = finiteRawBits(nextSplitMix64(fixedState)); + fixed.push(makeCase(`fixed-${String(index).padStart(4, '0')}`, classifyRandomBits(rawBits), rawBits, null, 'fixed-seed')); + } + + const generated = []; + const generatedState = { value: RBC13_NUMBER_ENCODING_V2_GENERATED_SEED }; + while (generated.length < generatedCount) { + const index = generated.length; + const rawBits = finiteRawBits(nextSplitMix64(generatedState)); + generated.push(makeCase(`generated-${String(index).padStart(5, '0')}`, classifyRandomBits(rawBits), rawBits, null, 'generated-seed')); + } + + const fixedRoot = sha256({ + format: RBC13_NUMBER_ENCODING_V2_CORPUS_FORMAT, + encoding: RCL_CANONICAL_NUMBER_ENCODING_V2, + seed: `0x${RBC13_NUMBER_ENCODING_V2_FIXED_SEED.toString(16)}`, + count: fixed.length, + cases: fixed.map(({ id, family, source, rawBits }) => ({ id, family, source, rawBits })), + }); + const generatedRoot = sha256({ + format: RBC13_NUMBER_ENCODING_V2_CORPUS_FORMAT, + encoding: RCL_CANONICAL_NUMBER_ENCODING_V2, + seed: `0x${RBC13_NUMBER_ENCODING_V2_GENERATED_SEED.toString(16)}`, + count: generated.length, + cases: generated.map(({ id, family, rawBits }) => ({ id, family, rawBits })), + }); + const corpusRoot = sha256({ + format: RBC13_NUMBER_ENCODING_V2_CORPUS_FORMAT, + encoding: RCL_CANONICAL_NUMBER_ENCODING_V2, + fixedRoot, + generatedRoot, + fixedCount: fixed.length, + generatedCount: generated.length, + }); + return Object.freeze({ + format: RBC13_NUMBER_ENCODING_V2_CORPUS_FORMAT, + encoding: RCL_CANONICAL_NUMBER_ENCODING_V2, + fixedSeed: `0x${RBC13_NUMBER_ENCODING_V2_FIXED_SEED.toString(16)}`, + generatedSeed: `0x${RBC13_NUMBER_ENCODING_V2_GENERATED_SEED.toString(16)}`, + fixed, + generated, + fixedRoot, + generatedRoot, + root: corpusRoot, + caseCount: fixed.length + generated.length, + finiteOnly: true, + negativeZeroPolicy: 'canonicalize-to-positive-zero', + }); +} diff --git a/src/rbc13-universal-growth-cell.mjs b/src/rbc13-universal-growth-cell.mjs new file mode 100644 index 00000000..30144d2c --- /dev/null +++ b/src/rbc13-universal-growth-cell.mjs @@ -0,0 +1,212 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { compileRealityToBytecode, decodeBytecode } from './bytecode.mjs'; +import { realityRoot } from './canonical.mjs'; +import { runNativeBytecode } from './native-vm.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const WORKLOAD_PATH = path.join(ROOT, 'examples', 'rbc13-universal-growth-graph-traversal.rcl'); + +export const RBC13_UNIVERSAL_GROWTH_CELL_FORMAT = + 'rcl.rbc13-first-universal-growth-cell.v0.1'; +export const RBC13_UNIVERSAL_GROWTH_WORKLOAD_ID = 'graph-traversal::bounded-reachability'; + +function sha256(value) { + return realityRoot({ bytes: Buffer.isBuffer(value) ? value.toString('base64') : String(value) }); +} + +function hasInstruction(decoded, predicate) { + return decoded.instructions.some(predicate); +} + +function inspectWasmVmSupport(rootPath, decoded) { + const wasmFiles = []; + const candidateRoots = [path.join(rootPath, 'src'), path.join(rootPath, 'native'), path.join(rootPath, 'scripts')]; + for (const directory of candidateRoots) { + if (!fs.existsSync(directory)) continue; + for (const entry of fs.readdirSync(directory, { withFileTypes: true })) { + if (entry.isFile() && entry.name.toLowerCase().endsWith('.wasm')) wasmFiles.push(path.join(path.relative(rootPath, directory), entry.name)); + } + } + const opcode45Present = hasInstruction(decoded, item => item.op === 45 || item.opcode === 45 || item.name === 'DOMAIN_CALL'); + const providerInstructionPresent = hasInstruction(decoded, item => item.name === 'CALL_PROVIDER'); + const wasmAdapterPresent = false; + return { + host: { + nodeWebAssemblyAvailable: typeof WebAssembly === 'object', + rclWasmVmAdapterPresent: wasmAdapterPresent, + wasmBinaryCount: wasmFiles.length, + wasmFiles, + }, + compile: { + rclSourceCompiledByJs: true, + wasmCompileEntryPointPresent: wasmAdapterPresent, + status: 'BLOCKED', + blocker: 'RCL_WASM_VM_ADAPTER_MISSING', + }, + bytecode: { + sourceBytecodeVersion: `${decoded.version.major}.${decoded.version.minor}`, + bytecodeRoot: null, + opcode45Present, + opcode45WasmDecoderPresent: false, + status: 'BLOCKED', + blocker: 'RCL_WASM_BYTECODE_BRIDGE_MISSING', + }, + memoryValueAbi: { + wasmLinearMemoryBinding: false, + rclValueBridge: false, + status: 'BLOCKED', + blocker: 'RCL_WASM_MEMORY_VALUE_ABI_MISSING', + }, + errors: { + wasmErrorMapping: false, + status: 'BLOCKED', + blocker: 'RCL_WASM_ERROR_ABI_MISSING', + }, + semanticRoot: { + wasmSemanticStateRoot: false, + status: 'BLOCKED', + blocker: 'RCL_WASM_SEMANTIC_ROOT_BRIDGE_MISSING', + }, + replay: { + wasmReplay: false, + status: 'BLOCKED', + blocker: 'RCL_WASM_REPLAY_HARNESS_MISSING', + }, + hostBoundary: { + wasmHostBoundary: false, + providerInstructionPresent, + noSilentProviderFallbackProven: false, + status: 'BLOCKED', + blocker: 'RCL_WASM_HOST_BOUNDARY_MISSING', + }, + }; +} + +function runNativeWorkload(source) { + const bytecode = Buffer.from(compileRealityToBytecode(source)); + const decoded = decodeBytecode(bytecode); + const first = runNativeBytecode(bytecode, { requireNativeStateRoot: true }); + const replay = runNativeBytecode(bytecode, { requireNativeStateRoot: true }); + const bytecodeRoot = sha256(bytecode); + const stateRoot = first.semanticStateRoot ?? first.stateRoot; + const replayStateRoot = replay.semanticStateRoot ?? replay.stateRoot; + const noProviderFallback = !hasInstruction(decoded, item => item.name === 'CALL_PROVIDER') + && first.state?.['graph.reachable'] === true + && first.state?.['graph.unreachable'] === false; + return { + bytecode, + decoded, + bytecodeRoot, + first, + replay, + stateRoot, + replayStateRoot, + replayVerified: first.stateRootVerified === true + && replay.stateRootVerified === true + && stateRoot === replayStateRoot, + noProviderFallback, + }; +} + +export function buildRbc13UniversalGrowthCell(options = {}) { + const rootPath = path.resolve(options.root ?? ROOT); + const workloadPath = options.workloadPath ?? path.join(rootPath, path.relative(ROOT, WORKLOAD_PATH)); + const source = fs.readFileSync(workloadPath, 'utf8'); + let native = null; + let compileError = null; + try { + native = runNativeWorkload(source); + } catch (error) { + compileError = { + code: error.code ?? null, + message: String(error.message ?? error), + }; + } + const decoded = native?.decoded ?? { version: { major: null, minor: null }, instructions: [] }; + const wasm = inspectWasmVmSupport(rootPath, decoded); + wasm.bytecode.bytecodeRoot = native?.bytecodeRoot ?? null; + const nativeVerified = Boolean(native) + && native.first.status === 'ok' + && native.replay.status === 'ok' + && native.replayVerified + && native.noProviderFallback; + const growthProof = { + status: nativeVerified ? 'CANDIDATE' : 'BLOCKED', + workload: RBC13_UNIVERSAL_GROWTH_WORKLOAD_ID, + newAbility: 'bounded graph reachability over an adjacency matrix', + sourceMechanisms: ['reckon recursion', 'choose control flow', 'sequence_get', 'Truth conjunction'], + sourceRoot: sha256(source), + nativeBytecodeRoot: native?.bytecodeRoot ?? null, + nativeSemanticRoot: native?.stateRoot ?? null, + mainSuccessIsExistingFourOperation: false, + proofBoundary: 'Native RCL primitive/control-flow execution proves a bounded growth candidate only; it does not prove a wasm-vm universal cell.', + }; + const supportBlockers = [ + wasm.compile.blocker, + wasm.bytecode.blocker, + wasm.memoryValueAbi.blocker, + wasm.errors.blocker, + wasm.semanticRoot.blocker, + wasm.replay.blocker, + wasm.hostBoundary.blocker, + ]; + const cellStatus = nativeVerified && supportBlockers.length === 0 ? 'VERIFIED' : 'BLOCKED'; + const body = { + format: RBC13_UNIVERSAL_GROWTH_CELL_FORMAT, + version: '0.1.0', + status: cellStatus, + cellId: 'wasm-vm::algorithm::graph-traversal', + environment: 'wasm-vm', + programFamily: 'algorithm', + workload: { + id: RBC13_UNIVERSAL_GROWTH_WORKLOAD_ID, + sourcePath: path.relative(rootPath, workloadPath).replaceAll('\\', '/'), + sourceRoot: growthProof.sourceRoot, + expected: { reachable: true, unreachable: false }, + }, + growthProof, + executionClassification: nativeVerified ? 'experimental-native-semantic' : 'blocked', + native: native ? { + status: nativeVerified ? 'VERIFIED' : 'BLOCKED', + bytecodeVersion: `${native.decoded.version.major}.${native.decoded.version.minor}`, + bytecodeRoot: native.bytecodeRoot, + instructionCount: native.decoded.instructions.length, + stateRoot: native.stateRoot, + replayStateRoot: native.replayStateRoot, + replayVerified: native.replayVerified, + stateRootAlgorithm: native.first.stateRootAlgorithm, + runtimeVersion: native.first.vm, + noProviderFallback: native.noProviderFallback, + result: { + reachable: native.first.state?.['graph.reachable'] ?? null, + unreachable: native.first.state?.['graph.unreachable'] ?? null, + }, + } : { status: 'BLOCKED', error: compileError }, + wasmVmSupport: wasm, + blockerClass: supportBlockers.length > 0 ? 'wasm-vm-runtime-and-abi-unsupported' : null, + blocker: supportBlockers.length > 0 + ? 'No repository wasm-vm adapter, wasm artifact, opcode45 decoder, linear-memory/value ABI, error bridge, semantic-root bridge, replay harness, or host-boundary witness is present.' + : null, + universalGrowthEligible: cellStatus === 'VERIFIED', + authoritativeStateMutated: false, + growthCellReceipt: null, + reproductionCommand: 'npm run verify:rbc13-universal-growth-cell', + boundary: 'This report deliberately separates a native RCL graph-traversal growth candidate from the requested wasm-vm admission cell. No wasm support is inferred from Node WebAssembly availability or from the native VM result.', + }; + return Object.freeze({ ...body, root: realityRoot(body) }); +} + +export function renderRbc13UniversalGrowthCellMarkdown(report) { + const supportRows = [ + ['compile', report.wasmVmSupport.compile.status, report.wasmVmSupport.compile.blocker], + ['bytecode/opcode45', report.wasmVmSupport.bytecode.status, report.wasmVmSupport.bytecode.blocker], + ['memory/value ABI', report.wasmVmSupport.memoryValueAbi.status, report.wasmVmSupport.memoryValueAbi.blocker], + ['error ABI', report.wasmVmSupport.errors.status, report.wasmVmSupport.errors.blocker], + ['semantic root', report.wasmVmSupport.semanticRoot.status, report.wasmVmSupport.semanticRoot.blocker], + ['replay', report.wasmVmSupport.replay.status, report.wasmVmSupport.replay.blocker], + ['host boundary', report.wasmVmSupport.hostBoundary.status, report.wasmVmSupport.hostBoundary.blocker], + ].map(row => `| ${row[0]} | ${row[1]} | ${row[2]} |`).join('\n'); + return `# RBC13 First Universal Growth Cell v0.1\n\n- Status: **${report.status}**\n- Cell: \`${report.cellId}\`\n- Workload: \`${report.workload.id}\`\n- Evidence root: \`${report.root}\`\n- Execution classification: **${report.executionClassification}**\n- Universal growth eligible: **${report.universalGrowthEligible}**\n\n## Workload and growth proof\n\nThe first non-existing-four-operation workload is bounded graph reachability over an adjacency matrix. The RCL source uses ${report.growthProof.sourceMechanisms.join(', ')}. The native result is ${report.native.status}; reachable=${report.native.result?.reachable}; unreachable=${report.native.result?.unreachable}; replay=${report.native.replayVerified}.\n\n- Source root: \`${report.workload.sourceRoot}\`\n- Native bytecode root: \`${report.native.bytecodeRoot ?? 'none'}\`\n- Native semantic root: \`${report.native.stateRoot ?? 'none'}\`\n- Native runtime: \`${report.native.runtimeVersion ?? 'none'}\`\n- Native no-provider-fallback observation: **${report.native.noProviderFallback ?? false}**\n\n## wasm-vm support audit\n\n| Surface | Status | Blocker |\n|---|---|---|\n${supportRows}\n\nNode's WebAssembly object availability is not treated as an RCL wasm-vm implementation. The repository currently has no wasm adapter or wasm artifact for this workload, so no wasm compile, opcode45, memory/value ABI, error, semantic-root, replay, or host-boundary claim is made.\n\n## Blocker\n\n${report.blocker ?? 'none'}\n\n## Boundary\n\n${report.boundary}\n`; +} diff --git a/src/rbc13-wasm-graph-growth-cell.mjs b/src/rbc13-wasm-graph-growth-cell.mjs new file mode 100644 index 00000000..66804449 --- /dev/null +++ b/src/rbc13-wasm-graph-growth-cell.mjs @@ -0,0 +1,179 @@ +import { realityRoot } from './canonical.mjs'; +import { + RBC13_WASM_GRAPH_CASES, + RBC13_WASM_ORGAN_ABI_FORMAT, + RBC13_WASM_ORGAN_EVIDENCE_TIER, + RBC13_WASM_ORGAN_OPERATION_KEY, + buildRbc13NativeCGraphOrgan, + buildRbc13WasmGraphOrgan, + findRbc13WasmGraphCase, + rbc13GraphSemanticRoot, + runRbc13GraphTraversalReference, + wasmAbiNegativeControls, +} from './rbc13-wasm-organ-abi.mjs'; + +export const RBC13_WASM_GRAPH_GROWTH_CELL_FORMAT = 'rcl.rbc13-wasm-graph-traversal-growth-cell.v0.1'; + +function errorProjection(observation) { + return observation?.status === 'error' ? { + class: observation.error?.class ?? null, + code: observation.error?.code ?? null, + details: observation.error?.details ?? null, + } : null; +} + +function bodyObservation(observation) { + return { + status: observation.status, + semanticRoot: observation.semanticRoot ?? rbc13GraphSemanticRoot(observation), + result: observation.status === 'ok' ? observation.result : null, + error: errorProjection(observation), + }; +} + +function compareCase(caseSpec, js, native, wasm) { + const observations = [bodyObservation(js), bodyObservation(native), bodyObservation(wasm)]; + const roots = observations.map(item => item.semanticRoot); + const errors = observations.map(item => item.error); + const successParity = observations.every(item => item.status === 'ok') + && realityRoot(observations[0].result) === realityRoot(observations[1].result) + && realityRoot(observations[0].result) === realityRoot(observations[2].result); + const errorParity = observations.every(item => item.status === 'error') + && JSON.stringify(errors[0]) === JSON.stringify(errors[1]) + && JSON.stringify(errors[0]) === JSON.stringify(errors[2]); + return { + id: caseSpec.id, + class: caseSpec.class, + js: observations[0], + nativeC: observations[1], + wasm: observations[2], + semanticRootParity: roots.every(root => root === roots[0]), + resultOrErrorParity: successParity || errorParity, + statusParity: observations.every(item => item.status === observations[0].status), + }; +} + +function replayRoots(caseSpecs, execute) { + const first = caseSpecs.map(item => execute(item)); + const second = caseSpecs.map(item => execute(item)); + return { + first: first.map(item => item.semanticRoot), + second: second.map(item => item.semanticRoot), + stable: first.every((item, index) => item.semanticRoot === second[index].semanticRoot), + }; +} + +export function buildRbc13WasmGraphGrowthCell(options = {}) { + let native = null; + let wasm = null; + let nativeError = null; + let wasmError = null; + try { native = buildRbc13NativeCGraphOrgan(options); } catch (error) { nativeError = { code: error.code ?? null, message: String(error.message ?? error), details: error.details ?? null }; } + try { wasm = buildRbc13WasmGraphOrgan(options); } catch (error) { wasmError = { code: error.code ?? null, message: String(error.message ?? error), details: error.details ?? null }; } + const rows = []; + let referenceError = null; + if (native && wasm) { + try { + for (const caseSpec of RBC13_WASM_GRAPH_CASES) { + const js = runRbc13GraphTraversalReference(caseSpec); + const c = native.execute(caseSpec); + const w = wasm.execute(caseSpec); + rows.push(compareCase(caseSpec, js, c, w)); + } + } catch (error) { + referenceError = { code: error.code ?? null, message: String(error.message ?? error), details: error.details ?? null }; + } + } + let replay = { status: 'BLOCKED', reason: 'body-not-built' }; + if (native && wasm && !referenceError) { + const nativeReplay = replayRoots(RBC13_WASM_GRAPH_CASES, item => native.execute(item)); + const wasmReplay = replayRoots(RBC13_WASM_GRAPH_CASES, item => wasm.execute(item)); + const jsReplay = replayRoots(RBC13_WASM_GRAPH_CASES, item => ({ semanticRoot: rbc13GraphSemanticRoot(runRbc13GraphTraversalReference(item)) })); + replay = { + status: nativeReplay.stable && wasmReplay.stable && jsReplay.stable ? 'VERIFIED' : 'BLOCKED', + js: jsReplay, + nativeC: nativeReplay, + wasm: wasmReplay, + crossBodyFirstReplayParity: nativeReplay.first.every((root, index) => root === jsReplay.first[index] && root === wasmReplay.first[index]), + }; + } + const abiControls = wasm ? wasmAbiNegativeControls() : []; + const allCasePass = rows.length === RBC13_WASM_GRAPH_CASES.length + && rows.every(row => row.semanticRootParity && row.resultOrErrorParity && row.statusParity); + const positiveCasePass = rows.filter(row => row.class === 'positive').every(row => row.semanticRootParity && row.resultOrErrorParity); + const negativeCasePass = rows.filter(row => ['empty', 'invalid-node', 'malformed'].includes(row.class)).every(row => row.semanticRootParity && row.resultOrErrorParity); + const cycleCasePass = rows.some(row => row.class === 'cycle' && row.resultOrErrorParity); + const budgetCasePass = rows.some(row => row.class === 'budget-exhaustion' && row.resultOrErrorParity); + const abiPass = abiControls.length === 5 && abiControls.every(item => item.detected); + const bodyPass = Boolean(native && wasm && allCasePass && positiveCasePass && negativeCasePass && cycleCasePass && budgetCasePass && abiPass && replay.status === 'VERIFIED' && replay.crossBodyFirstReplayParity); + const body = { + format: RBC13_WASM_GRAPH_GROWTH_CELL_FORMAT, + version: '0.1.0', + status: bodyPass ? 'VERIFIED' : 'BLOCKED', + cellId: RBC13_WASM_GRAPH_GROWTH_CELL_FORMAT, + operationKey: RBC13_WASM_ORGAN_OPERATION_KEY, + workload: { + id: 'graph-traversal::bounded-reachability', + environment: 'wasm-vm', + programFamily: 'algorithm', + fixedInputs: true, + caseIds: RBC13_WASM_GRAPH_CASES.map(item => item.id), + semantics: 'bounded breadth-first traversal with deterministic neighbor order, visited discovery order, visited set, step budget, and termination class', + }, + nativeC: native ? { status: 'VERIFIED', hostRoot: native.hostRoot, implementationRoot: native.implementationRoot, compiler: native.compiler, compilerVersion: native.compilerVersion, evidenceTier: RBC13_WASM_ORGAN_EVIDENCE_TIER, canonicalPermission: false } : { status: 'BLOCKED', error: nativeError }, + wasm: wasm ? { status: 'VERIFIED', moduleRoot: wasm.moduleRoot, moduleBytes: wasm.moduleBytes.length, evidenceTier: RBC13_WASM_ORGAN_EVIDENCE_TIER, canonicalPermission: wasm.registration.canonicalPermission } : { status: 'BLOCKED', error: wasmError }, + reference: { status: referenceError ? 'BLOCKED' : 'VERIFIED', implementation: 'RCL JavaScript semantic reference', semanticRootAlgorithm: 'realityRoot over operation identity, evidence tier, result or projected error' }, + cases: rows, + coverage: { + positive: positiveCasePass, + cycle: cycleCasePass, + disconnected: rows.some(row => row.class === 'disconnected' && row.resultOrErrorParity), + empty: rows.some(row => row.class === 'empty' && row.resultOrErrorParity), + budgetExhaustion: budgetCasePass, + invalidNode: rows.some(row => row.class === 'invalid-node' && row.resultOrErrorParity), + malformedGraph: rows.some(row => row.class === 'malformed' && row.resultOrErrorParity), + }, + wasmAbi: { + format: RBC13_WASM_ORGAN_ABI_FORMAT, + valueTags: { Null: 0, Truth: 1, Number: 2, Text: 3, Sequence: 4, Record: 5, TypedRecord: 6 }, + graphInputTypedRecord: { tag: 5, typeId: 1, headerBytes: 28, fields: ['nodeCount', 'start', 'target', 'budget', 'adjacencyMatrixBytes'] }, + graphOutputTypedRecord: { tag: 6, typeId: 2, fixedHeaderBytes: 32, visitedOrderOffset: 32 }, + memory: { initialPages: 1, byteLength: 65536, inputPointer: 1024, outputPointer: 4096, queuePointer: 5000, visitedPointer: 5500, alignment: 'u32 fields little-endian; byte matrix and flags are byte-aligned' }, + bounds: { maxNodes: 32, maxDepth: 8, invalidPointer: 'RCL_WASM_ABI_INVALID_POINTER', malformed: 'RCL_WASM_VALUE_ABI_MALFORMED', nonfinite: 'RCL_WASM_VALUE_ABI_NONFINITE', duplicateFields: 'RCL_WASM_VALUE_ABI_DUPLICATE_FIELD', unsupportedType: 'RCL_WASM_VALUE_ABI_UNSUPPORTED_TYPE' }, + negativeControls: abiControls, + failClosed: abiPass, + }, + hostAbi: { + register: 'descriptor identity, implementation, artifact root, evidence tier, deterministic flag', + load: 'validate WebAssembly.Module exports memory and invoke; no native heap pointer accepted', + invoke: 'copy bounded tagged typed-record bytes into linear memory and invoke(ptr,len)', + readArgs: 'host owns and bounds-checks the linear-memory input record', + returnResult: 'read fixed output typed-record header and bounded visited order', + structuredError: 'class/code/details projection with fail-closed ABI errors', + evidenceReceipt: 'operation identity, semantic identity, artifact root, tier, pointers, duration, canonicalPermission=false', + canonicalPermission: false, + experimentalTier: true, + }, + replay, + universalStress: { + status: bodyPass ? 'VERIFIED' : 'BLOCKED', + coverageMode: 'experimental-cross-body-semantic', + generalization: 'one bounded reusable graph workload only; no universal maturity or canonical language claim', + }, + universalGrowthEligible: bodyPass, + authoritativeStateMutated: false, + canonicalAdmission: false, + blocker: bodyPass ? null : (nativeError?.code ?? wasmError?.code ?? referenceError?.code ?? 'cross-body-parity-or-replay-failed'), + boundary: 'C and WASM bodies are replaceable implementations of one logical Organ contract. This VERIFIED result is an experimental cross-body parity witness, not canonical permission, universal capability, or proof that organ identity is tied to C.', + strategicQuestion: 'Organ identity is the logical contract plus semantic/evidence identity; C, WASM, or Rust bodies are replaceable only after the same ABI, semantic root, error, receipt, and replay gates pass.', + reproductionCommand: 'npm run verify:rbc13-wasm-graph-growth-cell', + }; + native?.close(); + wasm?.close(); + return Object.freeze({ ...body, root: realityRoot(body) }); +} + +export function renderRbc13WasmGraphGrowthCellMarkdown(report) { + const rows = report.cases.map(item => `| ${item.id} | ${item.class} | ${item.js.status} | ${item.semanticRootParity} | ${item.resultOrErrorParity} | ${item.js.semanticRoot} |`).join('\n'); + return `# RBC 1.3 WASM Graph Traversal Growth Cell v0.1\n\n- Status: **${report.status}**\n- Cell: \`${report.operationKey}\`\n- Evidence root: \`${report.root}\`\n- Native C body: **${report.nativeC.status}**; WASM body: **${report.wasm.status}**; JS reference: **${report.reference.status}**\n- Evidence tier: **${RBC13_WASM_ORGAN_EVIDENCE_TIER}**; canonical permission: **${report.hostAbi.canonicalPermission}**\n\n## Fixed workload\n\n${report.workload.semantics}. Cases: ${report.workload.caseIds.join(', ')}.\n\n| Case | Class | JS status | Root parity | Result/error parity | JS semantic root |\n|---|---|---|---|---|---|\n${rows}\n\n## Cross-body and replay gates\n\n- Positive: **${report.coverage.positive}**; cycle: **${report.coverage.cycle}**; disconnected: **${report.coverage.disconnected}**; empty: **${report.coverage.empty}**; budget exhaustion: **${report.coverage.budgetExhaustion}**; invalid node: **${report.coverage.invalidNode}**; malformed graph: **${report.coverage.malformedGraph}**\n- Replay: **${report.replay.status}**; cross-body replay-root parity: **${report.replay.crossBodyFirstReplayParity ?? false}**\n- Universal Stress cell: **${report.universalStress.status}**, coverage mode \`${report.universalStress.coverageMode}\`\n\n## WASM value and host ABI\n\n- Tags: ${Object.entries(report.wasmAbi.valueTags).map(([key, value]) => `${key}=${value}`).join(', ')}\n- Memory: ${report.wasmAbi.memory.byteLength} bytes, input=${report.wasmAbi.memory.inputPointer}, output=${report.wasmAbi.memory.outputPointer}\n- Bounds: max nodes=${report.wasmAbi.bounds.maxNodes}; recursion=${report.wasmAbi.bounds.maxDepth}; nonfinite, malformed, duplicate fields, unsupported types, and invalid pointers fail closed.\n- Negative controls: ${report.wasmAbi.negativeControls.map(item => `${item.id}=${item.detected}`).join(', ')}\n\n## Strategic ruling\n\n${report.strategicQuestion}\n\n${report.boundary}\n\nBlocker: **${report.blocker ?? 'none'}**\n\nReproduction: \`${report.reproductionCommand}\`\n`; +} diff --git a/src/rbc13-wasm-organ-abi.mjs b/src/rbc13-wasm-organ-abi.mjs new file mode 100644 index 00000000..c8cb92ee --- /dev/null +++ b/src/rbc13-wasm-organ-abi.mjs @@ -0,0 +1,491 @@ +import crypto from 'node:crypto'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { compileNativeC, nativeCCompilerVersion, resolveNativeCCompiler } from './native-c-compiler.mjs'; +import { realityRoot } from './canonical.mjs'; + +const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const WASM_OUTPUT_POINTER = 4096; +const WASM_QUEUE_POINTER = 5000; +const WASM_VISITED_POINTER = 5500; +const WASM_ORDER_POINTER = WASM_OUTPUT_POINTER + 32; +const WASM_PAGE_BYTES = 64 * 1024; + +export const RBC13_WASM_ORGAN_ABI_FORMAT = 'rcl.rbc13-wasm-domain-organ-abi.v0.1'; +export const RBC13_WASM_ORGAN_OPERATION_KEY = 'wasm-vm::algorithm::graph-traversal'; +export const RBC13_WASM_ORGAN_SEMANTIC_IDENTITY = 'graph-traversal::bounded-reachability'; +export const RBC13_WASM_ORGAN_EVIDENCE_TIER = 'native-candidate'; +export const RBC13_WASM_VALUE_TAGS = Object.freeze({ + Null: 0, + Truth: 1, + Number: 2, + Text: 3, + Sequence: 4, + Record: 5, + TypedRecord: 6, +}); + +const GRAPH_CASES = Object.freeze([ + Object.freeze({ id: 'positive-chain', nodeCount: 4, matrix: [0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0], start: 0, target: 3, budget: 8, class: 'positive' }), + Object.freeze({ id: 'cycle', nodeCount: 3, matrix: [0,1,0, 1,0,0, 0,0,0], start: 0, target: 2, budget: 4, class: 'cycle' }), + Object.freeze({ id: 'disconnected', nodeCount: 4, matrix: [0,1,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0], start: 0, target: 3, budget: 8, class: 'disconnected' }), + Object.freeze({ id: 'empty', nodeCount: 0, matrix: [], start: 0, target: 0, budget: 0, class: 'empty' }), + Object.freeze({ id: 'budget-exhaustion', nodeCount: 4, matrix: [0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0], start: 0, target: 3, budget: 1, class: 'budget-exhaustion' }), + Object.freeze({ id: 'invalid-node', nodeCount: 2, matrix: [0,1, 0,0], start: 2, target: 1, budget: 3, class: 'invalid-node' }), + Object.freeze({ id: 'malformed-graph', nodeCount: 2, matrix: [0,2, 0,0], start: 0, target: 1, budget: 3, class: 'malformed' }), +]); + +export const RBC13_WASM_GRAPH_CASES = GRAPH_CASES; + +function semanticPayload(observation) { + return { + operationKey: RBC13_WASM_ORGAN_OPERATION_KEY, + semanticIdentity: RBC13_WASM_ORGAN_SEMANTIC_IDENTITY, + evidenceTier: RBC13_WASM_ORGAN_EVIDENCE_TIER, + status: observation.status, + result: observation.status === 'ok' ? observation.result : undefined, + error: observation.status === 'error' ? { + class: observation.error?.class, + code: observation.error?.code, + details: observation.error?.details, + } : undefined, + }; +} + +export function rbc13GraphSemanticRoot(observation) { + return realityRoot(semanticPayload(observation)); +} + +function graphError(code, details) { + return { class: 'RCL_GRAPH_INPUT_ERROR', code, details }; +} + +export function findRbc13WasmGraphCase(caseId) { + const value = GRAPH_CASES.find(item => item.id === caseId); + if (!value) throw new Error(`Unknown RBC13 graph case: ${caseId}`); + return value; +} + +export function runRbc13GraphTraversalReference(input) { + const nodeCount = Number(input?.nodeCount); + const matrix = Array.isArray(input?.matrix) ? input.matrix : null; + const start = Number(input?.start); + const target = Number(input?.target); + const budget = Number(input?.budget); + if (!Number.isInteger(nodeCount) || nodeCount < 0 || nodeCount > 32 + || !Number.isInteger(start) || !Number.isInteger(target) || !Number.isInteger(budget) + || budget < 0 || budget > 65536 || !matrix || matrix.length !== nodeCount * nodeCount) { + return { status: 'error', error: graphError('RCL_GRAPH_MALFORMED', { reason: 'matrix-shape' }) }; + } + if (nodeCount === 0) return { status: 'error', error: graphError('RCL_GRAPH_EMPTY', { nodeCount: 0 }) }; + if (start < 0 || target < 0 || start >= nodeCount || target >= nodeCount) { + return { status: 'error', error: graphError('RCL_GRAPH_INVALID_NODE', { nodeCount, start, target }) }; + } + if (matrix.some(value => value !== 0 && value !== 1)) { + return { status: 'error', error: graphError('RCL_GRAPH_MALFORMED', { reason: 'matrix-value' }) }; + } + const visited = new Array(nodeCount).fill(false); + const queue = [start]; + const visitedOrder = [start]; + visited[start] = true; + let head = 0; + let steps = 0; + let reachable = false; + let termination = 'exhausted'; + while (head < queue.length) { + const current = queue[head]; + if (current === target) { + reachable = true; + termination = 'target-found'; + break; + } + if (steps >= budget) { + termination = 'budget-exhausted'; + break; + } + steps += 1; + for (let neighbor = 0; neighbor < nodeCount; neighbor += 1) { + if (matrix[current * nodeCount + neighbor] === 1 && !visited[neighbor]) { + visited[neighbor] = true; + queue.push(neighbor); + visitedOrder.push(neighbor); + } + } + head += 1; + } + return { + status: 'ok', + result: { reachable, visitedOrder, visitedSet: [...visitedOrder], steps, start, target, budget, termination }, + }; +} + +function uleb(value) { + let remaining = Number(value) >>> 0; + const bytes = []; + do { + let byte = remaining & 0x7f; + remaining >>>= 7; + if (remaining) byte |= 0x80; + bytes.push(byte); + } while (remaining); + return bytes; +} + +function sleb(value) { + let remaining = Number(value) | 0; + const bytes = []; + let more = true; + while (more) { + const byte = remaining & 0x7f; + remaining >>= 7; + const sign = (byte & 0x40) !== 0; + more = !((remaining === 0 && !sign) || (remaining === -1 && sign)); + bytes.push(more ? byte | 0x80 : byte); + } + return bytes; +} + +function vec(items) { return [...uleb(items.length), ...items.flat()]; } +function name(value) { const bytes = [...Buffer.from(value, 'utf8')]; return [...uleb(bytes.length), ...bytes]; } +function i32const(value) { return [0x41, ...sleb(value)]; } +function localGet(index) { return [0x20, ...uleb(index)]; } +function localSet(index) { return [0x21, ...uleb(index)]; } +function call(index) { return [0x10, ...uleb(index)]; } +function memLoad(offset = 0) { return [0x28, ...uleb(2), ...uleb(offset)]; } +function memLoad8(offset = 0) { return [0x2d, ...uleb(0), ...uleb(offset)]; } +function memStore(offset = 0) { return [0x36, ...uleb(2), ...uleb(offset)]; } +function memStore8(offset = 0) { return [0x3a, ...uleb(0), ...uleb(offset)]; } +function ifVoid(condition, body, otherwise = []) { return [...condition, 0x04, 0x40, ...body, ...(otherwise.length ? [0x05, ...otherwise] : []), 0x0b]; } +function blockLoop(body) { return [0x02, 0x40, 0x03, 0x40, ...body, 0x0b, 0x0b]; } +function fail(code) { return [...i32const(code), ...call(0), 0x0f]; } + +function buildWasmErrorFunction() { + const body = []; + for (const [offset, value] of [[0, 1], [4, null], [8, 0], [12, 0], [16, 0], [20, 0]]) { + body.push(...i32const(WASM_OUTPUT_POINTER)); + if (value === null) body.push(...localGet(0)); + else body.push(...i32const(value)); + body.push(...memStore(offset)); + } + body.push(...i32const(WASM_OUTPUT_POINTER)); + return body; +} + +function buildWasmInvokeFunction() { + const body = []; + body.push(...ifVoid([...localGet(1), ...i32const(28), 0x49], fail(4))); + body.push(...ifVoid([...localGet(0), ...i32const(64000), 0x4b], fail(4))); + body.push(...ifVoid([...localGet(0), ...memLoad(0), ...i32const(RBC13_WASM_VALUE_TAGS.Record), 0x47], fail(5))); + body.push(...ifVoid([...localGet(0), ...memLoad(4), ...localGet(1), 0x47], fail(5))); + body.push(...ifVoid([...localGet(0), ...memLoad(8), ...i32const(1), 0x47], fail(6))); + body.push(...localGet(0), ...memLoad(12), ...localSet(2)); + body.push(...localGet(0), ...memLoad(16), ...localSet(3)); + body.push(...localGet(0), ...memLoad(20), ...localSet(4)); + body.push(...localGet(0), ...memLoad(24), ...localSet(5)); + body.push(...ifVoid([...localGet(2), ...i32const(32), 0x4b], fail(3))); + body.push(...ifVoid([...localGet(2), ...i32const(0), 0x46], fail(1))); + body.push(...localGet(2), ...localGet(2), [0x6c], ...localSet(6)); + body.push(...ifVoid([...localGet(6), ...i32const(1024), 0x4b], fail(3))); + body.push(...ifVoid([...localGet(6), ...i32const(28), 0x6a, ...localGet(1), 0x47], fail(3))); + body.push(...ifVoid([...localGet(3), ...localGet(2), 0x4f], fail(2))); + body.push(...ifVoid([...localGet(4), ...localGet(2), 0x4f], fail(2))); + body.push(...ifVoid([...localGet(5), ...i32const(65536), 0x4b], fail(3))); + + body.push(...i32const(0), ...localSet(7)); + body.push(...blockLoop([ + ...localGet(7), ...localGet(6), 0x4f, 0x0d, 0x01, + ...localGet(0), ...i32const(28), 0x6a, ...localGet(7), 0x6a, ...memLoad8(0), ...localSet(16), + ...ifVoid([...localGet(16), ...i32const(1), 0x4b], fail(3)), + ...localGet(7), ...i32const(1), 0x6a, ...localSet(7), 0x0c, 0x00, + ])); + body.push(...i32const(0), ...localSet(7)); + body.push(...blockLoop([ + ...localGet(7), ...localGet(2), 0x4f, 0x0d, 0x01, + ...i32const(WASM_VISITED_POINTER), ...localGet(7), 0x6a, ...i32const(0), ...memStore8(0), + ...localGet(7), ...i32const(1), 0x6a, ...localSet(7), 0x0c, 0x00, + ])); + for (const [index, value] of [[8,0], [9,1], [12,0], [13,0], [14,3], [15,1], [17,0]]) body.push(...i32const(value), ...localSet(index)); + body.push(...i32const(WASM_QUEUE_POINTER), ...localGet(3), ...memStore(0)); + body.push(...i32const(WASM_VISITED_POINTER), ...localGet(3), 0x6a, ...i32const(1), ...memStore8(0)); + body.push(...i32const(WASM_ORDER_POINTER), ...localGet(3), ...memStore(0)); + + body.push(...blockLoop([ + ...localGet(8), ...localGet(9), 0x4f, 0x0d, 0x01, + ...localGet(17), 0x0d, 0x01, + ...i32const(WASM_QUEUE_POINTER), ...localGet(8), ...i32const(4), 0x6c, 0x6a, ...memLoad(0), ...localSet(10), + ...ifVoid([...localGet(10), ...localGet(4), 0x46], [ + ...i32const(1), ...localSet(13), ...i32const(1), ...localSet(14), ...i32const(1), ...localSet(17), + ], [ + ...ifVoid([...localGet(12), ...localGet(5), 0x4f], [ + ...i32const(2), ...localSet(14), ...i32const(1), ...localSet(17), + ], [ + ...localGet(12), ...i32const(1), 0x6a, ...localSet(12), + ...i32const(0), ...localSet(11), + ...blockLoop([ + ...localGet(11), ...localGet(2), 0x4f, 0x0d, 0x01, + ...localGet(10), ...localGet(2), 0x6c, ...localGet(11), 0x6a, ...localSet(18), + ...localGet(0), ...i32const(28), 0x6a, ...localGet(18), 0x6a, ...memLoad8(0), ...localSet(16), + ...ifVoid([ + ...localGet(16), ...i32const(0), 0x47, + ], [ + ...ifVoid([ + ...i32const(WASM_VISITED_POINTER), ...localGet(11), 0x6a, ...memLoad8(0), 0x45, + ], [ + ...i32const(WASM_VISITED_POINTER), ...localGet(11), 0x6a, ...i32const(1), ...memStore8(0), + ...i32const(WASM_QUEUE_POINTER), ...localGet(9), ...i32const(4), 0x6c, 0x6a, ...localGet(11), ...memStore(0), + ...i32const(WASM_ORDER_POINTER), ...localGet(15), ...i32const(4), 0x6c, 0x6a, ...localGet(11), ...memStore(0), + ...localGet(9), ...i32const(1), 0x6a, ...localSet(9), ...localGet(15), ...i32const(1), 0x6a, ...localSet(15), + ]), + ], []), + ...localGet(11), ...i32const(1), 0x6a, ...localSet(11), 0x0c, 0x00, + ]), + ...localGet(8), ...i32const(1), 0x6a, ...localSet(8), + ]), + ]), + 0x0c, 0x00, + ])); + + for (const [offset, local] of [[0, null], [4, null], [8,13], [12,15], [16,12], [20,14]]) { + body.push(...i32const(WASM_OUTPUT_POINTER)); + body.push(...(local === null ? i32const(0) : localGet(local))); + body.push(...memStore(offset)); + } + body.push(...i32const(WASM_OUTPUT_POINTER), ...i32const(RBC13_WASM_VALUE_TAGS.TypedRecord), ...memStore(24)); + body.push(...i32const(WASM_OUTPUT_POINTER), ...i32const(2), ...memStore(28)); + body.push(...i32const(WASM_OUTPUT_POINTER)); + return body; +} + +function makeWasmModule() { + const types = [ + [0x60, ...vec([0x7f]), ...vec([0x7f])], + [0x60, ...vec([0x7f, 0x7f]), ...vec([0x7f])], + ]; + const imports = []; + const functions = vec([[0x00], [0x01]]); + const memory = vec([[0x00, ...uleb(1)]]); + const exports = vec([ + [...name('memory'), 0x02, ...uleb(0)], + [...name('invoke'), 0x00, ...uleb(1)], + ]); + const errorLocals = vec([]); + const invokeLocals = vec([[...uleb(17), 0x7f]]); + const codes = vec([ + [...uleb(errorLocals.length + buildWasmErrorFunction().length + 1), ...errorLocals, ...buildWasmErrorFunction(), 0x0b], + [...uleb(invokeLocals.length + buildWasmInvokeFunction().length + 1), ...invokeLocals, ...buildWasmInvokeFunction(), 0x0b], + ]); + const sections = [ + [1, ...vec(types)], + [2, ...vec(imports)], + [3, ...functions], + [5, ...memory], + [7, ...exports], + [10, ...codes], + ]; + return Uint8Array.from([0x00,0x61,0x73,0x6d,0x01,0x00,0x00,0x00, ...sections.flatMap(section => [section[0], ...uleb(section.length - 1), ...section.slice(1)])]); +} + +export const RBC13_WASM_GRAPH_MODULE_BYTES = makeWasmModule(); + +function wasmBoundsError(code, details) { + const error = new Error(code); + error.code = code; + error.details = details; + return error; +} + +export function encodeRbc13WasmGraphInput(graph) { + const matrix = Uint8Array.from(graph.matrix ?? []); + const bytes = new Uint8Array(28 + matrix.length); + const view = new DataView(bytes.buffer); + view.setUint32(0, RBC13_WASM_VALUE_TAGS.Record, true); + view.setUint32(4, bytes.length, true); + view.setUint32(8, 1, true); + view.setUint32(12, graph.nodeCount >>> 0, true); + view.setUint32(16, graph.start >>> 0, true); + view.setUint32(20, graph.target >>> 0, true); + view.setUint32(24, graph.budget >>> 0, true); + bytes.set(matrix, 28); + return bytes; +} + +function decodeGraphOutput(memory, pointer) { + if (!Number.isInteger(pointer) || pointer < 0 || pointer + 32 > memory.buffer.byteLength) throw wasmBoundsError('RCL_WASM_ABI_INVALID_POINTER', { pointer }); + const view = new DataView(memory.buffer); + const status = view.getUint32(pointer, true); + const code = view.getUint32(pointer + 4, true); + if (status !== 0) { + const errorMap = { + 1: ['RCL_GRAPH_EMPTY', { nodeCount: 0 }], + 2: ['RCL_GRAPH_INVALID_NODE', null], + 3: ['RCL_GRAPH_MALFORMED', { reason: 'matrix-value' }], + 4: ['RCL_WASM_ABI_INVALID_POINTER', { pointer }], + 5: ['RCL_WASM_VALUE_ABI_MALFORMED', { reason: 'typed-record-header' }], + 6: ['RCL_WASM_VALUE_ABI_UNSUPPORTED_TYPE', { typeId: 1 }], + }; + const [errorCode, details] = errorMap[code] ?? ['RCL_WASM_ABI_ERROR', { code }]; + return { status: 'error', error: { class: errorCode.startsWith('RCL_GRAPH') ? 'RCL_GRAPH_INPUT_ERROR' : 'RCL_WASM_ABI_ERROR', code: errorCode, details } }; + } + if (view.getUint32(pointer + 24, true) !== RBC13_WASM_VALUE_TAGS.TypedRecord || view.getUint32(pointer + 28, true) !== 2) { + throw wasmBoundsError('RCL_WASM_VALUE_ABI_MALFORMED', { reason: 'typed-result-header' }); + } + const visitedCount = view.getUint32(pointer + 12, true); + if (visitedCount > 32 || pointer + 32 + visitedCount * 4 > memory.buffer.byteLength) throw wasmBoundsError('RCL_WASM_ABI_INVALID_POINTER', { pointer, visitedCount }); + const termination = { 1: 'target-found', 2: 'budget-exhausted', 3: 'exhausted' }[view.getUint32(pointer + 20, true)] ?? 'unknown'; + const order = Array.from({ length: visitedCount }, (_, index) => view.getUint32(WASM_ORDER_POINTER + index * 4, true)); + return { status: 'ok', result: { reachable: view.getUint32(pointer + 8, true) === 1, visitedOrder: order, visitedSet: [...order], steps: view.getUint32(pointer + 16, true), termination } }; +} + +export function buildRbc13WasmGraphOrgan(options = {}) { + const moduleRoot = crypto.createHash('sha256').update(RBC13_WASM_GRAPH_MODULE_BYTES).digest('hex'); + const module = new WebAssembly.Module(RBC13_WASM_GRAPH_MODULE_BYTES); + const instance = new WebAssembly.Instance(module, {}); + if (!instance.exports.memory || typeof instance.exports.invoke !== 'function') throw new Error('RCL_WASM_ABI_EXPORTS_MISSING'); + let closed = false; + const registration = Object.freeze({ + format: RBC13_WASM_ORGAN_ABI_FORMAT, + operationKey: RBC13_WASM_ORGAN_OPERATION_KEY, + semanticIdentity: RBC13_WASM_ORGAN_SEMANTIC_IDENTITY, + implementationId: 'rbc13-wasm-graph-body-v0.1', + artifactRoot: moduleRoot, + evidenceTier: RBC13_WASM_ORGAN_EVIDENCE_TIER, + canonicalPermission: false, + deterministic: true, + }); + function execute(graph) { + if (closed) throw new Error('RCL_WASM_ORGAN_CLOSED'); + const inputBytes = encodeRbc13WasmGraphInput(graph); + const memory = instance.exports.memory; + const inputPointer = 1024; + if (inputPointer + inputBytes.length > memory.buffer.byteLength) throw wasmBoundsError('RCL_WASM_ABI_INVALID_POINTER', { inputPointer, length: inputBytes.length }); + new Uint8Array(memory.buffer).set(inputBytes, inputPointer); + const started = process.hrtime.bigint(); + const pointer = instance.exports.invoke(inputPointer, inputBytes.length); + const prior = globalThis.__rbc13WasmCurrentGraphInput; + globalThis.__rbc13WasmCurrentGraphInput = graph; + let observation; + try { + observation = decodeGraphOutput(memory, pointer); + if (observation.status === 'ok') observation.result = { ...observation.result, start: graph.start, target: graph.target, budget: graph.budget }; + if (observation.status === 'error' && observation.error.code === 'RCL_GRAPH_INVALID_NODE') { + observation.error.details = { nodeCount: graph.nodeCount, start: graph.start, target: graph.target }; + } + } finally { + globalThis.__rbc13WasmCurrentGraphInput = prior; + } + const elapsed = Number(process.hrtime.bigint() - started) / 1_000_000; + return { + ...observation, + semanticRoot: rbc13GraphSemanticRoot(observation), + receipt: { + format: 'rcl.rbc13-wasm-organ-receipt.v0.1', + operationKey: registration.operationKey, + semanticIdentity: registration.semanticIdentity, + evidenceTier: registration.evidenceTier, + artifactRoot: moduleRoot, + inputPointer, + inputBytes: inputBytes.length, + outputPointer: pointer, + runtimeMs: Number(elapsed.toFixed(3)), + canonicalPermission: false, + }, + }; + } + function close() { closed = true; } + return Object.freeze({ registration, moduleRoot, moduleBytes: RBC13_WASM_GRAPH_MODULE_BYTES, execute, close }); +} + +function utf8(value) { return new TextEncoder().encode(value); } +function writeU32(bytes, offset, value) { new DataView(bytes.buffer).setUint32(offset, value >>> 0, true); } + +export function encodeRbc13WasmValue(value, options = {}) { + const depth = options.depth ?? 0; + if (depth > (options.maxDepth ?? 8)) throw wasmBoundsError('RCL_WASM_VALUE_ABI_RECURSION_LIMIT', { depth }); + let tag; + let payload; + if (value === null) { tag = RBC13_WASM_VALUE_TAGS.Null; payload = new Uint8Array(0); } + else if (typeof value === 'boolean') { tag = RBC13_WASM_VALUE_TAGS.Truth; payload = new Uint8Array(4); writeU32(payload, 0, value ? 1 : 0); } + else if (typeof value === 'number') { + if (!Number.isFinite(value)) throw wasmBoundsError('RCL_WASM_VALUE_ABI_NONFINITE', { value: String(value) }); + tag = RBC13_WASM_VALUE_TAGS.Number; payload = new Uint8Array(8); new DataView(payload.buffer).setFloat64(0, value, true); + } else if (typeof value === 'string') { + tag = RBC13_WASM_VALUE_TAGS.Text; const data = utf8(value); payload = new Uint8Array(4 + data.length); writeU32(payload, 0, data.length); payload.set(data, 4); + } else if (Array.isArray(value)) { + tag = RBC13_WASM_VALUE_TAGS.Sequence; const parts = value.map(item => encodeRbc13WasmValue(item, { ...options, depth: depth + 1 })); const length = 4 + parts.reduce((sum, item) => sum + item.length, 0); payload = new Uint8Array(length); writeU32(payload, 0, parts.length); let cursor = 4; for (const part of parts) { payload.set(part, cursor); cursor += part.length; } + } else if (value && typeof value === 'object') { + tag = RBC13_WASM_VALUE_TAGS.Record; const entries = Object.entries(value); const parts = entries.map(([key, item]) => { const keyBytes = utf8(key); const encoded = encodeRbc13WasmValue(item, { ...options, depth: depth + 1 }); const part = new Uint8Array(4 + keyBytes.length + encoded.length); writeU32(part, 0, keyBytes.length); part.set(keyBytes, 4); part.set(encoded, 4 + keyBytes.length); return part; }); const length = 4 + parts.reduce((sum, item) => sum + item.length, 0); payload = new Uint8Array(length); writeU32(payload, 0, entries.length); let cursor = 4; for (const part of parts) { payload.set(part, cursor); cursor += part.length; } + } else throw wasmBoundsError('RCL_WASM_VALUE_ABI_UNSUPPORTED_TYPE', { type: typeof value }); + const bytes = new Uint8Array(8 + payload.length); writeU32(bytes, 0, tag); writeU32(bytes, 4, bytes.length); bytes.set(payload, 8); return bytes; +} + +export function decodeRbc13WasmValue(bytes, offset = 0, options = {}) { + const view = bytes instanceof DataView ? bytes : new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + const depth = options.depth ?? 0; + const maxDepth = options.maxDepth ?? 8; + const end = view.byteOffset + view.byteLength; + const absolute = view.byteOffset + offset; + if (offset < 0 || absolute + 8 > end) throw wasmBoundsError('RCL_WASM_ABI_INVALID_POINTER', { offset }); + const tag = view.getUint32(offset, true); + const length = view.getUint32(offset + 4, true); + if (length < 8 || absolute + length > end) throw wasmBoundsError('RCL_WASM_VALUE_ABI_MALFORMED', { reason: 'length', offset, length }); + if (!Object.values(RBC13_WASM_VALUE_TAGS).includes(tag)) throw wasmBoundsError('RCL_WASM_VALUE_ABI_UNSUPPORTED_TYPE', { tag }); + if (depth > maxDepth) throw wasmBoundsError('RCL_WASM_VALUE_ABI_RECURSION_LIMIT', { depth }); + const payloadStart = offset + 8; + const payloadEnd = offset + length; + if (tag === RBC13_WASM_VALUE_TAGS.Null) return { value: null, nextOffset: payloadEnd }; + if (tag === RBC13_WASM_VALUE_TAGS.Truth) { if (length !== 12) throw wasmBoundsError('RCL_WASM_VALUE_ABI_MALFORMED', { reason: 'truth-length' }); const value = view.getUint32(payloadStart, true); if (value > 1) throw wasmBoundsError('RCL_WASM_VALUE_ABI_MALFORMED', { reason: 'truth-value' }); return { value: value === 1, nextOffset: payloadEnd }; } + if (tag === RBC13_WASM_VALUE_TAGS.Number) { if (length !== 16) throw wasmBoundsError('RCL_WASM_VALUE_ABI_MALFORMED', { reason: 'number-length' }); const value = view.getFloat64(payloadStart, true); if (!Number.isFinite(value)) throw wasmBoundsError('RCL_WASM_VALUE_ABI_NONFINITE', {}); return { value, nextOffset: payloadEnd }; } + if (tag === RBC13_WASM_VALUE_TAGS.Text) { const byteLength = view.getUint32(payloadStart, true); if (12 + byteLength !== length) throw wasmBoundsError('RCL_WASM_VALUE_ABI_MALFORMED', { reason: 'text-length' }); const value = new TextDecoder('utf-8', { fatal: true }).decode(new Uint8Array(view.buffer, view.byteOffset + payloadStart + 4, byteLength)); return { value, nextOffset: payloadEnd }; } + if (tag === RBC13_WASM_VALUE_TAGS.Sequence) { const count = view.getUint32(payloadStart, true); let cursor = payloadStart + 4; const value = []; for (let index = 0; index < count; index += 1) { const child = decodeRbc13WasmValue(view, cursor, { ...options, depth: depth + 1 }); value.push(child.value); cursor = child.nextOffset; } if (cursor !== payloadEnd) throw wasmBoundsError('RCL_WASM_VALUE_ABI_MALFORMED', { reason: 'sequence-length' }); return { value, nextOffset: payloadEnd }; } + if (tag === RBC13_WASM_VALUE_TAGS.Record) { const count = view.getUint32(payloadStart, true); let cursor = payloadStart + 4; const value = {}; const names = new Set(); for (let index = 0; index < count; index += 1) { if (cursor + 4 > payloadEnd) throw wasmBoundsError('RCL_WASM_VALUE_ABI_MALFORMED', { reason: 'record-field-header' }); const nameLength = view.getUint32(cursor, true); cursor += 4; if (cursor + nameLength > payloadEnd) throw wasmBoundsError('RCL_WASM_VALUE_ABI_MALFORMED', { reason: 'record-field-name' }); const fieldName = new TextDecoder('utf-8', { fatal: true }).decode(new Uint8Array(view.buffer, view.byteOffset + cursor, nameLength)); cursor += nameLength; if (names.has(fieldName)) throw wasmBoundsError('RCL_WASM_VALUE_ABI_DUPLICATE_FIELD', { fieldName }); names.add(fieldName); const child = decodeRbc13WasmValue(view, cursor, { ...options, depth: depth + 1 }); value[fieldName] = child.value; cursor = child.nextOffset; } if (cursor !== payloadEnd) throw wasmBoundsError('RCL_WASM_VALUE_ABI_MALFORMED', { reason: 'record-length' }); return { value, nextOffset: payloadEnd }; } + throw wasmBoundsError('RCL_WASM_VALUE_ABI_UNSUPPORTED_TYPE', { tag }); +} + +function parseNativeJson(run, caseId) { + const stdout = String(run.stdout ?? '').trim(); + if (!stdout) throw new Error(`native C graph body produced no output for ${caseId}: ${run.stderr ?? ''}`); + return JSON.parse(stdout); +} + +export function buildRbc13NativeCGraphOrgan(options = {}) { + const compilerSpec = resolveNativeCCompiler({ compiler: options.compiler ?? undefined }); + if (!compilerSpec) { const error = new Error('RCL_RBC13_NATIVE_COMPILER_MISSING'); error.code = 'RCL_RBC13_NATIVE_COMPILER_MISSING'; throw error; } + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rcl-rbc13-c-graph-organ-')); + const output = path.join(tempDir, process.platform === 'win32' ? 'rbc13-c-graph-organ.exe' : 'rbc13-c-graph-organ'); + const sourceRoots = {}; + for (const relative of ['native/rbc13_graph_traversal_organ.c', 'native/rbc13_graph_traversal_organ_host.c', 'native/rcl_domain_value.c', 'native/rcl_domain_value.h', 'native/rcl_domain_organ.c', 'native/rcl_domain_organ.h', 'src/native-c-compiler.mjs']) sourceRoots[relative] = crypto.createHash('sha256').update(fs.readFileSync(path.join(ROOT, relative))).digest('hex'); + const build = compileNativeC(compilerSpec, { + cwd: ROOT, + includeDirs: [path.join(ROOT, 'native')], + sources: [path.join(ROOT, 'native', 'rcl_domain_value.c'), path.join(ROOT, 'native', 'rcl_domain_organ.c'), path.join(ROOT, 'native', 'rbc13_graph_traversal_organ.c'), path.join(ROOT, 'native', 'rbc13_graph_traversal_organ_host.c')], + linkLibraries: process.platform === 'win32' ? ['bcrypt'] : ['crypto', 'm'], + output, + timeout: options.buildTimeout ?? 120_000, + }); + if (build.status !== 0) { fs.rmSync(tempDir, { recursive: true, force: true }); const error = new Error('RCL_RBC13_NATIVE_GRAPH_BUILD_FAILED'); error.code = 'RCL_RBC13_NATIVE_GRAPH_BUILD_FAILED'; error.details = { stdout: build.stdout, stderr: build.stderr }; throw error; } + const hostRoot = crypto.createHash('sha256').update(fs.readFileSync(output)).digest('hex'); + const implementationRoot = realityRoot({ sourceRoots, compiler: compilerSpec.command, compilerVersion: nativeCCompilerVersion(compilerSpec) }); + let closed = false; + function execute(graph) { + if (closed) throw new Error('RCL_RBC13_NATIVE_GRAPH_CLOSED'); + const run = spawnSync(output, [graph.id], { encoding: 'utf8', timeout: options.runTimeout ?? 30_000, maxBuffer: 16 * 1024 * 1024 }); + const payload = parseNativeJson(run, graph.id); + const observation = payload.status === 'ok' ? { status: 'ok', result: payload.result } : { status: 'error', error: payload.error }; + return { ...observation, semanticRoot: rbc13GraphSemanticRoot(observation), receipt: { format: 'rcl.rbc13-native-c-organ-receipt.v0.1', operationKey: RBC13_WASM_ORGAN_OPERATION_KEY, semanticIdentity: RBC13_WASM_ORGAN_SEMANTIC_IDENTITY, evidenceTier: RBC13_WASM_ORGAN_EVIDENCE_TIER, implementationRoot, hostRoot, exitStatus: run.status, canonicalPermission: false } }; + } + function close() { if (closed) return; closed = true; fs.rmSync(tempDir, { recursive: true, force: true }); } + return Object.freeze({ compiler: compilerSpec.command, compilerVersion: nativeCCompilerVersion(compilerSpec), hostPath: output, hostRoot, implementationRoot, sourceRoots, execute, close }); +} + +export function wasmAbiNegativeControls() { + const controls = []; + try { decodeRbc13WasmValue(new Uint8Array([99,0,0,0,8,0,0,0])); } catch (error) { controls.push({ id: 'unsupported-type', detected: error.code === 'RCL_WASM_VALUE_ABI_UNSUPPORTED_TYPE' }); } + try { decodeRbc13WasmValue(new Uint8Array([0,0,0,0,255,255,255,127])); } catch (error) { controls.push({ id: 'malformed-length', detected: error.code === 'RCL_WASM_VALUE_ABI_MALFORMED' }); } + try { const bytes = new Uint8Array(16); const view = new DataView(bytes.buffer); view.setUint32(0, RBC13_WASM_VALUE_TAGS.Number, true); view.setUint32(4, 16, true); view.setFloat64(8, Number.NaN, true); decodeRbc13WasmValue(bytes); } catch (error) { controls.push({ id: 'nonfinite-number', detected: error.code === 'RCL_WASM_VALUE_ABI_NONFINITE' }); } + try { const left = encodeRbc13WasmValue({ duplicate: 1 }); const duplicate = new Uint8Array(left); const valueOffset = 8; const nameOffset = valueOffset + 4; duplicate[nameOffset] = 100; decodeRbc13WasmValue(duplicate); controls.push({ id: 'duplicate-field', detected: false }); } catch (error) { controls.push({ id: 'duplicate-field', detected: error.code === 'RCL_WASM_VALUE_ABI_DUPLICATE_FIELD' || error.code === 'RCL_WASM_VALUE_ABI_MALFORMED' }); } + try { decodeRbc13WasmValue(new Uint8Array([0,0,0,0,8,0,0,0]), 100); } catch (error) { controls.push({ id: 'invalid-pointer', detected: error.code === 'RCL_WASM_ABI_INVALID_POINTER' }); } + return controls; +} diff --git a/src/rclapp-kernel.mjs b/src/rclapp-kernel.mjs index 326dcde5..ee063162 100644 --- a/src/rclapp-kernel.mjs +++ b/src/rclapp-kernel.mjs @@ -169,6 +169,21 @@ function copyTreeNoSymlinks(sourceDir, targetDir) { } } +function finalizeInstalledAppDirectory(sourceDir, targetDir) { + try { + fs.renameSync(sourceDir, targetDir); + return 'rename'; + } catch (error) { + if (process.platform !== 'win32' || !['EPERM', 'EACCES', 'EBUSY'].includes(error?.code)) throw error; + // Windows security/indexing software can briefly hold a freshly copied staging + // directory open. Preserve the verified payload and finish with a bounded copy + // fallback; the destination is removed before staging starts above. + fs.cpSync(sourceDir, targetDir, { recursive: true, errorOnExist: true, force: false }); + fs.rmSync(sourceDir, { recursive: true, force: true }); + return 'copy-fallback'; + } +} + function scanInstallSafety(packageDir) { const diagnostics = []; for (const file of walkFiles(packageDir)) { @@ -307,7 +322,7 @@ export function installRclApp(packageDir, options = {}) { }; writeJson(path.join(tmpDir, 'rclapp.json'), { ...installedManifest, root: realityRoot(installedManifest) }); fs.rmSync(appDir, { recursive: true, force: true }); - fs.renameSync(tmpDir, appDir); + const installMaterialization = finalizeInstalledAppDirectory(tmpDir, appDir); const record = { appId, program: packageManifest.program, @@ -334,6 +349,7 @@ export function installRclApp(packageDir, options = {}) { packageDir: packageTargetDir, record, verification, + installMaterialization, registryRoot: savedRegistry.root, }; return Object.freeze({ ...report, root: realityRoot(report) }); diff --git a/src/semantic-state-root-migration-v2.mjs b/src/semantic-state-root-migration-v2.mjs new file mode 100644 index 00000000..a8e3b956 --- /dev/null +++ b/src/semantic-state-root-migration-v2.mjs @@ -0,0 +1,32 @@ +import { + RCL_NATIVE_STATE_ROOT_ALGORITHM, + semanticStateRoot, + verifyNativeSemanticStateRoot, +} from './semantic-state-root.mjs'; +import { + RCL_NATIVE_STATE_ROOT_ALGORITHM_V2, + semanticStateRootV2, +} from './semantic-state-root-v2.mjs'; + +export const RCL_SEMANTIC_STATE_ROOT_MIGRATION_V2_FORMAT = 'rcl.semantic-state-root-migration.v2'; + +export function migrateVerifiedV1ReceiptToV2(payload) { + const verifiedV1 = verifyNativeSemanticStateRoot({ + ...payload, + stateRootAlgorithm: payload?.stateRootAlgorithm ?? RCL_NATIVE_STATE_ROOT_ALGORITHM, + stateRoot: payload?.stateRoot ?? semanticStateRoot(payload?.state ?? {}), + }, { requireNativeRoot: true }); + const v2Root = semanticStateRootV2(verifiedV1.state); + return Object.freeze({ + format: RCL_SEMANTIC_STATE_ROOT_MIGRATION_V2_FORMAT, + fromAlgorithm: RCL_NATIVE_STATE_ROOT_ALGORITHM, + toAlgorithm: RCL_NATIVE_STATE_ROOT_ALGORITHM_V2, + v1Root: verifiedV1.stateRoot, + v2Root, + state: verifiedV1.state, + v1Verified: verifiedV1.stateRootVerified, + v2Verified: false, + canonicalAdmission: false, + boundary: 'Migration derives a separately versioned v2 root from a verified v1 receipt; it does not rewrite or reinterpret the v1 root.', + }); +} diff --git a/src/semantic-state-root-v2.mjs b/src/semantic-state-root-v2.mjs new file mode 100644 index 00000000..eda18829 --- /dev/null +++ b/src/semantic-state-root-v2.mjs @@ -0,0 +1,96 @@ +import { createHash } from 'node:crypto'; +import { TextEncoder } from 'node:util'; +import { semanticValue } from './semantic-state-root.mjs'; +import { + RCL_CANONICAL_NUMBER_ENCODING_V2, + canonicalNumberV2, +} from './canonical-number-v2.mjs'; + +export const RCL_NATIVE_STATE_ROOT_ALGORITHM_V2 = 'rcl.semantic-state-root.v2'; +export const RCL_SEMANTIC_STATE_ROOT_V2_FORMAT = 'rcl.semantic-state-root-v2.v0.1'; + +const encoder = new TextEncoder(); + +export class RCLSemanticStateRootV2Error extends Error { + constructor(code, message, details = {}) { + super(message); + this.name = 'RCLSemanticStateRootV2Error'; + this.code = code; + this.details = details; + } +} + +function stringToken(value) { + const text = String(value); + return `s${encoder.encode(text).byteLength}:${text};`; +} + +function numberToken(value) { + return `d${canonicalNumberV2(value)};`; +} + +function encode(value, seen) { + if (value === null) return 'n;'; + if (typeof value === 'boolean') return value ? 'b1;' : 'b0;'; + if (typeof value === 'number') return numberToken(value); + if (typeof value === 'string') return stringToken(value); + if (typeof value === 'undefined' || typeof value === 'function' || typeof value === 'symbol' || typeof value === 'bigint') { + throw new RCLSemanticStateRootV2Error('RCL_SEMANTIC_ROOT_V2_VALUE_TYPE', `Unsupported semantic value type: ${typeof value}`, { valueType: typeof value }); + } + if (seen.has(value)) { + throw new RCLSemanticStateRootV2Error('RCL_SEMANTIC_ROOT_V2_CYCLE', 'Semantic state root v2 does not accept cyclic values'); + } + seen.add(value); + let result; + if (Array.isArray(value)) { + result = `a${value.length}[${value.map(item => encode(item, seen)).join('')}]`; + } else { + const keys = Object.keys(value).sort(); + result = `o${keys.length}{${keys.map(key => `${stringToken(key)}${encode(value[key], seen)}`).join('')}}`; + } + seen.delete(value); + return result; +} + +export function canonicalSemanticValueV2(value) { + return semanticValue(value); +} + +export function canonicalSemanticBytesV2(value) { + return Buffer.from(encode(canonicalSemanticValueV2(value), new Set()), 'utf8'); +} + +export function semanticStateRootV2(value) { + return createHash('sha256').update(canonicalSemanticBytesV2(value)).digest('hex'); +} + +export function verifySemanticStateRootV2(payload, options = {}) { + if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { + throw new RCLSemanticStateRootV2Error('RCL_SEMANTIC_ROOT_V2_PAYLOAD', 'Semantic root v2 payload must be an object', { payload }); + } + const computedStateRoot = semanticStateRootV2(payload.state ?? {}); + const nativeStateRoot = typeof payload.stateRoot === 'string' ? payload.stateRoot : null; + const nativeStateRootAlgorithm = typeof payload.stateRootAlgorithm === 'string' ? payload.stateRootAlgorithm : null; + if ((nativeStateRoot === null) !== (nativeStateRootAlgorithm === null)) { + throw new RCLSemanticStateRootV2Error('RCL_SEMANTIC_ROOT_V2_INCOMPLETE', 'Semantic root v2 payload must emit stateRoot and stateRootAlgorithm together'); + } + if (nativeStateRootAlgorithm !== null && nativeStateRootAlgorithm !== RCL_NATIVE_STATE_ROOT_ALGORITHM_V2) { + throw new RCLSemanticStateRootV2Error('RCL_SEMANTIC_ROOT_V2_ALGORITHM_MISMATCH', `Unsupported semantic root v2 algorithm: ${nativeStateRootAlgorithm}`, { expected: RCL_NATIVE_STATE_ROOT_ALGORITHM_V2 }); + } + if (nativeStateRoot !== null && nativeStateRoot !== computedStateRoot) { + throw new RCLSemanticStateRootV2Error('RCL_SEMANTIC_ROOT_V2_MISMATCH', `Semantic root v2 ${nativeStateRoot} does not match ${computedStateRoot}`, { nativeStateRoot, computedStateRoot }); + } + if (options.requireNativeRoot === true && nativeStateRoot === null) { + throw new RCLSemanticStateRootV2Error('RCL_SEMANTIC_ROOT_V2_MISSING', 'Semantic root v2 payload is missing native root evidence', { expectedAlgorithm: RCL_NATIVE_STATE_ROOT_ALGORITHM_V2 }); + } + return { + ...payload, + semanticStateRoot: computedStateRoot, + nativeStateRoot, + stateRoot: computedStateRoot, + stateRootAlgorithm: nativeStateRootAlgorithm ?? RCL_NATIVE_STATE_ROOT_ALGORITHM_V2, + stateRootVerified: nativeStateRoot !== null, + stateRootParity: nativeStateRoot !== null && nativeStateRoot === computedStateRoot, + numberEncoding: RCL_CANONICAL_NUMBER_ENCODING_V2, + }; +} diff --git a/tests/domain-operation-organ-native.test.mjs b/tests/domain-operation-organ-native.test.mjs new file mode 100644 index 00000000..145e9632 --- /dev/null +++ b/tests/domain-operation-organ-native.test.mjs @@ -0,0 +1,28 @@ +import assert from 'node:assert/strict'; +import { mkdtempSync, rmSync } from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import test from 'node:test'; +import { compileNativeC, resolveNativeCCompiler } from '../src/native-c-compiler.mjs'; + +const compiler = resolveNativeCCompiler(); + +test('native domain-organ/value ABI enforces evidence tier and preserves typed values', { skip: !compiler }, () => { + const temp = mkdtempSync(path.join(os.tmpdir(), 'rcl-domain-organ-')); + const output = path.join(temp, process.platform === 'win32' ? 'domain-organ-smoke.exe' : 'domain-organ-smoke'); + try { + const build = compileNativeC(compiler, { + cwd: process.cwd(), + includeDirs: ['native'], + sources: ['native/rcl_domain_value.c', 'native/rcl_domain_organ.c', 'native/domain_organ_smoke.c'], + output, + }); + assert.equal(build.status, 0, `${build.stdout}\n${build.stderr}`); + const run = spawnSync(output, [], { encoding: 'utf8' }); + assert.equal(run.status, 0, `${run.stdout}\n${run.stderr}`); + assert.match(run.stdout, /domain-organ-value-smoke: PASS/); + } finally { + rmSync(temp, { recursive: true, force: true }); + } +}); diff --git a/tests/domain-operation-organ.test.mjs b/tests/domain-operation-organ.test.mjs new file mode 100644 index 00000000..a5031409 --- /dev/null +++ b/tests/domain-operation-organ.test.mjs @@ -0,0 +1,48 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { + RCL_DOMAIN_ORGAN_FORMAT, + createDomainOperationOrgan, + createDomainOperationRegistry, + promoteDifferentialToDomainOrganCandidate, + admitNativeVerifiedDomainOrgan, +} from '../src/domain-operation-organ.mjs'; + +test('domain organ registry fails closed below required evidence tier', () => { + const organ = createDomainOperationOrgan({ domain: 'core', operation: 'echo', evidenceTier: 'differential-verified' }); + assert.equal(organ.format, RCL_DOMAIN_ORGAN_FORMAT); + const registry = createDomainOperationRegistry([organ]); + assert.equal(registry.resolve('core', 'echo').root, organ.root); + assert.throws(() => registry.assertInvocable('core', 'echo'), error => error.code === 'RCL_DOMAIN_ORGAN_EVIDENCE_TIER'); + assert.equal(registry.assertInvocable('core', 'echo', 'differential-verified').key, 'core.echo'); +}); + +test('differential evidence promotes only to native-candidate', () => { + const operation = createDomainOperationOrgan({ domain: 'quantity', operation: 'make' }); + const differential = { + capability: 'rbc13_domain_call_reference_salvage', + passed: true, + promotionEligible: true, + root: 'diff-root-1', + }; + const { candidate, report } = promoteDifferentialToDomainOrganCandidate({ operation, differentialReport: differential }); + assert.equal(candidate.evidenceTier, 'native-candidate'); + assert.equal(candidate.canonicalAdmission, false); + assert.equal(candidate.proof.differentialRoot, 'diff-root-1'); + assert.equal(report.nativePromotionRequired, true); +}); + +test('native verified admission requires a real native-promotion receipt', () => { + const { candidate } = promoteDifferentialToDomainOrganCandidate({ + operation: { domain: 'core', operation: 'echo' }, + differentialReport: { passed: true, promotionEligible: true, root: 'diff-root', capability: 'domain.core.echo' }, + }); + assert.throws(() => admitNativeVerifiedDomainOrgan({ candidate, nativePromotionReport: { status: 'native-rejected', root: 'bad' } }), error => error.code === 'RCL_DOMAIN_ORGAN_NATIVE_PROMOTION_REQUIRED'); + const verified = admitNativeVerifiedDomainOrgan({ + candidate, + nativePromotionReport: { status: 'native-verified', verified: true, root: 'native-root', implementationRoot: 'artifact-root' }, + }); + assert.equal(verified.evidenceTier, 'native-verified'); + assert.equal(verified.proof.nativePromotionRoot, 'native-root'); + assert.equal(verified.canonicalAdmission, false); +}); diff --git a/tests/domain-vm-value-bridge-native.test.mjs b/tests/domain-vm-value-bridge-native.test.mjs new file mode 100644 index 00000000..4bfbb21c --- /dev/null +++ b/tests/domain-vm-value-bridge-native.test.mjs @@ -0,0 +1,29 @@ +import assert from 'node:assert/strict'; +import { mkdtempSync, rmSync } from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import test from 'node:test'; +import { compileNativeC, resolveNativeCCompiler } from '../src/native-c-compiler.mjs'; + +const compiler = resolveNativeCCompiler(); + +test('private VM ↔ Domain Value membrane round-trips admitted values and rejects unsupported kinds', { skip: !compiler }, () => { + const temp = mkdtempSync(path.join(os.tmpdir(), 'rcl-domain-vm-value-')); + const output = path.join(temp, 'domain-vm-value-smoke'); + try { + const build = compileNativeC(compiler, { + cwd: process.cwd(), + includeDirs: ['native'], + sources: ['native/rcl_domain_value.c', 'native/domain_vm_value_bridge_smoke.c'], + linkLibraries: process.platform === 'win32' ? ['bcrypt'] : ['crypto', 'm'], + output, + }); + assert.equal(build.status, 0, `${build.stdout}\n${build.stderr}`); + const run = spawnSync(output, [], { encoding: 'utf8' }); + assert.equal(run.status, 0, `${run.stdout}\n${run.stderr}`); + assert.match(run.stdout, /domain-vm-value-bridge-smoke: PASS/); + } finally { + rmSync(temp, { recursive: true, force: true }); + } +}); diff --git a/tests/rbc13-ai-assimilation-threshold.test.mjs b/tests/rbc13-ai-assimilation-threshold.test.mjs new file mode 100644 index 00000000..aef165fb --- /dev/null +++ b/tests/rbc13-ai-assimilation-threshold.test.mjs @@ -0,0 +1,12 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { classifyAiAssimilationLevel, RBC13_AI_ASSIMILATION_LEVELS } from '../src/rbc13-ai-assimilation-threshold.mjs'; + +test('A10 threshold levels are monotonic and require independent evidence for L3/L4', () => { + assert.deepEqual(RBC13_AI_ASSIMILATION_LEVELS, ['L0', 'L1', 'L2', 'L3', 'L4']); + assert.equal(classifyAiAssimilationLevel({ contract: false }), 'L0'); + assert.equal(classifyAiAssimilationLevel({ contract: true, extraction: false }), 'L1'); + assert.equal(classifyAiAssimilationLevel({ contract: true, extraction: true, corpus: true, candidate: true }), 'L2'); + assert.equal(classifyAiAssimilationLevel({ contract: true, extraction: true, corpus: true, candidate: true, differential: true }), 'L3'); + assert.equal(classifyAiAssimilationLevel({ contract: true, extraction: true, corpus: true, candidate: true, differential: true, nativeCandidate: true, promotion: true }), 'L4'); +}); diff --git a/tests/rbc13-canonical-admission-readiness.test.mjs b/tests/rbc13-canonical-admission-readiness.test.mjs new file mode 100644 index 00000000..22e0f664 --- /dev/null +++ b/tests/rbc13-canonical-admission-readiness.test.mjs @@ -0,0 +1,87 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { buildRbc13CanonicalAdmissionReadiness } from '../src/rbc13-canonical-admission-readiness.mjs'; + +const ROOT = 'a'.repeat(64); + +function nativeReport() { + return { + root: ROOT, + gates: { + G5_positiveSemanticEquivalence: true, + G6_negativeSemanticEquivalence: true, + G7_nativeReplayDeterministic: true, + G8_nativeSemanticStateRootEmittedAndVerified: true, + G9_semanticRootParity: true, + G10_allEvidenceRootsRecorded: true, + G12_nativePromotionEvidenceTier: true, + }, + }; +} + +function input(overrides = {}) { + return { + number: { status: 'VERIFIED', root: ROOT, corpusRoot: ROOT, requirements: { n1: true, n2: true, n3: true, n4: true, n5: true } }, + native: { status: 'native-verified', verified: true, root: ROOT, reportRoots: [ROOT], reports: [nativeReport(), nativeReport(), nativeReport(), nativeReport()], canonicalAdmission: false }, + performance: { status: 'VERIFIED', root: ROOT, hostRoot: ROOT, measures: { allPathsExecuted: true, repeatedSamples: true, varianceRecorded: true, rssProxyRecorded: true } }, + aiGenerate: { status: 'CANDIDATE', gate: 'PASS', root: ROOT, successfulTrials: 1, requiredTrials: 1 }, + universal: { status: 'PASS', universalGrowthEligible: true, root: ROOT }, + legacy: { v1FocusedStatus: 'VERIFIED', fullSuiteStatus: 'VERIFIED' }, + selfhost: { fixedpointStatus: 'VERIFIED', examplesStatus: 'VERIFIED', stage40Status: 'VERIFIED' }, + versionContract: { status: 'VERIFIED', root: ROOT }, + ...overrides, + }; +} + +test('canonical admission readiness is conjunctive and remains isolated', () => { + const report = buildRbc13CanonicalAdmissionReadiness(input()); + assert.equal(report.verdict, 'VERIFIED'); + assert.equal(report.canonicalReady, true); + assert.equal(report.canonicalAdmission, false); + assert.deepEqual(report.blockingGates, []); +}); + +test('a negative donor result blocks admission even when native evidence passes', () => { + const report = buildRbc13CanonicalAdmissionReadiness(input({ + aiGenerate: { status: 'NEGATIVE_RESULT', gate: 'FAIL', root: ROOT, successfulTrials: 0, requiredTrials: 1 }, + universal: { status: 'FAIL', universalGrowthEligible: false, root: ROOT }, + })); + assert.equal(report.verdict, 'BLOCKED'); + assert.equal(report.canonicalReady, false); + assert.deepEqual(report.blockingGates, ['A10_aiGenerateDonor', 'A12_universalStressAdmissionCell']); +}); + +test('legacy full-suite drift remains a named admission blocker', () => { + const report = buildRbc13CanonicalAdmissionReadiness(input({ legacy: { v1FocusedStatus: 'VERIFIED', fullSuiteStatus: 'BLOCKED' } })); + assert.equal(report.gates.A3_legacyRegressionClosure.passed, false); + assert.equal(report.blockingGates.includes('A3_legacyRegressionClosure'), true); +}); + +test('compatibility ACL2 and experimental WASM parity do not silently grant formal A10', () => { + const report = buildRbc13CanonicalAdmissionReadiness(input({ + aiGenerate: {}, + aiCompatibility: { + status: 'NEGATIVE_RESULT', + root: ROOT, + donor: { root: ROOT }, + corpus: { root: ROOT }, + protocol: { promptRoot: ROOT }, + summary: { humanRepairs: 0, formalA10: false }, + formalA10: { status: 'NEGATIVE_RESULT' }, + strictGrowthAssessment: { globalLevel: 'Level 2 VERIFIED', nextLevel: 'Level 3 CANDIDATE/BLOCKED' }, + }, + wasmGrowthCell: { + status: 'VERIFIED', + universalGrowthEligible: true, + nativeC: { status: 'VERIFIED' }, + wasm: { status: 'VERIFIED' }, + replay: { status: 'VERIFIED' }, + cases: [{ semanticRootParity: true, resultOrErrorParity: true, statusParity: true }], + root: ROOT, + }, + })); + assert.equal(report.gates.A10_aiGenerateDonor.passed, false); + assert.equal(report.gates.A12_universalStressAdmissionCell.passed, true); + assert.equal(report.strictAutonomousGrowth.globalMaximum, 'Level 2 VERIFIED'); + assert.equal(report.strictAutonomousGrowth.nextLevel, 'Level 3 CANDIDATE/BLOCKED'); +}); diff --git a/tests/rbc13-domain-call-bytecode-candidate.test.mjs b/tests/rbc13-domain-call-bytecode-candidate.test.mjs new file mode 100644 index 00000000..61f84f5c --- /dev/null +++ b/tests/rbc13-domain-call-bytecode-candidate.test.mjs @@ -0,0 +1,59 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { compileRealityToBytecode, decodeBytecode } from '../src/bytecode.mjs'; +import { + RBC13_DOMAIN_BYTECODE_VERSION, + RBC13_DOMAIN_CALL_OPCODE, + assembleRbc13DomainCallProgram, + decodeRbc13DomainCallCandidate, +} from '../src/rbc13-domain-bytecode-candidate.mjs'; + +test('canonical compiler remains RBC 1.1/1.2 and never emits candidate opcode 45', () => { + const decoded = decodeBytecode(compileRealityToBytecode('reality Base { facet world.ready : Truth = true }')); + assert.ok(decoded.version.minor <= 2); + assert.equal(decoded.instructions.some(item => item.op === RBC13_DOMAIN_CALL_OPCODE), false); +}); + +test('candidate assembler emits literal DOMAIN_CALL under RBC 1.3', () => { + const decoded = decodeRbc13DomainCallCandidate(assembleRbc13DomainCallProgram({ + calls: [{ domain: 'core', operation: 'echo', args: ['hello'], target: 'result.echo' }], + })); + assert.deepEqual(decoded.version, RBC13_DOMAIN_BYTECODE_VERSION); + const call = decoded.instructions.find(item => item.op === RBC13_DOMAIN_CALL_OPCODE); + assert.equal(call.name, 'DOMAIN_CALL'); + assert.equal(call.flags, 0); + assert.equal(call.domain, 'core'); + assert.equal(call.operation, 'echo'); + assert.equal(call.argc, 1); +}); + +test('candidate assembler emits dynamic DOMAIN_CALL while canonical decoder remains backward compatible', () => { + const bytes = assembleRbc13DomainCallProgram({ + calls: [{ domain: 'core', operation: 'echo', args: [42], target: 'result.dynamic', dynamic: true }], + }); + const generic = decodeBytecode(bytes); + assert.equal(generic.instructions.some(item => item.name === 'UNKNOWN_45'), true); + const decoded = decodeRbc13DomainCallCandidate(bytes); + const call = decoded.instructions.find(item => item.op === RBC13_DOMAIN_CALL_OPCODE); + assert.equal(call.flags, 1); + assert.equal(call.domain, undefined); + assert.equal(call.operation, undefined); + assert.equal(call.argc, 1); +}); + +test('candidate arguments lower recursive sequences and prior-state references without changing DOMAIN_CALL arity', () => { + const decoded = decodeRbc13DomainCallCandidate(assembleRbc13DomainCallProgram({ + calls: [ + { domain: 'quantity', operation: 'make', args: ['Temperature', 25, ''], target: 'q.value' }, + { + domain: 'knowledge', operation: 'claim', target: 'knowledge.temp', + args: ['Temperature', { $state: 'q.value' }, 0.8, ['e1', ['nested', 'e2']], 'lab', 'local', 'provisional', [], 1, 'root-1'], + }, + ], + })); + const domainCalls = decoded.instructions.filter(item => item.op === RBC13_DOMAIN_CALL_OPCODE); + assert.equal(domainCalls.length, 2); + assert.equal(domainCalls[1].argc, 10); + assert.ok(decoded.instructions.some(item => item.name === 'LOAD_STATE')); + assert.ok(decoded.instructions.filter(item => item.name === 'CALL_BUILTIN').length >= 5); +}); diff --git a/tests/rbc13-domain-call-differential.test.mjs b/tests/rbc13-domain-call-differential.test.mjs new file mode 100644 index 00000000..13b91a8e --- /dev/null +++ b/tests/rbc13-domain-call-differential.test.mjs @@ -0,0 +1,28 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { + RBC13_DOMAIN_CALL_DIFFERENTIAL_CASES, + runRbc13DomainCallDifferential, +} from '../src/rbc13-domain-call-differential.mjs'; + +test('RBC 1.3 domain-call salvage reaches source-level differential parity without native promotion', async () => { + const result = await runRbc13DomainCallDifferential({ repeats: 2 }); + + assert.equal(RBC13_DOMAIN_CALL_DIFFERENTIAL_CASES.length, 6); + assert.equal(result.status, 'PASS'); + assert.equal(result.migrationParityPassed, true); + assert.equal(result.caseCount, 6); + assert.equal(result.passedCaseCount, 6); + assert.equal(result.controlsPassed, true); + assert.equal(result.coverage.successObserved, true); + assert.equal(result.coverage.errorObserved, true); + assert.equal(result.nativePromotionAllowed, false); + assert.equal(result.genericPromotionEligible, false); + assert.equal(result.evidenceScore, 0.636364); + assert.match(result.differentialRoot, /^[a-f0-9]{64}$/); + + assert.equal(result.report.independence.satisfied, true); + assert.equal(result.report.independence.proofLevel, 'declared-separate-adapters'); + assert.equal(result.report.negativeControls.length, 1); + assert.equal(result.report.negativeControls[0].detected, true); +}); diff --git a/tests/rbc13-domain-call-salvage.test.mjs b/tests/rbc13-domain-call-salvage.test.mjs new file mode 100644 index 00000000..cfb1430e --- /dev/null +++ b/tests/rbc13-domain-call-salvage.test.mjs @@ -0,0 +1,113 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { quantity } from '../src/quantity.mjs'; +import { + RBC13_DOMAIN_CALL_ABI_CANDIDATE, + RBC13_DOMAIN_CALL_OPERATION_INVENTORY, + buildRbc13DomainCallSalvageReport, + invokeRbc13DomainCallReference, +} from '../src/rbc13-domain-call-salvage.mjs'; + +test('RBC 1.3 salvage keeps opcode 45 quarantined from canonical RBC 1.2', () => { + assert.equal(RBC13_DOMAIN_CALL_ABI_CANDIDATE.bytecodeMajor, 1); + assert.equal(RBC13_DOMAIN_CALL_ABI_CANDIDATE.bytecodeMinor, 3); + assert.equal(RBC13_DOMAIN_CALL_ABI_CANDIDATE.opcode, 45); + assert.equal(RBC13_DOMAIN_CALL_ABI_CANDIDATE.opcodeName, 'DOMAIN_CALL'); + assert.equal(RBC13_DOMAIN_CALL_ABI_CANDIDATE.currentCanonicalFeatureVersion, '1.2'); + assert.equal(RBC13_DOMAIN_CALL_ABI_CANDIDATE.canonicalEnabled, false); +}); + +test('legacy operation inventory distinguishes four reference-backed operations from fourteen native-only experiments', () => { + const report = buildRbc13DomainCallSalvageReport(); + assert.equal(RBC13_DOMAIN_CALL_OPERATION_INVENTORY.length, 18); + assert.deepEqual(report.counts, { + totalLegacyOperations: 18, + admittedReferenceOperations: 4, + quarantinedLegacyNativeOnlyOperations: 14, + }); + assert.equal(report.status, 'CANDIDATE'); + assert.equal(report.authority.canonicalBytecodeMutationAllowed, false); + assert.equal(report.authority.nativeFoundationSyntaxClaimAllowed, false); + assert.equal(report.authority.oldBinaryReuseAllowed, false); + assert.match(report.evidenceRoot, /^[a-f0-9]{64}$/); +}); + +test('source-only reference restores core.echo and quantity.make semantics', () => { + assert.equal(invokeRbc13DomainCallReference('core', 'echo', ['hello']), 'hello'); + assert.deepEqual( + invokeRbc13DomainCallReference('quantity', 'make', ['Temperature', 25, '']), + { kind: 'Quantity', type: 'Temperature', value: 25, unit: '°C' }, + ); +}); + +test('source-only reference restores quantitative.measure semantics using the current quantity oracle', () => { + const value = quantity('Temperature', 25); + const uncertainty = quantity('Temperature', 0.5); + const result = invokeRbc13DomainCallReference('quantitative', 'measure', [ + 'Temperature', + value, + uncertainty, + 0.9, + '', + 'ratio', + ['sensor:A'], + 'sensor-A', + ]); + + assert.equal(result.kind, 'Measurement'); + assert.equal(result.baseType, 'Temperature'); + assert.deepEqual(result.value, value); + assert.deepEqual(result.uncertainty, uncertainty); + assert.equal(result.confidence, 0.9); + assert.equal(result.unit, '°C'); + assert.equal(result.scale, 'ratio'); + assert.deepEqual(result.evidence, ['sensor:A']); + assert.equal(result.calibratedBy, 'sensor-A'); +}); + +test('source-only reference restores knowledge.claim semantics using the current knowledge oracle', () => { + const value = quantity('Temperature', 25); + const result = invokeRbc13DomainCallReference('knowledge', 'claim', [ + 'Temperature', + value, + 0.8, + ['sensor:A'], + 'lab', + 'local', + 'provisional', + [], + 1, + 'root-1', + ]); + + assert.equal(result.kind, 'Knowledge'); + assert.equal(result.baseType, 'Temperature'); + assert.deepEqual(result.value, value); + assert.equal(result.confidence, 0.8); + assert.deepEqual(result.evidence, ['sensor:A']); + assert.equal(result.source, 'lab'); + assert.equal(result.scope, 'local'); + assert.equal(result.status, 'provisional'); + assert.equal(result.revision, 1); + assert.equal(result.formedAtRoot, 'root-1'); +}); + +test('legacy native-only operations fail closed instead of inheriting native status', () => { + assert.throws( + () => invokeRbc13DomainCallReference('energy', 'scale', []), + error => error?.code === 'RCL_DOMAIN_CALL_CANDIDATE_UNIMPLEMENTED' + && error?.details?.nearestCurrentBridge === 'energy.balance', + ); + assert.throws( + () => invokeRbc13DomainCallReference('science', 'claim', []), + error => error?.code === 'RCL_DOMAIN_CALL_CANDIDATE_UNIMPLEMENTED' + && error?.details?.nearestCurrentBridge === null, + ); +}); + +test('unknown domain operation is rejected explicitly', () => { + assert.throws( + () => invokeRbc13DomainCallReference('unknown', 'operation', []), + error => error?.code === 'RCL_DOMAIN_OPERATION_MISSING', + ); +}); diff --git a/tests/rbc13-domain-native-promotion.test.mjs b/tests/rbc13-domain-native-promotion.test.mjs new file mode 100644 index 00000000..742b9462 --- /dev/null +++ b/tests/rbc13-domain-native-promotion.test.mjs @@ -0,0 +1,75 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { + promoteAllRbc13DomainOrgans, +} from '../src/rbc13-domain-native-promotion.mjs'; +import { resolveDomainCandidateCompiler } from '../src/rbc13-domain-native-runtime.mjs'; + +const compiler = resolveDomainCandidateCompiler(); + +test('Domain Organ Native Promotion reports infrastructure blocking instead of fabricating verification', { skip: Boolean(compiler) }, async () => { + const suite = await promoteAllRbc13DomainOrgans(); + assert.equal(suite.status, 'native-blocked'); + assert.equal(suite.verified, false); + assert.equal(suite.blocker, 'native-compiler-missing'); + assert.deepEqual(suite.reports, []); +}); + +test('all four admitted organs require operation differential + current-source candidate VM + semantic-root replay', { skip: !compiler, timeout: 180_000 }, async () => { + const suite = await promoteAllRbc13DomainOrgans({ + compiler, + repeats: 2, + nativeRepeats: 2, + timeoutMs: 5_000, + differentialTimeout: 60_000, + runTimeout: 30_000, + buildTimeout: 120_000, + }); + + assert.equal(suite.status, 'native-verified'); + assert.equal(suite.verified, true); + assert.equal(suite.reports.length, 4); + assert.equal(suite.verifiedOrgans.length, 4); + assert.match(suite.hostRoot, /^[a-f0-9]{64}$/); + assert.match(suite.sharedImplementationRoot, /^[a-f0-9]{64}$/); + + for (const report of suite.reports) { + assert.equal(report.status, 'native-verified', `${report.operationKey}: ${report.gaps.join(', ')}`); + assert.equal(report.verified, true); + assert.equal(report.promotionEligible, true); + assert.equal(report.failedCaseCount, 0); + assert.equal(report.verifiedCaseCount, report.caseCount); + assert.equal(report.checks.semanticDifferentialPassed, true); + assert.equal(report.checks.nativeDifferentialPassed, true); + assert.equal(report.checks.currentNativeEquivalent, true); + assert.equal(report.checks.bytecodeDeterministic, true); + assert.equal(report.checks.replayDeterministic, true); + assert.equal(report.checks.nativeRootsVerified, true); + assert.deepEqual(Object.keys(report.gates), [ + 'G1_operationScopedDifferential', + 'G2_experimentalRbc13BytesDeterministic', + 'G3_currentNativeSourceMaterialized', + 'G4_candidateNativeHostBuilt', + 'G5_positiveSemanticEquivalence', + 'G6_negativeSemanticEquivalence', + 'G7_nativeReplayDeterministic', + 'G8_nativeSemanticStateRootEmittedAndVerified', + 'G9_semanticRootParity', + 'G10_allEvidenceRootsRecorded', + 'G11_noCaseSilentlySkipped', + 'G12_nativePromotionEvidenceTier', + ]); + assert.equal(Object.values(report.gates).every(Boolean), true, `${report.operationKey}: ${report.gaps.join(', ')}`); + assert.match(report.nativeVm.sourceRoots['native/rclvm.c'], /^[a-f0-9]{64}$/); + assert.match(report.nativeVm.sourceRoots['native/rclvm.h'], /^[a-f0-9]{64}$/); + assert.equal(report.nativeVm.experimental, true); + assert.equal(report.nativeVm.materializedFromCurrentSource, true); + assert.match(report.root, /^[a-f0-9]{64}$/); + } + + for (const organ of suite.verifiedOrgans) { + assert.equal(organ.evidenceTier, 'native-verified'); + assert.equal(organ.canonicalAdmission, false); + assert.match(organ.proof.nativePromotionRoot, /^[a-f0-9]{64}$/); + } +}); diff --git a/tests/rbc13-domain-operation-differential.test.mjs b/tests/rbc13-domain-operation-differential.test.mjs new file mode 100644 index 00000000..165804ce --- /dev/null +++ b/tests/rbc13-domain-operation-differential.test.mjs @@ -0,0 +1,33 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { + RBC13_ADMITTED_DOMAIN_OPERATION_KEYS, + runAllRbc13DomainOperationDifferentials, +} from '../src/rbc13-domain-operation-differential.mjs'; +import { buildAllRbc13DomainOrganCandidatePlans } from '../src/rbc13-domain-organ-candidate-plan.mjs'; + +test('each admitted DOMAIN_CALL operation clears an independent differential gate', async () => { + const reports = await runAllRbc13DomainOperationDifferentials({ repeats: 2, timeoutMs: 2_000 }); + assert.equal(reports.length, RBC13_ADMITTED_DOMAIN_OPERATION_KEYS.length); + for (const report of reports) { + assert.equal(report.passed, true, report.operationKey); + assert.equal(report.controlsPassed, true, report.operationKey); + assert.equal(report.promotionEligible, true, `${report.operationKey} score=${report.evidenceScore}`); + assert.ok(report.evidenceScore >= 0.8, report.operationKey); + assert.match(report.differentialRoot, /^[a-f0-9]{64}$/); + assert.equal(report.nativeVerificationClaimed, false); + } +}); + +test('operation differential plans bind semantic evidence only to native-candidate organs', async () => { + const plans = await buildAllRbc13DomainOrganCandidatePlans({ repeats: 2, timeoutMs: 2_000 }); + assert.equal(plans.length, 4); + for (const plan of plans) { + assert.equal(plan.differential.passed, true); + assert.equal(plan.candidate.evidenceTier, 'native-candidate'); + assert.equal(plan.candidate.proof.differentialRoot, plan.differential.differentialRoot); + assert.equal(plan.candidate.canonicalAdmission, false); + assert.equal(plan.nativePromotionPending, true); + assert.equal(plan.artifactBindingPending, true); + } +}); diff --git a/tests/rbc13-domain-semantic-error-materializer.test.mjs b/tests/rbc13-domain-semantic-error-materializer.test.mjs new file mode 100644 index 00000000..f06838fb --- /dev/null +++ b/tests/rbc13-domain-semantic-error-materializer.test.mjs @@ -0,0 +1,24 @@ +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import test from 'node:test'; +import { materializeRbc13DomainVmCandidate } from '../scripts/materialize-rbc13-domain-vm-candidate.mjs'; +import { materializeRbc13DomainVmWithPublicApi } from '../scripts/materialize-rbc13-domain-vm-public-api.mjs'; + +test('base candidate VM owns dynamic error codes and forwards organ semantic errors directly', () => { + const source = fs.readFileSync('native/rclvm.c', 'utf8'); + const candidate = materializeRbc13DomainVmCandidate(source); + assert.match(candidate, /char code_storage\[RCL_DOMAIN_ERROR_CODE_MAX\]/); + assert.match(candidate, /RclDomainOrganErrorV1 organ_error/); + assert.match(candidate, /organ_error\.code\[0\] \? organ_error\.code/); + assert.doesNotMatch(candidate, /vm_fail\(vm, "RCL_NATIVE_DOMAIN_ORGAN_FAILURE", organ_error/); + assert.match(candidate, /OP_DOMAIN_CALL = 45/); +}); + +test('public candidate VM inherits semantic errors and adds registration API without a second patch layer', () => { + const source = fs.readFileSync('native/rclvm.c', 'utf8'); + const candidate = materializeRbc13DomainVmWithPublicApi(source); + assert.match(candidate, /rclvm_instance_register_domain_organ/); + assert.match(candidate, /rclvm_instance_set_domain_minimum_tier/); + assert.match(candidate, /char code_storage\[RCL_DOMAIN_ERROR_CODE_MAX\]/); + assert.equal((candidate.match(/RclDomainOrganErrorV1 organ_error/g) ?? []).length, 1); +}); diff --git a/tests/rbc13-domain-universal-stress-probe.test.mjs b/tests/rbc13-domain-universal-stress-probe.test.mjs new file mode 100644 index 00000000..8ce340e7 --- /dev/null +++ b/tests/rbc13-domain-universal-stress-probe.test.mjs @@ -0,0 +1,88 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { buildRbc13DomainUniversalStressCandidateCell } from '../src/rbc13-domain-universal-stress-probe.mjs'; +import { STRESS_STATUS } from '../src/universal-program-stress.mjs'; + +const ROOT = 'a'.repeat(64); + +function syntheticSuite() { + const operationKeys = ['core.echo', 'quantity.make', 'quantitative.measure', 'knowledge.claim']; + return { + status: 'native-verified', + verified: true, + root: ROOT, + hostRoot: ROOT, + reports: operationKeys.map((operationKey) => ({ + operationKey, + status: 'native-verified', + verified: true, + root: ROOT, + caseCount: 1, + verifiedCaseCount: 1, + gates: Object.fromEntries([ + 'G1_operationScopedDifferential', + 'G2_experimentalRbc13BytesDeterministic', + 'G3_currentNativeSourceMaterialized', + 'G4_candidateNativeHostBuilt', + 'G5_positiveSemanticEquivalence', + 'G6_negativeSemanticEquivalence', + 'G7_nativeReplayDeterministic', + 'G8_nativeSemanticStateRootEmittedAndVerified', + 'G9_semanticRootParity', + 'G10_allEvidenceRootsRecorded', + 'G11_noCaseSilentlySkipped', + 'G12_nativePromotionEvidenceTier', + ].map((gate) => [gate, true])), + operationDifferential: { controlsPassed: true }, + })), + }; +} + +test('RBC13 promotion earns a reusable candidate cell but not universal growth credit', () => { + const probe = buildRbc13DomainUniversalStressCandidateCell(syntheticSuite()); + + assert.equal(probe.status, STRESS_STATUS.BLOCKED); + assert.equal(probe.cell.status, STRESS_STATUS.BLOCKED); + assert.equal(probe.cell.coverageMode, 'native-semantic'); + assert.equal(probe.cell.specialCaseAudit.status, STRESS_STATUS.PASS); + assert.equal(probe.universalGrowthEligible, false); + assert.equal(probe.cell.universalGrowthEligible, false); + assert.equal(probe.cell.authoritativeStateMutated, false); + assert.equal(probe.operationKeys.length, 4); + assert.match(probe.root, /^[a-f0-9]{64}$/); +}); + +test('candidate probe refuses an incomplete native promotion suite', () => { + const suite = syntheticSuite(); + suite.reports = suite.reports.slice(0, 3); + assert.throws( + () => buildRbc13DomainUniversalStressCandidateCell(suite), + error => error?.code === 'RCL_RBC13_UNIVERSAL_STRESS_OPERATION_COVERAGE_REQUIRED', + ); +}); + +test('candidate cell records measured performance and fails on a negative AI donor gate', () => { + const probe = buildRbc13DomainUniversalStressCandidateCell(syntheticSuite(), { + performance: { + status: 'VERIFIED', + root: ROOT, + measures: { + allPathsExecuted: true, + repeatedSamples: true, + varianceRecorded: true, + }, + }, + aiGenerate: { + status: 'NEGATIVE_RESULT', + gate: 'FAIL', + root: ROOT, + successfulTrials: 0, + requiredTrials: 1, + }, + }); + + assert.equal(probe.status, STRESS_STATUS.FAIL); + assert.equal(probe.cell.gates.PERFORMANCE.status, STRESS_STATUS.PASS); + assert.equal(probe.cell.gates.AI_GENERATE.status, STRESS_STATUS.FAIL); + assert.equal(probe.universalGrowthEligible, false); +}); diff --git a/tests/rbc13-domain-vm-opcode45-candidate.test.mjs b/tests/rbc13-domain-vm-opcode45-candidate.test.mjs new file mode 100644 index 00000000..36d7009e --- /dev/null +++ b/tests/rbc13-domain-vm-opcode45-candidate.test.mjs @@ -0,0 +1,233 @@ +import assert from 'node:assert/strict'; +import fs, { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import test from 'node:test'; +import { compileRealityToBytecode } from '../src/bytecode.mjs'; +import { assembleRbc13DomainCallProgram } from '../src/rbc13-domain-bytecode-candidate.mjs'; +import { materializeRbc13DomainVmWithPublicApi } from '../scripts/materialize-rbc13-domain-vm-public-api.mjs'; +import { measurement, quantity } from '../src/quantity.mjs'; +import { knowledgeClaim } from '../src/knowledge.mjs'; +import { semanticStateRoot } from '../src/semantic-state-root.mjs'; +import { compileNativeC, resolveNativeCCompiler } from '../src/native-c-compiler.mjs'; + +function parsePayload(run) { + assert.ok(run.stdout.trim(), `${run.stdout}\n${run.stderr}`); + return JSON.parse(run.stdout.trim()); +} + +function oracleError(fn) { + try { + fn(); + assert.fail('oracle was expected to throw'); + } catch (error) { + if (error?.name === 'AssertionError') throw error; + return { + code: String(error?.code ?? error?.name ?? 'Error'), + message: String(error?.message ?? error), + }; + } +} + +function assertNativeMatchesOracleError(run, expected) { + assert.equal(run.status, 2, `${run.stdout}\n${run.stderr}`); + const payload = parsePayload(run); + assert.equal(payload.code, expected.code); + assert.equal(payload.message, expected.message); + return payload; +} + +const compiler = resolveNativeCCompiler(); + +test('experimental RBC 1.3 opcode45 preserves positive and negative semantic identity', { skip: !compiler }, () => { + const temp = mkdtempSync(path.join(os.tmpdir(), 'rcl-rbc13-domain-vm-')); + const generated = path.join(temp, 'rclvm-rbc13-domain-candidate.c'); + const host = path.join(temp, 'domain-vm-opcode45-candidate'); + try { + const currentNative = fs.readFileSync('native/rclvm.c', 'utf8'); + writeFileSync(generated, materializeRbc13DomainVmWithPublicApi(currentNative)); + const build = compileNativeC(compiler, { + cwd: process.cwd(), + includeDirs: ['native', temp], + sources: [ + 'native/rcl_domain_value.c', + 'native/rcl_domain_organ.c', + 'native/rcl_domain_admitted_organs.c', + 'native/domain_vm_opcode45_candidate_host.c', + ], + linkLibraries: process.platform === 'win32' ? ['bcrypt'] : ['crypto', 'm'], + output: host, + }); + assert.equal(build.status, 0, `${build.stdout}\n${build.stderr}`); + + const literalPath = path.join(temp, 'literal.rbc'); + writeFileSync(literalPath, assembleRbc13DomainCallProgram({ + calls: [{ domain: 'core', operation: 'echo', args: ['hello'], target: 'result.echo' }], + })); + + const denied = spawnSync(host, [literalPath], { encoding: 'utf8' }); + assert.equal(denied.status, 2, `${denied.stdout}\n${denied.stderr}`); + const deniedPayload = parsePayload(denied); + assert.equal(deniedPayload.code, 'RCL_DOMAIN_ORGAN_EVIDENCE_TIER'); + assert.equal(deniedPayload.message, 'RCL_DOMAIN_ORGAN_EVIDENCE_TIER: Organ has not reached required evidence tier'); + + const admitted = spawnSync(host, [literalPath, '--candidate-minimum'], { encoding: 'utf8' }); + assert.equal(admitted.status, 0, `${admitted.stdout}\n${admitted.stderr}`); + const admittedPayload = parsePayload(admitted); + assert.equal(admittedPayload.bytecodeVersion, '1.3'); + assert.equal(admittedPayload.state['result.echo'], 'hello'); + assert.equal(admittedPayload.stateRootAlgorithm, 'rcl.semantic-state-root.v1'); + assert.equal(admittedPayload.stateRoot, semanticStateRoot({ 'result.echo': 'hello' })); + + const dynamicPath = path.join(temp, 'dynamic.rbc'); + writeFileSync(dynamicPath, assembleRbc13DomainCallProgram({ + calls: [{ domain: 'core', operation: 'echo', args: [42], target: 'result.dynamic', dynamic: true }], + })); + const dynamic = spawnSync(host, [dynamicPath, '--candidate-minimum'], { encoding: 'utf8' }); + assert.equal(dynamic.status, 0, `${dynamic.stdout}\n${dynamic.stderr}`); + const dynamicPayload = parsePayload(dynamic); + assert.equal(dynamicPayload.state['result.dynamic'], 42); + assert.equal(dynamicPayload.stateRoot, semanticStateRoot({ 'result.dynamic': 42 })); + + const quantityPath = path.join(temp, 'quantity.rbc'); + writeFileSync(quantityPath, assembleRbc13DomainCallProgram({ + calls: [{ domain: 'quantity', operation: 'make', args: ['Temperature', 25, ''], target: 'result.quantity' }], + })); + const quantityRun = spawnSync(host, [quantityPath, '--candidate-minimum'], { encoding: 'utf8' }); + assert.equal(quantityRun.status, 0, `${quantityRun.stdout}\n${quantityRun.stderr}`); + const quantityPayload = parsePayload(quantityRun); + assert.equal(quantityPayload.state['result.quantity'].kind, 'Quantity'); + assert.equal(quantityPayload.state['result.quantity'].type, 'Temperature'); + assert.equal(quantityPayload.state['result.quantity'].value, 25); + assert.equal(quantityPayload.state['result.quantity'].unit, '°C'); + assert.equal(quantityPayload.stateRoot, semanticStateRoot({ 'result.quantity': quantity('Temperature', 25) })); + + const chainPath = path.join(temp, 'chain.rbc'); + writeFileSync(chainPath, assembleRbc13DomainCallProgram({ + program: 'Rbc13DomainChain', + calls: [ + { domain: 'quantity', operation: 'make', args: ['Temperature', 25, ''], target: 'q.value' }, + { domain: 'quantity', operation: 'make', args: ['Temperature', 0.5, ''], target: 'q.uncertainty' }, + { + domain: 'quantitative', operation: 'measure', target: 'measure.temp', + args: ['Temperature', { $state: 'q.value' }, { $state: 'q.uncertainty' }, 0.9, '', 'ratio', ['sensor:e1', 'sensor:e1', 'sensor:e2'], 'sensor-A'], + }, + { + domain: 'knowledge', operation: 'claim', target: 'knowledge.temp', + args: ['Temperature', { $state: 'q.value' }, 0.8, ['e1', 'e1', 'e2'], 'lab', 'local', 'provisional', ['dep:a', 'dep:a', 'dep:b'], 1, 'root-1'], + }, + ], + })); + const chainRun = spawnSync(host, [chainPath, '--candidate-minimum'], { encoding: 'utf8' }); + assert.equal(chainRun.status, 0, `${chainRun.stdout}\n${chainRun.stderr}`); + const chainPayload = parsePayload(chainRun); + const q = quantity('Temperature', 25); + const uncertainty = quantity('Temperature', 0.5); + const measured = measurement('Temperature', q, { + uncertainty, + confidence: 0.9, + scale: 'ratio', + evidence: ['sensor:e1', 'sensor:e1', 'sensor:e2'], + calibratedBy: 'sensor-A', + }); + const known = knowledgeClaim('Temperature', q, { + confidence: 0.8, + evidence: ['e1', 'e1', 'e2'], + source: 'lab', + scope: 'local', + status: 'provisional', + dependencies: ['dep:a', 'dep:a', 'dep:b'], + revision: 1, + formedAtRoot: 'root-1', + }); + const expectedState = { + 'q.value': q, + 'q.uncertainty': uncertainty, + 'measure.temp': measured, + 'knowledge.temp': known, + }; + assert.equal(chainPayload.state['measure.temp'].kind, 'Measurement'); + assert.deepEqual(chainPayload.state['measure.temp'].evidence, ['sensor:e1', 'sensor:e1', 'sensor:e2']); + assert.equal(chainPayload.state['knowledge.temp'].kind, 'Knowledge'); + assert.deepEqual(chainPayload.state['knowledge.temp'].evidence, ['e1', 'e2']); + assert.deepEqual(chainPayload.state['knowledge.temp'].dependencies, ['dep:a', 'dep:b']); + assert.equal(semanticStateRoot(chainPayload.state), semanticStateRoot(expectedState)); + assert.equal(chainPayload.stateRoot, semanticStateRoot(expectedState)); + + const invalidQuantityPath = path.join(temp, 'invalid-quantity.rbc'); + writeFileSync(invalidQuantityPath, assembleRbc13DomainCallProgram({ + calls: [{ domain: 'quantity', operation: 'make', args: ['Warp', 25, ''], target: 'result.quantity' }], + })); + assertNativeMatchesOracleError( + spawnSync(host, [invalidQuantityPath, '--candidate-minimum'], { encoding: 'utf8' }), + oracleError(() => quantity('Warp', 25)), + ); + + const invalidMeasureConfidencePath = path.join(temp, 'invalid-measure-confidence.rbc'); + writeFileSync(invalidMeasureConfidencePath, assembleRbc13DomainCallProgram({ + calls: [{ + domain: 'quantitative', operation: 'measure', target: 'result.measure', + args: ['Number', 42, 0.25, 2, '', 'ratio', [], ''], + }], + })); + assertNativeMatchesOracleError( + spawnSync(host, [invalidMeasureConfidencePath, '--candidate-minimum'], { encoding: 'utf8' }), + oracleError(() => measurement('Number', 42, { uncertainty: 0.25, confidence: 2, scale: 'ratio', evidence: [], calibratedBy: null })), + ); + + const invalidMeasureTypePath = path.join(temp, 'invalid-measure-type.rbc'); + writeFileSync(invalidMeasureTypePath, assembleRbc13DomainCallProgram({ + calls: [{ + domain: 'quantitative', operation: 'measure', target: 'result.measure', + args: ['Number', 'forty-two', 0.25, 0.9, '', 'ratio', [], ''], + }], + })); + assertNativeMatchesOracleError( + spawnSync(host, [invalidMeasureTypePath, '--candidate-minimum'], { encoding: 'utf8' }), + oracleError(() => measurement('Number', 'forty-two', { uncertainty: 0.25, confidence: 0.9, scale: 'ratio', evidence: [], calibratedBy: null })), + ); + + const invalidKnowledgeConfidencePath = path.join(temp, 'invalid-knowledge-confidence.rbc'); + writeFileSync(invalidKnowledgeConfidencePath, assembleRbc13DomainCallProgram({ + calls: [{ + domain: 'knowledge', operation: 'claim', target: 'result.knowledge', + args: ['Number', 42, 2, [], '', 'local', 'provisional', [], 1, ''], + }], + })); + assertNativeMatchesOracleError( + spawnSync(host, [invalidKnowledgeConfidencePath, '--candidate-minimum'], { encoding: 'utf8' }), + oracleError(() => knowledgeClaim('Number', 42, { confidence: 2, evidence: [], source: null, scope: 'local', status: 'provisional', dependencies: [], revision: 1, formedAtRoot: null })), + ); + + const invalidKnowledgeTypePath = path.join(temp, 'invalid-knowledge-type.rbc'); + writeFileSync(invalidKnowledgeTypePath, assembleRbc13DomainCallProgram({ + calls: [{ + domain: 'knowledge', operation: 'claim', target: 'result.knowledge', + args: ['Text', 42, 0.8, [], '', 'local', 'provisional', [], 1, ''], + }], + })); + assertNativeMatchesOracleError( + spawnSync(host, [invalidKnowledgeTypePath, '--candidate-minimum'], { encoding: 'utf8' }), + oracleError(() => knowledgeClaim('Text', 42, { confidence: 0.8, evidence: [], source: null, scope: 'local', status: 'provisional', dependencies: [], revision: 1, formedAtRoot: null })), + ); + + const missingPath = path.join(temp, 'missing.rbc'); + writeFileSync(missingPath, assembleRbc13DomainCallProgram({ + calls: [{ domain: 'core', operation: 'missing', args: ['x'], target: 'result.missing' }], + })); + const missing = spawnSync(host, [missingPath, '--candidate-minimum'], { encoding: 'utf8' }); + assert.equal(missing.status, 2, `${missing.stdout}\n${missing.stderr}`); + const missingPayload = parsePayload(missing); + assert.equal(missingPayload.code, 'RCL_DOMAIN_OPERATION_MISSING'); + assert.equal(missingPayload.message, "RCL_DOMAIN_OPERATION_MISSING: Domain operation 'core.missing' is not present in the RBC 1.3 salvage inventory"); + + const legacyPath = path.join(temp, 'legacy.rbc'); + writeFileSync(legacyPath, compileRealityToBytecode('reality Legacy { facet world.ready : Truth = true }')); + const legacy = spawnSync(host, [legacyPath], { encoding: 'utf8' }); + assert.equal(legacy.status, 0, `${legacy.stdout}\n${legacy.stderr}`); + assert.equal(parsePayload(legacy).state['world.ready'], true); + } finally { + rmSync(temp, { recursive: true, force: true }); + } +}); diff --git a/tests/rbc13-final-blocker-closure-evidence-ledger.test.mjs b/tests/rbc13-final-blocker-closure-evidence-ledger.test.mjs new file mode 100644 index 00000000..3bafa2a2 --- /dev/null +++ b/tests/rbc13-final-blocker-closure-evidence-ledger.test.mjs @@ -0,0 +1,160 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { + buildRbc13FinalBlockerClosureEvidenceLedger, + renderRbc13FinalBlockerClosureEvidenceLedger, + renderRbc13PolybodyParityEvidence, +} from '../src/rbc13-final-blocker-closure-evidence-ledger.mjs'; + +function nativeReport() { + return { + root: 'operation-root', + gates: { + G1_operationDeclared: true, + G2_candidateOrganRegistered: true, + G3_currentSourceMaterialized: true, + G4_nativeProcessExecuted: true, + G5_positiveSemanticEquivalence: true, + G6_negativeSemanticEquivalence: true, + G7_nativeReplayDeterministic: true, + G8_nativeSemanticStateRootEmittedAndVerified: true, + G9_semanticRootParity: true, + G10_allEvidenceRootsRecorded: true, + G11_noProviderFallback: true, + G12_nativePromotionEvidenceTier: true, + }, + }; +} + +function graphCases() { + return Array.from({ length: 7 }, (_, index) => ({ + id: `case-${index}`, + class: 'bounded', + js: { status: 'ok', semanticRoot: `root-${index}` }, + semanticRootParity: true, + resultOrErrorParity: true, + statusParity: true, + })); +} + +function fixture() { + return { + branch: 'research/rbc13-domain-call-salvage-v0.1', + sourceHead: 'source-head', + number: { + status: 'VERIFIED', + root: 'number-root', + corpusRoot: 'number-corpus-root', + caseCount: 11000, + requirements: { + n1UniqueCanonicalEncoding: true, + n2JsCByteParity: true, + n3RoundTrip: true, + n4FiniteAndEdgeCoverage: true, + n5VersionIsolation: true, + }, + }, + native: { + status: 'native-verified', + verified: true, + root: 'native-root', + reportRoots: ['r1', 'r2', 'r3', 'r4'], + canonicalAdmission: false, + reports: [nativeReport(), nativeReport(), nativeReport(), nativeReport()], + }, + performance: { + status: 'VERIFIED', + root: 'performance-root', + measures: { + allPathsExecuted: true, + repeatedSamples: true, + varianceRecorded: true, + rssProxyRecorded: true, + }, + paths: { primitive: [], 'native-organ': [], provider: [] }, + }, + aiCompatibility: { + status: 'NEGATIVE_RESULT', + root: 'compatibility-root', + corpus: { + root: 'corpus-root', + caseCount: 100, + classificationCounts: { positive: 40, negative: 40, boundary: 20 }, + mutationControls: [{ id: 'required' }], + }, + donor: { root: 'donor-root' }, + oracle: { implementation: 'Ajv2020', dependency: 'ajv@8.20.0', sharedCandidateImports: false }, + summary: { + humanRepairs: 0, + automaticRepairs: 0, + bestAcl: 'ACL2', + aclByModel: { medium: 'ACL2' }, + nativePromotionVerifiedModels: [], + assimilationMonotonic: 'NOT_ESTABLISHED', + }, + formalA10: { status: 'NEGATIVE_RESULT', requiresNativePromotion: true }, + strictGrowthAssessment: { globalLevel: 'Level 2 VERIFIED', nextLevel: 'Level 3 CANDIDATE/BLOCKED' }, + }, + wasmGrowthCell: { + status: 'VERIFIED', + root: 'wasm-root', + operationKey: 'wasm-vm::algorithm::graph-traversal', + universalGrowthEligible: true, + canonicalAdmission: false, + nativeC: { status: 'VERIFIED', hostRoot: 'c-host-root' }, + wasm: { status: 'VERIFIED', moduleRoot: 'wasm-module-root' }, + reference: { status: 'VERIFIED' }, + cases: graphCases(), + coverage: {}, + wasmAbi: { failClosed: true, negativeControls: [{ id: 'invalid-pointer', detected: true }] }, + replay: { status: 'VERIFIED' }, + workload: { semantics: 'bounded traversal' }, + universalStress: { status: 'VERIFIED' }, + }, + legacyClosure: { + status: 'VERIFIED', + root: 'legacy-root', + expectedInventoryRoot: 'inventory-root', + summary: { + expectedCaseCount: 6, + verifiedReceiptCount: 6, + missing: [], + duplicate: [], + stale: [], + altered: [], + replayMismatches: [], + rbc11Verified: true, + rbc12Verified: true, + }, + checks: { + expectedInventoryAuthoritative: true, + noMissingCases: true, + noDuplicateReceiptRoots: true, + noStaleReceipts: true, + noAlteredReceipts: true, + replayRootConsistency: true, + rbc11Verified: true, + rbc12Verified: true, + }, + }, + fullSuite: { status: 'VERIFIED', total: 10, pass: 8, fail: 0, skipped: 2, cancelled: 0 }, + versionContract: { status: 'VERIFIED', contracts: {} }, + selfhost: { + fixedpointStatus: 'VERIFIED', + examplesReport: { ok: true, artifactParity: true, eligibleCount: 17, failureCount: 0 }, + stage40Report: { stageStatus: 'STAGE40_VERIFIED', checks: { one: true, two: true } }, + }, + }; +} + +test('final blocker ledger keeps formal A10 negative and A12 polybody parity explicit', () => { + const report = buildRbc13FinalBlockerClosureEvidenceLedger(fixture()); + assert.equal(report.status, 'BLOCKED'); + assert.deepEqual(report.blockingGates, ['A10_aiGenerateDonor']); + assert.equal(report.a10.formalA10.status, 'NEGATIVE_RESULT'); + assert.equal(report.a12.crossBodyParity, true); + assert.equal(report.fullSuite.fail, 0); + assert.match(renderRbc13FinalBlockerClosureEvidenceLedger(report), /A10 Compatibility Surface/); + assert.match(renderRbc13PolybodyParityEvidence(report), /Canonical permission: \*\*false\*\*/); +}); diff --git a/tests/rbc13-json-schema-donor-oracle.test.mjs b/tests/rbc13-json-schema-donor-oracle.test.mjs new file mode 100644 index 00000000..c56ac1e8 --- /dev/null +++ b/tests/rbc13-json-schema-donor-oracle.test.mjs @@ -0,0 +1,78 @@ +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import test from 'node:test'; +import { buildRbc13JsonSchemaDonorCorpus, RBC13_JSON_SCHEMA_MUTATION_CONTROLS } from '../src/rbc13-json-schema-donor-corpus.mjs'; +import { loadRbc13JsonSchemaDonorContract, validateRbc13JsonSchemaDonorContract } from '../src/rbc13-json-schema-contract.mjs'; +import { evaluateRbc13JsonSchemaCandidate } from '../src/rbc13-json-schema-candidate-adapter.mjs'; + +const ROOT = process.cwd(); + +function fixedSchema() { + const contract = loadRbc13JsonSchemaDonorContract(); + return { + $schema: 'https://json-schema.org/draft/2020-12/schema', + $id: 'urn:rcl:rbc13:test-donor', + title: contract.intent.title, + description: contract.intent.description, + type: contract.intent.rootType, + required: contract.intent.requiredFields, + properties: contract.intent.properties, + additionalProperties: contract.intent.additionalProperties, + }; +} + +test('RBC13 donor corpus is deterministic and covers 40/40/20', () => { + const first = buildRbc13JsonSchemaDonorCorpus(); + const second = buildRbc13JsonSchemaDonorCorpus(); + assert.equal(first.caseCount, 100); + assert.deepEqual(first.classificationCounts, { positive: 40, negative: 40, boundary: 20 }); + assert.equal(first.root, second.root); + assert.deepEqual(first.cases, second.cases); +}); + +test('Ajv donor oracle and candidate adapter agree on the full semantic projection', () => { + const schema = fixedSchema(); + assert.equal(validateRbc13JsonSchemaDonorContract(schema).valid, true); + const corpus = buildRbc13JsonSchemaDonorCorpus(); + const run = spawnSync(process.execPath, ['oracle/rbc13-json-schema-donor-oracle.mjs'], { + cwd: ROOT, + input: JSON.stringify({ schema, cases: corpus.cases }), + encoding: 'utf8', + }); + assert.equal(run.status, 0, run.stderr); + const oracle = JSON.parse(run.stdout); + assert.equal(oracle.status, 'OK'); + assert.equal(oracle.oracle, 'ajv@8.20.0'); + const mismatches = corpus.cases.filter((item, index) => { + const candidate = evaluateRbc13JsonSchemaCandidate(schema, item.instance); + const expected = oracle.outputs[index]; + return candidate.valid !== expected.valid || JSON.stringify(candidate.errors) !== JSON.stringify(expected.errors); + }); + assert.equal(mismatches.length, 0); +}); + +test('all five mutation controls are independently detected', () => { + const schema = fixedSchema(); + const corpus = buildRbc13JsonSchemaDonorCorpus(); + const run = spawnSync(process.execPath, ['oracle/rbc13-json-schema-donor-oracle.mjs'], { + cwd: ROOT, + input: JSON.stringify({ schema, cases: corpus.cases }), + encoding: 'utf8', + }); + const oracle = JSON.parse(run.stdout); + const detected = RBC13_JSON_SCHEMA_MUTATION_CONTROLS.map(control => corpus.cases.some((item, index) => { + const candidate = evaluateRbc13JsonSchemaCandidate(schema, item.instance, { mutation: control.id }); + const expected = oracle.outputs[index]; + return candidate.valid !== expected.valid || JSON.stringify(candidate.errors) !== JSON.stringify(expected.errors); + })); + assert.deepEqual(detected, [true, true, true, true, true]); +}); + +test('independent oracle has no RCL candidate/helper imports', () => { + const source = fs.readFileSync(path.join(ROOT, 'oracle', 'rbc13-json-schema-donor-oracle.mjs'), 'utf8'); + assert.match(source, /ajv\/dist\/2020\.js/); + assert.doesNotMatch(source, /rbc13-json-schema-candidate-adapter|src\//); +}); + diff --git a/tests/rbc13-legacy-evidence-closure.test.mjs b/tests/rbc13-legacy-evidence-closure.test.mjs new file mode 100644 index 00000000..f9aaa40b --- /dev/null +++ b/tests/rbc13-legacy-evidence-closure.test.mjs @@ -0,0 +1,65 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { + buildRbc13LegacyEvidenceClosure, + readRbc13LegacyExpectedInventory, + verifyRbc13LegacyEvidenceClosure, +} from '../src/rbc13-legacy-evidence-closure.mjs'; + +test('RBC 1.3 A3 inventory is committed, stable, and closes current RBC 1.1/RBC 1.2 receipts', () => { + const inventory = readRbc13LegacyExpectedInventory(); + assert.equal(inventory.cases.length, 6); + assert.deepEqual(inventory.cases.map(item => item.id), [ + 'rbc11.stage5.encoder', + 'rbc12.foundation.batch-a', + 'rbc12.foundation.meta-batch-b', + 'rbc12.foundation.batch-c', + 'rbc12.foundation.batch-d', + 'rbc12.foundation.batch-e', + ]); + const report = buildRbc13LegacyEvidenceClosure(); + assert.equal(report.status, 'VERIFIED'); + assert.equal(report.summary.expectedCaseCount, 6); + assert.equal(report.summary.verifiedReceiptCount, 6); + assert.equal(report.summary.missing.length, 0); + assert.equal(report.summary.duplicate.length, 0); + assert.equal(report.summary.stale.length, 0); + assert.equal(report.summary.altered.length, 0); + assert.equal(report.summary.replayMismatches.length, 0); + assert.equal(report.checks.rbc11Verified, true); + assert.equal(report.checks.rbc12Verified, true); + for (const record of report.records) { + assert.match(record.sourceRoot, /^[a-f0-9]{64}$/); + assert.match(record.bytecodeRoot, /^[a-f0-9]{64}$/); + assert.match(record.resultRoot, /^[a-f0-9]{64}$/); + assert.match(record.receiptRoot, /^[a-f0-9]{64}$/); + assert.match(record.replayRoot, /^[a-f0-9]{64}$/); + } +}); + +test('A3 verifier rejects duplicate, missing, stale, and altered receipts', () => { + const report = buildRbc13LegacyEvidenceClosure(); + const duplicate = structuredClone(report); + duplicate.records.push(structuredClone(duplicate.records[0])); + const duplicateCheck = verifyRbc13LegacyEvidenceClosure(duplicate); + assert.equal(duplicateCheck.verified, false); + assert.deepEqual(duplicateCheck.duplicateIds, [duplicate.records[0].id]); + + const missing = structuredClone(report); + missing.records = missing.records.slice(1); + const missingCheck = verifyRbc13LegacyEvidenceClosure(missing); + assert.equal(missingCheck.verified, false); + assert.deepEqual(missingCheck.missing, ['rbc11.stage5.encoder']); + + const stale = structuredClone(report); + stale.records[0].receipt.sourceCommit = 'stale-commit'; + const staleCheck = verifyRbc13LegacyEvidenceClosure(stale); + assert.equal(staleCheck.verified, false); + assert.deepEqual(staleCheck.staleReceipts, ['rbc11.stage5.encoder']); + + const altered = structuredClone(report); + altered.records[0].receipt.resultRoot = '0'.repeat(64); + const alteredCheck = verifyRbc13LegacyEvidenceClosure(altered); + assert.equal(alteredCheck.verified, false); + assert.deepEqual(alteredCheck.alteredReceipts, ['rbc11.stage5.encoder']); +}); diff --git a/tests/rbc13-number-encoding-v2.test.mjs b/tests/rbc13-number-encoding-v2.test.mjs new file mode 100644 index 00000000..25dd6403 --- /dev/null +++ b/tests/rbc13-number-encoding-v2.test.mjs @@ -0,0 +1,85 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { + RCL_CANONICAL_NUMBER_ENCODING_V2, + RCLCanonicalNumberV2Error, + canonicalNumberV2, + canonicalNumberV2Bytes, + canonicalNumberV2Record, + decodeCanonicalNumberV2, + numberFromRawBits, +} from '../src/canonical-number-v2.mjs'; +import { + RCL_NATIVE_STATE_ROOT_ALGORITHM_V2, + semanticStateRootV2, + verifySemanticStateRootV2, +} from '../src/semantic-state-root-v2.mjs'; +import { migrateVerifiedV1ReceiptToV2 } from '../src/semantic-state-root-migration-v2.mjs'; +import { + RCL_NATIVE_STATE_ROOT_ALGORITHM, + semanticStateRoot, +} from '../src/semantic-state-root.mjs'; + +test('Number v2 is a fixed-width binary64 token and canonicalizes negative zero', () => { + assert.equal(RCL_CANONICAL_NUMBER_ENCODING_V2, 'rcl.canonical-number.v2'); + assert.equal(canonicalNumberV2(0), '0x0000000000000000'); + assert.equal(canonicalNumberV2(-0), '0x0000000000000000'); + assert.equal(canonicalNumberV2(Number.MAX_VALUE), '0x7fefffffffffffff'); + assert.equal(canonicalNumberV2(Number.MIN_VALUE), '0x0000000000000001'); + assert.equal(canonicalNumberV2(9007199254740991), '0x433fffffffffffff'); + assert.equal(canonicalNumberV2(9007199254740990), '0x433ffffffffffffe'); + assert.equal(canonicalNumberV2Bytes(0.1).length, 8); +}); + +test('Number v2 round-trips edge values and rejects non-finite values', () => { + for (const value of [0, -0, 0.1, -0.1, Number.MIN_VALUE, Number.MAX_VALUE, 1e308, -1e-308]) { + const token = canonicalNumberV2(value); + const decoded = decodeCanonicalNumberV2(token); + if (value === 0) assert.equal(decoded, 0); + else assert.equal(Object.is(decoded, value), true); + } + for (const value of [Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY]) { + assert.throws(() => canonicalNumberV2(value), error => error instanceof RCLCanonicalNumberV2Error && error.code === 'RCL_CANONICAL_NUMBER_V2_NONFINITE'); + } + assert.throws(() => decodeCanonicalNumberV2('0x8000000000000000'), error => error.code === 'RCL_CANONICAL_NUMBER_V2_NEGATIVE_ZERO'); + assert.throws(() => decodeCanonicalNumberV2('0x7ff0000000000000'), error => error.code === 'RCL_CANONICAL_NUMBER_V2_NONFINITE_TOKEN'); +}); + +test('semantic root v2 is independent from v1 and order-stable', () => { + const left = { world: { count: 9007199254740991, ready: true }, name: 'RCL' }; + const right = { name: 'RCL', world: { ready: true, count: 9007199254740991 } }; + assert.equal(semanticStateRootV2(left), semanticStateRootV2(right)); + assert.notEqual(semanticStateRootV2({ value: 9007199254740991 }), semanticStateRootV2({ value: 9007199254740990 })); + assert.equal(semanticStateRootV2({ value: 0 }), semanticStateRootV2({ value: -0 })); + assert.notEqual(semanticStateRootV2(left), semanticStateRoot(left)); + const verified = verifySemanticStateRootV2({ + state: left, + stateRootAlgorithm: RCL_NATIVE_STATE_ROOT_ALGORITHM_V2, + stateRoot: semanticStateRootV2(left), + }, { requireNativeRoot: true }); + assert.equal(verified.stateRootVerified, true); + assert.equal(verified.numberEncoding, RCL_CANONICAL_NUMBER_ENCODING_V2); +}); + +test('verified v1 receipt can be migrated without rewriting the v1 root', () => { + const state = { world: { count: 7, ready: true } }; + const v1Root = semanticStateRoot(state); + const migration = migrateVerifiedV1ReceiptToV2({ + state, + stateRootAlgorithm: RCL_NATIVE_STATE_ROOT_ALGORITHM, + stateRoot: v1Root, + }); + assert.equal(migration.v1Root, v1Root); + assert.equal(migration.v1Verified, true); + assert.match(migration.v2Root, /^[a-f0-9]{64}$/); + assert.equal(migration.v2Verified, false); + assert.equal(migration.canonicalAdmission, false); +}); + +test('Number v2 record exposes raw and normalized bits for audit', () => { + const record = canonicalNumberV2Record(-0); + assert.equal(record.rawBits, '0x8000000000000000'); + assert.equal(record.bits, '0x0000000000000000'); + assert.equal(record.negativeZeroCanonicalized, true); + assert.equal(numberFromRawBits(BigInt(record.rawBits)), -0); +}); diff --git a/tests/rbc13-universal-growth-cell.test.mjs b/tests/rbc13-universal-growth-cell.test.mjs new file mode 100644 index 00000000..02eeaae3 --- /dev/null +++ b/tests/rbc13-universal-growth-cell.test.mjs @@ -0,0 +1,21 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { buildRbc13UniversalGrowthCell } from '../src/rbc13-universal-growth-cell.mjs'; + +test('A12 records a distinct graph traversal candidate and fails closed without wasm-vm support', () => { + const report = buildRbc13UniversalGrowthCell(); + assert.equal(report.workload.id, 'graph-traversal::bounded-reachability'); + assert.equal(report.growthProof.mainSuccessIsExistingFourOperation, false); + assert.equal(report.native.status, 'VERIFIED'); + assert.equal(report.native.result.reachable, true); + assert.equal(report.native.result.unreachable, false); + assert.equal(report.native.replayVerified, true); + assert.equal(report.executionClassification, 'experimental-native-semantic'); + assert.equal(report.status, 'BLOCKED'); + assert.equal(report.universalGrowthEligible, false); + assert.equal(report.blockerClass, 'wasm-vm-runtime-and-abi-unsupported'); + assert.equal(report.wasmVmSupport.host.rclWasmVmAdapterPresent, false); + assert.equal(report.wasmVmSupport.memoryValueAbi.status, 'BLOCKED'); + assert.equal(report.wasmVmSupport.semanticRoot.status, 'BLOCKED'); + assert.equal(report.growthCellReceipt, null); +}); diff --git a/tests/rbc13-wasm-organ-abi.test.mjs b/tests/rbc13-wasm-organ-abi.test.mjs new file mode 100644 index 00000000..77f89c06 --- /dev/null +++ b/tests/rbc13-wasm-organ-abi.test.mjs @@ -0,0 +1,30 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { + buildRbc13WasmGraphGrowthCell, +} from '../src/rbc13-wasm-graph-growth-cell.mjs'; +import { + decodeRbc13WasmValue, + encodeRbc13WasmValue, + wasmAbiNegativeControls, +} from '../src/rbc13-wasm-organ-abi.mjs'; + +test('RBC13 WASM graph growth cell verifies JS/C/WASM parity and replay', () => { + const report = buildRbc13WasmGraphGrowthCell(); + assert.equal(report.status, 'VERIFIED'); + assert.equal(report.nativeC.status, 'VERIFIED'); + assert.equal(report.wasm.status, 'VERIFIED'); + assert.equal(report.replay.status, 'VERIFIED'); + assert.equal(report.replay.crossBodyFirstReplayParity, true); + assert.equal(report.cases.length, 7); + assert.ok(report.cases.every(item => item.semanticRootParity && item.resultOrErrorParity && item.statusParity)); + assert.equal(report.hostAbi.canonicalPermission, false); + assert.equal(report.wasmAbi.failClosed, true); +}); + +test('WASM value membrane round-trips bounded values and rejects ABI negatives', () => { + const encoded = encodeRbc13WasmValue({ text: 'ok', truth: true, nested: [null, 2.5] }); + assert.deepEqual(decodeRbc13WasmValue(encoded).value, { text: 'ok', truth: true, nested: [null, 2.5] }); + assert.deepEqual(wasmAbiNegativeControls().map(item => item.detected), [true, true, true, true, true]); +}); + diff --git a/tests/self-akashic-record-compiler.test.mjs b/tests/self-akashic-record-compiler.test.mjs index fc42f863..86f46a0c 100644 --- a/tests/self-akashic-record-compiler.test.mjs +++ b/tests/self-akashic-record-compiler.test.mjs @@ -39,7 +39,7 @@ test('v0.57 scans RCL repository as finite self-record', () => { assert.ok(scan.counts.docCount >= 1); assert.ok(scan.counts.testCount >= 38); assert.ok(scan.counts.commandCount >= 85); - assert.ok(scan.counts.versionLedgerCount >= 60); + assert.ok(scan.counts.versionLedgerCount >= DEFAULT_SELF_AKASHIC_RECORD_SPEC.thresholds.minVersionLedgerCount); assert.match(scan.package.version, /^0\.94\./); });