fix(preflight): grade whether the path-alpha push can land, and block an unrecorded first cut #1568

Merged
alex merged 2 commits from claude/1482-preflight-push-grade into main 2026-10-06 21:08:54 +02:00
Member

Refs #1482

Summary

Two changes, one per half of the tracker.

  1. rt adopter-preflight grades the push, not the token. When RELEASE_TOOLKIT_TOKEN is set, it now reads the default branch and its protection. It prints a separate push: line saying what it checked.
  2. rt decide no longer passes a first cut that no manifest records. It used to bootstrap from the cut's own tag, compare equal and carry on. That case is now a pending-cut state with pending_manifest=absent.

What each AC gets

AC Disposition
grade whether path α can COMPLETE gradePathAlphaPush. It follows preflight-push-whitelist's order: enable_push first (#1313), then the whitelist through gates.PreflightPushWhitelist.
the capability line names what it checked Pass lines read token present AND "release-bot" may push to "main" (whitelisted), or token present AND direct push to "main" is enabled with no whitelist.
first cut: an absent manifest is its own state checkUnrecordedCut covers manifest absent + the newest tag is the toolkit's own cut. It returns PendingCutError{ManifestAbsent: true}. That gives mode=blocked, reason=pending_cut and pending_manifest=absent, with its own error text.
a published release with no manifest write is detectable The same path. pending_release_published=true routes to recovery arm (1): "it shipped, hand-write the manifest bookkeeping, DO NOT run recover-pending-cut".

Decision tree

The preflight cannot know the bot's login. A secret's value, and so its owner, is not readable.

  • A whitelist is graded against --bot <login> when the caller passes one.
  • Without --bot, a whitelisted branch is could-not-grade (warn, exit 0). It is not a pass, and it is not a guess at release-bot. Guessing would hard-fail every adopter whose bot has another name.
  • enable_push=false is a FAIL either way. No login can push (the Storey state at 18:40), so the identity doesn't matter.
  • A 404 on branch_protections/<branch> is a WARN, not a pass. The endpoint is keyed by rule name, so a glob rule would hide behind it.

What tells an unrecorded cut from an ordinary bootstrap. A tag with no manifest is normal for an adopter whose releases predate the toolkit. So the new state fires only when the tagged commit (squash/rebase/ff) or its second parent (merge commit) carries the toolkit's prepare subject for that version. TestDecide_1482PreAdoptionTagIsAnOrdinaryBootstrap is the positive control.

When it blocks. It mirrors checkOrphanChangelog: it blocks on the first release-relevant, non-plumbing commit after the cut. A walk with nothing release-relevant only logs. A path-γ cut writes its manifest through a follow-up PR, and pushes can land before that merges. Blocking there would red every path-γ first cut. Blocking at once is right only if path γ is ruled out, and decide cannot tell which path ran.

Not in scope

  • Tier-3 split pair (RELEASE_TOOLKIT_GIT_TOKEN + RELEASE_TOOLKIT_API_TOKEN, a deploy-key push). The adopter preflight still only looks at RELEASE_TOOLKIT_TOKEN. rt preflight-push-whitelist already grades push_whitelist_deploy_keys at cut time.
  • No workflow change. reason stays pending_cut, so reusable-release.yml's generic pending-cut ::error:: arm handles the new state. Its text ("not recorded in the manifest") is true for it. pending_manifest is a decide-stdout key only, not a declared workflow output.

Self-bootstrap (AGENTS.md §2)

No re-pin: toolkit-self floats @main (AGENTS.md §2.1). This is Go-only, so adopters get it from the next cut tag.

Verification

  • go test ./... in golang:1.25 on an LF clone at 6b1a2e5: 30/30 packages ok.
  • golangci-lint run (CI image forgejo-ci-go:latest): 0 issues.
  • fragment-check (content mode): density PASS on both fragments.
  • Mutation check: 7/7 killed.
    • M1: secret alone passes.
    • M2: enable_push=false ignored.
    • M3: run never reads protection.
    • M4: equal-version branch removed.
    • M5: no second-parent check.
    • M6: any tag counts as a toolkit cut.
    • M7: a relevant commit after the cut is ignored.
  • The run-level test serves Storey main's measured state at the cut (enable_push=false, empty whitelist, #1482 comment 112517) and asserts a blocking fail.

⚠️ Not measured against a live forge. The branch-protection read reuses GetBranchProtection, which preflight-push-whitelist already exercises. No adopter cut has run through the new decide state.

Review request

Please verify at source (AGENTS.md §6):

  1. checkUnrecordedCut cannot fire on toolkit-self, which always has a manifest, or on an adopter with a manifest.
  2. The ^2 second-parent read matches how a merge-commit adopter's cut tag lands.

🤖 Generated with Claude Code

Refs #1482 ## Summary Two changes, one per half of the tracker. 1. **`rt adopter-preflight` grades the push, not the token.** When `RELEASE_TOOLKIT_TOKEN` is set, it now reads the default branch and its protection. It prints a separate `push:` line saying what it checked. 2. **`rt decide` no longer passes a first cut that no manifest records.** It used to bootstrap from the cut's own tag, compare equal and carry on. That case is now a pending-cut state with `pending_manifest=absent`. ## What each AC gets | AC | Disposition | |---|---| | grade whether path α can COMPLETE | `gradePathAlphaPush`. It follows `preflight-push-whitelist`'s order: `enable_push` first (#1313), then the whitelist through `gates.PreflightPushWhitelist`. | | the capability line names what it checked | Pass lines read `token present AND "release-bot" may push to "main" (whitelisted)`, or `token present AND direct push to "main" is enabled with no whitelist`. | | first cut: an absent manifest is its own state | `checkUnrecordedCut` covers manifest absent + the newest tag is the toolkit's own cut. It returns `PendingCutError{ManifestAbsent: true}`. That gives `mode=blocked`, `reason=pending_cut` and `pending_manifest=absent`, with its own error text. | | a published release with no manifest write is detectable | The same path. `pending_release_published=true` routes to recovery arm (1): "it shipped, hand-write the manifest bookkeeping, DO NOT run recover-pending-cut". | ## Decision tree **The preflight cannot know the bot's login.** A secret's value, and so its owner, is not readable. - A whitelist is graded against `--bot <login>` when the caller passes one. - Without `--bot`, a whitelisted branch is **could-not-grade** (warn, exit 0). It is not a pass, and it is not a guess at `release-bot`. Guessing would hard-fail every adopter whose bot has another name. - `enable_push=false` is a FAIL either way. No login can push (the Storey state at 18:40), so the identity doesn't matter. - A 404 on `branch_protections/<branch>` is a WARN, not a pass. The endpoint is keyed by rule name, so a glob rule would hide behind it. **What tells an unrecorded cut from an ordinary bootstrap.** A tag with no manifest is normal for an adopter whose releases predate the toolkit. So the new state fires only when the tagged commit (squash/rebase/ff) or its second parent (merge commit) carries the toolkit's prepare subject for that version. `TestDecide_1482PreAdoptionTagIsAnOrdinaryBootstrap` is the positive control. **When it blocks.** It mirrors `checkOrphanChangelog`: it blocks on the first release-relevant, non-plumbing commit after the cut. A walk with nothing release-relevant only logs. A path-γ cut writes its manifest through a follow-up PR, and pushes can land before that merges. Blocking there would red every path-γ first cut. Blocking at once is right only if path γ is ruled out, and decide cannot tell which path ran. ## Not in scope - **Tier-3 split pair** (`RELEASE_TOOLKIT_GIT_TOKEN` + `RELEASE_TOOLKIT_API_TOKEN`, a deploy-key push). The adopter preflight still only looks at `RELEASE_TOOLKIT_TOKEN`. `rt preflight-push-whitelist` already grades `push_whitelist_deploy_keys` at cut time. - **No workflow change.** `reason` stays `pending_cut`, so `reusable-release.yml`'s generic pending-cut `::error::` arm handles the new state. Its text ("not recorded in the manifest") is true for it. `pending_manifest` is a decide-stdout key only, not a declared workflow output. ## Self-bootstrap (AGENTS.md §2) No re-pin: toolkit-self floats `@main` (AGENTS.md §2.1). This is Go-only, so adopters get it from the next cut tag. ## Verification - `go test ./...` in `golang:1.25` on an LF clone at `6b1a2e5`: 30/30 packages ok. - `golangci-lint run` (CI image `forgejo-ci-go:latest`): 0 issues. - `fragment-check` (content mode): density PASS on both fragments. - Mutation check: 7/7 killed. - M1: secret alone passes. - M2: `enable_push=false` ignored. - M3: run never reads protection. - M4: equal-version branch removed. - M5: no second-parent check. - M6: any tag counts as a toolkit cut. - M7: a relevant commit after the cut is ignored. - The run-level test serves Storey main's measured state at the cut (`enable_push=false`, empty whitelist, #1482 comment 112517) and asserts a blocking fail. ⚠️ Not measured against a live forge. The branch-protection read reuses `GetBranchProtection`, which `preflight-push-whitelist` already exercises. No adopter cut has run through the new decide state. ## Review request Please verify at source (AGENTS.md §6): 1. `checkUnrecordedCut` cannot fire on toolkit-self, which always has a manifest, or on an adopter with a manifest. 2. The `^2` second-parent read matches how a merge-commit adopter's cut tag lands. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(preflight): grade whether the path-alpha push can land, and block an unrecorded first cut
All checks were successful
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 5s
base-divergence-check / check (pull_request) Successful in 6s
go-ci / lint + build + test (pull_request) Successful in 35s
go-ci / record reviewed vs landed commit (pull_request) Has been skipped
pr / plan, and run every gate when combined (pull_request) Successful in 9s
readme-pin-check / mirror (pull_request) Successful in 7s
tests / bats (pull_request) Successful in 38s
tests / checks (pull_request) Successful in 8s
toolkit-self-gates / toolkit-self gates (PR's own rt) (pull_request) Successful in 8s
go-ci / page landing-tree failure (pull_request) Has been skipped
pr / manifest (pull_request) Has been skipped
pr / fragments (pull_request) Has been skipped
pr / changelog_body (pull_request) Has been skipped
pr / register (pull_request) Has been skipped
pr / ac_closure (pull_request) Has been skipped
pr / workflow_parse (pull_request) Has been skipped
pr / split result (pull_request) Has been skipped
pr / gates (pull_request) Successful in 0s
6b1a2e588c
rt adopter-preflight reported path alpha whenever RELEASE_TOOLKIT_TOKEN
existed. It now reads the default branch's protection and fails when
direct push is disabled or the whitelist leaves out the --bot login.

rt decide bootstrapped from a first cut's own tag when the manifest write
had failed, compared equal, and passed. A toolkit-made tag with no
manifest is now its own pending-cut state (pending_manifest=absent).

Refs #1482

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude left a comment

Verdict: CHANGES NEEDED at 6b1a2e588c50c3ddb85d67eaf892debecc9d1c18. The code is right, and I verified both of your review questions at source. One gap remains: the property you asked me to check first has no test, and its mutant survives the whole suite. This is posted as COMMENT because the forge identity is claude, the PR's author.

Refs #1482

Finding: nothing tests that checkUnrecordedCut stays off when a manifest exists

Today it holds, by code reading. a.bootstrapped is set only on the manifest-absent paths (decide.go:306, 310, 342, 359). loadManifest returns anchor{sha, version, tag: lastTag} with bootstrapped false. So the a.bootstrapped && topVersion == a.version guard keeps the new check off any repo with a manifest.

But the guard is the only thing doing that, and no test covers it. I dropped a.bootstrapped && and ran go test ./cmd/rt/ ./internal/decide/ in golang:1.25: both packages pass.

What the mutant does to an adopter. I wrote a probe in internal/decide, reusing your firstCutFixture, for the ordinary steady state of an adopter with a manifest:

  • a toolkit cut tagged at its prepare commit;
  • a manifest recording 0.1.0 at that SHA;
  • a bookkeeping commit;
  • then feat: ordinary work.

On this head the probe passes. With the guard dropped it fails with release-toolkit#1482 unrecorded cut: v0.1.0 was cut by this toolkit ... and NO manifest exists to record it. That would block every manifest-present adopter on its first release-relevant push after each cut, with a message that is false.

Why toolkit-self would not show it. rt decide --dry-run on toolkit-self, built with the guard dropped, still says mode=update. That is luck, not the guard: v0.67.0 points at the #794 digest-pin commit, one above the prepare commit, so toolkitPrepareAt finds no prepare subject there or at ^2. An adopter's tag sits on the prepare commit (squash, rebase or fast-forward) or on a merge whose ^2 is the prepare commit, so it has no such shield.

Fix: add that steady-state case as a test, red with the guard dropped. My probe is below; use it or your own.

func TestDecide_1482ManifestPresentSteadyStateDoesNotBlock(t *testing.T) {
	r := firstCutFixture(t)
	prep := r.head()
	r.write(".release-toolkit-manifest.json",
		`{"schema":1,"last_released_sha":"`+prep+`","last_released_version":"0.1.0","last_released_tag":"v0.1.0","last_released_at":"2026-01-02T00:00:00Z"}`+"\n")
	r.commit("chore(manifest): post-cut bookkeeping for v0.1.0", "")
	r.commit("feat: ordinary work after a recorded cut", "")
	if _, err := r.decideOn(nil, forgejo.Repo{}, Config{}, Options{DryRun: true}); err != nil {
		t.Fatalf("Decide: %v; a cut the manifest records must not read as unrecorded", err)
	}
}

Verified at source

1. Does not fire with a manifest. It holds, as above. On toolkit-self it is also unreachable for a second reason: the digest-pin tag placement.

2. The ^2 read.

  • rt release tags --commit, which defaults to HEAD. On the workflow path that is the merge commit (release.go: "the merge commit IS HEAD").
  • In a merge-commit merge, ^2 is the rolling branch's tip. rt prep builds that branch as main plus the prepare commit, so the tip is the prepare commit.
  • Squash, rebase and fast-forward land the prepare subject on the tagged commit. prepSubjectRE accepts both the prepare vX form and the squash vX (#N) form.
  • TestDecide_1482MergeCommitCutBlocks covers the merge shape.

3. Not blocking a walk with nothing release-relevant. This agrees with checkOrphanChangelog's rule, and TestDecide_1482NoRelevantCommitSinceTheCutDoesNotBlock pins it.

Preflight mutants, mine: a 404 rule graded as a pass, and a whitelist with no --bot graded as a pass. Both are killed by TestAdopterPreflight_PathAlphaGradesThePushNotTheToken.

Suite: go test ./... passes in every package. CI: all 18 statuses on the head are success.

Non-blocking notes

  • A "buried" prepare commit is not recognised. toolkitPrepareAt looks only at the tag and ^2. If the prepare commit is not the tagged commit or the rolling tip, for example because someone pushed an extra commit onto the rolling branch, it returns "", and the old silent pass comes back. That is the #259 case that detectCut's range scan handles. Scanning tag^1..tag for the version's prepare subject would cover both merge styles. The current result fails open, not closed, so this is fine as a follow-up.
  • The push grade does not read protected_file_patterns. That is a branch-protection field which can refuse a push touching listed files. I have not measured whether it refuses a whitelisted bot's manifest push. A line in the push: disclosure saying it is not read would keep the pass line honest. Live measurement is a separate step.
  • Not measured live (the body already says so): the branch-protection read under an adopter's token, and a real first cut through the new decide state.
Verdict: CHANGES NEEDED at `6b1a2e588c50c3ddb85d67eaf892debecc9d1c18`. The code is right, and I verified both of your review questions at source. One gap remains: the property you asked me to check first has no test, and its mutant survives the whole suite. This is posted as COMMENT because the forge identity is `claude`, the PR's author. Refs #1482 ## Finding: nothing tests that `checkUnrecordedCut` stays off when a manifest exists **Today it holds, by code reading.** `a.bootstrapped` is set only on the manifest-absent paths (`decide.go:306, 310, 342, 359`). `loadManifest` returns `anchor{sha, version, tag: lastTag}` with `bootstrapped` false. So the `a.bootstrapped && topVersion == a.version` guard keeps the new check off any repo with a manifest. **But the guard is the only thing doing that, and no test covers it.** I dropped `a.bootstrapped &&` and ran `go test ./cmd/rt/ ./internal/decide/` in `golang:1.25`: **both packages pass**. **What the mutant does to an adopter.** I wrote a probe in `internal/decide`, reusing your `firstCutFixture`, for the ordinary steady state of an adopter with a manifest: - a toolkit cut tagged at its prepare commit; - a manifest recording `0.1.0` at that SHA; - a bookkeeping commit; - then `feat: ordinary work`. On this head the probe passes. With the guard dropped it fails with `release-toolkit#1482 unrecorded cut: v0.1.0 was cut by this toolkit ... and NO manifest exists to record it`. That would block every manifest-present adopter on its first release-relevant push after each cut, with a message that is false. **Why toolkit-self would not show it.** `rt decide --dry-run` on toolkit-self, built with the guard dropped, still says `mode=update`. That is luck, not the guard: `v0.67.0` points at the #794 digest-pin commit, one above the prepare commit, so `toolkitPrepareAt` finds no prepare subject there or at `^2`. An adopter's tag sits on the prepare commit (squash, rebase or fast-forward) or on a merge whose `^2` is the prepare commit, so it has no such shield. **Fix:** add that steady-state case as a test, red with the guard dropped. My probe is below; use it or your own. ```go func TestDecide_1482ManifestPresentSteadyStateDoesNotBlock(t *testing.T) { r := firstCutFixture(t) prep := r.head() r.write(".release-toolkit-manifest.json", `{"schema":1,"last_released_sha":"`+prep+`","last_released_version":"0.1.0","last_released_tag":"v0.1.0","last_released_at":"2026-01-02T00:00:00Z"}`+"\n") r.commit("chore(manifest): post-cut bookkeeping for v0.1.0", "") r.commit("feat: ordinary work after a recorded cut", "") if _, err := r.decideOn(nil, forgejo.Repo{}, Config{}, Options{DryRun: true}); err != nil { t.Fatalf("Decide: %v; a cut the manifest records must not read as unrecorded", err) } } ``` ## Verified at source **1. Does not fire with a manifest.** It holds, as above. On toolkit-self it is also unreachable for a second reason: the digest-pin tag placement. **2. The `^2` read.** - `rt release` tags `--commit`, which defaults to HEAD. On the workflow path that is the merge commit (`release.go`: "the merge commit IS HEAD"). - In a merge-commit merge, `^2` is the rolling branch's tip. `rt prep` builds that branch as main plus the prepare commit, so the tip is the prepare commit. - Squash, rebase and fast-forward land the prepare subject on the tagged commit. `prepSubjectRE` accepts both the `prepare vX` form and the squash `vX (#N)` form. - `TestDecide_1482MergeCommitCutBlocks` covers the merge shape. **3. Not blocking a walk with nothing release-relevant.** This agrees with `checkOrphanChangelog`'s rule, and `TestDecide_1482NoRelevantCommitSinceTheCutDoesNotBlock` pins it. **Preflight mutants, mine:** a 404 rule graded as a pass, and a whitelist with no `--bot` graded as a pass. Both are killed by `TestAdopterPreflight_PathAlphaGradesThePushNotTheToken`. **Suite:** `go test ./...` passes in every package. CI: all 18 statuses on the head are success. ## Non-blocking notes - **A "buried" prepare commit is not recognised.** `toolkitPrepareAt` looks only at the tag and `^2`. If the prepare commit is not the tagged commit or the rolling tip, for example because someone pushed an extra commit onto the rolling branch, it returns `""`, and the old silent pass comes back. That is the #259 case that `detectCut`'s range scan handles. Scanning `tag^1..tag` for the version's prepare subject would cover both merge styles. The current result fails open, not closed, so this is fine as a follow-up. - **The push grade does not read `protected_file_patterns`.** That is a branch-protection field which can refuse a push touching listed files. I have not measured whether it refuses a whitelisted bot's manifest push. A line in the `push:` disclosure saying it is not read would keep the pass line honest. Live measurement is a separate step. - **Not measured live (the body already says so):** the branch-protection read under an adopter's token, and a real first cut through the new decide state.
Author
Member

HOLD: adding the manifest-present steady-state test from review 7284 as a new commit. I'll post the new head here when it's pushed.

HOLD: adding the manifest-present steady-state test from review 7284 as a new commit. I'll post the new head here when it's pushed.
test(decide): pin that a recorded cut never reads as unrecorded
All checks were successful
base-divergence-check / check (pull_request) Successful in 6s
go-ci / lint + build + test (pull_request) Successful in 35s
go-ci / record reviewed vs landed commit (pull_request) Has been skipped
pr / plan, and run every gate when combined (pull_request) Successful in 9s
readme-pin-check / mirror (pull_request) Successful in 8s
tests / bats (pull_request) Successful in 38s
tests / checks (pull_request) Successful in 8s
toolkit-self-gates / toolkit-self gates (PR's own rt) (pull_request) Successful in 7s
go-ci / page landing-tree failure (pull_request) Has been skipped
pr / manifest (pull_request) Has been skipped
pr / fragments (pull_request) Has been skipped
pr / changelog_body (pull_request) Has been skipped
pr / register (pull_request) Has been skipped
pr / ac_closure (pull_request) Has been skipped
pr / workflow_parse (pull_request) Has been skipped
pr / split result (pull_request) Has been skipped
pr / gates (pull_request) Successful in 0s
check-self-bootstrap / check (push) Successful in 6s
gitea-twin-check / check (push) Successful in 4s
go-ci / lint + build + test (push) Successful in 33s
go-ci / record reviewed vs landed commit (push) Successful in 2s
prepared-uncut-check / toolkit-self prepared-uncut controls (push) Successful in 5s
prepared-uncut-check / prepared-but-uncut release (push) Successful in 8s
prepared-uncut-check / check (push) Successful in 0s
release / decide + act (push) Successful in 11s
release / release (push) Successful in 0s
tests / bats (push) Successful in 37s
tests / checks (push) Successful in 7s
go-ci / page landing-tree failure (push) Has been skipped
release / fire-cut (push) Has been skipped
9048da01ef
Review 7284: dropping the a.bootstrapped guard left both packages green,
and it would block every manifest-present adopter on its first
release-relevant push after each cut.

Refs #1482

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Member

HOLD released. New head: 9048da01ef8ad035b6440d604bdd254745cd4e55

One new commit on top of 6b1a2e5, test-only: TestDecide_1482ManifestPresentSteadyStateDoesNotBlock (review 7284's probe, using the existing writeManifest helper).

  • On this head it passes. go test ./internal/decide/ ./cmd/rt/ in CI's forgejo-ci-go image: ok. Lint: 0 issues.
  • With a.bootstrapped && dropped from the guard, it fails. The mutant now dies on this test alone.

The two non-blocking notes are not done here: the buried prepare (scanning tag^1..tag) and the protected_file_patterns disclosure. Both fail open. I'll file them as follow-ups if you want them tracked.

HOLD released. New head: `9048da01ef8ad035b6440d604bdd254745cd4e55` One new commit on top of `6b1a2e5`, test-only: `TestDecide_1482ManifestPresentSteadyStateDoesNotBlock` (review 7284's probe, using the existing `writeManifest` helper). - On this head it passes. `go test ./internal/decide/ ./cmd/rt/` in CI's `forgejo-ci-go` image: ok. Lint: 0 issues. - With `a.bootstrapped &&` dropped from the guard, it fails. The mutant now dies on this test alone. The two non-blocking notes are not done here: the buried prepare (scanning `tag^1..tag`) and the `protected_file_patterns` disclosure. Both fail open. I'll file them as follow-ups if you want them tracked.
claude left a comment

Verdict: APPROVE at 9048da01ef8ad035b6440d604bdd254745cd4e55. This replaces review 7284, which was at 6b1a2e5. It is posted as COMMENT because the forge identity is claude, the PR's author, so @alex has to click Approve.

Refs #1482

Delta 6b1a2e5..9048da0, verified at source

  • History is intact: 6b1a2e5 is an ancestor of 9048da0, so nothing was rewritten. The base is still main 0e7f37d.
  • The delta is test-only: one new test, TestDecide_1482ManifestPresentSteadyStateDoesNotBlock, in internal/decide/unrecorded_cut_test.go. It covers a recorded cut, a bookkeeping commit, then a feat:.
  • The test is not a placebo. I ran internal/decide and cmd/rt in golang:1.25:
    • On this head, both packages pass.
    • With a.bootstrapped && dropped from decide.go, this test fails, and it is the only test that does in either package.
  • CI: all 18 statuses on 9048da0 are success.

Everything I verified in 7284 still holds: the ^2 read, the matching of both prepare-subject forms, the not-blocking window, my two preflight mutants, and the suite. The two non-blocking notes (a buried prepare commit, and protected_file_patterns) stay as possible follow-ups and do not hold up this PR.

Verdict: APPROVE at `9048da01ef8ad035b6440d604bdd254745cd4e55`. This replaces review 7284, which was at `6b1a2e5`. It is posted as COMMENT because the forge identity is `claude`, the PR's author, so @alex has to click Approve. Refs #1482 ## Delta `6b1a2e5..9048da0`, verified at source - **History is intact:** `6b1a2e5` is an ancestor of `9048da0`, so nothing was rewritten. The base is still main `0e7f37d`. - **The delta is test-only:** one new test, `TestDecide_1482ManifestPresentSteadyStateDoesNotBlock`, in `internal/decide/unrecorded_cut_test.go`. It covers a recorded cut, a bookkeeping commit, then a `feat:`. - **The test is not a placebo.** I ran `internal/decide` and `cmd/rt` in `golang:1.25`: - On this head, both packages pass. - With `a.bootstrapped &&` dropped from `decide.go`, **this test fails**, and it is the only test that does in either package. - **CI:** all 18 statuses on `9048da0` are success. Everything I verified in 7284 still holds: the `^2` read, the matching of both prepare-subject forms, the not-blocking window, my two preflight mutants, and the suite. The two non-blocking notes (a buried prepare commit, and `protected_file_patterns`) stay as possible follow-ups and do not hold up this PR.
alex approved these changes 2026-10-06 21:08:46 +02:00
alex merged commit 9048da01ef into main 2026-10-06 21:08:54 +02:00
alex deleted branch claude/1482-preflight-push-grade 2026-10-06 21:08:54 +02:00

Landing identity record

  • PR: #1568
  • landed commit (server merge_commit_sha): 9048da01ef8ad035b6440d604bdd254745cd4e55
  • effective official approval(s):
    • @alex, review #7287, stamped commit: 9048da01ef8ad035b6440d604bdd254745cd4e55
  • replay comparison: no identity change (stamped SHA equals landed SHA)

This is a post-merge identity record. It does not retroactively review the landed object; it records whether the server landed the object that an official approval named.

<!-- release-toolkit:landing-review-record-v1 landed=9048da01ef8ad035b6440d604bdd254745cd4e55 --> ## Landing identity record - PR: #1568 - landed commit (server merge_commit_sha): `9048da01ef8ad035b6440d604bdd254745cd4e55` - effective official approval(s): - @alex, review #7287, stamped commit: `9048da01ef8ad035b6440d604bdd254745cd4e55` - replay comparison: no identity change (stamped SHA equals landed SHA) This is a post-merge identity record. It does not retroactively review the landed object; it records whether the server landed the object that an official approval named.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
frankenbit/release-toolkit!1568
No description provided.