Skip to content

feat(snapshot): repository, actions, and rulesets read back - #143

Merged
Vivswan merged 7 commits into
mainfrom
feat/snapshot-core-sections
Sep 12, 2026
Merged

Vivswan merged 7 commits into
mainfrom
feat/snapshot-core-sections

Conversation

@Vivswan

@Vivswan Vivswan commented Sep 11, 2026 •

Copy link
Copy Markdown
Owner

What this adds

A mode: snapshot run of the seeded mock repository now reads these three sections back (excerpt of the rendered file):

# rulesets: rulesets[org-baseline]: inherited from the organization (source_type "Organization"), so it is not part of the repository's snapshot; manage it where it is defined
# repository: repository.enable_git_lfs: GitHub exposes no endpoint to read Git LFS back, so the snapshot leaves it out; declare it yourself to manage it
repository:
  description: End-to-end fixture repository for settings-as-code.
  visibility: public
  default_branch: main
  delete_branch_on_merge: true
  topics: [settings-as-code, automation]
  enable_vulnerability_alerts: true
  enable_immutable_releases: true
  enable_sponsorships: true
  issue_creation_policy: collaborators_only
rulesets:
  _undeclared: keep
  entries:
    - name: protect-main
      target: branch
      enforcement: active
      conditions: { ref_name: { include: ["~DEFAULT_BRANCH"], exclude: [] } }
      rules:
        - type: deletion
        - { type: pull_request, parameters: { required_approving_review_count: 2 } }
      bypass_actors: [{ actor_id: 5, actor_type: RepositoryRole, bypass_mode: always }]
