Skip to content

refactor(sections)!: milestones, webhooks, and rulesets join listSection; duplicate live items fail loudly everywhere - #202

Merged
Vivswan merged 7 commits into
mainfrom
refactor/sections-shared-engines
Sep 13, 2026
Merged

Vivswan merged 7 commits into
mainfrom
refactor/sections-shared-engines

Conversation

@Vivswan

@Vivswan Vivswan commented Sep 13, 2026 •

Copy link
Copy Markdown
Owner

Before / after

before   $ gsac check   # two live hooks share one url; a milestone title is repeated live
         webhooks: 1 declared url(s) each match more than one live hook ... "https://ci.example.com/hook" matches 2 live hooks (ids 11, 12)
         milestones: <plans against whichever "v1" was listed last, silently>
         custom_properties / secret_scanning_custom_patterns / environments[...].secrets: <same, silently>

after    $ gsac check
         webhooks: GitHub holds webhooks that resolve to one identity: "https://ci.example.com/hook (hook id 11)" and "https://ci.example.com/hook (hook id 12)". This section manages one webhook per identity, so it cannot tell them apart; delete all but one of each on GitHub, then run again
         milestones: GitHub holds milestones that resolve to one identity: "v1 (milestone number 1)" and "v1 (milestone number 2)". This section manages one milestone per identity, ...

         webhooks/index.ts, rulesets/index.ts, milestones/index.ts: three hand-written planners (357 + 288 + 167 lines)
         -> three listSection() declarations (130 + 178 + 59 lines): one planner, one guard, one wording for all six list sections

What changed

Milestones, webhooks, and rulesets move onto listSection, so every list section shares one planner, one live-duplicate guard (liveByIdentity, plan and snapshot), and one wording per concept.

Behavior changes

