From 545e5d7e538b556e67bf2bf6a85591a632272a78 Mon Sep 17 00:00:00 2001 From: Vivswan Shah <58091053+Vivswan@users.noreply.github.com> Date: Tue, 22 Sep 2026 06:10:40 -0400 Subject: [PATCH] docs: lean AGENTS.md to rules, fix the install sentence, and retire comments naming deleted workflows and paths - AGENTS.md keeps each rule as one sentence and drops the mechanism CONTRIBUTING.md or the code already carries. - Dropped there: generated output and its drift check, the built lib paths, the e2e command, the COMPAT marker, the test mirror, the registry's checks, and the release topology. - The intro sentence now points at CONTRIBUTING.md for the mechanics. - README.md and docs/reference/library.md no longer say a bare npm install installs the released version; until the first stable release, latest is the 0.0.0 placeholder, so both ask for the next tag as the command line guide does. - The stable-publish verdict's comment in release-pipeline.ts and update-release.yml's say the same: latest names the first publish (placeholder or pre-release) until a release takes it over. - The bundle-parity comments in test/e2e/runner.ts and runner.test.ts no longer name an e2e job that skipped on a package.json-only diff; they say why the pin stays a unit test. - The GenScenarioOptions.sections doc names the --sections flag instead of a smoke or PR-diff subset. - The webhooks events header names test/sections/webhooks/events.test.ts, where the test lives now. - Two auto-fix.yml comments no longer name a dependabot-bun-lockfile.yml workflow that does not exist; the stale-lockfile row says a close/reopen re-runs against the same lockfile and only a push with a real token brings a fresh one. --- .github/scripts/release-pipeline.ts | 6 +++--- .github/workflows/auto-fix.yml | 9 ++++----- .github/workflows/update-release.yml | 2 +- AGENTS.md | 18 +++++++++--------- README.md | 2 +- docs/reference/library.md | 6 +++--- src/sections/webhooks/events.ts | 2 +- test/e2e/generators.ts | 2 +- test/e2e/runner.test.ts | 4 ++-- test/e2e/runner.ts | 4 ++-- 10 files changed, 27 insertions(+), 28 deletions(-) diff --git a/.github/scripts/release-pipeline.ts b/.github/scripts/release-pipeline.ts index b0d3c052..abe40463 100644 --- a/.github/scripts/release-pipeline.ts +++ b/.github/scripts/release-pipeline.ts @@ -1070,9 +1070,9 @@ export function stablePublishVerdict(version: string, packument: Packument | nul }; } const latest = packument["dist-tags"].latest; - // Until the first release, latest names a pre-release: a packument always carries that key (npm/registry - // REGISTRY-API.md, "dist-tags: an object with at least one key, latest"), so the first publish took it whatever - // --tag asked for. A release must take latest over from it, so only a newer RELEASE holds one back. + // Until the first stable release, latest names whatever the first publish was (the 0.0.0 placeholder, or a + // pre-release): a packument always carries that key (npm/registry REGISTRY-API.md, "dist-tags: an object with at + // least one key, latest"), whatever --tag asked for. A release must take latest over, so only a newer RELEASE holds one back. const held = latest === undefined ? null : parseMinted(latest); if (held?.sha7 === null && newerRelease(held.release, parseMinted(version).release)) { return { diff --git a/.github/workflows/auto-fix.yml b/.github/workflows/auto-fix.yml index 77f7fd22..f17d2c66 100644 --- a/.github/workflows/auto-fix.yml +++ b/.github/workflows/auto-fix.yml @@ -16,8 +16,8 @@ # Dependabot-triggered runs read the Dependabot secrets store, not the Actions one, so REPO_PLATFORM_TOKEN goes in # both. A push made with github.token starts no workflows. # no token, install succeeds -> the fix lands but CI does not re-run; the job posts a PR comment and a warning (close/reopen the PR) -# no token, Dependabot bun PR -> the stale lockfile fails the frozen install here until dependabot-bun-lockfile.yml's -# dedupe push retriggers this workflow, which only a real token does +# no token, Dependabot bun PR -> a bun.lock the Bun updater left stale fails the frozen install here; a close/reopen +# re-runs it against the same stale lockfile; only a push made with a real token brings a fresh one name: Auto Fix @@ -153,9 +153,8 @@ jobs: PRUNED: ${{ needs.build.outputs.pruned }} shell: bash run: | - # The branch can move between the build and this push (a Dependabot rebase, or dependabot-bun-lockfile.yml's - # dedupe commit, which also invalidates the fix): the patch belongs to HEAD_SHA only, and the newer head's - # own run owns the rebuild. + # The branch can move between the build and this push (a Dependabot rebase): the patch belongs to + # HEAD_SHA only, and the newer head's own run owns the rebuild. superseded() { git fetch --quiet origin "$HEAD_REF" [ "$(git rev-parse "origin/$HEAD_REF")" != "$HEAD_SHA" ] diff --git a/.github/workflows/update-release.yml b/.github/workflows/update-release.yml index de8de14d..93436e5e 100644 --- a/.github/workflows/update-release.yml +++ b/.github/workflows/update-release.yml @@ -170,7 +170,7 @@ jobs: # registry: a version already there (a rerun) or a latest that is a # newer release (an older release's job rerun after a newer release) # publishes nothing, so no release ever displaces a newer one from - # latest (a pre-release there, the bootstrap's, is taken over). GITHUB_SHA on the + # latest (the first publish's placeholder or pre-release there is taken over). GITHUB_SHA on the # publish line sets only the SLSA predicate's source digest; the # sigstore certificate still carries this run's own head, so a release # recovered by a newer push's run has provenance naming two commits. diff --git a/AGENTS.md b/AGENTS.md index 68b6d0a0..feb37a10 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,29 +32,29 @@ GitHub Settings as Code: GitHub Action applying declarative repository settings: -Code is the source of truth: this section holds only the rules and the decisions a reader could not recover from the code. +Code is the source of truth: this section holds only the rules and the decisions a reader could not recover from the code. The mechanics (toolchain, generated output, tests, releases) are in CONTRIBUTING.md. ### Hard rules -- Generated artifacts (`lib/settings.schema.json`, `src/upstream-gaps/index.ts`, the generated docs and `action.yml` regions) are regenerated, never hand-edited; `.github/scripts/generated.ts` lists them and `bun run build:check` fails on drift. -- `lib/index.js` (the action bundle) and `lib/pkg/` (the npm library) are built, never committed on main; the packaged commits off main carry them. +- Generated artifacts are regenerated, never hand-edited; `.github/scripts/generated.ts` is the one list of them. +- `lib/index.js` and `lib/pkg/` are built, never committed on main. - Every GitHub list call goes through `listAll()` or `listAllEnveloped()`, and every API error through `call()`/`failureFor()`, so the permission policy holds (`src/sections/contract/requests.ts`). - What can be known wrong from the settings file alone is refused when the file is parsed, naming the key and the fix, never discovered at apply time: GET-only fields, enum violations, contradictory key pairs, unknown keys in a closed GitHub shape. Open passthrough shapes keep unknown keys and note them at check time when GitHub does not echo them back. - The import layering of `src/` is declared in `architecture.yml`; a new cross-layer import is a deliberate edit to that file. - A type a section module exposes is exported from its home module, or the bundled declarations cannot reach it and the package-smoke job fails. -- New sections and endpoints ship with e2e scenarios, and `bun run test:e2e` runs green before they land. +- New sections and endpoints ship with e2e scenarios. - Errors are values: `throw` only for `BUG:` invariants (programming errors no user can cause), a bare rethrow in its `catch`, and where a third party's contract demands it. The Biome plugin `lint/never-throw.grit` enforces it. -- No backward-compatibility shims: a change that breaks an input, key, format, or behavior ships the break behind a major with a loud error naming the fix; a one-shot migration only when many files must move at once. A shim that stays anyway carries a `COMPAT(vN)` marker naming the major that deletes it. +- No backward-compatibility shims: a change that breaks an input, key, format, or behavior ships the break behind a major with a loud error naming the fix; a one-shot migration only when many files must move at once. ### Decisions a reader would otherwise reverse - A flat `src/sections//` directory means repository scope, permanently; org/user scopes arrive as sibling scope directories with their own document, keys, and registry (the ":" reservation in `src/sections/registry.ts`). -- The section directory is the unit of work: the compiler flags every forgotten registration step, its tests and e2e fragments mirror it under `test/sections//`, and its prose is `docs/sections/.docs.yml`. +- The section directory is the unit of work; its prose is `docs/sections/.docs.yml`. - `src/upstream-gaps/` holds one file per GitHub feature an upstream artifact lags; `gap.ts` states how each kind graduates. -- The layered fold is a CSS-like cascade in which `null` is a value: GitHub's EMPTY or OFF state, not CSS `unset`. docs/operate/layering.md owns the rules, including the two CSS has no analogue for (an undeclared key keeps GitHub's current value; `_remove: true` drops a keyed entry). +- The layered fold is a CSS-like cascade in which `null` is a value: GitHub's EMPTY or OFF state, not CSS `unset`. docs/operate/layering.md owns the rules, including the two CSS has no analogue for. ### Releases -- The `release` job in ci.yml stays out of all-green's `needs`: it runs downstream of the gate so releases only happen on a green main. -- Every consumable ref points at a packaged commit off main, and a pointer only ever moves forward along main; main stays source-only and no tag ever lands on it. Topology and the rule: `.github/scripts/release-pipeline.ts`. +- The `release` job in ci.yml stays out of all-green's `needs`, so a release only happens from a green main. +- Every consumable ref points at a packaged commit off main, and a pointer only ever moves forward along main; no tag ever lands on main. - The npm package publishes from the release hooks through trusted publishing; docs/reference/library.md states the versioning rules. diff --git a/README.md b/README.md index 040741b8..7e366a2a 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Apply declarative repository settings from `.github/settings.yml`: a loud, state The same engine is the npm package `@vivswan/github-settings-as-code` (ESM, Node 22.14 or newer): validate, merge, check, and apply from your own code. -- `npm install @vivswan/github-settings-as-code` installs the released version; `@next` installs the pre-release of the `main` commit release-please last refreshed the release PR on ([Versioning](docs/reference/library.md#versioning)). The [library reference](docs/reference/library.md) has the API by group and the versioning rules. +- Until the first stable release publishes the package, ask for the `next` tag: `npm install @vivswan/github-settings-as-code@next` installs the pre-release of the `main` commit release-please last refreshed the release PR on ([Versioning](docs/reference/library.md#versioning)). The [library reference](docs/reference/library.md) has the API by group and the versioning rules. - `npx @vivswan/github-settings-as-code@next check --repository o/r --settings-file .github/settings.yml` runs the action's check from a terminal. The [command line guide](docs/start/cli.md) has every command. ## Docs diff --git a/docs/reference/library.md b/docs/reference/library.md index bbe466ce..6632ccf4 100644 --- a/docs/reference/library.md +++ b/docs/reference/library.md @@ -11,8 +11,8 @@ The engine behind the action is also an npm package, `@vivswan/github-settings-a Three ways in, one package: ```bash -npm install @vivswan/github-settings-as-code # the released version (npm dist-tag latest) -npm install @vivswan/github-settings-as-code@next # the pre-release of the main commit the release PR was last refreshed on +npm install @vivswan/github-settings-as-code@next # the pre-release of the main commit the release PR was last refreshed on; ask for it until the first stable release +npm install @vivswan/github-settings-as-code # the released version (npm dist-tag latest), a 0.0.0 placeholder until the first stable release npm install github:Vivswan/github-settings-as-code# # one packaged commit: a build tag's or a release tag's ``` @@ -386,7 +386,7 @@ The package and the action share one version, the one in `.release-please-manife | npm dist-tag | Publishes on | Version | Install | |---|---|---|---| | `next` | Every refresh of the release PR: release-please creates or refreshes it when a releasable commit (feat, fix, perf, revert, or a breaking marker) lands on `main` | The manifest's next patch, then `-main...g`: `2.0.1-main.446.20260913.g95d081d` | `npm install @vivswan/github-settings-as-code@next` | -| `latest` | Every release cut | The released version: `2.1.0`. Until the first release it names a `next` pre-release: a packument always carries `latest` (npm/registry REGISTRY-API.md, "dist-tags: an object with at least one key, latest"), so the first publish took it whatever `--tag` asked for, and the first stable release moves it | `npm install @vivswan/github-settings-as-code` | +| `latest` | Every release cut | The released version: `2.1.0`. Until the first stable release it names the `0.0.0` placeholder that reserved the package name: a packument always carries `latest` (npm/registry REGISTRY-API.md, "dist-tags: an object with at least one key, latest"), so the first publish took it, and the first stable release moves it; ask for `@next` until then | `npm install @vivswan/github-settings-as-code` | | none | Every green push to `main` (`build/.`, the ten newest kept) and every release tag | The commit itself | `npm install github:Vivswan/github-settings-as-code#` | The npm dist-tag `latest` is not the git tag `latest`: the git tag names the packaged commit of the newest `main` commit (every green push moves it forward, never back), the dist-tag names the newest release on the registry. diff --git a/src/sections/webhooks/events.ts b/src/sections/webhooks/events.ts index f3f2cd4b..48127a47 100644 --- a/src/sections/webhooks/events.ts +++ b/src/sections/webhooks/events.ts @@ -2,7 +2,7 @@ * The events GitHub delivers to repository webhooks, spelled as the wire event name: every webhook in GitHub's * webhooks OpenAPI description, as @octokit/openapi-webhooks ships it (generated/api.github.com.json), whose * supported-webhook-types names "repository". The list is committed, not imported: bundling the descriptor would - * double lib/index.js. test/sections/webhooks-events.test.ts recomputes it from the package and fails with the + * double lib/index.js. test/sections/webhooks/events.test.ts recomputes it from the package and fails with the * names added and dropped when a bump moves the list; the fix is to edit this file to match. */ diff --git a/test/e2e/generators.ts b/test/e2e/generators.ts index 7021c086..905cd405 100644 --- a/test/e2e/generators.ts +++ b/test/e2e/generators.ts @@ -1027,7 +1027,7 @@ export function validateAgainstPublishedSchema(doc: unknown): void { } export interface GenScenarioOptions { - /** Restrict generation to these sections (a smoke or PR-diff subset). */ + /** Restrict generation to these sections (the `--sections` flag); the default is every registered section. */ sections?: SectionKey[]; } diff --git a/test/e2e/runner.test.ts b/test/e2e/runner.test.ts index a70fc263..13e1be76 100644 --- a/test/e2e/runner.test.ts +++ b/test/e2e/runner.test.ts @@ -225,8 +225,8 @@ describe("snapshotCheckInputs (the round-trip check's inputs)", () => { describe("bundle build parity (harness vs production)", () => { test("the declared build:bundle script matches what the harness builds", () => { - // A unit test on purpose: a package.json-only diff selects no sections and skips the e2e smoke - // job, so this is the only place the pin can fire on the PR that trips it. + // A unit test on purpose: builtBundle() checks the same pin as a fast local signal that aborts + // the whole e2e run; this test is the binding assertion, failing by name on the PR that drifts. expect(bundleBuildParityFailure(declaredBuildBundleScript())).toBeUndefined(); }); diff --git a/test/e2e/runner.ts b/test/e2e/runner.ts index 1087f23b..882527e5 100644 --- a/test/e2e/runner.ts +++ b/test/e2e/runner.ts @@ -52,8 +52,8 @@ import { collectYmlFiles, type Expect, type Scenario, settingsYamlFor } from "./ const BUILD_BUNDLE_SCRIPT = "bun build src/main.ts --target=node --outfile lib/index.js"; /** - * Exported so a UNIT test asserts the parity on every PR: builtBundle() runs only when a scenario - * runs, and the e2e smoke job skips on a package.json-only diff, the very PR that changes this script. + * Exported so a UNIT test asserts the parity by name on every PR. builtBundle() checks it too, but + * as a fast local signal that aborts the whole e2e run; the unit test is the binding assertion. */ export function bundleBuildParityFailure(script: string | undefined): string | undefined { return script === BUILD_BUNDLE_SCRIPT