# Diagrams — SDD-B11: Tau: The Reference Harness to Attack and Harden

**Module**: SDD-B11 — Tau: The Reference Harness to Attack and Harden
**Diagram count**: 5
**Tool**: Mermaid (primary). Each diagram validated in [Mermaid Live Editor](https://mermaid.live).

---

## Diagram 1 — tau's Three-Layer Architecture

**Type**: Architecture / boundary map
**Purpose**: The foundational visual. Tau is Hugging Face's educational reimplementation of Pi, split into three packages with a load-bearing boundary: `tau_ai` (provider/model streaming), `tau_agent` (the reusable brain: harness, loop, tools, events, sessions), and `tau_coding` (the coding app: CLI/TUI, read/write/edit/bash tools, skills, on-disk sessions). The core does not know about Textual, Rich, local config paths, slash commands, or rendering. Frontends consume events. This separation is what makes tau a clean target: security controls attach in `tau_agent` (enforcement) or `tau_coding` (where the attack surface lives). The diagram is the map every extension point and every B-module wedge refers back to.
**Reading the diagram**: Three stacked layers. The provider-neutral streaming layer at the top; the reusable brain in the middle (where the loop, tools, events, and harness live); the coding app at the bottom (where the filesystem, credentials, sessions, and bash tool live). The teal boundary between `tau_agent` and `tau_coding` is the load-bearing one — the line enforcement belongs above, attack surface below.

```mermaid
flowchart TB
  subgraph AI["tau_ai — PROVIDER/MODEL STREAMING (provider-neutral)"]
    direction TB
    A1["OpenAI · Anthropic · Codex subscription<br/>OpenRouter · Hugging Face · local models"]
    A2["create_model_provider()<br/>provider_runtime.py:46<br/>CredentialReader protocol → provider config"]
  end

  subgraph AGENT["tau_agent — THE REUSABLE BRAIN"]
    direction TB
    G1["AgentHarness (harness.py)<br/>subscribe(listener) — EventListener · events.py<br/>run_agent_loop — loop.py"]
    G2["_execute_tool_calls (loop.py:190)<br/>tool.execute → tool.executor<br/>_tool_result_message (loop.py:260)"]
    G3["AgentTool (tools.py:61) — frozen dataclass<br/>executor: ToolExecutor (Protocol)<br/>→ EXTENSION POINT 1 (B2/B4)"]
    G4["events: TurnStart · ToolExecutionStart<br/>ToolExecutionEnd (carries result) · MessageEnd<br/>→ EXTENSION POINT 4 (B8, read-only)"]
  end

  subgraph CODING["tau_coding — THE CODING APP"]
    direction TB
    C1["CodingSession (session.py, 88KB)<br/>run_terminal_command (session.py:1183)<br/>load() tool-list construction (session.py:286)"]
    C2["create_bash_tool (tools.py:574)<br/>create_bash_tool_definition (tools.py:433)<br/>asyncio.create_subprocess_shell (tools.py:470)"]
    C3["SessionStorage JSONL (session.py:187)<br/>_persist_messages_since (session.py:1378)<br/>~/.tau/sessions/"]
    C4["FileCredentialStore (credentials.py)<br/>~/.tau/credentials.json (plaintext 0600)<br/>oauth.py"]
  end

  AI --> AGENT
  AGENT -->|"load-bearing boundary —<br/>enforcement lives ABOVE, attack surface BELOW"| BOUND["BOUNDARY<br/>tau_agent ↔ tau_coding"]:::teal
  BOUND --> CODING

  FE["FRONTENDS (Textual TUI, CLI)<br/>consume events — never in the trusted path"]:::muted
  AGENT -.->|"events"| FE

  classDef teal fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
  classDef muted fill:#14141f,stroke:#9494a0,stroke-width:1px,color:#9494a0
  style AI fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
  style AGENT fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
  style CODING fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
  style A1 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style A2 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style G1 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style G2 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style G3 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style G4 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style C1 fill:#101018,stroke:#f08080,color:#e4e4e8
  style C2 fill:#101018,stroke:#f08080,color:#e4e4e8
  style C3 fill:#101018,stroke:#f08080,color:#e4e4e8
  style C4 fill:#101018,stroke:#f08080,color:#e4e4e8
  style FE fill:#14141f,stroke:#9494a0,color:#9494a0
```

> **Note**: The three properties that make tau the ideal teaching target: it is real (a `bash` tool that actually runs `asyncio.create_subprocess_shell`; a credential store that actually holds plaintext API keys on disk), it is small (~3,000 LOC across three packages, readable in an afternoon), and it is undefended (none of the controls B2–B8 build exist today). Every control you add is a net-new feature, and its absence is the vulnerability you are closing. If DD-01 was "here is the simplest harness that works," SDD-B11 is "here is the simplest harness we can break and fix."

---

## Diagram 2 — The "Before" Picture: tau's 7 Surfaces Mapped to B-Modules

**Type**: Surface map / vulnerability matrix
**Purpose**: The core of the deep-dive. Each of tau's seven attack surfaces from B1's threat model, the OWASP ASI risk it exposes, the module that covers it, tau's current undefended state, and the control that closes it. This is the "before" picture — the raw, injectable, exfiltratable harness that Course 2B teaches you to harden. Every surface is currently undefended; every defense is a missing feature that attaches at a clean, explicit extension point. The delta between this diagram and the hardened session (Diagram 5) is the scorecard the capstone measures.
**Reading the diagram**: Seven rows, one per surface. Each surface (danger) is undefended today; the control that closes it (teal) attaches at the named extension point on the right. Note the three patterns that recur: tool-output is unfiltered and reaches the transcript (Surfaces 1, 2, 3); the bash tool runs with zero isolation and reads plaintext credentials (Surfaces 5, 6); the event stream is emitted but not analyzed (Surface 7).

```mermaid
flowchart LR
  subgraph SURF["TAU'S 7 SURFACES (B1) — ALL UNDEFENDED TODAY"]
    direction TB
    S1["SURFACE 1 — Agent loop (loop.py:190)<br/>no interception · executes on request<br/>ASI09 Agent Leftover"]:::danger
    S2["SURFACE 2 — Tool output (loop.py:260)<br/>unfiltered into transcript<br/>ASI01 Injection"]:::danger
    S3["SURFACE 3 — Sessions (session.py:1378)<br/>no memory-write gate · JSONL poison persists<br/>ASI06 Memory"]:::danger
    S4["SURFACE 4 — Provider (tau_ai, provider_config.py)<br/>no provider-authz check · any tool reaches API<br/>ASI03 Excessive Agency"]:::danger
    S5["SURFACE 5 — Credentials (credentials.py)<br/>plaintext 0600 · bash can cat/printenv<br/>ASI10 Broken Access Control"]:::danger
    S6["SURFACE 6 — Sandbox (tools.py:470)<br/>zero isolation · no egress/allowlist/caps<br/>ASI03 Excessive Agency"]:::danger
    S7["SURFACE 7 — Observability (events.py, harness.py:124)<br/>events emitted · no listener analyzes<br/>ASI11 Cross-session"]:::danger
  end

  subgraph CTRL["THE CONTROL THAT CLOSES IT"]
    direction TB
    D1["B2 — Taint gate + injection detector<br/>wrap the executor"]:::teal
    D2["B2/B4 — Tag tool output untrusted<br/>sanitize at the executor wrapper"]:::teal
    D3["B3 — Harness-managed writes<br/>validate at _persist_messages_since"]:::teal
    D4["B0/B1 — Scope enforcement<br/>gated tool list at construction"]:::teal
    D5["B5 — Credential vault<br/>replace FileCredentialStore"]:::teal
    D6["B7 — Default-deny egress + allowlist<br/>wrap the bash factory"]:::teal
    D7["B8 — Session intent tracker<br/>subscribe a listener"]:::teal
  end

  S1 -->|"Extension Point 1"| D1
  S2 -->|"Extension Point 1"| D2
  S3 -->|"Extension Point 1 or _persist chokepoint"| D3
  S4 -->|"Extension Point 5"| D4
  S5 -->|"Extension Point 3"| D5
  S6 -->|"Extension Point 2"| D6
  S7 -->|"Extension Point 4 (additive)"| D7

  classDef danger fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
  classDef teal fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
  style SURF fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
  style CTRL fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
  style S1 fill:#101018,stroke:#f08080,color:#e4e4e8
  style S2 fill:#101018,stroke:#f08080,color:#e4e4e8
  style S3 fill:#101018,stroke:#f08080,color:#e4e4e8
  style S4 fill:#101018,stroke:#f08080,color:#e4e4e8
  style S5 fill:#101018,stroke:#f08080,color:#e4e4e8
  style S6 fill:#101018,stroke:#f08080,color:#e4e4e8
  style S7 fill:#101018,stroke:#f08080,color:#e4e4e8
  style D1 fill:#101018,stroke:#5eead4,color:#5eead4
  style D2 fill:#101018,stroke:#5eead4,color:#5eead4
  style D3 fill:#101018,stroke:#5eead4,color:#5eead4
  style D4 fill:#101018,stroke:#5eead4,color:#5eead4
  style D5 fill:#101018,stroke:#5eead4,color:#5eead4
  style D6 fill:#101018,stroke:#5eead4,color:#5eead4
  style D7 fill:#101018,stroke:#5eead4,color:#5eead4
```

> **Note**: The recurring architectural lesson: enforcement belongs in the tool, not in the loop. The loop delegates to `tool.execute`, which delegates to `tool.executor`. You do not patch `run_agent_loop` or `_execute_tool_calls` to add a pre-execution gate — you wrap the executor (Extension Point 1) and the loop stays untouched. The only control that wedges higher than the tool layer is B3's memory-write gate at `_persist_messages_since` (session.py:1378), the single chokepoint where every message becomes durable.

---

## Diagram 3 — The Five Extension Points Map

**Type**: Control-attachment map
**Purpose**: This is the map the capstone and the plugin pack use. Tau has no plugin system — no `register_tool`, no hook framework, no middleware (verified by grepping the entire source). Every control attaches by wrapping or replacing objects at explicit, named sites. This is a feature, not a limitation: it makes every wedge point auditable and teachable. Five extension points, five B-modules, no monkey-patching required for four of the five. The diagram shows exactly where each control attaches and what it replaces.
**Reading the diagram**: Five lanes, top to bottom. Each lane is an extension point: where it lives in the code, what it wraps or replaces, and which B-module's control attaches there. The teal lanes are wrapping (no code modification); the warn lane (Extension Point 3) is replacement (the vault replaces `FileCredentialStore` by implementing the `CredentialReader` protocol). Note that four of the five are additive or wrapping; only the credential store is a straight replacement.

```mermaid
flowchart TB
  subgraph EP1["EXTENSION POINT 1 — Tool-executor wrapping (B2, B4)"]
    direction TB
    P1A["AgentTool (tools.py:61) — frozen dataclass<br/>ToolExecutor is a Protocol (async callable)"]
    P1B["wrap_with_gate(inner, gate)<br/>dataclasses.replace(inner, executor=gated)<br/>flows to CodingSessionConfig.tools → AgentHarnessConfig"]
    P1C["NO monkey-patching · NO subclassing<br/>NO framework registration"]:::teal
    P1A --> P1B --> P1C
  end

  subgraph EP2["EXTENSION POINT 2 — The bash tool factory (B7)"]
    direction TB
    P2A["create_bash_tool (tools.py:574) — BOTH bash paths:<br/>(1) create_coding_tools() harness list (tools.py:96)<br/>(2) run_terminal_command bar (session.py:1183)"]
    P2B["create_hardened_bash_tool(cwd, prefix, policy)<br/>wraps executor returned by create_bash_tool_definition<br/>policy.check(command) → allowlist/denylist/egress"]
    P2C["wedge at the FACTORY, not the instance<br/>or run_terminal_command bypasses you"]:::warn
    P2A --> P2B --> P2C
  end

  subgraph EP3["EXTENSION POINT 3 — Credential store replacement (B5)"]
    direction TB
    P3A["create_model_provider (provider_runtime.py:46)<br/>FileCredentialStore() default at line 56<br/>CredentialReader protocol: get(name) → str | None"]
    P3B["inject vault: create_model_provider(credential_store=my_vault)<br/>ALSO replace the second FileCredentialStore<br/>in CodingSession.__init__ (session.py:253-255)"]
    P3C["B5 secures at rest + provider boundary<br/>B7 blocks the exfiltration channel (coupled)"]:::warn
    P3A --> P3B --> P3C
  end

  subgraph EP4["EXTENSION POINT 4 — Event-stream subscription (B8)"]
    direction TB
    P4A["AgentHarness.subscribe(listener) (harness.py:124)<br/>EventListener = Callable[[AgentEvent], Awaitable|None]"]
    P4B["attach_intent_tracker(harness)<br/>ToolExecutionStartEvent → observe_request<br/>ToolExecutionEndEvent → observe_result"]
    P4C["purely ADDITIVE · read-only<br/>cannot block — enforcement is EP1"]:::teal
    P4A --> P4B --> P4C
  end

  subgraph EP5["EXTENSION POINT 5 — Tool-list construction (B0/B1)"]
    direction TB
    P5A["CodingSession.load (session.py:286-293)<br/>config.tools if provided, else create_coding_tools()"]
    P5B["inject hardened tools list — every executor wrapped:<br/>scope gate (B0/B1) + taint gate (B2) + sandbox (B7)<br/>flows to AgentHarnessConfig(tools=...) at session.py:310"]
    P5C["single injection site · persists across reloads<br/>the entry point for the hardened session"]:::teal
    P5A --> P5B --> P5C
  end

  classDef teal fill:#101018,stroke:#5eead4,color:#5eead4
  classDef warn fill:#101018,stroke:#f0a868,color:#f0a868
  style EP1 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
  style EP2 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
  style EP3 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
  style EP4 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
  style EP5 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
  style P1A fill:#101018,stroke:#5eead4,color:#e4e4e8
  style P1B fill:#101018,stroke:#5eead4,color:#e4e4e8
  style P2A fill:#101018,stroke:#5eead4,color:#e4e4e8
  style P2B fill:#101018,stroke:#5eead4,color:#e4e4e8
  style P3A fill:#101018,stroke:#5eead4,color:#e4e4e8
  style P3B fill:#101018,stroke:#5eead4,color:#e4e4e8
  style P4A fill:#101018,stroke:#5eead4,color:#e4e4e8
  style P4B fill:#101018,stroke:#5eead4,color:#e4e4e8
  style P5A fill:#101018,stroke:#5eead4,color:#e4e4e8
  style P5B fill:#101018,stroke:#5eead4,color:#e4e4e8
```

> **Note**: The anti-patterns this map prevents: (1) patching the loop instead of wrapping the executor — the loop delegates to `tool.executor`, wrap there; (2) wrapping only the harness bash tool and missing `run_terminal_command` — wedge at the factory; (3) overloading `shell_command_prefix` as an allowlist — it is a blind prepend (`f"{prefix}\n{command}"`) that cannot see or reject the command; (4) looking for a `register_tool` hook — there is none, construct a custom `CodingSessionConfig`; (5) using the event listener for enforcement — events fire after the decision, they are read-only observability.

---

## Diagram 4 — The Two-Bash-Tool Finding

**Type**: Bypass flow / factory-level fix
**Purpose**: The single most important implementation detail in tau for a security engineer. A B7 sandbox that only wraps tools in the harness tool list (Extension Point 1) will be bypassed by the terminal-command bar, which constructs its own bash tool outside the list and calls it directly. Both paths go through `create_bash_tool` (tools.py:574), but only the harness path is reachable from the tool-list injection site. The clean fix is to wedge at the factory level — provide a hardened `create_bash_tool` replacement that both call sites use. The wedge point is the factory, not the individual tool instances.
**Reading the diagram**: Two parallel bash paths from the same factory. The danger path (terminal command bar) bypasses any tool-list-level control because it never enters `CodingSessionConfig.tools`. The fix (teal) is the hardened factory that replaces `create_bash_tool` itself, so both paths receive the sandbox policy regardless of how they were constructed.

```mermaid
flowchart TB
  FACT["create_bash_tool factory<br/>tools.py:574"]:::teal

  subgraph PATH1["PATH 1 — HARNESS TOOL LIST (reachable from EP1/EP5)"]
    direction TB
    H1["create_coding_tools()<br/>tools.py:96"]
    H2["create_bash_tool(cwd=root, shell_command_prefix=...)<br/>→ enters CodingSessionConfig.tools"]
    H3["flows to AgentHarnessConfig(tools=...)<br/>→ loop executes via tool.executor"]
    H1 --> H2 --> H3
  end

  subgraph PATH2["PATH 2 — TERMINAL COMMAND BAR (BYPASS)"]
    direction TB
    T1["CodingSession.run_terminal_command<br/>session.py:1183"]
    T2["constructs its OWN create_bash_tool<br/>(cwd=self.cwd, shell_command_prefix=...)<br/>calls it directly at session.py:1187"]
    T3["NEVER enters CodingSessionConfig.tools<br/>NEVER seen by a tool-list-level gate"]:::danger
    T1 --> T2 --> T3
  end

  FACT --> PATH1
  FACT --> PATH2

  PATH1 -.->|"tool-list wrap (EP1) covers this"| COVERED1["SANDBOX APPLIES<br/>if you wrap EP1"]:::good
  PATH2 -.->|"tool-list wrap (EP1) does NOT cover this"| BYPASSED["SANDBOX BYPASSED<br/>tool-list gate never sees it"]:::danger

  FIX["THE FIX — wedge at the FACTORY<br/>replace create_bash_tool itself<br/>both call sites receive the sandbox policy"]:::teal
  FACT ==>|"hardened factory replacement"| FIX
  FIX -.->|"covers PATH 1"| COVERED1
  FIX -.->|"covers PATH 2"| COVERED2["SANDBOX APPLIES<br/>both paths"]:::good

  NOTE["shell_command_prefix is NOT an allowlist hook<br/>it is a blind prepend: f'{prefix}\\n{command}'<br/>evaluated as a shell preamble — cannot see/reject the command"]:::warn

  classDef teal fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
  classDef danger fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#f08080
  classDef good fill:#14141f,stroke:#82e0aa,stroke-width:1.5px,color:#82e0aa
  classDef warn fill:#14141f,stroke:#f0a868,stroke-width:1.5px,color:#f0a868
  style FACT fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
  style PATH1 fill:#14141f,stroke:#82e0aa,stroke-width:1px,color:#e4e4e8
  style PATH2 fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
  style H1 fill:#101018,stroke:#82e0aa,color:#e4e4e8
  style H2 fill:#101018,stroke:#82e0aa,color:#e4e4e8
  style H3 fill:#101018,stroke:#82e0aa,color:#e4e4e8
  style T1 fill:#101018,stroke:#f08080,color:#e4e4e8
  style T2 fill:#101018,stroke:#f08080,color:#e4e4e8
  style T3 fill:#101018,stroke:#f08080,color:#f08080
  style COVERED1 fill:#101018,stroke:#82e0aa,color:#82e0aa
  style COVERED2 fill:#101018,stroke:#82e0aa,color:#82e0aa
  style BYPASSED fill:#101018,stroke:#f08080,color:#f08080
  style FIX fill:#14141f,stroke:#5eead4,stroke-width:2px,color:#5eead4
  style NOTE fill:#14141f,stroke:#f0a868,stroke-width:1px,color:#f0a868
```

> **Note**: This finding generalizes: any control that wedges at the tool-list layer (Extension Point 1 or 5) is bypassable by any code path that constructs its own tool outside the list. The factory is the chokepoint. When you audit a harness for a control's coverage, do not ask "is the tool in the list?" — ask "is there any code path that constructs this tool outside the list?" The factory-level wedge is the only one that answers both.

---

## Diagram 5 — The Hardened Session Composition

**Type**: Composition / assembled architecture
**Purpose**: The "after" picture. The plugin pack assembles all five extension points into a single entry point (`create_hardened_session`). This diagram shows the composition: every tool wrapped with the taint gate (B2) and the scope gate (B0/B1); bash wrapped with the sandbox policy (B7) at the factory level so both paths are covered; credentials read from the vault, not plaintext (B5); the intent tracker subscribed to the event stream (B8). The scorecard harness measures the before/after: run the InjecAgent-style injection battery against unmodified tau, then against this hardened session. The delta is the defense scorecard.
**Reading the diagram**: The five extension points from Diagram 3, assembled. The teal boxes are the controls now in place; the danger boxes are the surfaces they close (each surface from Diagram 2 now has its control attached). The single entry point at the top is `create_hardened_session(cwd=...)` — the composition that the capstone installs and the scorecard measures.

```mermaid
flowchart TB
  ENTRY["create_hardened_session(cwd='/my/project')<br/>tau_plugins.hardened_session — single entry point"]:::teal

  subgraph COMP["THE COMPOSITION — ALL 5 EXTENSION POINTS ASSEMBLED"]
    direction TB

    subgraph C1["EP5 + EP1 — Tool-list + executor wrapping"]
      direction TB
      E5["inject hardened tools at CodingSession.load (session.py:286)<br/>EVERY tool's executor wrapped:"]
      E5A["scope gate (B0/B1) — every outbound action gated"]:::teal
      E5B["taint gate (B2) — pre-execution inspect + post-execution sanitize"]:::teal
      E5C["tool output tagged <untrusted> before transcript"]:::teal
      E5 --> E5A --> E5B --> E5C
    end

    subgraph C2["EP2 — Factory-level bash sandbox"]
      direction TB
      E2["hardened create_bash_tool REPLACES the factory<br/>BOTH paths (harness list + run_terminal_command) covered"]
      E2A["default-deny network egress"]:::teal
      E2B["command allowlist / denylist"]:::teal
      E2C["resource caps (CPU, memory, time)"]:::teal
      E2 --> E2A --> E2B --> E2C
    end

    subgraph C3["EP3 — Credential vault"]
      direction TB
      E3["vault replaces FileCredentialStore in BOTH sites:<br/>create_model_provider (provider_runtime.py:46)<br/>CodingSession.__init__ (session.py:253)"]
      E3A["credentials never plaintext on disk"]:::teal
      E3B["agent process cannot reach the store<br/>only the provider layer reads via CredentialReader"]:::teal
      E3C["bash denylist blocks cat ~/.tau/credentials.json + printenv"]:::teal
      E3 --> E3A --> E3B --> E3C
    end

    subgraph C4["EP4 — Intent tracker subscription"]
      direction TB
      E4["harness.subscribe(intent_tracker)<br/>purely additive, read-only"]
      E4A["ToolExecutionStartEvent → observe_request"]:::teal
      E4B["ToolExecutionEndEvent → observe_result"]:::teal
      E4C["drift detection → flag / halt / alert"]:::teal
      E4 --> E4A --> E4B --> E4C
    end
  end

  SCORECARD["THE SCORECARD<br/>InjecAgent-style injection battery:<br/>unmodified tau (before) vs hardened session (after)<br/>the delta is the defense score — the artifact Capstone B1 ships"]:::good

  ENTRY --> COMP
  COMP --> SCORECARD

  classDef teal fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
  classDef good fill:#14141f,stroke:#82e0aa,stroke-width:1.5px,color:#82e0aa
  style ENTRY fill:#14141f,stroke:#5eead4,stroke-width:2px,color:#5eead4
  style COMP fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
  style C1 fill:#14141f,stroke:#5eead4,stroke-width:1px,color:#e4e4e8
  style C2 fill:#14141f,stroke:#5eead4,stroke-width:1px,color:#e4e4e8
  style C3 fill:#14141f,stroke:#5eead4,stroke-width:1px,color:#e4e4e8
  style C4 fill:#14141f,stroke:#5eead4,stroke-width:1px,color:#e4e4e8
  style E5 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style E5A fill:#101018,stroke:#5eead4,color:#5eead4
  style E5B fill:#101018,stroke:#5eead4,color:#5eead4
  style E5C fill:#101018,stroke:#5eead4,color:#5eead4
  style E2 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style E2A fill:#101018,stroke:#5eead4,color:#5eead4
  style E2B fill:#101018,stroke:#5eead4,color:#5eead4
  style E2C fill:#101018,stroke:#5eead4,color:#5eead4
  style E3 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style E3A fill:#101018,stroke:#5eead4,color:#5eead4
  style E3B fill:#101018,stroke:#5eead4,color:#5eead4
  style E3C fill:#101018,stroke:#5eead4,color:#5eead4
  style E4 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style E4A fill:#101018,stroke:#5eead4,color:#5eead4
  style E4B fill:#101018,stroke:#5eead4,color:#5eead4
  style E4C fill:#101018,stroke:#5eead4,color:#5eead4
  style SCORECARD fill:#14141f,stroke:#82e0aa,stroke-width:1.5px,color:#82e0aa
```

> **Note**: The composition is the point. No single extension point is sufficient — B5 secures credentials at rest but the agent can still `cat` them without B7; B7 blocks exfiltration but the bash tool still runs injected commands without B2; B2 taints tool output but the JSONL still persists poison without B3. The hardened session composes all five because the surfaces overlap and the controls are coupled. The scorecard measures the delta, not any individual control — and that delta is what Capstone B1 (harden tau) and Capstone B2 (red-team tau) trade against each other.
