From dc460f0945e95b96605b9aa148d7c6ed0875a3df Mon Sep 17 00:00:00 2001 From: Marcus Burghardt Date: Tue, 12 May 2026 12:33:48 +0200 Subject: [PATCH 1/5] docs: add change spec for team restructuring and CODEOWNERS cleanup Define proposal, design, specs, and tasks for restructuring peribolos teams to reflect the provider split from complyctl to complytime-providers, creating per-provider approval teams, establishing Gemara content ownership, and standardizing CODEOWNERS across repositories. Assisted-by: OpenCode (claude-opus-4-6) Signed-off-by: Marcus Burghardt --- .../.openspec.yaml | 2 + .../design.md | 133 ++++++++++++++++++ .../proposal.md | 62 ++++++++ .../specs/codeowners-management/spec.md | 101 +++++++++++++ .../specs/team-restructuring/spec.md | 130 +++++++++++++++++ .../specs/test-validation/spec.md | 80 +++++++++++ .../restructure-teams-and-codeowners/tasks.md | 39 +++++ 7 files changed, 547 insertions(+) create mode 100644 openspec/changes/restructure-teams-and-codeowners/.openspec.yaml create mode 100644 openspec/changes/restructure-teams-and-codeowners/design.md create mode 100644 openspec/changes/restructure-teams-and-codeowners/proposal.md create mode 100644 openspec/changes/restructure-teams-and-codeowners/specs/codeowners-management/spec.md create mode 100644 openspec/changes/restructure-teams-and-codeowners/specs/team-restructuring/spec.md create mode 100644 openspec/changes/restructure-teams-and-codeowners/specs/test-validation/spec.md create mode 100644 openspec/changes/restructure-teams-and-codeowners/tasks.md diff --git a/openspec/changes/restructure-teams-and-codeowners/.openspec.yaml b/openspec/changes/restructure-teams-and-codeowners/.openspec.yaml new file mode 100644 index 0000000..40cc12f --- /dev/null +++ b/openspec/changes/restructure-teams-and-codeowners/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-12 diff --git a/openspec/changes/restructure-teams-and-codeowners/design.md b/openspec/changes/restructure-teams-and-codeowners/design.md new file mode 100644 index 0000000..4427a5d --- /dev/null +++ b/openspec/changes/restructure-teams-and-codeowners/design.md @@ -0,0 +1,133 @@ +## Context + +The complytime GitHub organization manages 12 repositories with peribolos +(declarative GitHub org management via YAML). The organization recently split +content from `complyctl` by moving the openscap-plugin to `complytime-providers` +(now called "providers"). Two providers exist today (openscap, ampel), a third +(opa) is expected. Additionally, `complytime-policies` needs dedicated ownership +for Gemara compliance content. + +Current state: +- `openscap-plugin-approvers` team still points at `complyctl` with stale naming +- No per-provider teams exist for ampel or opa +- No CODEOWNERS in complytime-providers or complytime-policies +- complyctl CODEOWNERS references `/cmd/openscap-plugin/` which no longer exists +- CODEOWNERS file locations are inconsistent (root vs `.github/`) +- `config_test.go` only validates individual users in CODEOWNERS, not team refs + +## Goals / Non-Goals + +**Goals:** +- Reflect the provider split in peribolos team structure +- Enable per-provider code review gates via CODEOWNERS +- Establish Gemara content ownership in complytime-policies +- Repurpose complytime-approvers for non-code repo stakeholder access +- Standardize CODEOWNERS location to `.github/CODEOWNERS` across all repos +- Update config_test.go to validate team references in CODEOWNERS +- Document why `privacy: closed` is required for all teams + +**Non-Goals:** +- Reducing member duplication across teams via YAML anchors or nested teams + (explored and deferred; explicit lists kept for clarity) +- Changing complytime-dev membership or its broad write-access model +- Modifying repository settings beyond team access and CODEOWNERS +- Automating CODEOWNERS generation from peribolos config + +## Decisions + +### 1. Team naming convention: `*-provider-approvers` + +Rename `openscap-plugin-approvers` to `openscap-provider-approvers` and follow +the same pattern for new teams: `ampel-provider-approvers`, +`opa-provider-approvers`. This reflects the terminology shift from "plugins" to +"providers." + +**Alternative considered**: Generic `*-approvers` naming. Rejected because the +`-provider-` infix makes it clear these teams scope to the complytime-providers +repository specifically. + +### 2. All complytime-dev members in every provider team + +Every provider team includes all complytime-dev members (maintainers as team +maintainers, members as team members). Provider-specific contributors are added +on top (e.g., `fortiz-ai` for opa). This ensures the core dev team can always +review any provider code. + +**Alternative considered**: Nested teams where provider teams inherit +complytime-dev membership. Rejected because GitHub CODEOWNERS only resolves +direct team members, not parent-team members. Child teams inherit repo +permissions but not CODEOWNERS review eligibility. + +### 3. CODEOWNERS standardized to `.github/CODEOWNERS` + +GitHub searches for CODEOWNERS in `.github/`, root, then `docs/`, using the +first found. The `.github/` location is recommended by GitHub documentation as +the most secure option, particularly for protecting the CODEOWNERS file itself. + +**Reference**: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +### 4. `privacy: closed` for all teams (requirement, not preference) + +All teams MUST use `privacy: closed`. GitHub CODEOWNERS requires teams to be +"visible" to be referenced. In GitHub's team privacy model, `closed` means +visible to all organization members, while `secret` teams cannot be referenced +in CODEOWNERS files. + +**References**: +- CODEOWNERS visibility requirement: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners +- Team privacy values: https://docs.github.com/en/rest/teams/teams#create-a-team + +### 5. complytime-approvers repurposed for non-code repos + +Rather than deleting `complytime-approvers` and creating a new team, repurpose +it with updated membership and repo access. This team grants write access to +non-code repositories: `.github`, `community`, `complytime-demos`, and +`website`. Note that `complytime-dev` does NOT have write access to `.github`; +only `complytime-approvers` provides write access to that repository. + +Membership: jflowers, jpower432, marcusburghardt (maintainers), +beatrizmcouto, hbraswelrh (members). + +### 6. config_test.go validation strategy + +Split CODEOWNERS owner parsing into individual users and team references +(detected by presence of `/` in the owner string). Individual users are +validated as org admins (existing behavior). Team references are validated +against peribolos.yaml team definitions. + +**Alternative considered**: Skipping team references entirely in validation. +Rejected because this would allow typos or references to non-existent teams. + +### 7. complytime-providers CODEOWNERS uses provider teams only (not dev + provider) + +Each provider path references only its provider team, not `complytime-dev`: +``` +/cmd/openscap-provider/ @complytime/openscap-provider-approvers +``` + +Since all complytime-dev members are already in each provider team, adding +`@complytime/complytime-dev` would be redundant. The `*` fallback to +`@complytime/complytime-dev` covers shared code and any paths not matching a +provider-specific rule. + +## Risks / Trade-offs + +**[Member list duplication]** Provider teams duplicate complytime-dev members +explicitly. Adding/removing a dev requires updating multiple teams. +-> Mitigation: Accepted trade-off. The teams are defined in a single file +(peribolos.yaml) and validated by tests. YAML anchors or nested teams were +explored and deferred for simplicity. + +**[Cross-repo coordination]** Changes span 4 repositories. CODEOWNERS changes +in complyctl, complytime-providers, and complytime-policies depend on the +teams existing first (via peribolos apply). +-> Mitigation: Apply peribolos.yaml changes first (teams must exist before +CODEOWNERS references them). CODEOWNERS updates in other repos follow. + +**[Provider team divergence from CODEOWNERS]** If the last-matching-pattern +rule in CODEOWNERS selects only a provider team and a future member is removed +from that team but stays in complytime-dev, they lose review access for that +provider. +-> Mitigation: This is the intended behavior. Provider teams are the authority +for provider-specific code review. The `*` fallback ensures complytime-dev +reviews shared/non-provider code. diff --git a/openspec/changes/restructure-teams-and-codeowners/proposal.md b/openspec/changes/restructure-teams-and-codeowners/proposal.md new file mode 100644 index 0000000..1bc69c6 --- /dev/null +++ b/openspec/changes/restructure-teams-and-codeowners/proposal.md @@ -0,0 +1,62 @@ +## Why + +The complytime organization has undergone structural changes: the openscap-plugin +was moved from complyctl to complytime-providers (now called "providers"), and +complytime-providers now hosts multiple providers (openscap, ampel) with a third +(opa) expected. Additionally, complytime-policies needs dedicated ownership for +Gemara content. The current peribolos team structure and CODEOWNERS files do not +reflect these changes, leaving stale references, missing ownership rules, and +no per-provider review gates. + +## What Changes + +- **Rename** `openscap-plugin-approvers` to `openscap-provider-approvers`, expand + membership to all complytime-dev members, and point repo access at + `complytime-providers` instead of `complyctl`. +- **Create** `ampel-provider-approvers` and `opa-provider-approvers` teams, each + with all complytime-dev members as approvers (opa additionally includes + `fortiz-ai`). +- **Create** `complytime-policies-approvers` team for Gemara content ownership in + complytime-policies, with `fortiz-ai` as initial member. +- **Repurpose** `complytime-approvers` for write access to non-code repositories + (.github, community, complytime-demos, website) for project stakeholders. +- **Standardize** all CODEOWNERS files to `.github/CODEOWNERS` across repositories, + following GitHub's recommended location. +- **Create** CODEOWNERS for complytime-providers with per-provider path rules. +- **Create** CODEOWNERS for complytime-policies with combined team ownership. +- **Clean up** complyctl CODEOWNERS by removing stale openscap-plugin references + and simplifying to a single complytime-dev fallback. +- **Move** this repo's CODEOWNERS from root to `.github/CODEOWNERS` and add + `@complytime/complytime-approvers` as a code owner. +- **Update** `config_test.go` to handle team references in CODEOWNERS and adjust + the file path for the new CODEOWNERS location. + +## Capabilities + +### New Capabilities + +- `team-restructuring`: Peribolos team definitions reflecting the new + organizational structure (rename, create, and repurpose teams). +- `codeowners-management`: CODEOWNERS file creation, cleanup, and standardization + across complytime-providers, complytime-policies, complyctl, and .github repos. +- `test-validation`: Updated config_test.go to validate team references in + CODEOWNERS and support the new `.github/CODEOWNERS` location. + +### Modified Capabilities + +(none -- no existing specs to modify) + +## Impact + +- **peribolos.yaml**: Team definitions restructured (1 rename, 3 creates, + 1 repurpose). Repo access mappings change for multiple teams. +- **config_test.go**: Test logic updated to split CODEOWNERS parsing into + individual users and team references, with validation that referenced teams + exist in peribolos.yaml. +- **CODEOWNERS (this repo)**: Moved from root to `.github/`, team reference added. +- **CODEOWNERS (complyctl)**: Stale rules removed, simplified to single fallback. +- **CODEOWNERS (complytime-providers)**: New file with per-provider ownership. +- **CODEOWNERS (complytime-policies)**: New file with combined team ownership. +- **Cross-repo**: Changes span 4 repositories (.github, complyctl, + complytime-providers, complytime-policies). Each repo's changes are independent + but should be coordinated. diff --git a/openspec/changes/restructure-teams-and-codeowners/specs/codeowners-management/spec.md b/openspec/changes/restructure-teams-and-codeowners/specs/codeowners-management/spec.md new file mode 100644 index 0000000..7cd74e1 --- /dev/null +++ b/openspec/changes/restructure-teams-and-codeowners/specs/codeowners-management/spec.md @@ -0,0 +1,101 @@ +## ADDED Requirements + +### Requirement: CODEOWNERS standardized to .github directory + +All CODEOWNERS files across the complytime organization SHALL be located at +`.github/CODEOWNERS` within each repository. This follows GitHub's recommended +location and search priority order (`.github/`, root, `docs/`). + +Reference: +https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +#### Scenario: This repo CODEOWNERS moved from root to .github + +- **WHEN** the CODEOWNERS file is created at `.github/CODEOWNERS` in the + `.github` repository +- **THEN** the root `CODEOWNERS` file SHALL be deleted and all ownership + rules SHALL be defined in `.github/CODEOWNERS` + +### Requirement: .github repo CODEOWNERS includes complytime-approvers team + +The `.github` repository CODEOWNERS SHALL include both individual admin +users and the `@complytime/complytime-approvers` team as code owners for +all files. The format SHALL be: + +``` +* @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers +``` + +#### Scenario: Team and individual owners on same line + +- **WHEN** a pull request is opened in the `.github` repository +- **THEN** review is requested from the individual admins and any member + of the `complytime-approvers` team, and approval from any one of them + satisfies the CODEOWNERS requirement + +### Requirement: complyctl CODEOWNERS cleaned up + +The complyctl repository CODEOWNERS SHALL be simplified to a single fallback +rule assigning `@complytime/complytime-dev` as the owner for all files. The +stale `/cmd/openscap-plugin/` rule and the `/cmd/complyctl/` specific rule +SHALL be removed. + +#### Scenario: Stale openscap-plugin rule removed + +- **WHEN** the complyctl CODEOWNERS is updated +- **THEN** there is no rule referencing `/cmd/openscap-plugin/` or + `@complytime/openscap-plugin-approvers` + +#### Scenario: Single fallback rule + +- **WHEN** a pull request is opened in complyctl modifying any file +- **THEN** review is requested from `@complytime/complytime-dev` + +### Requirement: complytime-providers CODEOWNERS created with per-provider rules + +The complytime-providers repository SHALL have a CODEOWNERS file at +`.github/CODEOWNERS` with a fallback rule for `@complytime/complytime-dev` +and per-provider path rules for each provider directory under `cmd/`. + +The file SHALL contain: +``` +* @complytime/complytime-dev +/cmd/openscap-provider/ @complytime/openscap-provider-approvers +/cmd/ampel-provider/ @complytime/ampel-provider-approvers +/cmd/opa-provider/ @complytime/opa-provider-approvers +``` + +#### Scenario: Provider-specific change triggers provider team review + +- **WHEN** a pull request modifies files only under `/cmd/openscap-provider/` +- **THEN** review is requested from `@complytime/openscap-provider-approvers` + only (last matching pattern takes precedence over the `*` fallback) + +#### Scenario: Shared code change triggers dev team review + +- **WHEN** a pull request modifies files under `/internal/` or root-level files +- **THEN** review is requested from `@complytime/complytime-dev` + +#### Scenario: Cross-provider change triggers multiple teams + +- **WHEN** a pull request modifies files in both `/cmd/openscap-provider/` + and `/cmd/ampel-provider/` +- **THEN** review is requested from both `@complytime/openscap-provider-approvers` + and `@complytime/ampel-provider-approvers` + +### Requirement: complytime-policies CODEOWNERS created + +The complytime-policies repository SHALL have a CODEOWNERS file at +`.github/CODEOWNERS` with a single fallback rule assigning both +`@complytime/complytime-policies-approvers` and `@complytime/complytime-dev` +as code owners for all files. + +``` +* @complytime/complytime-policies-approvers @complytime/complytime-dev +``` + +#### Scenario: Both teams requested for review + +- **WHEN** a pull request is opened in complytime-policies modifying any file +- **THEN** review is requested from both `@complytime/complytime-policies-approvers` + and `@complytime/complytime-dev` diff --git a/openspec/changes/restructure-teams-and-codeowners/specs/team-restructuring/spec.md b/openspec/changes/restructure-teams-and-codeowners/specs/team-restructuring/spec.md new file mode 100644 index 0000000..8f10e72 --- /dev/null +++ b/openspec/changes/restructure-teams-and-codeowners/specs/team-restructuring/spec.md @@ -0,0 +1,130 @@ +## ADDED Requirements + +### Requirement: Rename openscap-plugin-approvers to openscap-provider-approvers + +The peribolos configuration SHALL rename the team `openscap-plugin-approvers` to +`openscap-provider-approvers`. The team description SHALL reference +"openscap-provider in complytime-providers". Maintainers SHALL be `jpower432` +and `marcusburghardt`. Members SHALL be `gvauter`, `hbraswelrh`, `sonupreetam`, +and `trevor-vaughan`. The team SHALL have write access to `complytime-providers` +and SHALL NOT have access to `complyctl`. + +#### Scenario: Team renamed and repo access updated + +- **WHEN** peribolos applies the configuration +- **THEN** the team `openscap-plugin-approvers` is renamed to + `openscap-provider-approvers` with write access to `complytime-providers` + and no access to `complyctl` + +### Requirement: Create ampel-provider-approvers team + +The peribolos configuration SHALL define a team `ampel-provider-approvers` with +`privacy: closed`. Maintainers SHALL be `jpower432` and `marcusburghardt`. +Members SHALL be `gvauter`, `hbraswelrh`, `sonupreetam`, and `trevor-vaughan`. +The team SHALL have write access to `complytime-providers`. + +#### Scenario: Team created with correct membership + +- **WHEN** peribolos applies the configuration +- **THEN** the team `ampel-provider-approvers` exists with `privacy: closed`, + the specified maintainers and members, and write access to + `complytime-providers` + +### Requirement: Create opa-provider-approvers team + +The peribolos configuration SHALL define a team `opa-provider-approvers` with +`privacy: closed`. Maintainers SHALL be `jpower432` and `marcusburghardt`. +Members SHALL be `fortiz-ai`, `gvauter`, `hbraswelrh`, `sonupreetam`, and +`trevor-vaughan`. The team SHALL have write access to `complytime-providers`. + +#### Scenario: Team created with provider-specific member + +- **WHEN** peribolos applies the configuration +- **THEN** the team `opa-provider-approvers` exists with `fortiz-ai` as a + member in addition to all complytime-dev members + +### Requirement: Create complytime-policies-approvers team + +The peribolos configuration SHALL define a team `complytime-policies-approvers` +with `privacy: closed`. Maintainers SHALL be `jflowers`, `jpower432`, and +`marcusburghardt`. Members SHALL include `fortiz-ai`. The team SHALL have write +access to `complytime-policies`. + +#### Scenario: Team created for Gemara content ownership + +- **WHEN** peribolos applies the configuration +- **THEN** the team `complytime-policies-approvers` exists with `privacy: closed`, + the specified maintainers, `fortiz-ai` as a member, and write access to + `complytime-policies` + +### Requirement: Repurpose complytime-approvers team + +The peribolos configuration SHALL update `complytime-approvers` with +description "Write access to non-code repos for project stakeholders". +Maintainers SHALL be `jflowers`, `jpower432`, and `marcusburghardt`. Members +SHALL be `beatrizmcouto` and `hbraswelrh`. The team SHALL have write access to +`.github`, `community`, `complytime-demos`, and `website`. The team SHALL NOT +have write access to `complyctl` or `complytime`. + +#### Scenario: Team repurposed with updated membership and repos + +- **WHEN** peribolos applies the configuration +- **THEN** the team `complytime-approvers` has the updated description, + maintainers (jflowers, jpower432, marcusburghardt), members (beatrizmcouto, + hbraswelrh), and write access only to `.github`, `community`, + `complytime-demos`, and `website` + +#### Scenario: Previous members removed + +- **WHEN** peribolos applies the configuration +- **THEN** `gvauter`, `sonupreetam`, and `trevor-vaughan` are no longer + members of `complytime-approvers` + +### Requirement: All teams use privacy closed + +All teams in peribolos.yaml SHALL use `privacy: closed`. This is required +because CODEOWNERS team references require teams to be visible to all +organization members. In GitHub's team privacy model, `closed` means visible +to all organization members, while `secret` teams are only visible to team +members and organization owners and cannot be referenced in CODEOWNERS files. + +References: +- CODEOWNERS visibility requirement: + https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners +- Team privacy values: + https://docs.github.com/en/rest/teams/teams#create-a-team + +#### Scenario: Secret team rejected by validation + +- **WHEN** a team in peribolos.yaml uses `privacy: secret` +- **THEN** the config_test.go validation fails with an error indicating + the team does not have the `privacy: closed` field + +### Requirement: Team maintainers must be org admins + +All team maintainers in peribolos.yaml SHALL be organization admins. Non-admin +users SHALL be listed as team members, not maintainers. Organization admins +listed in a team SHALL be listed as maintainers, not members. + +#### Scenario: Non-admin listed as maintainer + +- **WHEN** a non-admin user is listed as a team maintainer in peribolos.yaml +- **THEN** the config_test.go validation fails with an error indicating + the user should be in the members list + +#### Scenario: Admin listed as member + +- **WHEN** an org admin is listed as a team member in peribolos.yaml +- **THEN** the config_test.go validation fails with an error indicating + the user should be in the maintainers list + +### Requirement: Team member and maintainer lists must be sorted + +All maintainer and member lists in peribolos.yaml team definitions SHALL be +sorted alphabetically. + +#### Scenario: Unsorted member list + +- **WHEN** a team has an unsorted member list in peribolos.yaml +- **THEN** the config_test.go validation fails with an error indicating + the list is unsorted diff --git a/openspec/changes/restructure-teams-and-codeowners/specs/test-validation/spec.md b/openspec/changes/restructure-teams-and-codeowners/specs/test-validation/spec.md new file mode 100644 index 0000000..91a4f25 --- /dev/null +++ b/openspec/changes/restructure-teams-and-codeowners/specs/test-validation/spec.md @@ -0,0 +1,80 @@ +## ADDED Requirements + +### Requirement: CODEOWNERS path updated for .github location + +The config_test.go `--owners-dir` flag default SHALL be updated from `"../"` +to `"../.github"` to reflect the new standardized CODEOWNERS location at +`.github/CODEOWNERS`. The `loadOwners` function SHALL read CODEOWNERS from +the `.github/` directory. + +#### Scenario: Test reads CODEOWNERS from .github directory + +- **WHEN** config_test.go runs with default flags +- **THEN** the test reads the CODEOWNERS file from `../.github/CODEOWNERS` + instead of `../CODEOWNERS` + +### Requirement: CODEOWNERS parsing separates users from teams + +The `loadOwners` function SHALL separate CODEOWNERS entries into individual +users and team references. A team reference is identified by the presence of +a `/` character in the owner string (e.g., `@complytime/complytime-approvers`). +Individual users do not contain a `/`. + +#### Scenario: Mixed individual and team owners parsed + +- **WHEN** the CODEOWNERS file contains + `* @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers` +- **THEN** individual users are `["jflowers", "jpower432", "marcusburghardt"]` + and team references are `["complytime/complytime-approvers"]` + +### Requirement: Individual CODEOWNERS users validated as org admins + +The test SHALL validate that all individual users listed in CODEOWNERS are +org admins. This preserves the existing validation behavior. The minimum of +3 individual approvers requirement SHALL be maintained. + +#### Scenario: Non-admin individual in CODEOWNERS + +- **WHEN** the CODEOWNERS file lists an individual user who is not an org admin +- **THEN** the test fails with an error indicating the user does not match + org admins + +#### Scenario: Fewer than 3 individual approvers + +- **WHEN** the CODEOWNERS file lists fewer than 3 individual users +- **THEN** the test fails with an error indicating insufficient approvers + +### Requirement: Team references validated against peribolos config + +The test SHALL validate that all team references in CODEOWNERS correspond to +teams defined in peribolos.yaml. The team name extracted from the CODEOWNERS +entry (the part after the `/`, e.g., `complytime-approvers` from +`@complytime/complytime-approvers`) MUST exist as a key in the org's teams map. + +#### Scenario: Valid team reference + +- **WHEN** the CODEOWNERS file references `@complytime/complytime-approvers` + and the team `complytime-approvers` exists in peribolos.yaml +- **THEN** the test passes the team reference validation + +#### Scenario: Invalid team reference + +- **WHEN** the CODEOWNERS file references `@complytime/nonexistent-team` + and no team `nonexistent-team` exists in peribolos.yaml +- **THEN** the test fails with an error indicating the team does not exist + in the org configuration + +### Requirement: No duplicate owners in CODEOWNERS + +The test SHALL validate that there are no duplicate entries in the CODEOWNERS +file, checking both individual users and team references. + +#### Scenario: Duplicate individual user + +- **WHEN** the CODEOWNERS file lists the same user twice +- **THEN** the test fails with a duplicate approvers error + +#### Scenario: Duplicate team reference + +- **WHEN** the CODEOWNERS file lists the same team twice +- **THEN** the test fails with a duplicate teams error diff --git a/openspec/changes/restructure-teams-and-codeowners/tasks.md b/openspec/changes/restructure-teams-and-codeowners/tasks.md new file mode 100644 index 0000000..4aceaff --- /dev/null +++ b/openspec/changes/restructure-teams-and-codeowners/tasks.md @@ -0,0 +1,39 @@ +## 1. Peribolos Team Definitions (this repo) + +- [ ] 1.1 Rename `openscap-plugin-approvers` to `openscap-provider-approvers` in peribolos.yaml: update team name, description, expand maintainers to include `jpower432`, expand members to all complytime-dev members, change repo access from `complyctl: write` to `complytime-providers: write` +- [ ] 1.2 Create `ampel-provider-approvers` team in peribolos.yaml with `privacy: closed`, maintainers `jpower432` and `marcusburghardt`, members `gvauter`, `hbraswelrh`, `sonupreetam`, `trevor-vaughan`, and `complytime-providers: write` +- [ ] 1.3 Create `opa-provider-approvers` team in peribolos.yaml with `privacy: closed`, maintainers `jpower432` and `marcusburghardt`, members `fortiz-ai`, `gvauter`, `hbraswelrh`, `sonupreetam`, `trevor-vaughan`, and `complytime-providers: write` +- [ ] 1.4 Create `complytime-policies-approvers` team in peribolos.yaml with `privacy: closed`, maintainers `jflowers`, `jpower432`, `marcusburghardt`, member `fortiz-ai`, and `complytime-policies: write` +- [ ] 1.5 Repurpose `complytime-approvers` in peribolos.yaml: update description to "Write access to non-code repos for project stakeholders", set maintainers to `jflowers`, `jpower432`, `marcusburghardt`, set members to `beatrizmcouto`, `hbraswelrh`, set repos to `.github`, `community`, `complytime-demos`, `website` (all write) + +## 2. CODEOWNERS for This Repo (.github) + +- [ ] 2.1 Create `.github/CODEOWNERS` with content: `* @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers` +- [ ] 2.2 Delete the root `CODEOWNERS` file + +## 3. CODEOWNERS for complyctl + +- [ ] 3.1 Update `complyctl/.github/CODEOWNERS` to a single rule: `* @complytime/complytime-dev` (remove stale `/cmd/openscap-plugin/` rule and `/cmd/complyctl/` specific rule) + +## 4. CODEOWNERS for complytime-providers + +- [ ] 4.1 Create `complytime-providers/.github/CODEOWNERS` with fallback `* @complytime/complytime-dev` and per-provider rules for `/cmd/openscap-provider/`, `/cmd/ampel-provider/`, `/cmd/opa-provider/` + +## 5. CODEOWNERS for complytime-policies + +- [ ] 5.1 Create `complytime-policies/.github/CODEOWNERS` with rule: `* @complytime/complytime-policies-approvers @complytime/complytime-dev` + +## 6. Test Validation Updates (this repo) + +- [ ] 6.1 Update `config_test.go` `--owners-dir` flag default from `"../"` to `"../.github"` +- [ ] 6.2 Update `loadOwners` function to return separate lists for individual users and team references (split on `/` presence) +- [ ] 6.3 Add validation in `TestOrgs` that team references from CODEOWNERS exist as teams in peribolos.yaml +- [ ] 6.4 Maintain existing validation that individual CODEOWNERS users are org admins with minimum 3 required +- [ ] 6.5 Add duplicate check for team references +- [ ] 6.6 Run `go test ./...` and verify all tests pass + +## 7. Verification + +- [ ] 7.1 Run `yamllint peribolos.yaml` and verify no lint errors +- [ ] 7.2 Verify all team member and maintainer lists are alphabetically sorted +- [ ] 7.3 Verify all teams have `privacy: closed` From a8d67e2a0be9074782526ecbdf12b68f7a61032a Mon Sep 17 00:00:00 2001 From: Marcus Burghardt Date: Tue, 12 May 2026 13:24:33 +0200 Subject: [PATCH 2/5] feat: restructure peribolos teams and CODEOWNERS for provider split Reflect the organizational split of providers from complyctl to complytime-providers by restructuring GitHub team definitions and CODEOWNERS files across the complytime org. - Rename openscap-plugin-approvers to openscap-provider-approvers - Create ampel-provider-approvers, opa-provider-approvers, and complytime-policies-approvers teams - Repurpose complytime-approvers for non-code repo stakeholder access - Move CODEOWNERS from root to .github/ (GitHub recommended location) - Update config_test.go to validate team references in CODEOWNERS - Update spec artifacts with review council feedback (GIVEN clauses, PRESERVED requirements, dependency annotations) Assisted-by: OpenCode (claude-opus-4-6) Signed-off-by: Marcus Burghardt --- .github/CODEOWNERS | 1 + CODEOWNERS | 1 - config/config_test.go | 49 +++++++--- .../design.md | 24 ++++- .../proposal.md | 14 ++- .../specs/codeowners-management/spec.md | 88 ++++++++++-------- .../specs/team-restructuring/spec.md | 92 +++++++++++-------- .../specs/test-validation/spec.md | 52 ++++++++--- .../restructure-teams-and-codeowners/tasks.md | 52 ++++++----- peribolos.yaml | 54 ++++++++++- 10 files changed, 294 insertions(+), 133 deletions(-) create mode 100644 .github/CODEOWNERS delete mode 100644 CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..510fe88 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index edc5ee2..0000000 --- a/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @jflowers @jpower432 @marcusburghardt diff --git a/config/config_test.go b/config/config_test.go index a0e4c82..20ea9ac 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -32,7 +32,7 @@ import ( ) var configPath = flag.String("config", "../peribolos.yaml", "Path to peribolos config") -var ownersDir = flag.String("owners-dir", "../", "Directory to CODEOWNERS") +var ownersDir = flag.String("owners-dir", "../.github", "Directory to CODEOWNERS") var cfg org.FullConfig @@ -62,34 +62,37 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func loadOwners(dir string) ([]string, error) { - var owners []string - +func loadOwners(dir string) (users []string, teams []string, err error) { dir = path.Clean(dir) file, err := os.Open(path.Join(dir, "CODEOWNERS")) if err != nil { - return nil, err + return nil, nil, err } ruleset, err := codeowners.ParseFile(file) if err != nil { - return nil, err + return nil, nil, err } rule, err := ruleset.Match(*configPath) if err != nil { - return nil, err + return nil, nil, err } if rule == nil { - return nil, fmt.Errorf("no matching rule found for %s", *configPath) + return nil, nil, fmt.Errorf("no matching rule found for %s", *configPath) } for _, owner := range rule.Owners { - owners = append(owners, owner.String()) + ownerStr := owner.String() + if strings.Contains(ownerStr, "/") { + teams = append(teams, ownerStr) + } else { + users = append(users, ownerStr) + } } - return owners, nil + return users, teams, nil } func testDuplicates(list sets.Set[string]) error { @@ -185,7 +188,7 @@ func testTeamMembers(teams map[string]org.Team, admins sets.Set[string], orgMemb } func TestOrgs(t *testing.T) { - own, err := loadOwners(*ownersDir) + ownUsers, ownTeams, err := loadOwners(*ownersDir) if err != nil { t.Fatalf("failed to load CODEOWNERS: %v", err) } @@ -195,7 +198,8 @@ func TestOrgs(t *testing.T) { admins := normalize(sets.New(org.Admins...)) allOrgMembers := members.Union(admins) - approvers := normalize(sets.New(own...)) + // Validate individual CODEOWNERS users are org admins + approvers := normalize(sets.New(ownUsers...)) if diff := approvers.Difference(admins); len(diff) > 0 { t.Errorf("users do not match in CODEOWNERS and org admins '%s': %s", *org.Name, strings.Join(diff.UnsortedList(), ", ")) @@ -209,6 +213,27 @@ func TestOrgs(t *testing.T) { t.Errorf("duplicate approvers: %v", err) } + // Validate CODEOWNERS team references exist in peribolos config + teamRefs := normalize(sets.New(ownTeams...)) + + if err := testDuplicates(teamRefs); err != nil { + t.Errorf("duplicate team references in CODEOWNERS: %v", err) + } + + if org.Teams != nil { + for _, ref := range teamRefs.UnsortedList() { + // Team references are in the form "org/team-name"; + // extract the team name (part after the last "/"). + teamName := ref + if idx := strings.LastIndex(ref, "/"); idx >= 0 { + teamName = ref[idx+1:] + } + if _, exists := org.Teams[teamName]; !exists { + t.Errorf("CODEOWNERS references team '%s' which does not exist in org '%s' teams", ref, *org.Name) + } + } + } + if both := admins.Intersection(members); len(both) > 0 { t.Errorf("users in both org admin and member roles for org '%s': %s", *org.Name, strings.Join(both.UnsortedList(), ", ")) } diff --git a/openspec/changes/restructure-teams-and-codeowners/design.md b/openspec/changes/restructure-teams-and-codeowners/design.md index 4427a5d..f94987c 100644 --- a/openspec/changes/restructure-teams-and-codeowners/design.md +++ b/openspec/changes/restructure-teams-and-codeowners/design.md @@ -3,10 +3,15 @@ The complytime GitHub organization manages 12 repositories with peribolos (declarative GitHub org management via YAML). The organization recently split content from `complyctl` by moving the openscap-plugin to `complytime-providers` -(now called "providers"). Two providers exist today (openscap, ampel), a third +(the concept formerly called "plugins" is now called "providers"). Two providers exist today (openscap, ampel), a third (opa) is expected. Additionally, `complytime-policies` needs dedicated ownership for Gemara compliance content. +This change assumes the `fix-peribolos-implementation` change has been applied. +That change wires the `testTeamMembers()` validation function into `TestOrgs()` +and fixes admin/member role placement in existing teams. The new teams defined +here follow the corrected role assignment pattern established by that change. + Current state: - `openscap-plugin-approvers` team still points at `complyctl` with stale naming - No per-provider teams exist for ampel or opa @@ -120,9 +125,22 @@ explored and deferred for simplicity. **[Cross-repo coordination]** Changes span 4 repositories. CODEOWNERS changes in complyctl, complytime-providers, and complytime-policies depend on the -teams existing first (via peribolos apply). +teams existing first (via peribolos apply). If CODEOWNERS references a +non-existent team, GitHub silently ignores the reference — PRs merge without +the intended review gate, which is a silent security degradation. -> Mitigation: Apply peribolos.yaml changes first (teams must exist before -CODEOWNERS references them). CODEOWNERS updates in other repos follow. +CODEOWNERS references them). CODEOWNERS updates in other repos follow. After +peribolos apply, trigger the `drift_detection.yml` workflow manually to confirm +convergence between peribolos.yaml and the actual GitHub org state. + +**[Team rename partial failure]** Renaming `openscap-plugin-approvers` to +`openscap-provider-approvers` is a destructive, non-atomic operation — peribolos +deletes the old team and creates the new one. If the apply fails midway, the +old team may be deleted before the new team is created, temporarily leaving +affected users without team-based write access. +-> Mitigation: Risk accepted. The impact is limited to the openscap-plugin team +rename only. Users retain org-level read access and complytime-dev write access +during any transient state. The drift detection workflow catches divergence. **[Provider team divergence from CODEOWNERS]** If the last-matching-pattern rule in CODEOWNERS selects only a provider team and a future member is removed diff --git a/openspec/changes/restructure-teams-and-codeowners/proposal.md b/openspec/changes/restructure-teams-and-codeowners/proposal.md index 1bc69c6..84736fe 100644 --- a/openspec/changes/restructure-teams-and-codeowners/proposal.md +++ b/openspec/changes/restructure-teams-and-codeowners/proposal.md @@ -1,7 +1,8 @@ ## Why The complytime organization has undergone structural changes: the openscap-plugin -was moved from complyctl to complytime-providers (now called "providers"), and +was moved from complyctl to complytime-providers (the concept formerly called +"plugins" is now called "providers"), and complytime-providers now hosts multiple providers (openscap, ampel) with a third (opa) expected. Additionally, complytime-policies needs dedicated ownership for Gemara content. The current peribolos team structure and CODEOWNERS files do not @@ -46,6 +47,10 @@ no per-provider review gates. (none -- no existing specs to modify) +### Removed Capabilities + +(none -- `openscap-plugin-approvers` is being renamed, not removed) + ## Impact - **peribolos.yaml**: Team definitions restructured (1 rename, 3 creates, @@ -58,5 +63,8 @@ no per-provider review gates. - **CODEOWNERS (complytime-providers)**: New file with per-provider ownership. - **CODEOWNERS (complytime-policies)**: New file with combined team ownership. - **Cross-repo**: Changes span 4 repositories (.github, complyctl, - complytime-providers, complytime-policies). Each repo's changes are independent - but should be coordinated. + complytime-providers, complytime-policies). CODEOWNERS changes in other repos + MUST NOT be merged until peribolos has applied the new team definitions. +- **Documentation**: No README or CONTRIBUTING updates required. PR descriptions + for cross-repo CODEOWNERS changes should explain the new review gate behavior + to contributors of the affected repositories. diff --git a/openspec/changes/restructure-teams-and-codeowners/specs/codeowners-management/spec.md b/openspec/changes/restructure-teams-and-codeowners/specs/codeowners-management/spec.md index 7cd74e1..54c23b1 100644 --- a/openspec/changes/restructure-teams-and-codeowners/specs/codeowners-management/spec.md +++ b/openspec/changes/restructure-teams-and-codeowners/specs/codeowners-management/spec.md @@ -11,45 +11,54 @@ https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-f #### Scenario: This repo CODEOWNERS moved from root to .github -- **WHEN** the CODEOWNERS file is created at `.github/CODEOWNERS` in the - `.github` repository -- **THEN** the root `CODEOWNERS` file SHALL be deleted and all ownership - rules SHALL be defined in `.github/CODEOWNERS` +- **GIVEN** the `.github` repository previously had CODEOWNERS at the root +- **WHEN** the migration is complete +- **THEN** `.github/CODEOWNERS` exists with the ownership rules +- **AND** the root `CODEOWNERS` file is deleted ### Requirement: .github repo CODEOWNERS includes complytime-approvers team The `.github` repository CODEOWNERS SHALL include both individual admin users and the `@complytime/complytime-approvers` team as code owners for -all files. The format SHALL be: +all files. The file at `.github/CODEOWNERS` SHALL contain the line: ``` * @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers ``` -#### Scenario: Team and individual owners on same line +#### Scenario: CODEOWNERS file content validated -- **WHEN** a pull request is opened in the `.github` repository -- **THEN** review is requested from the individual admins and any member - of the `complytime-approvers` team, and approval from any one of them - satisfies the CODEOWNERS requirement +- **GIVEN** the `.github/CODEOWNERS` file exists in this repository +- **WHEN** the file is read +- **THEN** it contains the line + `* @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers` + +Note: GitHub will request review from the individual admins and any member of +the `complytime-approvers` team when a PR is opened. Approval from any one of +them satisfies the CODEOWNERS requirement. ### Requirement: complyctl CODEOWNERS cleaned up -The complyctl repository CODEOWNERS SHALL be simplified to a single fallback -rule assigning `@complytime/complytime-dev` as the owner for all files. The -stale `/cmd/openscap-plugin/` rule and the `/cmd/complyctl/` specific rule -SHALL be removed. +The complyctl repository (at `.github/CODEOWNERS`, which is the file's current +location) SHALL be simplified to a single fallback rule assigning +`@complytime/complytime-dev` as the owner for all files. The stale +`/cmd/openscap-plugin/` rule and the `/cmd/complyctl/` specific rule SHALL be +removed. #### Scenario: Stale openscap-plugin rule removed -- **WHEN** the complyctl CODEOWNERS is updated +- **GIVEN** the complyctl `.github/CODEOWNERS` file has been updated +- **WHEN** the file is read - **THEN** there is no rule referencing `/cmd/openscap-plugin/` or `@complytime/openscap-plugin-approvers` #### Scenario: Single fallback rule -- **WHEN** a pull request is opened in complyctl modifying any file -- **THEN** review is requested from `@complytime/complytime-dev` +- **GIVEN** the complyctl `.github/CODEOWNERS` file has been updated +- **WHEN** the file is read +- **THEN** the only rule is `* @complytime/complytime-dev` + +Note: GitHub will request review from `@complytime/complytime-dev` for all PRs. ### Requirement: complytime-providers CODEOWNERS created with per-provider rules @@ -65,23 +74,19 @@ The file SHALL contain: /cmd/opa-provider/ @complytime/opa-provider-approvers ``` -#### Scenario: Provider-specific change triggers provider team review - -- **WHEN** a pull request modifies files only under `/cmd/openscap-provider/` -- **THEN** review is requested from `@complytime/openscap-provider-approvers` - only (last matching pattern takes precedence over the `*` fallback) - -#### Scenario: Shared code change triggers dev team review +#### Scenario: CODEOWNERS file content validated -- **WHEN** a pull request modifies files under `/internal/` or root-level files -- **THEN** review is requested from `@complytime/complytime-dev` +- **GIVEN** the complytime-providers `.github/CODEOWNERS` file has been created +- **WHEN** the file is read +- **THEN** it contains the fallback rule `* @complytime/complytime-dev` and + per-provider rules for `/cmd/openscap-provider/`, `/cmd/ampel-provider/`, + and `/cmd/opa-provider/` -#### Scenario: Cross-provider change triggers multiple teams - -- **WHEN** a pull request modifies files in both `/cmd/openscap-provider/` - and `/cmd/ampel-provider/` -- **THEN** review is requested from both `@complytime/openscap-provider-approvers` - and `@complytime/ampel-provider-approvers` +Note: GitHub uses last-matching-pattern semantics. A PR modifying only +`/cmd/openscap-provider/` triggers review from `openscap-provider-approvers` +only. A PR modifying both `/cmd/openscap-provider/` and `/internal/` triggers +review from both the provider team and `complytime-dev`. Shared code under +`/internal/` or root-level files match only the `*` fallback. ### Requirement: complytime-policies CODEOWNERS created @@ -94,8 +99,19 @@ as code owners for all files. * @complytime/complytime-policies-approvers @complytime/complytime-dev ``` -#### Scenario: Both teams requested for review +#### Scenario: CODEOWNERS file content validated + +- **GIVEN** the complytime-policies `.github/CODEOWNERS` file has been created +- **WHEN** the file is read +- **THEN** it contains the line + `* @complytime/complytime-policies-approvers @complytime/complytime-dev` + +Note: GitHub will request review from both teams for all PRs. + +### Scope Note -- **WHEN** a pull request is opened in complytime-policies modifying any file -- **THEN** review is requested from both `@complytime/complytime-policies-approvers` - and `@complytime/complytime-dev` +Validation of CODEOWNERS files in complyctl, complytime-providers, and +complytime-policies is out of scope for `config_test.go` in this repository. +Each repository's own CI pipeline is responsible for validating its CODEOWNERS +file. The `config_test.go` in this repo only validates the `.github/CODEOWNERS` +file within this repository. diff --git a/openspec/changes/restructure-teams-and-codeowners/specs/team-restructuring/spec.md b/openspec/changes/restructure-teams-and-codeowners/specs/team-restructuring/spec.md index 8f10e72..e1b8ef7 100644 --- a/openspec/changes/restructure-teams-and-codeowners/specs/team-restructuring/spec.md +++ b/openspec/changes/restructure-teams-and-codeowners/specs/team-restructuring/spec.md @@ -3,18 +3,22 @@ ### Requirement: Rename openscap-plugin-approvers to openscap-provider-approvers The peribolos configuration SHALL rename the team `openscap-plugin-approvers` to -`openscap-provider-approvers`. The team description SHALL reference -"openscap-provider in complytime-providers". Maintainers SHALL be `jpower432` -and `marcusburghardt`. Members SHALL be `gvauter`, `hbraswelrh`, `sonupreetam`, -and `trevor-vaughan`. The team SHALL have write access to `complytime-providers` -and SHALL NOT have access to `complyctl`. +`openscap-provider-approvers`. The team SHALL have `privacy: closed`. The team +description SHALL reference "openscap-provider in complytime-providers". +Maintainers SHALL be `jpower432` and `marcusburghardt`. Members SHALL be +`gvauter`, `hbraswelrh`, `sonupreetam`, and `trevor-vaughan`. The team SHALL +have write access to `complytime-providers` and SHALL NOT have access to +`complyctl`. #### Scenario: Team renamed and repo access updated -- **WHEN** peribolos applies the configuration -- **THEN** the team `openscap-plugin-approvers` is renamed to - `openscap-provider-approvers` with write access to `complytime-providers` - and no access to `complyctl` +- **GIVEN** the peribolos.yaml file contains the updated team definition +- **WHEN** peribolos.yaml is parsed +- **THEN** a team `openscap-provider-approvers` exists with `privacy: closed`, + maintainers `["jpower432", "marcusburghardt"]`, members `["gvauter", + "hbraswelrh", "sonupreetam", "trevor-vaughan"]`, and repos including + `complytime-providers: write` +- **AND** no team named `openscap-plugin-approvers` exists ### Requirement: Create ampel-provider-approvers team @@ -25,10 +29,12 @@ The team SHALL have write access to `complytime-providers`. #### Scenario: Team created with correct membership -- **WHEN** peribolos applies the configuration +- **GIVEN** the peribolos.yaml file contains the team definition +- **WHEN** peribolos.yaml is parsed - **THEN** the team `ampel-provider-approvers` exists with `privacy: closed`, - the specified maintainers and members, and write access to - `complytime-providers` + maintainers `["jpower432", "marcusburghardt"]`, members `["gvauter", + "hbraswelrh", "sonupreetam", "trevor-vaughan"]`, and repos including + `complytime-providers: write` ### Requirement: Create opa-provider-approvers team @@ -39,9 +45,11 @@ Members SHALL be `fortiz-ai`, `gvauter`, `hbraswelrh`, `sonupreetam`, and #### Scenario: Team created with provider-specific member -- **WHEN** peribolos applies the configuration -- **THEN** the team `opa-provider-approvers` exists with `fortiz-ai` as a - member in addition to all complytime-dev members +- **GIVEN** the peribolos.yaml file contains the team definition +- **WHEN** peribolos.yaml is parsed +- **THEN** the team `opa-provider-approvers` exists with `privacy: closed`, + members including `fortiz-ai` in addition to all complytime-dev members, + and repos including `complytime-providers: write` ### Requirement: Create complytime-policies-approvers team @@ -52,10 +60,11 @@ access to `complytime-policies`. #### Scenario: Team created for Gemara content ownership -- **WHEN** peribolos applies the configuration +- **GIVEN** the peribolos.yaml file contains the team definition +- **WHEN** peribolos.yaml is parsed - **THEN** the team `complytime-policies-approvers` exists with `privacy: closed`, - the specified maintainers, `fortiz-ai` as a member, and write access to - `complytime-policies` + maintainers `["jflowers", "jpower432", "marcusburghardt"]`, member `fortiz-ai`, + and repos including `complytime-policies: write` ### Requirement: Repurpose complytime-approvers team @@ -68,18 +77,26 @@ have write access to `complyctl` or `complytime`. #### Scenario: Team repurposed with updated membership and repos -- **WHEN** peribolos applies the configuration -- **THEN** the team `complytime-approvers` has the updated description, - maintainers (jflowers, jpower432, marcusburghardt), members (beatrizmcouto, - hbraswelrh), and write access only to `.github`, `community`, - `complytime-demos`, and `website` +- **GIVEN** the peribolos.yaml file contains the updated team definition +- **WHEN** peribolos.yaml is parsed +- **THEN** the team `complytime-approvers` has maintainers `["jflowers", + "jpower432", "marcusburghardt"]`, members `["beatrizmcouto", "hbraswelrh"]`, + and repos `.github`, `community`, `complytime-demos`, `website` (all write) #### Scenario: Previous members removed -- **WHEN** peribolos applies the configuration -- **THEN** `gvauter`, `sonupreetam`, and `trevor-vaughan` are no longer +- **GIVEN** the peribolos.yaml file contains the updated team definition +- **WHEN** peribolos.yaml is parsed +- **THEN** `gvauter`, `sonupreetam`, and `trevor-vaughan` are not listed as members of `complytime-approvers` +## PRESERVED Requirements + +The following validations already exist in `config_test.go` (via +`testTeamMembers`). They are documented here to confirm they MUST be maintained +and will apply to all new and modified teams. No new test code is needed for +these — the existing validation covers them. + ### Requirement: All teams use privacy closed All teams in peribolos.yaml SHALL use `privacy: closed`. This is required @@ -96,9 +113,10 @@ References: #### Scenario: Secret team rejected by validation +- **GIVEN** peribolos.yaml is loaded and config_test.go runs `testTeamMembers` - **WHEN** a team in peribolos.yaml uses `privacy: secret` -- **THEN** the config_test.go validation fails with an error indicating - the team does not have the `privacy: closed` field +- **THEN** the validation fails with an error indicating the team does not have + the `privacy: closed` field ### Requirement: Team maintainers must be org admins @@ -108,15 +126,17 @@ listed in a team SHALL be listed as maintainers, not members. #### Scenario: Non-admin listed as maintainer -- **WHEN** a non-admin user is listed as a team maintainer in peribolos.yaml -- **THEN** the config_test.go validation fails with an error indicating - the user should be in the members list +- **GIVEN** peribolos.yaml is loaded and config_test.go runs `testTeamMembers` +- **WHEN** a non-admin user is listed as a team maintainer +- **THEN** the validation fails with an error indicating the user should be in + the members list #### Scenario: Admin listed as member -- **WHEN** an org admin is listed as a team member in peribolos.yaml -- **THEN** the config_test.go validation fails with an error indicating - the user should be in the maintainers list +- **GIVEN** peribolos.yaml is loaded and config_test.go runs `testTeamMembers` +- **WHEN** an org admin is listed as a team member +- **THEN** the validation fails with an error indicating the user should be in + the maintainers list ### Requirement: Team member and maintainer lists must be sorted @@ -125,6 +145,6 @@ sorted alphabetically. #### Scenario: Unsorted member list -- **WHEN** a team has an unsorted member list in peribolos.yaml -- **THEN** the config_test.go validation fails with an error indicating - the list is unsorted +- **GIVEN** peribolos.yaml is loaded and config_test.go runs `testTeamMembers` +- **WHEN** a team has an unsorted member list +- **THEN** the validation fails with an error indicating the list is unsorted diff --git a/openspec/changes/restructure-teams-and-codeowners/specs/test-validation/spec.md b/openspec/changes/restructure-teams-and-codeowners/specs/test-validation/spec.md index 91a4f25..7d7940e 100644 --- a/openspec/changes/restructure-teams-and-codeowners/specs/test-validation/spec.md +++ b/openspec/changes/restructure-teams-and-codeowners/specs/test-validation/spec.md @@ -9,21 +9,30 @@ the `.github/` directory. #### Scenario: Test reads CODEOWNERS from .github directory -- **WHEN** config_test.go runs with default flags +- **GIVEN** config_test.go runs with default flags +- **WHEN** `loadOwners` is invoked - **THEN** the test reads the CODEOWNERS file from `../.github/CODEOWNERS` instead of `../CODEOWNERS` ### Requirement: CODEOWNERS parsing separates users from teams -The `loadOwners` function SHALL separate CODEOWNERS entries into individual -users and team references. A team reference is identified by the presence of -a `/` character in the owner string (e.g., `@complytime/complytime-approvers`). -Individual users do not contain a `/`. +The `loadOwners` function SHALL return separate lists for individual users +and team references. The return signature SHALL be +`(users []string, teams []string, err error)`. + +A team reference is identified by the presence of a `/` character in the owner +string (e.g., `@complytime/complytime-approvers`). Individual users do not +contain a `/`. + +Individual users SHALL have the `@` prefix stripped (e.g., `"jflowers"` not +`"@jflowers"`). Team references SHALL retain the org-qualified form without +`@` (e.g., `"complytime/complytime-approvers"`). #### Scenario: Mixed individual and team owners parsed -- **WHEN** the CODEOWNERS file contains +- **GIVEN** the CODEOWNERS file contains `* @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers` +- **WHEN** `loadOwners` parses the file - **THEN** individual users are `["jflowers", "jpower432", "marcusburghardt"]` and team references are `["complytime/complytime-approvers"]` @@ -35,12 +44,14 @@ org admins. This preserves the existing validation behavior. The minimum of #### Scenario: Non-admin individual in CODEOWNERS +- **GIVEN** peribolos.yaml is loaded with its org admin list - **WHEN** the CODEOWNERS file lists an individual user who is not an org admin - **THEN** the test fails with an error indicating the user does not match org admins #### Scenario: Fewer than 3 individual approvers +- **GIVEN** peribolos.yaml is loaded - **WHEN** the CODEOWNERS file lists fewer than 3 individual users - **THEN** the test fails with an error indicating insufficient approvers @@ -49,32 +60,49 @@ org admins. This preserves the existing validation behavior. The minimum of The test SHALL validate that all team references in CODEOWNERS correspond to teams defined in peribolos.yaml. The team name extracted from the CODEOWNERS entry (the part after the `/`, e.g., `complytime-approvers` from -`@complytime/complytime-approvers`) MUST exist as a key in the org's teams map. +`complytime/complytime-approvers`) MUST exist as a key in the org's teams map. #### Scenario: Valid team reference +- **GIVEN** peribolos.yaml is loaded and contains the team `complytime-approvers` - **WHEN** the CODEOWNERS file references `@complytime/complytime-approvers` - and the team `complytime-approvers` exists in peribolos.yaml - **THEN** the test passes the team reference validation #### Scenario: Invalid team reference +- **GIVEN** peribolos.yaml is loaded and contains no team `nonexistent-team` - **WHEN** the CODEOWNERS file references `@complytime/nonexistent-team` - and no team `nonexistent-team` exists in peribolos.yaml - **THEN** the test fails with an error indicating the team does not exist in the org configuration ### Requirement: No duplicate owners in CODEOWNERS The test SHALL validate that there are no duplicate entries in the CODEOWNERS -file, checking both individual users and team references. +file, checking both individual users and team references independently. #### Scenario: Duplicate individual user -- **WHEN** the CODEOWNERS file lists the same user twice +- **GIVEN** the CODEOWNERS file has been parsed +- **WHEN** the same user appears twice in the owners list - **THEN** the test fails with a duplicate approvers error #### Scenario: Duplicate team reference -- **WHEN** the CODEOWNERS file lists the same team twice +- **GIVEN** the CODEOWNERS file has been parsed +- **WHEN** the same team appears twice in the owners list - **THEN** the test fails with a duplicate teams error + +## PRESERVED Requirements + +The following validations already exist in `config_test.go` and MUST be +maintained. The `loadOwners` changes MUST NOT regress these behaviors: + +- **Privacy check**: `testTeamMembers` validates all teams have `privacy: closed` +- **Admin-as-maintainer check**: `testTeamMembers` validates non-admins are not + listed as maintainers and admins are not listed as regular members +- **Sorted lists check**: `testTeamMembers` validates maintainer and member lists + are alphabetically sorted +- **Org membership check**: `testTeamMembers` validates all team members are org + members +- **Duplicate check**: `testTeamMembers` validates no duplicate maintainers or + members within a team diff --git a/openspec/changes/restructure-teams-and-codeowners/tasks.md b/openspec/changes/restructure-teams-and-codeowners/tasks.md index 4aceaff..6f89f86 100644 --- a/openspec/changes/restructure-teams-and-codeowners/tasks.md +++ b/openspec/changes/restructure-teams-and-codeowners/tasks.md @@ -1,39 +1,41 @@ -## 1. Peribolos Team Definitions (this repo) +## 1. Peribolos Team Definitions (this repo: .github) -- [ ] 1.1 Rename `openscap-plugin-approvers` to `openscap-provider-approvers` in peribolos.yaml: update team name, description, expand maintainers to include `jpower432`, expand members to all complytime-dev members, change repo access from `complyctl: write` to `complytime-providers: write` -- [ ] 1.2 Create `ampel-provider-approvers` team in peribolos.yaml with `privacy: closed`, maintainers `jpower432` and `marcusburghardt`, members `gvauter`, `hbraswelrh`, `sonupreetam`, `trevor-vaughan`, and `complytime-providers: write` -- [ ] 1.3 Create `opa-provider-approvers` team in peribolos.yaml with `privacy: closed`, maintainers `jpower432` and `marcusburghardt`, members `fortiz-ai`, `gvauter`, `hbraswelrh`, `sonupreetam`, `trevor-vaughan`, and `complytime-providers: write` -- [ ] 1.4 Create `complytime-policies-approvers` team in peribolos.yaml with `privacy: closed`, maintainers `jflowers`, `jpower432`, `marcusburghardt`, member `fortiz-ai`, and `complytime-policies: write` -- [ ] 1.5 Repurpose `complytime-approvers` in peribolos.yaml: update description to "Write access to non-code repos for project stakeholders", set maintainers to `jflowers`, `jpower432`, `marcusburghardt`, set members to `beatrizmcouto`, `hbraswelrh`, set repos to `.github`, `community`, `complytime-demos`, `website` (all write) +- [x] 1.1 Rename `openscap-plugin-approvers` to `openscap-provider-approvers` in peribolos.yaml: update team name, description, add `privacy: closed`, expand maintainers to include `jpower432`, expand members to all complytime-dev members, change repo access from `complyctl: write` to `complytime-providers: write` +- [x] 1.2 Create `ampel-provider-approvers` team in peribolos.yaml with `privacy: closed`, maintainers `jpower432` and `marcusburghardt`, members `gvauter`, `hbraswelrh`, `sonupreetam`, `trevor-vaughan`, and `complytime-providers: write` +- [x] 1.3 Create `opa-provider-approvers` team in peribolos.yaml with `privacy: closed`, maintainers `jpower432` and `marcusburghardt`, members `fortiz-ai`, `gvauter`, `hbraswelrh`, `sonupreetam`, `trevor-vaughan`, and `complytime-providers: write` +- [x] 1.4 Create `complytime-policies-approvers` team in peribolos.yaml with `privacy: closed`, maintainers `jflowers`, `jpower432`, `marcusburghardt`, member `fortiz-ai`, and `complytime-policies: write` +- [x] 1.5 Repurpose `complytime-approvers` in peribolos.yaml: update description to "Write access to non-code repos for project stakeholders", set maintainers to `jflowers`, `jpower432`, `marcusburghardt`, set members to `beatrizmcouto`, `hbraswelrh`, set repos to `.github`, `community`, `complytime-demos`, `website` (all write) ## 2. CODEOWNERS for This Repo (.github) -- [ ] 2.1 Create `.github/CODEOWNERS` with content: `* @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers` -- [ ] 2.2 Delete the root `CODEOWNERS` file +- [x] 2.1 Create `.github/CODEOWNERS` with content: `* @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers` +- [x] 2.2 Delete the root `CODEOWNERS` file if it exists -## 3. CODEOWNERS for complyctl +## 3. CODEOWNERS for complyctl (repo: complyctl) [blocked-by: 1.x applied via peribolos] -- [ ] 3.1 Update `complyctl/.github/CODEOWNERS` to a single rule: `* @complytime/complytime-dev` (remove stale `/cmd/openscap-plugin/` rule and `/cmd/complyctl/` specific rule) +- [x] 3.1 [P] In the `complyctl` repository, update `.github/CODEOWNERS` to a single rule: `* @complytime/complytime-dev` (remove stale `/cmd/openscap-plugin/` rule and `/cmd/complyctl/` specific rule) -## 4. CODEOWNERS for complytime-providers +## 4. CODEOWNERS for complytime-providers (repo: complytime-providers) [blocked-by: 1.x applied via peribolos] -- [ ] 4.1 Create `complytime-providers/.github/CODEOWNERS` with fallback `* @complytime/complytime-dev` and per-provider rules for `/cmd/openscap-provider/`, `/cmd/ampel-provider/`, `/cmd/opa-provider/` +- [x] 4.1 [P] In the `complytime-providers` repository, create `.github/CODEOWNERS` with fallback `* @complytime/complytime-dev` and per-provider rules for `/cmd/openscap-provider/`, `/cmd/ampel-provider/`, `/cmd/opa-provider/` -## 5. CODEOWNERS for complytime-policies +## 5. CODEOWNERS for complytime-policies (repo: complytime-policies) [blocked-by: 1.x applied via peribolos] -- [ ] 5.1 Create `complytime-policies/.github/CODEOWNERS` with rule: `* @complytime/complytime-policies-approvers @complytime/complytime-dev` +- [x] 5.1 [P] In the `complytime-policies` repository, create `.github/CODEOWNERS` with rule: `* @complytime/complytime-policies-approvers @complytime/complytime-dev` -## 6. Test Validation Updates (this repo) +## 6. Test Validation Updates (this repo: .github) [should complete before or alongside sections 2-5] -- [ ] 6.1 Update `config_test.go` `--owners-dir` flag default from `"../"` to `"../.github"` -- [ ] 6.2 Update `loadOwners` function to return separate lists for individual users and team references (split on `/` presence) -- [ ] 6.3 Add validation in `TestOrgs` that team references from CODEOWNERS exist as teams in peribolos.yaml -- [ ] 6.4 Maintain existing validation that individual CODEOWNERS users are org admins with minimum 3 required -- [ ] 6.5 Add duplicate check for team references -- [ ] 6.6 Run `go test ./...` and verify all tests pass +- [x] 6.1 Update `config_test.go` `--owners-dir` flag default from `"../"` to `"../.github"` +- [x] 6.2 Update `loadOwners` function to return separate lists for individual users and team references (split on `/` presence). Return signature: `(users []string, teams []string, err error)` +- [x] 6.3 Add validation in `TestOrgs` that team references from CODEOWNERS exist as teams in peribolos.yaml +- [x] 6.4 Maintain existing validation that individual CODEOWNERS users are org admins with minimum 3 required +- [x] 6.5 Add duplicate check for team references +- [x] 6.6 Run `go test ./...` and verify all tests pass (covers privacy:closed, sorted lists, admin-as-maintainer from existing `testTeamMembers` — no new code needed for those) -## 7. Verification +## 7. Verification [blocked-by: all prior sections] -- [ ] 7.1 Run `yamllint peribolos.yaml` and verify no lint errors -- [ ] 7.2 Verify all team member and maintainer lists are alphabetically sorted -- [ ] 7.3 Verify all teams have `privacy: closed` +- [x] 7.1 Run `yamllint peribolos.yaml` and verify no lint errors +- [x] 7.2 Run `go test ./config/... -v -count=1` and verify all structural validations pass (privacy:closed, sorted lists, admin-as-maintainer, team reference validation) +- [ ] 7.3 After peribolos apply, trigger `drift_detection.yml` workflow manually to confirm convergence + + diff --git a/peribolos.yaml b/peribolos.yaml index 10c370c..dbbe18e 100644 --- a/peribolos.yaml +++ b/peribolos.yaml @@ -104,7 +104,22 @@ orgs: org-infra: triage website: triage complytime-approvers: - description: This would be a CODEOWNERS group for cmd complytime + description: Write access to non-code repos for project stakeholders + privacy: closed + maintainers: + - jflowers + - jpower432 + - marcusburghardt + members: + - beatrizmcouto + - hbraswelrh + repos: + ".github": write + community: write + complytime-demos: write + website: write + ampel-provider-approvers: + description: CODEOWNERS group for ampel-provider in complytime-providers privacy: closed maintainers: - jpower432 @@ -115,16 +130,45 @@ orgs: - sonupreetam - trevor-vaughan repos: - complyctl: write - openscap-plugin-approvers: - description: This would be a CODEOWNERS group for cmd openscap-plugin + complytime-providers: write + openscap-provider-approvers: + description: CODEOWNERS group for openscap-provider in complytime-providers privacy: closed maintainers: + - jpower432 - marcusburghardt members: - gvauter + - hbraswelrh + - sonupreetam + - trevor-vaughan repos: - complyctl: write + complytime-providers: write + opa-provider-approvers: + description: CODEOWNERS group for opa-provider in complytime-providers + privacy: closed + maintainers: + - jpower432 + - marcusburghardt + members: + - fortiz-ai + - gvauter + - hbraswelrh + - sonupreetam + - trevor-vaughan + repos: + complytime-providers: write + complytime-policies-approvers: + description: CODEOWNERS group for complytime-policies + privacy: closed + maintainers: + - jflowers + - jpower432 + - marcusburghardt + members: + - fortiz-ai + repos: + complytime-policies: write complytime-dev: description: People working on complytime repo privacy: closed From 7d9b49e12c1cf64b678091c419ba88b728ff49a6 Mon Sep 17 00:00:00 2001 From: Marcus Burghardt Date: Wed, 13 May 2026 11:09:08 +0200 Subject: [PATCH 3/5] chore: add 5 missing org members to peribolos.yaml Add fkolacek-rh, jiprocha, jpadmanrh, ppsomiad, and rmonk-redhat to the org members list. These users exist in the GitHub org but were not declared in peribolos.yaml, causing the apply workflow to fail with "cannot delete 5 memberships (exceeds limit of 0.250)". Assisted-by: OpenCode (claude-opus-4-6) Signed-off-by: Marcus Burghardt --- peribolos.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/peribolos.yaml b/peribolos.yaml index dbbe18e..28fa20e 100644 --- a/peribolos.yaml +++ b/peribolos.yaml @@ -16,15 +16,20 @@ orgs: - benroose - bplaxco - eeasley2014 + - fkolacek-rh - fortiz-ai - gvauter - hbraswelrh - jamimoor - JenniferPrivette + - jiprocha + - jpadmanrh - mraml - nladha09 - phoward-rh + - ppsomiad - rbaratam-psc + - rmonk-redhat - sonupreetam - trevor-vaughan - vojtapolasek From 6be08c8e9eea83d375d160d96cf9f02fd5779f01 Mon Sep 17 00:00:00 2001 From: Marcus Burghardt Date: Wed, 13 May 2026 11:09:41 +0200 Subject: [PATCH 4/5] chore: remove pme-bot from org admins pme-bot is a regular user account that was used for the old device flow token approach. It no longer exists in the GitHub org and is not needed as an admin now that authentication uses the complytime-bot GitHub App installation token. Assisted-by: OpenCode (claude-opus-4-6) Signed-off-by: Marcus Burghardt --- peribolos.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/peribolos.yaml b/peribolos.yaml index 28fa20e..e31e32e 100644 --- a/peribolos.yaml +++ b/peribolos.yaml @@ -9,7 +9,6 @@ orgs: - jflowers - jpower432 - marcusburghardt - - pme-bot members: - au-der - beatrizmcouto From 0de861ece842f7fc4271e11c9d5fb6d5189a33b9 Mon Sep 17 00:00:00 2001 From: Marcus Burghardt Date: Tue, 19 May 2026 09:26:38 +0200 Subject: [PATCH 5/5] fix: harden .github repo access and peribolos admin protection - Remove complytime-approvers from .github CODEOWNERS (admin-only approval) - Remove .github from complytime-approvers repo access (no non-admin write) - Add --required-admins flags to apply_peribolos.yml for all current admins - Update spec artifacts to document security decisions These changes prevent privilege escalation through the org management repo: only org admins can approve merges (CODEOWNERS gate), and existing admins cannot be removed by peribolos even if a malicious config passes review. Assisted-by: OpenCode (claude-opus-4-6) Signed-off-by: Marcus Burghardt --- .github/CODEOWNERS | 2 +- .github/workflows/apply_peribolos.yml | 3 +++ .../design.md | 18 +++++++++++--- .../specs/codeowners-management/spec.md | 24 ++++++++++--------- .../specs/team-restructuring/spec.md | 7 +++--- .../restructure-teams-and-codeowners/tasks.md | 8 +++++-- peribolos.yaml | 1 - 7 files changed, 42 insertions(+), 21 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 510fe88..edc5ee2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers +* @jflowers @jpower432 @marcusburghardt diff --git a/.github/workflows/apply_peribolos.yml b/.github/workflows/apply_peribolos.yml index 597442f..11df929 100644 --- a/.github/workflows/apply_peribolos.yml +++ b/.github/workflows/apply_peribolos.yml @@ -89,6 +89,9 @@ jobs: --fix-repos --fix-team-repos --min-admins 2 + --required-admins jflowers + --required-admins jpower432 + --required-admins marcusburghardt --require-self=false ) diff --git a/openspec/changes/restructure-teams-and-codeowners/design.md b/openspec/changes/restructure-teams-and-codeowners/design.md index f94987c..70288ea 100644 --- a/openspec/changes/restructure-teams-and-codeowners/design.md +++ b/openspec/changes/restructure-teams-and-codeowners/design.md @@ -86,13 +86,25 @@ in CODEOWNERS files. Rather than deleting `complytime-approvers` and creating a new team, repurpose it with updated membership and repo access. This team grants write access to -non-code repositories: `.github`, `community`, `complytime-demos`, and -`website`. Note that `complytime-dev` does NOT have write access to `.github`; -only `complytime-approvers` provides write access to that repository. +non-code repositories: `community`, `complytime-demos`, and `website`. + +The `.github` repository is explicitly excluded from this team's repo access. +Write access to the org management repo would provide no practical benefit +(contributors can fork to create PRs) while unnecessarily expanding the attack +surface. Only org admins retain write access to `.github` through their admin +role. Membership: jflowers, jpower432, marcusburghardt (maintainers), beatrizmcouto, hbraswelrh (members). +### 5a. Peribolos `--required-admins` for admin removal protection + +The `apply_peribolos.yml` workflow includes `--required-admins` flags for each +current org admin (jflowers, jpower432, marcusburghardt). This causes peribolos +to fail if any of these admins are removed from the `admins:` list in +peribolos.yaml, providing defense-in-depth against admin removal even if a +malicious change passes code review. + ### 6. config_test.go validation strategy Split CODEOWNERS owner parsing into individual users and team references diff --git a/openspec/changes/restructure-teams-and-codeowners/specs/codeowners-management/spec.md b/openspec/changes/restructure-teams-and-codeowners/specs/codeowners-management/spec.md index 54c23b1..6fc08ab 100644 --- a/openspec/changes/restructure-teams-and-codeowners/specs/codeowners-management/spec.md +++ b/openspec/changes/restructure-teams-and-codeowners/specs/codeowners-management/spec.md @@ -16,26 +16,28 @@ https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-f - **THEN** `.github/CODEOWNERS` exists with the ownership rules - **AND** the root `CODEOWNERS` file is deleted -### Requirement: .github repo CODEOWNERS includes complytime-approvers team +### Requirement: .github repo CODEOWNERS lists only org admins -The `.github` repository CODEOWNERS SHALL include both individual admin -users and the `@complytime/complytime-approvers` team as code owners for -all files. The file at `.github/CODEOWNERS` SHALL contain the line: +The `.github` repository CODEOWNERS SHALL list only individual org admin +users as code owners. The `@complytime/complytime-approvers` team SHALL NOT +be included in CODEOWNERS, despite having write access to the repository. +The file at `.github/CODEOWNERS` SHALL contain the line: ``` -* @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers +* @jflowers @jpower432 @marcusburghardt ``` +This separation between write access (via team) and approval authority (via +CODEOWNERS) prevents privilege escalation. With `require_code_owner_review: +true` in the repository ruleset, only org admins can approve merges to +peribolos.yaml and other org management files. + #### Scenario: CODEOWNERS file content validated - **GIVEN** the `.github/CODEOWNERS` file exists in this repository - **WHEN** the file is read -- **THEN** it contains the line - `* @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers` - -Note: GitHub will request review from the individual admins and any member of -the `complytime-approvers` team when a PR is opened. Approval from any one of -them satisfies the CODEOWNERS requirement. +- **THEN** it contains the line `* @jflowers @jpower432 @marcusburghardt` +- **AND** no team references appear in the file ### Requirement: complyctl CODEOWNERS cleaned up diff --git a/openspec/changes/restructure-teams-and-codeowners/specs/team-restructuring/spec.md b/openspec/changes/restructure-teams-and-codeowners/specs/team-restructuring/spec.md index e1b8ef7..7180ba5 100644 --- a/openspec/changes/restructure-teams-and-codeowners/specs/team-restructuring/spec.md +++ b/openspec/changes/restructure-teams-and-codeowners/specs/team-restructuring/spec.md @@ -72,8 +72,8 @@ The peribolos configuration SHALL update `complytime-approvers` with description "Write access to non-code repos for project stakeholders". Maintainers SHALL be `jflowers`, `jpower432`, and `marcusburghardt`. Members SHALL be `beatrizmcouto` and `hbraswelrh`. The team SHALL have write access to -`.github`, `community`, `complytime-demos`, and `website`. The team SHALL NOT -have write access to `complyctl` or `complytime`. +`community`, `complytime-demos`, and `website`. The team SHALL NOT have write +access to `complyctl`, `complytime`, or `.github`. #### Scenario: Team repurposed with updated membership and repos @@ -81,7 +81,8 @@ have write access to `complyctl` or `complytime`. - **WHEN** peribolos.yaml is parsed - **THEN** the team `complytime-approvers` has maintainers `["jflowers", "jpower432", "marcusburghardt"]`, members `["beatrizmcouto", "hbraswelrh"]`, - and repos `.github`, `community`, `complytime-demos`, `website` (all write) + and repos `community`, `complytime-demos`, `website` (all write) +- **AND** `.github` is not in the team's repos list #### Scenario: Previous members removed diff --git a/openspec/changes/restructure-teams-and-codeowners/tasks.md b/openspec/changes/restructure-teams-and-codeowners/tasks.md index 6f89f86..60303b7 100644 --- a/openspec/changes/restructure-teams-and-codeowners/tasks.md +++ b/openspec/changes/restructure-teams-and-codeowners/tasks.md @@ -4,11 +4,11 @@ - [x] 1.2 Create `ampel-provider-approvers` team in peribolos.yaml with `privacy: closed`, maintainers `jpower432` and `marcusburghardt`, members `gvauter`, `hbraswelrh`, `sonupreetam`, `trevor-vaughan`, and `complytime-providers: write` - [x] 1.3 Create `opa-provider-approvers` team in peribolos.yaml with `privacy: closed`, maintainers `jpower432` and `marcusburghardt`, members `fortiz-ai`, `gvauter`, `hbraswelrh`, `sonupreetam`, `trevor-vaughan`, and `complytime-providers: write` - [x] 1.4 Create `complytime-policies-approvers` team in peribolos.yaml with `privacy: closed`, maintainers `jflowers`, `jpower432`, `marcusburghardt`, member `fortiz-ai`, and `complytime-policies: write` -- [x] 1.5 Repurpose `complytime-approvers` in peribolos.yaml: update description to "Write access to non-code repos for project stakeholders", set maintainers to `jflowers`, `jpower432`, `marcusburghardt`, set members to `beatrizmcouto`, `hbraswelrh`, set repos to `.github`, `community`, `complytime-demos`, `website` (all write) +- [x] 1.5 Repurpose `complytime-approvers` in peribolos.yaml: update description to "Write access to non-code repos for project stakeholders", set maintainers to `jflowers`, `jpower432`, `marcusburghardt`, set members to `beatrizmcouto`, `hbraswelrh`, set repos to `community`, `complytime-demos`, `website` (all write). `.github` excluded — no non-admin write access to the org management repo. ## 2. CODEOWNERS for This Repo (.github) -- [x] 2.1 Create `.github/CODEOWNERS` with content: `* @jflowers @jpower432 @marcusburghardt @complytime/complytime-approvers` +- [x] 2.1 Create `.github/CODEOWNERS` with content: `* @jflowers @jpower432 @marcusburghardt` (team excluded for privilege escalation prevention) - [x] 2.2 Delete the root `CODEOWNERS` file if it exists ## 3. CODEOWNERS for complyctl (repo: complyctl) [blocked-by: 1.x applied via peribolos] @@ -32,6 +32,10 @@ - [x] 6.5 Add duplicate check for team references - [x] 6.6 Run `go test ./...` and verify all tests pass (covers privacy:closed, sorted lists, admin-as-maintainer from existing `testTeamMembers` — no new code needed for those) +## 7a. Peribolos Admin Protection (this repo: .github) + +- [x] 7a.1 Add `--required-admins` flags for jflowers, jpower432, and marcusburghardt to apply_peribolos.yml + ## 7. Verification [blocked-by: all prior sections] - [x] 7.1 Run `yamllint peribolos.yaml` and verify no lint errors diff --git a/peribolos.yaml b/peribolos.yaml index e31e32e..8fa80bd 100644 --- a/peribolos.yaml +++ b/peribolos.yaml @@ -118,7 +118,6 @@ orgs: - beatrizmcouto - hbraswelrh repos: - ".github": write community: write complytime-demos: write website: write