# Stack Research — v1.7.1 Seamlessification II

**Domain:** Polish/UX iteration on existing SPT Rust binary + plugin skill markdown
**Researched:** 2026-05-16
**Confidence:** HIGH

## TL;DR

**Zero new crates. Zero new hook events. Zero new manifest fields.** Every v1.7.1 polish item is implementable with the existing stack:

- `clap 4.6` (derive) already handles every flag pattern needed (`--all` / `--offline` / `--here` / `--auto`)
- `serde_json` already powers `pick-spec` JSON output
- `std::env::current_dir` + path canonicalization is sufficient for `--here` repo-scoping (no `git2` needed)
- `argument-hint` is a stable Claude Code frontmatter field — already used by 14 of 17 skills in-tree; the milestone needs coverage on the remaining 3 + audit of existing values
- Version-sentinel changelog flow uses existing `SessionStart`/`PreToolUse` hooks + `env!("CARGO_PKG_VERSION")` + plain filesystem I/O (already standard pattern in `src/owl/setup.rs`, `src/owl/poll.rs`, `src/live/stop.rs`)

The single-binary, zero-runtime-deps constraint is preserved entirely.

## Existing Stack — Validated Sufficient

### Core Technologies (no changes)

| Technology | Version | Purpose | Why Sufficient for v1.7.1 |
|------------|---------|---------|---------------------------|
| Rust | 2021 edition | All runtime in `src/` | All polish items are flag additions, output shape changes, sentinel I/O — no new language features needed |
| clap | 4.6 (derive) | CLI parsing | `derive` macros + `ArgGroup` already cover mutually-exclusive flags (`--all` vs `--offline`); existing `$LIVE pick-spec` subcommand pattern extends cleanly to `--auto` flag |
| serde / serde_json | 1.0 (with `preserve_order`) | `pick-spec` JSON envelope + `info.json` | Item 4 (pick-spec bug) is purely a filter-predicate fix in existing code path; no schema changes |
| rusqlite | 0.39 (bundled) | Spool fallback | Untouched by v1.7.1 |
| chrono | 0.4 (clock + std) | ISO-8601 timestamps in EVENT envelopes | Untouched; reused if changelog notification stamps last-prompted-at |
| ctrlc | 3.5 (termination) | Signal handling | Untouched |
| libc / windows-sys | 0.2 / 0.61 | Platform PID + socket | Untouched |

### Supporting Libraries (no additions)

No new dependencies. The `Cargo.toml` deltas for v1.7.1 should be **zero new lines** unless we bump existing crates for unrelated reasons.

## Item-by-Item Stack Impact

### Item 1 — `list` overhaul (`--all` / `--offline` / `--here`)

**Stack impact:** Zero new deps. Pure refactor of `src/owl/list.rs` (currently 1-flag `online_only: bool` → 3-flag enum) plus equivalent path on the `$LIVE` side.

**clap pattern:** Use `ArgGroup` (already part of `clap` 4.x derive) to enforce `--all` and `--offline` are mutually exclusive while `--here` composes with either:

```rust
#[derive(clap::Args)]
#[command(group(
    ArgGroup::new("scope").args(["all", "offline"]).multiple(false)
))]
struct ListArgs {
    /// Include offline perches in output
    #[arg(long)]
    all: bool,
    /// Only show offline perches (exclusive with --all)
    #[arg(long)]
    offline: bool,
    /// Filter to perches whose recorded cwd matches the current repo
    #[arg(long)]
    here: bool,
}
```

**`--here` repo-scoping:** `std::env::current_dir()` is already imported across 13 files in `src/` — no `git2` crate needed. Resolve the current cwd, canonicalize it, then walk parents looking for `.git`. `info.json` may need a `cwd` field if not already universal — verify in execution phase. MEDIUM confidence on info.json having `cwd` universally. Worst case: add `cwd: Option<String>` to `InfoJson` and backfill at next start.

### Item 2 — `argument-hint` frontmatter coverage

**Stack impact:** Zero code changes. Markdown frontmatter edits only.

**Spec source (HIGH confidence):** Official Claude Code skills docs confirms `argument-hint` is a stable, optional field shown during `/` autocomplete. Format is free-form string. Spec examples: `[issue-number]`, `[filename] [format]`. Bracketed convention indicates optional positional args.

**Current coverage audit (Grep `argument-hint` across `plugin/spt/skills/`):**

Has the field (14): `clear-psyche`, `commune`, `context-save`, `listen`, `listen-stop`, `live`, `live-stop`, `new-alarm`, `psyche-download`, `reboot`, `revive`, `send`, `signoff`, `whoami`.

