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 <bfgr> -k
              <KWD.py> -o <out>` with PYTHONPATH=<pkg>; `node
              <pkg>/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):
              <root>/python/{<G>KeyWordDefinition.py, <stem>.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.
