---
phase: quick-260515-uf1
plan: 01
subsystem: live-agent/psyche-context
tags: [psyche-download, drop-files, destructive-consume, amend-signoff-deprecation, deploy]

# Dependency graph
requires:
  - phase: Phase 30 Plan 03
    provides: append_pending_sections helper takes cwd parameter (parallel-test-safe); existing non-destructive output-only append behavior to be replaced
  - phase: Phase 30 Plan 04
    provides: tests/file_drop_integration.rs Test 5 + Test 6 (psyche_download_appends_pending_commune / both) as canonical integration coverage for download_payload
provides:
  - Destructive consumption of .claude/{id}-{commune|signoff}.md drop files by psyche-download (CLI path + SessionStart-injection path)
  - mtime-stamped section headers `## Pending {Kind} (written {iso8601})` replacing legacy `(uncommitted)` shape
  - Retain-on-error semantics: drop file preserved if on-disk append to {psyche_dir}/{id}.md fails
  - One canonical post-session-end context-update flow: drop the file, let next psyche-download absorb it
affects:
  - amend-signoff workflow (skill removed; binary subcommand preserved)
  - signoff offline-listener UX (now persists until absorbed instead of error)
  - psyche-download CLI/injection output (one-shot pending sections instead of forever-repeating)

# Tech tracking
tech-stack:
  added: []
  patterns:
    - "Destructive consume with write-then-delete ordering: on-disk append SUCCEEDS first, only then unlink drop file (retry-safe under transient FS errors)"
    - "mtime-as-header-source: drop file's modification time is the authoritative timestamp (user wrote it), not psyche-download invocation wall clock (could be hours later)"
    - "Race-loser short-circuit reuse: scan_drop_files (poll.rs) and append_pending_sections (context.rs) both read+remove the same files; the loser hits ErrorKind::NotFound on fs::read_to_string, which the existing `if let Ok(content)` arm silently skips — no new locking added"
    - "Skill deletion without binary deletion: removing the SKILL.md directory drops the slash command surface while leaving the $LIVE amend-signoff subcommand intact for backward compat"

key-files:
  created:
    - .planning/quick/260515-uf1-psyche-download-consumes-drop-files/260515-uf1-PLAN.md
    - .planning/quick/260515-uf1-psyche-download-consumes-drop-files/260515-uf1-CONTEXT.md
    - .planning/quick/260515-uf1-psyche-download-consumes-drop-files/260515-uf1-SUMMARY.md
  modified:
    - src/live/context.rs — reworked append_pending_sections to do destructive write-then-delete with mtime-header; in-module tests retargeted from `(uncommitted)` literal to `(written ` prefix match with on-disk assertion
    - tests/file_drop_integration.rs — Test 5 + Test 6 assert drop file is DELETED post-run and {psyche_dir}/{id}.md contains the appended section
    - plugin/spt/skills/psyche-download/SKILL.md — added "Pending drop-file consumption" section documenting destructive-consume semantics + amend-signoff replacement note
    - plugin/spt/skills/signoff/SKILL.md — added "Offline path (no live listener)" paragraph documenting persist-until-absorbed semantics
    - plugin/spt/.claude-plugin/plugin.json — version 1.10.7 → 1.10.8 (via DEPLOY.ps1 -Bump patch)
    - Cargo.toml — version 1.10.7 → 1.10.8 (via DEPLOY.ps1 -Bump patch)
    - Cargo.lock — version 1.10.7 → 1.10.8 (deploy chore follow-up)
  deleted:
    - plugin/spt/skills/amend-signoff/ — entire skill directory removed (slash command surface gone; binary subcommand preserved)

