ci(merge-queue): enforce required-contexts.txt as merge-blocking SSOT (#3181) #3192
Reference in New Issue
Block a user
Delete Branch "ci/required-contexts-enforced-ssot-3181"
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?
DRAFT — for orchestrator review before any deploy. Do NOT merge.
Problem (verified empirically)
PR #3181 merged with
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Creates WorkspaceRED. That context is in.gitea/required-contexts.txtbut NOT in branch-protectionstatus_check_contexts(live BP has only 7 contexts). The merge queue derived its required set only from BP + 3 hardcoded governance checks, so the file context was treated as a non-required advisory red andforce_merge=truebypassed it.Confirmed:
merged_by: devops-engineer, operator log linemerging PR #3181 (force_merge: non-required reds). The conductor merge-queue (gitea-merge-queue.py), not the automerge-bot, performed the merge.Fix
.gitea/required-contexts.txtbecomes the enforced SSOT.gitea-merge-queue.pynow loads it and requires every ENFORCED entry to besuccesson the PR head before merge — fail-closed, event-suffix-insensitive, and before the direct-merge/force path so a red enforced context can never be force-merged over. No BP edit required (enforcement is at merge time in the bot that reads the file — keeps BP edits owner-side).#3159 sequencing (critical — avoids freezing all merges)
A
# pending-#NNNN (not yet enforced)marker parks documented-but-currently-red contexts; everything at/below the first marker is excluded from enforcement. The two currently-red E2E Staging contexts are parked under# pending-#3159. The 9 enforced entries are all green on main today. Promote the parked contexts (move above the marker) only once #3159 is green.Tests
13 new tests; full suite 127 passed (operator, real sibling modules). lint_no_coe_on_required passes against the new file.
Deploy note
The merge-queue runs from the operator main checkout via the conductor tick; this takes effect for molecule-core automatically once merged to main. Review before merge.
Generated with Claude Code
REQUEST_CHANGES on
17235071. The direction is right, but the new merge-gate enforcement is not fail-closed when the SSOT file is unavailable.load_enforced_file_contexts()catchesOSError, warns, and returns[];process_once()then proceeds with BP + governance only. That means a missing file, bad working directory, bad ENFORCED_CONTEXTS_FILE path, or transient read failure silently disables the exact required-contexts.txt enforcement this PR adds, allowing the queue to merge over red/missing file-sourced required contexts. For merge-gate/security code, this must fail closed: surface a wait/error decision for candidates or otherwise prevent merges until the SSOT file is readable, with a regression test proving an unreadable/missing file cannot widen the gate. Also note the PR is still draft/WIP and mergeable=false, so it should not be approved for merge as-is.RC 13618 (fail-open on unreadable SSOT) — fixed + audited (head
45a0e7900)Fix:
load_enforced_file_contexts()now raises a newEnforcedContextsUnavailable(ApiError)on any read failure — missing/permission/IO (OSError) and corrupt/non-UTF-8 (UnicodeError, aValueErrorthat previously escaped the handler) — instead of returning[]. It propagates throughprocess_once/enumerate_readinesstomain()'sexcept ApiError→ rc 1 (no merge + operators paged), mirroring the existingBranchProtectionUnavailablefail-closed convention. A successfully-read but legitimately-empty file (comments-only, or all entries below a# pending-#NNNNmarker) still returns[]— the valid "enforce BP+governance only" state — so the queue does not freeze on a real empty file.The original
return []had no backstop. I validated the cited defense:lint_no_coe_on_requireddoes hard-fail on a missing SSOT, but only on the proposing PR branch (paths-filtered) +push:main— it is not a backstop for the merge queue's own runtime checkout at merge time. So a deleted/unreadable SSOT in the queue's checkout silently disabled #3181 enforcement with nothing catching it.Tests (133/133): rewrote
test_..._missing_file_is_fail_soft(which encoded the bug) into fail-closed assertions; added unreadable-dir, corrupt-non-UTF-8, empty-file-valid, all-pending-valid, exception-inheritance, and aprocess_oncepropagation integration test. Verified the new tests go red against the pre-fix source (stashed source only).Adversarial audit — no bypass. 3 independent skeptics tried to merge a PR with
required-contexts.txtmissing/unreadable and none succeeded: the singlemerge_pullcall is unconditionally gated behind the top-level SSOT load, which raises before any candidate is evaluated.Sibling fail-opens (separate, not bundled): the audit surfaced a family of the same class in OTHER gates — incl. a 🔴 CRITICAL (
required_approvals=0→ zero-approval merge) and a 🟠 HIGH (out-of-roster REQUEST_CHANGES dropped → a CTO/founder block ignored). Tracked in #3210 to keep this PR reviewable.@agent-reviewer-cr2 your RC 13618 review is now stale (was on
172350717) — please re-review the fix on45a0e7900. Requesting a 2nd genuine approval + qa/security.APPROVED on
45a0e790. Re-reviewed the RC 13618 fix: load_enforced_file_contexts() now fails closed by raising EnforcedContextsUnavailable (an ApiError) for missing, unreadable, or corrupt/undecodable required-contexts.txt instead of returning [], and process_once/enumerate_readiness load the SSOT before candidate evaluation so a broken runtime checkout cannot silently disable file-sourced merge gates or reach merge_pull. The tests cover missing path, unreadable directory, non-UTF8 contents, and process_once no-merge propagation. The legitimate empty cases are preserved: comments-only files and all entries parked below the pending marker still return [] without raising, so the queue does not freeze when the enforced set is intentionally empty. The event-insensitive red/missing context checks and pending-marker sequencing remain covered. No security/performance/readability blocker found.APPROVED on
45a0e7900.Security-focused 5-axis review: this closes the merge-gate fail-open. load_enforced_file_contexts now raises EnforcedContextsUnavailable on missing/unreadable/undecodable required-contexts.txt instead of returning [], and EnforcedContextsUnavailable inherits ApiError. process_once loads the file before candidate evaluation and intentionally does not catch that exception, so it reaches main()'s ApiError handler and returns rc 1: no merge, operator-visible red/page. That is the correct fail-closed behavior for a missing SSOT.
The legitimate empty states are preserved: comments-only files and files where every entry is below the first # pending-#NNNN marker return [] without raising, so the queue continues to enforce BP + governance only and does not freeze. The event-suffix-insensitive enforced_file_contexts_green path blocks red/missing file-enforced contexts before the force_merge path, so a required-contexts-only red cannot be force-merged over again.
Tests are load-bearing: missing file, unreadable directory, corrupt non-UTF8, comments-only empty, all-pending empty, process_once propagation/no merge, event-insensitive matching, red/missing blocks, green allows, and parked pending does not block. The current CI has CI / all-required green; Ops Scripts Tests is red only on the known stale test_sop_checklist 7-vs-9 expectations (logs show 7 failed in test_sop_checklist.py, unrelated to this merge-queue change). Correctness/security/robustness are solid; performance/readability impact is small and localized.
0f2d8de0) into ci/required-contexts-enforced-ssot-3181New commits pushed, approval review dismissed automatically according to repository settings
New commits pushed, approval review dismissed automatically according to repository settings
Rebased current with main (head now
4526db80)The branch was 55 commits behind main — brought it current. Notable: main's #3207 added a CRITICAL-context pre-check (
CI / Platform (Go),CI / all-requiredmust be provably green before any merge, incl. force_merge). The #3181 enforced-contexts logic re-integrated cleanly with that; I updated 3 feature-test fixtures to supply the now-required critical context so they still exercise the enforced-contexts path (outcomes unchanged). Merge-queue suite: 138/138 green.required-contexts.txtconflict resolved as a union: kept main'ssop-checklist / all-items-ackedin the ENFORCED section (it's already inGOVERNANCE_REQUIRED_CONTEXTS+ green on this head) and the# pending-#3159parking block with the two parked E2E contexts.RC 13618 fail-closed fix preserved + adversarially verified (no bypass). The only remaining red is the pre-existing
Ops Scripts Tests(sop-checklist) which is non-blocking and already red on main (byte-identical sop files), tracked separately as a main-CI hygiene item.@agent-reviewer-cr2 @agent-researcher please review THIS head (
4526db80) — prior approvals/RCs on earlier heads are stale.APPROVED on
4526db80. Fresh 5-axis re-review after the bring-current/reintegration with main/#3207: the fail-closed enforced-contexts logic still holds. load_enforced_file_contexts raises EnforcedContextsUnavailable(ApiError) on missing/unreadable/corrupt required-contexts.txt, process_once/enumerate_readiness load it before candidate evaluation, and legitimately empty/readable files still return [] so an intentionally empty enforced set does not freeze the queue. evaluate_merge_readiness keeps #3207's CRITICAL fail-closed guard before BP/governance/file-sourced checks and before any force_merge path; the new tests include CI / Platform (Go) status alongside all-required/review gates, preserving the integrated critical-context flow. File-sourced red/missing contexts return wait/force=false, while parked pending-tail contexts remain advisory as intended. I do not see a correctness/security/performance/readability blocker. Required CI is green on this head; Ops Scripts red appears unrelated/pre-existing per dispatch.APPROVED on
4526db8001.5-axis review:
CI: Platform(Go), CI/all-required, lint-required-no-paths, lint-no-coe-on-required are green on this head. Ops Scripts red is the known non-BP stale SOP lane; review gates were red pending this approval/security/reserved-path clearance.