step.1.txt -- b3ubot U0.4: dev environment + smoke check (P0.M1) Step: 1 -- promote UoW **U0.4** (end_to_end.md §2, P0.M1) to execution depth: a reproducible local dev environment plus a smoke script proving the two external seams this whole engine is built on top of both exist and are reachable -- WITHOUT b3ubot's own code ever touching the CCS compiler itself. Parent: end_to_end.md §2 P0.M1 ("Dev environment + smoke check (uv venv; cppcc sibling probe; mock-provider hello)"; exit = "a fresh checkout builds its venv and passes a smoke script that proves the two external seams exist") + design.md §1.3 (what already exists to build on) + §2.8/B-1 (b3ubot contains no CCS code -- the boundary this step's cppcc probe must respect from its very first line). UoW: U0.4 -- "Dev environment + smoke check". Depends on U0.1-U0.3 (done, founding commit 03ba794 + follow-ons). Sole blocker of U1.1 (the PAL contract + real mock adapter). Date: 2026-07-18 (SKELETON -- not executed. Execute on explicit request.) Origin: User directive 2026-07-18: "create the first step file... based on the current project status" -- the operator's decision to start b3ubot now, in parallel with b3u.dev's remaining U6.5 (attorney EULA + Stripe live-mode), since that remaining b3u work is largely independent of b3ubot's own early development. ## (0) Status SKELETON. Nothing built. This step is deliberately small (~half day), matching the b3u sibling's OWN U0.4 (step.2) in spirit and size: it is the boundary between planning artifacts and running code, and its ONLY job is to de-risk the two integration points this engine depends on -- a buildable cppcc sibling, and a zero-network mock AI exchange -- in a short script instead of mid-way through U1.1's real PAL contract work. ## (1) What U0.4 proves (and what it explicitly does NOT) PROVES: - A fresh checkout of b3ubot can be brought to a working dev environment by following README instructions alone (uv venv + pinned deps -- the README already names uv as a prerequisite). - The cppcc sibling resolves from the b3ubot context (env var, with a sane sibling-layout fallback) and is a REAL, executable binary -- proven by invoking it, not just checking the file exists on disk -- while NEVER running the CCS compiler pipeline itself (see Q-1-A; this is the load-bearing distinction from b3u's own smoke check, which DOES compile a trusted grammar -- b3ubot may never do that, by design, from its very first step). - A scripted, hardcoded "mock provider" exchange -- zero network, zero keys -- runs end to end inside the venv, proving the Python stack + package-import machinery work before U1.1 builds the real PAL contract on top of it. EXPLICITLY OUT OF SCOPE (each belongs to a later UoW): - The real PAL envelope / adapter interface (capabilities/run/abort) -> U1.1 - The real mock adapter (fixture-scripted, contract-tested) -> U1.1 - Any provider that touches the network (claude-code, openai) -> U1.2 - The ledger engine, orchestrator, tool surface, oracle harness -> P2/P3 - The CCSService port / B3uDevClient / actually running b3u locally -> U5.1 - The workflow DAG substrate build (dag/'s own makealls) -> U3.9 - The data-model pipeline (B3UBOT.sgr etc.) -> P4 - Egress policy / sandboxing / any hardening posture -> P7 The smoke script proves PLUMBING, not capability. It is the same discipline b3u's step.2 named for itself: "a plumbing proof, not a validation harness." ## (2) Deliverables b3ubot/ requirements.txt -- pinned Python deps (§3) Makefile -- venv / smoke / test / clean targets (§5) scripts/ smoke.sh -- the U0.4 oracle (§4); bash, executable adapters/ mock/ hello.py -- the throwaway mock-provider stub (§4.2; Q-1-D: lives at the FUTURE adapters/mock/ path so U1.1 replaces file contents in place rather than relocating a directory) README.md -- + a filled-in "Getting started" section (§6; the file already sketches this, this step makes it literally replayable) (work/ stays gitignored scratch; smoke writes under work/smoke/) ## (3) requirements.txt -- minimal, per Q-1-C Unlike b3u's own U0.4 (which pre-staged fastapi/uvicorn because b3u's U1.1 WAS the web layer), b3ubot's bot surface is CLI-first (design.md d2, LEANING) and U1.1 is the PAL contract, not a web server -- there is nothing web-shaped to pre-stage yet. Pin exactly: pytest -- test runner (referenced by `make test` from day one, vacuous until U1.x adds real tests) Pin the exact resolved version (`uv pip freeze` after a fresh resolve) so a fresh checkout reproduces THIS environment, not whatever PyPI serves later. Python floor: the system python3 (3.12, per the b3u sibling's own uv-managed convention) -- uv itself handles interpreter management, no pyenv machinery. The smoke script's cppcc-probe half must depend on NONE of these -- bash + coreutils + the cppcc binary only, exactly like b3u's C-2-C precedent. The mock-provider half NECESSARILY depends on the venv (this is new relative to b3u's smoke.sh, which stayed fully venv-independent) -- that half of the script requires `make venv` to have already run, and says so plainly if it hasn't. ## (4) scripts/smoke.sh -- specification ### 4.1 cppcc sibling probe (bash + coreutils only, no venv needed) Resolution rule (mirrors b3u step.2 §4 verbatim, one repo-name swap): CPPCCHOME := $CPPCCHOME if set, else /../cppcc (the sibling layout) then REQUIRE $CPPCCHOME/bin/cppcc to exist and be executable; on failure: print a one-line diagnosis naming the missing path and the fix ("export CPPCCHOME=... / build via $CPPCCHOME/build/makeall"), exit 1. No stack traces, no silent fallthrough. Execution proof, NOT a compile (Q-1-A): run `$CPPCCHOME/bin/cppcc -h` and require exit 0 and non-empty stdout. Per cppcc's own CLAUDE.md, `-h` "short-circuits before the compiler/logger machinery starts" -- this proves the binary actually RUNS (catches a wrong-architecture or missing-shared-library binary that a bare file-existence check would miss) WITHOUT b3ubot's process ever invoking the CCS compiler pipeline on any grammar, trusted or otherwise. b3ubot NEVER escalates this probe to an actual compile -- that is b3u's own smoke test's job (already done, b3u step.2), and stays there. ### 4.2 mock-provider hello (needs the venv) `adapters/mock/hello.py` exports one function, `hello() -> str`, that returns a hardcoded canned string from an in-file fixture dict keyed by a literal request string -- no envelope, no dataclasses, no resemblance yet to the real PAL contract (Q-1-B: deliberately throwaway). `scripts/smoke.sh` invokes it via `.venv/bin/python -c "from adapters.mock.hello import hello; print(hello())"` (or an equivalent one-liner) and requires the exact expected string back, byte-for-byte -- zero network calls, zero environment variables consulted, zero keys anywhere near this code path (foreshadowing B-4, though there is no credential of any kind yet to protect). ### 4.3 Overall procedure 1. cppcc probe (§4.1); on failure, exit 1 immediately -- do not attempt the mock-provider half (fail fast, one diagnosis at a time, matching b3u's own smoke.sh discipline). 2. mock-provider hello (§4.2); require the venv exists first (`.venv/bin/python` present) -- if not, print "run `make venv` first" and exit 1, not a Python traceback. 3. print "SMOKE PASS: cppcc=, mock-provider="; exit 0. Any failure: print the failing stage name, exit 1. Nothing under work/ is needed for this step (unlike b3u's step.2, which copied a grammar into a scratch dir to compile it) -- there is no compile happening here at all. ## (5) Makefile targets make venv -- uv venv .venv && uv pip install -r requirements.txt (uv from the start -- the b3u sibling's own step.2 retro found plain venv insufficient and switched to uv; the b3ubot README already names uv as a prerequisite, so there is no reason to repeat that discovery here) make smoke -- bash scripts/smoke.sh make test -- .venv/bin/python -m pytest (vacuous until U1.x adds tests; must exit 0 on an empty suite, document the flag used) make clean -- rm -rf work/ .venv/ ## (6) README "Getting started" (fresh-checkout contract) The README already sketches prerequisites (§"Getting started" / §"Prerequisites"); this step makes the lines LITERALLY replayable and adds the missing execution step: git clone # local-only repo; no remote exists (B-3) cd b3ubot make venv make smoke # expects: SMOKE PASS The G4 gate executes these lines LITERALLY -- if the README needs an unwritten step, the gate fails. ## (7) Procedure (when executed) 1. requirements.txt (§3) -- resolve fresh via uv, pin the exact version. 2. adapters/mock/hello.py (§4.2). 3. scripts/smoke.sh (§4), executable bit set. 4. Makefile (§5). 5. README replay-checked (§6). 6. Run gates G1-G6 (§8), including the G3 negative test. 7. Retro pair per the b3u dual-format rule (code-changing step): narrative steps/step.1.diff.txt + automated cdiff/gdiff (Q-1-E: whichever retro-diff tool the operator wants b3ubot to use -- flagged, not pre-decided, since b3ubot has no scripts/ sibling of its own yet and shouldn't assume it inherits cppcc's scripts/step_gdiff without being told to). 8. Same commit as the retro: flip end_to_end.md §11 ledger row U0.4 -> done / step.1 / ; backfill §12 hashes here. ## (8) Verification gates G1 Fresh env: rm -rf .venv && make venv succeeds from requirements.txt alone (pinned resolve, no network surprises noted). G2 make smoke exits 0; output names the cppcc probe result AND the mock-provider's returned string. G3 NEGATIVE: CPPCCHOME=/nonexistent make smoke exits 1 with the §4.1 one-line diagnosis (no traceback, no partial state mistakable for success); a SECOND negative -- make smoke run before make venv (no .venv/) -- exits 1 with the §4.3 step-2 diagnosis, not a Python traceback. G4 README replay: following §6's lines verbatim on a pristine tree (fresh clone or `git stash -u` equivalent) reproduces G1+G2. G5 Hygiene: b3ubot porcelain clean after commit; work/ + .venv/ untracked; the cppcc sibling repo byte-untouched (its porcelain clean before == after -- nothing under $CPPCCHOME is ever written). G6 B-1/B-3 DISCIPLINE (b3ubot-specific, no b3u equivalent): grep scripts/smoke.sh + adapters/mock/hello.py for any invocation of cppcc other than `-h` -- must be EXACTLY ONE cppcc invocation in the whole script, and it must be the `-h` form. This is the regression guard that keeps the very first line of b3ubot code honest about B-1 ("cppcc never leaves the server / b3ubot contains no CCS code") before there is enough code written for the violation to hide inside. ## (9) LOCKs C-1-A b3ubot porcelain clean after each commit. C-1-B THE CPPCC PROBE NEVER COMPILES: scripts/smoke.sh's cppcc check is exactly one `cppcc -h` invocation -- no grammar is ever passed to the binary from b3ubot's own process, now or in any later step that touches this file (B-1's first concrete enforcement point, G6 is its regression guard). C-1-C the cppcc sibling is READ-ONLY for this step: binary invoked with `-h` only; nothing under $CPPCCHOME written, ever (the b3u step.2 C-2-B precedent, narrowed further by C-1-B above). C-1-D the mock-provider hello touches NO network and consults NO environment variable or credential of any kind (B-4's spirit, ahead of there being any real credential to protect). C-1-E standing locks inherited: B-1 (cppcc/CCS runtime never leaves the server -- this step's whole design, C-1-B/C-1-C above are its concrete form here), B-2 (no public exposure -- moot at this step, nothing is exposed), B-3 (local-only, no disclosure, no push). ## (10) Open questions Q-1-A cppcc probe depth: bare existence + executable-bit check (`[ -x ... ]`) vs actually invoking `cppcc -h` for a REAL execution proof (LEAN -- existence alone doesn't prove the binary actually runs, e.g. a stale/wrong-architecture binary; `-h` is documented to short-circuit before any compiler/logger machinery starts, so it is a safe, real proof) vs invoking cppcc on a trusted grammar the way b3u's OWN step.2 does (REJECTED OUTRIGHT -- b3ubot must never run the CCS compiler pipeline from its own process, by design, B-1/§2.8; that proof belongs to b3u's smoke test, already done, and to b3ubot's later CCSService client work at U5.1, which goes through the API, never a direct binary call). LEAN: `-h` invocation only. Q-1-B mock-provider "hello" shape: the absolute minimal hardcoded stub, zero resemblance to the eventual PAL envelope (LEAN -- U1.1 owns the real contract; building even a rough envelope now risks this throwaway stub becoming accidentally load-bearing, forcing U1.1 to work around it instead of replacing it cleanly) vs a rough draft of the eventual request/response shape even if trivial (more "useful" today, more to unwind later). LEAN: minimal hardcoded stub, explicitly commented as throwaway, U1.1 replaces the file's CONTENTS wholesale. Q-1-C requirements.txt scope: pytest only (LEAN -- matches d2's CLI-first lean and the fact that U1.1 is the PAL contract, not a web layer; no reason yet to guess what U1.1-U1.3 will need) vs pre-staging likely-future deps the way b3u did for its own (web-shaped) U1.1. LEAN: pytest only; each later step adds exactly what it needs, when it needs it. Q-1-D Mock-provider stub location: `adapters/mock/hello.py`, the FUTURE real location per design.md §2.12's layout (LEAN -- zero cost today, saves U1.1 a directory move, and the throwaway-ness lives in the FILE CONTENTS being trivial, not in the path being temporary) vs a standalone `scripts/`-local stub kept deliberately separate from the future adapters/ tree. LEAN: adapters/mock/hello.py now. Q-1-E Retro-diff tooling: b3u's dual-format retro convention (`.diff.txt` narrative + an automated diff) is well- established there, but b3ubot has no `scripts/step_gdiff` (or its retired predecessor `scripts/claude_diff`) of its own yet, and it is a SEPARATE repo -- inheriting cppcc's sibling script by relative path the way b3u does, vs writing b3ubot's OWN minimal version, vs skipping the automated half for this one small step and doing the narrative retro only. NO LEAN recorded -- flagged for the operator to decide at execution time, since it is a tooling-ownership question outside this step's own small scope, not a design question this skeleton should pre-answer. ## (11) Acceptance - [x] requirements.txt with pinned pytest version (§3). - [x] adapters/mock/hello.py implementing §4.2 exactly (hardcoded, throwaway, no network/env/credential touched). - [x] scripts/smoke.sh implementing §4 exactly (cppcc `-h` probe only, mock-provider hello, fail-fast one-diagnosis-at-a-time). - [x] Makefile targets venv/smoke/test/clean (§5, uv-based from the start; test target carries the pytest-exit-5 fix, same one b3u's own Makefile needed). - [x] README "Getting started" filled in + replayable (§6). - [x] G1-G6 pass; both G3 negative branches demonstrated; G6's single-`-h`-invocation grep demonstrated (see step.1.diff.txt). - [x] Retro pair landed: Q-1-E resolved by reading scripts/step_gdiff itself (fully portable, no b3u-specific assumptions) -- reused as-is rather than a b3ubot-local copy. - [x] end_to_end.md §11: U0.4 -> done / step.1 / (this commit), same commit as the retro. - [x] §12 hashes backfilled. ## (12) Hash backfill SKELETON commit (this file + ledger row U0.4 -> active): 3e6b580 BUILD commit (env + smoke + mock stub + README): 4dd2b4b RETRO commit (retro pair + ledger done): (HEAD) End of skeleton.