key-decisions:
  - "Header shape `## Pending {Kind} (written {mtime})` — mtime sourced from drop-file fs::metadata.modified() formatted as `%Y-%m-%dT%H:%M:%S%:z`; falls back to chrono::Local::now() on metadata failure so the consume never aborts (degraded but recoverable)"
  - "Write-then-delete ordering: on-disk OpenOptions::append succeeds → fs::remove_file(drop). If append fails, drop file retained AND owl_err logged. Idempotent at the on-disk file level if the post-write unlink itself fails (next call re-appends with possibly-different mtime; no data loss)"
  - "Output buffer push moved AFTER successful on-disk append (originally pre-append) so stdout reflects only what was actually persisted — prevents the user from seeing a 'Pending Commune' section in psyche-download output that the on-disk context.md never received"
  - "Race resolution with scan_drop_files: no new locking. Both code paths use fs::read_to_string → if Err (NotFound included), continue. The loser silently no-ops; the winner consumes. Acceptable because the only contention pattern is rare (live listener + simultaneous CLI psyche-download), and a duplicate consume would just produce two sections with adjacent mtimes — annoying but not corrupt"
  - "Skill deletion ≠ binary deletion for /spt:amend-signoff: deleted plugin/spt/skills/amend-signoff/ to drop the slash command surface, but src/live/context.rs::{run_amend_signoff, amend_signoff_result} + the LiveCommands::AmendSignoff CLI arm are preserved out of scope per CONTEXT.md. Backward compat for any script still invoking `$LIVE amend-signoff` directly"
  - "Signoff doc kept as single flow + offline-path note (NOT branched into two flows) — the Write-tool drop step is the same regardless of listener liveness; the note clarifies what happens to the file when no listener picks it up"
  - "Tests retargeted from byte-equal assertions on `(uncommitted)` literal to structural assertions: header-prefix `## Pending Commune (written ` + body substring + on-disk file presence + drop-file absence. mtime value itself is timing-dependent and untestable byte-for-byte; prefix match is the structural invariant"
  - "Deploy required -Bump patch (1.10.7 → 1.10.8) to sidestep the v1.10.7 owl.exe file-lock on prior deploy attempt — fresh cache path means no copy-item sharing violation. Binary-handoff machinery (Phase 18.4/18.5) migrates live listeners + Psyche wrappers transparently"

patterns-established:
  - "Drop-file consumption protocol: read → metadata mtime → format header → on-disk append (atomic via append-mode open) → on success only, unlink. On failure: log, leave file, return false-ish from the caller to keep retry pressure"
  - "Output-vs-persistence ordering: when a CLI emits content that should reflect on-disk state, persist FIRST then emit. The previous order (emit-then-persist) breaks retain-on-error because the user reads what looks like a persisted section but isn't"

requirements-completed:
  - PSYDL-CONSUME-01    # destructive consume in download_payload (CLI path)
  - PSYDL-CONSUME-02    # destructive consume in download_payload_for_injection (SessionStart path) — inherits via shared inner producer
  - PSYDL-CONSUME-03    # mtime-stamped headers `## Pending {Kind} (written {mtime})`
  - PSYDL-CONSUME-04    # write-on-disk-then-delete ordering; retain drop file on write failure
  - PSYDL-CONSUME-05    # tests updated: drop file DELETED, on-disk context.md APPENDED
  - PSYDL-DEPRECATE-01  # /spt:amend-signoff skill removed
  - PSYDL-PSYDL-DOC-01  # /spt:psyche-download SKILL.md teaches destructive consumption
  - PSYDL-SIGNOFF-DOC-01 # /spt:signoff SKILL.md teaches offline persist-until-absorbed path

# Metrics
duration: ~50min (across two execution sessions; original session deploy hit owl.exe file-lock, resumed with -Bump patch)
completed: 2026-05-15
---

# Quick 260515-uf1: psyche-download destructively consumes drop files — Summary

**`psyche-download` now destructively absorbs `.claude/{id}-{commune|signoff}.md` drop files into the on-disk psyche-context file under `## Pending {Kind} (written {mtime})` headers, then deletes the drop file. This is now the single canonical path for post-session-end context updates — `/spt:amend-signoff` skill is removed (binary preserved for back-compat). `/spt:signoff` SKILL.md teaches the offline persist-until-absorbed flow. Deployed as v1.10.8.**

## Performance

- **Duration:** ~50 minutes wall (two sessions — first stopped at deploy due to owl.exe v1.10.7 file lock; resumed with `-Bump patch` to bypass)
- **Completed:** 2026-05-15
- **Tasks:** 3 plan tasks + 1 deploy chore commit = 4 commits
- **Files modified:** 5 source/skill files; 1 skill dir deleted; plugin.json + Cargo.toml + Cargo.lock version bumps

## Accomplishments

