fix: facts.sh reads a parentless head as base_ver=(none), not exit 128 #136
No reviewers
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:docs
scope:guards
scope:labels
scope:release-flow
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/ceremony#136
Loading…
Reference in a new issue
No description provided.
Delete branch "build/134-facts-root-commit"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #134.
A repository's first push to
mainis a branch-create push (event.beforeall-zeros) whose head is a root commit —MERGE_SHA^1does not exist,rev-parsefails, andset -ekilledlib/facts.shat exit 128 before a single fact was established. Both 0.2.0 drills hit it independently. Per the issue's decided spec: the head's parent count is read as a fact (git rev-list --parents -n 1, D2), a zero-parent head takes the existingbase_ver="(none)"path (D1) with the belt-and-braces fetch and the basegit showskipped so no rev-scoped command ever runs with an empty rev (D3), and the comments carry the 0.2.0-drill war story (D4). An unresolvableMERGE_SHAstill dies loudly — theparents=assignment propagatesrev-list's 128 underset -e, never masquerading as(none).Acceptance criteria (from #134)
facts.shwithEVENT_BEFOREall-zeros against a root commit exits0and printsbase_ver=(none)—test/facts.test.sh"root commit, all-zeros event.before"EVENT_BEFOREempty (the non-push caller) — "root commit, empty event.before"VERSIONis0.1.0-devproducesceremony=noand a green NOTICE through the full facts→decide chain —test/release-chain.test.shgreenfield casesVERSIONis bare0.1.0with no merged release-labeled PR still refuses (exit 1) and creates nothing — release-chain "a bare unlabeled root commit still refuses"MERGE_SHAstill fails loudly (exit 128) and is not reported asbase_ver=(none)— the D2 pin, both the positive (dies with "bad object") and the negative (no(none)in output) assertionsgit show,git cat-fileorgit fetchruns with an empty rev on the no-base path — both are behind[ -n "$base_sha" ]; the greenfield fixtures run with the refuse-by-default gh stub and noorigin, so a stray fetch/show would surfacetest/facts.test.shcase passes unchanged — including L96–L99, all-zeros/emptyevent.beforeon a parented head still resolve to the first parenttest/run.sh(17 files), CI shellcheck sweep (shellcheck -x, 37 scripts), actionlint — all green locallyWorklog
lib/facts.sh: parent count read viarev-list --parents; zero parents →(none)path; fetch + base show skipped on no-base; comments name the 0.2.0 drills and #134 (D1–D4)test/facts.test.sh: greenfield fixtures (zeros + emptyevent.before, bare root withlabeled-no), D2 pin (badMERGE_SHA→ 128, no(none)in output)test/release-chain.test.sh: end-to-end — greenfield-devroot goes greenceremony=nowith a NOTICE; bare unlabeled root refuses exit 1changelog.d/134.md🔎 reviewing head
fa768510bf🔎 reviewing head
fa768510bfAll acceptance criteria from #134 are satisfied on
fa768510bff3624f40716b53c115968271015ae1.The implementation detects a root commit from a successful
git rev-list --parentsresult, preserves a loud exit 128 for an unresolvableMERGE_SHA, and guards every base-scopedcat-file, fetch, andgit showbehind a non-emptybase_sha. The tests cover all-zeros and emptyEVENT_BEFORE, the parented-head fallback, the green-devfacts→decide path, the bare unlabeled refusal, and the invalid-head negative case. The changelog fragment and incident-linked comments are present.Verified locally:
test/run.sh: 17 test files passed.github/scripts/shellcheck-all.sh: 37 tracked scripts clean.github/scripts/actionlint-all.sh: 6 workflow files cleangit diff --check: cleanCurrent GitHub checks are also green; the two release-on-event jobs are expectedly skipped for this PR.
Verdict: approve
Reviewed head
fa768510bff3624f40716b53c115968271015ae1against #134.Acceptance criteria
event.before→ exit 0,base_ver=(none)event.before→ same-dev→ facts→decide green NOTICE,ceremony=norelease-chain.test.shMERGE_SHAdies loudly (128), never(none)git show/ fetch with empty rev on no-base path[ -n "$base_sha" ]; greenfield fixtures use refuse-by-default gh stubshellcheck -x lib/facts.shclean; CI greenSpec fidelity (D1–D4)
(none)path; decide's table unchanged.git rev-list --parents -n 1— fact, not|| trueon a failed rev-parse. Bad SHA still propagates exit 128 underset -e.git showgated on non-emptybase_sha— avoids the index-read wrong-fact trap the issue names.Diff
Minimal and correct:
lib/facts.sh, the two test files with greenfield + D2 fixtures, andchangelog.d/134.md. No behavior outside the crash path changes.Approve.
🔎 reviewing head
fa768510bfVerdict: approve
Reviewed head
fa768510bff3624f40716b53c115968271015ae1against #134, in a detached worktree.Acceptance criteria
event.before→ exit 0,base_ver=(none)event.before→ same-dev→ceremony=nogreen NOTICE through facts→deciderelease-chain.test.shgreenfield cases, 8/80.1.0unlabeled → refuse exit 1, creates nothingMERGE_SHAdies loudly, never reported as(none)base_ver=(none); reproduced manually (grep count 0)git show/cat-file/fetchwith an empty rev on the no-base pathgit showboth behind[ -n "$base_sha" ]; the index-read hazard from D3 is commentedpackage-json:cases needingnode, absent in this environment and green in CISpec fidelity (D1–D4)
base_ver=(none)path;decide's table is untouched — a-devroot is the green bootstrap, a bare root still refuses.git rev-list --parents -n 1; underset -ean unresolvable head still propagates the 128 — detection, not inference from a failed command. The*" "*split is the right discriminator.git show ":VERSION"), which is the reason this invariant matters.Conventions
changelog.d/134.md, published prose only; noCHANGELOG.mdedit.Notes (non-blocking)
Meets every #134 criterion. Approve.
Handoff — round converged, over to @danmt
What shipped.
lib/facts.shno longer dies at exit 128 when the merge head is a root commit — the shape both 0.2.0 drills hit on a repository's first push tomain(event.beforeall-zeros,MERGE_SHA^1nonexistent). Per #134's decided spec: the head's parent count is read as a fact viagit rev-list --parents -n 1(D2); a zero-parent head takes the existingbase_ver="(none)"path (D1) with the belt-and-braces fetch and the basegit showskipped, so no rev-scoped command ever runs with an empty rev (D3); the comments carry the 0.2.0-drill war story (D4). An unresolvableMERGE_SHAstill fails loudly at 128 — it never masquerades as(none).Rounds. One. The panel (codex, grok, kimi) was requested at 12:16Z on head
fa76851; all three approved that head with no changes requested, so the round converged whole on the first pass — no post-review fix commits, and the reviewed head is the merge head.What was verified.
test/facts.test.shgreenfield fixtures (all-zeros and emptyevent.before, bare root withlabeled-no) plus the D2 loud-death pin (badMERGE_SHA→ 128, no(none)in output);test/release-chain.test.shend-to-end greenfield cases (root-devgoes greenceremony=nowith a NOTICE; bare unlabeled root still refuses exit 1). All pre-existingfacts.test.shcases pass unchanged.fa76851: test suite (17 files), shellcheck sweep (37 scripts), actionlint, labels/scope, labels/reconcile, release-exercise (fixture-chain + both step-replays), self-guards, action-exercise, docs-sync-exercise.Residue / notes.
blocker:ci-redat 12:17Z was false — a queued duplicatelabels/reconcilerun was cancelled and read as red (same shape as #138); the empty-commit retriggerfa76851cleared it and became the reviewed head. No underlying red ever existed.changelog.d/134.mdpublishes with the next release PR's assembly.state:needs-human+ the review request itself, so this summary completes the handoff.This PR could be merged right now. The merge is yours.