step.22.txt -- b3ubot U5.1: the CCS plane's client side -- the CCSService port (design §2.8), ReplayCCSService (deterministic fixtures, zero network), and B3uDevClient: the real HTTP implementation, written against b3u/docs/api_contract.md v1 and proven LIVE against a launcher-booted local b3u instance. P5 opens; the trinity's b3ubot->b3u leg exists for real. Step: 22 Parent: end_to_end.md §7 P5 U5.1 row verbatim ("CCSService port + ReplayCCSService (deterministic fixtures) + B3uDevClient against the LOCAL b3u instance (login-token auth; B-1 by construction -- no CCS code in this repo)"). design.md §2.8 (the port sketch + d5's client resolution), §2.9 (the trinity: 127.0.0.1 local b3u, the PL runbook), §2.5 (the T3 tier this plane will feed at U5.2). Cross-repo inputs, both landed 2026-07-22: b3u UB.1 (verdict-only validate, b3u step.51) and b3u UB.2 (docs/api_contract.md v1, b3u step.52) -- the client is written against THAT document, never against b3u source (C-22-D). UoW: U5.1 -- first P5 UoW; U5.2 (T3 oracle over validate) consumes this port next; U5.5 is the trinity gate. Date: 2026-07-22 Origin: User directive 2026-07-22: "go with U5.1" (the (p1) sequence -- b3u UB.1 -> UB.2 -> b3ubot U5.1..U5.5). ## (0) Status EXECUTED same-session. app/ccs/ package live (port + replay + client + error family); 31 new pytest oracles (24 hermetic + 7 live); scripts/p5_gates.sh opened the P5 battery and ran ALL PASS on the first invocation -- a real local b3u booted via the sibling's own PL runbook, a throwaway account provisioned, and the 7 live legs green over actual HTTP: the trinity's b3ubot->b3u leg is no longer a diagram. Full suite 584 passed + 18 skipped = 602 collected (the 7 live legs skip honestly on a box with no b3u up; the last +2 are THIS FILE entering the own-corpus parse/round-trip parametrization -- the step file is data to the machinery it documents). One genuine cross-repo finding: the b3u-corpus exact-count tripwire (==50) fired exactly as its own comment designed, because b3u grew to 52 steps during this same (p1) arc -- resolved by retiring the pin for floor+contiguity (see (5) Q-22-F). ## (1) Ground truth probed at drafting (outputs -> named writers) READ design.md §2.8 (364-419): the port sketch names generate/validate/deliver/entitlement; B3uDevClient (HTTP, /login HMAC token, first target 127.0.0.1:8941) and ReplayCCSService (fixtures, zero network) are the two named implementations; B-1 by construction. READ b3u/docs/api_contract.md v1 (b3u step.52): the SEVEN frozen paths (health, login, me, generate, validate, packages list+fetch), the additive-only law, the 200-on-FAIL verdict law, the 401/402/403/400/413/422 taxonomy, bearer-only auth, 86400s TTL + UJ.4 revocation semantics. CONFIRMED contract-v1 reality vs the §2.8 sketch: there is NO separate deliver op -- generate's 200 response IS the delivery (zip + X-B3U-Unit/-Revision/-Spec-Sha256), and b3u's eviction posture keeps no artifact at rest to fetch later -> Q-22-A. CONFIRMED no prior art in this repo: no app/ccs, no HTTP client, no requests/httpx dep (requirements.txt was pytest only), no base-URL config key. Greenfield, with the PAL port (app/pal/adapter.py ABC) as the shape precedent and app/orchestrator/errors.py as the error-family precedent. CONFIRMED the live-leg vehicle: b3u/scripts/b3u_local.sh (the PL runbook design §2.9 names) boots on $B3U_LOCAL_HOME/ $B3U_LOCAL_PORT and its `signup` subcommand provisions + verifies an operator account with a caller-supplied $B3U_PASSWORD -- everything a gate script needs. CONFIRMED the skip-gating idiom to mirror: ClaudeCodeAdapter. is_available() -> (bool, reason) read at module scope by the keyed tests (tests/test_cli.py:31) -- never a bash re-derivation (the m1_oracle.sh rule). ## (2) What U5.1 delivers 2.1 **app/ccs/service.py** -- the CCSService port (ABC, the app/pal mold): health() / validate() -> Verdict / generate() -> Artifacts / entitlement(). Frozen dataclasses Verdict{unit, passed, diagnostics} and Artifacts{unit, revision, spec_sha256, zip_bytes}. The Q-22-A amendment recorded in its docstring. 2.2 **app/ccs/errors.py** -- the error family under one CCSError base: CCSUnavailableError, CCSAuthError, CCSEntitlementError (carries status+reason -- the 402/403 split), CCSRequestError (400/413), CCSGenerateRejected (422 + diagnostics), CCSFixtureError (replay's refuse-loudly). 2.3 **app/ccs/replay.py** -- ReplayCCSService: pure lookups keyed by sha256(spec bytes) (content-determined, like the oracle it replays); from_dir() loads the JSON half of a fixture set; missing fixture = CCSFixtureError naming key + next action (Q-22-E). Shipped fixture set at tests/fixtures/ccs/ (verdicts.json GOOD+BAD, entitlement.json + health.json modeled on the real local-mode bodies) -- U5.2's T3 tier eats these. 2.4 **app/ccs/client.py** -- B3uDevClient: requests-based, written against contract v1 only. Lazy /login; ONE transparent re-login on 401 (token expiry is contract behavior, not a caller concern); the shared error mapping; base URL resolution $B3U_BASE_URL > config.txt b3u_base_url > loopback default; credentials $B3U_EMAIL/$B3U_PASSWORD env-ONLY (B-4), repr() redacted; is_available() -> (ok, reason) for honest gating. 2.5 **Config + deps**: app/config/config.txt gains `b3u_base_url = "http://127.0.0.1:8941"` (the §2.8 first target; env overrides); requirements.txt gains requests==2.34.2 (the contract's own named client shape -- Q-22-B). 2.6 **Tests** (31 new): tests/test_ccs_port.py (13 -- ABC enforcement, value types, error taxonomy, URL precedence, B-4 redaction), tests/test_ccs_replay.py (11 -- shipped set, determinism, content-keying, refuse-loudly, programmatic artifacts), tests/test_ccs_client_live.py (7 -- env-gated module skipif via is_available(): health, PASS verdict on meta.sgr, FAIL-is-a-verdict + scrub, generate zip + identity headers, /me local row, wrong-password CCSAuthError, stale- token transparent refresh). 2.7 **scripts/p5_gates.sh** -- the P5 battery, opened here: sibling+cppcc presence -> launcher-booted local b3u (scratch home, port 8951) -> runbook signup provisioning -> the 7 live pytest legs -> the hermetic twin (proves the same file SKIPS honestly keyless) -> B-4 grep (the generated password in NO gate artifact). 2.8 **Docs**: design §2.8 realized addendum (the port as implemented + Q-22-A); end_to_end §11 U5.1 row done; tests/testing.md same-commit update (3 new sections, corpus-pin retirement noted, phase overview + totals re-measured). EXPLICITLY OUT OF SCOPE (each a named later UoW, never silent): - wiring Verdict into VerificationRecord / the repair loop's oracle_feedback (the T3 tier) -> U5.2 - SCB quartet byte-identity checks -> U5.3 - grammar-aware workspace arcs (compile/validate/regenerate) -> U5.4 - driving the port from a foreign workspace -> U5.4a - the full trinity AP arc, mock AND live -> U5.5 - hosted-b3u.dev auth (API tokens/scopes -- b3u UB.3, d15) -> later - any CLI surface for the CCS plane (no consumer yet; the orchestrator wires it at U5.2 where the consumer exists) -> U5.2 ## (3) Verification gates G1 Port + values + errors: ABC refuses incomplete implementations at instantiation; Verdict/Artifacts frozen; every error subclasses CCSError -- hermetic pytest green. G2 Replay determinism: same spec bytes -> identical Verdict, content-keyed not unit-keyed; every missing fixture refuses loudly with the key named; the SHIPPED fixture set loads and answers -- hermetic pytest green, zero network. G3 LIVE (the U5.1 done-when): against a launcher-booted local b3u -- health ok; validate PASS on meta.sgr with empty diagnostics; validate FAIL is a VERDICT (never an exception), diagnostics non-empty and path-scrubbed; generate delivers a real zip + the three identity headers; /me shows the local entitlement row; wrong password -> CCSAuthError; a stale token survives via ONE transparent re-login. 7/7. G4 HONEST SKIPS: the same live file, run keyless, reports 7 SKIPPED (never a fake pass) -- proven inside the battery as its own leg. G5 B-4: the generated password appears in NO gate artifact (grep leg); repr(client) never carries credentials (pytest). G6 NO REGRESSION: full suite 584 passed + 18 skipped = 602 collected, 0 failed (measured AFTER this file joined the own-corpus parametrization) -- including the two corpus-pin tests REPAIRED per Q-22-F (the only prior-test delta, a fired tripwire, not a regression). G7 Hygiene: porcelain after the WORK commit; testing.md moved in the same commit as the tests; ledger + design current; gdiff via ../scripts/step_gdiff. ## (4) LOCKs C-22-A b3ubot porcelain clean after each commit. C-22-B B-1 BY CONSTRUCTION, kept visible: app/ccs moves bytes over HTTP and nothing else -- no cppcc invocation, no CCS algorithm, no SCB parsing lands in this package, ever. C-22-C B-4 GENERALIZED TO THE CCS PLANE: b3u credentials are environment-only ($B3U_EMAIL/$B3U_PASSWORD) -- never a config key, never committed, never logged, never in repr(), never echoed into gate output (the p5_gates grep leg is the falsifier). C-22-D THE CONTRACT IS THE SEAM: B3uDevClient is written against b3u/docs/api_contract.md v1 exclusively -- never imports b3u code, never touches an unfrozen operation (signup/verify provisioning lives in the GATE script via the sibling's own runbook, not in the client). C-22-E LOOPBACK POSTURE (B-2): every committed default and every gate target is 127.0.0.1; pointing the client elsewhere is a deliberate operator act via $B3U_BASE_URL. C-22-F standing locks inherited: B-3 (local-only, no push), B-5 (untouched -- the CCS plane is not an AI provider; no workspace bytes leave except to the loopback b3u), B-6. ## (5) Open questions Q-22-A deliver() vs contract v1 (DECIDED HERE, recorded as a design amendment in §2.8): the sketch's deliver(unit) cannot exist against v1 -- generate's response IS the delivery and nothing stays at rest server-side. DECIDED: generate() returns the Artifacts handle; health() (the liveness op a trinity client genuinely needs) takes the fourth port slot; a server-side deliver op, if ever wanted, is ADDITIVE under the contract's own law. Q-22-B HTTP dependency (DECIDED: requests==2.34.2). The b3u UB.2 card itself names "requests-based -- the b3ubot B3uDevClient's shape"; the sibling's contract client uses the same library at the same pin. stdlib urllib rejected: multipart uploads by hand are exactly the kind of code that grows bugs the contract can't see. Q-22-C Config vs env split (DECIDED): base URL = config key with env override (not a secret; per-deployment); credentials = env ONLY (B-4). Never the reverse. Q-22-D Live-leg vehicle (DECIDED): a NEW scripts/p5_gates.sh booting the SIBLING'S OWN runbook (b3u_local.sh) rather than a hand-rolled uvicorn line -- the trinity design names the PL runbook as the deployment, so the gate proves THAT path; port 8951 avoids the real instance (8941) and both siblings' batteries. Q-22-E Replay missing-fixture behavior (DECIDED: refuse loudly, CCSFixtureError naming the sha + the next action). A synthesized default verdict would be a fake oracle -- the one thing an oracle plane must never produce. Q-22-F The fired corpus tripwire (DECIDED HERE): the ==50 pin in test_ledger_parser/test_ledger_roundtrip fired because b3u grew to 52 steps in this same arc -- exactly the re-survey trigger its comment promised. DECIDED: retire the exact pin for floor (>=50) + contiguity (1..N) -- b3u is a LIVE sibling now, gaining a file per step BY DESIGN; the per-file parametrized round-trips (which immediately covered step.51/52.txt, byte-identical, with zero parser changes) keep the oracle's teeth. The roundtrip guard renamed accordingly (..._covers_the_whole_corpus). ## (6) Acceptance - [x] app/ccs package: port + errors + replay + client (2.1-2.4); config key + requests dep (2.5). - [x] 31 new pytest oracles green (2.6): 24 hermetic + 7 live; G1/G2 hermetic, G3 live 7/7. - [x] scripts/p5_gates.sh ALL PASS first invocation (2.7): boot, provision, live legs, honest-skip twin (G4), B-4 grep (G5). - [x] Full suite 584 passed + 18 skipped = 602 collected, 0 failed (G6); corpus tripwire resolved per Q-22-F. - [x] Docs moved in the same commit (2.8): design §2.8 addendum, end_to_end U5.1 done, testing.md re-measured (G7). ## (7) Hash backfill WORK commit (everything above + this file): (this commit) GDIFF commit (step.22.gdiff.txt): (HEAD) End of step.