# Phase 33 Research — Fresh-Start Commune + Auto-Resume

**Researched:** 2026-05-17
**Domain:** Claude Code skill flow (live/SKILL.md) + Rust SessionStart hook (plugin_session_start.rs)
**Confidence:** HIGH (all gates and code paths verified against repo; predicate audit cites exact file:line citations)

## Summary

Phase 33 splits cleanly into three coordinated surfaces:

1. **FRESH branch (FRESH-01..06)** — purely SKILL.md edits. No new binary subcommand. The first-commune flow lives in `plugin/spt/skills/live/SKILL.md` as (a) a new pre-Step-1 NO-CONTEXT predicate run after `$LIVE psyche-download`, and (b) a new arm under the existing Step 2 `kind:"prompt-new"` dispatch (SKILL.md:146). Synthesis is a skill-instruction at the AskUserQuestion call site — Claude composes the summary from its own session memory + project-brief files (README.md, CLAUDE.md, .planning/STATE.md when present).
2. **AUTO `--auto` flow (AUTO-01, AUTO-02, AUTO-05..08)** — SKILL.md edits only. New "Step 0" branch dispatches on `$LIVE pick-spec` BEFORE Step 1 when `--auto` is present (or when entry was via SessionStart auto-pick or casual-language trigger). AUTO-02 H2-marker scan runs AFTER successful `$LIVE start` and uses an exact-prefix match on three headers.
3. **AUTO-03 SessionStart hook** — Rust change in `src/owl/plugin_session_start.rs`. Emits `<spt-live-auto-pick>` JSON-bearing XML between `inject_reorientation_if_needed` (line 40) and `super::resume::run_with_input(&input)` (line 45). Predicate factored into a pure `should_emit_auto_pick(&EnvSnapshot, &SourceInput) -> bool` for unit-testability. 15+/15+ test corpus delivered as `tests/auto_pick_predicate.rs` exercising the pure helper plus 3-4 integration tests proving the wire emission.

**Primary recommendation:** Three plans — (A) FRESH bundle + REQUIREMENTS/ROADMAP amendment commit, (B) AUTO `--auto` skill flow including AUTO-08 argument-hint + casual-language description rewrite, (C) AUTO-03 hook + predicate + test corpus. Plan A must land first because it amends REQUIREMENTS.md; Plans B and C are independent in code scope but share SKILL.md edits in (B) so they should land in (B)→(C) order to keep the description-frontmatter dispatcher contract clean.

## Architectural Responsibility Map

| Capability | Primary Tier | Secondary Tier | Rationale |
|------------|-------------|----------------|-----------|
| First-commune summary composition (FRESH-01/02/03) | Skill (Claude as agent) | — | D-01 locks in-skill synthesis; no binary subcommand |
| NO-CONTEXT predicate (FRESH-02) | Skill (Bash + stderr substring match) | — | `$LIVE psyche-download` already emits `NO-CONTEXT:{id}` on stderr (context.rs:449) |
| Pre-Step-1 dispatch ordering | Skill (SKILL.md text) | — | Skill must invoke psyche-download BEFORE Step 1 `$LIVE start` to detect NO-CONTEXT |
| `--auto` resume dispatch (AUTO-01) | Skill | Binary (`$LIVE pick-spec` reused) | Existing pick-spec emitter is the dispatch source; skill chooses what to do with each kind |
| Next-work surfacing (AUTO-02) | Skill (Claude scans payload) | — | H2 marker scan + Claude synthesis fallback per D-05/D-06 |
| SessionStart `<spt-live-auto-pick>` emission (AUTO-03/04) | Rust SessionStart hook | Skill (consumer) | Hook owns env/source/perch checks; skill consumes the injected block on next prompt |
| Allow-list predicate (AUTO-03) | Rust pure function | — | Pure predicate over `EnvSnapshot + SourceInput` enables unit-test corpus |
| Casual-language dispatcher routing (AUTO-05/06) | Claude Code natural-language dispatcher | Skill description frontmatter | D-07 locks dispatcher reliance; no in-skill predicate |
| AUTO-07 confirmation hop | Skill (AskUserQuestion) | — | Safety net in ALL casual paths; runs before any `$LIVE start` |
| AUTO-08 argument-hint | Skill frontmatter | — | YAML scalar value; regression-guarded by `tests/skill_hints.rs` |

## Standard Stack

### Core

| Library / Tool | Version | Purpose | Why Standard |
|----------------|---------|---------|--------------|
| `serde_json` (existing) | workspace | parse hook stdin JSON for `source`, `agent_type`, `session_id`; emit JSON inside `<spt-live-auto-pick>` payload | already in `Cargo.toml`; matches existing `inject_reorientation_if_needed` pattern (plugin_session_start.rs:120) |
| Claude Code AskUserQuestion (native) | n/a | FRESH-03 surface, AUTO-07 confirmation hop, all casual-trigger safety nets | existing SKILL.md convention (SKILL.md:144-148) |
| Claude Code Monitor tool (existing) | n/a | `$LIVE start` background launch | unchanged convention (SKILL.md:62) |
| `tests/skill_hints.rs` regression-guard | repo-local | AUTO-08 argument-hint pinned-value test | extend existing `argument_hint_keys_known_set` array (skill_hints.rs:153) |

### Alternatives Considered

| Instead of | Could Use | Tradeoff |
|------------|-----------|----------|
| In-skill synthesis (D-01) | New `$LIVE first-commune-summary <id>` binary subcommand | Locked OUT by D-01 — would require Rust to read project files which is fragile and out-of-scope; skill-instructs-Claude is the established pattern (SKILL.md:102 "absorb context after psyche-download") |
| Exact-string H2 match (D-06) | Regex fuzz match | D-06 explicitly chose exact prefix to keep skill logic trivial; planner discretion is prefix-vs-exact (recommend prefix to tolerate `## Current Focus (gen 45)` variants per specifics) |
| New env var `SPT_PSYCHE_WRAPPER` | `agent_type` field in hook stdin JSON | `SPT_PSYCHE_WRAPPER` env var DOES NOT EXIST in code (verified via `grep -r SPT_PSYCHE_WRAPPER src/`). Wrapper sessions identified via `agent_type: Some(_)` in hook stdin (plugin_session_start.rs:134, resume.rs:36). Use the existing `agent_type` gate. |

## Architecture Patterns

### Pattern 1: NO-CONTEXT detection via stderr substring

Source: `src/live/context.rs:444-452`

```rust
pub fn run_download(self_id: &str) {
    match download_payload(self_id) {
        Some(payload) => print!("{}", payload),
        None => output::live_status(
            output::S_WARN,
            &format!("NO-CONTEXT:{} (no stored context)", self_id),
        ),
    }
}
```

The token `NO-CONTEXT:{id} (no stored context)` is emitted on stderr via `live_status(S_WARN, ...)`. Skill predicate:

```bash
psyche_out=$( { $LIVE psyche-download "$ID" 2>&1 >/dev/null ; } )
if [[ "$psyche_out" == *"NO-CONTEXT:$ID"* ]]; then
  # FRESH-02 fires
fi
```

**Recommended skill instruction**: capture stderr to a variable, check for the literal substring `NO-CONTEXT:` followed by the agent id. **Exit code is unreliable** — `run_download` exits 0 on the NO-CONTEXT path (verified — no explicit `exit(1)`; `live_status` is a print-then-return helper). Substring match on stderr is the only reliable signal.

### Pattern 2: pick-spec dispatch (existing, extend)

Source: SKILL.md:136-151 (Step 2). Five existing arms: `auto`, `pick`, `prompt-new`, `all-live`, `resolve` (Step 3). The FRESH branch attaches under `kind:"prompt-new"` (FRESH-01 trigger) AND as a separate pre-Step-1 predicate (FRESH-02 trigger).

### Pattern 3: SessionStart additionalContext injection

Source: `src/owl/resume.rs:415-424`. The existing `inject_reorientation` builds a JSON envelope:

```rust
let response = serde_json::json!({
    "hookSpecificOutput": {
        "hookEventName": "SessionStart",
        "additionalContext": context
    }
});
println!("{}", out);
```

Same mechanism applies for `<spt-live-auto-pick>`. The emission MUST emit a SessionStart hookSpecificOutput envelope so Claude Code injects the XML into the assistant's prompt context. Direct `println!` on raw XML (without the hookSpecificOutput wrapper) is NOT picked up by the dispatcher.

### Anti-Patterns to Avoid

- **Don't gate AUTO-03 on `SPT_PSYCHE_WRAPPER`** — that env var doesn't exist. The correct gate is `agent_type` (hook stdin JSON field) and `OWL_HANDOFF_CHILD` (handoff successor env).
- **Don't emit `<spt-live-auto-pick>` outside the hookSpecificOutput envelope** — bare stdout println is not consumed by Claude Code as additionalContext.
- **Don't add a regex-based casual-language predicate inside SKILL.md** — D-07 locks dispatcher-reliance + description curation; AUTO-07 is the safety net.

---

## 1. FRESH Branch — In-Skill Synthesis (D-01 / D-02 / FRESH-01/02/03)

### 1.1 First-commune summary composition

**Skill-instruction language** (recommend verbatim in SKILL.md):

> "Compose a first-commune summary for the user by combining (a) what we have done together in this session so far (read your own session memory — the conversation context above), and (b) a brief snapshot of the project state (read `README.md`, `CLAUDE.md`, and `.planning/STATE.md` if present; treat their absence silently). Keep the synthesis to 4-8 sentences. Skip if either input is empty."

**Key insight**: "Session memory" in skill-instruction terms = Claude's own conversation context up to this point. The skill instruction is reading-its-own-context, not invoking a tool. The project brief is a tool-driven step (Read tool on the three named files).

**Synthesis order priority (D-01)**:
1. Session memory (primary) — this is THE first context for the new agent
2. Project brief (secondary) — README.md, CLAUDE.md, .planning/STATE.md

### 1.2 Attachment points in SKILL.md