**Missing (3):** `list-live/SKILL.md`, `list-psyche/SKILL.md`, `list-ready/SKILL.md`. These are precisely the surfaces being overhauled in Item 1 — natural pairing. Suggested values:

- `list-ready`: `[--all] [--offline] [--here]`
- `list-live`: `[--all] [--offline] [--here]`
- `list-psyche`: `[--all] [--offline] [--here]`

**Convention audit hits in existing values:**
- `whoami` uses `(no arguments)` — verbose; spec convention is empty string `""` (already used by `clear-psyche`, `psyche-download`, `signoff`). Recommend standardizing on `""` for zero-arg skills.
- `signoff` already uses `""` — good template.

### Item 3 — Fresh-start commune prompt

**Stack impact:** Zero new deps. Pure logic change on the skill markdown side (`live/SKILL.md` Step 3 dispatch table) + a small return-field extension on `pick-spec --resolve` JSON (`in_this_repo_history` already exists; possibly add `is_fresh: bool` or reuse `!in_this_repo_history && other_repos.is_empty()`).

**Implementation surface:** `serde_json` already serializes `pick-spec` output; adding a field is a one-line struct change in `src/live/pick_spec.rs`. The user-facing first-commune prompt is markdown text in `plugin/spt/skills/live/SKILL.md` — no Rust changes needed for the prompt itself if we reuse `AskUserQuestion` (already documented in live/SKILL.md Step 3).

### Item 4 — `$LIVE pick-spec` bug (live agents missing when all online)

**Stack impact:** Zero new deps. Single-line filter-predicate fix in `src/live/pick_spec.rs`. The current code likely filters to `!is_online` to find "offline candidates"; the fix is to include online-and-live agents in the picker output (calling skill flow already documents the D9 "Refuse + re-pick" path for already-live names, per live/SKILL.md). HIGH confidence on the fix shape; the existing skill markdown anticipates this case.

### Item 5 — `/spt:live --auto` flag

**Stack impact:** Zero new deps. clap derive subcommand flag addition + SessionStart hook output extension.

**clap pattern:** Add `#[arg(long)] auto: bool` to the `pick-spec` (or new `live auto-pick`) subcommand args. No new infrastructure.

**SessionStart auto-surface:** Existing `plugin-session-start` hook (`src/owl/...` per `hooks.json` line 8) already emits XML tags like `<owl-auto-resume>` (per listen/SKILL.md "Session Auto-Resume" section). Pattern is well-established. New emission: `<spt-live-auto-pick>{pick-spec-json}</spt-live-auto-pick>` when the session has no live/listening identity. Claude Code hook stdin contract is documented in `~/.claude/reference_docs/claude-code-hooks.md`; SessionStart returns JSON with `hookSpecificOutput.additionalContext` — use the same shape as the existing `<psyche-context>` injection (Phase 28).

**Casual-prompt activation ("pick up where we left off"):** Pure skill-markdown change in `live/SKILL.md` `description` trigger phrases. The `description` frontmatter field is what Claude Code uses to model-invoke skills (per spec); adding `"pick up where we left off"`, `"keep going"`, `"resume work"` to the description text is sufficient — no Rust.

### Item 6 — Version-change changelog notification

**Stack impact:** Zero new deps. Two existing patterns combine.

**Sentinel pattern (already in tree):**
- `src/owl/setup.rs`, `src/owl/poll.rs`, and `src/live/stop.rs` already use `env!("CARGO_PKG_VERSION")` — confirmed by Grep. The binary knows its own version at compile time.
- Filesystem sentinel I/O is standard `std::fs` usage; example pattern is in `src/owl/poll.rs` for the `.stop` poison file.

**Storage location:**
- `%LOCALAPPDATA%\spt\current-version` (Windows) / `~/.spt/current-version` (Unix) — sibling to `owlery/`. Resolve via existing `src/common/owlery.rs` helpers (`SPT_HOME` override already supported).
- Zero-byte default sentinel: `%LOCALAPPDATA%\spt\changelog-acked` (or per-version `changelog-acked-v1.10.10`). On filesize==0 → prompt user; after print, write non-zero (1 byte: `\n`) to mark acked.

