Skip to content

feat(repo): publish CLAUDE.md, the backlog and the worktree guide; restore the backlog gate#6

Merged
wshallwshall merged 1 commit into
mainfrom
repo-hygiene
Jul 27, 2026
Merged

feat(repo): publish CLAUDE.md, the backlog and the worktree guide; restore the backlog gate#6
wshallwshall merged 1 commit into
mainfrom
repo-hygiene

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

The cutover put these in a gitignore private-only block. Three of them didn't need to be there, and keeping them out cost more than it protected.

Why CLAUDE.md is the important one

git worktree add cannot deliver an untracked file. Verified on both live worktrees before this change:

MessageFoundry-flake-monitor-spin:     MISSING  CLAUDE.md
mefor-jdbc-support-c275bd:             MISSING  CLAUDE.md

So every worktree came up with zero project conventions loaded — no HL7 parsing rules, no PHI rules, no architecture constraints. Silently. It only appeared to work in the primary checkout because an untracked copy happens to sit on disk there. Tracking it fixes that permanently and versions it for the first time since the cutover.

The backlog gate was stranded, not dead

backlog-hygiene.yml was never published — not gitignored, just never shipped — so the banner/number-reuse gate ran only on the private vault, which no longer receives the work. And tests/test_backlog_status_check.py was skipping permanently on a reason string describing the retired mirror topology.

Both now run: 229 backlog items validated, 15 previously-skipped assertions live.

Redactions (2 sites)

  • L592–593 enumerated the real estate/vendor tokens and the site-code pattern — i.e. the leak gate's own protected list. Replaced with a pointer to the private token file.
  • L6837 carried a phrase a [names] detector flags as a migration artifact. Rephrased to the hyphenated form the same document already uses in item #105; meaning unchanged.

How this was verified

The leak gate was run with the real token set, reconstructed from the retired scripts/publish/scan_forbidden.py in the vault. It reports:

loaded names=7, estate=13, estate_file_scanned=12, site_prefixes=1

— identical to the counts CI logs using its own secret — and exits 0 with no findings. So this was checked by the project's authoritative gate, not an approximation of it.

Worth stating plainly: the token-value scan I ran first missed the second redaction. The real gate caught it. That's the whole argument for not trusting a partial scan before an irreversible publish.

Ledger gate fix, forced by the import

The gate crashed on git show origin/main:docs/BACKLOG.md (exit 128) — the base has no version of a file being added. Treating that as an empty ledger would be worse than the crash: every heading then reads as a brand-new number and the gate reports ~229 items as "not allocated to this worktree".

Absence is now probed explicitly (base_has) rather than inferred from an error, so git() keeps raising on real failures — the property that stops a swallowed error reading as "no numbers taken".

Two tests added. The first is mutation-verified (neutralise the guard, it fails). The second pins a system-level property only, and its docstring says so — removing base_has's own rev-parse guard leaves it green, so claiming otherwise would be false assurance.

Still private, each for its own reason

Path Reason
docs/security/ (32 files), docs/reviews/ Posture and findings detail — an attacker roadmap.
docs/CI-TOPOLOGY.md Stale. Documents the private/mirror split and scripts/publish/, which the cutover deleted. Publishing it would actively mislead. Needs a rewrite or deletion.
docs/Secure_Development_Standards.md Scans clean, but was deliberately pulled from the public PyPI sdist (#1020). Reversing that is an owner decision, not a side effect.

🤖 Generated with Claude Code

…store the backlog gate

The cutover put these in a gitignore private-only block. Three of them did not need to be there, and
keeping them out cost more than it protected.

CLAUDE.md is the important one. It is the project's persistent context, and `git worktree add` cannot
deliver an untracked file -- so EVERY worktree came up with zero project conventions loaded. Verified
on both live worktrees before this change: no HL7 parsing rules, no PHI rules, no architecture
constraints, nothing. It only appeared to work in the primary checkout because an untracked copy
happens to sit on disk there. Tracking it fixes that permanently and versions it for the first time
since the cutover.

docs/BACKLOG.md and .github/workflows/backlog-hygiene.yml go together. The workflow was never
published at all -- not gitignored, just never shipped -- so the banner/number-reuse gate ran only on
the private vault, which no longer receives the work. tests/test_backlog_status_check.py was
skipping permanently on a reason string that describes the retired mirror topology. Both now run:
229 backlog items validated, 15 previously-skipped assertions live.

docs/WORKTREES.md is a process doc with no customer content, and CLAUDE.md links to it.

REDACTIONS (2 sites, both verified):
  * L592-593 enumerated the real estate/vendor tokens and the site-code pattern -- i.e. the leak
    gate's own protected list. Replaced with a pointer to the private token file.
  * L6837 carried a phrase a [names] detector flags as a migration artifact. Rephrased to the
    hyphenated form the same document already uses in item #105; meaning unchanged.

VERIFICATION. The leak gate was run with the REAL token set, reconstructed from the retired
scripts/publish/scan_forbidden.py in the vault. It reports `names=7, estate=13, estate_file_scanned=12,
site_prefixes=1` -- identical to the counts CI logs with its own secret -- and exits 0 with no
findings. So this was checked by the project's authoritative gate, not an approximation of it. Both
redactions were found BY that gate; the token-value scan I ran first missed the second one.

LEDGER GATE FIX (scripts/hooks/ledger_check.py), forced by the import itself. The gate crashed on
`git show origin/main:docs/BACKLOG.md` (exit 128) because the base has no version of a file being
ADDED. Treating that as an empty ledger would have been worse than the crash: every heading in the
imported file then reads as a brand-new number and the gate reports ~229 items as "not allocated to
this worktree". So absence is now probed EXPLICITLY (`base_has`) instead of inferred from an error --
`git()` keeps raising on real failures, which is the property that stops a swallowed error reading as
"no numbers taken". Two tests added; the first is mutation-verified (neutralise the guard, it fails).
The second pins a system-level property only -- its docstring says so, because removing base_has's own
rev-parse guard leaves it green.

STILL PRIVATE, each for its own reason:
  * docs/security/ (32 files) and docs/reviews/ -- posture and findings detail; an attacker roadmap.
  * docs/CI-TOPOLOGY.md -- STALE. It documents the private-repo/public-mirror split and scripts/publish/,
    which the cutover deleted. Publishing it would actively mislead; it needs a rewrite or deletion.
  * docs/Secure_Development_Standards.md -- scans clean, but it was deliberately pulled from the public
    PyPI sdist (#1020). Reversing that is an owner decision, not a side effect of this change.
@wshallwshall
wshallwshall merged commit 9e4e614 into main Jul 27, 2026
32 checks passed
@wshallwshall
wshallwshall deleted the repo-hygiene branch July 27, 2026 14:17
wshallwshall added a commit that referenced this pull request Jul 27, 2026
…olation (#10)

Publishing docs/BACKLOG.md (#6) broke the ledger gate on every branch cut before that merge, including
PR #7 -- whose diff touched release.yml and leak-gate docs and nothing near a ledger:

    OSError: git show HEAD:docs/BACKLOG.md failed (128): fatal: path 'docs/BACKLOG.md' does not exist
    in 'HEAD'

CI's change set is `diff base HEAD`. Once origin/main gained the file, a branch that predates it lists
docs/BACKLOG.md as CHANGED -- as a deletion relative to base -- despite never touching it. check_backlog
then read HEAD for a copy that was never there.

`base_has` (added when the file was imported) covers the opposite end: absent on BASE, being added.
This is the mirror case -- present on base, absent on HEAD -- and it is the one that fires repeatedly,
because it hits every open branch at once rather than the single commit that publishes the file. Both
probes now share `_obj_exists`, which asks `git cat-file -e` directly rather than inferring absence
from an exception: `git()` must keep raising on real failures, since an error swallowed as "empty
ledger" reads as "no numbers taken" -- the false-clean this gate exists to prevent. "The path is not on
that ref" is the one non-failure, so it gets its own probe instead of a broad `except`.

`head_has` mirrors `head_text`'s ref exactly (HEAD in CI, the INDEX locally), so the guard cannot
disagree with the read it guards.

Mutation-verified: neutralise the guard and the new test fails with the same exit-128 shape seen in CI.

Worth noting for whoever meets this next: the failure looked like an unrelated flake on somebody else's
PR, and the first re-run "fixed" nothing -- the run before it had failed for a genuinely different,
genuinely random reason (a 3-char needle colliding with base64 ciphertext, fixed separately). Two
different faults on consecutive attempts of the same leg is exactly the shape that gets misread as one
flaky test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant