Repository navigation
Description
The three organisation rulesets and the quality_gate property schema are declared in src/opentofu/github and imported from code, so no ruleset lives only in the web UI.
Caller
- Every fleet repository's GitHub settings and the organisation rulesets live as code in platform #10, slice G2, under the operator's lines 1 A, 2 A and 3 B of 06.11.2026 and his sentence relayed there: „1 => du hast es platform gesagt?“. The slice table's sentence is carried here whole, except that the apply is an agent's, by his standing ruling on steps without sudo or an outside secret.
- Distributor: fleet audit of CI and scripts (06.11.2026) marketplace#484, row S4: repository security settings differed across the fleet with nobody owning them.
Neighbours: G3 (the scheduled drift check, later under #10), which reads what this slice declares; quality, whose docs/gate.md still describes the ruleset and whose item follows this landing.
Today (API read, 08.11.2026)
Organisation overnightworks, all three active, read them with gh api orgs/overnightworks/rulesets/<id>:
- 25224011
main: PR only (all repositories), target branch - 25227067
quality / verdict required, target branch - 25224012
release tags are immutable (all repositories), target tag
Property schema (gh api orgs/overnightworks/properties/schema): quality_gate, true_false, values editable by organisation actors. The schema also holds role (single_select), which the contract does not cover and this slice leaves undeclared, unchanged.
The contract with quality (quality's head and platform's head, 06.11.2026)
- The check's name is quality's. The ruleset requires the context
quality / verdictfrom GitHub Actions, written exactly, with no pattern, and pinned to GitHub Actions withintegration_id = 15368. Today's ruleset has no pin; quality's head agreed to it, sincegate.ymlis the only producer and quality's merge command posts no status or check run. - The targeting stays on the property. The ruleset reaches a repository only through
quality_gate=true. Type true_false, values editable by organisation actors, which the operator's login is. quality's merge command flips the value per repository, so the values are never declared here: only the schema. - The bypass list stays empty. quality's merge command is the one deliberate way past, logged on the pull request.
- Enforcement stays active.
What binds
- The three rulesets and the property schema are declared in a child module
src/opentofu/github/organization/, and the root holds its call and theimportblocks, so a mocked test can plan the module directly (tofu testcannot plan a resource animportblock names). scripts/apply_github_settings.shon today's organisation, on this lane's commit, plans exactly four imports and exactly one change:integration_id = 15368added to thequality / verdictcheck. It adds and destroys nothing. Any other difference is matched in the declaration to what the organisation holds today, or reported and not silently accepted.- Every ruleset carries
lifecycle { prevent_destroy = true }, and every resource of the moduledestroy = falseas G1's do, so that removing a block forgets the object and never deletes it. OpenTofu ignoresprevent_destroyoncedestroy = falseholds in state, and the rulesets keep the line as ruled. - A mocked
tofu testasserts the four points above on the planned resources of the module: the exact context with no pattern and the integration id, targeting only through the property with no repository name or id condition, the empty bypass list and enforcementactiveon all three rulesets, and the schema's type and who may edit its values. It also asserts the required approving review count ofmain: PR onlystays 0, naming the field below. - The root holds no token, the plan and apply stay the operator's own gh login, and the command's existing refusals (undeclared listing entries, a dirty or foreign checkout for
--apply) still hold. The command refuses any plan that would delete an object, naming its address, whether the plan is only shown or applied, and applies nothing. An imported object that the first apply leaves unchanged holds itsdestroy = falsein state only after a later apply that changes it, so thelifecyclealone cannot be relied on. A deliberate removal uses aremovedblock withdestroy = false.
Paths traced
src/opentofu/github/main.tf (providers), repositories.tf (the pattern), tests/repository_listing.tftest.hcl (mock provider and import blocks under it), scripts/apply_github_settings.sh (plans the whole root, and gains the refusal above). .terraform.lock.hcl pins integrations/github 6.13.0 and stays unless that version cannot represent one of these objects, in which case the builder says so before changing it. mise run github:test and the job opentofu already gate the root.
Unknowns the build settles
- Whether provider 6.13.0 has a resource for the property schema, or the existing pattern must use another. Name what was checked.
- Whether the operator's gh login has the scope to write organisation rulesets. Reading them works. A refused apply becomes the operator's one-line
gh auth refresh, never a workaround.
Known limits of provider 6.13.0, named in the declaration and AGENTS.md
require_extra_approval_for_unattributed_changes = trueonmain: PR onlycannot be carried: import and this apply leave it alone, and a later update of that ruleset sends it not at all, whether GitHub then keeps it is unknown without a write. It takes effect only at a non-zero required approval count, which is 0, so the test above holds the count at 0 and a change that raises it must first get a provider that carries the field. Head's decision, against a broader refusal, since the setting is inert while the count is 0.quality_gate's default is undeclared because the provider returns none for a true_false property. A later update of the schema resource would send an empty default, with GitHub's answer unknown; G3 does not see this default.
Not in this slice
The apply, which an agent runs from a clean origin/main after this lands, once quality's head has been told of the integration_id line. The landing dispatches quality's item to point docs/gate.md at platform's definition. G3 is its own slice.
Material risk: organisation-wide security settings. The final gate runs on another provider than the build.
version = 1
now = "Cut on 08.11.2026 from #10's slice G2."
next = "A builder declares the three rulesets and the property schema and shows the plan."
done_when = "The three organisation rulesets and the quality_gate property schema are declared and imported from code, a plan on today's organisation shows exactly four imports and one change (integration_id 15368 on the quality / verdict check), every ruleset carries prevent_destroy, and a mocked tofu test asserts the four points of the contract with quality."
scope = ["AGENTS.md", "mise.toml", "scripts/apply_github_settings.sh", "src/opentofu/github/organization", "src/opentofu/github/organization.tf", "src/opentofu/github/tests/organization_rulesets.tftest.hcl", "tests/github"]
size = "M"
whole = "A ruleset module, its root call and imports and the test that plans it are one declaration with one proof."
Activity
FlexOr2 commentedon Nov 8, 2026
Landed: #221, d47e7e0.