step.18.txt -- b3ubot U3.5: EXECUTING loop -- scheduling from the
               execution DAG's ready frontier; tool acting + evidence
               recorded into the step file AND node kv state.
               design.md §4's "Everything between the gates is
               machine: context assembly, provider runs, tool acting,
               oracle invocation, evidence recording" and §2.13's
               "during EXECUTING/VERIFYING node kv state updated as
               tasks complete; the DAG IS the live progress surface --
               what may run next = the ready frontier" -- as code.
               Also resolves Q-15-B (child env scrub). Stops here --
               does NOT continue into U3.6 or any later P3 UoW.

Step:     18
Parent:   end_to_end.md §5 U3.5 bullet verbatim ("EXECUTING loop:
          scheduling from the execution DAG's ready frontier; tool
          acting + evidence recorded into the step file AND node kv
          state"). design.md §4 (the RATIFIED->EXECUTING->VERIFYING
          span and the "materialized form is DAG instances"
          paragraph) + §2.13's lifecycle table are what this step
          implements directly; §2.12's "file trees for ... workflow
          DAG instances" governs where instances live.
          app/orchestrator/ (U3.1-U3.4) is the package this grows;
          app/tools/ (U3.2/U3.4) is the surface every node handler
          acts through; dag/dagModelPy + dag/cppcc/bin/dagModel are
          the substrate the instances are written to and verified
          against.
