# Phase 23: Commune & Signoff Project-Root + HEAD SHA Stamping - Discussion Log

> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.

**Date:** 2026-05-19
**Phase:** 23-commune-signoff-project-root-head-sha-stamping
**Areas discussed:** Field placement, Drift surface on download, project_root form + not-a-repo fallback, Echo commune scope + git-call hot-path safety

---

## Source Todo (surfaced at user request)

`.planning/todos/done/2026-04-18-track-project-root-and-latest-commit-in-commune-and-amend-si.md`
created 2026-04-18T09:42:35.939Z, promoted to Phase 23 on 2026-05-10.

Title: *Track project root and latest commit in commune/amend-signoff*. Area: `psyche`.
Touch points listed in todo: LIVE-SKILL.md commune instructions, `spt:commune` skill,
`spt:amend-signoff` skill, Psyche context save format. Goal: anchor commune/signoff
to repo state so resumed Self can compare stored HEAD vs current HEAD.

---

## Area Selection

| Option | Description | Selected |
|--------|-------------|----------|
| Field placement | Where in payloads (EVENT attrs / nested meta / prose). | ✓ |
| Drift surface on download | Raw fields vs computed delta block. | ✓ |
| project_root form + not-a-repo fallback | Identifier semantics + repo-absent shape. | ✓ |
| Echo commune scope + git hot-path safety | echo_commune stamping + git subprocess cost. | ✓ |

**User's choice:** All four. User also requested the source todo be quoted verbatim before any options were asked.

---

## Area 1 — Field placement

### Q1.1 EVENT-wrapped payloads (init_signoff, echo_commune)

| Option | Description | Selected |
|--------|-------------|----------|
| Inline attrs | Add project_root / head_sha / head_subject to the EVENT tag. Parseable. | ✓ |
| Nested `<meta>` child | Body becomes `<meta .../>` + original body. Schema change for parsers. | |
| Prose header inside body | Prepend `project: foo @ abc1234 "subject"\n\n`. Zero schema change; not machine-extractable. | |

**User's choice:** Inline attrs — **with expanded field set**: `machine`, `project`, `branch`, `head_sha`, `head_subject` (5 fields, not 3). Plain COMMUNE also promoted to EVENT envelope: `<EVENT type="commune" timestamp="..." machine="..." project="..." branch="..." head_sha="..." head_subject="...">body</EVENT>`.
**Notes:** Decisions D-01 / D-05 / D-06 in CONTEXT.md.

### Q1.2 Plain COMMUNE envelope vs prose

| Option | Description | Selected |
|--------|-------------|----------|
| Promote to EVENT envelope | `<EVENT type="commune" .../>`. Uniform with init_signoff/echo_commune. | ✓ |
| Keep prose; prepend metadata line | `COMMUNE (ts) [foo @ abc1234 "subject"]: body`. | |
| Append metadata footer | `COMMUNE (ts): body\n\n[meta: ...]`. | |

**User's choice:** Promote to EVENT envelope.
**Notes:** Clean cutover (parallel to Phase 29 echo_commune envelope cutover). No legacy prose fallback. D-06.

### Q1.3 Context-save body + amend-signoff section

| Option | Description | Selected |
|--------|-------------|----------|
| YAML front-matter block | Prepend `--- project_root: ... ---` block to save / amendment. | ✓ |
| H2 section with bullet list | `## Context Stamp` + bullets. | |
| Inline parenthetical in heading | `## Post-Signoff Amendment (ts · foo @ abc1234)`. | |

**User's choice:** YAML front-matter.
**Notes:** D-07.

---

## Area 2 — Drift surface on download

### Q2.1 How psyche-download presents stamped fields

| Option | Description | Selected |
|--------|-------------|----------|
| Raw fields only — Self diffs | `<psyche-stamp .../>` only. Resumed Self compares against current HEAD itself. | |
| Computed `<drift>` block | `<drift commits_ahead=... repo_changed=... branch_changed=.../>`. Pre-digested. | |
| Both: raw stamp + commit-log preview | Raw stamp + `<commits-since>` containing `git log --oneline`. | |

**User's choice:** Custom — **raw fields + `<current/>` block**, where `<current/>` carries the current value of each raw field plus `commits_since` (stored→HEAD) and `commits_unpulled` (HEAD→upstream).
**Notes:** D-08. Two separate counts. `commits_unpulled` may be `0` when no upstream / offline.

### Q2.2 Mismatch handling (stamp differs from current resume context)

| Option | Description | Selected |
|--------|-------------|----------|
| Loud warning banner | Prepend `⚠️ STAMP DRIFT: ...` before payload. | |
| Silent — fields surfaced, Self interprets | No banner. Trust Self. | |
| Block download with refuse-and-confirm | Exit nonzero unless `--force` / `--cross-project` passed. | |

**User's choice:** Custom — **same-project drift** → instruct Self via AskUserQuestion: *"This project has advanced since my involvement. Should I catch up?"* with multiSelect options `Observe new commits` / `Peek at peer contexts` / `Skip for now` / `Don't ask again`. The `Peek at peer contexts` option only renders when (a) other live-agent contexts for this project are discoverable AND (b) the Phase 24/25 tracked/ restructure has landed; until then hidden. **Cross-project drift** → silent.
**Notes:** D-09 / D-10. AskUserQuestion text + option labels are locked.

---

## Area 3 — project_root form + not-a-repo fallback

### Q3.1 How `project` is derived

| Option | Description | Selected |
|--------|-------------|----------|
| git toplevel basename | `basename(git rev-parse --show-toplevel)`; fallback to cwd basename if not a repo. | ✓ |
| cwd basename | Always cwd basename. | |
| Full absolute path | Full path. Verbose; weak cross-machine. | |
| Both: name + path | `project="foo" project_path="/abs/path"`. | |

**User's choice:** git toplevel basename **if `git` CLI installed**; else cwd basename (not path).
**Notes:** D-02. Subdir-within-repo invocations still stamp the same project.

### Q3.2 How `machine` is derived

| Option | Description | Selected |
|--------|-------------|----------|
| OS hostname | `gethostname()` / `COMPUTERNAME`. Stable, cheap. | ✓ |
| User-configured `$SPT_MACHINE` env | Override knob + fallback. | |
| `USER@HOST` | More uniquely identifying. | |

**User's choice:** OS hostname.
**Notes:** D-03. `$SPT_MACHINE` override deferred.

### Q3.3 Not-a-repo fallback shape

| Option | Description | Selected |
|--------|-------------|----------|
| Omit attrs entirely | Only `machine` + `project` (cwd basename); absence is the signal. | ✓ |
| Sentinel string `'none'` | `head_sha="none" branch="none"`. | |
| Explicit `repo="false"` marker | Self-documenting. | |

**User's choice:** Omit attrs entirely.
**Notes:** D-11. Applies to YAML front-matter too — omit keys, not empty.

### Q3.4 Subject line length cap

| Option | Description | Selected |
|--------|-------------|----------|
| 72 chars + ellipsis | Conventional git subject length. | ✓ |
| 200 chars | Generous. | |
| No cap | Trust git; risk of pathological bloat. | |

**User's choice:** 72 + ellipsis.
**Notes:** D-04.

---

## Area 4 — Echo commune scope + git-call hot-path safety

### Q4.1 echo_commune cwd semantics

| Option | Description | Selected |
|--------|-------------|----------|
| Self's project, looked up from perch | Wrapper resolves Self's project via perch metadata. | ✓ |
| Psyche's psyche_dir basename | Stamp whatever cwd the wrapper happens to be in. | |
| Stamp BOTH: self_project + psyche_project | Adds attr noise. | |

**User's choice:** Self's project, looked up from perch.
**Notes:** D-14. Today via `$OWL list` / perch `info.json`; Phase 24 formalises the lookup. Fall back to psyche_dir basename if lookup fails.

### Q4.2 Git rev-parse caching strategy

| Option | Description | Selected |
|--------|-------------|----------|
| Direct call, soft-fail | git rev-parse per fire; never block. | ✓ |
| Per-process TTL cache | 5s cache; staleness risk. | |
| `.git/HEAD` mtime-invalidated cache | Cheap, correct, FS dependency. | |

**User's choice:** Direct call, soft-fail.
**Notes:** D-12. Trade marginal CPU for correctness + simplicity.

### Q4.3 Git subprocess timeout / failure handling

| Option | Description | Selected |
|--------|-------------|----------|
| 500ms timeout; soft-fail to no-repo shape | Never blocks delivery; stderr warning. | ✓ |
| No timeout; trust git | Will block on pathological repos / fs hangs. | |
| Async background fetch | First fire stamps without git; complexity. | |

**User's choice:** 500ms timeout; soft-fail.
**Notes:** D-13.

---

## Claude's Discretion

- EVENT-attr escaping for `head_subject` — reuse existing `event_attr_escape` in `src/owl/poll.rs`. No new escaper.
- File layout for per-(self_id, project) "don't ask again" suppression marker — planner decides location (likely under `$SPT_HOME/suppressions/`); coordinate with Phase 24 tracked/ layout.
- Module placement for the stamp helper (`fn stamp() -> Stamp`) — `src/common/git.rs` (new) vs extending `src/common/owlery.rs` — planner's call.
- Test coverage breakdown across unit / golden / integration — researcher + planner triangulate; golden fixtures need Linux refresh.

## Deferred Ideas

- `$SPT_MACHINE` env override for `machine` field.
- Computed `<drift>` block with files-changed / shortstat.
- `project_path` (full absolute path alongside basename).
- Cross-project AskUserQuestion opt-in surface.
- Per-process / mtime-based git caching (revisit only on real hot-path impact).
- Reviewed-but-not-folded inbox todos: `add-psyche-peek` (Phase 25 candidate), `capsule-psmux-winname` (v1.9), `sync-ccs-instance-settings` (tooling), `capsule-reattach-existing` (v1.9).
