What happened
update-epic <id> --attribute-commit <sha> was run for four commits across two epics. The
engine reported success for each. .conductor/state.json in the working tree held all four.
The commit made immediately afterwards — 0645872, whose message says
chore(conductor): attribute gh-128/gh-134 commits, register #139 — contains
"attributedCommits": [] for both epics. So do the two commits after it. The values sat in the
working tree, uncommitted and unnoticed, across three commits and two branch merges.
$ for c in 0645872 8758026 728dc1e HEAD; do
git show "$c:.conductor/state.json" | jq -c '[.epics[]|select(.id=="gh-134-…")][0].attributedCommits'
done
[]
[]
[]
[] # …while the working tree held ["17f8665","dc62cc9","56c50ab"]
The commit that added --register #139 in the same invocation DID land. So the file was written
and committed; the attribution array specifically was not in what got committed.
Root cause: NOT established
Stating that plainly rather than guessing. Candidates not yet distinguished:
- A later engine invocation in the same turn re-serialising
state.json from a copy read before
the attribution (the lost-update shape 0.26.0's write-conflict guard exists for). Against this:
.conductor/write-conflicts.log is empty, so the guard did not fire.
git commit -- <pathspec> snapshotting the path at commit time, with a hook rewriting the file
inside that window. The pre-commit hook runs the full suite, which is a ~75s window.
- Something specific to
git add -A <path> && git commit -- <path> used together.
Why it matters more than one lost array
attributedCommits is the ONLY input to whether a recorded Gate 2 verdict is fresh or stale — the
last entry is the endpoint a verdict's headSha is compared against. An epic whose attributions
silently vanish gets a Gate 2 that reads fresh when it is not, which is the precise failure
0.27.0's gate-evidence work was built to stop. It is also the second time this repository has hit
"the working tree holds the intended edit and the commit does not"; the first produced the
standing task item verify against the commit, not the working tree, and that item is the only
reason this was caught at all.
What would have caught it
Nothing did, and nothing would have. The existing check
delivered-epic-attributed-no-commits fires only on a delivered epic with a passing Gate
2 — these epics were queued at the time. There is no signal for "an epic has commits in
flight and an empty attribution array."
Proposed
Two independent pieces, both cheap:
- Make the write verifiable. Have
--attribute-commit read back what it wrote and fail loudly
if the value is not present on disk after the write. A verb that reports success for a write
that did not persist is the whole defect.
- Add the missing detector. An
integrity check for an epic in a non-terminal status whose
attributedCommits is present and empty while its epic has a recorded gate verdict or a
release assignment — i.e. work is demonstrably underway and nothing is attributed.
Then chase the mechanism with the two candidates above, ideally by reproducing under a hook that
sleeps inside the commit window.
Current state
Not data-lost: the values were re-applied and verified against the commit rather than the tree,
and are correct as of HEAD. Filed rather than chased now, deliberately — the record is right,
the mechanism is not understood, and understanding it is not urgent.
What happened
update-epic <id> --attribute-commit <sha>was run for four commits across two epics. Theengine reported success for each.
.conductor/state.jsonin the working tree held all four.The commit made immediately afterwards —
0645872, whose message sayschore(conductor): attribute gh-128/gh-134 commits, register #139— contains"attributedCommits": []for both epics. So do the two commits after it. The values sat in theworking tree, uncommitted and unnoticed, across three commits and two branch merges.
The commit that added
--register #139in the same invocation DID land. So the file was writtenand committed; the attribution array specifically was not in what got committed.
Root cause: NOT established
Stating that plainly rather than guessing. Candidates not yet distinguished:
state.jsonfrom a copy read beforethe attribution (the lost-update shape 0.26.0's write-conflict guard exists for). Against this:
.conductor/write-conflicts.logis empty, so the guard did not fire.git commit -- <pathspec>snapshotting the path at commit time, with a hook rewriting the fileinside that window. The pre-commit hook runs the full suite, which is a ~75s window.
git add -A <path> && git commit -- <path>used together.Why it matters more than one lost array
attributedCommitsis the ONLY input to whether a recorded Gate 2 verdict is fresh or stale — thelast entry is the endpoint a verdict's
headShais compared against. An epic whose attributionssilently vanish gets a Gate 2 that reads fresh when it is not, which is the precise failure
0.27.0's gate-evidence work was built to stop. It is also the second time this repository has hit
"the working tree holds the intended edit and the commit does not"; the first produced the
standing task item verify against the commit, not the working tree, and that item is the only
reason this was caught at all.
What would have caught it
Nothing did, and nothing would have. The existing check
delivered-epic-attributed-no-commitsfires only on a delivered epic with a passing Gate2 — these epics were
queuedat the time. There is no signal for "an epic has commits inflight and an empty attribution array."
Proposed
Two independent pieces, both cheap:
--attribute-commitread back what it wrote and fail loudlyif the value is not present on disk after the write. A verb that reports success for a write
that did not persist is the whole defect.
integritycheck for an epic in a non-terminal status whoseattributedCommitsis present and empty while its epic has a recorded gate verdict or arelease assignment — i.e. work is demonstrably underway and nothing is attributed.
Then chase the mechanism with the two candidates above, ideally by reproducing under a hook that
sleeps inside the commit window.
Current state
Not data-lost: the values were re-applied and verified against the commit rather than the tree,
and are correct as of
HEAD. Filed rather than chased now, deliberately — the record is right,the mechanism is not understood, and understanding it is not urgent.