Skip to content

feat(snapshot): collaborators and teams read back - #148

Merged
Vivswan merged 1 commit into
mainfrom
feat/snapshot-collaborators-teams
Sep 12, 2026
Merged

Vivswan merged 1 commit into
mainfrom
feat/snapshot-collaborators-teams

Conversation

@Vivswan

@Vivswan Vivswan commented Sep 11, 2026 •

Copy link
Copy Markdown
Owner
collaborators:
  _undeclared: delete
  entries:
    - username: alice
      permission: push
    - username: carol
      permission: security-team
    - username: bob            # a pending invitation
      permission: pull
teams:
  - name: platform
    permission: push
  - name: auditors
    permission: security-auditor
  • Both membership sections declare snapshot(ctx); every emitted value plans as a no-op against the state it was read from (enrolment rows in test/sections/snapshot-roundtrip.test.ts, and the *-snapshot-roundtrip scenarios feed each written file back to a check run against the same mock state).
  • Collaborators reads the direct collaborators and the pending username invitations back under the delete default. The owner, email invitations, and expired invitations are noted, not declared.
  • Teams lists the teams with access through a new endpoint and reads each team's role through the same probe plan() compares against, so a custom repository role reads back by name.
  • The permission lens is the inverse of roleForPermission. A role no declaration plans as (a custom role literally named push or pull) fails the collaborators snapshot loudly and is omitted with a note in teams.
  • The exceptions table on the snapshot page (docs/operate/snapshot.md) gains one row per exception below.
Technical details

New endpoint

Section Role Route Called by Reached by
teams list GET /repos/{owner}/{repo}/teams snapshot() only teams-snapshot-roundtrip

The mock serves it from the seeded teams state; the trimmed OpenAPI spec picks the path up through USED_PATHS.

Scenarios (corpus pin 305 -> 311: each snapshot scenario is two corpus documents, its settings and its pinned expect.snapshot)

Scenario Proves
collaborators-snapshot-roundtrip owner, write, custom role, pending read invitation, email invitation; the written file reads clean, the owner and email notes are pinned
collaborators-snapshot-expired-invitation an expired invitation is omitted with its note; no convergence claim, since the delete default would cancel it
teams-snapshot-roundtrip write, custom role, read, and a custom role named push (omitted, note pinned); the written file reads clean

Permission lens (permissionForRole)

Live role (role_name / invitation permissions) Declared permission Plans as
write push write
read pull read
admin, maintain, triage same same
custom role name, e.g. security-team same same
push, pull (custom roles named like the declared vocabulary) none: collaborators throws, teams omits with a note would plan write / read

Round-trip exceptions, each a note on the snapshot

Live state Snapshot plan() on the snapshot
The repository owner listed as a direct collaborator omitted no-op (never removed)
An email invitation (null invitee) omitted note only
An expired invitation omitted cancels it under the delete default (declared, it would be cancelled and re-sent); when nothing else is declared the section is omitted and apply leaves it
Team access with access_source other than direct omitted no-op (undeclared teams are never removed)
A team the listing names but the probe finds no access or no role for omitted no-op

Mechanical pins outside the two sections, required by the new endpoint and the new snapshot-helpers import: the endpoint key list in test/sections/registry.test.ts, the UNSUPPORTED list in test/engine/snapshot.test.ts, the fan-out layout in test/scripts/changed-sections.test.ts, and the corpus size in test/schema-corpus.test.ts.

Recorded for follow-up, outside this PR's files: the e2e mock's HandlerContext exposes no request headers, so the team probe handler cannot answer GitHub's bare 204 when the repository media type is missing; dropping the Accept header from probeTeamRole would pass every test today. The header predates this branch.

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

@github-actions

github-actions Bot commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

File size check

1 over a hard cap (fails), 5 warning(s).

File Size Tier Cap
test/scripts/check-compat-markers.test.ts:55 336 chars hard 256
.github/scripts/release-pipeline.ts:333 168 chars warn 150
.github/workflows/post-green.yml:78 19 comment lines warn 10
src/flows/snapshot.ts:129 11 comment lines warn 10
test/docs/guides.test.ts:468 155 chars warn 150
test/e2e/schema.ts:132 11 comment lines warn 10

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-collaborators-teams branch 5 times, most recently from 88f3cad to cede6d2 Compare September 12, 2026 00:58
@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-collaborators-teams branch from cede6d2 to dba9f4c Compare September 12, 2026 02:29
@Vivswan Vivswan closed this Sep 12, 2026
@Vivswan Vivswan reopened this Sep 12, 2026
@Vivswan
Vivswan force-pushed the feat/snapshot-collaborators-teams branch from dba9f4c to 8a9e92c 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-collaborators-teams branch from 8a9e92c to c482be2 Compare September 12, 2026 03:49
@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-collaborators-teams branch from c482be2 to 3bc5b31 Compare September 12, 2026 05:54
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-collaborators-teams branch from 3bc5b31 to 930219c 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