Each has a numbered entry in docs/upgrading/v2-to-v3.md (sections 11-15, after #204's 10):

  • Two live items under one identity fail every list section, claimed or not, in plan and snapshot.
  • The webhook snapshot placeholder is $SECRET_WEBHOOK_<id> (was $WEBHOOK_SECRET_<id>).
  • One wording per concept: webhooks[<url>] labels, declared X != live Y; apply will ... operand order (top-level, nested mapping, and role fields), one line per element for the webhook events set, <label>: left out of the snapshot - <reason>, the cannot-verify template, two change lines.
  • Webhooks: service hooks and url-less hooks are outside the section; no write carries name; insecure_ssl is written as the string GitHub stores; the snapshot writes config keys in GitHub's order.
  • Rulesets: a list entry without source_type is repository-owned (deletable under _undeclared: delete); a repeated rule type is a settings-file error.

Proof

  • Tests and gates: 3458 unit tests green (bun run check clean), bun run test:e2e 258/258 after bun .github/scripts/trim-openapi.ts; the prose-and-request harness below classifies every main-vs-head difference (0 unclassified, control caught).
  • Line accounting: production src +1019 -859 = +160 (the three moves cut 439 lines; the factory grew 422 for the roles they needed; the guard's bespoke call sites and the shared helpers carry the rest). Two sections bought general roles: the payoff is one planner, one guard, one wording, not fewer lines.
Technical details

Factory features added, and who uses them

feature (approx. lines) used by
get role, per-item read (25) rulesets
updateConfig role + mapping split (60) webhooks
dotted identity path (45) webhooks
secrets: a dotted path under mapping, resolvedWrite, the facet, snapshot references, secretValues (85) webhooks
concealed (30) rulesets
foreign (35) rulesets, webhooks
layering.nested (3) rulesets
prose.undeclaredNote.action (3) milestones
type pins: OnlyListRoles, Address over item routes, MappingFacet, RenameFacet (60) the factory itself
liveByIdentity + plural (contract/live.ts, 28), addressed (11) every list section, the engines, collaborators, custom_properties, secret_scanning, environments
valueDrift, cannotVerifyNote, secretValuesOf (contract/module.ts), leftOutOfSnapshot (snapshot-helpers.ts) the factory, collaborators, teams, writeOnlyCheckNote, the secrets engine, readOrNote

Two sections bought general roles; the payoff is one planner, one guard, one wording, not fewer lines. Trimmed on review: the top-level secret arm (rode update; no section used it), the multi-secret _SUFFIX on snapshot reference ids, the store-name sanitizing, missingDrift and unreadableSecretNote (one caller each, inlined; they return in PR C with their conversions), the remedy: null arm, a required describe on the guard where the key already names the item.

Prose-and-request harness (main vs head, identical inputs): plan, apply, re-plan, and snapshot over test/e2e's own mock handlers for 20 sections x (row seed, empty, duplicate-live seed) x (snapshot value, empty, minus first, keep/delete wrappers, first perturbed, plus fresh, fresh only); ops rendered with resolved payloads, drift, notes, describe, change, and the ordered request list.

compared differing unclassified
9869 leaves 585 0
intended difference leaves
duplicate live items now fail loudly (every facet of a duplicate-live case) 479
cannot-verify template on the webhook secret facet 20
a service hook is outside the webhooks section (plan neither notes nor deletes it) 18
webhook snapshot placeholder $SECRET_WEBHOOK_<id> 16
snapshot omission phrase <label>: left out of the snapshot - <reason> 16
webhook label form webhooks[<url>] 15
no webhook write carries name (GitHub defaults web; the update endpoint takes no name) 13
milestones DELETED change line drops the parenthetical (the drift line keeps it) 2
drift operand form declared X != live Y; apply will set the declared value (top-level fields) 2
the same form on roles (collaborators, teams) and nested mapping fields 2
rulesets change line drops (id N) 2
webhook events compare as a set (one line per element) 0 in the rows; pinned by src/sections/webhooks/webhooks.test.ts
a ruleset list entry without source_type is deletable 0 in the rows; pinned by src/sections/rulesets/rulesets.test.ts

Control: a one-character edit to the factory's update remedy in the head tree produced exactly 4 unclassified differences through the same comparison (the rendered lines that carry it in the rows).

Line accounting (production src = src/** minus *.test.ts, mock.ts, scenarios, docs.yml; against main at d92b738):

file net
src/sections/webhooks/index.ts -227
src/sections/milestones/index.ts -108
src/sections/rulesets/index.ts -104
src/sections/deploy_keys/index.ts -12
src/sections/shared/snapshot-helpers.ts -12
src/sections/shared/secrets-engine.ts -3
src/sections/environments/branch-policies.ts, protection-rules.ts -1, -1
src/engine/secrets.ts, src/sections/shared/variables-engine.ts 0, 0
src/sections/contract/requests.ts +2
src/sections/shared/repo-secrets.ts, repo-variables.ts +7, +7 (the snapshot guard)
src/sections/custom_properties/index.ts, secret_scanning_custom_patterns/index.ts +9, +9 (one guard closure each)
src/sections/teams/index.ts +11 (valueDrift call and import wrap)
src/sections/environments/nested.ts +16 (two guards)
src/sections/environments/snapshot.ts +22 (four guards)
src/sections/contract/live.ts +28 (liveByIdentity, plural)
src/sections/collaborators/index.ts +30 (guarded maps for both pools, two valueDrift calls)
src/sections/contract/module.ts +65 (valueDrift, cannotVerifyNote, secretValuesOf)
src/sections/shared/list-section.ts +422 (the roles, facets, and type pins above)
total +1019 -859 = +160

The three moves cut 439 lines and the factory grew 422 for the roles they needed; the guard's call sites in the bespoke sections (+~110, each a refusal where v2 picked silently) and the helpers (+93) carry the rest. Test support: src mock.ts +41 (three seed specs), src tests +36, test/ -95. Trio projection: PR B is -84 in production src on its branch; PR C (roles rule, org-owner probe, missingDrift and unreadableSecretNote with their conversions) is estimated at -40 to -60, so the trio ends about +40.

Small fixes on the way: deploy_keys docs.yml described the retired same-title conflict text; mintSecretReference (src/engine/secrets.ts) lost its export (knip); the repo variable and secret family snapshots, custom_properties, secret_scanning_custom_patterns, collaborators, and environments' nested snapshots wrote a duplicate live pair silently and refuse it now; the guard runs before a section's own live conflicts, so a duplicated live pair is reported before a deploy key's material-holder conflict; the environments snapshot keys its branch-policy guard through livePolicyName, so a nameless API row is the same malformed-response error in plan and snapshot (Copilot).

Out-of-territory edits (each granted by the team-lead): test/scripts/changed-sections.test.ts (the list-section.ts fan-out pin names milestones, rulesets, webhooks); test/e2e/mock/state.ts (three imports and three LIST_MOCKS entries; the seed-completion specs live in each section's mock.ts); test/engine/secrets.test.ts (the expected webhook create body no longer carries name: "web"); src/engine/secrets.ts (export dropped on mintSecretReference); docs/operate/snapshot.md (the placeholder rows); test/e2e/scenarios/snapshot-private-notes-redacted.yml and snapshot-private-notes-shown.yml (four lines each: the WEBHOOK_SECRET_701 -> SECRET_WEBHOOK_701 rename in the env, the expected snapshot, the summary pin, and the leak needle; they landed in #204 after the rename); scenario prose pins, one line each: webhooks-check-drift (3), webhooks-snapshot-roundtrip (3), rulesets-snapshot-bypass-hidden (4), rulesets-snapshot-roundtrip (1), milestones-undeclared-delete (2).

Codex rubber-duck review, 12 rounds: round 1: 0 blocking, 3 non-blocking (updateConfig-without-update typechecked; runtime checks the types could carry; nested carrier rejected annotated siblings), applied. Round 2: 0/2 (SecretPath's top-level arm admitted a dotted path; a migration step renamed one side only), applied. Round 3: 0/1 (a summary table row contradicted its section), applied. Round 4: 0/0. Round 5 (Copilot fold): 0/0. Round 6 (gate fold): 0/2 (collaborators rebuilt the guarded maps; a restating comment), applied. Round 7 reviewed a bespoke revert the owner then vetoed (not pushed). Round 8 (the trims and the Copilot folds): 0 blocking, 2 non-blocking (a test comment still said the types could not enforce the carrier facet; two helper comments restated their names), applied. Rounds 9-10 (a doc-only fold of two Copilot threads: section 13 names the sites each template reaches in this PR): round 9 1 blocking (the left-out scope omitted the actions and repository reads) and 1 non-blocking (rows over the line cap), round 10 0/1 (a denied primary read says skipped, not left out); all applied. Round 11 (the branch-policy guard fix from a Copilot thread): 0/0. Round 12 (the lead gate re-review's three one-helper trims): 0/1 (a JSDoc restating the renderer), applied.

Gate review, folded as the second commit: B1 (a repeated rule type crashed as a BUG), B2 (the guard was plan-only in the bespoke sections), and the seven nits (plural, addressed names, checkDecl's message, collaborators/teams operand order, the body's class table, nested mapping mismatches declared-first, the snapshot key order note).

Rebases: onto f1f938e (#204), pure replay, git range-diff differs only in docs/upgrading/v2-to-v3.md (this PR's sections renumbered 11-15 after #204's 10, the intro counts fifteen breaks); onto 3bf8574 (#200), every commit =; onto d92b738 (#205), two commits ! from the GitHubClient rename in context lines only (the interdiff of added and removed lines is empty), git grep for the old names over src and test is empty, the public-surface pin and lint:arch pass.

Recorded, not built: MappingFacet accepts any scalar entry key as mapping, not only a mapping-valued one; mockFragmentFor reads a dotted identity literally (webhooks keeps hand-written handlers); phantom-name drift on a live hook body lacking name.

Stacked: PR B (environments through the secrets and variables engines) and PR C (roles, org-probe, remaining prose sites) follow on this branch.

BEGIN_COMMIT_OVERRIDE
refactor(sections)!: milestones, webhooks, and rulesets join listSection; duplicate live items fail loudly everywhere

BREAKING CHANGE: two live items under one identity fail every list section, in plan and snapshot; the webhook snapshot placeholder is $SECRET_WEBHOOK_; webhooks manage web hooks only and never write name; a ruleset without source_type is repository-owned, a repeated rule type a settings-file error; one wording per concept in drift lines and notes (docs/upgrading/v2-to-v3.md sections 11-15).
END_COMMIT_OVERRIDE

Copilot AI balanced review requested due to automatic review settings September 13, 2026 07:50
@github-actions

github-actions Bot commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

File size check

0 over a hard cap (fails), 7 warning(s).

File Size Tier Cap
.github/scripts/release-pipeline.ts 1755 lines warn 1600
.github/scripts/release-pipeline.ts:339 168 chars warn 150
.github/scripts/release-pipeline.ts:1 26 comment lines (header) warn 25
.github/scripts/release-pipeline.ts:1572 13 comment lines warn 10
.github/workflows/post-green.yml:139 11 comment lines warn 10
test/action/run.test.ts:759 151 chars warn 150
test/docs/guides.test.ts:468 155 chars warn 150

Split the file, wrap the line, shorten or exempt the comment, or list the path in .file-size-allow.local with a # reason.

4 managed file(s) skipped; repo-platform owns them.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Bespoke snapshots still emit duplicate identities, and webhook updates can send an unsupported name field.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Refactors milestones, rulesets, and webhooks onto the shared list-section framework while standardizing duplicate detection, snapshots, secrets, and diagnostics.

Changes:

  • Extends listSection with item reads, nested mappings, secret handling, concealed fields, and foreign resources.
  • Adds shared live-identity collision detection across list-based sections.
  • Updates tests, mocks, documentation, and v3 migration guidance.
File summaries
File Description
test/sections/snapshot-rows/webhooks.ts Updates webhook snapshot expectations.
test/sections/snapshot-rows/rulesets.ts Updates ruleset omission wording.
test/sections/snapshot-roundtrip.test.ts Tests snapshot duplicate handling.
test/sections/registry.test.ts Updates list identity metadata expectations.
test/sections/list-section.test.ts Tests new factory roles and facets.
test/scripts/changed-sections.test.ts Expands list-section fan-out coverage.
test/engine/secrets.test.ts Updates webhook create payload expectation.
test/e2e/mock/state.ts Registers migrated list-section mocks.
src/sections/webhooks/webhooks.test.ts Updates webhook planning tests.
src/sections/webhooks/webhooks.docs.yml Documents revised webhook behavior.
src/sections/webhooks/scenarios/webhooks-snapshot-roundtrip.yml Updates secret snapshot references.
src/sections/webhooks/scenarios/webhooks-check-drift.yml Updates webhook drift assertions.
src/sections/webhooks/mock.ts Adds webhook list mock metadata.
src/sections/webhooks/index.ts Migrates webhooks to listSection.
src/sections/shared/variables-engine.ts Rejects duplicate live variables.
src/sections/shared/snapshot-helpers.ts Adds shared snapshot-note helpers.
src/sections/shared/secrets-engine.ts Rejects duplicate live secrets.
src/sections/shared/repo-variables.ts Passes section metadata to planning.
src/sections/shared/list-section.ts Expands the list-section framework.
src/sections/secret_scanning_custom_patterns/index.ts Adds live-pattern duplicate detection.
src/sections/rulesets/scenarios/rulesets-snapshot-roundtrip.yml Updates inherited-ruleset output.
src/sections/rulesets/scenarios/rulesets-snapshot-bypass-hidden.yml Updates concealed-field output.
src/sections/rulesets/rulesets.test.ts Updates ruleset behavior tests.
src/sections/rulesets/rulesets.docs.yml Documents full-item reads and ownership.
src/sections/rulesets/mock.ts Adds ruleset list mock metadata.
src/sections/rulesets/index.ts Migrates rulesets to listSection.
src/sections/milestones/scenarios/milestones-undeclared-delete.yml Updates deletion output.
src/sections/milestones/mock.ts Adds milestone list mock metadata.
src/sections/milestones/milestones.test.ts Updates milestone expectations.
src/sections/milestones/index.ts Migrates milestones to listSection.
src/sections/environments/protection-rules.ts Detects duplicate protection rules.
src/sections/environments/nested.ts Detects duplicate nested variables and secrets.
src/sections/environments/branch-policies.ts Detects duplicate branch policies.
src/sections/deploy_keys/index.ts Uses shared live-duplicate handling.
src/sections/deploy_keys/deploy_keys.test.ts Updates duplicate-key tests.
src/sections/deploy_keys/deploy_keys.docs.yml Updates duplicate-key documentation.
src/sections/custom_properties/index.ts Detects duplicate live properties.
src/sections/contract/requests.ts Generalizes declared-duplicate wording.
src/sections/contract/module.ts Adds shared drift and secret helpers.
src/sections/contract/live.ts Introduces liveByIdentity.
src/sections/collaborators/index.ts Detects duplicate collaborators and invitations.
src/engine/secrets.ts Makes secret-reference minting internal.
docs/upgrading/v2-to-v3.md Documents five additional breaking changes.
docs/operate/snapshot.md Updates webhook secret placeholders.
COVERAGE.md Regenerates feature coverage details.
Review details
  • Files reviewed: 45/45 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/sections/webhooks/index.ts Outdated
Comment thread src/sections/contract/live.ts
@Vivswan Vivswan added the merge-when-green Owner approved: merge once every gate is green label Sep 13, 2026
Copilot AI review requested due to automatic review settings September 13, 2026 08:41
@Vivswan
Vivswan force-pushed the refactor/sections-shared-engines branch from 8830a45 to 6503c45 Compare September 13, 2026 08:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Section-specific conflict checks can still preempt the promised shared duplicate-live error.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 50/50 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread src/sections/shared/list-section.ts Outdated
Comment thread docs/upgrading/v2-to-v3.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Note

This error may be related to your runner configuration. You can now configure runners for Copilot code review separately from Copilot cloud agent by creating a copilot-code-review.yml file with your setup steps. Read the docs for details.

@Vivswan Vivswan changed the title refactor(sections)!: milestones, webhooks, and rulesets join listSection; duplicate live items fail loudly everywhere refactor(sections)!: milestones joins listSection; one live-duplicate guard and one wording per concept everywhere Sep 13, 2026
Copilot AI review requested due to automatic review settings September 13, 2026 09:59
@Vivswan
Vivswan force-pushed the refactor/sections-shared-engines branch from 3dfe731 to 9c2794e Compare September 13, 2026 09:59
@Vivswan Vivswan changed the title refactor(sections)!: milestones joins listSection; one live-duplicate guard and one wording per concept everywhere refactor(sections)!: milestones, webhooks, and rulesets join listSection; duplicate live items fail loudly everywhere Sep 13, 2026
@Vivswan
Vivswan force-pushed the refactor/sections-shared-engines branch from 9c2794e to 710c209 Compare September 13, 2026 10:04

Copilot AI commented Sep 13, 2026

Copy link
Copy Markdown

One or more custom setup steps configured for this repository failed during this Copilot code review run:

Run ./.github/actions/setup

Setup steps run before each review. If the review above is missing context, or no review was posted at all, the failing step above may be the cause. See the workflow run for failure details, fix your setup steps configuration, and re-request a review.

Note

You can configure setup steps for Copilot code review separately from Copilot cloud agent with a copilot-code-review.yml file. Read the docs for details.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The upgrade guide promises universal wording that teams, collaborators, and interaction limits do not yet emit.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 52/52 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread docs/upgrading/v2-to-v3.md
Comment thread docs/upgrading/v2-to-v3.md Outdated
Copilot AI review requested due to automatic review settings September 13, 2026 10:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Environment snapshots misclassify branch policies whose API response omits the required name.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 52/52 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/sections/environments/snapshot.ts
…ion; duplicate live items fail loudly everywhere

The list-section factory gains a per-item get role, an updateConfig role over one nested mapping, a dotted identity path, secret fields, concealed fields, foreign items, nested layering, and a note-action hook; the three hand-written planners move onto it. One live-duplicate guard (liveByIdentity) serves plan and snapshot in every section. Shared prose helpers: missingDrift, valueDrift, cannotVerifyNote, secretValuesOf, leftOutOfSnapshot, unreadableSecretNote.

BREAKING CHANGE: two live items under one identity fail every list section; the webhook snapshot placeholder is SECRET_WEBHOOK_<id>; one wording per concept in drift lines and notes; webhooks manage web hooks only; a ruleset without source_type is repository-owned. docs/upgrading/v2-to-v3.md sections 5-9.
…ated rule type is a settings-file error; addressed names and one plural in the guard

Copilot and gate review folds: the guard runs before every bespoke snapshot's projection (repo secrets and variables, custom properties, secret scanning patterns, collaborators and invitations, environments' nested lists); rulesets name a repeated rule type before the read instead of a BUG crash and refuse a live body repeating one; the guard names a live item with its address; plural() spells the nouns; collaborators and teams role drift and nested mapping mismatches render declared-first; webhook writes never carry name.
…a section declares; the guard runs before a section's live conflicts

SecretPath admits a dotted path under mapping only, where both carriers declare the unverifiable facet; the top-level arm rode update and no section used it. The snapshot reference id drops the multi-secret suffix and the store is the noun itself. missingDrift and unreadableSecretNote (one caller each) are inlined; Remedies.value is a string suffix again; liveByIdentity's describe defaults to the key and the call sites that repeated it drop the argument; custom_properties and secret_scanning_custom_patterns guard through one closure each; the secrets engine's listSecretValues goes through secretValuesOf.

The live-duplicate guard now runs before a section's own live conflicts, so two deploy keys under one title are reported before a colliding holder's material. The upgrade doc's duplicate-webhook example carries the hook ids the guard prints, and the two #204 snapshot scenarios pin the SECRET_WEBHOOK_<id> placeholder.
Copilot AI review requested due to automatic review settings September 13, 2026 10:24
@Vivswan
Vivswan force-pushed the refactor/sections-shared-engines branch from 710c209 to bb6fd9d Compare September 13, 2026 10:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The cross-cutting planner, snapshot, and breaking-behavior changes warrant final human validation despite broad test coverage.

Review details
  • Files reviewed: 52/52 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

…ivePolicyName, so a nameless row is a malformed response in plan and snapshot alike
Copilot AI review requested due to automatic review settings September 13, 2026 10:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The shared planner rewrite changes reconciliation and snapshot behavior across many independently managed GitHub resources.

Review details
  • Files reviewed: 52/52 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI review requested due to automatic review settings September 13, 2026 10:44
@Vivswan
Vivswan marked this pull request as ready for review September 13, 2026 10:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The shared planner and snapshot engine changes affect reconciliation and deletion behavior across many sections and warrant final human validation.

Review details
  • Files reviewed: 52/52 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-when-green Owner approved: merge once every gate is green

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants