Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ production_mode: hand-written
found_at: "Makefile"
remedy: "Let preflight mint a receipt without the test and race stages for a diff that touches only the record tiers (.abcd/work/, .abcd/development/), using the same fail-closed path classifier CI uses to stand the race lane down on a pull request confined to those paths, so the receipt's scope matches what CI would run."
refines: [iss-2610161639025702]
resolution: "make preflight runs precheck's tests in place of the unit and race stages for a committed change confined to the record tiers that no test names, judged fail-closed by scripts/records-only.sh against the merge base with origin's default branch; the run says so and its receipt names the scope, the verdict and the merge base"
impact: internal
---

make preflight cannot land a one-record change on a busy machine: on 2026-10-24 a branch whose whole diff is one new issue record (iss-2610241228359748) ran preflight twice, every gate before the test stages passed both times, and both runs then waited the load gate's full two hours (abcd implement load --wait --timeout 2h) behind two to four running preflights and six to eight queued waiters, and failed with 'load gate still shut after 2h0m0s'. With no receipt the pre-push hook refuses the push, so a change that touches no Go code waits behind the full test and race stages of every other session and in practice cannot be pushed.
37 changes: 36 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,27 @@ asking-sync:
asking-sync-check:
@go run ./cmd/asking-sync -check

# A records-only change (iss-2610251555094324) takes a shorter preflight: when
# every path HEAD changes against the merge base with origin's default branch
# lies in .abcd/development/ or .abcd/work/, no test names one of them (by its
# path, its file name or its record id), and the
# tree matches HEAD, scripts/records-only.sh says so and the second preflight
# rule below runs in place of the first. It runs the same gates, build and vet,
# then precheck's test lines in place of the unit and race stages, and says so
# before its tests and in the receipt it mints. Anything else, a failed check included,
# is the full rule. The verdict is read while this Makefile is parsed, as the
# starting state is, and only yes or nothing reaches make: the reason can name a
# changed path, so it is printed by the script itself, never placed in a recipe
# line. The records rule's first step and its mint each run the classifier
# again, so a verdict that no longer holds fails the run rather than skipping
# its tests. `make preflight PREFLIGHT_RECORDS_ONLY=` takes the full rule
# whatever the change. TestPreflightRecordsRuleRunsPrechecksTests holds the
# second rule's gates to the first's and its test lines to precheck's.
ifneq ($(filter preflight,$(MAKECMDGOALS)),)
PREFLIGHT_RECORDS_ONLY := $(shell scripts/records-only.sh >/dev/null 2>&1 && echo yes)
endif

ifneq ($(PREFLIGHT_RECORDS_ONLY),yes)
# Pre-push gate (run before a push, never by it: .githooks/pre-push checks the
# receipt the last step mints, below): the load check first (a
# warning, never a failure: load-check), then the format gate (fmt-check), the
Expand Down Expand Up @@ -360,11 +381,25 @@ preflight: load-check fmt-check lint-reviews lint-issues record-lint issue-drift
go test -race -p "$$p" -timeout 40m ./internal/surface/cli ./internal/core/reading ./internal/core/launch ./internal/core/lifeboat ./internal/core/lint ./internal/adapter/scanner ./internal/core/implement/loop ./internal/core/interview ./internal/core/capture ./internal/core/ahoy ./internal/core/site ./internal/...
@scripts/preflight-receipt.sh mint "$(PREFLIGHT_BEGAN)"

else
preflight: load-check fmt-check lint-reviews lint-issues record-lint issue-drift docs-lint check-attribution secrets-check vulncheck site-render smoke evals-cold-reading
@scripts/records-only.sh
@echo "preflight: a records-only change, so the unit and race stages are skipped and precheck's tests run in their place; CI's unit lane still runs every test"
go build ./...
go vet ./...
go test -timeout 20m ./internal/core/lint ./internal/core/launch/scaffold ./internal/core/mdrecord ./internal/core/issueschema ./internal/core/guard ./internal/core/glossary ./internal/core/reading ./internal/core/recordid ./internal/core/changelog ./internal/core/intent ./internal/core/release ./internal/core/releasedigest ./internal/core/decisions ./internal/core/record/match ./internal/core/lifeboat
go test -timeout 20m -run '$(PRECHECK_AUDIT_TESTS)' ./internal/surface/cli ./internal/core/site
@scripts/preflight-receipt.sh mint "$(PREFLIGHT_BEGAN)" records-only
endif

# The precheck (iss-2610230016523625): the minutes-long pass a lane runs before
# it queues a preflight, so a test that reads the whole tree fails before the
# queue rather than after it. It runs preflight's gates (all but the load check,
# which reads the machine and only warns), build and vet, the packages whose
# tests audit the record, the generated pages and the agent definitions, in full,
# and the packages whose tests read the committed record tree whole (record ids
# and slugs, the changelog derivation, intents, release pages, decisions, the
# brief's lifeboat table),
# and the command and site packages' audit tests, chosen by name: a test in those
# two packages that reads the committed tree carries one of the PRECHECK_AUDIT_TESTS
# words in its name, which is how a new one joins without a list to keep. It skips
Expand All @@ -377,7 +412,7 @@ PRECHECK_AUDIT_TESTS := Surface|Page|Brief|Parity|Reference|Snapshot|Appendix|Ap
precheck: fmt-check lint-reviews lint-issues record-lint issue-drift docs-lint check-attribution secrets-check vulncheck site-render smoke evals-cold-reading
go build ./...
go vet ./...
go test -timeout 20m ./internal/core/lint ./internal/core/launch/scaffold ./internal/core/mdrecord ./internal/core/issueschema ./internal/core/guard ./internal/core/glossary ./internal/core/reading
go test -timeout 20m ./internal/core/lint ./internal/core/launch/scaffold ./internal/core/mdrecord ./internal/core/issueschema ./internal/core/guard ./internal/core/glossary ./internal/core/reading ./internal/core/recordid ./internal/core/changelog ./internal/core/intent ./internal/core/release ./internal/core/releasedigest ./internal/core/decisions ./internal/core/record/match ./internal/core/lifeboat
go test -timeout 20m -run '$(PRECHECK_AUDIT_TESTS)' ./internal/surface/cli ./internal/core/site

precheck: export GOTOOLCHAIN := go$(GO_TOOLCHAIN_VERSION)
Expand Down
4 changes: 3 additions & 1 deletion internal/core/lint/precheck_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ func TestPrecheckRunsTheAuditTests(t *testing.T) {
"go build ./...",
"go vet ./...",
"go test -timeout 20m ./internal/core/lint ./internal/core/launch/scaffold ./internal/core/mdrecord " +
"./internal/core/issueschema ./internal/core/guard ./internal/core/glossary ./internal/core/reading",
"./internal/core/issueschema ./internal/core/guard ./internal/core/glossary ./internal/core/reading " +
"./internal/core/recordid ./internal/core/changelog ./internal/core/intent ./internal/core/release " +
"./internal/core/releasedigest ./internal/core/decisions ./internal/core/record/match ./internal/core/lifeboat",
"go test -timeout 20m -run '$(PRECHECK_AUDIT_TESTS)' ./internal/surface/cli ./internal/core/site",
} {
if !strings.Contains(recipe, want) {
Expand Down
Loading
Loading