From 90000b944cc438f6fa87e6874939597bdf714a30 Mon Sep 17 00:00:00 2001 From: Vivswan Shah <58091053+Vivswan@users.noreply.github.com> Date: Tue, 6 Oct 2026 01:46:15 -0400 Subject: [PATCH 1/3] docs(upgrading): count every public-entry break among the library sections --- docs/upgrading/v2-to-v3.md | 2 +- docs/upgrading/v2-to-v3/layering.md | 2 ++ docs/upgrading/v2-to-v3/refusals-lists.md | 2 -- docs/upgrading/v2-to-v3/run.md | 4 ++++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/upgrading/v2-to-v3.md b/docs/upgrading/v2-to-v3.md index 60bb9944..7746cd26 100644 --- a/docs/upgrading/v2-to-v3.md +++ b/docs/upgrading/v2-to-v3.md @@ -4,7 +4,7 @@ order: 20 # Upgrading from v2 to v3 -Sixty-one breaks. Eleven are for library consumers (sections 9, 23, 24, 27, 34, 35, 54, 56, 58, 60, and 61), and two are for anyone pinning a sha or installing `@next` (sections 21 and 59). +Sixty-one breaks. Sixteen are for library consumers (sections 9, 22, 23, 24, 25, 26, 27, 28, 29, 34, 35, 54, 56, 58, 60, and 61), and two are for anyone pinning a sha or installing `@next` (sections 21 and 59). Eighteen are parse-time refusals (sections 36 to 52 and 55): a declaration GitHub would reject, or that could never converge, now fails before any request. Sections 53 and 57 are silent: YAML merge keys resolve, and one validation message is respelled. diff --git a/docs/upgrading/v2-to-v3/layering.md b/docs/upgrading/v2-to-v3/layering.md index a88975ce..bdaea275 100644 --- a/docs/upgrading/v2-to-v3/layering.md +++ b/docs/upgrading/v2-to-v3/layering.md @@ -80,6 +80,8 @@ Every list section folds this way, by the key its planner matches on: labels, co The value `merge` is gone. `labels: {_layering: merge, entries: [...]}` fails with `labels._layering must be one of "replace", "shallow", "deep"; got a string that is none of them`, and the `layering` input refuses it too, naming the same three values. +For `@vivswan/github-settings-as-code` consumers, the `Layering` type is `"replace" | "shallow" | "deep"`, so a `layering: "merge"` in `MergeOptions` stops compiling; write `"deep"`, the fold it meant. + Fix: write `deep` where a layer said `merge`, and `_layering: replace` on any list section a higher layer meant to replace whole. Under `shallow` and `deep` an empty higher list adds nothing; clearing a list takes `replace` with an empty list. The [layering guide](../../operate/layering.md#four-knobs) owns the rules. ## 29. mode: merge is mode: render diff --git a/docs/upgrading/v2-to-v3/refusals-lists.md b/docs/upgrading/v2-to-v3/refusals-lists.md index f9a6e7b3..af2bddcb 100644 --- a/docs/upgrading/v2-to-v3/refusals-lists.md +++ b/docs/upgrading/v2-to-v3/refusals-lists.md @@ -205,8 +205,6 @@ GitHub answers `protection_rules: []` for an unprotected environment, and the fl | More than 6 `reviewers` | 422 | Refused: GitHub's cap | | `deployment_branch_policies[].type: wildcard` | Deleted the live policy, then the create 422ed; every run retried | Refused: `branch` or `tag` | -For library consumers, `DeploymentBranchPolicyConfig.type` narrows from `string` to `"branch" | "tag"`. - ## 52. Autolinks: charset, placeholder, and overlapping prefixes ```text diff --git a/docs/upgrading/v2-to-v3/run.md b/docs/upgrading/v2-to-v3/run.md index 71316f2b..31c9d2b3 100644 --- a/docs/upgrading/v2-to-v3/run.md +++ b/docs/upgrading/v2-to-v3/run.md @@ -119,6 +119,8 @@ The action and the command line print the same line for the same problem; the re | `gsac init --settings-file a,b` | `the --settings-file value "a,b" contains a comma or a newline, which check and apply read as a list separator ...` | | `the "settings-file" input is "a,b", which contains a list separator: init writes exactly one settings file ... Name one file` | | An unreadable `repos-dir` file | `cannot read settings from : ... Fix the file, or delete it to stop managing this repository` | | `cannot read the central settings file : ... Fix the file, or delete it to stop managing this repository` (a YAML syntax error in it reads the same way, where v2 said `cannot parse`) | +For `@vivswan/github-settings-as-code` consumers: `parseConfig` takes a third argument, the face's `RunCapabilities`, and refuses `private-report: artifact` there (`input-artifact-unsupported`); a two-argument call stops compiling. + ## 25. Every live read is parsed at the port Every GET and GraphQL query a section issues now passes through one parser before the section sees the body, so a response off the documented shape fails the section instead of flowing into a comparison. @@ -136,6 +138,8 @@ Two smaller moves ride along: - A section that reads anything must declare `snapshot()`; only the write-only `check_suite_preferences` reports `unsupported`, and the `snapshot is not implemented for this section yet` note is gone. - The organization-only sections (`teams`, `custom_properties`) are probed for the owner kind by the registry, ahead of their own plan and snapshot. The personal-account note is unchanged. A settings-file mistake in those sections (two entries naming one team) is validation's, before any request ([section 34](#34-file-only-checks-run-before-the-first-write)). +For `@vivswan/github-settings-as-code` consumers, `SectionModule` requires `snapshot()` on a module whose endpoints declare a read; a custom reading module without one stops compiling, so implement it. + ## 26. The snapshot file is canonical and undated The file `mode: snapshot`, `gsac init`, and `snapshotRepository` write carries no timestamp, and its document renders in the one canonical order the rendered file shares, so a snapshot of an unchanged repository is byte for byte the last one and a diff between two snapshots shows only what changed on GitHub. From d830d6914fab60c1a552f576a4791a539c502f3e Mon Sep 17 00:00:00 2001 From: Vivswan Shah <58091053+Vivswan@users.noreply.github.com> Date: Tue, 6 Oct 2026 02:35:52 -0400 Subject: [PATCH 2/3] docs(upgrading): state when SectionModule requires snapshot() --- docs/upgrading/v2-to-v3/run.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/upgrading/v2-to-v3/run.md b/docs/upgrading/v2-to-v3/run.md index 31c9d2b3..116cecca 100644 --- a/docs/upgrading/v2-to-v3/run.md +++ b/docs/upgrading/v2-to-v3/run.md @@ -138,7 +138,7 @@ Two smaller moves ride along: - A section that reads anything must declare `snapshot()`; only the write-only `check_suite_preferences` reports `unsupported`, and the `snapshot is not implemented for this section yet` note is gone. - The organization-only sections (`teams`, `custom_properties`) are probed for the owner kind by the registry, ahead of their own plan and snapshot. The personal-account note is unchanged. A settings-file mistake in those sections (two entries naming one team) is validation's, before any request ([section 34](#34-file-only-checks-run-before-the-first-write)). -For `@vivswan/github-settings-as-code` consumers, `SectionModule` requires `snapshot()` on a module whose endpoints declare a read; a custom reading module without one stops compiling, so implement it. +For `@vivswan/github-settings-as-code` consumers, a `SectionModule` typed over its literal endpoint dictionaries requires `snapshot()` when an endpoint declares a read; with the erased default, which `sectionModule()` returns, nothing is checked. ## 26. The snapshot file is canonical and undated From 01ae66a25140a8644da5b12b76d3651795d9e4bd Mon Sep 17 00:00:00 2001 From: Vivswan Shah <58091053+Vivswan@users.noreply.github.com> Date: Tue, 6 Oct 2026 03:16:44 -0400 Subject: [PATCH 3/3] docs(upgrading): count the breaks a consumer meets through SettingsFile and SectionInput Section 51 and nine other parse-time refusals narrowed a nested type under the exported SettingsFile and SectionInput, so a literal that compiled under the pre-release public entry fails now. Each of the ten states the break through the exported type, and the intro counts twenty-six library sections. The eight refusals that are refinements alone (36, 42, 43, 44, 46, 48, 50, 52) change no type and stay out of the list. --- docs/upgrading/v2-to-v3.md | 2 +- docs/upgrading/v2-to-v3/refusals-lists.md | 10 ++++++++++ docs/upgrading/v2-to-v3/refusals-repository.md | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/upgrading/v2-to-v3.md b/docs/upgrading/v2-to-v3.md index 7746cd26..f12a11f7 100644 --- a/docs/upgrading/v2-to-v3.md +++ b/docs/upgrading/v2-to-v3.md @@ -4,7 +4,7 @@ order: 20 # Upgrading from v2 to v3 -Sixty-one breaks. Sixteen are for library consumers (sections 9, 22, 23, 24, 25, 26, 27, 28, 29, 34, 35, 54, 56, 58, 60, and 61), and two are for anyone pinning a sha or installing `@next` (sections 21 and 59). +Sixty-one breaks. Twenty-six are for library consumers (sections 9, 22 to 29, 34, 35, 37 to 41, 45, 47, 49, 51, 54 to 56, 58, 60, and 61), ten of those also refusals below, and two are for anyone pinning a sha or installing `@next` (sections 21 and 59). Eighteen are parse-time refusals (sections 36 to 52 and 55): a declaration GitHub would reject, or that could never converge, now fails before any request. Sections 53 and 57 are silent: YAML merge keys resolve, and one validation message is respelled. diff --git a/docs/upgrading/v2-to-v3/refusals-lists.md b/docs/upgrading/v2-to-v3/refusals-lists.md index af2bddcb..7541dc33 100644 --- a/docs/upgrading/v2-to-v3/refusals-lists.md +++ b/docs/upgrading/v2-to-v3/refusals-lists.md @@ -52,6 +52,8 @@ v3 branches[0].protection.required_status_checks.enforcement_level: ... is Git | A scalar where `required_status_checks` or `required_pull_request_reviews` goes | Refused on a wildcard entry, passed through to the PUT on a literal one | Refused: a mapping or `null` on every entry | | An actor object copied from the GET (`restrictions.users: [{login: octocat}]`) | Sent to the PUT, which 422ed | Refused: write the `login` or `slug` string, `octocat` | +For `@vivswan/github-settings-as-code` consumers, `required_status_checks` and `restrictions` under `SectionInput<"branches">` (and so `SettingsFile`) are typed shapes, once `unknown`; a check list without `strict`, or an actor object, stops compiling. + Fix: declare the PUT's shape (bare booleans, `strict` beside the check list, actors as their login or slug strings) instead of pasting the GET. Also new: a live `restrictions` holder whose `users`, `teams`, and `apps` are all empty is a push restriction that lets nobody through, so a file that omits it now sees the omitted-live drift line and a loud PUT instead of a silent lift. ## 42. Collaborators and teams: permissions and slugs @@ -121,6 +123,8 @@ v3 webhooks[0].config.url: "hooks.example.com/ci" is not an absolute URL (the webhooks[0].events[1]: "pushes" is not an event GitHub delivers to repository webhooks ("*" means every event); the accepted names are GitHub's list at https://docs.github.com/webhooks/webhook-events-and-payloads, read from @octokit/openapi-webhooks, so an event GitHub added since arrives in the release that bumps that package ``` +For `@vivswan/github-settings-as-code` consumers, under `SectionInput<"webhooks">` (and so `SettingsFile`) `content_type`, `insecure_ssl`, and each `events` item are the unions above, once `string` or `number`; `JSON`, `2`, or `pushes` stops compiling. + Fix: an absolute URL, `json` or `form`, `"0"` or `"1"`, and event names from GitHub's repository list. The list is pinned to `@octokit/openapi-webhooks`, so an event GitHub adds later is refused until the release that bumps that package. ## 48. Secret scanning patterns must compile @@ -160,6 +164,8 @@ v3 rulesets[0].enforcement: Invalid option: expected one of "active"|"evaluate Bypass actors are typed from the spec: Integration, RepositoryRole, Team, and User need an `actor_id`; DeployKey takes none and never `pull_request`; `pull_request` applies to branch rulesets only. The 23 known rule types carry their parameters typed from the spec, in GitHub's casing; an unknown rule type still passes through, so a type GitHub ships tomorrow works the day it ships. +For `@vivswan/github-settings-as-code` consumers, `enforcement` and `actor_type` under `SectionInput<"rulesets">` (and so `SettingsFile`) are the enums above, once `string` and `unknown`; `enabled` stops compiling; a rule's parameters still compile. + Fix: spell the enums as GitHub does, give each actor its id, and write `~ALL` or `~DEFAULT_BRANCH`. ## 50. Milestones: due_on is a day @@ -205,6 +211,8 @@ GitHub answers `protection_rules: []` for an unprotected environment, and the fl | More than 6 `reviewers` | 422 | Refused: GitHub's cap | | `deployment_branch_policies[].type: wildcard` | Deleted the live policy, then the create 422ed; every run retried | Refused: `branch` or `tag` | +For `@vivswan/github-settings-as-code` consumers, a policy's `type` under `SectionInput<"environments">` (and so `SettingsFile`) is `"branch" | "tag"`, where the pre-release v3 builds typed `string`; a `string` there no longer compiles. + ## 52. Autolinks: charset, placeholder, and overlapping prefixes ```text @@ -247,6 +255,8 @@ v3 branches[0].protection.restrictions.users: protection.restrictions must car GitHub's protection PUT requires `users` and `teams` under `restrictions` and takes `apps` as optional, so each missing list is refused before any request. The two review-side holders are unchanged: `dismissal_restrictions: {}` and `bypass_pull_request_allowances: {}` stay legal, since GitHub documents the empty mapping there as "disabled". +For `@vivswan/github-settings-as-code` consumers, `restrictions` under `SectionInput<"branches">` (and so `SettingsFile`) requires `users` and `teams`, where the pre-release v3 builds typed the block `unknown`; `restrictions: {}` stops compiling. + Fix: declare both lists (`users: []` and `teams: []` when none), or write `restrictions: null` to lift the push restriction. ## 57. A closed section's unrecognized key names the entry by index diff --git a/docs/upgrading/v2-to-v3/refusals-repository.md b/docs/upgrading/v2-to-v3/refusals-repository.md index 218f7747..69d012e5 100644 --- a/docs/upgrading/v2-to-v3/refusals-repository.md +++ b/docs/upgrading/v2-to-v3/refusals-repository.md @@ -28,6 +28,8 @@ v3 repository.has_downloads: has_downloads is reported by GitHub but cannot be | A topic outside `^[a-z0-9][a-z0-9-]{0,49}$` after lowercasing, or more than 20 | 422 from the topics PUT | Refused naming the topic | | An empty topic (`topics: [""]`, `topics: ""`, `ci,,tooling`) | Dropped silently, so `[""]` cleared every topic | Refused by index | +For `@vivswan/github-settings-as-code` consumers, the commit-message keys under `SectionInput<"repository">` (and so `SettingsFile`) are enums and `security_and_analysis` is a closed shape, once `unknown`; a `string` or an unknown sub-key stops compiling. + Fix: delete the GET-only key, declare the title beside the message, and spell topics in GitHub's alphabet. A key in neither the GET nor the PATCH still passes through, so a field GitHub adds later works on day one. ## 38. Pages: GET-only fields and the source path @@ -45,6 +47,8 @@ v3 pages.custom_404: GitHub reports this field on the Pages site and the updat `url`, `html_url`, `status`, `custom_404`, `protected_domain_state`, `pending_domain_unverified_at`, and `https_certificate` are refused. `public: false` stays declarable: Enterprise Cloud shares the same host, so the check run's note beside the drift says why it cannot converge elsewhere. +For `@vivswan/github-settings-as-code` consumers, `source.path` under `SectionInput<"pages">` (and so `SettingsFile`) is `"/" | "/docs"`, where the pre-release v3 builds typed `string`; another path there stops compiling. + Fix: delete the reported field from the file, and publish from `/` or `/docs`. ## 39. Actions: fields that could never converge @@ -69,6 +73,8 @@ v3 actions.artifact_and_log_retention.maximum_allowed_days: maximum_allowed_da | An `approval_policy` outside GitHub's three; a malformed or repeated OIDC claim key; `include_claim_keys` beside `use_default: true` | Refused naming the key | | A fractional or non-positive retention or cache limit | Refused: positive integers | +For `@vivswan/github-settings-as-code` consumers, in `SectionInput<"actions">` (and so `SettingsFile`) `approval_policy` is an enum, once `string`, and `selected_actions` is closed, once an open mapping; a `string` or an unknown key there stops compiling. + Fix: delete the reported-only field, fix the `selected_actions` spelling, and write booleans and integers unquoted. `fork_pr_workflows_private_repos` now requires only `run_workflows_from_fork_pull_requests`, as the request body does. ## 40. Setup sections: GET-only keys, languages, and the runner pair @@ -89,6 +95,8 @@ v3 code_scanning_default_setup.schedule: "schedule" is reported by GitHub but code_quality_setup.runner_label: runner_label "gpu" is declared under runner_type: "standard", where GitHub ignores it; set runner_type: "labeled", or remove runner_label ``` +For `@vivswan/github-settings-as-code` consumers, `languages` under `SectionInput<"code_scanning_default_setup" | "code_quality_setup">` (and so `SettingsFile`) is an array whose items are the PATCH's enum, once `string`; `javascript` stops compiling. + Fix: drop `schedule` and `updated_at`, spell languages as the PATCH takes them (`javascript-typescript`; code quality's GET-only `rust` has no declarable name), and pair a string `runner_label` with `runner_type: labeled`. A declared key outside the slice that the GET never echoes now earns the never-converges note in check and apply. ## 44. Check-suite preferences: app_id @@ -122,4 +130,6 @@ v3 interaction_limits.limit: limit is one of existing_users, contributors_only interaction_limits: Unrecognized key: "expires_at"; interaction_limits takes limit, expiry, pull_request_creation_cap, and pull_request_creation_bypass (origin and expires_at are what GitHub reports, not what it accepts); remove the key, or fix its spelling ``` +For `@vivswan/github-settings-as-code` consumers, `limit` and `expiry` under `SectionInput<"interaction_limits">` (and so `SettingsFile`) are the enums above, where the pre-release v3 builds typed `string`; another value there stops compiling. + Fix: spell the enum values as listed, keep the cap in range, and delete `origin` and `expires_at`, which GitHub reports but never accepts.