- **Destructive consume in `append_pending_sections`** — for each kind in `[commune, signoff]`: read drop file → mtime header → on-disk append to `{psyche_dir}/{id}.md` (create-if-absent via `OpenOptions::append(true).create(true)`) → on success, unlink drop file; on failure, log via `owl_err` and retain. Output buffer push moved AFTER on-disk write to preserve emit-reflects-persistence invariant.
- **Both CLI and SessionStart paths absorb drop files** — `download_payload` (CLI) and `download_payload_for_injection` (SessionStart hook) share the same inner producer; the change is captured once in `append_pending_sections` and inherited by both.
- **mtime-stamped headers replace `(uncommitted)`** — section header is now `## Pending Commune (written 2026-05-15T14:23:11-07:00)` (drop-file mtime, local-zone ISO-8601). Falls back to `Local::now()` on metadata failure.
- **`/spt:amend-signoff` skill deleted** — `plugin/spt/skills/amend-signoff/` directory removed. Slash command surface gone; `$LIVE amend-signoff` binary subcommand preserved for backward compat (out-of-scope per CONTEXT.md).
- **Doc updates** — `/spt:psyche-download` SKILL.md adds a "Pending drop-file consumption" section explaining the destructive-consume semantics and the replacement of the deprecated `/amend-signoff` workflow. `/spt:signoff` SKILL.md adds an "Offline path (no live listener)" paragraph (single flow with note, NOT a branched two-flow).
- **Race resolution with `scan_drop_files`** — confirmed `ErrorKind::NotFound` short-circuit in the existing `if let Ok(content)` arm handles both code paths losing the read+unlink race; no new locking introduced.
- **Test rework** — in-module `context::tests::append_pending_sections_*` tests retargeted from byte-equal `(uncommitted)` assertions to structural assertions (header-prefix + body substring + on-disk file presence + drop-file absence). `tests/file_drop_integration.rs` Test 5 + Test 6 updated to assert drop file deletion + on-disk section presence. Full `cargo test` green.
- **Deployed v1.10.8** to `~/.claude/plugins/cache/cplugs/spt/1.10.8/`; marketplace pushed to `cplugs` HEAD `5964c50`; `installed_plugins.json` pointer verified by DEPLOY.ps1 step 10.

## Task Commits

Each task committed atomically. All commits independently green under `cargo build --release` and `cargo test`.

1. **Task 1: Rust impl + tests — destructive consume in `append_pending_sections`** — `4648a50` (feat)
   - `src/live/context.rs`: reworked helper per CONTEXT D11; write-then-delete ordering; mtime header; retain-on-error
   - `src/live/context.rs::tests::*`: 7 in-module tests retargeted to structural assertions + on-disk-file checks under `SptHomeGuard`
   - `tests/file_drop_integration.rs`: Test 5 + Test 6 assert drop file DELETED + on-disk `{psyche_dir}/{id}.md` APPENDED
   - Race partner `src/owl/poll.rs::scan_drop_files` unchanged — NotFound short-circuit reused
   - Commit message: `feat(psyche-download): destructively consume drop files into on-disk context`

2. **Task 2: Skill doc updates** — `b2749fd` (docs)
   - `plugin/spt/skills/amend-signoff/` — entire directory DELETED
   - `plugin/spt/skills/psyche-download/SKILL.md` — added "Pending drop-file consumption" section + removed amend-signoff cross-reference
   - `plugin/spt/skills/signoff/SKILL.md` — added "Offline path" paragraph explaining persist-until-absorbed + amend-signoff deprecation
   - Commit message: `docs(skills): destructive consume in psyche-download; drop amend-signoff; signoff offline path`

3. **Planning artifacts** — `ddc44bd` (docs)
   - `.planning/quick/260515-uf1-.../{PLAN.md, CONTEXT.md}` — task plan + design context
   - Commit message: `docs(quick-260515-uf1): add planning artifacts for psyche-download destructive consume`

4. **Task 3: Build + deploy (v1.10.8)** — two commits (auto-bump + Cargo.lock follow-up)
   - **`5be20a8` (chore)** — DEPLOY.ps1 -Bump patch atomic-committed `plugin/spt/.claude-plugin/plugin.json` + `Cargo.toml` (`1.10.7 → 1.10.8`). Commit message: `chore: bump spt plugin to v1.10.8`
   - **`add6bc6` (chore)** — Cargo.lock refresh (cargo rebuild updated owl crate entry to 1.10.8; DEPLOY.ps1 does not stage Cargo.lock — precedent from `c26465c` / `62cebea` follow-up commits in Phase 18.8 + 18.8.1). Commit message: `chore(deploy): rebuild + deploy psyche-download destructive consume (v1.10.8)`

## Deviations from Plan

### Deploy bump deviation (Rule 3 — blocking issue)

