Post-push cleanup deletes an uncondensed shadow ref when its session state is malformed
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 5.1k
- Forks
- 475
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 178
Description
What happened?
StateStore.List skips an individual session-state file when Load returns a read
or JSON-decoding error, but still returns the remaining inventory successfully.
CleanupPushedShadowBranches treats that inventory as complete during both its
initial protection scan and its pre-deletion recheck.
In a real-Git regression, valid state protected the shadow branch (deleted=0).
After replacing only the associated state JSON with malformed bytes,
ListSessionStates returned success with an empty inventory. Cleanup then
returned success, reported deleted=1, and removed the shadow ref while leaving
the malformed state file in place.
The checkpoint was no longer reachable through any ref. Its object remained
readable and was reported as unreachable by git fsck, meaning later Git garbage
collection may permanently remove the shadow-only history.
Expected behavior: unattended destructive cleanup should fail closed when any
state record cannot be loaded. It should preserve the ref and surface an error.
Steps to reproduce
- Check out commit b58e65cd722400eddb5c790c76aeca4fa8248703.
- Using newShadowCleanupEnv, create a strict-format shadow branch and an active,
uncondensed session state mapped to it. - Call CleanupPushedShadowBranches with valid state; observe deleted=0.
- Replace only .git/entire-sessions/.json with {"session_id":.
- Observe ListSessionStates returns no error and zero states.
- Call CleanupPushedShadowBranches again; observe deleted=1 and the ref removed.
Run:
go test ./cmd/entire/cli/strategy
-run '^TestETOEExp03901_MalformedSessionStateUnprotectsShadowBranch$'
-count=10 -v
Two separate 10-run invocations reproduced the result.
Entire CLI version
Installed stable CLI: Entire CLI 0.10.5
OS and architecture
Microsoft Windows [Version 10.0.26200.9168], amd64
Agent
Codex. The behavior is agent-independent and was reproduced by calling the Go cleanup function directly.
Terminal
PowerShell 7.6.5 , Codex integrated terminal
Logs / debug output
control: deleted=0 branch_exists=true state_count=1
canary: deleted=1 branch_exists=false state_count=0 state_file_exists=true
marker: ref_reachable=false object_readable=true fsck_unreachable=true named_refs=[]
PASS
Additional context
Go 1.26.6; Git 2.46.0.windows.1. The reproduction used a disposable ordinary
repository, synthetic data, no remote push, and no Git GC.
This does not claim deletion of working-tree files or normal committed history.
Real-world state-corruption frequency is unknown. The confirmed problem is that
cleanup fails open once a state-load failure exists.
A likely fix is a strict inventory path for destructive consumers: any per-file
load failure should preserve candidate refs and return an observable error.
PR #2230 is related but distinct: it tightened branch ownership and expected-hash
deletion, not session-inventory completeness.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with CleanupPushedShadowBranches and the StateStore.List/ListSessionStates paths in cmd/entire/cli/strategy, then run the named regression test: TestETOEExp03901_MalformedSessionStateUnprotectsShadowBranch. Confirm that a malformed session-state file causes cleanup to return an error, preserve the shadow ref, and avoid reporting deletion.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- cli, devtools
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100