Repository navigation
feat(promotion-gate): say when a promotion owes the admin-read contexts stage, and when to run it - #2536
Merged
Conversation
…ts stage `contexts` needs an admin-read token no CI job holds, so it runs by hand. A new `fedcourts contexts-due` names the paths in a batch that change what the stage judges settings against (workflows, the gate script, the required-check, ruleset and environment judgments and the configs they read). The gate's `all` ends with the verdict, never failing on it, and the promotion train puts it in the PR's generated block as due or not due. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019NtSiNeMSRGjEKSx7yTfdu
contexts reads main's own workflows, so a batch's workflow change is checked once it merges; what it reads from the checkout is checked before. The verdict and the promotion body now say which. Drops review-hold.yaml (nothing the stage reads) and adds corpus_check.py (it parses the approver). Fixes the doubled line on the train's candidate body, and every doc that still said contexts runs on every promotion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019NtSiNeMSRGjEKSx7yTfdu
modelmirror
approved these changes
Oct 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
scripts/promotion-gate.sh contextsreads repository settings (the rulesets, the environments, the contextsmainrequires) and judges them against what the code expects. It needs repository-administration read, which no CI token holds, so it was never part ofall. Nothing said which promotions needed it, though, anddocs/security.mdand several other docs still said it ran "unconditionally on every promotion". This is part B of #2502:contextsstays manual and on demand, and each promotion now says whether it owes it.What's added
fedcourts contexts-due(src/fedcourtsai/contexts_due.py).main) changes anything the stage judges settings against:.github/workflows/: the required jobs, and every environment a job names, which GitHub creates unprotected;scripts/promotion-gate.sh;required_checks.py,ruleset_checks.py,environment_checks.py;config/corpus-check.yamlandcorpus_check.py, which parses it.main's own workflows, so a batch's workflow change is checked only after the promotion merges; everything else it reads from the checkout, so before merging. A clean run before the merge is therefore no evidence about the batch's workflows, and the verdict says so.promotion-gate.sh allends with acontexts-duestage. It prints the verdict and never setsfail: a missingorigin/mainor an unreadable diff is a warning. ci.yml's requiredpromotion-gatecheck therefore shows the verdict on every promotion PR without being able to go red on it.promotion-prep.yml→promotion_body.py) gets one line, kept apart from the gate checks:contexts_due: null), and the CLI's error goes to the run log.contexts_due.pyand its test are added to CODEOWNERS (beside the othercontextsjudgments) and to AGENTS.md's promotion-gate owned list.Produced example
Run against real history:
scripts/promotion-gate.shandenvironment_checks.py. That means before and after.dbb042ca4): due, 22 paths, after merging for its workflow changes. That's correct, since it carried ci(workflows): keep the dev App's write token and every App key out of jobs that run agents #2503's and feat(pipeline): chain and automate the dev and data flows #2519's workflow changes, and nobody had a signal to runcontexts.main's last 12 promotion merges, 8 come out due, and 3 of those only through workflows.Checks
tests/test_contexts_due.py: the path list (every entry exists; near-misses andreview-hold.yamldon't trigger); the before/after split; a three-dot diff on a throwaway repo, showing that whatmaingained after the batch branched isn't the batch's; the CLI's verdict,--jsonand exit codes; and that the script stage never touchesfailand warns withoutorigin/main.tests/test_promotion_body.py: the line appears once on both the staging and thepromote/candidate headers, and says before and/or after.tests/test_workflow_promote.py::test_the_contexts_stage_stays_out_of_the_automated_gates. It asserted that the substring "contexts" was absent fromall, which the newcontexts_duecall trips. It now matches thecontextsstage as a whole word: it still catchescontexts,contexts "$2"andcontexts;, and letscontexts_duethrough. Its docstring says why.scripts/gate.sh lint types docs-tests: green.promotion-prep.ymlandci.yml, andzizmor@1.26.1 --offlinehas no findings. The online audits can't reach the network from this sandbox.Reviews
promote/head and counts it.main's workflows. The verdict now splits before and after.review-hold.yamldropped (nothing the stage reads) andcorpus_check.pyadded;failcheck;cli.pywiring of the assert-* commands should stay off the list: it's pinned by tests, and listing it would make nearly every batch due.target_shais set on every path, thatorigin/mainhas full history, the|| …behaviour underset -e, and jq's handling of a list vsnull.contexts-due.log, printed on failure rather than discarded, and ci.yml's step name mentions the verdict.Merge
This changes the promotion gate (
scripts/promotion-gate.sh,ci.yml's gate step name,promotion-prep.yml,tests/test_workflow_promote.py) and AGENTS.md / CODEOWNERS, so it's left for the maintainer.Refs #2502: part A (sync → test → prepare) shipped with #2519's promotion train, and this is part B.
Effect check (after promotion)
promotion-gatejob log ends with acontexts: due — …orcontexts: not due — …line, and the job's conclusion is unchanged by it.contexts (admin read, run by hand)line exactly once.To check:
Generated by Claude Code