UoW:      U3.5
Date:     2026-07-21
Origin:   Operator directive 2026-07-21 (same dispatch shape as
          step.14-17's): draft AND execute steps/step.18.txt for
          U3.5 in a single pass (the step.7/9/10/11/14-17
          convention, NOT a skeleton-split), with the SAME explicit
          HARD scope boundary -- stop after U3.5, do not continue
          into U3.6 or any later UoW; the operator directs P3 one
          UoW at a time with strict per-UoW checkpoints.

## (0) Status

EXECUTED in this pass. Not a skeleton-then-later-execute split --
app/orchestrator/executing.py, the ExecuteError/ExecutionHaltedError
additions, the Q-15-B env scrub in app/tools/shell.py, the compiler's
ASCII-description fix (a live finding, see §(1)), the mock execution
fixture, the `b3ubot execute` CLI subcommand,
tests/test_orchestrator_executing.py, the env-scrub tests grown into
tests/test_tools_shell.py, the design.md / end_to_end.md /
docs/tool_surface.md / docs/orchestrator_state_machine.md edits, and
this step file were all built together, per the operator's explicit
"draft AND execute (single pass)" direction. Per the operator's
explicit scope boundary (Origin, above), this session STOPS after
U3.5 -- U3.6 (VERIFYING tiers / verification-kind DAG / d9 repair
budget) is the next UoW but is NOT started here: an executed UoW
lands in VERIFYING and just sits there; nothing runs gates.

## (1) Ground truth probed at drafting (outputs -> named writers)

    READ    design.md §4 in full (the "Everything between the gates
            is machine" list is exactly this step's handler set; "the
            orchestrator schedules from the DAG's ready frontier,
            writes task state into node kv attributes") and §2.13's
            lifecycle table (row 2 is this step's contract verbatim).
            §2.12's storage wording ("file trees for ledgers,
            artifacts, AND workflow DAG instances") drove the
            instance-location decision (2.2).
    READ    app/orchestrator/ in full: state_machine.py (the
            RATIFIED->EXECUTING and EXECUTING->VERIFYING edges both
            exist with rationale strings naming U3.5; Q-17-B's
            RATIFIED->SKELETON stays undriven -- confirmed NOT needed
            here, no state-machine change made); persistence.py (the
            resume primitive this step's failure semantics lean on);
            compiler.py (U3.1's fixed 4-5-node template, `status =
            pending` kvs, `precedes` edges -- consumed as-is, the
            template deliberately NOT enriched); drafting.py (the
            bounded probe reused for context_assembly; the C-4-C
            egress-record-before-validation posture mirrored);
            ratify.py (the §(0) engine-stamp pattern with post-stamp
            re-parse guard, reused for the execution record; the
            legality-first lesson).
    READ    dag/dagModelPy/dag_model/ (entities/loader/renderer):
            from_dag shells out to dag/cppcc/bin/dagModel -- CHECKED,
            not assumed: the binary IS built in this environment and
            from_dag -> to_dag_text round-trips execution_example.dag
            byte-identically (verified live before designing).
            Droppings observed live: a parse leaves
            .bfgr/.fgr/.lis/.urt/.log beside the input. The renderer
            writes latin-1 (its own documented rationale) while
            tools.write_file writes utf-8 -- the encoding seam behind
            this step's compiler finding (below).
    READ    dag/instances/execution_example.dag: the kv evidence
            shape ((status = done), (provider = mock), (approved =
            true)) this step's node facts follow -- and the detail
            that the hand-authored instance deliberately writes
            "design.md section 4", never the section-sign character.
    READ    app/tools/ in full: shell.py (_execute shared by
            run_shell/run_gate -- the one place the Q-15-B scrub
            belongs; exit-as-data already anticipated "the U3.5
            scheduler needs that verdict as data"); workspace.py
            (write_file's C-4-B scope enforcement -- relied on AT the
            apply); trail.py (logged_call -- every handler action
            lands on the trail for free).
    READ    steps/step.15.txt §(5) Q-15-B verbatim (the LEAN said
            "revisit at U3.5 -- that is the moment inherited env
            becomes a provider-reachable surface"; this step is that
            moment, resolved in 2.8) and design.md §2.10's B-4/B-5
            lock table.
    READ    app/cli.py in full (the ask/draft/ratify subcommand row
            `execute` joins; the Q-8-B non-TTY refusal pattern;
            --yes precedents) and adapters/mock/fixtures.py (the
            Q-4-B first-entry routing rule the execution scenario
            must respect).
    CONFIRMED `git log --oneline -30` for the 2-commit phrasing
            (step.17's precedent, 8e14637/1217e79).
    CONFIRMED baseline: full suite 443 passed / 11 skipped before any
            edit (matches step.17's reported final).
    CONFIRMED live smoke before committing: mock draft + ratify --yes
            + `b3ubot execute <ws> UT.1 --yes` in a scratch workspace
            with isolated $B3UBOT_HOME -> instance written beside the
            skeleton, HUMAN GATE diff presented, hello.py applied,
            ENGINE EXECUTION RECORD in §(0) on disk, engine record
            history [INTENT, SKELETON, RATIFIED, EXECUTING,
            VERIFYING], round-trip "verified" through the real
            bin/dagModel, zero droppings left in steps/.

## (2) What U3.5 delivers

  2.1 **app/orchestrator/executing.py** (new; the package's
      per-concern peer of drafting.py/ratify.py):
      `execute_uow(workspace_root, uow, *, adapter, provider,
      output_fn, input_fn, assume_yes=, home=)` -- the full arc:
      state legality FIRST (an EXECUTING-state record RESUMES; any
      other state must pass validate_transition(state, "EXECUTING"),
      so only RATIFIED enters -- zero reads on an illegal move),
      compile via U3.1's compile_execution_dag (the compiler consumed
      AS-IS: the V0 template deliberately not enriched), instance
      written, ready-frontier loop with per-node-kind handlers, node
      kv persistence on every change, §(0) evidence stamp, advance to
      VERIFYING. Plus the generic engine (run_ready_frontier /
      ready_frontier / node_status), ExecuteError +
      ExecutionHaltedError in errors.py, all re-exported through
      app/orchestrator/__init__.
  2.2 **Where the instance lives (decision, justified):**
      `steps/<step>.execution.dag` BESIDE the skeleton -- the U3.4
      amendments-file precedent applied to the next per-step
      artifact. §2.12 names "file trees for ... workflow DAG
      instances"; §4 calls the frozen instance "the step's evidence
      graph" -- per-step evidence is workspace content that travels
      with the step file. NOT a workspace-level dag/ dir (in the one
      real workspace, this repo, `dag/` IS the substrate -- name
      collision), and NEVER this repo's own dag/instances/ (the
      substrate's regression corpus; tests assert it untouched).
  2.3 **How node kv state is persisted (decision, justified):** full
      rewrite (to_dag_text) through the trail-recorded tools.write_file
      on EVERY node status change -- `running` written BEFORE the
      handler fires (a crash mid-node leaves an honest marker in the
      file), then done/failed/declined plus the node's fact kvs
      (started_at/finished_at timestamps; provider + run_id on
      provider_run; approved on human_gate; applied on tool_call;
      gate + exit_code on oracle_gate; files_probed on
      context_assembly -- §2.13's "status, run ids, timestamps" and
      the execution_example.dag attr shape). The honest
      files-as-truth V0: the .dag text is the single truth; 4-5-node
      instances make a delta format complexity without a consumer.
      1 initial + 2-per-node writes, trail-asserted in the tests.
  2.4 **Substrate round-trip verification (the cross-repo dependency,
      handled honestly both ways):** bin/dagModel IS available here,
      so the written instance is verified (from_dag -> to_dag_text,
      BYTES-identical) at initial write and at loop completion, with
      the parser droppings removed after each parse (the text is the
      truth; a stale .bfgr sibling that stopped tracking kv rewrites
      would be a lie). Parser absent => writes degrade to a NAMED
      "skipped (parser not found)" outcome stamped into the evidence
      -- but RESUME refuses loudly naming the build command, because
      reading state back IS parsing.
  2.5 **Per-node-kind V0 semantics (the core design work, mapped onto
      ONLY what exists):** context_assembly = U3.3's probe_workspace
      through the trail-recorded tool surface, intent = the ratified
      skeleton's title line + §(2) deliverables body (the section
      whose tokens name the files the work touches; honest fallback
      to title-only if no §(2)). provider_run = ONE PAL call through
      the INJECTED adapter (no provider name in the orchestrator,
      §2.11): deterministic first context entry "EXECUTE <uow>:
      <title line>" (Q-4-B routing -- the mock scenario keys off it),
      an instruction block fixing the V0 proposal shape (line 1 = the
      target's workspace-relative path, then the COMPLETE new file
      content -- the proven ask arc widened to let the provider pick
      the file), the ratified skeleton text (the plan; it was
      PAL-drafted so it already legitimately left the machine once),
      and the probe; the step.8 TEXT-or-content-bearing-DONE
      acceptance rule; egress-recorded UNCONDITIONALLY before the
      proposal is parsed or judged (C-4-C). human_gate = the in-loop
      proposal review (design §4's gate applied inside EXECUTING,
      distinct from ratify): unified diff through output_fn, approve/
      decline through input_fn (C-7-B injectable end to end), EOF =
      decline, assume_yes = the --yes bypass. tool_call = the gated
      apply, tools.write_file of the approved proposal -- C-4-B
      enforced AT the write, so an escaping proposal path FAILS the
      node (spy-tested with ../evil.txt). oracle_gate = run_gate on
      the NAMED convention script scripts/oracle_gate.sh, ONLY when
      the compiled DAG contains the node (the U3.1 compiler adds it
      only for skeletons naming a T3/CCS gate); exit-as-data: 0 =>
      done with exit_code kv, non-zero/timeout => failed + halt.
  2.6 **The frontier engine is GENERIC over the DAG, not the
      template:** ready = every runnable node whose `precedes`
      predecessors are all done (edges of other kinds are annotations
      and never constrain scheduling); sequential execution in
      declaration order (deterministic; parallelism has no current
      consumer -- named, not smuggled); a stalled DAG (nothing ready,
      not all done -- a cycle) and an unknown node kind both refuse
      loudly. Proven against a synthetic branching diamond, a
      multi-root join, an annotates back-edge, and a cycle -- the
      done-when's own point of scheduling from a frontier.
  2.7 **Failure semantics (V0, decision, justified):** halt loudly,
      leave a resumable honest state -- NOT d9's repair budget (no
      retry counting, no cost ceiling; that is U3.6's open decision).
      A node failure marks failed + an `error` kv (ASCII-sanitized,
      truncated) in the instance; a human decline marks declined +
      approved=false (a decline is a decision, not an error); both
      raise ExecutionHaltedError (node_name/node_status attached) and
      the engine state STAYS EXECUTING. Re-running execute_uow
      resumes: done nodes skipped, failed/declined/running re-run. A
      resumed human_gate/tool_call whose in-memory proposal is gone
      recovers it from the EGRESS record the provider_run node's own
      run_id kv names -- the B-5 audit trail doubling as the resume
      source (proven: the resume-after-decline test passes a
      MustNotBeCalledAdapter -- zero provider calls, applied result
      byte-identical).
  2.8 **Q-15-B RESOLVED (app/tools/shell.py): scrubbed child env, not
      inheritance.** _execute (shared by run_shell AND run_gate) now
      passes env=scrubbed_env(): the parent environment MINUS every
      variable whose NAME matches API_KEY/ACCESS_KEY/SECRET/TOKEN/
      PASSWORD/PASSWD/CREDENTIAL/PRIVATE_KEY (case-insensitive).
      Justification against B-4: provider keys are env-only, so
      inheritance handed the ONE copy to every allowlisted child --
      tolerable while every caller was the developer/test suite
      (step.15's LEAN), a provider-reachable surface the moment this
      step's loop started running work downstream of provider
      proposals (a proposed gate script could env-dump).
      DENY-BY-PATTERN, not allow-by-name: the venv/make/pytest
      legitimately need most of the env (PATH/HOME/VIRTUAL_ENV/
      B3UBOT_HOME/...), and an allowlist would break real builds
      unpredictably; credentials are nameable by convention.
      Deliberately no bare "AUTH" match -- SSH_AUTH_SOCK is a socket
      path a child build may need, not a credential value. Proven
      with a REAL python3 -c child asserting the key is ABSENT (and
      a non-secret var visible), for run_shell AND run_gate.
  2.9 **End-of-loop state (decision, justified):** completion
      advances EXECUTING -> VERIFYING with verification itself
      ABSENT, named as U3.6's job. design §4's edge fires when "tool
      calls complete"; leaving a completed execution in EXECUTING
      would be a lie, and VERIFYING-with-nothing-running mirrors
      exactly how U3.4 left RATIFIED sitting until this step gave
      EXECUTING an entry point. The §(0) ENGINE EXECUTION RECORD
      (timestamp, instance path + round-trip verdict, per-node
      outcome lines, the state advance) is stamped ONCE, on
      completion, via the emitter/parser machinery with the
      post-stamp re-parse guard (the step.16/17 pattern; a
      double-stamp is guarded by a marker check). A resumable halt
      stamps NOTHING into §(0): §2.13 names the DAG instance, not the
      step file, as the LIVE progress surface -- the step file gets
      the durable ledger fact.
  2.10 **CLI: top-level `b3ubot execute`** (ask/draft/ratify's
      sibling): workspace_root + uow + --provider + --yes. Q-8-B
      non-TTY refusal unless --yes (the in-loop human gate needs
      input; --yes auto-approves it, the ask --yes rule); --provider
      human needs a TTY REGARDLESS of --yes (the relay itself is
      interactive). Presentation-layer error handling only;
      execute_uow stays pure and raises; a halted loop presents and
      exits 1 with the state left resumable.
  2.11 **tests/test_orchestrator_executing.py** (32 new): happy path
      (VERIFYING + full history; proposal applied; instance beside
      the skeleton with ZERO droppings; every node kv done with its
      facts, in the returned result AND in the file; §(0) stamp once,
      still parseable, ratification record preserved; round-trip
      "verified" through the real parser; egress record's first
      entry; 9 trail-counted instance rewrites; this repo's own
      steps/ AND dag/instances/ untouched); human gate (diff
      presented; decline halts resumably with declined/approved=false
      kvs; EOF = decline; resume-after-decline recovers the proposal
      from egress with a MustNotBeCalledAdapter and completes);
      failure (provider exception -> failed + error kv in the file,
      state EXECUTING, hello.py untouched; failed node re-runs on
      resume; malformed proposal fails AFTER egress recording;
      ../evil.txt proposal fails at the apply, nothing written
      outside); legality (SKELETON-state refuses before anything with
      zero provider calls; unknown UoW; EXECUTING-without-instance
      refuses with named repair); the generic frontier (diamond
      order, multi-root, annotates-edge ignored, cycle refused,
      unknown kind refused, ready_frontier unit); oracle_gate (green
      completes 5 nodes with exit_code=0; red halts with exit_code=1
      in the file -- and the change WAS applied, the gate verdict
      being about the applied state); CLI (non-TTY refusal, human-
      needs-TTY-even-with---yes, unattended --yes success, error
      presentation). Plus 4 Q-15-B tests grown into
      tests/test_tools_shell.py (child cannot see the key; non-secret
      passes; the pattern family incl. SSH_AUTH_SOCK kept; run_gate
      child scrubbed too).
  2.12 **Docs (same commit):** end_to_end.md §5 U3.5 row -> DONE in
      the U3.2-U3.4 row style; §11 ledger THROUGH the ledger CLI
      (transition U3.5 active --step step.18; close U3.5 PENDING;
      hash backfilled in the gdiff commit -- the step.13-17 shape).
      design.md §2.3 (record(evidence) delivered), §2.7 (DAG wiring +
      env scrub delivered-state note), §2.13 (delivered-state note:
      lifecycle rows 1-2 are code). docs/tool_surface.md (title +
      wiring + the env-scrub posture bullet).
      docs/orchestrator_state_machine.md (the live-inventory
      delivered-state updates). Every stale "U3.5 pending" docstring
      updated (app/tools/__init__.py, shell.py, gitops.py,
      app/orchestrator/__init__.py, compiler.py, ratify.py's
      post-ratify print now names `b3ubot execute`). No new docs/*.md
      file -- this file is the writeup.

EXPLICITLY OUT OF SCOPE (named here per the operator's brief, not
silently built -- and NOT continued into after this step, per the
operator's explicit stop-here scope boundary):
  - U3.6 VERIFYING: T0/T1 tiers, the verification-kind DAG, d9's
    repair-loop budget -- an executed UoW lands in VERIFYING and
    SITS; nothing runs gates; no VERIFYING->RETRO or
    VERIFYING->EXECUTING call site exists anywhere.           -> next
  - U3.7 RETRO/freeze (the instance is NOT frozen/pointed-at
    here) / U3.8 M1.                                          -> later
  - Auto re-draft on amendments (Q-17-A stays open).          -> later
  - Track B / P4 (the instance stays file-only; no WorkflowDag
    row exists to point at it).                               -> later
  - Content-aware DAG compilation (the V0 template consumed
    as-is; the generic frontier is what makes a richer compile
    worth building LATER).                                    -> later

## (3) Verification gates

    G1  tests/test_orchestrator_executing.py 32/32 PASS live: the
        full happy-path arc (mock provider, injected I/O, zero
        network) on a toy workspace -- ratified skeleton -> executed
        DAG -> node kvs done -> evidence stamped -> state VERIFYING.
    G2  Frontier proven GENERIC: branching diamond respects
        dependency order (a first, d last, b/c between), multi-root
        join runs all roots, an `annotates` back-edge does not
        constrain (would be a cycle if it did), a `precedes` cycle
        refuses loudly, an unknown node kind refuses loudly.
    G3  Decline + failure paths resumable: declined/failed kv state
        visible IN THE INSTANCE FILE, engine state stays EXECUTING,
        and the follow-up run completes -- with the
        resume-after-decline leg proving ZERO provider calls
        (MustNotBeCalledAdapter) via egress-record recovery.
    G4  Q-15-B proven with real children: run_shell and run_gate
        children print ABSENT for a monkeypatched
        $ANTHROPIC_API_KEY; a non-secret var passes through;
        SSH_AUTH_SOCK survives the pattern.
    G5  Substrate round-trip: the written instance parses under the
        REAL dag/cppcc/bin/dagModel and re-renders bytes-identically
        (asserted via the "verified" outcome), droppings cleaned
        (steps/ holds exactly step.1.txt + step.1.execution.dag).
    G6  Live CLI smoke (not just tests): draft -> ratify --yes ->
        `b3ubot execute <ws> UT.1 --yes` in a scratch workspace +
        isolated $B3UBOT_HOME -> HUMAN GATE diff presented,
        "executed: UT.1 ... engine state VERIFYING", hello.py has
        the goodbye line, engine history [INTENT, SKELETON,
        RATIFIED, EXECUTING, VERIFYING]. Confirmed live before
        committing.
    G7  Full suite before this step's edits: 443 passed / 11 skipped
        (confirmed live, matching step.17's reported final). After:
        479p/11s pre-step-file (+32 executing, +4 env-scrub);
        481p/11s once this step.18.txt lands in the corpus (+2
        self-inclusive pickups -- the steps 10-17 growth pattern).
        Zero regressions; zero existing tests edited.
    G8  Scope boundary held: no VERIFYING-tier code, no
        VERIFYING->RETRO or VERIFYING->EXECUTING call site, no d9
        budget, no Track B row; this repo's own steps/ and
        dag/instances/ untouched by every test (asserted in-suite);
        this session stops after U3.5.
    G9  §11 ledger row updated THROUGH the existing `ledger` CLI
        (transition -> active --step step.18, close -> done/PENDING),
        against the REAL end_to_end.md -- dogfooding, the
        step.13-17 precedent.
    G10 b3ubot porcelain clean after each commit; retro pair via the
        SHARED v3_claude/scripts/step_gdiff (steps/step.18.gdiff.txt);
        exactly 2 commits (work + gdiff-with-hash-backfill-folded-in),
        the step.7-17 precedent.

## (4) LOCKs

    C-18-A  b3ubot porcelain clean after each commit.
    C-18-B  THE INSTANCE FILE IS THE LIVE PROGRESS SURFACE (§2.13):
            node kv state is rewritten through the trail-recorded
            tool surface on EVERY status change (running included),
            and the .dag TEXT is the single truth -- parser droppings
            are removed after every verification parse; no shadow
            state store may exist beside it.
    C-18-C  A HALT IS RESUMABLE, NEVER A BUDGET: node failure and
            human decline leave state EXECUTING with the halting
            node's kvs persisted; re-run resumes (done skipped,
            failed/declined re-run). No retry counting, no cost
            ceiling -- d9 stays U3.6's open decision (the drafting
            one-retry precedent's spirit).
    C-18-D  EVIDENCE IS ENGINE-STAMPED MECHANICAL FACT (C-16-B/C-17-B
            carried): the §(0) EXECUTION RECORD and every node fact
            kv are engine-written (timestamps, run ids, exit codes,
            applied paths) -- never provider prose; stamped ONCE on
            completion with the post-stamp re-parse guard.
    C-18-E  THE GATE AND SCOPE NEVER WEAKEN INSIDE THE LOOP: the
            human gate runs BEFORE any write (the frontier's own
            precedes edge, and assume_yes is the only bypass --
            C-17-E's injectable-I/O rule holds); C-4-B is enforced AT
            the apply, so approval never widens scope
            (../evil.txt-tested).
    C-18-F  SECRETS NEVER REACH A CHILD (Q-15-B resolved): run_shell/
            run_gate children get scrubbed_env() -- deny-by-pattern
            on secret-shaped names; any future child-process surface
            must route through _execute or match its posture (B-4).
    C-18-G  NO SILENT SCOPE CREEP: U3.6-U3.8, Q-17-A, Track B and
            content-aware compilation named out of scope in (2) and
            confirmed not started (G8); the operator's explicit
            "stop after U3.5" direction is honored.
    C-18-H  standing locks inherited: B-3 (no disclosure/no push);
            B-6 (deterministic pytest oracles gate this step); B-4
            (served harder: the env scrub); B-5 (every provider_run
            egress-recorded unconditionally BEFORE the proposal is
            judged, C-4-C -- and the egress record is now also the
            resume source); C-15-B (every handler action, instance
            rewrite included, is on the tool trail).

## (5) Open questions

    Q-18-A  Should a resumable halt also leave a marker in the step
            file's §(0) (a "halted at <node>" line), not only in the
            instance kvs + trails? LEAN: no -- §2.13 names the DAG
            the LIVE progress surface and the step file the ledger
            artifact; transient halt state in §(0) would pile up
            stamp blocks across retries and blur the one durable
            completion record. Revisit only if real operation shows
            humans reading the step file, not the instance, mid-UoW.
    Q-18-B  Should the oracle_gate node name its script from the
            skeleton's own Verification gates text instead of the
            fixed scripts/oracle_gate.sh convention? LEAN: not yet --
            the compiler's T3 detection is regex-V0 and no real
            skeleton names a T3 gate today (P5 hasn't landed); a
            naming scheme designed against zero real instances would
            be speculation. Revisit at U3.6/U5.2 when real gate
            scripts exist.
    Q-18-C  Should provider_run send the whole ratified skeleton, or
            a §(2)-only slice (egress minimization)? LEAN: whole
            skeleton for V0 -- it is the ratified PLAN (the natural
            unit of execution context), it was PAL-drafted so its
            content already legitimately crossed the egress boundary
            once, and every send is C-4-C-recorded; slice design
            belongs to the P6/P7 context-budgeting work (§2.4's
            policy layer), not to an ad-hoc cut here.

## (6) Acceptance

    - [x] app/orchestrator/executing.py: legality-first execute_uow,
          compile -> write -> frontier loop -> kv persistence ->
          §(0) stamp -> VERIFYING (2.1, 2.9, G1, G6).
    - [x] Instance at steps/<step>.execution.dag beside the skeleton;
          full-rewrite persistence trail-recorded; droppings cleaned
          (2.2, 2.3, G5, C-18-B).
    - [x] Substrate round-trip verified through the real bin/dagModel
          both ways (available -> verified; absent -> named skip for
          writes, loud refusal for resume) (2.4, G5).
    - [x] Per-node-kind V0 handlers mapped onto what exists,
          provider_run egress-recorded unconditionally, tool_call
          C-4-B-enforced at the apply, oracle_gate exit-as-data
          (2.5, G1, C-18-D/E/H).
    - [x] Frontier generic over the DAG: diamond/multi-root/
          annotates/cycle/unknown-kind all proven (2.6, G2).
    - [x] Halt-loudly-resumably failure semantics; egress-record
          proposal recovery on resume (zero provider calls, proven)
          (2.7, G3, C-18-C).
    - [x] Q-15-B resolved: scrubbed_env() in shell.py, real-child
          proofs for run_shell AND run_gate (2.8, G4, C-18-F).
    - [x] `b3ubot execute` CLI beside ask/draft/ratify: non-TTY
          refusal unless --yes; human-needs-TTY regardless (2.10,
          G6).
    - [x] 32 + 4 new tests, zero regressions, zero edited tests
          (443p/11s -> 479p/11s -> 481p/11s with this file)
          (2.11, G7).
    - [x] design.md §2.3/§2.7/§2.13 delivered-state notes;
          docs/tool_surface.md; docs/orchestrator_state_machine.md;
          every stale "U3.5 pending" docstring; end_to_end.md §5 row
          + §11 ledger via the `ledger` CLI (2.12, G9).
    - [x] Scope boundary held: U3.6-U3.8 + Q-17-A + Track B not
          started (G8, C-18-G) -- this session stops after U3.5.
    - [x] Retro pair: this file + step.18.diff.txt + step.18.gdiff.txt
          via the SHARED scripts/step_gdiff (G10).
    - [x] Exactly 2 commits (work + gdiff-with-backfill), not 3 (G10).

## (7) Hash backfill
WORK commit (app/orchestrator/executing.py + errors/__init__ +
compiler ASCII fix + app/tools/shell.py env scrub + __init__/gitops +
adapters/mock/fixtures.py + cli + ratify print + tests +
design.md/end_to_end.md/docs + this file):  ae78fe1
GDIFF commit (step.18.gdiff.txt via scripts/step_gdiff):  (HEAD)

End of step.