**Current dispatch table** (SKILL.md:136-151) — Step 2 dispatches on `kind`:
- `kind:"auto"` → SKILL.md:138-142
- `kind:"pick"` → SKILL.md:144
- `kind:"prompt-new"` → SKILL.md:146 ← **FRESH-01 attaches under this arm**
- `kind:"all-live"` → SKILL.md:148-151

**Required reshape**:

**Edit site A — FRESH-02 NO-CONTEXT predicate**:

Insert a NEW block BEFORE "## Step 1: Start in background" (SKILL.md:48). New Step ordering:

```
## Step A: Probe for existing context (FRESH-02 trigger)
## Step 1: Start in background          (existing)
## Step 2: Retrieve Psyche context      (existing, but Step A may have already done it)
```

Wait — re-reading: Step 1 is `$LIVE start`, Step 2 is `$LIVE psyche-download`. To detect NO-CONTEXT, the skill must call `psyche-download` BEFORE `start` so it can branch on the result. This means a re-ordering — **move psyche-download invocation earlier OR add a separate FRESH-02 probe step** that calls psyche-download read-only first.

**Recommended edit**: insert a "## Step 0: First-commune probe (FRESH-02)" between SKILL.md:18 and SKILL.md:48 that runs `$LIVE psyche-download <id> 2>&1 >/dev/null`, checks for `NO-CONTEXT:<id>`, and if present routes into the FRESH first-commune flow before any `start`. If not NO-CONTEXT, proceeds to Step 1 normally. Step 2's existing psyche-download invocation remains (it's the post-start absorb-context step) — calling it twice is cheap (no side effects on read).

**Edit site B — FRESH-01 under `kind:"prompt-new"`**:

Inside Step 2's `prompt-new` arm (SKILL.md:146), prepend the first-commune branch BEFORE the existing starter AskUserQuestion. The current arm runs an AskUserQuestion offering starter names. FRESH-01 says: when explicit-id path uses a new id (kind:"prompt-new" returned), surface the first-commune summary first, then proceed to the existing prompt-new picker.

**Wait — re-reading FRESH-01**: "When `/spt:live` is invoked with an agent identity that pick-spec identifies as known-new (`kind:"prompt-new"`), the skill skips `$LIVE psyche-download` entirely and goes straight into the first-commune flow." This applies when a fresh agent is being created (kind:"prompt-new"); the user picks/types a new id, and BEFORE `$LIVE start` runs, the FRESH-03 prompt fires.

**Cleanest dispatch ordering**:
- `/spt:live <known-id>` → Step 0 probe (FRESH-02 if NO-CONTEXT) → Step 1 start
- `/spt:live` (no id) → Step 1 pick-spec → kind dispatch → kind:"prompt-new" arm now has TWO sub-arms:
  - User picks an existing starter or types an id → run FRESH first-commune flow → on "proceed to init" → `$LIVE start <id>` (FRESH-01 path)
  - Cancel → exit
- `/spt:live --auto` → AUTO Step 0 (see §2)

### 1.3 FRESH-03 prompt template mechanics

**Verbatim template** (locked by REQUIREMENTS.md FRESH-03):

```
Here is a summary of my first context as live agent {agent_id}. Anything I should add, or proceed to init? {first commune summary}
```

**Recommended AskUserQuestion shape — single call**:

```yaml
header: "First commune"
question: "Here is a summary of my first context as live agent {agent_id}. Anything I should add, or proceed to init?"
options:
  - label: "Proceed to init"
    description: "Start the agent now with the summary above as initial context"
body_addendum: "{first commune summary}"
```

Rationale for single AskUserQuestion (not two):
- Native "Other" free-text field on AskUserQuestion captures the "anything to add" path — user types additions inline.
- Selecting "Proceed to init" with no Other text → run `$LIVE start <id>` immediately, no additions.
- Selecting "Proceed to init" with Other text → append the Other text to the summary, then run `$LIVE start <id>` (the additions become part of the first commune sent post-start).
- Free-text in Other without selecting an option → treat as additions + proceed.

This avoids a second prompt; matches the precedent from kind:"pick" and `kind:"prompt-new"` arms that rely on native Other (SKILL.md:144 "AskUserQuestion already provides a native free-text 'Other' input").

### 1.4 NO-CONTEXT predicate — exit code vs substring

**Verified**:
- `run_download` (context.rs:444) prints via `live_status(S_WARN, ...)` then returns — no explicit `exit(1)`. Exit code is 0 on NO-CONTEXT.
- Stderr token is exactly `NO-CONTEXT:{self_id} (no stored context)` (context.rs:449).

**Recommendation**: stderr-substring check is the only reliable signal. Skill instruction:

```bash
PSYCHE_STDERR=$($LIVE psyche-download "$ID" 2>&1 >/dev/null)
if [[ "$PSYCHE_STDERR" == *"NO-CONTEXT:$ID"* ]]; then
  # FRESH-02 first-commune flow
fi
```

**Edge case — fork NO_CONTEXT confusion**: `src/live/fork.rs:56` emits `NO_CONTEXT:{id} (no {}.md in psyches/tracked/ — nothing to fork)` with an **underscore** (not hyphen). The skill predicate must match the **hyphen** form `NO-CONTEXT:` to avoid false-positives on fork-failure output. Recommend including the colon to anchor: `NO-CONTEXT:$ID`.

---

## 2. AUTO Branch — `--auto` Skill Flow (AUTO-01, AUTO-02, AUTO-07, AUTO-09)

### 2.1 New `--auto` Step placement

**Recommended structure**: insert a new top-level section between "Messaging Command Reference" (SKILL.md:20-46) and "Step 1: Start in background" (SKILL.md:48):

```markdown
## Auto-resume (--auto and SessionStart auto-pick)

When invoked with `--auto` (no positional id), OR when entering this skill via SessionStart `<spt-live-auto-pick>` block, OR via a casual-language trigger (see Description), run this Step before Step 1.

1. Run `$LIVE pick-spec` to get pick JSON.
2. Dispatch on `kind`:
   - kind:"auto" → AskUserQuestion confirmation (AUTO-07) → on yes: `$LIVE start <id>`
   - kind:"pick" → AskUserQuestion confirmation on options[0] (most-recently-active) (AUTO-07) → on yes: `$LIVE start <options[0].label>` → on no: fall through to normal Step 2 kind:"pick" dispatch
   - kind:"prompt-new" → fall through to normal Step 2 kind:"prompt-new" dispatch (D-09 no-op enhancement)
   - kind:"all-live" → fall through to normal Step 2 kind:"all-live" dispatch (D-09 no-op enhancement)
3. After `$LIVE start` returns success, run Step 2 (psyche-download) and then run the AUTO-02 next-work scan (see below).
```

Note: kind:"auto" already auto-launches in normal Step 2 WITHOUT confirmation (SKILL.md:138). For the `--auto` path, AUTO-07 mandates confirmation even on kind:"auto" because the casual-language safety-net rule applies uniformly. Spec is explicit: "Before any `$LIVE start` is executed via the casual-language path, the skill MUST issue an AskUserQuestion confirmation hop."

**Clarification on AUTO-09 (CC-1) scoping** — per phase spec language: AUTO-09 = casual-language triggers MUST always go through AUTO-07 confirmation before launching. AUTO-01's "if kind:auto, auto-launch directly" is the BARE `--auto` flag path (user explicitly typed `/spt:live --auto`). The casual-language path always confirms regardless of pick-spec kind. Recommend: ALWAYS confirm on the casual-language path (AUTO-07 mandate); for explicit `--auto`, confirm on kind:"pick"/"prompt-new"/"all-live" but allow direct launch on kind:"auto". This is a minor judgment call and the planner should align with whatever is least-surprising. Recommendation: **always confirm on every entry point** (uniform behavior, single rule, no kind-special-casing in casual path).

### 2.2 Dispatch table — `kind` mapping

| pick-spec kind | `--auto` behavior | Casual-trigger behavior |
|----------------|-------------------|--------------------------|
| `"auto"` | Confirm via AskUserQuestion → on yes: `$LIVE start <id>` (AUTO-07 uniformly applied) | Same (AUTO-07 mandatory) |
| `"pick"` | Confirm on options[0] → yes: start; no: fall through to normal Step 2 pick flow | Confirm on options[0]; same fall-through |
| `"prompt-new"` | Fall through to normal Step 2 prompt-new dispatch (D-09 no-op) | Same |
| `"all-live"` | Fall through to normal Step 2 all-live dispatch (D-09 no-op) | Same |

Optional debug breadcrumb on D-09 fall-through (per specifics): `eprintln!("AUTO_FALLTHROUGH:{}", kind)` — not user-visible. Recommend implementing as a Bash `echo` to stderr inside the skill instruction, since there's no Rust component in the AUTO `--auto` flow.

### 2.3 AUTO-07 confirmation hop — proposed AskUserQuestion shape

For kind:"pick" (most-recently-active = options[0]):

```yaml
header: "Resume agent"
question: "Resume {options[0].label} (last active {options[0].description})?"
options:
  - label: "Resume {options[0].label}"
    description: "Launch $LIVE start {options[0].label}"
  - label: "Pick a different agent"
    description: "Fall through to the normal picker"
```

For kind:"auto" (single offline agent):

```yaml
header: "Resume agent"
question: "Resume {id}?"
options:
  - label: "Resume {id}"
    description: "Launch $LIVE start {id}"
  - label: "Cancel"
    description: "Do nothing"
```

### 2.4 AUTO-02 next-work surface

Runs AFTER successful `$LIVE start` and the post-start `psyche-download` (existing SKILL.md Step 2). Skill instruction:

```
Scan the psyche-download stdout for the FIRST line that prefix-matches one of:
  ## Current Focus
  ## Next Up
  ## Next Steps

If a match is found, extract the section from the matched line to the next H2 boundary (next "## " at column 0) or EOF. Print this section verbatim to the user.

If no match: ask Claude (yourself) to synthesize a 1-2 sentence "Here's where we left off; next step looks like X" summary from the psyche-download output. Print the synthesis.
```