actions:
  enabled: true
  allowed_actions: selected
  selected_actions: { github_owned_allowed: true, verified_allowed: true, patterns_allowed: [docker/*] }
  default_workflow_permissions: write
  artifact_and_log_retention: { days: 14 }
  cache: { max_cache_retention_days: 7, max_cache_size_gb: 10 }
  • repository: the documented PATCH fields the GET reports (an allowlist pinned to the octokit PATCH body type, so a new PATCH field fails typecheck until listed), topics, every readable toggle, and the two GraphQL keys.
  • actions: every endpoint read back onto its key through the routing table; a sub-endpoint the token cannot read is a note naming the key, never a section failure.
  • rulesets: repository-owned rulesets as keep entries; an inherited ruleset is a note, a ruleset whose bypass_actors the token cannot see is left out (kept undeclared) with a note, and two live rulesets under one name refuse the snapshot naming both.
  • Round trip proven twice: each snapshot value plans as a no-op against the state it was read from (zero drift, zero writes) in test/sections/snapshot-rows/, and each section's *-snapshot-roundtrip.yml scenario runs mode: snapshot end to end, then feeds the written file back to a check run against the same seeded state (snapshot_converges: true), which must read clean.
  • Documented exceptions: the round-trip exception table in docs/operate/snapshot.md gains the repository, actions, and rulesets rows.

Proof

  • Tests: 3 enrolment rows, 4 e2e snapshot scenarios (a round trip per section plus rulesets-snapshot-bypass-hidden, the read-grade token case that writes the empty keep wrapper; corpus pin 315 -> 323, since a snapshot scenario counts its settings and its expected snapshot), 13 section unit tests (denied sub-read notes, hidden bypass list, dropped server fields, unreadable GraphQL value, duplicate live names); bun run check green (3213 tests); bun run test:e2e 253/253; codex rubber-duck review converged over 6 rounds (two rebases onto feat(snapshot): branches and environments read back #147 and feat(snapshot): collaborators and teams read back #148 included).
Technical details

Projection per section

Section Read Kept Dropped
repository GET /repos the 27 documented PATCH fields plus has_discussions, with security_and_analysis narrowed to its PATCHable sub-keys; topics when any; 4 toggles; enable_sponsorships, issue_creation_policy name, null-valued fields, ids, urls, counts, owner, license, permissions, has_downloads, security_and_analysis.dependabot_security_updates (the automated-security-fixes toggle's state)
actions 10 GETs each body onto its slice of ActionsConfig selected_actions_url, maximum_allowed_days, any key the slice does not name
rulesets list + GET per owned ruleset name, target, enforcement, conditions.ref_name, rules, bypass_actors when present id, node_id, source, source_type, _links, timestamps, current_user_can_bypass

Documented round-trip exceptions (noted, not declared)

  • repository.enable_git_lfs: no read endpoint; noted on every snapshot.
  • repository.enable_immutable_releases under enforced_by_owner: reads back as true with a note that it cannot be changed from the repository.
  • All four toggle GETs answering 404: left out under one note, since a fine-grained token missing the Administration grant is answered the same way; any other answer (including private-vulnerability-reporting's 422) proves the grant and the 404s beside it read as off.
  • A repository toggle probe or the features query the token is denied: left out with the denial's own grant advice.
  • repository.issue_creation_policy outside the known vocabulary: left out with a note.
  • actions.<key> the token cannot read (the OIDC template without the Actions permission, the private-repo fork policy on a public repository): left out with a note; a cache limit is noted on its own (actions.cache.<key>) so the sibling limit survives.
  • actions.selected_actions: read only under allowed_actions: selected.
  • rulesets[<name>].bypass_actors absent from the GET (no write access): the whole ruleset is left out with a note, since an entry without the key would clear the bypass list on the next full-payload update; when that leaves no entry at all, the section emits the empty keep wrapper rather than reading as "nothing exists".
  • An organization or enterprise ruleset: noted, not declared.
  • Two repository-owned rulesets under one name: the snapshot fails naming both (rejectLiveDuplicates), since plan() upserts by name.

Recorded, not built

  • A ruleset GET without target would snapshot without it while normalizeRuleset defaults branch; no live GET omits it.

Out-of-territory edits this change needed: test/engine/snapshot.test.ts (supported-section pins), test/scripts/changed-sections.test.ts (fan-out canary for the three new snapshot-helpers.ts importers).

Reviewer note (Copilot): report a finding only with a concrete input and the wrong output it produces.

@Vivswan
Vivswan force-pushed the feat/snapshot-core-sections branch from 9beb462 to 6673eba Compare September 11, 2026 23:03
@github-actions

github-actions Bot commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

File size check

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

File Size Tier Cap
.github/scripts/release-pipeline.ts:337 168 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.

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

@Vivswan
Vivswan force-pushed the feat/snapshot-sections branch from 9a6d86b to 15b7cc5 Compare September 11, 2026 23:14
@Vivswan
Vivswan force-pushed the feat/snapshot-core-sections branch 4 times, most recently from e295b26 to f22528c Compare September 12, 2026 01:17
@Vivswan
Vivswan force-pushed the feat/snapshot-sections branch from 0ac317b to 2a92e5b Compare September 12, 2026 01:49
@Vivswan
Vivswan force-pushed the feat/snapshot-core-sections branch from f22528c to 854be94 Compare September 12, 2026 02:40
@Vivswan Vivswan closed this Sep 12, 2026
@Vivswan Vivswan reopened this Sep 12, 2026
@Vivswan
Vivswan force-pushed the feat/snapshot-core-sections branch from 854be94 to f404aa3 Compare September 12, 2026 03:02
@Vivswan
Vivswan force-pushed the feat/snapshot-sections branch from d8284c2 to c2332ba Compare September 12, 2026 03:37
@Vivswan
Vivswan force-pushed the feat/snapshot-core-sections branch from f404aa3 to 601ab1f Compare September 12, 2026 03:51
@Vivswan
Vivswan force-pushed the feat/snapshot-sections branch 2 times, most recently from fd5c230 to da2af61 Compare September 12, 2026 05:43
@Vivswan
Vivswan force-pushed the feat/snapshot-core-sections branch from 601ab1f to c6a2fcd Compare September 12, 2026 05:55
Base automatically changed from feat/snapshot-sections to main September 12, 2026 06:03
Copilot AI balanced review requested due to automatic review settings September 12, 2026 06:22
@Vivswan
Vivswan force-pushed the feat/snapshot-core-sections branch from c6a2fcd to 88d2126 Compare September 12, 2026 06:22

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

A denied cache-limit read currently discards another cache limit that was read successfully.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds snapshot read-back support for repository settings, Actions settings, and rulesets.

Changes:

  • Implements section-specific snapshot projections and permission-denial notes.
  • Adds round-trip, unit, and section-selection tests.
  • Updates generated coverage documentation.
File summaries
File Description
src/sections/repository/index.ts Implements repository snapshot projection.
src/sections/repository/repository.test.ts Tests repository snapshot behavior.
src/sections/repository/repository.docs.yml Documents repository snapshots.
src/sections/actions/index.ts Implements Actions snapshot routing.
src/sections/actions/actions.test.ts Tests Actions snapshot behavior.
src/sections/actions/actions.docs.yml Documents Actions snapshots.
src/sections/rulesets/index.ts Implements ruleset snapshots.
src/sections/rulesets/rulesets.test.ts Tests ruleset snapshot behavior.
src/sections/rulesets/rulesets.docs.yml Documents ruleset snapshots.
src/sections/shared/snapshot-helpers.ts Adjusts shared helper visibility documentation.
test/sections/snapshot-rows/repository.ts Adds repository round-trip fixture.
test/sections/snapshot-rows/actions.ts Adds Actions round-trip fixture.
test/sections/snapshot-rows/rulesets.ts Adds ruleset round-trip fixture.
test/engine/snapshot.test.ts Updates supported-section expectations.
test/scripts/changed-sections.test.ts Updates snapshot-helper fan-out expectations.
COVERAGE.md Records the new snapshot coverage.
Review details
  • Files reviewed: 16/16 changed files
  • Comments generated: 1
  • 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/actions/index.ts Resolved
Copilot AI review requested due to automatic review settings September 12, 2026 06:35
@Vivswan
Vivswan force-pushed the feat/snapshot-core-sections branch from 88d2126 to 4b4929d Compare September 12, 2026 06:35

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

Ambiguous toggle denials can produce destructive false values, and partial ruleset snapshots can erase hidden bypass actors.

Get a fresh assessment by requesting another Copilot review.

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

Comment thread src/sections/repository/index.ts Resolved
Comment thread src/sections/rulesets/index.ts Resolved
Copilot AI review requested due to automatic review settings September 12, 2026 07:09
@Vivswan
Vivswan force-pushed the feat/snapshot-core-sections branch from 4b4929d to 92f0eee Compare September 12, 2026 07:09

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

Ruleset omissions can produce a contradictory rendered message claiming that no rulesets exist.

Get a fresh assessment by requesting another Copilot review.

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

Comment thread src/sections/rulesets/index.ts Resolved
@Vivswan
Vivswan force-pushed the feat/snapshot-core-sections branch from 92f0eee to a8c58e0 Compare September 12, 2026 07:22
Copilot AI review requested due to automatic review settings September 12, 2026 07:22

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.

🟢 Approval recommended

The snapshot implementations handle unsafe or unreadable state conservatively and are supported by focused unit and round-trip tests.

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

Copilot AI review requested due to automatic review settings September 12, 2026 12:24
@Vivswan
Vivswan force-pushed the feat/snapshot-core-sections branch from a8c58e0 to 65e023b Compare September 12, 2026 12: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.

🟢 Approval recommended

The implementations preserve no-op round trips and cover permission, visibility, and projection edge cases comprehensively.

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

Copilot AI review requested due to automatic review settings September 12, 2026 12:30

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

Cache policy denials currently produce incorrect permission-remediation guidance.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/sections/actions/index.ts:336

  • An organization-managed cache limit is reported as a missing token grant. For example, when GET /actions/cache/retention-limit returns the documented policy 403, this emits "To fix, grant Administration write" even if that grant is already present and cannot override the policy. Add the org/enterprise-managed-policy caveat to both cache GET errors (for example via their endpoint denial hints) so the snapshot gives actionable advice.
  • Files reviewed: 21/21 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI review requested due to automatic review settings September 12, 2026 19:04

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 change spans three permission-sensitive GitHub API projections and requires final human validation.

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

Comment thread test/schema-corpus.test.ts Resolved
The three bespoke sections gain snapshot(): repository reads back the documented PATCH fields (an allowlist pinned to the octokit PATCH body type, since the passthrough slice cannot tell server fields apart; name and null fields are left out), topics, every readable toggle, and the two GraphQL keys, noting Git LFS, an owner-enforced toggle, and any denied sub-read; actions reads every endpoint back onto its key through the routing table (each routed destination now carries its own snapshot), with a denied sub-read a note naming the key and the allowlist read only under the selected policy; rulesets reads repository-owned rulesets back as keep entries, noting an inherited ruleset and an invisible bypass_actors instead of claiming an empty list.

The round-trip harness fake now dispatches GraphQL onto the mock's GraphQL handlers, which the repository row (and the GraphQL-reading sections still to come) needs; the engine test's supported-section pins and the changed-sections fan-out canary follow the new snapshot() declarations and import edges.
… tests, and table the repository snapshot rules
…out of the exception table, and order the allowlist outcomes by registry
Copilot AI review requested due to automatic review settings September 12, 2026 19:39
@Vivswan
Vivswan force-pushed the feat/snapshot-core-sections branch from 5a81eef to d5c7d7f Compare September 12, 2026 19:39

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

Enabled automated security fixes return a 204/null response that the new snapshot path currently rejects.

Get a fresh assessment by requesting another Copilot review.

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

Comment thread src/sections/repository/index.ts Resolved
@Vivswan
Vivswan marked this pull request as ready for review September 12, 2026 19:46
@Vivswan
Vivswan merged commit 3d256b8 into main Sep 12, 2026
39 checks passed
@Vivswan
Vivswan deleted the feat/snapshot-core-sections branch September 12, 2026 19:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants