step.24.txt -- b3ubot U5.3: the SCB validation tier -- [T3:scb_identity] proves the workspace's delivered bundles decompile BYTE-IDENTICALLY through the python and js SCB runtimes, with the runtimes themselves fetched from the plane's own /packages op (client posture end to end); proven hermetically over stub runtimes AND live on a plane-generated bundle. Step: 24 Parent: end_to_end.md §7 P5 U5.3 row ("SCB validation: quartet byte-identity checks where the workspace consumes SCB APIs"); design.md §2.5's T3 description ("SCB byte-identity across the API quartet where relevant"). House precedent: the b3u U2.3 first-run oracle (b3u tests/test_first_run.py -- py+js decompile the delivered .bfgr, byte-identical; cpp/rust integrity-only, its Q-14-E) -- V0 mirrors that scope exactly, on the CLIENT side. UoW: U5.3 -- third P5 UoW; U5.4 (CCS-unit workflows) builds on the now-complete T3 vocabulary; U5.5 is the trinity gate. Date: 2026-07-22 Origin: User directive 2026-07-22: "go with U5.3, step file and its execution" (the (p1) sequence). ## (0) Status EXECUTED same-session. [T3:scb_identity] is the second live T3 check: bundle discovery (the delivered python/+js/ layout), the runtime cache (the plane's OWN scb-api packages, fetched through the port's new package() op -- an additive port extension under the contract's law -- and cached in the data home), py+js decompile through the allowlisted shell, byte-compare with the divergence offset in the FAIL detail. Tool surface grew twice, both named: `node` in the default allowlist (+config mirror) and an engine-only `extra_env` on run_shell (PYTHONPATH for the delivered runtime; still secret-name- filtered, B-4 defense-in-depth). Suite 608 passed + 21 skipped = 629 collected; p5_gates ALL PASS (10/10 live legs -- the new one generates a REAL bundle through the plane and proves the delivered runtimes agree byte-for-byte; hermetic twin 43 + 10 SKIPPED). ## (1) Ground truth probed at drafting (outputs -> named writers) READ b3u tests/test_first_run.py (the whole invocation ground truth): `python -m ccs_scb decompile -k -o ` with PYTHONPATH=; `node /bin/ccs-scb.js decompile ...`; py-vs-js outputs byte-identical on the SAME delivered artifacts; rust/ cpp integrity-only (toolchain-gated, its own Q-14-E). CONFIRMED all four scb-api-*.zip packages BUILT in the b3u sibling's store; node v18 on PATH; GET /packages/{lang} is a FROZEN contract op (api_contract.md v1) -- the runtime fetch needs nothing outside the contract. CONFIRMED tool-surface gaps: `node` absent from the shell allowlist; run_shell had no env extension (scrubbed_env hardcoded) -- the python runtime needs PYTHONPATH. Both changes small, both named (2.1). CONFIRMED the delivered-bundle layout (b3u bundle_matrix): /python/{KeyWordDefinition.py, .sgr.fgr, .bfgr} + js sibling -- discovery can pair one KWD + one .bfgr per half without name arithmetic (cppcc names by INPUT FILE STEM, so KWD prefix != bfgr stem). ## (2) What U5.3 delivers 2.1 **Tool surface, extended and named**: `node` joins DEFAULT_SHELL_ALLOWLIST (+ the config.txt mirror; its own test updated); run_shell/_execute gain `extra_env` -- ENGINE-ONLY additions applied on top of the scrubbed base and STILL filtered through the secret-name pattern (the engine has no business passing a secret either); trail rows record the extra_env KEY NAMES. 2.2 **The port grows package()** (additive under C-52-B's law): CCSService.package(lang) -> zip bytes = the contract's GET /packages/{lang}; B3uDevClient implements (404/unreachable -> CCSUnavailableError with the server's own remedy text); ReplayCCSService takes programmatic `packages=` fixtures (refuse-loudly otherwise). 2.3 **app/ccs/scb.py**: find_bundles() (the delivered layout; single-half trees skipped -- nothing to compare; ambiguous member sets REFUSED, never guessed); ensure_runtime() (fetch- once cache under the data home -- client posture, the sibling repos never on any path, b3u C-14-B inherited); the py_decompile/js_decompile argv+env builders (the b3u first-run invocations verbatim). B-1 stays structural: this module locates files and builds argv -- the DELIVERED runtimes do all SCB work in allowlisted children. 2.4 **[T3:scb_identity] in the VERIFYING pass**: vocabulary + handler branch -- runtimes ensured (CCSError/missing-node/ ambiguity => verdict-less DEFECTS), no bundle => NAMED vacuous pass, decompile failure or byte divergence => FAIL verdict (the detail carries the divergence offset + both lengths); outputs in a data-home scratch removed in a finally (the probe-law analog: the workspace gains nothing). 2.5 **Tests**: tests/test_orchestrator_scb.py (10 hermetic -- discovery incl. ambiguity refusal + single-half skip, the fetch-once cache, builder shapes, and the FULL arc over STUB runtimes: tiny py/js "decompilers" shipped as replay package fixtures copy input->output, so identity holds iff the two halves' .bfgr bytes agree -- green -> RETRO, divergence -> FAIL with the byte offset, vacuous named, scratch-hygiene; js-driving tests gate on node, 0 skips here) + tests/test_orchestrator_scb_live.py (1 live -- a REAL bundle generated through the plane, unpacked into the workspace, the REAL delivered runtimes fetched via /packages, byte-identical: the SCB isomorphism at full client distance). 2.6 **p5_gates.sh extended**: live leg 9 -> 10; hermetic twin 33+9 -> 43+10; summary U5.1-U5.3. 2.7 **Docs**: design §2.5 U5.3 delivered-state note; end_to_end U5.3 done; tests/testing.md re-measured same-commit. EXPLICITLY OUT OF SCOPE (each a named later UoW, never silent): - cpp/rust identity legs (toolchain-gated: cargo build + g++/ libscbcpp first-runs -- the b3u Q-14-E deferral, inherited verbatim; integrity-only there too) -> later - .fgr-vs-.bfgr cross-FORMAT identity (a third axis; no present consumer names it) -> later - grammar-aware workspace arcs (compile/validate/regenerate) -> U5.4 - foreign-workspace operation -> U5.4a - the full trinity AP arc -> U5.5 - runtime-cache versioning/invalidation (stale cache = delete the dir, the named manual remedy -- no version signal exists in the package snapshots to key on) -> later ## (3) Verification gates G1 DISCOVERY: the delivered layout is found (nested roots too); single-half trees are skipped (nothing to compare); ambiguous member sets refuse loudly -- hermetic green. G2 CACHE: the runtime is fetched ONCE through the port and reused (call-counted); lives under the data home, never the workspace; a missing package fixture refuses loudly. G3 THE ARC over stub runtimes: identical halves -> PASS verdict -> RETRO; divergent halves -> FAIL verdict carrying the divergence byte offset -> escalation (max_repairs=0), state stays VERIFYING; no bundle -> NAMED vacuous pass; the workspace gains NOTHING (scratch removed in finally). G4 LIVE (the isomorphism at client distance): a plane-generated meta bundle, the plane's own delivered runtimes, byte- identical decompilations -- green through the full VERIFYING pass on the launcher-booted instance. p5_gates 10/10 live; hermetic twin 43 passed + 10 SKIPPED; B-4 grep clean. G5 TOOL-SURFACE DISCIPLINE: node allowlisted by default + config mirror (its test updated with the step provenance); extra_env engine-only, secret-name-filtered, key-names trailed -- the shell suite green (25/25). G6 NO REGRESSION: full suite 608 passed + 21 skipped = 629 collected, 0 failed (measured with this file in the corpus). G7 Hygiene: porcelain after the WORK commit; testing.md moved with the tests; ledger + design current; gdiff via ../scripts/step_gdiff. ## (4) LOCKs C-24-A b3ubot porcelain clean after each commit. C-24-B CLIENT POSTURE FOR RUNTIMES: the SCB runtimes come from the plane's /packages op through the port -- the sibling repos are NEVER on any path (b3u C-14-B, inherited); a box without a reachable plane cannot run scb_identity and says so (defect), it never silently falls back to local siblings. C-24-C B-1 STAYS STRUCTURAL: app/ccs/scb.py contains no SCB parsing -- it locates files and builds argv; all SCB work happens in the DELIVERED runtimes as allowlisted child processes. C-24-D extra_env IS ENGINE-ONLY: never provider-controlled, always secret-name-filtered, key names in the trail -- an env side door would gut Q-15-B/B-4, so the parameter is documented and tested as the narrow thing it is. C-24-E THE WORKSPACE GAINS NOTHING from an scb_identity run: outputs live in data-home scratch, removed in a finally (the C-51-B probe-law analog, client side). C-24-F standing locks inherited: C-23-B (verdicts repair, defects escalate), C-23-C (the applied/discovered set is engine fact), C-22-D (the contract is the seam -- package() is an op the contract already froze), B-3/B-4. ## (5) Open questions Q-24-A Identity scope (DECIDED: python+js, the interpreted pair -- the b3u first-run precedent verbatim; cpp needs g++/libscbcpp and rust needs cargo, both are the NAMED future with integrity-only status even on the b3u side). "Quartet byte-identity" becomes a four-way check when the toolchain-gated pair lands; V0's two-way check is the honest interim, named here. Q-24-B Target discovery (DECIDED: workspace-wide bundle-layout glob, NOT the applied set -- bundles arrive by generate/unpack, not by provider tool_calls, so the T3:validate applied-set rule does not transfer; the vacuous pass names the none-found case). Q-24-C Divergence-as-verdict (DECIDED: FAIL verdict, not defect -- the artifacts are workspace content; the repair path (regenerate) is a real remedy even though V0 repair providers are unlikely to fix it; escalation after the budget is the honest outcome either way). Q-24-D Runtime acquisition (DECIDED: the plane's /packages op via the port -- already frozen in contract v1, zero new surface; env-pointing at local sibling checkouts was REJECTED as a client-posture violation, C-24-B). ## (6) Acceptance - [x] Tool surface: node + extra_env, tested (2.1, G5). - [x] package() port op + client + replay fixtures (2.2). - [x] app/ccs/scb.py: discovery/cache/builders (2.3, G1, G2). - [x] [T3:scb_identity] handler: arc green/FAIL/vacuous/defect semantics + scratch hygiene (2.4, G3). - [x] 11 new tests (10 hermetic + 1 live) green; p5_gates extended, ALL PASS live 10/10 (2.5, 2.6, G4). - [x] Full suite 608 passed + 21 skipped = 629 collected (G6). - [x] Docs moved in the same commit (2.7, G7). ## (7) Hash backfill WORK commit (everything above + this file): (this commit) GDIFF commit (step.24.gdiff.txt): (HEAD) End of step.