**D-06 prefix-vs-exact**: recommend **prefix match** (not exact) so `## Current Focus (gen 45)` matches. Specifics section explicitly says "Confirm prefix-vs-exact during planning"; prefix tolerates agent-authoring variation while still being trivial logic (single starts-with check per line).

**Placement in skill flow**: AFTER `$LIVE start` success AND AFTER `$LIVE psyche-download` (Step 2). NOT part of init-context absorption (which is the existing "absorb context" instruction at SKILL.md:101). The AUTO-02 surface is a USER-FACING display, distinct from Claude's internal context absorption — it answers "where did we leave off, and what's next" for the operator.

### 2.5 AUTO-09 (CC-1) reconciliation

Per CONTEXT.md / spec analysis:
- `kind:"auto"` is the **identity-selection** trigger (single known offline agent → auto-launch is acceptable on the bare `--auto` flag path).
- AUTO-02's "next body of work" surfacing is **post-launch display** of the resumed agent's psyche state.

There's no tension: they are sequential surfaces (auto-pick identity → launch → display next work). The unified `--auto` Step in §2.1 step (3) covers it.

---

## 3. AUTO-03 SessionStart `<spt-live-auto-pick>` Emission (CRITICAL — STATE.md research flag)

### 3a. XML Envelope Shape (Claude's Discretion)

**Verified context**: SessionStart hook outputs Claude Code additionalContext via the `hookSpecificOutput` envelope (resume.rs:415-424). `<spacetime-reorientation>` is wrapped inside this envelope's `additionalContext` string. The skill (and downstream Claude prompt) sees the XML inline in its context.

**Recommended envelope shape**:

```xml
<spt-live-auto-pick>
{"kind":"auto","id":"doyle"}
</spt-live-auto-pick>
```

Carrier: pick-spec JSON verbatim as text content. Justification:

1. **Mirrors precedent**: `<spacetime-reorientation>` uses freeform inline body text (resume.rs:384-413), not nested children. JSON-as-text-content is the simplest carrier and lets the skill parse with one `jq` call.
2. **Schema additivity**: pick-spec v1 is frozen (PICK-04 D5 schema freeze). Wrapping verbatim means the envelope automatically inherits any future kind additions without re-versioning the XML.
3. **Parser collision**: `<spacetime-reorientation>` and `<owl-active-perch>` are emitted only on `/clear`/`/compact` (resume.rs:218) or in active-perch context (resume.rs:101). `<spt-live-auto-pick>` is emitted ONLY on `source=startup` AND when no active perch is attached — so the three blocks are temporally disjoint at the SessionStart point. The tag prefix `spt-` is unique enough to avoid future drift.

**Full emission code (recommended)**:

```rust
fn emit_auto_pick(pick_spec_json: &str) {
    let context = format!(
        "<spt-live-auto-pick>\n{}\n</spt-live-auto-pick>",
        pick_spec_json
    );
    let response = serde_json::json!({
        "hookSpecificOutput": {
            "hookEventName": "SessionStart",
            "additionalContext": context
        }
    });
    println!("{}",
        serde_json::to_string(&response).unwrap_or_default()
    );
}
```

