---
name: commune-drop-dir-is-per-endpoint
description: "A commune is watched at <the endpoint's own info.cwd>/.claude/<id>-commune.md — one written into another project's .claude/ is silently discarded forever, with no failure surface because no attempt is ever made"
metadata: 
  node_type: memory
  type: project
  originSessionId: 698e20b9-a0cd-4e59-bbe3-dc521afcfb25
  modified: 2026-07-30T02:50:33.846Z
---

⚖ **RULED FROM SOURCE (doyle 2026-07-30, at main `8f3e10b`).** Closes todlando's register item 9(e) toward candidate (ii).

## The mechanism, two files
`crates/spt/src/api/reporting.rs:686-698` — `resolve_filedrops` builds the path as `PathBuf::from(commune_dir).join("<id>-commune.md")` from the manifest's `[session] commune_dir`, which for claude-spt is the **relative** string `".claude"`. **Relative, with no base** — so the consumer picks the base, and that is the whole question.

`crates/spt-daemon/src/lifecycle.rs:851-866` answers it:
```rust
let perch_path   = perch::resolve_perch_path(&self.id, ParentHint::Infer);
let endpoint_cwd = spt_store::info::read_info(&perch_path).and_then(|i| i.cwd);
let Some(dir)    = resolve_endpoint_drop_dir(raw, endpoint_cwd.as_deref().map(Path::new)) else { ... };
```
and its doc comment (L847-848) states the scope in words: *"the SAME resolver **the ingest leg** and `psyche_drop_file` already use."*

⇒ **The watched path is `<that endpoint's own recorded info.cwd>/.claude/<id>-commune.md`. Per-endpoint. Not global, not per-daemon, not per-checkout.**

## ⭐⭐ Why a misplaced commune is WORSE than a failed one
The only loud path in that function is `warn_echo_no_cwd_once` (L864), which fires when the endpoint has **no recorded cwd at all**. When the endpoint HAS a cwd and the file merely sits in a *different project*, the resolver returns a perfectly valid path, the daemon looks there, and finds nothing. **"Nothing to ingest" and "your commune is in the wrong directory" are the same observation.**

A failed ingest at least stamps `psyche-host: FAILED`. A misplaced one produces **no attempt, so nothing to fail, so nothing to annotate.** Combined with [[psyche-host-failure-is-self-only]] (no success surface at all; the failure field renders only on the author's own SELF line), there are three distinct ways for one silence to mean three different things.

## ⭐⭐ It is SHIPPED CONFORMANCE, so the story is RECURRENCE not discovery (chert, re-measured)
`traceable-reqs.toml:1732` (F026 SI-1, shipped **v0.22.0**) documents the *identical* RCA — ".claude declared RELATIVE, nothing resolved it, ingest joined against the DAEMON cwd, `drop_path.exists()==false` every pulse, zero ingest, BranchStore pristine box-wide" — with KH §7.28 as the class rule and a RED-first int test `relative_commune_dir_resolves_against_endpoint_cwd_and_fills_project_branch`. **A hazard class was closed at one end (unresolved relative dir) and the neighbouring case was never surfaced: correctly resolved, to a directory the author was not in.**

## ⚠ CORRECTIONS TO MY OWN FIRST RULING (both chert's, both accepted)
1. **`info.cwd` is exposed by NO read verb** — `spt endpoint list --json` has zero `cwd` fields. So flynn's actual base was never measured; we both read the roster's derived **project LABEL** and I mistook it for the field. `~/Documents/projects/spt-progress-tool` does not even exist. **WITHDRAWN: any claim about where flynn's commune should have gone.** The data loss stands (nothing watches the path it is at); the destination does not. ⭐⭐ The bigger finding underneath: **an agent cannot check where its own commune is supposed to land** — the field governing the resolver's base has no CLI surface. Grill candidate.
2. **TWO orphaned drops, not one** — counted (`ls .claude/ | grep -cE 'commune|signoff'` = 2): `flynn-commune.md` 307 B / 20 days, and `rc-exit-probe-commune.md` 859 B / 10 days whose id is on **no endpoint** in the 14-endpoint roster (a *different* cause — no live endpoint at all vs a live endpoint bound elsewhere — reaching identical silence). I stated one without counting; [[absence-needs-sibling-probe]], and the sibling was in a directory listing I had already run.
3. ⚖ **The `.claude` value is QUOTED, not measured.** claude-spt's manifest is not in this repo; the only tracked `commune_dir` literal at 8f3e10b is `adapters/mock/manifest.toml:37 = ".mock"`. `.claude` rests on REQ prose — testimony inside a tracked artifact. The mechanism holds for **any** relative dir, which is what it actually rests on.

## The evidence that forced the read
| endpoint | commune location | outcome |
|---|---|---|
| chert | matching project dir | consumed ~5.2s |
| todlando | matching project dir | consumed <~3s |
| **flynn** | `spt-core/.claude/` while flynn's project is `spt-progress-tool/` | **unconsumed 20 days, endpoint ONLINE** |

`flynn-commune.md`, 307 B, mtime 2026-07-09 — not a broken ingest. **A file at an unwatched path, quietly discarded for 20 days.** Real data loss; tell flynn.

## ⭐ The transferable rule
The file existed, the daemon was healthy, the endpoint was ONLINE, **every component worked correctly.** The defect was that **two correct things disagreed about a base path** — same filename, same schema, different root. [[audit-the-boring-claims]]: a shared filename is not a shared file, and here the divergence was one directory level up and invisible from either side.

**OPEN (grill scope, nobody builds it):** refuse-and-warn when a commune lands outside the endpoint's resolved drop dir, and/or a positive `last-ingested-at` surface (hertz's requirement — last-success and failed-at kept distinct, and memory-index freshness is NOT a proxy for psyche ingestion).

Related: [[psyche-host-failure-is-self-only]] · [[commune-is-testimony-not-an-artifact]] · [[commune-ingest-pulse-latency-gap]] · [[next-batch-register-v0461]] · [[audit-the-boring-claims]]
