<!-- owner: RStack developed by Richardson Gunde -->

# RFC-0007: Tau-first, harness-independent architecture

## Status
Accepted

Accepted **with changes** on 2026-08-08, and revised again on **2026-08-09** when Tau 0.9.3 shipped
every Tau-side interface requirement this design was waiting on. The independent audit (2026-08-07)
returned *ACCEPT WITH CHANGES*; a fresh-HEAD re-analysis (2026-08-08) found several load-bearing facts
stale. Both sets of corrections are folded in, and the plan is reframed around the **staged,
worker-provider-first** posture actually being executed rather than the original monolithic milestone
order.

Implementation is in progress and partially shipped — see [Implementation plan](#implementation-plan)
for exactly which parts are done and which are not.

**The Tau-side interface requirements (T-01, T-03, T-06) were recorded as requests, not commitments —
and all three shipped in Tau 0.9.3** (2026-08-09). They were Jeomon George's to accept, design and
release; nothing here presumed that acceptance, and RStack's posture had to be correct either way.
It still must be: the floor is enforced per *observed runtime*, so a session on an older Tau degrades
honestly rather than inheriting a guarantee its build cannot make.

## Context

> **Reading note.** This section describes the **pre-implementation** state established by the
> 2026-08-06 audit, corrected on 2026-08-08. Several items have since been fixed by this epic's own
> children, and those are marked **✅ SHIPPED** inline. Read the section as the historical problem
> statement, never as present-day state; [What has shipped](#what-has-shipped-since-the-audit) is the
> current picture.

Tau (Jeomon George's Tau-Coding-Agent) is RStack's strategic reference runtime. RStack must (1) be
fully usable from inside Tau, (2) be able to launch and supervise Tau as a coding worker, (3) carry
no internal dependency on Pi or any other coding harness, and (4) stay backward compatible with the
existing Pi, Claude Code, Operator, and Hermes adapters.

The blocking current-state problems:

- **The Pi extension is the de-facto SDLC core.** `src/integrations/pi/rstack-sdlc.ts` is **4,366
  lines** (the audit cited ~4,250; it has grown since). Roughly 95% of it is host-independent — all
  18 `sdlc_*` tool bodies, registry/routing, prompt assembly, claim/validate flows — but it is shaped
  as a Pi extension: `activate(pi: ExtensionAPI)`. *Still true.*
- **Every non-Pi harness inherits SDLC behavior through a fake Pi.** `bin/rstack-bridge.ts`
  fabricates a mocked Pi host (`mockPi`, `bin/rstack-bridge.ts:64`) and calls the Pi adapter's
  `activate()` (`:81`). Tau included. *Still true.*
- **Two npm dependencies exist only for Pi.** `@earendil-works/pi-ai` (a single `StringEnum` import
  leaking into otherwise-neutral schemas — still live at `rstack-sdlc.ts:2` with use sites at
  `:2048`, `:2058`, `:2095`, `:2258`) and `@earendil-works/pi-coding-agent` (the `ExtensionAPI` type
  plus the runtime `sdlc_delegate` workers spawn with Pi CLI grammar). A Tau-only install cannot
  exclude them. The pi-coding-agent shrinkwrap subtree is also the sole carrier of **all eight**
  tolerated GHSA advisories in `scripts/security-audit.mjs:63-70` — three `brace-expansion` plus
  five `undici`. *Still true. (The audit said five; that was wrong.)*
- **Live enforcement was asymmetric.** ✅ **SHIPPED.** Pi had an in-process `pi.on("tool_call")`
  pre-tool hook while Tau reached the guard only through three shadowed built-ins, and its
  `_run_guard` hardcoded `--context builder`, so a Tau *validator* session never received the
  read-only sandbox context. Both halves are fixed: a universal `tool_call` gate now sits behind a
  behavioral capability probe (#575, PR #581), and the guard derives its context from
  `RSTACK_AGENT_CONTEXT` (`rstack_sdlc_pkg/guard.py:63`) instead of asserting a role (#573, PR #579).
  Operator and Hermes carried the identical defect and were fixed the same way (#580, PR #587).
  **Zero hardcoded `--context builder` sites remain in any adapter.**
- **The 18 tool schemas are duplicated four ways** (Pi typebox / Tau Pydantic / Operator Pydantic /
  Hermes JSON schema) with **name-only** conformance pinning; operator command surfaces are
  duplicated per host with no pinning at all. *Still substantially true* — #560 mirrored the
  free-text query across all four adapters, but content-level schema conformance remains open.

## What has shipped since the audit

Recorded so the decisions below are read against reality rather than against the audit's snapshot.
Each row is a merged PR.

| Issue / PR | What it established |
|---|---|
| #573 / PR #579 | The Tau guard enforces the **session's** role, not a hardcoded builder. |
| #575 / PR #581 | A **universal `tool_call` gate** behind a *behavioral* capability probe (`rstack_sdlc_pkg/capability.py`) — see the version-indistinguishability note under T-01. |
| #575 / PRs #582, #583 | Read-only posture injected into role-scoped sessions. |
| #584 / PR #585 | Core agent prompts were truncated >50%; budget corrected and guarded. |
| #577 / PR #586 | Agents know their **execution runtime and its limits** (the tier model). |
| #580 / PR #587 | Operator + Hermes validator-context fix (the #573 class, two more adapters). |
| #576 / PR #588 | Neutral worker contracts (`src/core/workers/`) + `TauRpcWorkerProvider`. |
| #578 / PR #589 | Unknown tools **fail closed** for a read-only role under governed mode. *(OPEN — awaiting CI, not merged.)* |
| #576 / PR #590 | Delegation **lifecycle parity** on Tau + run pinning + a recorded security refusal. *(OPEN — awaiting CI, not merged.)* |
| #591 / PR #593 | A Tau worker must **prove** it is governed (capabilities + guard-presence probe, fail-closed), and the Hub reports the posture it actually had. *(OPEN — awaiting CI, not merged.)* |

Two Tau-specific security requirements were discovered during implementation, both now implemented
and mutation-checked, and both worth stating as durable design constraints:

1. **A worker whose RStack guard extension failed to load is refused.** Tau replays extension load
   errors immediately after its `ready` handshake, so such a worker looks perfectly healthy while
   running completely ungoverned.
2. **Every `extension_ui_request` is cancelled.** An unanswered dialog wedges the turn forever, and
   answering it would silently approve things like project trust.

**#591 — CLOSED, and worth keeping as a worked example.** Requirement 1 catches *failed to load*, not
*never wired*. An extension that is simply absent emits no error, so the refusal never fired and the
worker ran ungoverned while reporting success — the same failure mode, through a different door.

The first design (scan Tau's settings for an `rstack` entry in `extensions.list`) was **rejected for a
false-negative risk**: a `tau install`ed pip adapter never appears there, so it would have refused
setups that genuinely work. T-03 removed the guesswork. Governance is now proven from the **running
session**: `ready.capabilities.toolCallBlocking` says whether the engine honors a block, and the
`sdlc` command in `get_commands` proves our extension loaded and registered. Correct for both wiring
routes, no false negatives.

The transferable rule: **absence of bad news is not good news.** Any check that fires only on a
reported failure has a sibling case where nothing is reported at all.

## Decision

Pi and Tau become sibling adapters over a neutral RStack core. Tau is the reference runtime; Pi is an
optional compatibility adapter. Decisions, by boundary:

**1. Neutral core boundary.** A new `src/core/sdlc/` facade (service, registry, tool-definitions,
invocation-context, errors) owns: the 18 `sdlc_*` tool names, request/response schemas and parameter
validation, tool dispatch, task claims, approvals, contracts, validation, lifecycle state, evidence
production, retry decisions, and error/result envelopes. It builds on the existing neutral
`src/core/harness/*` modules, which stay where they are. The public dependency direction is
`adapter → core`, never `Tau adapter → Pi adapter → core`. The `StringEnum` leak is replaced with a
neutral typebox enum helper.

> **Corrected 2026-08-08 (audit item 6).** The draft called this a *strangler* migration in which
> "the facade may initially delegate to the current implementations inside `rstack-sdlc.ts`". That
> directly contradicted this RFC's own Milestone-1 acceptance test — no `src/integrations/` import
> inside `src/core/sdlc/`. A facade that delegates into the Pi adapter cannot satisfy it. The
> extraction is therefore **move-first**: code moves into the core and the Pi adapter imports it,
> which satisfies the dependency-direction test at every step. Revertibility is preserved by moving
> in small, independently revertible slices rather than by keeping a delegating shim.

**2. Adapter boundary.** Adapters (`src/integrations/{pi,tau,claude,operator,hermes}`) own only host
tool registration, conversion between host arguments and canonical requests/results, host lifecycle
hooks, host UI/command registration, and process communication. No adapter may contain governance
logic; the Pi adapter shrinks to `activate()` glue over the core.

**3. Bridge-to-core invocation.** `bin/rstack-bridge.ts` invokes the neutral registry directly. The
mocked Pi host is deleted. The bridge protocol (JSON-in/JSON-out per tool call,
`RSTACK_BRIDGE_CALLER`) is unchanged, so the Tau/Operator/Hermes adapters keep working through the
migration without modification.

**4. Packaging independence.** Chosen approach: **move the two Pi packages to
`optionalDependencies` with lazy imports** inside the Pi adapter only. Rationale: a separately
installable adapter package is the better end state but a larger repo split than the risk warrants
now; install profiles fight npm rather than use it. A missing Pi package must produce a clear
Pi-only diagnostic at Pi-adapter load — never a silently caught module failure. Acceptance: a
Tau-only install runs bridge + all core tests with Pi packages absent, and carries **zero** tolerated
security advisories (all eight live in that subtree).

**5. Tool and command schema ownership.** The core's `tool-definitions.js` is the single source of
truth for tool names, descriptions, and parameter schemas; adapter schemas (Pydantic/JSON-schema) are
generated or conformance-pinned **by content**, not by name only. A canonical command manifest owns
operator command surfaces (id, description, parameters, required tool, capability, guidance source,
supported harnesses, aliases, security classification, help text); host command files are generated
from it. Tau's native surface is `/sdlc <verb>`; **capability** parity is required, exact
command-name parity is not.

**6. WorkerProvider and Tau RPC boundaries.** Versioned provider-neutral contracts in
`src/core/workers/`. The Tau provider uses **Tau's existing RPC mode** (JSONL over stdio, prompt
after readiness, `settled` as the completion boundary) — no second Tau headless runner and no Pi CLI
arguments. Worker processes get an explicit canonical working directory, a positive capability
allowlist, a scrubbed environment, pinned-version compatibility validation, process-group termination
on timeout/abort (the #487 discipline), and reconciliation after an RStack restart.

*As built (PRs #588, #590), with four facts worth not re-deriving:*

- **`settled` is the only end-of-turn signal.** Tau RPC exits **0 on clean shutdown whether or not a
  turn ran**, so an exit code is never success for a persistent runtime. `workerResult` therefore
  *requires* an explicit `completed` boolean from the provider rather than defaulting one.
- **Capabilities are translated, never forwarded.** Tau's shell tool is `terminal`; Pi's is `bash`.
  Forwarding RStack's capability name verbatim hands a worker a tool that does not exist and silently
  drops the restriction a `--tools` allowlist is meant to impose.
- **The real handoff is the on-disk contract** the worker writes (`builder.json` /
  `validator-*.json`), exactly as with Pi. stdout is telemetry, not the source of truth. Note that
  `sdlc_delegate` never touches `builder.json` at all — it builds its own prompt and delegated agents
  return **text**; the contract path is `sdlc_build_next`.
- **The worker observes its own tools; the supervisor observes the session.** A Tau worker's adapter
  already feeds `rstack-agents observe --source tau` from its own tool hooks, so the supervisor must
  **not** mirror Tau's `tool_execution_*` stream — doing so double-counts every call. The supervisor
  narrates the delegation lifecycle instead, and its lifecycle writes are queued rather than awaited:
  `agent_session_ready` fires inside the stdout read loop, where awaiting a locked ledger append
  would gate the reader on disk work and let the worker deadlock behind its own pipe backpressure.

**7. Project-trust behavior.** Workers never auto-approve project trust unless RStack policy
explicitly authorizes it. Until Tau exposes a machine-readable trust outcome (T-06), the provider
cancels trust prompts in unattended mode and surfaces the block honestly. *As built: every
`extension_ui_request` is cancelled, which refuses trust rather than approving it.*

**8. Environment boundaries.** Worker environments are allowlist-scrubbed (a documented minimal base
plus job-declared variables); secrets never flow implicitly from the scheduler's environment into
workers. `RSTACK_ALLOW_DESTRUCTIVE` and stale validator stamps are never inheritable. *As built:
`scrubEnv` is deny-by-default, and the delegating run id is pinned into the worker so its own
observability cannot be attributed to a different run.*

**9. Version compatibility.** The Tau provider and adapter pin tested Tau versions; a harness
capability registry records harness id, runtime/adapter versions, support state
(`native | adapter-backed | instruction-backed | reference-only`), per-capability enforcement tier,
and verification evidence. Unverified runtime versions fail closed in governed production mode.
Documentation is generated from registry data so it cannot overclaim.

Tau's security capability was recorded as **partial** for two independent reasons, and both have now
cleared: T-01 shipped in **0.9.3**, and #591 closed the unwired-extension hole using T-03's
capability report. The tier is therefore raised to **full on `>=0.9.3`** — and, critically, it stays
**partial below that floor**, because the tier is a property of the *observed runtime*, not of RStack.
A worker that cannot prove `toolCallBlocking` is refused rather than recorded at a tier it has not
earned.

**10. Lifecycle and evidence events; attempt and claim binding.** The attempt ledger gains a real
execution lifecycle: `READY → CLAIMED → EXECUTING → OUTPUT_READY → VALIDATING →
{PASS | CHANGES_REQUESTED | BLOCKED_INFRA | BLOCKED_POLICY | NEEDS_CONTEXT}`, with `BUILT` retained
as an alias of `OUTPUT_READY` for old ledgers.

> **Corrected 2026-08-08 (audit item 5).** `EXECUTING` is **not** gained — it is **activated**. It is
> already declared in `ATTEMPT_STATES` (`src/core/harness/attempt-ledger.js:47`) with transitions
> defined both into and out of it (`:76-77`), and nothing in the codebase ever enters it. The work is
> to make a real transition reach it, not to add a state.

Only the current claim nonce may start execution; the ledger records provider, runtime version,
worker/session id, PID or remote job id, worktree, and timestamps; duplicate workers per idempotency
key are rejected; stale workers are rejected; builder and validator identities stay independent
(RFC-0003). An action-policy boundary normalizes every mutating action into an
`ActionRequest`/`PolicyDecision` pair: unknown mutating capabilities fail closed in governed mode
(shipped for read-only roles in #578), approvals bind to the exact normalized action and attempt (the
#482 discipline), validators never receive builder mutation authority, and policy-service failure
blocks mutating actions (the #371 discipline).

**11. Migration and backward compatibility.** Pi behavior is preserved throughout: the
bridge-conformance suite, plugin-command conformance, and the golden-path e2e stay green at every
step. Existing `.rstack/` state, ledgers, and approval records remain readable unmodified. Work lands
as separate issues/PRs; no massive refactoring PR.

**12. Rollback strategy.** Each step is independently revertible: the bridge re-point is a one-import
change, `optionalDependencies` can be restored to `dependencies` in one commit, and the ledger
lifecycle keeps old statuses readable so a revert never strands run state. No step deletes the Pi
adapter or its tests. Under move-first extraction (decision 1), revertibility comes from slice size
rather than from a delegating shim.

### Tau-side interface requirements — ALL DELIVERED in Tau 0.9.3

**Updated 2026-08-09.** Jeomon released **0.9.3 to PyPI** carrying T-01, T-03 and T-06. Every claim
below was re-verified against the **`v0.9.3` tag** in a fresh clone — not `main`, which carries an
unreleased `remote` mode that must not be built against. **Version floor: `tau-coding-agent>=0.9.3`.**

- **T-01 — universal pre-tool gate. DELIVERED.** `tool_call` is in the engine's interceptable set
  (`tau/extensions/runtime.py:36`) and `ToolCallEventResult.metadata` exists
  (`tau/hooks/engine.py:307`); a block cancels the call before the tool runs, and the contract is
  declared stable in Tau's own `docs/extensions.md`.

  *Two earlier statements in this RFC's own history were wrong and are corrected here.* The draft said
  commit `a35a7f0` was "in v0.9.2" — it was in **no tag at all**. The first revision then said the
  remaining ask was a release; that release has now happened. Worth keeping: **0.9.2 is more broken
  than "missing the fix"** — the entire interception path postdates it, so a handler there is silently
  ignored rather than partially working. That is why the floor is hard.

  **A version check still cannot substitute for reading the capability**, and this is not a
  historical footnote: Tau HEAD and the v0.9.2 tag both declared version `0.9.2` and there is no
  `__version__`, so nothing in a version string could distinguish an engine that honors a block from
  one that discards it. RStack therefore reads `capabilities.toolCallBlocking` over RPC, and the
  in-process adapter — which cannot see the `ready` line, being inside the session — keeps its
  *behavioral* probe (`rstack_sdlc_pkg/capability.py`). That probe already used only Tau's public
  API, never the private `_INTERCEPTABLE_EVENTS` name.

  **Built-in shadowing stays regardless**, for a reason unrelated to the release: a hook handler that
  raises is swallowed upstream and read as consent, so layer 1 is never traded away for layer 2.

- **T-03 — version and capabilities in `ready`. DELIVERED.** `mode.py:1412-1421` announces
  `protocolVersion`, `runtimeVersion`, `capabilities` and the trust fields. Each capability is read
  from the object that implements it (`:430` derives `toolCallBlocking` from the interceptable set),
  so it cannot advertise a behavior the build lacks. Announced, not negotiated — no round trip.
  **This is what made #591 fixable**: absent `capabilities` now positively identifies a pre-0.9.3
  build, rather than leaving a supervisor to guess.

- **T-06 — project trust reported *and* settleable. DELIVERED.** `ready` and `get_state` both carry
  `projectTrusted` + `projectTrustSource`; the `trust` command (`mode.py:738-790`) can read and set
  it, with `remember`/`forget` for per-directory persistence. The **source** is the load-bearing part:
  a bare `false` collapsed "the user refused" and "nobody was ever asked", which are different
  problems for an unattended run. Values include **`undecided`** — the one to act on.

  Two operational facts recorded so they are not rediscovered: **`reloaded: true` means extensions and
  context files skipped at startup were just loaded**, so anything cached from an earlier `get_state`
  is stale and must be re-read; and **closing stdin immediately after a `trust` write can kill the
  loop before the reload response arrives** — the pipe must stay open. RStack's provider does
  `stdin.end()` in its shutdown path, so any future trust-granting must sequence around that.

- **T-05 — the `tau install` event-loop bug. Closed.** No action.

**Not shipped: multi-client.** `tau --mode remote` (unix socket, several clients per session,
revision-stamped events, replay on reconnect) exists on `main` but is in **no release** — confirmed at
the tag, where `tau/modes/` holds only `interactive`, `print`, `rpc`. Do not build against it. 0.9.3
is one client per process over stdio.

RStack tests the interfaces; Tau owns the implementations.

### Sequencing note

The original draft's precondition — triage and land PRs #564–#570 before starting extraction — is
**satisfied**: epic #556's whole wave merged (PRs #564–#571).

## Alternatives considered

1. **Keep Pi as the shared core (status quo).** Rejected: every harness inherits a fake Pi host,
   Tau-only installs drag in Pi packages carrying all **eight** tolerated advisories, Pi-only live
   enforcement stays structurally unavailable elsewhere, and `sdlc_delegate` can never launch Tau
   workers.
2. **Copy RStack logic into Tau.** Rejected: forks the governance core into two drifting
   implementations in two languages; violates the goal of enforcement backed by one runtime source of
   truth, and makes evidence/contracts diverge.
3. **Rewrite RStack in Python.** Rejected: discards ~2,100 passing tests and the entire hardened
   `src/core/harness/` lineage for zero governance gain; the JS core is already host-neutral in
   substance.
4. **Rewrite the complete Pi adapter at once.** Rejected: a massive PR violates repo protocol and is
   un-reviewable against the no-weakening rule; staged extraction reaches the same end state in
   revertible steps.
5. **Merge RStack and Tau into one repository.** Rejected: separate ownership (Richard / Jeo),
   separate release cadences, and an explicit rule against modifying Tau-owned code without
   authorization.
6. **Make the RStack core depend on Tau internals.** Rejected: replaces one harness coupling with
   another; the capability registry exists precisely so hosts declare capabilities to a neutral core,
   not the reverse.
7. **A delegating facade (strangler) for the core extraction.** Rejected during this revision: it
   contradicts the dependency-direction test this RFC itself requires. See decision 1.
8. **Mirror Tau's `tool_execution_*` stream into `events.jsonl` from the supervisor.** Rejected
   during implementation (#576): the worker's own adapter already writes those events, so the
   supervisor would double-count every tool call in the feed and in the Hub's burst rollup. The
   supervisor narrates the session instead. Recorded because the double-write is the *obvious*
   reading of "mirror Tau's events" and would otherwise be re-added.
9. **Gate Tau delegation on a version check instead of a behavioral probe.** Rejected: HEAD and the
   v0.9.2 tag are indistinguishable by version string (see T-01), so a version check would report a
   capability the runtime may not have.

## Research references

- **The 2026-08-06 Tau-First audit** — a conversational 12-question source-backed dependency map.
  **It exists only as analysis, not as a committed document**, so it is cited here as provenance
  rather than as a followable link. Its load-bearing findings are reproduced inline in
  [Context](#context), each anchored to a named file and line so any reader can re-verify them
  against the source rather than trusting the summary. Every such claim was re-verified against the
  tree on 2026-08-08; two were found stale and are corrected above.
- **The Tau-First engineering brief (Richardson, 2026-08-06)** — likewise a prompt, not a document.
  Its decisions that this RFC depends on are restated inline above rather than cited by reference.
- `docs/integrations/adapter-contract.md` — the current 18-tool adapter surface this RFC preserves.
- RFC-0003 (cross-harness validation) and RFC-0004 (attestation envelope) — the independence and
  evidence guarantees the worker/policy boundaries must not weaken.
- Shipped disciplines this design generalizes: #371 guard fail-closed, #482 exact action/attempt
  approval binding, #487 process-tree termination, #481 attempt ledger, #554 "a gate that cannot ask
  must never answer clean".
- **Tau-Coding-Agent at the `v0.9.3` tag** — the single pinned Tau state for T-01/T-03/T-06, and the
  version floor RStack enforces. Pinned to a TAG rather than a commit deliberately: `main` carries an
  unreleased `remote` (multi-client) mode, and building against unreleased behavior is the mistake the
  T-01 correction already cost this document once. *The draft pinned two conflicting commits (`5246834c`, which
  predated the commit it analyzed, and `4746e77`); both are superseded by this one pin. Tau-side
  facts must be re-verified against HEAD at certification time, since RStack does not control that
  tree.*

## Implementation plan

Reframed 2026-08-08 (audit item 7) around the **staged, targeted-worker-provider-first** posture
actually in execution, rather than the draft's monolithic milestone order. The worker provider was
deliberately built **before** the core extraction because it is the one seam that unblocks Tau
execution without requiring the 4,366-line extraction first.

**Shipped:**

1. ~~Audit + this ADR.~~ Committed by this change.
2. ~~Tau guard enforces the session's role~~ (#573, PR #579) — and the same defect fixed in Operator
   and Hermes (#580, PR #587).
3. ~~Universal `tool_call` gate behind a behavioral capability probe~~ (#575, PRs #581–#583).
4. ~~Execution-runtime awareness / tier model~~ (#577, PR #586).
5. ~~Prompt-budget correction~~ (#584, PR #585).
6. ~~`src/core/workers/` contracts + deterministic fake Tau RPC fixtures + `TauRpcWorkerProvider`~~
   (#576, PR #588).

**In flight — open PRs, neither merged.** Both are locally green and both are blocked on GitHub
Actions being billing-blocked repo-wide; #588 already merged on a local gate alone and doing that
twice would be a pattern rather than an exception:

7. Unknown mutating capabilities fail closed for read-only roles (#578, PR #589).
8. Delegation lifecycle parity on Tau, run pinning, recorded refusals (#576, PR #590).

**Next, in order:**

9. ~~Close #591: positive proof that a Tau worker's guard is loaded, fail-closed with a documented
   escape hatch in the #371 mould.~~ **Done** (PR #593), together with the Business Hub panel that
   reports the posture — `RSTACK_TAU_GOVERNANCE_FAIL_OPEN` is the escape hatch, and a tolerated
   worker is recorded as tolerated, never as verified.
10. Neutral core facade (`src/core/sdlc/`), **move-first** extraction from `rstack-sdlc.ts`, in small
    revertible slices.
11. `bin/rstack-bridge.ts` invokes the neutral registry; the mocked Pi host is deleted.
12. Pi packages → `optionalDependencies` with lazy Pi-adapter imports + Pi-only diagnostics.
13. Canonical command manifest + generated host surfaces + Tau-native `/sdlc` UX.
14. Attempt-ledger execution lifecycle (**activate** `EXECUTING`); opt-in scheduler execution.
15. Universal `ActionRequest`/`PolicyDecision` boundary for all roles.
16. ~~Tau hook certification once T-01 is released~~ — **unblocked**: 0.9.3 shipped it. What remains is
    RStack-side certification against a pinned real 0.9.3 (the provider's whole failure catalog runs
    today against a fake, with no Tau install).
17. Harness capability registry + generated documentation; full Tau certification matrix.

## Validation

- **Every step:** `npm test` / lint / typecheck / `validate` / `validate:schemas` /
  `node scripts/security-audit.mjs` / `git diff --check`, plus the bridge-conformance,
  plugin-command-conformance, and golden-path e2e suites green (Pi preserved).
- **Core extraction:** a dependency-direction test asserts no `src/integrations/` import inside
  `src/core/sdlc/`, and that `bin/rstack-bridge.ts` imports nothing from `src/integrations/pi`. Under
  move-first extraction this test is satisfiable at every step, which was not true of the draft's
  delegating facade.
- **Packaging:** a CI job installs with `--omit=optional`, runs the core + Tau conformance suites,
  asserts **zero** tolerated advisories in that tree, and asserts the Pi adapter emits its named
  diagnostic when the packages are absent.
- **Command manifest:** a drift test regenerates host command surfaces and fails on any diff.
- **Worker provider:** the fake-RPC fixture suite covers the failure catalog — partial/malformed
  JSONL, stderr isolation, pre-ready commands, timeout, crash, process-tree cleanup, restart
  reconciliation, duplicate scheduler, stale claim, missing builder contract — plus both security
  refusals, so the whole catalog runs in CI with **no Tau install**. Lifecycle narration is pinned
  end-to-end through a real `sdlc_delegate` call, so un-wiring it cannot pass while unit tests do.
- **Policy boundary:** a red-team suite for unknown mutating capabilities, alias normalization,
  validator mutation attempts, and extension/MCP bypass attempts — fail closed, in the #369
  red-team style.
- **Capability registry:** a documentation-from-registry test — no prose claim of enforcement without
  a registry record carrying verification evidence.
- **Every fix mutation-checked**, including fixes for review findings: a guard that cannot fail is
  not a guard.