**Hook integration:** `SessionStart` is the natural fire point (already fires `plugin-session-start` per `hooks.json`). Read `current-version` file, compare with `env!("CARGO_PKG_VERSION")`, on mismatch emit prompt XML, then update `current-version`. `PreToolUse` (`hook-check`, already in tree) is a secondary trigger for sessions that started before deploy completed — same code path can be exposed by both. HIGH confidence: identical pattern to how `<psyche-context>` injection works today.

**Changelog source:** Plugin manifest `plugin/spt/.claude-plugin/plugin.json` has `version` (currently `1.10.10`) but no changelog body. Options:
- Embed `CHANGELOG.md` via `include_str!` (same pattern as `psyche.md`) — single-binary preserved, but bloats the binary on every release.
- Read `CHANGELOG.md` at runtime from `$CLAUDE_PLUGIN_ROOT` (env var available in hook context per `hooks.json` usage) — keeps binary lean, but couples to plugin install layout.

Recommendation: `include_str!("../CHANGELOG.md")` if a changelog file exists at repo root; the file is small (<5KB typical) and the embed-pattern matches `psyche.md`. No new tooling. LOW confidence on whether a `CHANGELOG.md` file exists today — verify in Phase 1 of execution; if absent, generate-from-git-log is a one-time script (no runtime impact).

## Skill Frontmatter Field Reference (HIGH confidence)

Source: <https://code.claude.com/docs/en/slash-commands> (current as of 2026-05-16; redirect from `docs.anthropic.com`).

| Field | Required | Behavior |
|-------|----------|----------|
| `name` | No | Display name; defaults to dir name. lowercase + hyphens, max 64 chars. |
| `description` | Recommended | Used by Claude to decide auto-invoke; truncated at 1,536 chars combined with `when_to_use`. |
| `when_to_use` | No | Trigger phrases / example requests appended to description. |
| **`argument-hint`** | **No** | **Free-form string shown in `/` autocomplete. Spec examples: `[issue-number]`, `[filename] [format]`.** |
| `arguments` | No | Named positional args for `$name` substitution. |
| `disable-model-invocation` | No | If true, only user can `/skill-name` invoke. |
| `user-invocable` | No | If false, hidden from `/` menu. |
| `allowed-tools` | No | Space-separated or YAML list. |

The convention this milestone should standardize across the 17 skills:
- Optional args: `[--flag]` square brackets
- Required args: `<arg>` angle brackets
- Mutually exclusive: `(a | b)` parens with pipe (current `listen-stop` already uses `"[<id>] | --all"`)
- Zero args: empty string `""`

## Installation

No installation deltas. Existing `cargo build --release` + `docs/DEPLOY.ps1` flow is unchanged.

## Alternatives Considered

| Recommended | Alternative | When to Use Alternative |
|-------------|-------------|-------------------------|
| `std::env::current_dir` + walk parents for `--here` | `git2` crate (libgit2 bindings) | If we needed full git ops (status, log, diff) — overkill for "find the repo root". Adds ~2MB to binary, breaks zero-deps-beyond-binary promise. |
| `include_str!("../CHANGELOG.md")` | Runtime read from `$CLAUDE_PLUGIN_ROOT` | If CHANGELOG grows past ~50KB or needs hot-reload between deploys. Current scale doesn't justify the coupling. |
| Hook XML emission (`<spt-live-auto-pick>`) for `--auto` | New top-level hook event type | Hook event types are Claude Code-fixed; the existing `additionalContext` pattern is sufficient and battle-tested in v1.7 Phase 28. |
| clap `ArgGroup` for `--all`/`--offline` mutex | Custom validation in `run()` body | `ArgGroup` is declarative, generates correct `--help`, fails fast at parse time. Custom validation duplicates clap's job. |
| Per-version sentinel file (`changelog-acked-v1.10.10`) | Single sentinel with version-stamp content | Per-version means a downgrade also re-prompts; arguably correct behavior. Single sentinel with content is also fine — minor preference, decide in execution. |

## What NOT to Use

| Avoid | Why | Use Instead |
|-------|-----|-------------|
| Adding `git2` crate | Pulls libgit2 C deps; breaks single-binary portability promise | `std::env::current_dir` + filesystem walk for `.git` directory |
| Adding `clap_complete` for shell completions | Out of scope; `argument-hint` is the in-Claude-Code surface that matters here | Just populate `argument-hint` frontmatter |
| New hook event types | Claude Code hook events are fixed; can't add custom ones | Reuse `SessionStart`/`PreToolUse` `additionalContext` envelope |
| Storing changelog ack state in `owlery/` per-perch | Cross-perch state belongs at the `spt/` root | `%LOCALAPPDATA%\spt\changelog-acked` (sibling to `owlery/`) |
| External version-comparison crate (`semver`) | Overkill for string-compare-and-prompt; we control both ends | Plain `==` on `env!("CARGO_PKG_VERSION")` vs file contents |
| `async`/`tokio` for any of this | Documented architectural decision: sync TCP only; same applies to all new code | `std::sync` + blocking I/O, matching existing module conventions |

