Repository navigation
refactor(sections)!: milestones, webhooks, and rulesets join listSection; duplicate live items fail loudly everywhere - #202
Conversation
File size check0 over a hard cap (fails), 7 warning(s).
Split the file, wrap the line, shorten or exempt the comment, or list the path in 4 managed file(s) skipped; repo-platform owns them. |
There was a problem hiding this comment.
🟡 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
listSectionwith 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.
8830a45 to
6503c45
Compare
There was a problem hiding this comment.
🟡 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
There was a problem hiding this comment.
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.
3dfe731 to
9c2794e
Compare
9c2794e to
710c209
Compare
|
One or more custom setup steps configured for this repository failed during this Copilot code review run: 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 |
There was a problem hiding this comment.
🟡 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
There was a problem hiding this comment.
🟡 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
…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.
710c209 to
bb6fd9d
Compare
…ivePolicyName, so a nameless row is a malformed response in plan and snapshot alike
…e types render once
There was a problem hiding this comment.
🔵 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
Before / after
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):
$SECRET_WEBHOOK_<id>(was$WEBHOOK_SECRET_<id>).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.name;insecure_sslis written as the string GitHub stores; the snapshot writesconfigkeys in GitHub's order.source_typeis repository-owned (deletable under_undeclared: delete); a repeated rule type is a settings-file error.Proof
bun run checkclean),bun run test:e2e258/258 afterbun .github/scripts/trim-openapi.ts; the prose-and-request harness below classifies every main-vs-head difference (0 unclassified, control caught).Technical details
Factory features added, and who uses them
getrole, per-item read (25)updateConfigrole +mappingsplit (60)secrets: a dotted path undermapping, resolvedWrite, the facet, snapshot references, secretValues (85)concealed(30)foreign(35)layering.nested(3)prose.undeclaredNote.action(3)liveByIdentity+plural(contract/live.ts, 28),addressed(11)valueDrift,cannotVerifyNote,secretValuesOf(contract/module.ts),leftOutOfSnapshot(snapshot-helpers.ts)writeOnlyCheckNote, the secrets engine,readOrNoteTwo 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_SUFFIXon snapshot reference ids, the store-name sanitizing,missingDriftandunreadableSecretNote(one caller each, inlined; they return in PR C with their conversions), theremedy: nullarm, a requireddescribeon 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.
$SECRET_WEBHOOK_<id><label>: left out of the snapshot - <reason>webhooks[<url>]name(GitHub defaultsweb; the update endpoint takes no name)declared X != live Y; apply will set the declared value(top-level fields)(id N)source_typeis deletableControl: 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):
src/sections/webhooks/index.tssrc/sections/milestones/index.tssrc/sections/rulesets/index.tssrc/sections/deploy_keys/index.tssrc/sections/shared/snapshot-helpers.tssrc/sections/shared/secrets-engine.tssrc/sections/environments/branch-policies.ts,protection-rules.tssrc/engine/secrets.ts,src/sections/shared/variables-engine.tssrc/sections/contract/requests.tssrc/sections/shared/repo-secrets.ts,repo-variables.tssrc/sections/custom_properties/index.ts,secret_scanning_custom_patterns/index.tssrc/sections/teams/index.tsvalueDriftcall and import wrap)src/sections/environments/nested.tssrc/sections/environments/snapshot.tssrc/sections/contract/live.tsliveByIdentity,plural)src/sections/collaborators/index.tsvalueDriftcalls)src/sections/contract/module.tsvalueDrift,cannotVerifyNote,secretValuesOf)src/sections/shared/list-section.tsThe 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,
missingDriftandunreadableSecretNotewith 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 itsexport(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 throughlivePolicyName, 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 (exportdropped 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: theWEBHOOK_SECRET_701->SECRET_WEBHOOK_701rename 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, notleft 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-diffdiffers 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 theGitHubClientrename in context lines only (the interdiff of added and removed lines is empty),git grepfor the old names over src and test is empty, the public-surface pin andlint:archpass.Recorded, not built:
MappingFacetaccepts any scalar entry key asmapping, not only a mapping-valued one;mockFragmentForreads a dotted identity literally (webhooks keeps hand-written handlers); phantom-name drift on a live hook body lackingname.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