rdmsm4x-changelog-20260830-1224-issueroo-queue-cleanup-and-git-baseline
Host: rdmsm4x ·
Session: dev-73 (claude@rdmsm4x)
Window: 2026-08-30 12:17:54 → 12:24:51 EDT
Scope: rdmsm4x:~/dev/issues/ (canonical
fleet ticket store), plus fixture HTML in
~/dev/sites/dev.ecs0.net/issues/ and
~/Desktop.
The canonical fleet ticket store was 91% QA fixture noise and had no version control despite CLAUDE.md rule 28 describing it as "Git/SQLite-backed". The store is now under git, the fixtures are archived (not deleted), and the open queue reads 9 real tickets instead of 256.
Why this was the lane
Rich asked this session to resume all work and coordinate with the
other agents on the host so nothing collided. Six other Claude sessions
were live on rdmsm4x. Scope checks were exchanged with
dev-bf, dev-76, dev-e2, and
dev-d5 before any write:
| Session | Owns |
|---|---|
dev-bf |
rdmsm4x launchd delivery failure; fleet app rebuild/deploy; rdmbair15m5 consolidation |
dev-e2 |
fleet CPU-storm guard (host_storm_guard.zsh,
com.eastcoastscience.stormguard) |
dev-76 |
ISSUE-20260830-25 only |
cd4dc58c / 0da93685 |
apps/replicantDB / apps/logTTY |
dev-73 (this) |
~/dev/issues/ — uncontested, confirmed free by three
peers |
What changed
1. The store is under git for the first time — a rule-28 gap, not just hygiene
~/dev/issues/ had no .git since it was
stood up 2026-08-29. Rule 28 has been asserting a Git-backed store that
did not exist, so every ticket write since then had no history, no audit
trail, and no recovery path. Four commits now exist:
dedb9ddbaseline — all 417 tickets exactly as found, the rollback pointc894e2auntrackindex.sqlite(derived;ticket reindexrebuilds it — a 680K binary blob would churn every commit and conflict on the first cross-host sync)69c1389the fixture archivef88ec7athe two defects filed below
2. 331 QA fixture tickets archived out of the live queue
The adversarial test suite writes into the canonical
store — tests/*.py hardcode
/Users/richh/dev/issues/index.sqlite and
bin/ticket. Every run created real tickets in production.
Result: 234 of 256 open tickets were fixtures, across 12 project slugs
(test-concurrent 48, test 46,
concurrent-test 36, mcp-test 32,
mcp-cli 30, testproj 23,
test-project 6, recovery 3,
recovery-test 3, contention-test 3,
adversarial-suite 3, tree-test 1), plus 97
more in resolved/.
Selection was inclusion-only on those 12 slugs — no title matching, no recency heuristic — so real tickets were untouchable by construction. Everything was moved, never deleted.
3. The fixtures were also PUBLISHED — swept in the same pass
bin/ticket:35-36 exports every ticket's HTML to two
further destinations. Archiving only the Markdown would have left the
fixtures live on a real site:
| Destination | Before | Fixtures pulled | After |
|---|---|---|---|
sites/dev.ecs0.net/issues/ (published) |
352 | 268 | 84 |
~/Desktop |
365 | 284 | 89 |
4. Six empty-title artifacts archived under a separate named category
project: general, empty title,
boilerplate-only body, created inside the test-run windows. Deliberately
not swept under the fixture-project rule — archived to
archive/empty-title-artifacts-20260830/ so the distinction
stays auditable.
Two real defects found and filed — not auto-fixed
ISSUE-20260830-26 (high) —
ticket resolve copies instead of moving.
Four real tickets exist simultaneously in open/
and resolved/ with divergent bodies and contradictory
status: INC-20260829-01, ISSUE-20260829-02,
-03, -04. For INC-20260829-01
even the titles differ, so the open/ copy is a stale
earlier version. Nine archived fixture IDs showed the same shape, so it
is systematic. Not auto-fixed: choosing the authoritative copy per
ticket needs the owning project's judgement.
ISSUE-20260830-27 — the exporter
behaviour in §3. It fires on every real ticket too, so it
re-accumulates immediately; filing one legitimate incident triggers all
three side effects.
Verification
open/ 256 -> 16 files = 7 open + 5 in-progress epics + 4 twinned by ISSUE-20260830-26
ticket list --status open -> 9 rows, all real (7 pre-existing + the 2 filed here)
ticket reindex -> "Reindexed 80 tickets ... regenerated dashboard" (exit 0)
ISSUE-20260830-25 present in open/, absent from every delete list (asserted before the move)
LogTTY/logtty matched case-insensitively — all 3 survive
A safety assertion fired mid-run and was worth having: a substring
check flagged 20260830-25 in the delete list. It was
FEAT-20260830-25 (project testproj, a genuine
fixture) sharing a numeric suffix with ISSUE-20260830-25.
Ticket IDs are only unique per type+date+sequence — a substring
match on the numeric part is unsafe. The real incident was
never in the list.
How to undo
cd ~/dev/issues && git revert 69c1389 # restores all 331 fixture tickets + both HTML sets
cd ~/dev/issues && git reset --hard dedb9dd # or return the whole store to as-found
ticket reindex # rebuild index.sqlite either wayPre-git filesystem snapshot of the index:
~/dev/_backups/issues-index-20260830-1223.sqlite.
Outstanding — owner actions
ISSUE-20260830-26needs a per-ticket decision on which copy of the four is authoritative. Agent judgement is not the right tool; the bodies differ in substance.ISSUE-20260830-27: dropDESKTOP_DIRfrombin/ticket(or make it opt-in). Until then~/Desktopre-accumulates a file per ticket.- The store has no remote. Git gives history but not off-host backup.
~/dev/issues/is still not in any parent repo — it sits inside~/dev, which is not itself a git repo, so nothing nests. Confirmed safe.
Handoffs
dev-d5took the root-cause slice: maketests/*.pyuse an isolated store instead of the canonical one. Writes confined totests/. Archiving without that fix re-pollutes on the next test run.- Collision I caused and disclosed: my
git add -Ain69c1389swept dev-d5's in-progresstests/isolation.py(new, 233 lines) andtests/test_jsonrpc_stdio.pyinto my commit. Nothing lost or altered; dev-d5 notified at 12:29. Later commits are path-scoped. dev-e2was given two guarantees, both held: itsstormguardincidents are protected by construction (project-slug inclusion only, no title or recency matching), and nothing created after 12:30 is swept on that basis.
Not touched
~/Library/LaunchAgents,
~/dev/apps/{Tyrell,LogTTY,replicantDB,XEntropy,updateRoo,AINetNode},
~/dev/_migration/fleet-ingest,
~/dev/issues/tests/, ~/dev/issues/bin/. No
secrets read, written, or referenced. Nothing pushed to any remote.
Addendum — 12:28 → 12:33 EDT: both filed defects fixed and verified
ISSUE-20260830-26 and
ISSUE-20260830-27 are RESOLVED. A peer edited
bin/ticket during the session (backup
bin/ticket.bak-20260830, 12:28). I verified the change
before committing it (80cd202) rather than trusting it:
DESKTOP_DIR→~/Desktop/issues/tickets/, and added to the startup mkdir loop (bin/ticket:36,40). This fixes two things at once: the Desktop clutter, and aFileNotFoundErrorthat wrote the ticket file and then failed the command on any account without~/Desktop— a partially-applied write. Re-ran the repro under aHOMEwith no Desktop: now succeeds.cmd_resolveglobsOPEN_DIRfor every copy of the ID (bin/ticket:1808-1811) instead of unlinking only the path derived from the current title slug. That slug mismatch was the root cause of the twins. All four are clean; zero ticket IDs remain in bothopen/andresolved/.
Residual
gap found by probe — ISSUE-20260830-33 (new, open)
The glob cleanup is unreachable when the index's
file_path is stale. cmd_resolve reads
file_path from SQLite, then
if not old_path.exists(): sys.exit(1) — which fires
before the cleanup, in exactly the drift case the cleanup
defends against. The ticket then cannot be resolved through the CLI at
all until someone reindexes by hand.
This matters more now than it did yesterday: the store went
under git today, and a git revert or
checkout produces precisely that stale-index state. Anyone
using the documented undo command should run ticket reindex
immediately after, which the undo instructions above already
specify.
Verification of dev-d5's test-isolation slice (checked, not taken on trust)
Zero hardcoded /Users/richh/dev/issues remain in
executable test code — the 2 grep hits are docstring prose.
tests/isolation.py present with IsolatedStore,
.seed(), FIXTURE_PROJECTS,
guard_not_canonical; the symlink hardening is real
(candidate resolved at :63, root at :66). Canonical open/
carries 0 fixture-project tickets. dev-d5's read of the isolation
mechanism is correct: bin/ticket:29 derives everything from
expanduser("~/dev") with no --root and no env
var, so a temp HOME is the only lever.
dev-d5's six modified files remain uncommitted by
their own choice (they won't commit without Rich asking). Their
enhancement suggestion — a first-class TICKET_ROOT env var
so isolation is explicit rather than a side effect of
expanduser() — is sound but unrequested, so it is recorded
here rather than filed.
The queue is now doing its job
Within minutes of the cleanup, peers filed five real, high-value
tickets that would previously have been invisible among 234 fixtures —
ISSUE-20260830-28 (LogTTY bundle-identifier mismatch would
create a new app identity on deploy), -30
(XEntropy/updateRoo/AINetNode are arm64-only, so the fleet deploy
directive is a build task), -31 (replicantDB
main tracks roodb-legacy; a plain push would
inject 125 commits into the wrong project), -32, and
-33.
Net: open queue 256 → 11, all real. Store at 7 commits.
CORRECTION — 12:35 EDT: the fixture pages were never actually served
§3 above overstated the impact and is corrected here. I reported that 268 fixture pages were "live on the published dev.ecs0.net site". They were not reachable.
infra/compose.yaml:37 mounts ../wiki as the
nginx docroot: - ../wiki:/usr/share/nginx/html:ro. The
exporter was writing to sites/dev.ecs0.net/issues/, which
is not under that docroot. So the fixture pages were
written to an unserved directory and never appeared on the site.
The defect was real — the exporter wrote 352 files to a path nothing
serves, which is its own bug, and the fixture sweep was still correct
housekeeping. But nothing was publicly exposed, and my
earlier "live on a real site" framing was wrong.
SITES_ISSUES_DIR is now corrected to
sites/dev.ecs0.net/wiki/issues/ (86 pages, matching the
real ticket count) and committed in 7c58362.
I did not catch this. A peer did, while fixing
ISSUE-20260830-33.
ISSUE-20260830-33
— FIXED, verified in both directions
cmd_resolve now globs OPEN_DIR and
RESOLVED_DIR for the ID when the indexed
file_path is absent, adopts the match, and announces
(index was stale; adopted <name>). Verified in an
isolated store:
- A — the fix works: renamed file + stale index now
resolves cleanly (
open=0 resolved=1). - B — anti-overshoot holds: a ticket whose file is
genuinely gone still exits 1 with
missing on disk. This was the case worth checking — a glob fallback is exactly the shape of fix that can turn a real "this ticket is missing" error into a silent success. It does not.
Regression cover: tests/test_stale_index_resolve.py
(dev-d5, 3 scenarios including the anti-overshoot case), plus
IsolatedStore.make_index_stale() as a reusable fixture for
"simulate a git checkout leaving the index behind".
Store now at 9 commits. All four defects filed this session are fixed and verified.
12:36
EDT — the fix for -26 introduced a data-loss path:
ISSUE-20260830-35 (open)
cmd_resolve's stale-copy cleanup unlinks every
open/ copy of an ID unconditionally — no
compare, no merge, no backup. Probe in an isolated store: a second
open/ copy carrying a unique marker line survives in
zero files after ticket resolve.
This is not theoretical, and the evidence is in the repair commit
itself. ced12b1 records that for the real twins
ISSUE-20260829-02/03/04 the open/ copies
"carried parent links and clean comments added AFTER
resolution", and its author deliberately merged both sides
rather than deleting. That is precisely the content this code path now
destroys automatically. The historical twins were repaired by hand; the
code that replaced them still discards.
The code comment assumes the stray is a rename artifact ("title edited after creation"). True for renames — not true for the divergent-content case that actually occurred here.
Suggested fix: compare before unlinking; if different, move to
archive/ or closed/ rather than delete. "So
status can never be ambiguous" is satisfied by moving the file out of
open/.
Also corrected in
ced12b1, against my report
INC-20260829-01's open/ copy was a
fixture (template placeholders, children pointing at archived
fixture FEATs) colliding with a real ticket ID — not "a stale earlier
version of a real ticket" as I characterised it in
ISSUE-20260830-26. My reading of the twin mechanism was
right; my reading of that one ticket's content was not.
Closing state — 12:36 EDT
- Store: 10 commits, working tree clean, history linear and intact.
- Open queue: 12, all real (was 256, 91% fixtures).
- Filed this session:
-26fixed ·-27fixed ·-33fixed, verified both directions ·-35open (the data-loss path above). - Ownership handed back:
bin/ticketis being actively worked by session01Rp1aeE7pKFUzcWyjRgU88Y;tests/by dev-d5. Both notified on the bus.
What I got wrong this session, for the record
- Claimed 268 fixture pages were "live on the published site". They
were written to an unserved directory —
infra/compose.yaml:37mounts../wiki, notissues/. Caught by a peer, not by me. The export defect was real; the exposure was not. - Characterised
INC-20260829-01's open copy as a stale real ticket; it was a fixture collision. git add -Aswept dev-d5's in-progress work into my commit. Disclosed; later commits scoped.