- **Found during:** Task 3 (resume session)
- **Issue:** Original deploy attempt without `-Bump` failed at Step 6 with `Copy-Item` sharing violation on `~/.claude/plugins/cache/cplugs/spt/1.10.7/owl.exe` — file locked by an in-flight `owl.exe` listener still exec'ing from the v1.10.7 cache dir. Phase 18.4/18.5 binary-handoff design tolerates this for the OLD version (it stays put until the listener self-migrates), but blocks overwriting the SAME version in place.
- **Fix:** Per user decision, re-ran with `-Bump patch` (`1.10.7 → 1.10.8`). New cache path `1.10.8/` is fresh ground — no file to be locked. Binary-handoff machinery migrates live listeners transparently to the new version.
- **Files modified:** `plugin/spt/.claude-plugin/plugin.json`, `Cargo.toml`, `Cargo.lock`
- **Commits:** `5be20a8` (auto-bump), `add6bc6` (Cargo.lock follow-up)
- **Tracked as:** legitimate Rule 3 deviation; documented up-front in the user's resume directive

### Marketplace clone divergence note

- Marketplace clone `cplugs` had advanced to `b7aa946` during the prior failed deploy attempt (skills tree + binary were synced before Step 6 hit the file lock). The successful retry pushed `5964c50` on top — clean linear history, expected outcome.

### No other deviations

- Rules 1-3 not triggered elsewhere; Rule 4 not triggered.
- All plan acceptance criteria met as written (with the deploy deviation above being explicitly user-sanctioned during resume).

## Deploy Log

- **Tool:** `powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -Bump patch`
- **Outcome:** All 10 steps green; deploy completed clean
- **Version stamp:** `1.10.7 → 1.10.8`
- **Cache path:** `C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.10.8\`
- **Marketplace HEAD pushed:** `5964c50` (preceded by `b7aa946` from prior failed run; together they form clean linear history on cplugs `main`)
- **Pointer assertion (Step 10):** `spt@cplugs version=1.10.8 installPath=...\cache\cplugs\spt\1.10.8` — verified
- **Cache prune:** kept `1.10.8` + `1.10.7`; renamed `1.10.6` to `.pending-prune-1.10.6-*` (still-locked owl.exe inside, deferred to next deploy per documented carry-over policy)
- **Deployed skills verified:** `amend-signoff/` ABSENT in `cache\cplugs\spt\1.10.8\skills\`. 17 skill dirs present (clear-psyche, commune, context-save, list-live, list-psyche, list-ready, listen, listen-stop, live, live-stop, new-alarm, psyche-download, reboot, revive, send, signoff, whoami).
- **`claude plugin install spt@cplugs`:** ran clean (no-op as expected; cache warm-up only)

## Reload Required

>>> **User must run `/reload-plugins` inside Claude Code to activate v1.10.8 + drop the `/spt:amend-signoff` slash command from the registered skill list.** <<<

Per CLAUDE.md, `/reload-plugins` is user-controlled — executor stops short of running it.

## Verification Smoke Path (post-`/reload-plugins`)

User-driven manual check:

1. Create `.claude/doyle-commune.md` with body `"smoke test"`.
2. Run `$LIVE psyche-download doyle`.
3. Observe stdout contains `## Pending Commune (written 2026-05-15T...)`.
4. Observe `.claude/doyle-commune.md` is GONE (destructive consume succeeded).
5. Observe `{SPT_HOME}/owlery/psyches/tracked/doyle.md` ends with the appended `## Pending Commune (written ...)` section + the smoke-test body.
6. Re-run `$LIVE psyche-download doyle` immediately — pending-section does NOT re-appear (one-shot consumption confirmed).

## Self-Check: PASSED

Verified post-write:

- `.planning/quick/260515-uf1-psyche-download-consumes-drop-files/260515-uf1-PLAN.md` — exists
- `.planning/quick/260515-uf1-psyche-download-consumes-drop-files/260515-uf1-CONTEXT.md` — exists
- `.planning/quick/260515-uf1-psyche-download-consumes-drop-files/260515-uf1-SUMMARY.md` — exists (this file)
- `plugin/spt/skills/amend-signoff/` — DOES NOT exist in repo (deleted in Task 2) and DOES NOT exist in deployed cache `~/.claude/plugins/cache/cplugs/spt/1.10.8/skills/`
- Commits `4648a50`, `b2749fd`, `ddc44bd`, `5be20a8`, `add6bc6` — all present in `git log` on `main`
- Marketplace commit `5964c50` — pushed to `cplugs/main`
- `installed_plugins.json` — points to `spt@cplugs` version `1.10.8`, installPath `...\cache\cplugs\spt\1.10.8` (verified by DEPLOY.ps1 Step 10 assertion)
