Fleet changelogs · dev.ecs0.net
rdmsm4x-changelog-20260830-2136-global-standards-shared-tree-discipline

rdmsm4x-changelog-20260830-2136-global-standards-shared-tree-discipline

Host: rdmsm4x · Session: dev-73 (claude@rdmsm4x) Window: 2026-08-30 21:29:54 → 21:36 EDT Instruction: Rich — "resume all approve all work, apply anything that makes sense as global standard."

Approved and committed the day's outstanding ticket-store work, then promoted this session's hard-won lessons into fleet-wide standards and pushed them to all six Macs. Found and fixed a reconciler gap that had left a mandated skill missing on every peer.


1. Approved all outstanding work

~/dev/issues/ working tree was holding 28 uncommitted paths across several agents. Committed as 4ce9d12:

Working tree clean afterwards. Store now at 12 commits.

2. New global standard — shared trees & destructive edits

Added to ~/.claude/CLAUDE.md (compact, operative) with full evidence in the new fleet-operations → references/shared-tree-discipline.md. Kept the CLAUDE.md block short on purpose: that file's own v4.0 header records that bloat is what degraded instruction-following last time.

Every rule is derived from an incident this session, not from principle:

Rule Incident
Re-read before writing a file back; never write back a whole file loaded earlier ISSUE-20260830-39 — a whole-file write-back silently reverted 3 committed fixes, one a data-loss fix
Path-scope every git add 69c1389 swept another agent's in-flight work
Move, never delete; snapshot first the 234-ticket fixture archive
Filter by inclusion, never exclusion made peers' future tickets safe by construction
A fix that deletes to resolve ambiguity is a data-loss path ISSUE-20260830-35
Verify a fix in BOTH directions the glob-fallback anti-overshoot test
Never claim live/published/exposed without reading the docroot I reported 268 pages "live"; the docroot was ../wiki
A test suite must never write to a canonical store the store reached 91% fixture noise

Also added to the fleet-ticketing skill: ticket IDs are unique only per type+date+sequence, so never substring-match on the numeric part (FEAT-20260830-25 vs ISSUE-20260830-25), plus the store-hygiene rules and the "reindex after any git operation" requirement.

3. Reconciler gap found and fixed — ISSUE-20260830-42

fleet_config_reconcile.zsh:177 had a hardcoded single-item loop, for sk in fleet-operations, written 2026-08-25 when that was the only CLAUDE.md-referenced skill. Since then ~/dev/CLAUDE.md rule 28 has required every host to file tickets via the fleet-ticketing skill.

Measured before the fix (test -f per host): fleet-ticketing/SKILL.md was MISSING on all five peers. Present only on rdmsm4x. Exactly the dead-pointer failure the script's own comment warns about. Added fleet-ticketing to the loop with a comment stating the general rule.

Residual risk, filed not fixed: the list is still hand-maintained, so the next CLAUDE.md-referenced skill drifts the same way. Deriving it by grepping canonical CLAUDE.md would be the stronger fix; it changes reconciler behaviour and deserves its own change.

4. Verification — all six hosts

reconcile 21:35:46  canon=536908870030f03a  current=6 updated=0 diverged=0 offline=0 err=0
settings current on all 6 (model=claude-opus-5, defaultMode=bypassPermissions) — no drift

Verified independently rather than trusting the reconciler's own report:

Check Result
CLAUDE.md contains "Shared trees & destructive edits" present on all peers
references/shared-tree-discipline.md exists present on all peers
references/fleet-hosts.md sha 6236fdc9… — identical on all 6
fleet-ticketing/SKILL.md sha 2f170f32… — identical on all 6 (was missing on 5)

A false alarm I nearly reported, now documented

Verifying with a bare hostname made rdmbair15m5 look unreachable while the reconciler claimed success — which reads exactly like a false success report. The reconciler was right and my check was wrong: /etc/hosts maps bare names to LAN IPs, and the host was off the LAN but fine over Tailscale, which is the form the reconciler itself uses (richh@${h}.ts.dataroo.net, line 109).

Rule added to references/fleet-hosts.md: always verify a fleet operation using the same address form the operation used.

5. bin/ticket — a success message that named the wrong path

Both export messages hardcoded ~/Desktop/{id}.html while the code writes to DESKTOP_DIR (~/Desktop/issues/tickets). The message reported a path the file was not at, which read exactly like the containment fix having been reverted again and cost a full investigation to disprove. Fixed both to print the real DESKTOP_DIR (4a8ca76).

A success message that names the wrong path is a real defect — it is the only evidence most callers ever see.

Also swept 22 pre-fix ticket exports out of the Desktop root, using an inclusion filter on the full ticket-ID pattern so the four unrelated pages (directory, ecs0lib-documentation, issues-dashboard, ui-mockups) were left alone. Four duplicates were compared, not clobbered: three root copies were newer and were promoted; one was older and divergent and went to Desktop/issues/superseded/ rather than being deleted. Desktop root now holds 0 ticket exports.

Verification commands

grep -c "Shared trees & destructive edits" ~/.claude/CLAUDE.md          # 1
ls ~/.claude/skills/fleet-operations/references/shared-tree-discipline.md
zsh ~/dev/fleet/maintenance/scripts/fleet_config_reconcile.zsh          # current=6 err=0
cd ~/dev/issues && git log --oneline | head -4

Outstanding — owner actions

  1. ISSUE-20260830-42 residual: derive the reconciler's skill list from canonical CLAUDE.md instead of maintaining it by hand.
  2. ~/dev/issues still has no remote — git gives history, not off-host backup.
  3. Standards are now enforced by documentation, not by tooling. A pre-commit hook rejecting git add -A in shared trees would make the highest-value rule mechanical rather than remembered.

Not touched

No secrets read, written, or referenced. Nothing pushed to any remote. permissions.defaultMode verified bypassPermissions on all six hosts and not modified — reported only, per the standing rule.