**Round-trip**:
1. Hook computes pick-spec JSON via `crate::live::pick_spec::build_spec(None)` (already pub(crate); may need `pub` promotion per Phase 18.7.1/18.8.1 precedent for cross-crate test access, but not for in-crate use).
2. Wraps in `<spt-live-auto-pick>...</spt-live-auto-pick>` inside hookSpecificOutput envelope.
3. Claude Code injects the additionalContext into the user-visible prompt as next-turn context.
4. The next user prompt (or the dispatcher's implicit invocation) sees the XML; Claude's natural-language skill dispatcher OR explicit user invocation of `/spt:live` routes into the new `--auto` Step (§2).
5. Skill parses the inner JSON, dispatches as if `$LIVE pick-spec` was just run.

**Skill consumption note**: the SKILL.md "Auto-resume" Step must include an instruction like:

> "If your context contains an `<spt-live-auto-pick>` XML block, parse the inner JSON as the pick-spec result and dispatch immediately into this Step's table (skip the `$LIVE pick-spec` invocation — it has already run)."

**Disjointness verification**:
- `<spacetime-reorientation>` emitted ONLY in `inject_reorientation_if_needed` (plugin_session_start.rs:40 → resume.rs:367) on source=clear/compact.
- AUTO-03 emitted ONLY on source=startup with no active perch.
- `inject_reorientation_if_needed` short-circuits with `return` on clear/compact (line 41). So AUTO-03 code runs AFTER that branch, only when it returned false (i.e., source≠clear/compact).
- `<owl-active-perch>` and `<owl-auto-resume>` are emitted by `super::resume::run_with_input` (resume.rs:218, 338). AUTO-03 must emit BEFORE this call so the auto-pick block appears in the same additionalContext stream — recommend emitting INSIDE `run_with_input`'s envelope OR emitting as a separate hookSpecificOutput JSON line (Claude Code merges multiple hookSpecificOutput JSON lines from a single hook). **Simpler approach**: emit `<spt-live-auto-pick>` as a separate `println!` line BEFORE the call to `super::resume::run_with_input(&input)` at line 45.

### 3b. Allow-list Predicate (CP-3 — STATE.md research flag)

**Confirmed gates from CONTEXT.md**:
1. `source = "startup"` (not `clear`/`compact`)
2. No live perch attached to current `parent_pid` (via `find_perch_by_parent_pid`)
3. No `OWL_HANDOFF_CHILD` env var
4. No `SPT_PSYCHE_WRAPPER` env var ← **DOES NOT EXIST IN CODE; see below**

**Audit of additional subagent indicators**:

| Indicator | File:Line | Purpose | Include in allow-list? | Reason |
|-----------|-----------|---------|------------------------|--------|
| `agent_type` (hook stdin JSON field) | plugin_session_start.rs:134, resume.rs:36 | Set by Claude Code when session was launched via `claude --agent` (psyche wrapper path) | **YES (REQUIRED)** | Already gated by `inject_reorientation_if_needed`; AUTO-03 must use the same gate. The actual replacement for the non-existent `SPT_PSYCHE_WRAPPER` env var. |
| `OWL_HANDOFF_CHILD` | poll.rs:41, common/handoff.rs:33 | Set when poll is a handoff successor child | **YES (CONFIRMED)** | Handoff successor is mid-binary-swap, not a fresh user-driven startup |
| `SPT_TRAMPOLINE_GUARD` | main.rs:12, common/handoff.rs:21 | Set when main.rs trampoline has already re-exec'd to newer binary | **YES** | Trampoline successor is mid-handoff; should not emit auto-pick |
| `OWL_UNDER_WRAPPER` | poll.rs:399, common/handoff.rs:40 | Set by psyche wrapper when spawning inner poll | **NO** | Inner-poll subprocess does not fire SessionStart hooks (it's not a Claude Code session). Defensive inclusion is cheap but not required. |
| `OWL_ECHO_COMMUNE` | hook_idle.rs:64, echo_commune.rs:159 | Recursion guard for echo-commune haiku subprocess | **YES (DEFENSIVE)** | Echo-commune subprocess spawns `claude -p` which may trigger SessionStart hooks in the haiku session. Reject to prevent emit into echo transcript. |
| `OWL_SKIP_RESUME` | resume.rs:28 | Explicit opt-out for scripted/automated sessions | **YES** | If user opted out of resume, they opted out of auto-pick too. |
| `CLAUDE_CODE_TEAM_NAME` | plugin_session_start.rs:138, resume.rs:23 | Set when Claude Code is running as a team member | **YES (CONFIRMED — existing gate)** | Already gated in `inject_reorientation_if_needed`; same rationale applies. |
| `SPT_PSYCHE_WRAPPER` | (none — grep returns 0 hits) | Phantom from spec; never implemented | **N/A** | Replaced by `agent_type` gate; do NOT add this env var check (would be dead code). |
| `CLAUDE_PLUGIN_ROOT` | main.rs:31 | Set by Claude Code when running as plugin (presence is positive signal — required for normal operation) | **NO** | Presence is REQUIRED for the hook to even fire; not a discriminator. |
| `OWL_SESSION_ID` | many sites | Set by `write_session_id` (same hook execution) | **NO** | Set BY this hook; presence on entry would be unusual but not a subagent indicator. |
| `CLAUDE_CONFIG_DIR` | owlery.rs:209 | ccs CLI sets this | **NO** | Indicates ccs-launched session, NOT a subagent. Should still get auto-pick. |
| Existence of live perch matching parent_pid | hook_output.rs:148 (`find_perch_by_parent_pid`) | If parent_pid has an attached live perch, this is a resume scenario, not a fresh-start scenario | **YES (CONFIRMED)** | Skill is already attached; auto-pick would be redundant/confusing. |

**FINAL recommended predicate** (Rust pseudocode, pure function):

```rust
struct EnvSnapshot {
    handoff_child: bool,         // OWL_HANDOFF_CHILD
    trampoline_guard: bool,      // SPT_TRAMPOLINE_GUARD
    echo_commune: bool,          // OWL_ECHO_COMMUNE
    skip_resume: bool,           // OWL_SKIP_RESUME
    team_name: bool,             // CLAUDE_CODE_TEAM_NAME
}

impl EnvSnapshot {
    fn capture() -> Self {
        Self {
            handoff_child: std::env::var("OWL_HANDOFF_CHILD").is_ok(),
            trampoline_guard: std::env::var("SPT_TRAMPOLINE_GUARD").is_ok(),
            echo_commune: std::env::var("OWL_ECHO_COMMUNE").is_ok(),
            skip_resume: std::env::var("OWL_SKIP_RESUME").is_ok(),
            team_name: std::env::var("CLAUDE_CODE_TEAM_NAME").is_ok(),
        }
    }
}

struct SourceInput {
    source: Option<String>,
    agent_type: Option<String>,
}

/// Pure predicate — returns true iff AUTO-03 should emit
/// <spt-live-auto-pick> on this SessionStart.
///
/// `parent_pid_has_active_perch`: caller resolves via
/// `hook_output::find_perch_by_parent_pid().is_some()`.
fn should_emit_auto_pick(
    env: &EnvSnapshot,
    src: &SourceInput,
    parent_pid_has_active_perch: bool,
) -> bool {
    // Gate 1: source MUST be "startup" (not clear/compact)
    match src.source.as_deref() {
        Some("startup") => {},
        _ => return false,
    }
    // Gate 2: agent_type absent (rejects psyche-wrapper sessions)
    if src.agent_type.is_some() {
        return false;
    }
    // Gate 3: no handoff/trampoline successor
    if env.handoff_child || env.trampoline_guard {
        return false;
    }
    // Gate 4: no echo-commune recursion
    if env.echo_commune {
        return false;
    }
    // Gate 5: no resume opt-out / team member
    if env.skip_resume || env.team_name {
        return false;
    }
    // Gate 6: no active perch already attached to this parent_pid
    if parent_pid_has_active_perch {
        return false;
    }
    true
}
```

**Silent rejection contract**: when any gate fires, the predicate returns false; the caller emits **nothing** (no stderr, no stdout) before delegating to `super::resume::run_with_input`. CONTEXT.md is explicit: "the allow-list rejects silently inside subagent/wrapper sessions (no stderr leakage)."

### 3c. Test Corpus Design (CP-4 — STATE.md research flag, 15+/15+)

**Test mechanic recommendation**: pure-predicate unit tests against `should_emit_auto_pick(&EnvSnapshot, &SourceInput, bool) -> bool`. The function is side-effect-free and takes all inputs by value. No subprocess spawning, no env mutation, no SPT_HOME setup. ~15µs per test instead of multi-second subprocess fixtures.

**Factoring**: extract `should_emit_auto_pick` as a module-level pub(crate) function in `plugin_session_start.rs`. The `run()` entry calls it with `EnvSnapshot::capture()`, parsed `SourceInput`, and `find_perch_by_parent_pid().is_some()`. Tests construct `EnvSnapshot` and `SourceInput` directly without env mutation.

**Recommended test file**: `tests/auto_pick_predicate.rs` (new integration test) — but actually since the predicate is pure and lives in `plugin_session_start.rs`, the cleanest home is the existing `#[cfg(test)] mod` inside `plugin_session_start.rs`. Place the corpus in a new `auto_pick_predicate_tests` submodule. This matches the `dispatch_tests` precedent (plugin_session_start.rs:890).

**Should-fire corpus (15+)**:

| # | Source | agent_type | OWL_HANDOFF_CHILD | SPT_TRAMPOLINE_GUARD | OWL_ECHO_COMMUNE | OWL_SKIP_RESUME | CLAUDE_CODE_TEAM_NAME | parent_pid_has_perch | Expected |
|---|--------|-----------|--------------------|----------------------|------------------|------------------|------------------------|----------------------|----------|
| F1 | startup | None | false | false | false | false | false | false | true (vanilla fresh start) |
| F2 | startup | None | false | false | false | false | false | false | true (no perches anywhere) |
| F3 | startup | None | false | false | false | false | false | false | true (perches exist for OTHER parent_pids) |
| F4 | startup | None | false | false | false | false | false | false | true (CLAUDE_CONFIG_DIR set — ccs session; not in EnvSnapshot, so irrelevant) |
| F5 | startup | None | false | false | false | false | false | false | true (after `claude` exits and user re-opens — fresh ppid) |
| F6 | startup | None | false | false | false | false | false | false | true (long-idle live perches for stale ppids exist) |
| F7 | startup | None | false | false | false | false | false | false | true (OWL_SESSION_ID empty on entry — set by THIS hook) |
| F8 | startup | None | false | false | false | false | false | false | true (CLAUDE_PLUGIN_ROOT set — required for hook to fire) |
| F9 | startup | None | false | false | false | false | false | false | true (HOME set / USERPROFILE set — normal) |
| F10 | startup | None | false | false | false | false | false | false | true (psyche perches exist but no live SELF perch on this ppid) |
| F11 | startup | None | false | false | false | false | false | false | true (only OFFLINE perches exist for this ppid — they don't count as "attached") |
| F12 | startup | None | false | false | false | false | false | false | true (after binary deploy completes — no handoff env vars residual) |
| F13 | startup | None | false | false | false | false | false | false | true (Windows; LOCALAPPDATA path resolves; no diff) |
| F14 | startup | None | false | false | false | false | false | false | true (Unix; HOME path resolves; no diff) |
| F15 | startup | None | false | false | false | false | false | false | true (SPT_HOME override set — no diff for predicate) |

Note: many "should-fire" cases collapse to the same predicate input. The test corpus value comes from naming each behavioral scenario (even if the boolean inputs are identical) so future regressions on the predicate (e.g., someone adds a new gate that catches "ccs session") are caught with descriptive failure messages.

**Should-NOT-fire corpus (15+)**:

| # | Source | agent_type | OWL_HANDOFF_CHILD | SPT_TRAMPOLINE_GUARD | OWL_ECHO_COMMUNE | OWL_SKIP_RESUME | CLAUDE_CODE_TEAM_NAME | parent_pid_has_perch | Expected | Rationale |
|---|--------|-----------|--------------------|----------------------|------------------|------------------|------------------------|----------------------|----------|-----------|
| N1 | clear | None | false | false | false | false | false | false | false | source=clear short-circuits |
| N2 | compact | None | false | false | false | false | false | false | false | source=compact short-circuits |
| N3 | resume | None | false | false | false | false | false | false | false | unknown source token |
| N4 | None | None | false | false | false | false | false | false | false | source missing |
| N5 | startup | Some("psyche") | false | false | false | false | false | false | false | psyche-wrapper agent session |
| N6 | startup | Some("haiku") | false | false | false | false | false | false | false | any agent_type — wrapper indicator |
| N7 | startup | None | true | false | false | false | false | false | false | OWL_HANDOFF_CHILD set (handoff successor) |
| N8 | startup | None | false | true | false | false | false | false | false | SPT_TRAMPOLINE_GUARD set (trampoline successor) |
| N9 | startup | None | true | true | false | false | false | false | false | both handoff vars (defensive combination) |
| N10 | startup | None | false | false | true | false | false | false | false | OWL_ECHO_COMMUNE set (echo recursion guard) |
| N11 | startup | None | false | false | false | true | false | false | false | OWL_SKIP_RESUME set (scripted session) |
| N12 | startup | None | false | false | false | false | true | false | false | CLAUDE_CODE_TEAM_NAME set (team member) |
| N13 | startup | None | false | false | false | false | false | true | false | live perch already attached to this ppid (skill already running) |
| N14 | startup | Some("x") | true | false | false | false | false | false | false | agent_type + handoff (combo) |
| N15 | startup | None | false | false | false | false | false | true | false | active perch + clean env (already-attached, should not double-emit) |
| N16 | clear | None | false | false | false | false | false | true | false | clear + active perch (handled by inject_reorientation path) |
| N17 | startup | None | false | false | true | false | false | true | false | echo recursion + active perch (defense in depth) |

Plus 3-4 integration tests proving the wire emission:
- Integration test: subprocess spawn of `owl plugin-session-start` with stdin `{"source":"startup","session_id":"deadbeef..."}` and clean env → asserts stdout contains `<spt-live-auto-pick>` and hookSpecificOutput envelope shape.
- Integration test: same with `agent_type:"psyche"` → asserts stdout does NOT contain `<spt-live-auto-pick>`.
- Integration test: same with `OWL_HANDOFF_CHILD=1` → asserts no `<spt-live-auto-pick>` in stdout AND no stderr leakage (silent rejection).
- Integration test: source=clear with active perch on ppid → asserts existing `<spacetime-reorientation>` block emitted AND no `<spt-live-auto-pick>` (disjointness).

Integration tests need `SPT_TRAMPOLINE_GUARD=1` to bypass the trampoline re-exec (per Phase 28 Plan 04 precedent: "integration tests subprocess-invoking owl plugin-session-start must set SPT_TRAMPOLINE_GUARD=1"). BUT — `SPT_TRAMPOLINE_GUARD` is also in the allow-list rejection list (N8). This is a **direct conflict**: the integration test cannot both bypass the trampoline AND assert positive emission.

**Resolution**: pure unit tests are the primary verification surface (no subprocess, no env conflicts). The 3-4 integration tests use `cargo run` from `target/<profile>/owl.exe` directly (which won't re-exec to the installed plugin cache when launched from cargo's target dir — verified by handoff.rs:67-80 which requires `~/.claude/plugins/installed_plugins.json` resolution + path-existence check). For positive-emission integration tests, do NOT set `SPT_TRAMPOLINE_GUARD=1`; rely on the from-cargo-target launch path skipping trampoline naturally (current_exe lives in `target/`, not in a versioned plugin cache dir, so trampoline early-returns at plugin_session_start.rs:486 "Not running from a versioned plugin cache dir").

---

## 4. Casual-Language Trigger Plumbing (D-07 / D-08)

### 4.1 Skill description frontmatter rewrite

**Current** (SKILL.md:1-8):

```yaml
---
name: live
description: |
  Start as a live Self agent with Psyche. Use when the user says "live as",
  "start live", "go live", or wants a persistent agent with Psyche companion.
argument-hint: "<id> [--period <seconds>]"
allowed-tools: [Bash, Read, Monitor]
---
```

**Proposed**:

```yaml
---
name: live
description: |
  Start, resume, or manage a live Self agent with Psyche companion.

  EXPLICIT START phrases: "live as", "start live", "go live", "start a live agent".

  AUTO-RESUME phrases (route to --auto, resumes most-recently-active live agent):
    - "continue live work"
    - "resume live work"
    - "continue live agent"
    - "resume live agent"
    - "live agent continue"
    - "live agent resume"
    - "live work continue"
    - "live work resume"

  Does NOT route here: "keep going", "resume work", "continue" (bare) — these are
  too ambiguous and may refer to unrelated tasks. Require the user to include
  both "live" AND ("agent" or "work") for auto-resume routing.
argument-hint: "[<id>] [--period <seconds>] [--auto]"
allowed-tools: [Bash, Read, Monitor]
---
```

### 4.2 Eight accepted phrases (verbatim from D-08)

All eight contain BOTH `live` AND (`agent` or `work`):

1. `continue live work`
2. `resume live work`
3. `continue live agent`
4. `resume live agent`
5. `live agent continue`
6. `live agent resume`
7. `live work continue`
8. `live work resume`

### 4.3 Three explicitly rejected phrases

1. `keep going`
2. `resume work`
3. `continue` (bare)

These must NOT route into `--auto`. Description must call them out as non-triggers so the dispatcher learns the negative example.

### 4.4 Dispatcher consumption (brief)

Claude Code's natural-language skill dispatcher reads the skill's `description` field and matches user prompts against it. Specificity in the description (explicit phrase listings) increases match precision. No code-side mechanism — pure prompt-engineering of the dispatcher's matching corpus.

### 4.5 AUTO-07 safety net always fires

Confirmed by skill design: the new "Auto-resume" Step (§2.1) ALWAYS routes through AskUserQuestion confirmation before any `$LIVE start`. Casual-language path enters via the same Step, so AUTO-07 is enforced uniformly. Even if dispatcher false-positives on "let's continue the live demo" (a non-trigger phrase that happened to contain "live"), the user sees a confirmation prompt and can Cancel cleanly.

---

## 5. AUTO-08 Argument-Hint Update

### 5.1 YAML quoting (Phase 32 HINT-04 rules)

Per `tests/skill_hints.rs:120-138` (`argument_hint_values_quote_yaml_special_chars`):
- Values containing `|`, `#`, `{`, `}`, `[`, `]`, or unquoted `:` MUST be double-quoted (or single-quoted).
- `[--auto]` contains `[` and `]` → quotes REQUIRED.
- `<id>` contains no special chars → quotes optional but conventionally used.

### 5.2 Option choice

**Option A**: `argument-hint: "<id> [--period <seconds>] | [--auto]"`
- Uses `|` as a disjunction symbol meaning "either an id (with optional period) OR --auto".
- Accurate model — `--auto` and positional `<id>` are mutually exclusive (one means "resume the recent agent", the other names a specific agent).
- Contains `|`, `[`, `]` → must be double-quoted (already is).

**Option B**: `argument-hint: "[<id>] [--period <seconds>] [--auto]"`
- All bracketed = all optional, no exclusion shown.
- Slightly less accurate (a user could read it as "id and --auto can be combined" — they can't meaningfully).

**Recommendation: Option A**: `argument-hint: "<id> [--period <seconds>] | [--auto]"`

Rationale: `--auto` IS mutually exclusive with positional `<id>` per AUTO-01 ("`/spt:live --auto` (no positional arg)"). The `|` correctly conveys the disjunction. Phase 32 D-12/D-13 already established that `argument-hint` is a discovery surface (not a parser spec), so semantic accuracy is worth the explicit disjunction symbol.

### 5.3 Regression-guard test extension

`tests/skill_hints.rs:147-199` (`argument_hint_keys_known_set`) has a pinned-value `expected` array. AUTO-08 update requires adding `live` to the array:

```rust
let expected: &[(&str, &str)] = &[
    ("list-ready", "[--all] [--offline] [--here]"),
    ("list-live", "[--all] [--offline] [--here]"),
    ("list-psyche", "[--all] [--offline] [--here]"),
    ("commune", ""),
    ("psyche-download", "[<id>]"),
    ("whoami", ""),
    ("live", "<id> [--period <seconds>] | [--auto]"),  // AUTO-08
];
```

The two existing test invariants (`every_skill_has_argument_hint` and `argument_hint_values_quote_yaml_special_chars`) require NO changes — they already cover the `live` skill generically. Only the pinned-values test needs the new entry.

---

## 6. REQUIREMENTS.md + ROADMAP.md Amendment Commit (D-03 / D-04)

### 6.1 REQUIREMENTS.md edits

**Strike entirely** (REQUIREMENTS.md:52-53):
- Line 52: `- [ ] **FRESH-04**: Single-fire sentinel (`.first-commune-sent` or equivalent on-disk marker) prevents double-fire across `/clear`, wrapper restart, binary handoff.`
- Line 53: `- [ ] **FRESH-05**: `clear-psyche` lineage is distinguished from "never had context" via a `cleared_at` metadata field so first-commune does NOT re-fire for an intentionally cleared psyche.`

Also strike the corresponding rows in the Traceability table (REQUIREMENTS.md:140-141):
- `| FRESH-04 | Phase 33 | Pending |`
- `| FRESH-05 | Phase 33 | Pending |`

Update Coverage line (REQUIREMENTS.md:162) from `FRESH 6` to `FRESH 4` and total from 41 to 39 (if the v1 total is recomputed).

**Reword** (REQUIREMENTS.md:54) FRESH-06:

Original:
> `- [ ] **FRESH-06**: Fork (`$LIVE fork`) and revive paths suppress first-commune (existing psyche context inherited).`

Replacement (per specifics § D-04):
> `- [ ] **FRESH-06**: Fork (`$LIVE fork`) and revive paths bypass the first-commune branch naturally — both paths route through `$LIVE psyche-download` first, which returns content (not NO-CONTEXT) for any forked or revived identity. No active suppression mechanism required.`

### 6.2 ROADMAP.md edits

Phase 33 success criterion 2 (ROADMAP.md:629):

Original:
> `2. The first-commune prompt fires exactly once per identity across `/clear`, wrapper restart, and binary handoff — guarded by an on-disk sentinel and a `cleared_at` metadata distinction so an intentionally cleared psyche is NOT re-prompted; fork and revive paths suppress it entirely`

Replacement:
> `2. The first-commune prompt fires only when `$LIVE psyche-download` returns NO-CONTEXT (or when pick-spec returns `kind:"prompt-new"`) — a natural-transition contract: once any commune lands (first-commune answer, regular commune, or auto-fired echo-commune per Phase 29 AUTO-EC), psyche-download returns content and the predicate stops firing automatically. An intentionally cleared psyche correctly re-prompts because the user's `clear-psyche` action is itself a request to start fresh. Fork and revive paths bypass naturally because both populate psyche-md before any first-commune check.`

### 6.3 Amendment commit pattern (mirrors Phase 31 D-11 / Phase 32 LIST-04)

Single atomic commit at the START of Plan 01:
- Subject: `docs(33): strike FRESH-04/05; reword FRESH-06; revise ROADMAP SC#2 per D-03/D-04`
- Files staged: `.planning/REQUIREMENTS.md`, `.planning/ROADMAP.md`
- Body: brief rationale citing CONTEXT.md D-03 and D-04.

This must land BEFORE any other Plan 01 task so the downstream `/gsd-verify-work 33` doesn't fail against the now-removed FRESH-04/05 IDs.

---

## 7. Plan Split Recommendation

**Confirmed: 3 plans**, matching CONTEXT.md's "likely 3 plans" guidance with the explicit ordering and dependency contract below.

### Plan 01: FRESH bundle + REQUIREMENTS/ROADMAP amendment

**Tasks (estimated 3-4)**:
1. **(Wave 0 commit)** REQUIREMENTS.md + ROADMAP.md amendment (per §6).
2. SKILL.md edit: insert "Step 0: First-commune probe (FRESH-02)" block between SKILL.md:18 and SKILL.md:48; instructions per §1.2/§1.4.
3. SKILL.md edit: modify `kind:"prompt-new"` arm at SKILL.md:146 to prepend the FRESH-01 first-commune flow; instructions per §1.2/§1.3.
4. (Optional) Doc-only commit updating `## Edge Cases` section to mention the first-commune flow exists.

**Atomic commits**: 2-3 (amendment + skill edits combined into one or two skill commits).
**File scope**: 3 files (`REQUIREMENTS.md`, `ROADMAP.md`, `plugin/spt/skills/live/SKILL.md`).
**Cross-plan dependency**: Plan 02 SKILL.md edits depend on Plan 01's Step 0 already existing (the new `--auto` Step needs to sit alongside Step 0 cleanly).

### Plan 02: AUTO `--auto` skill flow + description rewrite + AUTO-08 argument-hint

**Tasks (estimated 4-5)**:
1. SKILL.md edit: rewrite `description:` frontmatter per §4.1 (D-07/D-08 casual-language triggers).
2. SKILL.md edit: update `argument-hint:` frontmatter per §5.2 (AUTO-08).
3. SKILL.md edit: insert new "Auto-resume" Step between SKILL.md:46 and SKILL.md:48 per §2.1 (AUTO-01, AUTO-07, AUTO-09).
4. SKILL.md edit: insert AUTO-02 next-work scan instruction after Step 2 per §2.4.
5. `tests/skill_hints.rs` edit: add `live` to pinned-values `expected` array per §5.3 (HINT-05 regression guard for AUTO-08).

**Atomic commits**: 2-3 (frontmatter + skill body + test).
**File scope**: 2 files (`plugin/spt/skills/live/SKILL.md`, `tests/skill_hints.rs`).
**Cross-plan dependency**: depends on Plan 01 (description rewrite + new Step 0/Auto-resume sit alongside FRESH Step 0). Independent from Plan 03 in source-file scope.

### Plan 03: AUTO-03 SessionStart hook + predicate + test corpus

**Tasks (estimated 4-5)**:
1. Add `should_emit_auto_pick`, `EnvSnapshot::capture`, `SourceInput` parsing in `src/owl/plugin_session_start.rs` (pure helper, no side effects).
2. Wire emission call site in `plugin_session_start::run()` between line 40 (`inject_reorientation_if_needed`) and line 45 (`super::resume::run_with_input`); invoke `crate::live::pick_spec::build_spec(None)` and wrap output per §3a.
3. Add `auto_pick_predicate_tests` `#[cfg(test)]` submodule inside `plugin_session_start.rs` with the 15+ should-fire and 17+ should-NOT-fire cases per §3c.
4. Add `tests/auto_pick_integration.rs` (or extend an existing `tests/plugin_session_start_*.rs` if one exists) with 3-4 subprocess-based wire-emission tests per §3c.
5. (Optional) `pub(crate)` → `pub` promotion of `build_spec` if integration tests need cross-crate access (matches Phase 18.7.1 / 18.8.1 precedent).

**Atomic commits**: 3-4 (helper + wire + unit corpus + integration corpus).
**File scope**: 2-3 files (`src/owl/plugin_session_start.rs`, `tests/auto_pick_integration.rs` (new), possibly `src/live/pick_spec.rs` for `pub` promotion).
**Cross-plan dependency**: independent from Plans 01/02 at the file-scope level. Skill consumer of `<spt-live-auto-pick>` is in Plan 02's "Auto-resume" Step — so Plan 02 should land BEFORE Plan 03 OR Plan 03's emission can be gated behind a feature flag for the half-deployed window. Recommend Plan 02 → Plan 03 ordering for clean dispatcher contract.

### Cross-plan dependency summary

```
Plan 01 (amendments + FRESH SKILL edits)
  ↓
Plan 02 (AUTO --auto SKILL flow + description rewrite + argument-hint + test)
  ↓
Plan 03 (AUTO-03 Rust hook + predicate + 30+ test corpus + integration tests)
```

Plan 03 could theoretically interleave with Plan 02 — emission code is independent of skill consumer — but staging them sequentially avoids the half-state where the hook emits a block the skill doesn't yet know to consume.

---

## 8. Pitfalls / Open Questions / Risks

### 8.1 Dispatcher false-positive on casual triggers

**Failure mode**: a user paste of a non-trigger phrase like "Let me continue the live demo from yesterday" contains "live" and could match. Dispatcher routes to `/spt:live --auto`.

**Mitigation (verified)**: AUTO-07 confirmation hop fires before any `$LIVE start`. User sees a clean AskUserQuestion ("Resume doyle (last active 3h ago)?") and Cancels. Skill exits silently. **No agent is launched without explicit user confirmation.**

### 8.2 AUTO-03 hook firing in psyche-wrapper sessions

**Failure mode**: silent-rejection contract is violated — even one stderr line of debug output leaks into the wrapper's transcript and may be misinterpreted as a directive.

**Mitigation**: the recommended predicate (§3b) returns false WITHOUT any side effects. The call site MUST be:

```rust
if should_emit_auto_pick(&env, &src, parent_has_perch) {
    emit_auto_pick(&pick_json);  // wraps in hookSpecificOutput envelope
}
// fall through to super::resume::run_with_input — no else branch, no logging
```

No `eprintln!` anywhere in the rejected-path. Unit test should assert that rejecting predicate inputs produce no stdout AND no stderr.

### 8.3 Composition: AUTO-03 → `--auto` → NO-CONTEXT → FRESH-02

**Scenario**: SessionStart fires `<spt-live-auto-pick>` with `kind:"auto", id:"doyle"`. Skill enters `--auto` Step (§2.1). User confirms. `$LIVE start doyle` runs. Step 2 `psyche-download doyle` runs. But — what if doyle has NO-CONTEXT?

**Expected flow**:
1. SessionStart emits auto-pick (kind:"auto" means doyle is the single known offline agent).
2. Skill enters Auto-resume Step, confirms via AskUserQuestion → user says yes.
3. `$LIVE start doyle` succeeds.
4. Step 2 `psyche-download doyle` returns NO-CONTEXT.
5. Skill instruction: at THIS point, the FRESH-02 trigger should fire because we're in a freshly-started agent with no psyche content. However, FRESH-02 in §1.2 is gated to run BEFORE Step 1 start. So there's an ordering edge case.

**Resolution**: re-read FRESH-02 spec — "When `/spt:live` proceeds with an existing identity but `$LIVE psyche-download` returns `NO-CONTEXT`, the skill enters the same first-commune flow." This implies FRESH-02 can fire AFTER start as well, as long as psyche-download returned NO-CONTEXT. The cleanest answer:

- Run psyche-download ONCE (the existing Step 2 invocation).
- If it returns NO-CONTEXT, branch into FRESH-02 first-commune flow (synthesize summary, AskUserQuestion, user "proceeds to init" — i.e., sends the synthesized summary as the first commune via `$LIVE commune <id>`).
- This works equally well post-start or pre-start; the FRESH-02 predicate doesn't care about ordering.

**Recommended planner instruction**: collapse the "Step 0 first-commune probe" (§1.2) into the existing Step 2 psyche-download invocation, so the FRESH-02 branch fires at a single ordering point regardless of whether the user got here via `--auto` (post-start) or explicit `/spt:live <id>` (pre/post-start). Simplification: only ONE psyche-download call site, and the NO-CONTEXT check + first-commune branch happens AFTER it.

This means moving FRESH-02 BACK into Step 2 (not a separate pre-Step-1 Step 0). FRESH-01 (kind:"prompt-new" arm in pick-spec dispatch) stays pre-start because in that path there's no agent yet to download from. **Planner: pick one ordering decision and lock it.**

### 8.4 `--auto` + `--period`

Per deferred items (CONTEXT.md). Recommend: `--auto` accepts and forwards `--period <seconds>` to `$LIVE start`. No surprise interaction with SessionStart auto-pick path (the auto-pick block doesn't carry --period; that's a CLI flag).

### 8.5 Idempotency: SessionStart auto-pick + immediate user `/spt:live --auto`

**Scenario**: hook emits `<spt-live-auto-pick>` block at SessionStart. Block sits in Claude's prompt context. User then immediately types `/spt:live --auto` themselves.

**Expected behavior**: both paths route into the SAME Auto-resume Step (§2.1). The skill instruction for the `<spt-live-auto-pick>` path says "skip the `$LIVE pick-spec` invocation — JSON is already provided". The user-invoked `--auto` path runs `$LIVE pick-spec` fresh. Both then dispatch on `kind`, both fire AUTO-07 confirmation, both await user yes/no.

**Risk**: two AskUserQuestion prompts could pile up. But Claude Code processes one prompt at a time; the second `--auto` invocation only fires AFTER the user resolves the first AskUserQuestion. If user confirmed, $LIVE start ran; if they ran it again, `$LIVE start <id>` returns `COLLISION` (existing edge case at SKILL.md:204) and the skill surfaces the error. No double-launch.

**Mitigation**: existing COLLISION guard in `$LIVE start` is the natural safety. No new code required.

### 8.6 Open question: AUTO-02 H2 scan timing relative to Claude context absorption

Step 2 already instructs Claude to "absorb context" from psyche-download. AUTO-02 wants the skill to ALSO surface a user-visible "next work" pane. Are these two distinct artifacts or one combined surface?

**Recommendation**: keep them distinct. Claude's absorption is silent (internal context). AUTO-02 is an explicit `print` to user-visible terminal: "Here's where you left off: {section}". The order: absorb → AUTO-02 print. Single instruction in SKILL.md tells Claude to do both sequentially.

---

## Runtime State Inventory

| Category | Items Found | Action Required |
|----------|-------------|------------------|
| Stored data | None — Phase 33 introduces NO new on-disk artifacts (D-03 strikes the sentinel) | none |
| Live service config | None — no external service touched | none |
| OS-registered state | None — no scheduler/launchd/systemd changes | none |
| Secrets / env vars | None — no new env vars introduced; predicate READS existing env vars (OWL_HANDOFF_CHILD, SPT_TRAMPOLINE_GUARD, OWL_ECHO_COMMUNE, OWL_SKIP_RESUME, CLAUDE_CODE_TEAM_NAME) all already in code | none |
| Build artifacts | None — pure source/skill edits; existing `cargo build --release` produces updated owl.exe; DEPLOY.ps1 is end-of-milestone (Phase 34) per ROADMAP cadence | none |

**Nothing found in category**: stated explicitly above.

---

## Common Pitfalls

### Pitfall 1: Stderr substring drift

If the `NO-CONTEXT:` literal in context.rs:449 ever changes (e.g., to `NO-CONTEXT-FOUND:`), the skill's stderr-substring check silently breaks (FRESH-02 stops firing). **Mitigation**: add a unit test in `src/live/context.rs` that asserts the exact stderr format on the NO-CONTEXT path. Cross-reference comment in SKILL.md pointing to context.rs:449 line citation so future refactors notice.

### Pitfall 2: hookSpecificOutput double-emission

If Plan 03 emits `<spt-live-auto-pick>` as a bare `println!` AND `super::resume::run_with_input` also emits a hookSpecificOutput JSON line, Claude Code may receive two JSON lines on stdout. The hook spec accepts multiple JSON lines (they're concatenated). Verify by checking the resume.rs emission shape — it only emits via `inject_reorientation` (resume.rs:415) which is gated on clear/compact (and we've already short-circuited THAT path before reaching AUTO-03 emit). The `run_with_input` happy path does `println!` on raw XML (resume.rs:51, 100), NOT inside a hookSpecificOutput envelope. **Confirm with a quick test** that bare-XML println alongside hookSpecificOutput envelope JSON is well-formed input for Claude Code's hook consumer. If not, the AUTO-03 emission must be merged into the additionalContext stream of an existing or new envelope.

### Pitfall 3: pick-spec build_spec called twice per SessionStart

If AUTO-03 calls `build_spec(None)` AND the user immediately runs `/spt:live`, pick-spec runs twice. This is read-only (no side effects) so it's safe — but the agent-ids cache hot path can be measured (~ms). Not a real perf concern. No mitigation needed.

### Pitfall 4: SKILL.md edit conflicts between Plan 01 and Plan 02

Both plans edit `plugin/spt/skills/live/SKILL.md`. Recommend single-author serial execution (no parallel waves) so the edits don't conflict mid-file. The Plan 02 "Auto-resume" Step should be inserted ABOVE the Plan 01 "Step 0" probe (cleanest layout: Auto-resume first, then Step 0 first-commune, then Step 1 start, then Step 2 absorb). Plan ordering ensures this.

### Pitfall 5: `argument-hint` quote regression

If a future edit strips the quotes from `<id> [--period <seconds>] | [--auto]`, the YAML parser will misread the `|` as a multi-line scalar indicator and break Claude Code skill loading. `tests/skill_hints.rs:102` catches this; ensure the test runs in CI as part of `cargo test --lib`.

---

## Validation Architecture

Per `.planning/config.json` (assumed `workflow.nyquist_validation` enabled — default).

### Test Framework

| Property | Value |
|----------|-------|
| Framework | `cargo test` (rust workspace) |
| Config file | `Cargo.toml` (workspace root); per-test crate setup via `[dev-dependencies]` |
| Quick run command | `cargo test --lib auto_pick_predicate_tests` (Plan 03 unit corpus) |
| Full suite command | `cargo test --release -- --test-threads=1` (mirrors existing repo convention for state-dependent tests) |

### Phase Requirements → Test Map

| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|--------|----------|-----------|-------------------|-------------|
| FRESH-01 | first-commune fires under kind:"prompt-new" | manual (skill-level — no Rust testable surface) | n/a — UAT step |  manual |
| FRESH-02 | NO-CONTEXT stderr triggers first-commune | unit (assert stderr token format) | `cargo test --lib live::context::tests::no_context_token_format` | ❌ Wave 0 (new test) |
| FRESH-03 | prompt template verbatim | manual (skill text) | n/a | manual |
| FRESH-06 | fork/revive bypass via psyche-download | regression — existing tests in `src/live/fork.rs`, `src/live/start.rs` already cover psyche-download return shape | `cargo test --lib live::fork::tests::` | ✅ exists |
| AUTO-01 | `--auto` dispatches on kind | manual (skill-level) | n/a | manual |
| AUTO-02 | H2 scan + Claude synthesis fallback | manual (skill instruction) | n/a | manual |
| AUTO-03 | hook emits auto-pick block under predicate | unit (15+ should-fire/should-NOT-fire on `should_emit_auto_pick`) | `cargo test --lib plugin_session_start::auto_pick_predicate_tests` | ❌ Wave 0 (new submodule) |
| AUTO-03 wire | hook actually emits expected JSON envelope | integration (subprocess spawn) | `cargo test --test auto_pick_integration` | ❌ Wave 0 (new integration test file) |
| AUTO-04 | silent rejection — no stderr | unit (capture stderr, assert empty) | covered by AUTO-03 predicate tests | ❌ Wave 0 |
| AUTO-05/06 | casual-language description includes 8 phrases, rejects 3 | unit (parse SKILL.md frontmatter, assert phrase presence/absence) | extension to `tests/skill_hints.rs` | ❌ Wave 0 (new test in skill_hints.rs) |
| AUTO-07 | confirmation hop before $LIVE start | manual (skill instruction) | n/a | manual |
| AUTO-08 | argument-hint updated to "<id> [--period <seconds>] | [--auto]" | unit (pinned-value regression guard) | `cargo test --test skill_hints argument_hint_keys_known_set` | ✅ exists (extend `expected` array) |

### Sampling Rate

- **Per task commit**: `cargo test --lib plugin_session_start::auto_pick_predicate_tests` (~30s)
- **Per wave merge**: `cargo test --release -- --test-threads=1` (full suite)
- **Phase gate**: full suite green before `/gsd-verify-work 33`

### Wave 0 Gaps

- [ ] `src/owl/plugin_session_start.rs` — `auto_pick_predicate_tests` submodule (new) — covers AUTO-03 + AUTO-04
- [ ] `tests/auto_pick_integration.rs` (new) — wire-emission integration tests (3-4 subprocess cases)
- [ ] `tests/skill_hints.rs` — new test asserting description frontmatter contains the 8 accepted phrases and explicitly mentions the 3 rejected phrases (AUTO-05/06 regression guard)
- [ ] `tests/skill_hints.rs` — extend `expected` array with `live` argument-hint pinned value (AUTO-08)
- [ ] `src/live/context.rs::tests` — new test pinning the `NO-CONTEXT:{id} (no stored context)` stderr token format (FRESH-02 contract anchor)

No new framework install required — `cargo test` is the established test runner.

---

## Security Domain

`security_enforcement` assumed enabled (absent = enabled). All AUTO-03 / AUTO-04 work involves a Claude Code SessionStart hook that runs at the trust boundary of `claude` startup — and emits user-visible XML that the next prompt's natural-language dispatcher may route on.

### Applicable ASVS Categories

| ASVS Category | Applies | Standard Control |
|---------------|---------|-----------------|
| V2 Authentication | no | hook runs in trusted local user context |
| V3 Session Management | no | no session/cookie handling |
| V4 Access Control | yes | predicate IS the access control for emission — must reject silently in subagent/wrapper contexts (AUTO-04) |
| V5 Input Validation | yes | hook stdin JSON parsed via serde_json; `source` matched against literal token set; pick-spec JSON output validated via `serde_json::to_string` (no manual concatenation) |
| V6 Cryptography | no | no crypto |

### Known Threat Patterns for SessionStart Hook

| Pattern | STRIDE | Standard Mitigation |
|---------|--------|---------------------|
| XML injection into skill consumer via pick-spec id field | Tampering | id is validated by `validate_agent_id` upstream of pick-spec (`id_validate.rs`); regex `[A-Za-z0-9_-]{1,64}` — no XML special chars possible |
| stderr leakage into subagent transcript misinterpreted as directive | Spoofing / Tampering | predicate (§3b) returns false WITHOUT any side effects; unit test asserts empty stderr on rejection paths |
| Predicate bypass via env var manipulation | Tampering | predicate is pure; tests cover all subagent-indicator combinations (15+ rejection cases) |
| TOCTOU between predicate check and emit (live perch attached) | Tampering | predicate snapshot is taken once at hook entry; emit follows immediately — no async window |
| pick-spec JSON injection via maliciously-named perch dir | Tampering | serde_json::to_string handles escaping; perch ids are id_validate-gated; no manual JSON concatenation |

---

## Code Examples

Verified patterns from official sources:

### NO-CONTEXT detection (skill bash idiom)

```bash
# Source: src/live/context.rs:449
PSYCHE_STDERR=$($LIVE psyche-download "$ID" 2>&1 >/dev/null)
if [[ "$PSYCHE_STDERR" == *"NO-CONTEXT:$ID"* ]]; then
  echo "Fresh agent — entering first-commune flow"
fi
```

### Pure predicate factoring (Rust)

```rust
// Source: pattern mirrors src/owl/plugin_session_start.rs:115-181
//         (inject_reorientation_if_needed) and dispatch_tests:951-974
//         (caller-gate verification via include_str byte scan)
pub(crate) fn should_emit_auto_pick(
    env: &EnvSnapshot,
    src: &SourceInput,
    parent_pid_has_active_perch: bool,
) -> bool {
    // (full body in §3b)
}
```

### hookSpecificOutput envelope (existing precedent)

```rust
// Source: src/owl/resume.rs:415-424
let response = serde_json::json!({
    "hookSpecificOutput": {
        "hookEventName": "SessionStart",
        "additionalContext": context
    }
});
println!("{}", serde_json::to_string(&response).unwrap_or_default());
```

---

## Sources

### Primary (HIGH confidence — verified via repo read)

- `plugin/spt/skills/live/SKILL.md` — full file read; frontmatter at lines 1-8, dispatch table at lines 136-151
- `src/owl/plugin_session_start.rs` — full file read; entry `run()` at lines 10-46, allow-list candidate site between lines 40-45
- `src/owl/resume.rs` — full file read; `<spacetime-reorientation>` emission at lines 384-413, hookSpecificOutput envelope at 415-424
- `src/live/context.rs:444-452` — `run_download` NO-CONTEXT emission verified
- `src/live/context.rs:431-437` — `download_payload_for_injection` (Pulse Log stripped; SessionStart variant)
- `src/live/pick_spec.rs:144-195` — `run` and `build_spec` and `build_pick_spec`; all five kinds verified including `kind:"all-live"` (Phase 31)
- `src/common/hook_output.rs:117-156` — `find_perch_by_session` + `find_perch_by_parent_pid` verified
- `src/common/owlery.rs:354-370` — `is_perch_online` verified read-only
- `src/common/handoff.rs:21-41` — env var constants for trampoline/handoff/wrapper guards
- `tests/skill_hints.rs` — full file read; regression-guard pattern verified for AUTO-08 extension
- `psyche.md` — read first 150 lines for `## Current Focus` H2 convention origin (D-06 anchor)
- `src/live/fork.rs:54-58` — NO_CONTEXT (underscore) variant verified disjoint from psyche-download's NO-CONTEXT (hyphen)
- Grep verification: `SPT_PSYCHE_WRAPPER` returns 0 hits in `src/` — env var does NOT exist
- Grep verification: full subagent-indicator env-var audit completed via `std::env::var` grep across `src/`
- `.planning/REQUIREMENTS.md`, `.planning/ROADMAP.md`, `.planning/STATE.md`, `.planning/phases/33-fresh-start-commune-auto-resume/33-CONTEXT.md` all read

### Secondary (MEDIUM confidence)

- `.planning/research/PITFALLS.md:70-77` — references SPT_PSYCHE_WRAPPER but this is pre-implementation speculation; superseded by agent_type gate in actual code
- `.planning/research/SUMMARY.md:91` — original CP-3 framing (same SPT_PSYCHE_WRAPPER speculation; treat as historical)

### Tertiary (LOW confidence)

- Claude Code SessionStart hook spec for multi-line hookSpecificOutput JSON emission — assumed compatible based on existing repo patterns; verify via local manual test if Plan 03 implementation hits unexpected dispatcher behavior (Pitfall 2)

---

## Assumptions Log

| # | Claim | Section | Risk if Wrong |
|---|-------|---------|---------------|
| A1 | Bare `println!` of `<spt-live-auto-pick>` XML BEFORE the hookSpecificOutput JSON envelope from `super::resume::run_with_input` is accepted as additionalContext by Claude Code's hook consumer | §3a, Pitfall 2 | LOW — may need to merge AUTO-03 emission INTO an envelope JSON. Mitigation: emit ALL AUTO-03 content inside the hookSpecificOutput envelope just to be safe (uses the resume.rs precedent verbatim). |
| A2 | "Prefix match" on H2 markers (`## Current Focus (gen 45)` matches `## Current Focus`) is the correct interpretation of D-06 over "exact match" | §2.4 | LOW — judgment call. Spec says "Confirm prefix-vs-exact during planning." If exact is preferred, change the skill instruction to `if line == "## Current Focus"` instead of `if line.starts_with(...)`. |
| A3 | Description-frontmatter dispatcher correctly weighs explicit negative examples ("Does NOT route here") as anti-matches | §4.4 | MEDIUM — Claude Code's natural-language matcher behavior is not fully documented. AUTO-07 safety net catches false-positives; worst case is occasional spurious confirmation prompts, not silent wrong-agent launches. |
| A4 | Plan 02's SKILL.md "Auto-resume" Step placement above Plan 01's "Step 0 first-commune probe" produces the cleanest document layout | §7 | LOW — pure document ordering preference; can be swapped without behavioral change. |
| A5 | `agent_type` field in hook stdin JSON is set by Claude Code for ALL psyche-wrapper sessions (i.e., gating on `agent_type.is_some()` is sufficient to catch all wrapper-context emissions) | §3b | LOW — existing `inject_reorientation_if_needed` (plugin_session_start.rs:134) and `should_skip_resume_from_input` (resume.rs:36) both gate on this same field; if it were insufficient for those, we'd already be leaking reorientation blocks into wrapper transcripts. No reports of that. |
| A6 | Predicate ordering decision: collapse "FRESH-02 pre-Step-1 probe" back into Step 2 (single psyche-download call site, NO-CONTEXT check after) is cleaner than two probe sites | §8.3 | LOW — both orderings are functionally equivalent; the single-site approach is simpler. Planner should lock one before implementation. |

**Resolution required by planner**: A2 (prefix-vs-exact match) and A6 (FRESH-02 ordering) are the two judgment calls that should be locked in the PLAN before task execution starts.

---

## Open Questions (RESOLVED)

1. **AUTO-03 emission alongside `run_with_input` envelope** — does bare `<spt-live-auto-pick>` println AND `inject_reorientation`'s hookSpecificOutput JSON line coexist? (See A1 / Pitfall 2.)
   - What we know: resume.rs:415-424 uses hookSpecificOutput envelope; resume.rs:51-83 (`resume_xml`) uses bare `println!` of raw XML.
   - What's unclear: whether mixing bare XML stdout with envelope JSON stdout in the same hook execution is well-defined behavior.
   - Recommendation: emit AUTO-03 INSIDE a hookSpecificOutput envelope (safer, matches the formal contract) — but verify during Plan 03 implementation whether bare XML works (cheaper).
   - **RESOLVED**: emit AUTO-03 wrapped in `hookSpecificOutput` envelope per Pattern S2 (resume.rs:415-424). Implemented in Plan 03 Task 2.

2. **AUTO-02 surface format** — H2 section verbatim or distilled? D-06 says "surfaces the matched section verbatim". For a long `## Current Focus` section (e.g., 30 lines), is verbatim acceptable or should the skill auto-summarize?
   - What we know: D-06 says verbatim.
   - What's unclear: UX experience for long sections.
   - Recommendation: verbatim per D-06; if long, the user can ask Claude to summarize manually. Out of scope to truncate.
   - **RESOLVED**: surface H2 section verbatim per D-06; no auto-summarization. Implemented in Plan 02 Task 1 Edit C Step 3.

3. **`--auto` + `--period` combination** — flagged as deferred. Confirm during planning whether Plan 02 wires `--period` through `--auto` (one extra arg passthrough in the skill instruction) or whether it's strictly out of scope.
   - Recommendation: simplest = wire it (one line of skill text). Defer if planner prefers minimal scope.
   - **RESOLVED**: deferred — Plan 02 does NOT wire `--period` through `--auto`. Minimal scope per CONTEXT.md Deferred Ideas convention; revisit if user demand surfaces.

---

## Environment Availability

| Dependency | Required By | Available | Version | Fallback |
|------------|------------|-----------|---------|----------|
| `cargo` | All Rust changes + test runs | ✓ | (workspace pinned) | none required |
| `claude` CLI | Skill flow execution at UAT | ✓ | (user-installed) | manual UAT |
| `git` | Amendment commits | ✓ | — | none required |

No missing dependencies. Phase is pure code/config edits within existing toolchain.

---

## Implementation-Ready Recommendations

The planner should bake these into PLAN.md:

1. **Three plans, sequential execution**: Plan 01 (FRESH + amendment) → Plan 02 (AUTO `--auto` skill flow + description + argument-hint + test) → Plan 03 (AUTO-03 Rust hook + predicate + 30+ test corpus + integration tests).

2. **Plan 01 first commit is the REQUIREMENTS.md + ROADMAP.md amendment** per §6 (mirrors Phase 31 D-11, Phase 32 LIST-04 pattern). Strike FRESH-04, FRESH-05; reword FRESH-06; revise ROADMAP SC#2.

3. **FRESH-02 NO-CONTEXT detection: stderr substring match on `NO-CONTEXT:$ID`**, NOT exit code (context.rs:449 exits 0). Skill bash idiom in §1.4. Add unit test pinning the stderr token format (Wave 0 gap).

4. **FRESH-03 single AskUserQuestion shape**: header "First commune", question = template verbatim, options=["Proceed to init"], body_addendum={summary}. Native Other captures additions. (§1.3)

5. **FRESH-02 ordering**: collapse into Step 2 (single psyche-download call site). FRESH-01 stays pre-start under `kind:"prompt-new"` arm. (§8.3, Assumption A6)

6. **AUTO `--auto` Step: NEW Step inserted between SKILL.md:46 and SKILL.md:48.** Routes (a) bare `--auto` flag, (b) `<spt-live-auto-pick>` injected block from SessionStart, (c) casual-language phrases — all through the SAME dispatch table, all gated by AUTO-07 confirmation. (§2.1)

7. **Always confirm via AUTO-07 on every Auto-resume entry path** (uniform rule — no kind-specific exception). Simpler than carving out kind:"auto" for the bare `--auto` path. (§2.1)

8. **AUTO-02 next-work scan: PREFIX match on three H2 markers** (`## Current Focus`, `## Next Up`, `## Next Steps`). Surface verbatim until next H2 boundary. Synthesis fallback if no match. (§2.4, Assumption A2)

9. **AUTO-03 XML shape: `<spt-live-auto-pick>{pick-spec JSON verbatim}</spt-live-auto-pick>` wrapped in hookSpecificOutput envelope** for safety (matches resume.rs:415-424 precedent). (§3a)

10. **AUTO-03 allow-list predicate (CP-3 RESOLVED)**: `should_emit_auto_pick(&EnvSnapshot, &SourceInput, parent_pid_has_active_perch: bool) -> bool` per the full Rust pseudocode in §3b. Gates: source=startup AND agent_type=None AND no OWL_HANDOFF_CHILD AND no SPT_TRAMPOLINE_GUARD AND no OWL_ECHO_COMMUNE AND no OWL_SKIP_RESUME AND no CLAUDE_CODE_TEAM_NAME AND parent_pid_has_active_perch=false. **`SPT_PSYCHE_WRAPPER` env var DOES NOT EXIST in code**; use `agent_type` gate instead.

11. **AUTO-03 test corpus (CP-4 RESOLVED)**: 15+ should-fire cases + 17+ should-NOT-fire cases as pure unit tests in `plugin_session_start::auto_pick_predicate_tests` submodule. Plus 3-4 subprocess-based integration tests in `tests/auto_pick_integration.rs`. Tables in §3c.

12. **Silent rejection contract**: predicate-false → ZERO stdout, ZERO stderr. Unit tests must assert empty stderr on rejection paths. No `eprintln!` anywhere in the rejected-emit path. (§3b, §8.2, AUTO-04)

13. **Casual-language description rewrite**: 8 accepted phrases listed verbatim, 3 rejected phrases explicitly called out as non-triggers. Format per §4.1.

14. **AUTO-08 argument-hint**: `argument-hint: "<id> [--period <seconds>] | [--auto]"` (Option A, double-quoted per HINT-04). Extend `tests/skill_hints.rs:153` `expected` array. (§5.2/§5.3)

15. **AUTO-05/06 regression test**: new test in `tests/skill_hints.rs` asserting description frontmatter contains all 8 accepted phrases AND mentions the 3 rejected phrases by name. Wave 0 gap.

16. **No DEPLOY in Phase 33** — per ROADMAP cadence ("Single end-of-milestone DEPLOY.ps1 -Bump patch at Phase 34 SUMMARY"). Plan 03 SUMMARY notes "deferred to milestone DEPLOY".

17. **Cross-plan SKILL.md ordering**: Plan 01 lands first (Step 2 modification for FRESH-02 + kind:"prompt-new" arm modification for FRESH-01). Plan 02 lands second (inserts Auto-resume Step ABOVE Step 1; rewrites description; updates argument-hint). Plan 03 is independent at file scope (only `src/owl/plugin_session_start.rs` + new test files) and can land in parallel with Plan 02 if needed.

18. **Sealed runtime contract**: NO new env vars, NO new on-disk artifacts, NO new binary subcommands, NO new pick-spec kinds. Phase 33 is purely SKILL.md text + one Rust hook addition + tests. Schema-additive invariant preserved.

---

*Phase: 33-fresh-start-commune-auto-resume*
*Research date: 2026-05-17*
*Valid until: 2026-06-16 (30 days, stable code surface)*