## Stack Patterns by Variant

**If we discover `info.json` lacks `cwd` field universally:**
- Add `cwd: Option<String>` to `InfoJson` struct in `src/common/types.rs`
- Backfill at next `start`/`revive`
- `--here` filter degrades gracefully: missing `cwd` → "include" (loud absence is worse than over-inclusion)

**If CHANGELOG.md doesn't exist at repo root:**
- Phase 0 of execution generates it from `git log --oneline v1.5.0..HEAD`
- Subsequent deploys append at each `Bump-Version` step (`DEPLOY.ps1` hook)
- `include_str!` embeds the result

**If Claude Code adds native skill auto-suggestions before this ships:**
- `--auto` flag is still useful as the imperative form
- SessionStart auto-surface can become opt-in via a settings flag rather than default

## Version Compatibility

All existing crate versions remain compatible. No bumps required for v1.7.1.

| Package | Current | v1.7.1 needs | Notes |
|---------|---------|--------------|-------|
| clap | 4.6 | 4.6 | `ArgGroup` derive available since 4.0; safe. |
| serde / serde_json | 1.0 | 1.0 | No changes. |
| rusqlite | 0.39 (bundled) | 0.39 | Untouched by milestone. |
| chrono | 0.4 | 0.4 | Untouched. |
| ctrlc | 3.5 | 3.5 | Untouched. |
| windows-sys | 0.61 | 0.61 | Untouched. |
| libc | 0.2 | 0.2 | Untouched. |

## Integration Points for Downstream Phases

1. **`src/owl/list.rs`** — refactor `run(online_only: bool)` to `run(args: ListArgs)`. Equivalent path on `$LIVE list`.
2. **`src/live/pick_spec.rs`** — fix predicate to include online-and-live agents when all repo agents are online; verify `--resolve` JSON shape unchanged (matches `live/SKILL.md` Step 3 table contract).
3. **`src/common/owlery.rs`** (or new sibling) — add `current_repo_root()` helper for `--here` filter.
4. **`src/common/types.rs`** — possibly add `cwd: Option<String>` to `InfoJson` (verify field presence first).
5. **SessionStart hook plugin entry** (`plugin-session-start` dispatcher) — extend to emit `<spt-live-auto-pick>` XML for non-live/non-listening sessions and `<spt-changelog-prompt>` on version mismatch.
6. **`plugin/spt/skills/{list-ready,list-live,list-psyche}/SKILL.md`** — add `argument-hint` frontmatter.
7. **`plugin/spt/skills/live/SKILL.md`** — extend Step 3 dispatch table with fresh-start commune branch; add casual-prompt triggers to `description`.
8. **Repo root** — `CHANGELOG.md` (new file if absent) for `include_str!`.

## Sources

- `Cargo.toml` (in-repo) — confirmed crate versions and feature flags. HIGH confidence.
- `plugin/spt/.claude-plugin/plugin.json` (in-repo) — current v1.10.10. HIGH confidence.
- `plugin/spt/skills/*/SKILL.md` (Grep audit) — 14/17 skills already have `argument-hint`. HIGH confidence.
- `plugin/spt/hooks/hooks.json` (in-repo) — confirms 7 hook events already wired; no new hook decls needed. HIGH confidence.
- [Claude Code Skills / Slash Commands docs](https://code.claude.com/docs/en/slash-commands) — official frontmatter spec for `argument-hint`. HIGH confidence (current docs, fetched 2026-05-16; redirected from `docs.anthropic.com/en/docs/claude-code/slash-commands`).
- `src/owl/list.rs` (in-repo) — confirmed `run(online_only: bool)` is the current entry point. HIGH confidence.
- `src/owl/poll.rs`, `src/owl/setup.rs`, `src/live/stop.rs` (Grep for `CARGO_PKG_VERSION`) — confirmed compile-time version is already accessed in three call sites. HIGH confidence.
- `Grep cwd|current_dir` across `src/` — 13 files already use `std::env::current_dir`; no new dep needed for `--here`. HIGH confidence.

---
*Stack research for: SPT v1.7.1 Seamlessification II polish iteration*
*Researched: 2026-05-16*