Expired invitations break snapshot round trips, and the new team endpoint lacks required e2e coverage.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds snapshot read-back support for collaborators and repository teams.

Changes:

  • Maps live roles into declarative permissions.
  • Adds team listing, mocks, and snapshot tests.
  • Updates endpoint registration and generated documentation.
File summaries
File Description
src/sections/collaborators/index.ts Implements collaborator snapshot logic.
src/sections/collaborators/collaborators.test.ts Tests collaborator snapshots.
src/sections/collaborators/collaborators.docs.yml Documents snapshot behavior.
src/sections/teams/index.ts Implements team snapshot logic and listing.
src/sections/teams/mock.ts Mocks repository team listings.
src/sections/teams/schema.ts Exports the team configuration type.
src/sections/teams/teams.test.ts Tests team snapshots and read ports.
src/sections/teams/teams.docs.yml Documents team snapshot coverage.
test/sections/snapshot-rows/collaborators.ts Adds collaborator round-trip enrollment.
test/sections/snapshot-rows/teams.ts Adds team round-trip enrollment.
test/sections/registry.test.ts Registers the team list endpoint expectation.
test/engine/snapshot.test.ts Marks both sections as snapshot-supported.
test/scripts/changed-sections.test.ts Updates snapshot-helper fan-out expectations.
docs/reference/sections.md Updates generated section documentation.
COVERAGE.md Describes the new snapshot coverage.
Review details
  • Files reviewed: 15/15 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/collaborators/index.ts Resolved
Comment thread src/sections/teams/index.ts Resolved
Copilot AI review requested due to automatic review settings September 12, 2026 12:23
@Vivswan
Vivswan force-pushed the feat/snapshot-collaborators-teams branch from 930219c to f3fba8e Compare September 12, 2026 12:23

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, endpoint registration, tests, scenarios, and documentation consistently cover the stated snapshot behavior and recorded exceptions.

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

Both membership sections declare snapshot(). Collaborators reads the direct collaborators and the pending username invitations back as entries under the delete default; teams lists the teams with access through a new GET /repos/{owner}/{repo}/teams endpoint and reads each team's role through the same probe plan() compares against, so a custom repository role reads back by name.

Both read roles through the shared permissionForRole lens. Where it answers undefined (a custom role named "push" or "pull", which the settings vocabulary reads as write or read), collaborators fails the snapshot loudly, since dropping the entry would plan a removal, and teams omits the team with a note, since the section never removes an undeclared team. The enrolment rows prove plan() converges on every emitted value.

Not declared, each with a note: the repository owner (plan() never manages them), email invitations (plan() leaves them alone), expired invitations (a declared one would be cancelled and re-sent), team access granted at the organization or enterprise level (declaring it would grant direct access), and a team whose role the probe cannot read.

The e2e mock serves the team listing from the seeded team access; the trimmed OpenAPI spec picks the path up through USED_PATHS. The round-trip scenarios feed each written snapshot back to a check run against the same state (collaborators-snapshot-roundtrip, teams-snapshot-roundtrip), and collaborators-snapshot-expired-invitation pins the one exception that cannot read clean. The snapshot page's exception table names each.
Copilot AI review requested due to automatic review settings September 12, 2026 12:43
@Vivswan
Vivswan force-pushed the feat/snapshot-collaborators-teams branch from f3fba8e to 0efa74b Compare September 12, 2026 12:43

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 implementation is consistent with planning behavior and includes focused unit, round-trip, e2e, mock, and documentation updates.

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

@Vivswan
Vivswan marked this pull request as ready for review September 12, 2026 18:54
@Vivswan
Vivswan merged commit 3b4f753 into main Sep 12, 2026
39 checks passed
@Vivswan
Vivswan deleted the feat/snapshot-collaborators-teams branch September 12, 2026 18:54
Vivswan added a commit that referenced this pull request Sep 12, 2026
…out of the exception table, and order the allowlist outcomes by registry
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