rdmsm4x — project-state hook v1.3 (dirty-tree gate scope) + resume re-pin
The hook's own Stop reminder fired on a clean project; the gate behind it was counting the whole repository instead of the project, and has been scoped and redeployed to all six hosts.
Window: 2026-09-01 18:27 – 18:35 EDT · Host: rdmsm4x · Author: claude@rdmsm4x
1. What changed
| scope | file | from → to |
|---|---|---|
| all six hosts | ~/dev/fleet/ops/project-state/project_state_hook.py |
v1.2 00c0ca0031bd → v1.3
1163565b2e99 |
| rdmsm4x | ~/dev/fleet/ops/resume/PINNED-SESSION-fleetcc |
re-pinned, stale first-action removed |
| rdmsm4x | ops/project-state/{SESSION-STATE.md,README.md} |
v1.3 sections added |
Commits in ~/dev/fleet: 85493fc (re-pin),
1282dff (v1.3).
2. The defect
tree_changed() gates the Stop reminder so it only fires
when the project actually changed. It ran:
git -C <proj> status --porcelain
git status reports the whole
repository. -C sets the working directory; it does
not scope the report. Every project nested inside
~/dev/fleet — a repo six agents write concurrently —
therefore read as "changed" permanently.
Measured: ~/dev/fleet 292 dirty
entries, ~/dev/fleet/ops/project-state 0.
The gate said 292 and nagged about a clean project.
Fix: pass the project as a pathspec —
git -C <proj> status --porcelain -- <proj>.
3. Verification
Both directions, because a gate whose failure mode is silence needs the negative case proven:
| project | dirty lines | fires? | expected |
|---|---|---|---|
portfolio-consolidation (untouched) |
0 | no | no |
ops/project-state (hook edited this turn) |
1 | yes | yes |
ops/resume (another agent in flight) |
15 | yes | yes |
Before the fix all three returned 292. Deployment verified by sha on
each host, not by exit code: all six report
1163565b2e99.
4. The pattern this is the third instance of
| claimed to measure | actually measured | |
|---|---|---|
wedged syspolicyd (2026-08-31) |
daemon health | CPU% — 0.0%, healthy-looking because wedged |
| hook v1.1 (2026-08-31) | project containing the marker | marker's grandparent, truncated to depth 2 |
| hook v1.2 (2026-09-01) | this project's dirty files | the entire repository's dirty files |
All three returned a plausible value. All three were caught by the thing they were breaking, not by a test. A boolean cannot detect this class — record counts.
5. Resume path (informational, changed by a peer)
com.eastcoastscience.claude-resume-fleetcc was retired
today under ISSUE-20260831-21 and moved to
ops/resume/archive/launchagents-retired-20260901/ — nothing
deleted. One agent, com.eastcoastscience.resume-dispatcher,
now reads pins.d/30-fleetcc.pin, which points at
PINNED-SESSION-fleetcc. Verified independently before
relying on it: plist archived, dispatcher loaded, pin resolves,
dispatcher.log shows five correct skips of this session by
id.
The pin's own text was stale — it still named a checklist that ran
this morning as FIRST ACTION ON RESUME. Rewritten.
--status --dry-run after the edit still parses it.
Peer stated this session is "third of three under the cap", implying
it is marginal. From the 18:19 run
(dispatched=3 capped=2 live=11), the cap counts
actual dispatches, not enumerated pins — eleven live
sessions skipped without consuming a slot. At a cold boot priority 30 is
inside the cap. Correction sent back.
6. How to undo
cd ~/dev/fleet && git revert 1282dff 85493fc
then redeploy the reverted project_state_hook.py to the
five spokes the same way (base64 over ssh, verify sha per host). v1.2
backup for this host is in the session scratchpad only — the git history
is the durable copy.
7. Outstanding, unchanged, none blocking
OPEN-4 (rdmsm4x LAN path; mitigated by a REMOVE-ME
.local→Tailscale block in ~/.ssh/config),
OPEN-2 for the four unrebooted spokes, OPEN-3, ISSUE-20260831-19 (why
syspolicyd wedged — if it recurs, capture
spindump <pid> BEFORE restarting the daemon). All are
Rich's calls.