From 0e5526acf87ba4de120448e601267ce351327f1e Mon Sep 17 00:00:00 2001 From: shcommit Date: Sat, 5 Sep 2026 23:16:41 +0900 Subject: [PATCH 01/15] chore: pin dev tooling and add a scoped ruff config Pins pytest/pytest-cov/mypy/ruff so Dependabot's pip ecosystem has real versions to track, and adds an E9+F ruff selection scoped to what already passes today (broader rule sets are a follow-up). Co-Authored-By: Claude Sonnet 5 --- pyproject.toml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 58f8e4b..74d282c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,21 @@ dependencies = [] [project.scripts] adr = "scripts.adr:main" +[project.optional-dependencies] +dev = [ + "pytest==8.4.2", + "pytest-cov==7.1.0", + "mypy==1.19.1", + "ruff==0.16.6", +] + +[tool.ruff] +target-version = "py39" +extend-exclude = ["docs/decisions"] + +[tool.ruff.lint] +select = ["E9", "F"] + [tool.setuptools] packages = ["scripts", "scripts.core", "scripts.commands", "scripts.rules"] package-dir = {"scripts" = "skills/adr-toolkit/scripts"} From 15bfc96581f1b5510e1678b080ff4e816a8139ac Mon Sep 17 00:00:00 2001 From: shcommit Date: Sat, 5 Sep 2026 23:19:42 +0900 Subject: [PATCH 02/15] fix: resolve pre-existing ruff findings (unused imports/variable) Clears the codebase for the new lint CI job added in a later task. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01TAZPZwzSutmru6kSCEogu6 --- scripts/adoption_metrics/reporting.py | 2 -- scripts/verify_examples.py | 4 +--- skills/adr-toolkit/scripts/core/atomic_io.py | 1 - tests/integration/test_index_markdown_injection.py | 1 - tests/unit/test_config.py | 1 - tests/unit/test_dependency_scanner.py | 1 - tests/unit/test_identifiers.py | 1 - tests/unit/test_repository_paths.py | 1 - 8 files changed, 1 insertion(+), 11 deletions(-) diff --git a/scripts/adoption_metrics/reporting.py b/scripts/adoption_metrics/reporting.py index d58bd23..9bcf4ad 100644 --- a/scripts/adoption_metrics/reporting.py +++ b/scripts/adoption_metrics/reporting.py @@ -2,8 +2,6 @@ import argparse import json -import subprocess -import sys from datetime import datetime, timezone from pathlib import Path from typing import Any, Dict, List, Optional diff --git a/scripts/verify_examples.py b/scripts/verify_examples.py index acc33e1..d2b5b9a 100644 --- a/scripts/verify_examples.py +++ b/scripts/verify_examples.py @@ -13,8 +13,6 @@ import argparse import json -import os -import re import subprocess import sys import tempfile @@ -250,7 +248,7 @@ def main(argv=None) -> int: parser = argparse.ArgumentParser(description="Verify and sync examples/*.md with adr.py") parser.add_argument("--check", action="store_true", help="Check that examples are executable and valid") parser.add_argument("--update", action="store_true", help="Auto-update examples if needed") - args = parser.parse_args(argv) + parser.parse_args(argv) if hasattr(sys.stdout, "reconfigure"): sys.stdout.reconfigure(encoding="utf-8", errors="backslashreplace") diff --git a/skills/adr-toolkit/scripts/core/atomic_io.py b/skills/adr-toolkit/scripts/core/atomic_io.py index 8b38115..0bdc9aa 100644 --- a/skills/adr-toolkit/scripts/core/atomic_io.py +++ b/skills/adr-toolkit/scripts/core/atomic_io.py @@ -17,7 +17,6 @@ from contextlib import contextmanager from pathlib import Path from typing import Any, Iterator -from types import FrameType if sys.platform == "win32": import msvcrt diff --git a/tests/integration/test_index_markdown_injection.py b/tests/integration/test_index_markdown_injection.py index f182bc8..39bc19c 100644 --- a/tests/integration/test_index_markdown_injection.py +++ b/tests/integration/test_index_markdown_injection.py @@ -1,7 +1,6 @@ """Proves INDEX's generated README cannot be split into a second, attacker-controlled link via an ADR title (docs/adr-toolkit-audit-report.md, Top-3 #2).""" -from pathlib import Path from types import SimpleNamespace from scripts.commands import index diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index d986376..3d7faa1 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -1,5 +1,4 @@ import json -import os import pytest diff --git a/tests/unit/test_dependency_scanner.py b/tests/unit/test_dependency_scanner.py index 1ef8a8f..07f5a94 100644 --- a/tests/unit/test_dependency_scanner.py +++ b/tests/unit/test_dependency_scanner.py @@ -1,4 +1,3 @@ -from pathlib import Path from scripts.evidence import dependency_scanner diff --git a/tests/unit/test_identifiers.py b/tests/unit/test_identifiers.py index f105801..f09a757 100644 --- a/tests/unit/test_identifiers.py +++ b/tests/unit/test_identifiers.py @@ -1,4 +1,3 @@ -from pathlib import Path import pytest diff --git a/tests/unit/test_repository_paths.py b/tests/unit/test_repository_paths.py index c0f2997..512a513 100644 --- a/tests/unit/test_repository_paths.py +++ b/tests/unit/test_repository_paths.py @@ -1,6 +1,5 @@ """Tests for resolve_from_root's boundary enforcement (docs/adr-toolkit-audit-report.md §2.2 2.3).""" -from pathlib import Path import pytest From 819efd1a388b9240d47794cbc6dfce8fd6054bf4 Mon Sep 17 00:00:00 2001 From: shcommit Date: Sat, 5 Sep 2026 23:20:21 +0900 Subject: [PATCH 03/15] docs: add OSS repo governance & hardening spec and implementation plan Records the audit findings (repo public since 2026-08-29 with v1.0.1 released but zero branch protection, develop/master release drift, missing label taxonomy/dependabot/issue forms/lint job) and the NOW-tier implementation plan those findings led to. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01TAZPZwzSutmru6kSCEogu6 --- ...05-oss-repo-governance-hardening-design.md | 266 ++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-05-oss-repo-governance-hardening-design.md diff --git a/docs/superpowers/specs/2026-09-05-oss-repo-governance-hardening-design.md b/docs/superpowers/specs/2026-09-05-oss-repo-governance-hardening-design.md new file mode 100644 index 0000000..39a119b --- /dev/null +++ b/docs/superpowers/specs/2026-09-05-oss-repo-governance-hardening-design.md @@ -0,0 +1,266 @@ +# OSS Repository Governance & Hardening Design + +## Scope + +Make ADR Toolkit's GitHub operations (issue/PR triage, labels, dependency +updates, CI gates, branch protection, contributor onboarding) hold up if +issues and PRs grow from single digits into the hundreds, without adding +automation the current one-maintainer, one-repository reality doesn't need +yet. Covers `.github/**` configuration, CI workflow structure, and the +GitHub-side settings that can't live in code. Does not change ADR Toolkit's +product code, CLI behavior, or `docs/decisions/` governance model. + +## Context — audit findings as of 2026-09-05 + +Re-verified against the live repository (`gh api`/`gh repo view`), not just +file presence, because `docs/enterprise-adoption.md` §4/§8 and +`improvements.md`'s "Low" backlog describe this repo as still private with a +single precondition ("저장소 public 전환") blocking the public-readiness gate. +That precondition has already been met and those documents are now stale: + +- The repository has been **public since 2026-08-29**, and `master` has + already shipped `v1.0.0` and `v1.0.1` (PyPI publishing pipeline, antigravity + harness-parity CI, a Windows lock-metadata fix, mypy `--strict` fix). +- Despite being public, `master` has **zero branch protection** + (`gh api repos/.../branches/master/protection` → `404 Branch not + protected`). `develop` is unprotected too. `docs/enterprise-adoption.md` + §9's "Public" stage completion condition ("ruleset API 검증") is therefore + **not actually satisfied**, even though the PR template, `CONTRIBUTING.md`, + and `SECURITY.md` pieces of that same stage are done. +- `develop` was frozen at the `v0.3.2` sync point while `master` advanced + through the `v1.0.0`/`v1.0.1` releases — the git-flow-mandated + release-branch back-merge into `develop` never happened. **Fixed as a + prerequisite to this spec** via `chore/sync-develop-with-v1.0.1` (PR #17, + not yet merged at spec time) — this branch is based on that synced state. +- No `dependabot.yml`, no `CODEOWNERS`, no path-based auto-labeler. Labels are + the unmodified GitHub defaults (`bug`, `enhancement`, `documentation`, + `good first issue`, `help wanted`, `question`, `duplicate`, `invalid`, + `wontfix`, `accessibility`) — no `type:`/`area:`/`priority:`/`size:` axis at + all, so nothing about a large backlog would currently self-organize. + Issue templates are legacy Markdown (`bug_report.md`, `feature_request.md`), + not structured Issue Forms. +- CI (`.github/workflows/test.yml`) is already more mature than a typical + small OSS repo: multi-OS/multi-Python pytest matrix, a coverage floor + (`--cov-fail-under=85`), a scoped mypy `--strict` job, generated-file drift + checks (`sync_version.py --check`, `verify_examples.py --check`), a + Conventional-Commit PR title gate, and a `harness-parity` job that installs + the real Codex/Gemini CLIs against this repo. There is **no lint job** at + all (no ruff/flake8/black anywhere in the repo) and no dependency/security + scan step. +- `harness-parity` contains one remote-installer pattern the request asked to + scrutinize: `curl -fsSL https://antigravity.google/cli/install.sh | bash`. + (Antigravity itself is manually verified only, per + `adapters/antigravity/README.md` — this curl step doesn't currently run in + CI for that adapter.) +- GitHub Actions are pinned to major-version tags from verified publishers + (`actions/*@v4/@v5`, `softprops/action-gh-release@v2`, + `pypa/gh-action-pypi-publish@release/v1`) — not full commit-SHA pinning, but + not `@latest`/`@master` either. `release.yml` already scopes permissions + per-job (`contents: write`, `id-token: write`, `attestations: write`) and + `test.yml` sets repo-wide `permissions: contents: read`; least-privilege is + already largely in place, not a gap. +- Dependency ecosystems actually in use: Python (`pyproject.toml`) and GitHub + Actions. No `package.json` anywhere — the `npm install -g` calls in + `harness-parity` install pinned global CLIs for testing, not a project + dependency Dependabot should manage. No Docker. +- `project-roadmap.md`'s "Public and enterprise governance" section and + `docs/enterprise-adoption.md` §8 already correctly defer **mandatory** + CODEOWNERS review and organization-wide ruleset/reusable-workflow work + behind explicit preconditions (2+ qualified maintainers; 2+ repositories) + that still aren't met. This spec does not revisit that call — see + Out of Scope. + +## Problem Statement + +The repository's day-to-day contributor mechanics (label triage, dependency +freshness, PR gating, branch protection) were never built out because the +project was small and private. It is now public with two releases shipped, +but none of the operational scaffolding that keeps a growing issue/PR queue +navigable for a single maintainer exists yet, and the one piece of the +public-readiness plan that mattered most for actually protecting the release +history — branch protection — silently never got applied when the repo went +public. Left as-is, the first sign of trouble will be either an unreviewed +force-push/deletion incident on `master`, or a backlog of unlabeled, +untriaged issues and PRs once external contributors show up. + +## Solution + +Add the minimum GitHub-native automation that lets issues and PRs +self-organize (path-based labels, a label taxonomy, Dependabot, Issue Forms) +and close the branch-protection gap that public status already requires, +while explicitly deferring anything that assumes a maintainer team or an +issue volume this repository doesn't have yet (mandatory CODEOWNERS review, +stale-bot, org-wide rulesets). Every "not now" gets a written trigger +condition instead of a vague "later," matching how `project-roadmap.md` and +`docs/enterprise-adoption.md` already record deferred work in this repo. + +## User Stories + +1. As the sole maintainer, I want new issues/PRs to arrive pre-labeled by the + file paths they touch so I don't manually triage every one. +2. As the sole maintainer, I want dependency and Action version bumps to + arrive as routine, reviewable PRs instead of silent drift or a manual audit. +3. As a first-time contributor, I want a structured bug/feature form instead + of a blank Markdown template, so I give the maintainer what's needed on + the first pass. +4. As the sole maintainer, I want `master`/`develop`/`v*` tags protected from + force-push and deletion now that the repo is public, without being forced + into mandatory independent code-owner review I can't actually staff. +5. As a future contributor, I want a small set of well-labeled "good first + issue" candidates to exist so I know where to start. +6. As the sole maintainer, I want CI to catch missing lint/security-scan + coverage that today has no job at all, without turning every PR into a + slow, heavyweight gate. + +## Decisions + +### NOW — apply in this branch + +- **Path-based auto-labeler**: a labeler config mapping changed-file globs to + `area:*` labels (e.g. `skills/adr-toolkit/**` / `scripts/**` → `area:core`, + `adapters/**` → `area:adapter`, `.github/**` → `area:github`, `docs/**` → + `area:docs`, `tests/**` → `type:test`), run by a workflow triggered on + `pull_request_target` with `contents: read`/`pull-requests: write` only. +- **Label taxonomy**: introduce `type:*`, `area:*`, `priority:*`, `size:*`, + plus `needs-triage` and `blocked`, sized to this repo's actual directories + (`core`/`cli`/`adapter`/`github`/`docs`) rather than the generic list + verbatim. Keep the existing default labels (`bug`, `enhancement`, + `good first issue`, `help wanted`, `documentation`) rather than replacing + them, since GitHub's defaults already cover part of `type:*`/`area:docs` + and dropping them would break any existing issue/PR history referencing + them. +- **Dependabot**: `pip` (root `pyproject.toml`) and `github-actions` + ecosystems only — no `npm`/`docker` entries, since neither has a manifest + Dependabot could act on. Weekly schedule, grouped updates, auto-created + PRs. +- **Issue Forms**: replace the two Markdown templates with structured YAML + forms (bug report: version, OS, environment, affected + component/adapter, expected/actual behavior, repro steps, logs; feature + request: problem, use case, proposed solution, alternatives, affected + area, contribution willingness), plus a `config.yml` pointing to + Discussions/`SECURITY.md` for questions and vulnerabilities instead of a + blank issue. +- **Lint CI job**: add a fast lint job (the repo has no lint tool configured + at all today) to the existing fast-gate jobs in `test.yml`, separate from + the slower `harness-parity` job. +- **Dependency/security scan**: add a lightweight `pip-audit`-style check for + the Python dependency surface as a fast-gate job. +- **Branch protection / ruleset** (GitHub UI/API, not a file in this repo — + see the UI section below): require PR + passing required checks, + block force-push and branch deletion on `master` and `develop`, restrict + `v*` tag creation/deletion, require conversation resolution. Do **not** + require code-owner review or signed commits yet — no second qualified + maintainer exists to review against, and no CODEOWNERS file exists to + reference. +- **Update stale docs**: correct `docs/enterprise-adoption.md` §4/§8's + "저장소가 아직 private" framing and `improvements.md`'s already-removed + precondition note now that the public transition and its ruleset gate are + being closed out, so the next reader doesn't re-derive the same stale + premise this session had to correct first. + +### NEXT — once issue/PR volume or contributor count actually grows + +- CODEOWNERS file (drafted but **not** wired to mandatory review) — ready to + flip on once 2+ qualified maintainers exist, per the existing decision in + `docs/enterprise-adoption.md` §4 and `project-roadmap.md`. Do not enable + required review as part of this spec. +- GitHub Projects board with an Inbox → Backlog → Ready → In Progress → + Review → Blocked → Done flow and `Priority`/`Area`/`Size` custom fields, + once there's enough issue volume for a Kanban view to earn its keep over a + flat issue list. +- PR size labeling (`size:XS`…`size:XL`) as a workflow addition once PR + volume makes "the queue is large, which PRs are actually small" a real + question. +- Splitting `test.yml` into an explicit Fast Gate / Integration Gate + workflow pair, once the existing single-workflow job list gets unwieldy + enough that PR authors need the distinction visible rather than just + reading job names. + +### LATER — only once scale actually demands it + +- Stale issue/PR bot. Current issue/PR count doesn't warrant it — explicit + decision **not to adopt now**, matching how this repo already declines + automation it doesn't need (`docs/enterprise-adoption.md` §8 "지금 구현하지 + 않을 것"). Revisit once there's a real backlog of abandoned issues, with + exclusions for `security`, `pinned`, `roadmap`, `help wanted`, + `priority:P0`, and `blocked`. +- Organization-level rulesets, reusable workflows, RBAC, audit export — all + already correctly gated behind "2+ repositories" in `improvements.md`; + this spec doesn't touch that precondition. +- Advanced/ML-based triage bots, automatic contributor assignment, complex + multi-stage release trains. + +## Testing / Validation Decisions + +- Labeler config: validate with a dry-run against recent merged PRs' + changed-file lists to confirm expected labels before enabling the workflow + for real. +- Dependabot: confirm via a manual `dependabot.yml` schema check (GitHub + validates on push) and by watching the first scheduled run produce the + expected grouped PRs. +- Issue Forms: manually preview each form in GitHub's issue-form preview + before merging; forms have no automated test surface. +- New CI jobs (lint, dependency/security scan): must pass on this branch's + own diff and not regress the existing 515 unit tests or `sync_version.py + --check` / `verify_examples.py --check` drift gates. +- Branch protection: after applying via UI/API, re-query + `gh api repos/SHcommit/ADR-toolkit/branches//protection` and paste + the actual response into the audit doc — this spec's own investigation + showed a prior "done" claim (docs saying the public gate was satisfied) + was false, so the closing step here is re-verification, not another + written claim. + +## Out of Scope + +- Mandatory CODEOWNERS-backed independent review (no second qualified + maintainer yet — explicit existing decision, not revisited here). +- Organization-level rulesets, reusable workflows, audit export, central + taxonomy (single-repository precondition unmet — explicit existing + decision, not revisited here). +- Stale-bot / auto-triage-bot / auto-assignment automation (volume doesn't + warrant it yet). +- Signed-commit requirements. +- Any change to ADR Toolkit's product code, CLI, or `docs/decisions/` + governance model. +- Converting the Antigravity `curl | bash` install step into an + artifact+checksum flow — that step doesn't currently run for the + Antigravity adapter in CI (manual-only per + `adapters/antigravity/README.md`); if/when Antigravity CI verification is + added (tracked in `improvements.md`, blocked on Antigravity publishing to a + package registry), that installer should be revisited then, not + speculatively hardened now for a path nothing currently executes. +- Full commit-SHA pinning for all third-party Actions (current major-version + tag pinning from verified publishers is a reasonable middle ground for this + repo's risk level; revisit only if a specific tag-mutation incident in one + of the pinned Actions ever demonstrates the gap matters). + +## Open Questions + +- Should PR #17 (the develop/master sync) merge before or independently of + this branch's PR? This branch is based on its tip either way, so either + order is safe, but the maintainer should decide merge order. +- Exact `priority:*` and `size:*` thresholds (e.g. what LOC/file-count counts + as `size:M` vs `size:L`) — left to the maintainer's judgment at + implementation time rather than guessed here. +- Whether `needs-triage` should auto-apply to every new issue/PR via the + labeler workflow, or only to issues (PRs already get `area:*` + + `pr-title-check`) — implementation-time call. + +## Further Notes + +- Rollout order: branch-protection/ruleset (GitHub UI/API, highest + risk-reduction per unit effort) → Dependabot + auto-labeler (lowest + maintenance cost) → Issue Forms → new CI jobs (lint, dependency scan) → + stale-docs correction, so the highest-value, lowest-risk items land first + and the doc correction reflects the final state rather than needing a + second pass. +- The public-transition gap found here (public since 2026-08-29, but branch + protection never applied, and `docs/enterprise-adoption.md`/`improvements.md` + both still describing the repo as private) should be treated as the + headline finding when this work is reported back — it's a real exposure + window on a public repo with release tags, not a paperwork gap. +- This spec intentionally does not propose a `.github/labeler.yml` / + `dependabot.yml` schema inline — implementation will write those files + directly against this repo's actual directory names, which is faster to + verify than hand-transcribing YAML into a design doc that could drift from + the real paths by the time it's implemented. From 7b5e9685a0a1bd4395f55c98ad1ed6c278f45284 Mon Sep 17 00:00:00 2001 From: shcommit Date: Sat, 5 Sep 2026 23:23:03 +0900 Subject: [PATCH 04/15] feat(github): add label taxonomy as code type:/area:/priority:/size: labels plus needs-triage and blocked, synced from .github/labels.yml by EndBug/label-sync on push to develop or manual dispatch. Keeps existing default labels rather than deleting them. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01TAZPZwzSutmru6kSCEogu6 --- .github/labels.yml | 91 ++++++++++++++++++++++++++++++++++++ .github/workflows/labels.yml | 22 +++++++++ 2 files changed, 113 insertions(+) create mode 100644 .github/labels.yml create mode 100644 .github/workflows/labels.yml diff --git a/.github/labels.yml b/.github/labels.yml new file mode 100644 index 0000000..f21259e --- /dev/null +++ b/.github/labels.yml @@ -0,0 +1,91 @@ +# Label taxonomy for ADR Toolkit. Synced to the live repo by +# .github/workflows/labels.yml via EndBug/label-sync. Add or edit labels +# here, not in the GitHub UI — UI-only edits are overwritten on the next +# sync run. + +# --- type: what kind of change/report --- +- name: "type:bug" + color: "d73a4a" + description: "Something isn't working" +- name: "type:feature" + color: "a2eeef" + description: "New feature or request" +- name: "type:docs" + color: "0075ca" + description: "Documentation only" +- name: "type:refactor" + color: "cfd3d7" + description: "Code change with no behavior change" +- name: "type:test" + color: "bfdadc" + description: "Test-only change" + +# --- area: which part of the repo --- +- name: "area:core" + color: "5319e7" + description: "skills/adr-toolkit/scripts core and CLI commands" +- name: "area:cli" + color: "5319e7" + description: "adr.py entrypoint and command wiring" +- name: "area:adapter" + color: "5319e7" + description: "adapters/** harness adapters (Claude, Codex, Gemini, Antigravity)" +- name: "area:github" + color: "5319e7" + description: ".github/** workflows, templates, and repo automation" +- name: "area:docs" + color: "5319e7" + description: "docs/**, README, CONTRIBUTING, and other project documentation" + +# --- priority --- +- name: "priority:P0" + color: "b60205" + description: "Drop everything -- breaks release, security, or data integrity" +- name: "priority:P1" + color: "d93f0b" + description: "Should land in the next release" +- name: "priority:P2" + color: "fbca04" + description: "Normal priority, no target release yet" + +# --- workflow state --- +- name: "needs-triage" + color: "ededed" + description: "Not yet reviewed by a maintainer" +- name: "blocked" + color: "000000" + description: "Waiting on an external dependency or decision" + +# --- size (applied manually at review time, not automated in this plan) --- +- name: "size:XS" + color: "c2e0c6" + description: "Trivial change" +- name: "size:S" + color: "c2e0c6" + description: "Small, single-concern change" +- name: "size:M" + color: "fef2c0" + description: "Moderate change, multiple files" +- name: "size:L" + color: "f9d0c4" + description: "Large change, review carefully" +- name: "size:XL" + color: "e99695" + description: "Very large -- consider splitting" + +# --- kept from GitHub's defaults: still useful, not replaced --- +- name: "good first issue" + color: "7057ff" + description: "Good for newcomers" +- name: "help wanted" + color: "008672" + description: "Extra attention is needed" +- name: "duplicate" + color: "cfd3d7" + description: "This issue or pull request already exists" +- name: "invalid" + color: "e4e669" + description: "This doesn't seem right" +- name: "wontfix" + color: "ffffff" + description: "This will not be worked on" diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml new file mode 100644 index 0000000..cd77ff4 --- /dev/null +++ b/.github/workflows/labels.yml @@ -0,0 +1,22 @@ +name: sync-labels + +on: + push: + branches: [develop] + paths: [".github/labels.yml"] + workflow_dispatch: {} + +permissions: + contents: read + issues: write + pull-requests: write + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: EndBug/label-sync@52074158190acb45f3077f9099fea818aa43f97a # v2.3.3 + with: + config-file: .github/labels.yml + delete-other-labels: false From 51af064c2ebda48db19979622c3c06c6f88abc0a Mon Sep 17 00:00:00 2001 From: shcommit Date: Sat, 5 Sep 2026 23:25:07 +0900 Subject: [PATCH 05/15] feat(github): auto-label PRs by changed path, tag new issues needs-triage Co-Authored-By: Claude Sonnet 5 --- .github/labeler.yml | 33 ++++++++++++++++++++++++++++++++ .github/workflows/labeler.yml | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/labeler.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..c98869c --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,33 @@ +"area:core": + - changed-files: + - any-glob-to-any-file: + - "skills/adr-toolkit/scripts/**" + - "scripts/**" + +"area:adapter": + - changed-files: + - any-glob-to-any-file: + - "adapters/**" + - ".claude-plugin/**" + +"area:github": + - changed-files: + - any-glob-to-any-file: + - ".github/**" + +"area:docs": + - changed-files: + - any-glob-to-any-file: + - "docs/**" + - "*.md" + +"type:test": + - changed-files: + - any-glob-to-any-file: + - "tests/**" + +"type:docs": + - changed-files: + - any-glob-to-any-file: + - "docs/**" + - "*.md" diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 0000000..02f2074 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,36 @@ +name: labeler + +on: + pull_request_target: + types: [opened, synchronize, reopened] + issues: + types: [opened] + +permissions: + contents: read + pull-requests: write + issues: write + +jobs: + label-pr: + if: github.event_name == 'pull_request_target' + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v7 + with: + configuration-path: .github/labeler.yml + sync-labels: false + + triage-issue: + if: github.event_name == 'issues' + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + with: + script: | + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ["needs-triage"], + }); From 2e8674fa9ee3298c6a05e6f370bf8b48630a1dca Mon Sep 17 00:00:00 2001 From: shcommit Date: Sat, 5 Sep 2026 23:27:13 +0900 Subject: [PATCH 06/15] feat(github): convert issue templates to structured Issue Forms Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01TAZPZwzSutmru6kSCEogu6 --- .github/ISSUE_TEMPLATE/bug_report.md | 39 ----------- .github/ISSUE_TEMPLATE/bug_report.yml | 79 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 8 +++ .github/ISSUE_TEMPLATE/feature_request.md | 19 ------ .github/ISSUE_TEMPLATE/feature_request.yml | 54 +++++++++++++++ 5 files changed, 141 insertions(+), 58 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 2c4e92b..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -name: Bug report -about: Report a defect in ADR Toolkit -title: "" -labels: bug ---- - -## Summary - -## Steps to Reproduce - -1. -2. -3. - -## Expected Behavior - -## Actual Behavior - -## Environment - -- Harness: Claude Code / Codex CLI / Gemini CLI / Antigravity CLI / generic / other -- ADR Toolkit version (`skills/adr-toolkit/VERSION`): -- OS: - -## Relevant Output - -Paste the command and its output, including `--json` output where -applicable. - -``` -``` - -## Scope - -- [ ] This affects `adr.py validate`, `create`, `index`, `check`, `graph`, - or `search`. -- [ ] This affects a specific harness adapter (Codex, Gemini CLI, - Antigravity, or generic). diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..891306e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,79 @@ +name: Bug Report +description: Report something that isn't working as expected +title: "[Bug]: " +labels: ["needs-triage", "type:bug"] +body: + - type: input + id: version + attributes: + label: Version + description: "Output of `adr --version` or the release tag you're on" + placeholder: "v1.0.1" + validations: + required: true + - type: dropdown + id: os + attributes: + label: Operating System + options: + - Linux + - macOS + - Windows + - Other (specify in Additional context) + validations: + required: true + - type: dropdown + id: environment + attributes: + label: Environment + description: How you're running ADR Toolkit + options: + - Claude Code + - Codex CLI + - Gemini CLI + - Antigravity CLI + - Direct CLI (pip install / python script) + - Other (specify in Additional context) + validations: + required: true + - type: input + id: component + attributes: + label: Affected component / adapter + description: "e.g. `adr.py check`, `adapters/codex`, ADR index generation" + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual behavior + validations: + required: true + - type: textarea + id: repro + attributes: + label: Reproduction steps + description: Exact commands, in order + render: shell + validations: + required: true + - type: textarea + id: logs + attributes: + label: Logs + description: "Output of the failing command with `--verbose` or `--debug` if available" + render: shell + validations: + required: false + - type: textarea + id: additional + attributes: + label: Additional context + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..e9bb723 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Security vulnerability + url: https://github.com/SHcommit/ADR-toolkit/security/policy + about: Please report vulnerabilities privately per SECURITY.md, not as a public issue. + - name: Question / discussion + url: https://github.com/SHcommit/ADR-toolkit/discussions + about: For open-ended questions that aren't a bug or a concrete feature proposal. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 4f7f064..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: Feature request -about: Propose new functionality for ADR Toolkit -title: "" -labels: enhancement ---- - -## Problem - -What are you trying to do that ADR Toolkit does not support today? - -## Proposed Solution - -## Alternatives Considered - -## Additional Context - -- [ ] I checked `project-roadmap.md` and `improvements.md` for existing - plans that already cover this. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..c3549a8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,54 @@ +name: Feature Request +description: Propose a new capability or improvement +title: "[Feature]: " +labels: ["needs-triage", "type:feature"] +body: + - type: textarea + id: problem + attributes: + label: Problem + description: What can't you do today, or what's needlessly painful? + validations: + required: true + - type: textarea + id: use-case + attributes: + label: Use case + description: The concrete scenario where this problem shows up + validations: + required: true + - type: textarea + id: solution + attributes: + label: Proposed solution + validations: + required: false + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + validations: + required: false + - type: dropdown + id: area + attributes: + label: Affected area + options: + - Core / CLI (scripts, adr.py) + - Adapter (Claude / Codex / Gemini / Antigravity) + - GitHub automation (.github/**) + - Documentation + - Other + validations: + required: true + - type: dropdown + id: contribution + attributes: + label: Contribution willingness + description: Would you be willing to open a PR for this yourself? + options: + - "Yes, I'd like to implement this" + - "Maybe, with guidance" + - "No, just proposing the idea" + validations: + required: true From a574e2e998237c5de16917cbb93963c7751a868b Mon Sep 17 00:00:00 2001 From: shcommit Date: Sat, 5 Sep 2026 23:28:55 +0900 Subject: [PATCH 07/15] feat(github): add dependabot for pip and github-actions ecosystems Co-Authored-By: Claude Sonnet 5 --- .github/dependabot.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ade07ae --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,21 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + groups: + dev-dependencies: + patterns: + - "*" + open-pull-requests-limit: 5 + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + groups: + actions: + patterns: + - "*" + open-pull-requests-limit: 5 From 925472d553ad38ea616121b9b987dcf294a5d17a Mon Sep 17 00:00:00 2001 From: shcommit Date: Sat, 5 Sep 2026 23:53:19 +0900 Subject: [PATCH 08/15] ci: add lint and dependency-audit jobs; drop Python 3.9, fix pytest CVE Lint is scoped to E9+F (already clean, prior commit); broader ruff rule sets are a follow-up, not bundled here. Dependency audit installs the pinned dev toolchain directly, not this project's own unpublished package (pip-audit's --strict and --skip-editable can never coexist, so the audited environment simply never includes anything editable), and upgrades pip first to clear pip's own known CVEs. pytest==8.4.2 carries a real CVE (CVE-2025-71176 / PYSEC-2026-1845, local /tmp directory hijack) whose fix requires pytest>=9.0.3, which in turn requires Python>=3.10. Decided (human call, project has no users yet) to drop Python 3.9 support rather than carry a permanent ignore-vuln exception: requires-python, classifiers, ruff target-version, and the CI matrix all move to 3.10 as the floor. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01TAZPZwzSutmru6kSCEogu6 --- .github/workflows/test.yml | 44 ++++++++++++++++++++++++++++++++------ pyproject.toml | 7 +++--- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9de48b3..7786804 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,17 +19,14 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.9", "3.12"] - exclude: - - os: macos-latest - python-version: "3.9" + python-version: ["3.10", "3.12"] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies - run: pip install pytest pytest-cov + run: pip install -e ".[dev]" - name: Run tests run: python -m pytest tests/unit tests/integration -v --cov=skills/adr-toolkit/scripts --cov-branch --cov-report=term-missing --cov-fail-under=85 @@ -41,7 +38,7 @@ jobs: with: python-version: "3.12" - name: Install mypy - run: pip install mypy + run: pip install -e ".[dev]" - name: Type-check the fully-typed core modules run: >- mypy @@ -50,6 +47,41 @@ jobs: skills/adr-toolkit/scripts/core/contracts.py --strict + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install ruff + run: pip install -e ".[dev]" + - name: Lint (E9 + F -- syntax errors and pyflakes only for now) + run: ruff check . + + dependency-audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Upgrade pip (fixes known pip CVEs before auditing anything else) + run: python -m pip install --upgrade pip + - name: Install pinned dev tools (not the project itself) + # Deliberately skips `pip install -e .`: adr-toolkit isn't + # published to PyPI (confirmed 404 on pypi.org/pypi/adr-toolkit/json), + # so pip-audit can never resolve it there, and --skip-editable + # can't be combined with --strict (pip-audit treats every skipped + # dependency as fatal under --strict, unconditionally -- see + # pip_audit/_cli.py). This job audits the pinned dev toolchain, + # not this project's own unpublished package. + run: pip install pytest==9.1.1 pytest-cov==7.1.0 mypy==1.19.1 ruff==0.16.6 + - name: Install pip-audit + run: pip install pip-audit==2.9.0 + - name: Audit installed dev dependencies for known vulnerabilities + run: pip-audit --strict + version-drift: runs-on: ubuntu-latest steps: diff --git a/pyproject.toml b/pyproject.toml index 74d282c..92daaf4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,14 +14,13 @@ classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: Software Development :: Quality Assurance", "Topic :: Software Development :: Documentation", ] -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [] [project.scripts] @@ -29,14 +28,14 @@ adr = "scripts.adr:main" [project.optional-dependencies] dev = [ - "pytest==8.4.2", + "pytest==9.1.1", "pytest-cov==7.1.0", "mypy==1.19.1", "ruff==0.16.6", ] [tool.ruff] -target-version = "py39" +target-version = "py310" extend-exclude = ["docs/decisions"] [tool.ruff.lint] From 8edd52b8eb2b2f06db18712450499d078cc05b74 Mon Sep 17 00:00:00 2001 From: shcommit Date: Sat, 5 Sep 2026 23:59:10 +0900 Subject: [PATCH 09/15] docs(ci): note dev-tool pin sync requirement in dependency-audit job The dependency-audit job can't install via `.[dev]` (it must not install this project's own unpublished package -- see the existing comment), so its pytest/pytest-cov/mypy/ruff versions are a manual copy of pyproject.toml's dev extra. Dependabot only updates the extra, not this workflow file, so flag the duplication for whoever bumps one and not the other. Also verified the full CI pytest invocation (with --cov-fail-under=85) under the pytest==9.1.1 pin, not just the bare test run from the prior commit's verification. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01TAZPZwzSutmru6kSCEogu6 --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7786804..19c0952 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -69,6 +69,9 @@ jobs: - name: Upgrade pip (fixes known pip CVEs before auditing anything else) run: python -m pip install --upgrade pip - name: Install pinned dev tools (not the project itself) + # KEEP IN SYNC with pyproject.toml's [project.optional-dependencies].dev -- + # this job can't use `pip install -e ".[dev]"` (see below), so these + # versions are a manual copy Dependabot won't update here. # Deliberately skips `pip install -e .`: adr-toolkit isn't # published to PyPI (confirmed 404 on pypi.org/pypi/adr-toolkit/json), # so pip-audit can never resolve it there, and --skip-editable From cc4b9bdcef68059315aeb784ac24578878dfdac6 Mon Sep 17 00:00:00 2001 From: shcommit Date: Sun, 6 Sep 2026 00:00:53 +0900 Subject: [PATCH 10/15] docs(github): add dormant CODEOWNERS draft (not wired to required review) Co-Authored-By: Claude Sonnet 5 --- .github/CODEOWNERS | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..c7f72d4 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,13 @@ +# This file has NO effect on required reviews today -- "Require review from +# Code Owners" is intentionally left OFF in branch protection (see +# docs/superpowers/specs/2026-09-05-oss-repo-governance-hardening-design.md, +# "Out of Scope": a single-maintainer repo can't satisfy independent +# code-owner review, so enabling this would either block merges or produce +# meaningless self-review). +# +# This file exists so that turning required review on later (once a second +# qualified maintainer exists, per project-roadmap.md's "Public and +# enterprise governance" section) is a one-line branch-protection change, +# not a design task done under time pressure. + +* @SHcommit From 03e72c57168a1f5bda0f0f9bdf30b20ad6f9c939 Mon Sep 17 00:00:00 2001 From: shcommit Date: Sun, 6 Sep 2026 00:04:06 +0900 Subject: [PATCH 11/15] docs: correct stale 'still private' framing now that the repo is public The public transition happened 2026-08-29 with v1.0.0/v1.0.1 already released, but branch protection was never applied -- update the docs to reflect actual re-verified state instead of the old plan. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01TAZPZwzSutmru6kSCEogu6 --- docs/enterprise-adoption.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/enterprise-adoption.md b/docs/enterprise-adoption.md index 0086ac5..78652ff 100644 --- a/docs/enterprise-adoption.md +++ b/docs/enterprise-adoption.md @@ -62,8 +62,10 @@ CI 통과는 구조화된 정책의 증거일 뿐, 결정의 사업적 타당성 ## 4. 공개 저장소 전환 Gate -사용자가 계획한 public 전환 직후 다음 repository ruleset을 적용하고 API로 실제 -상태를 재조회한다. +저장소는 2026-08-29부터 public이다. 아래 repository ruleset은 이미 적용 +대상이며, 적용 후 API로 실제 상태를 재조회해 확인한다 (2026-09-05 확인 결과 +`master`/`develop` 모두 branch protection이 없는 상태 -- 이 문서 §9 "Public" +단계 완료 조건이 아직 충족되지 않았다). | 대상 | 권장 통제 | 도입 이유 | | --- | --- | --- | @@ -167,8 +169,13 @@ Toolkit 자체에 계정 시스템을 서둘러 넣기보다 GitHub의 인증· 1. ~~ADR directory 로딩을 공통 iterator로 통합해~~ **완료 (2026-08-30).** `core.adr_directory.iter_adr_files`로 validate/index/related/CHECK를 통합했고 각 명령의 warning 의미는 그대로 유지했다. -2. public 전환 시 PR template과 CONTRIBUTING/SECURITY 문서를 만들고 ruleset 적용을 - 자동 검증한다. **저장소가 아직 private이라 시작 조건이 충족되지 않았다.** +2. PR template과 CONTRIBUTING/SECURITY 문서를 만들고 ruleset 적용을 자동 + 검증한다. **전제조건 충족 (2026-08-29 public 전환, v1.0.0/v1.0.1 릴리스 + 완료). PR template/CONTRIBUTING/SECURITY는 이미 존재하며, 남은 것은 + ruleset 적용과 API 재검증뿐이다 -- 코드 작업이 아니라 GitHub Settings/API + 작업이므로 + `docs/superpowers/specs/2026-09-05-oss-repo-governance-hardening-design.md`의 + GitHub UI/API 설정 안내를 따른다.** 3. ~~CHECK 결과를 `VERIFIED`, `VIOLATED`, `NOT_APPLICABLE`, `UNVERIFIABLE`의 안정된 machine-readable contract로 승격한다.~~ **완료 (2026-08-30).** 모든 finding이 기존 `kind` 값과 별개로 `confidence` 필드를 직접 갖는다 @@ -202,7 +209,7 @@ Toolkit 자체에 계정 시스템을 서둘러 넣기보다 GitHub의 인증· | 단계 | 완료 조건 | | --- | --- | | v0.2.0 | P0 전체 통과, 최종 PR CI, 승인된 version bump와 release 절차 | -| Public | `master`/`develop`/`v*` ruleset API 검증, PR template, CONTRIBUTING, SECURITY | +| Public | `master`/`develop`/`v*` ruleset API 검증 (미완료, 2026-09-05 기준 branch protection 없음), PR template (완료), CONTRIBUTING (완료), SECURITY (완료) | | Team | 2명 이상 qualified maintainer, CODEOWNERS 독립 승인, 예외 owner/expiry | | Enterprise | 조직 ruleset·reusable workflow, audit export, taxonomy, 정의된 adoption metrics | | Multi-repo | 반복된 탐색 실패와 운영 요구를 근거로 registry/portal 도입 | From f17cc2abf6a1fb198bc7dfd025e1fe6895200e1f Mon Sep 17 00:00:00 2001 From: shcommit Date: Sun, 6 Sep 2026 00:07:48 +0900 Subject: [PATCH 12/15] =?UTF-8?q?docs:=20reconcile=20=C2=A72=20confirmed-f?= =?UTF-8?q?acts=20with=20the=20public-transition=20correction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §4/§8/§9 already state the repo has been public since 2026-08-29 with branch protection still missing; §2's "확인된 사실" bullets still said the repo "is private" and that PR template doesn't exist, directly contradicting those sections. The missing-branch-protection fact itself was still accurate and is kept -- only the private-repo framing and the now-false "no PR template" claim needed correcting. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01TAZPZwzSutmru6kSCEogu6 --- docs/enterprise-adoption.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/enterprise-adoption.md b/docs/enterprise-adoption.md index 78652ff..41698e0 100644 --- a/docs/enterprise-adoption.md +++ b/docs/enterprise-adoption.md @@ -23,11 +23,14 @@ ADR Toolkit의 강점은 AI가 초안과 언어 표현을 돕더라도 repositor ### 확인된 사실 -- 저장소는 private이며 `master`와 `develop` 보호 규칙 및 repository ruleset이 없다. -- 현재 플랜에서는 private 저장소 protection API가 활성화되지 않는다. +- 저장소는 2026-08-29부터 public이며, `master`/`develop` 보호 규칙과 repository + ruleset은 2026-09-05 API 재확인 기준으로 여전히 없다. +- 이는 plan 제약이 아니라 public 전환 이후 아직 적용하지 않은 설정 작업이다 + (§4 공개 저장소 전환 Gate, §9 단계별 완료 조건 참고). - CI는 Ubuntu, macOS, Windows와 지원 Python 조합에서 동작한다. - 첫 PR은 모든 CI를 통과했지만 독립 review 없이 병합됐다. -- CODEOWNERS와 PR template이 없다. +- PR template은 있지만, CODEOWNERS는 활성화되지 않은 dormant draft뿐이다 + (독립 승인 가능한 qualified maintainer가 2명 이상이 되기 전까지 미활성). - 코어는 locale, ID, lifecycle, schema, relationship, index, CHECK의 repository state를 결정론적으로 검증한다. - 중앙 서비스, 사용자 계정, 조직 RBAC, audit export, telemetry는 없다. From 34c4c05570d92f97329f96bc2e5d1a7ec8fd1824 Mon Sep 17 00:00:00 2001 From: shcommit Date: Sun, 6 Sep 2026 00:37:33 +0900 Subject: [PATCH 13/15] fix(github): close governance hardening review gaps --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + .github/dependabot.yml | 2 + .github/labeler.yml | 10 +- .github/labels.yml | 2 +- .github/workflows/labeler.yml | 7 +- .github/workflows/labels.yml | 1 - .github/workflows/release.yml | 13 +- .github/workflows/test.yml | 27 ++- SECURITY.md | 17 +- changelog.md | 17 ++ docs/enterprise-adoption.md | 34 +-- docs/oss-repository-governance-audit.md | 225 ++++++++++++++++++ ...05-oss-repo-governance-hardening-design.md | 97 ++++---- handoff.md | 73 ++++-- improvements.md | 4 + project-roadmap.md | 15 +- pyproject.toml | 3 + scripts/export_dev_requirements.py | 31 +++ tests/unit/test_github_governance.py | 102 ++++++++ 19 files changed, 553 insertions(+), 128 deletions(-) create mode 100644 docs/oss-repository-governance-audit.md create mode 100644 scripts/export_dev_requirements.py create mode 100644 tests/unit/test_github_governance.py diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 891306e..63b8d73 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -32,6 +32,7 @@ body: - Codex CLI - Gemini CLI - Antigravity CLI + - Cline CLI - Direct CLI (pip install / python script) - Other (specify in Additional context) validations: diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ade07ae..aac4144 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,6 +2,7 @@ version: 2 updates: - package-ecosystem: "pip" directory: "/" + target-branch: "develop" schedule: interval: "weekly" groups: @@ -12,6 +13,7 @@ updates: - package-ecosystem: "github-actions" directory: "/" + target-branch: "develop" schedule: interval: "weekly" groups: diff --git a/.github/labeler.yml b/.github/labeler.yml index c98869c..b96970c 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,9 +1,17 @@ "area:core": - changed-files: - any-glob-to-any-file: - - "skills/adr-toolkit/scripts/**" + - "skills/adr-toolkit/scripts/core/**" + - "skills/adr-toolkit/scripts/evidence/**" + - "skills/adr-toolkit/scripts/rules/**" - "scripts/**" +"area:cli": + - changed-files: + - any-glob-to-any-file: + - "skills/adr-toolkit/scripts/adr.py" + - "skills/adr-toolkit/scripts/commands/**" + "area:adapter": - changed-files: - any-glob-to-any-file: diff --git a/.github/labels.yml b/.github/labels.yml index f21259e..5d18388 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -29,7 +29,7 @@ description: "adr.py entrypoint and command wiring" - name: "area:adapter" color: "5319e7" - description: "adapters/** harness adapters (Claude, Codex, Gemini, Antigravity)" + description: "adapters/** and .claude-plugin/** harness integrations" - name: "area:github" color: "5319e7" description: ".github/** workflows, templates, and repo automation" diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 02f2074..114baac 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -8,12 +8,13 @@ on: permissions: contents: read - pull-requests: write - issues: write jobs: label-pr: if: github.event_name == 'pull_request_target' + permissions: + contents: read + pull-requests: write runs-on: ubuntu-latest steps: - uses: actions/labeler@v7 @@ -23,6 +24,8 @@ jobs: triage-issue: if: github.event_name == 'issues' + permissions: + issues: write runs-on: ubuntu-latest steps: - uses: actions/github-script@v7 diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index cd77ff4..c5e2640 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -9,7 +9,6 @@ on: permissions: contents: read issues: write - pull-requests: write jobs: sync: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cac8552..f8ca0d2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,12 +27,12 @@ jobs: id-token: write attestations: write steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: "3.12" - name: Install dependencies - run: pip install pytest build + run: pip install -e ".[dev]" - name: Run tests run: python -m pytest tests/unit tests/integration -v - name: Check manifest versions are in sync @@ -54,11 +54,11 @@ jobs: run: python -m build - name: Generate build provenance attestation if: ${{ !github.event.repository.private }} - uses: actions/attest-build-provenance@v2 + uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2 with: subject-path: ${{ steps.package.outputs.archive }} - name: Create GitHub Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 with: generate_release_notes: true files: | @@ -68,9 +68,8 @@ jobs: dist/*.tar.gz - name: Publish Python Package to PyPI if: ${{ !github.event.repository.private }} - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 continue-on-error: true with: skip-existing: true - diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 19c0952..664ef19 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -68,18 +68,17 @@ jobs: python-version: "3.12" - name: Upgrade pip (fixes known pip CVEs before auditing anything else) run: python -m pip install --upgrade pip + - name: Export pinned dev requirements + run: python scripts/export_dev_requirements.py > "$RUNNER_TEMP/adr-dev-requirements.txt" - name: Install pinned dev tools (not the project itself) - # KEEP IN SYNC with pyproject.toml's [project.optional-dependencies].dev -- - # this job can't use `pip install -e ".[dev]"` (see below), so these - # versions are a manual copy Dependabot won't update here. # Deliberately skips `pip install -e .`: adr-toolkit isn't - # published to PyPI (confirmed 404 on pypi.org/pypi/adr-toolkit/json), + # audited as a third-party dependency, # so pip-audit can never resolve it there, and --skip-editable # can't be combined with --strict (pip-audit treats every skipped # dependency as fatal under --strict, unconditionally -- see # pip_audit/_cli.py). This job audits the pinned dev toolchain, # not this project's own unpublished package. - run: pip install pytest==9.1.1 pytest-cov==7.1.0 mypy==1.19.1 ruff==0.16.6 + run: pip install -r "$RUNNER_TEMP/adr-dev-requirements.txt" - name: Install pip-audit run: pip install pip-audit==2.9.0 - name: Audit installed dev dependencies for known vulnerabilities @@ -110,8 +109,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Validate PR Title Conventional Commits Format + env: + PR_TITLE: ${{ github.event.pull_request.title }} run: | - TITLE="${{ github.event.pull_request.title }}" + TITLE="$PR_TITLE" echo "Checking PR Title: $TITLE" REGEX="^(feat|fix|docs|style|refactor|perf|test|chore|ci)(\([a-z0-9-]+\))?!?: .+" if [[ ! "$TITLE" =~ $REGEX ]]; then @@ -142,8 +143,18 @@ jobs: run: npm install -g @openai/codex@0.151.0 - name: Install Gemini CLI run: npm install -g @google/gemini-cli@0.46.0 - - name: Install Antigravity CLI - run: curl -fsSL https://antigravity.google/cli/install.sh | bash + - name: Install pinned Antigravity CLI artifact + run: | + set -euo pipefail + ARCHIVE="$RUNNER_TEMP/antigravity-cli-1.1.27-linux-x64.tar.gz" + curl --proto '=https' --tlsv1.2 -fsSL \ + https://storage.googleapis.com/antigravity-public/antigravity-cli/1.1.27-5211191891591168/linux-x64/cli_linux_x64.tar.gz \ + -o "$ARCHIVE" + printf '%s %s\n' \ + '793d4b9ea2c08d9a7e50bafa02cfc8c19424bd60d6e83f91408d45f9c6d4ce79a5d576fede5bef164d823abf84f81359a14b4ca665952c47b0a7cfd743bb69c0' \ + "$ARCHIVE" | sha512sum --check - + tar -xzf "$ARCHIVE" -C "$RUNNER_TEMP" antigravity + install -D -m 0755 "$RUNNER_TEMP/antigravity" "$HOME/.local/bin/agy" - name: Verify Codex CLI adapter end to end run: | set -euo pipefail diff --git a/SECURITY.md b/SECURITY.md index d4d34a4..fe55430 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,8 +6,9 @@ Security fixes target the latest released version of ADR Toolkit. ## Reporting a Vulnerability -Please report security issues privately to the repository owner before opening a -public issue. Include: +Please use GitHub's private vulnerability reporting form at + instead of +opening a public issue. Include: - affected version or commit - reproduction steps @@ -29,13 +30,10 @@ signature -- no private key is held or rotated by this project -- proving the archive was produced by this repository's own CI, from the exact commit the release tag points to. -**While this repository is private, attestation is not generated** -- -GitHub's attestation API rejects it for a user-owned private repository. -The release workflow skips that step automatically and still publishes -the archive and its checksum; attestation starts appearing on releases -once the repository goes public, with no workflow change required. Check -a given release's assets on the Releases page to see whether an -attestation is available for it. +The repository has been public since 2026-08-29, so current releases are +eligible for attestation. The workflow keeps a private-repository guard only +so forks or future visibility changes fail safely; check a given release's +assets to confirm that its attestation was actually published. To verify a downloaded archive: @@ -45,7 +43,6 @@ sha256sum -c adr-toolkit-skill-vX.Y.Z.tar.gz.sha256 # Provenance: confirms the archive was actually built by this repo's CI, # not a look-alike release from a compromised account or a different repo. -# Only available once this repository is public -- see the note above. gh attestation verify adr-toolkit-skill-vX.Y.Z.tar.gz -R SHcommit/ADR-toolkit ``` diff --git a/changelog.md b/changelog.md index 15f4133..a20e5ac 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,23 @@ Lightweight human-readable summary of meaningful repository changes. ## Unreleased +- Added scalable GitHub governance: structured Issue Forms, source-controlled + label taxonomy, path-based PR labels, new-issue triage, weekly grouped + Dependabot updates targeting `develop`, and a dormant CODEOWNERS draft. +- Added ruff and strict dependency-audit CI gates, upgraded the supported + Python floor to 3.10, and added regression tests for repository governance + configuration. +- Hardened CI supply chain behavior: replaced the Antigravity `curl | bash` + installer with a versioned SHA-512-verified artifact, pinned release Actions + to commit SHAs, pinned release build tooling, and removed PR-title shell + expression injection. +- Corrected the governance audit to recognize the already-active repository + branch/tag rulesets through the ruleset APIs rather than treating a classic + branch-protection 404 as proof of no protection. +- Enabled live Discussions, dependency security updates and alerts, secret + scanning with push protection, private vulnerability reporting, and + automatic deletion of merged branches. + ## v1.0.1 (2026-09-02) - Added PyPI packaging support (`pyproject.toml`) for `pip install adr-toolkit` and `pipx install adr-toolkit`. diff --git a/docs/enterprise-adoption.md b/docs/enterprise-adoption.md index 41698e0..e21e9c7 100644 --- a/docs/enterprise-adoption.md +++ b/docs/enterprise-adoption.md @@ -23,10 +23,12 @@ ADR Toolkit의 강점은 AI가 초안과 언어 표현을 돕더라도 repositor ### 확인된 사실 -- 저장소는 2026-08-29부터 public이며, `master`/`develop` 보호 규칙과 repository - ruleset은 2026-09-05 API 재확인 기준으로 여전히 없다. -- 이는 plan 제약이 아니라 public 전환 이후 아직 적용하지 않은 설정 작업이다 - (§4 공개 저장소 전환 Gate, §9 단계별 완료 조건 참고). +- 저장소는 2026-08-29부터 public이며, repository ruleset API 재확인 결과 + `master`/`develop`/`release/*` branch와 `v*` tag 보호 규칙이 2026-09-02부터 + active다 (ruleset IDs `22101891`, `22102322`). +- classic branch-protection API의 404는 ruleset 부재를 의미하지 않는다. 실제 + 적용 여부는 `repos/SHcommit/ADR-toolkit/rules/branches/`와 + `repos/SHcommit/ADR-toolkit/rulesets/`로 검증한다. - CI는 Ubuntu, macOS, Windows와 지원 Python 조합에서 동작한다. - 첫 PR은 모든 CI를 통과했지만 독립 review 없이 병합됐다. - PR template은 있지만, CODEOWNERS는 활성화되지 않은 dormant draft뿐이다 @@ -65,10 +67,12 @@ CI 통과는 구조화된 정책의 증거일 뿐, 결정의 사업적 타당성 ## 4. 공개 저장소 전환 Gate -저장소는 2026-08-29부터 public이다. 아래 repository ruleset은 이미 적용 -대상이며, 적용 후 API로 실제 상태를 재조회해 확인한다 (2026-09-05 확인 결과 -`master`/`develop` 모두 branch protection이 없는 상태 -- 이 문서 §9 "Public" -단계 완료 조건이 아직 충족되지 않았다). +저장소는 2026-08-29부터 public이다. 아래 repository ruleset은 2026-09-02에 +적용됐고 2026-09-06에 ruleset API와 effective-rules API로 재검증했다. Branch +ruleset `22101891`은 PR, required checks, conversation resolution, +force-push/deletion 차단을 적용하며 tag ruleset `22102322`는 `v*` 수정·삭제를 +차단한다. 단, CI matrix가 Python 3.9에서 3.10으로 바뀌므로 이 변경을 merge한 +직후 required-check context도 함께 갱신해야 한다. | 대상 | 권장 통제 | 도입 이유 | | --- | --- | --- | @@ -172,13 +176,11 @@ Toolkit 자체에 계정 시스템을 서둘러 넣기보다 GitHub의 인증· 1. ~~ADR directory 로딩을 공통 iterator로 통합해~~ **완료 (2026-08-30).** `core.adr_directory.iter_adr_files`로 validate/index/related/CHECK를 통합했고 각 명령의 warning 의미는 그대로 유지했다. -2. PR template과 CONTRIBUTING/SECURITY 문서를 만들고 ruleset 적용을 자동 - 검증한다. **전제조건 충족 (2026-08-29 public 전환, v1.0.0/v1.0.1 릴리스 - 완료). PR template/CONTRIBUTING/SECURITY는 이미 존재하며, 남은 것은 - ruleset 적용과 API 재검증뿐이다 -- 코드 작업이 아니라 GitHub Settings/API - 작업이므로 - `docs/superpowers/specs/2026-09-05-oss-repo-governance-hardening-design.md`의 - GitHub UI/API 설정 안내를 따른다.** +2. ~~PR template과 CONTRIBUTING/SECURITY 문서를 만들고 ruleset 적용을 자동 + 검증한다.~~ **완료 (2026-09-06 재검증).** PR template, + CONTRIBUTING/SECURITY가 존재하고 branch/tag ruleset의 effective rules를 API로 + 확인했다. CI job 이름이 바뀌면 required-check context도 같은 변경의 rollout + 절차에서 갱신한다. 3. ~~CHECK 결과를 `VERIFIED`, `VIOLATED`, `NOT_APPLICABLE`, `UNVERIFIABLE`의 안정된 machine-readable contract로 승격한다.~~ **완료 (2026-08-30).** 모든 finding이 기존 `kind` 값과 별개로 `confidence` 필드를 직접 갖는다 @@ -212,7 +214,7 @@ Toolkit 자체에 계정 시스템을 서둘러 넣기보다 GitHub의 인증· | 단계 | 완료 조건 | | --- | --- | | v0.2.0 | P0 전체 통과, 최종 PR CI, 승인된 version bump와 release 절차 | -| Public | `master`/`develop`/`v*` ruleset API 검증 (미완료, 2026-09-05 기준 branch protection 없음), PR template (완료), CONTRIBUTING (완료), SECURITY (완료) | +| Public | `master`/`develop`/`release/*`/`v*` ruleset API 검증 (완료, 2026-09-06), PR template (완료), CONTRIBUTING (완료), SECURITY 및 private vulnerability reporting (완료) | | Team | 2명 이상 qualified maintainer, CODEOWNERS 독립 승인, 예외 owner/expiry | | Enterprise | 조직 ruleset·reusable workflow, audit export, taxonomy, 정의된 adoption metrics | | Multi-repo | 반복된 탐색 실패와 운영 요구를 근거로 registry/portal 도입 | diff --git a/docs/oss-repository-governance-audit.md b/docs/oss-repository-governance-audit.md new file mode 100644 index 0000000..b50aea2 --- /dev/null +++ b/docs/oss-repository-governance-audit.md @@ -0,0 +1,225 @@ +# OSS Repository Governance Audit + +검증 기준일: 2026-09-06 (Asia/Seoul) + +이 보고서는 파일 존재 여부뿐 아니라 backlog가 수백~수천 건으로 늘어났을 때 +한 명의 maintainer가 반복 분류·보안 점검·merge 통제를 감당할 수 있는지를 +기준으로 작성했다. 로컬 브랜치의 구성과 GitHub API로 조회한 live 설정을 +구분한다. + +## 1. Executive Summary + +ADR Toolkit은 문서, 테스트, release provenance, 다중 플랫폼 CI가 이미 강한 +소형 OSS 저장소다. 이번 hardening으로 label taxonomy, path labeler, +Dependabot, Issue Forms, lint, dependency audit, dormant CODEOWNERS를 추가했고, +live repository에는 Discussions, 보안 알림, 자동 security update, secret +scanning/push protection, private vulnerability reporting을 활성화했다. + +가장 중요한 리뷰 결과는 기존 감사의 전제 오류다. Classic branch-protection +API가 404를 반환했지만 repository ruleset은 2026-09-02부터 active였다. +Ruleset `22101891`은 `master`/`develop`/`release/*`에 PR, required checks, +conversation resolution, deletion/non-fast-forward 차단을 적용하고, ruleset +`22102322`는 `v*` tag 수정·삭제를 차단한다. 실제 남은 위험은 보호 규칙의 +부재가 아니라 CI matrix 변경 후 required-check 이름이 drift하는 것이다. + +현재 운영 수준은 **🟢 성장 가능한 기본 구조, 단 rollout 1단계 필요**로 +평가한다. 이 브랜치를 `develop`에 merge한 직후 ruleset에서 Python 3.9 check를 +3.10 check로 교체하고 `lint`/`dependency-audit`를 required로 추가해야 한다. + +## 2. Repository Audit + +| 영역 | 현재 상태 | 문제 또는 근거 | 우선순위 | +| --- | --- | --- | --- | +| README | 🟢 충분함 | 설치, 기능, scope, 기여 링크 제공 | P2 | +| CONTRIBUTING | 🟢 충분함 | Git Flow, local checks, ADR/adapter 규칙 제공 | P2 | +| CODE_OF_CONDUCT | 🟢 충분함 | 행동 기준·신고·집행 범위 명시 | P2 | +| SECURITY | 🟢 충분함 | private advisory 경로, 지원 버전, checksum/attestation 검증 제공 | P1 | +| LICENSE | 🟢 충분함 | MIT license 존재 | P2 | +| CHANGELOG | 🟢 충분함 | `Unreleased`와 release별 human-readable 기록 | P2 | +| Roadmap | 🟢 충분함 | trigger 기반 deferred work와 완료 항목 구분 | P2 | +| ADR / architecture docs | 🟢 충분함 | `docs/decisions/`와 설계·감사 문서 존재 | P2 | +| Issue templates | 🟢 충분함 | bug/feature Issue Forms와 blank issue 차단 | P1 | +| PR template | 🟢 충분함 | Conventional title, ADR/example 영향 확인 | P1 | +| Labels | 🟢 충분함 | 25개 source taxonomy 중 type/area/priority/size/workflow 20개를 live sync 완료 | P1 | +| GitHub Projects | 🟡 개선 필요 | 기능은 enabled지만 Project 없음; open issue 0개라 지금 생성은 과함 | NEXT | +| Milestones | 🟡 개선 필요 | milestone 없음; release backlog가 생길 때 도입 | NEXT | +| CODEOWNERS | 🟡 개선 필요 | dormant draft 존재, 1인 maintainer라 required review는 의도적으로 OFF | NEXT | +| Discussions | 🟢 충분함 | live 활성화 및 Q&A category 확인 | P1 | +| Branch/tag ruleset | 🟡 개선 필요 | active/effective 확인; required checks는 아직 Python 3.9 이름을 포함 | P0 rollout | +| Merge 정책 | 🟢 충분함 | PR 강제와 merge 후 short-lived branch 자동 삭제 활성화 | P1 | +| Build | 🟢 충분함 | release에서 wheel/sdist와 skill archive 생성 | P1 | +| Test | 🟢 충분함 | unit+integration 550+ cases | P1 | +| Lint | 🟢 충분함 | ruff E9/F fast gate 추가 | P1 | +| Type check | 🟢 충분함 | 핵심 typed module에 mypy `--strict` | P1 | +| Coverage | 🟢 충분함 | branch coverage 85% floor | P1 | +| Multi-platform | 🟢 충분함 | Linux/macOS/Windows × Python 3.10/3.12 | P1 | +| E2E | 🟢 충분함 | Codex/Gemini/Antigravity adapter install-and-run | P1 | +| Release | 🟡 개선 필요 | direct-tag workflow와 version gate 있음; PyPI publish는 `continue-on-error` | P1 | +| Artifact verification | 🟢 충분함 | SHA-256 release asset과 version/tag drift gate | P1 | +| Provenance / attestation | 🟢 충분함 | public release에 GitHub Artifact Attestation | P1 | +| Changelog requirement | 🟡 개선 필요 | template/process는 있으나 자동 path-based gate 없음 | NEXT | +| Documentation drift | 🟢 충분함 | examples execution/drift gate 제공 | P1 | +| Dependabot | 🟢 충분함 | pip/actions weekly grouped PR, target `develop` | P1 | +| GitHub Actions pinning | 🟡 개선 필요 | write-capable release actions는 SHA pin; read-only CI first-party actions는 major pin | P2 | +| Remote installer | 🟢 충분함 | Antigravity fixed artifact + official SHA-512 verification, `curl | bash` 제거 | P0 | +| Package lock | 🟡 개선 필요 | runtime dependency 0개; dev tools exact pin이라 lock 부재 위험은 제한적 | P2 | +| Dependency/security audit | 🟢 충분함 | `pip-audit --strict`, alerts/security updates/secret scanning enabled | P1 | + +## 3. NOW + +- 완료: `.github/labels.yml`과 live label taxonomy 동기화. +- 완료: changed path 기반 PR labeler와 new issue `needs-triage` 적용. +- 완료: structured Issue Forms와 Discussions/Q&A 경로. +- 완료: pip/GitHub Actions Dependabot을 weekly grouped update로 구성하고 + `develop`을 target branch로 고정. +- 완료: ruff lint와 `pip-audit --strict` CI gate. +- 완료: Antigravity CI installer를 versioned artifact + SHA-512 검증으로 교체. +- 완료: release workflow dependency pin 및 write-capable workflow Action SHA pin. +- 완료: vulnerability alerts, automated security fixes, secret scanning, + push protection, private vulnerability reporting 활성화. +- rollout 직후: ruleset required contexts에서 Python 3.9 두 개를 제거하고 + Python 3.10 세 개, `lint`, `dependency-audit`를 추가. + +## 4. NEXT + +- Issue가 약 20~30개 이상 지속되거나 동시에 진행 중인 work item이 10개를 + 넘으면 Project를 만든다. +- 2명 이상의 qualified maintainer가 생기면 dormant CODEOWNERS를 영역별로 + 분리하고 code-owner review를 required로 전환한다. +- PR queue가 10개 이상 지속될 때 `size:XS`~`size:XL` 자동 분류를 추가한다. +- release 목표 issue가 5개 이상 모이면 milestone을 source of truth로 쓴다. +- ruleset check-context drift가 다시 발생하면 settings verification script나 + Terraform/GitHub provider 기반 ruleset-as-code를 도입한다. + +권장 수동 size 기준은 XS 10 LOC 이하, S 50 이하, M 200 이하, L 500 이하, +XL 500 초과다. Generated file, security-sensitive workflow, schema 변경은 LOC와 +무관하게 한 단계 상향할 수 있다. + +## 5. LATER + +- stale bot: abandoned backlog가 실제로 누적된 뒤에만 도입한다. 기준은 + 90일 inactive → stale, 추가 30일 → close이며 `security`, `pinned`, + `roadmap`, `help wanted`, `priority:P0`, `blocked`는 제외한다. +- organization ruleset/reusable workflow/RBAC/audit export: 동일 운영을 + 반복하는 repository가 2개 이상일 때 도입한다. +- ML triage, automatic assignment, 복잡한 release train은 현재 도입하지 않는다. + +## 6. Security / Supply Chain Findings + +1. **해결 — remote code execution:** `curl install.sh | bash`가 실제 PR CI에서 + 실행되고 있었다. 공식 manifest가 가리킨 Antigravity CLI `1.1.27` Linux + artifact URL과 SHA-512를 고정해 다운로드·검증·설치 단계로 분리했다. +2. **해결 — release mutability:** `release.yml`의 Action ref를 현재 commit SHA로 + 고정하고 version comment를 남겼다. Python build/test tool도 dev extra pin을 + 사용한다. +3. **해결 — dependency visibility:** Dependabot security updates와 alerts, + `pip-audit --strict`, secret scanning/push protection을 활성화했다. +4. **해결 — PR title command injection:** untrusted PR title expression을 inline + shell 문자열에 삽입하지 않고 step environment로 전달하도록 변경했다. +5. **남음 — partial release:** PyPI publish가 `continue-on-error: true`라 GitHub + Release 성공과 PyPI 실패가 동시에 가능하다. Trusted Publisher 안정성이 + 확인되면 fail-closed 전환을 검토한다. +6. **수용 — read-only CI Action tags:** 일반 test/labeler의 GitHub-owned Actions는 + major tag를 사용한다. Release처럼 write 권한을 갖는 경로는 SHA pin을 적용했다. + +## 7. 추가/수정 파일 + +```text +.github/ +├── CODEOWNERS +├── dependabot.yml +├── labeler.yml +├── labels.yml +├── ISSUE_TEMPLATE/ +│ ├── bug_report.yml +│ ├── feature_request.yml +│ └── config.yml +└── workflows/ + ├── labeler.yml + ├── labels.yml + ├── release.yml + └── test.yml +scripts/ +└── export_dev_requirements.py +tests/unit/ +└── test_github_governance.py +``` + +각 파일의 실행 가능한 실제 내용은 위 경로 자체가 source of truth다. 추가로 +`pyproject.toml`, `SECURITY.md`, `project-roadmap.md`, +`docs/enterprise-adoption.md`, 본 설계 문서를 현재 운영 상태에 맞게 수정했다. + +## 8. GitHub UI 설정 + +API로 확인/적용한 live 상태: + +- Discussions: enabled. +- Repository Projects: enabled, Project instance는 없음. +- Dependabot security updates: enabled. +- Vulnerability alerts: enabled. +- Secret scanning / push protection: enabled. +- Private vulnerability reporting: enabled. +- Delete branch on merge: enabled. +- Branch ruleset `22101891`: active, bypass actor 없음. +- Tag ruleset `22102322`: active, bypass actor 없음. + +이 브랜치 merge 직후 Settings → Rules → Rulesets에서 `protected-branches`의 +required checks를 다음과 같이 맞춘다. + +```text +remove: pytest (ubuntu-latest, 3.9) +remove: pytest (windows-latest, 3.9) +add: pytest (ubuntu-latest, 3.10) +add: pytest (macos-latest, 3.10) +add: pytest (windows-latest, 3.10) +add: lint +add: dependency-audit +keep: all Python 3.12 checks, type-check, version-drift, + examples-drift, pr-title-check, harness-parity +``` + +Required approving review count와 code-owner review는 maintainer가 1명인 동안 +0/OFF를 유지한다. Signed commits도 현재는 요구하지 않는다. + +## 9. Issue Backlog 후보 + +| 후보 | 출처 | 추천 labels | 크기 | +| --- | --- | --- | --- | +| `adapters/README.md`에 “새 harness adapter 추가하기” tutorial 작성 | `docs/adr-toolkit-audit-report.md` §2.6 | `type:docs`, `area:adapter`, `good first issue` | S | +| harness parity를 `check/search/graph/create`까지 확장 | `project-roadmap.md` Harness parity | `type:test`, `area:adapter`, `help wanted` | M | +| 실제 사용자 피드백 후 Traditional Chinese `zh-TW` catalog 검토 | `project-roadmap.md` Internationalization | `type:feature`, `area:core`, `help wanted` | M | +| Accepted ADR metadata factual-correction policy 설계 | `project-roadmap.md` Lifecycle research | `type:docs`, `area:docs`, `priority:P2` | M | +| ruleset required-check drift 자동 검증 | 이번 감사의 재발 방지 항목 | `type:test`, `area:github`, `priority:P1` | S | + +첫 good-first issue는 문서 tutorial이 가장 적합하다. Product behavior를 바꾸지 +않고 기존 adapter를 비교해 명확한 acceptance criteria를 만들 수 있다. + +## 10. 최종 목표 구조 + +```text +Idea + ↓ +Issue Form / Discussion + ↓ +Auto Triage / Label + ↓ +Issue backlog (Project는 규모 trigger 충족 시) + ↓ +Contributor + ↓ +Pull Request + ↓ +Automated Quality Gates + ↓ +Review + conversation resolution + ↓ +Merge to develop + ↓ +Release branch → master → v* tag + ↓ +Attestation / checksum / dependency & security maintenance +``` + +목표는 자동화 개수가 아니라 maintainer 판단이 필요한 우선순위·설계·review만 +사람에게 남기고, 입력 구조화·경로 분류·반복 검증·dependency 감시는 GitHub와 +CI가 수행하게 하는 것이다. diff --git a/docs/superpowers/specs/2026-09-05-oss-repo-governance-hardening-design.md b/docs/superpowers/specs/2026-09-05-oss-repo-governance-hardening-design.md index 39a119b..7816049 100644 --- a/docs/superpowers/specs/2026-09-05-oss-repo-governance-hardening-design.md +++ b/docs/superpowers/specs/2026-09-05-oss-repo-governance-hardening-design.md @@ -12,21 +12,28 @@ product code, CLI behavior, or `docs/decisions/` governance model. ## Context — audit findings as of 2026-09-05 +> **2026-09-06 review correction:** the original audit queried only the +> classic branch-protection endpoint. A 404 from that endpoint does not prove +> repository rulesets are absent. Ruleset and effective-rules APIs show that +> active branch/tag rulesets have existed since 2026-09-02 (IDs `22101891` +> and `22102322`). The original audit also incorrectly described the +> Antigravity adapter as manual-only even though `harness-parity` executed its +> remote bootstrapper. The decisions below are corrected accordingly. + Re-verified against the live repository (`gh api`/`gh repo view`), not just -file presence, because `docs/enterprise-adoption.md` §4/§8 and -`improvements.md`'s "Low" backlog describe this repo as still private with a -single precondition ("저장소 public 전환") blocking the public-readiness gate. -That precondition has already been met and those documents are now stale: +file presence, because `docs/enterprise-adoption.md` §4/§8 described this repo +as still private with a single precondition ("저장소 public 전환") blocking the +public-readiness gate. That precondition had already been met and the document +was stale: - The repository has been **public since 2026-08-29**, and `master` has already shipped `v1.0.0` and `v1.0.1` (PyPI publishing pipeline, antigravity harness-parity CI, a Windows lock-metadata fix, mypy `--strict` fix). -- Despite being public, `master` has **zero branch protection** - (`gh api repos/.../branches/master/protection` → `404 Branch not - protected`). `develop` is unprotected too. `docs/enterprise-adoption.md` - §9's "Public" stage completion condition ("ruleset API 검증") is therefore - **not actually satisfied**, even though the PR template, `CONTRIBUTING.md`, - and `SECURITY.md` pieces of that same stage are done. +- Active repository rulesets protect `master`, `develop`, `release/*`, and + `v*` tags. They require PRs, status checks, conversation resolution, and + prohibit deletion/non-fast-forward updates without a bypass actor. The + remaining rollout concern is keeping required-check contexts synchronized + with CI matrix changes (this branch replaces Python 3.9 with 3.10). - `develop` was frozen at the `v0.3.2` sync point while `master` advanced through the `v1.0.0`/`v1.0.1` releases — the git-flow-mandated release-branch back-merge into `develop` never happened. **Fixed as a @@ -49,9 +56,8 @@ That precondition has already been met and those documents are now stale: scan step. - `harness-parity` contains one remote-installer pattern the request asked to scrutinize: `curl -fsSL https://antigravity.google/cli/install.sh | bash`. - (Antigravity itself is manually verified only, per - `adapters/antigravity/README.md` — this curl step doesn't currently run in - CI for that adapter.) + This does run in CI before the Antigravity end-to-end adapter verification, + so it must be replaced with a versioned artifact plus checksum verification. - GitHub Actions are pinned to major-version tags from verified publishers (`actions/*@v4/@v5`, `softprops/action-gh-release@v2`, `pypa/gh-action-pypi-publish@release/v1`) — not full commit-SHA pinning, but @@ -60,9 +66,9 @@ That precondition has already been met and those documents are now stale: `test.yml` sets repo-wide `permissions: contents: read`; least-privilege is already largely in place, not a gap. - Dependency ecosystems actually in use: Python (`pyproject.toml`) and GitHub - Actions. No `package.json` anywhere — the `npm install -g` calls in - `harness-parity` install pinned global CLIs for testing, not a project - dependency Dependabot should manage. No Docker. + Actions. The only `package.json` is a test fixture; the `npm install -g` + calls in `harness-parity` install pinned global CLIs for testing, not a + project dependency Dependabot should manage. No Docker. - `project-roadmap.md`'s "Public and enterprise governance" section and `docs/enterprise-adoption.md` §8 already correctly defer **mandatory** CODEOWNERS review and organization-wide ruleset/reusable-workflow work @@ -72,22 +78,20 @@ That precondition has already been met and those documents are now stale: ## Problem Statement -The repository's day-to-day contributor mechanics (label triage, dependency -freshness, PR gating, branch protection) were never built out because the +The repository's day-to-day contributor mechanics (label triage and dependency +freshness) were never built out because the project was small and private. It is now public with two releases shipped, -but none of the operational scaffolding that keeps a growing issue/PR queue -navigable for a single maintainer exists yet, and the one piece of the -public-readiness plan that mattered most for actually protecting the release -history — branch protection — silently never got applied when the repo went -public. Left as-is, the first sign of trouble will be either an unreviewed -force-push/deletion incident on `master`, or a backlog of unlabeled, -untriaged issues and PRs once external contributors show up. +but the operational scaffolding that keeps a growing issue/PR queue navigable +for a single maintainer does not exist yet. Existing rulesets protect release +history, but their required status-check names can drift when CI changes. +Left as-is, the first sign of trouble will be either a permanently blocked PR +after a matrix rename, or a backlog of unlabeled, untriaged work. ## Solution Add the minimum GitHub-native automation that lets issues and PRs self-organize (path-based labels, a label taxonomy, Dependabot, Issue Forms) -and close the branch-protection gap that public status already requires, +and verify/update the already-active rulesets alongside CI changes, while explicitly deferring anything that assumes a maintainer team or an issue volume this repository doesn't have yet (mandatory CODEOWNERS review, stale-bot, org-wide rulesets). Every "not now" gets a written trigger @@ -103,9 +107,9 @@ condition instead of a vague "later," matching how `project-roadmap.md` and 3. As a first-time contributor, I want a structured bug/feature form instead of a blank Markdown template, so I give the maintainer what's needed on the first pass. -4. As the sole maintainer, I want `master`/`develop`/`v*` tags protected from - force-push and deletion now that the repo is public, without being forced - into mandatory independent code-owner review I can't actually staff. +4. As the sole maintainer, I want `master`/`develop`/`v*` protection verified + through the correct ruleset APIs and required checks kept in sync, without + mandatory independent code-owner review I can't actually staff. 5. As a future contributor, I want a small set of well-labeled "good first issue" candidates to exist so I know where to start. 6. As the sole maintainer, I want CI to catch missing lint/security-scan @@ -146,9 +150,10 @@ condition instead of a vague "later," matching how `project-roadmap.md` and - **Dependency/security scan**: add a lightweight `pip-audit`-style check for the Python dependency surface as a fast-gate job. - **Branch protection / ruleset** (GitHub UI/API, not a file in this repo — - see the UI section below): require PR + passing required checks, - block force-push and branch deletion on `master` and `develop`, restrict - `v*` tag creation/deletion, require conversation resolution. Do **not** + see the UI section below): verify the existing active rulesets, then update + required status-check contexts after CI job/matrix changes. They already + require PR + passing checks, block force-push/deletion on protected branches + and `v*`, and require conversation resolution. Do **not** require code-owner review or signed commits yet — no second qualified maintainer exists to review against, and no CODEOWNERS file exists to reference. @@ -203,12 +208,9 @@ condition instead of a vague "later," matching how `project-roadmap.md` and - New CI jobs (lint, dependency/security scan): must pass on this branch's own diff and not regress the existing 515 unit tests or `sync_version.py --check` / `verify_examples.py --check` drift gates. -- Branch protection: after applying via UI/API, re-query - `gh api repos/SHcommit/ADR-toolkit/branches//protection` and paste - the actual response into the audit doc — this spec's own investigation - showed a prior "done" claim (docs saying the public gate was satisfied) - was false, so the closing step here is re-verification, not another - written claim. +- Branch protection: query `repos/SHcommit/ADR-toolkit/rulesets/` and + `repos/SHcommit/ADR-toolkit/rules/branches/`. Do not use the classic + `branches//protection` endpoint alone as evidence of ruleset absence. ## Out of Scope @@ -222,13 +224,6 @@ condition instead of a vague "later," matching how `project-roadmap.md` and - Signed-commit requirements. - Any change to ADR Toolkit's product code, CLI, or `docs/decisions/` governance model. -- Converting the Antigravity `curl | bash` install step into an - artifact+checksum flow — that step doesn't currently run for the - Antigravity adapter in CI (manual-only per - `adapters/antigravity/README.md`); if/when Antigravity CI verification is - added (tracked in `improvements.md`, blocked on Antigravity publishing to a - package registry), that installer should be revisited then, not - speculatively hardened now for a path nothing currently executes. - Full commit-SHA pinning for all third-party Actions (current major-version tag pinning from verified publishers is a reasonable middle ground for this repo's risk level; revisit only if a specific tag-mutation incident in one @@ -248,17 +243,15 @@ condition instead of a vague "later," matching how `project-roadmap.md` and ## Further Notes -- Rollout order: branch-protection/ruleset (GitHub UI/API, highest - risk-reduction per unit effort) → Dependabot + auto-labeler (lowest +- Rollout order: ruleset verification/check-context sync (GitHub UI/API) → + Dependabot + auto-labeler (lowest maintenance cost) → Issue Forms → new CI jobs (lint, dependency scan) → stale-docs correction, so the highest-value, lowest-risk items land first and the doc correction reflects the final state rather than needing a second pass. -- The public-transition gap found here (public since 2026-08-29, but branch - protection never applied, and `docs/enterprise-adoption.md`/`improvements.md` - both still describing the repo as private) should be treated as the - headline finding when this work is reported back — it's a real exposure - window on a public repo with release tags, not a paperwork gap. +- The headline review correction is methodological: classic branch protection + and repository rulesets use different APIs. Operational audits must inspect + both, then query effective rules for concrete refs before declaring a gap. - This spec intentionally does not propose a `.github/labeler.yml` / `dependabot.yml` schema inline — implementation will write those files directly against this repo's actual directory names, which is faster to diff --git a/handoff.md b/handoff.md index 67780d2..8d2483d 100644 --- a/handoff.md +++ b/handoff.md @@ -2,32 +2,59 @@ ## Current task -Completed Production Readiness P1/P2 Backlog Improvements via Parallel Subagent Execution (Groups A-F). -Strictly audited codebase improvements completed; all 550 tests passing. - -## Scope - -- Updated `Agent-toolkit` plugin bundle to v0.3.6. -- Production Readiness Audit completed for `ADR-toolkit` (`analyzing-system`). -- Implemented and verified all High (P1) and Medium (P2) action items: - - Group A: `.adr-toolkit.json` `adr_dir` config & `ADR_DIR`/`ADR_LOCALE` env vars. - - Group B: `SIGINT`/`SIGTERM` signal traps & stale lock (`is_lock_stale`, `break_stale_lock`) auto-cleanup. - - Group C: `adoption_metrics.py` (41KB) refactored into `scripts/adoption_metrics/` subpackage. - - Group D: `skills/adr-toolkit/scripts/commands/doctor.py` (`adr doctor` diagnostic command). - - Group E: 10MB file size cap & streaming parse protection in `frontmatter.py`. - - Group F: `--verbose`, `--debug`, `--quiet` logging flags & `doctor` subcommand integrated in `adr.py`. - -## Next step (for a new session picking this up cold) - -- All P1/P2 Production Readiness backlog items are resolved and committed. -- Future work: Low priority tasks (CODEOWNERS once there are 2+ qualified maintainers, organization-wide governance once there are 2+ repositories). +OSS repository governance hardening implementation and independent review. +Original SDD Tasks 1–9 are complete; the follow-up review corrected false live +GitHub assumptions and closed additional supply-chain/configuration gaps. + +## Touched files + +- Governance config: `.github/CODEOWNERS`, `.github/dependabot.yml`, + `.github/labeler.yml`, `.github/labels.yml`, `.github/ISSUE_TEMPLATE/**`, + `.github/workflows/{labeler,labels,test,release}.yml`. +- Tooling/tests: `pyproject.toml`, `scripts/export_dev_requirements.py`, + `tests/unit/test_github_governance.py`, pre-existing ruff cleanup files. +- Docs: `SECURITY.md`, `project-roadmap.md`, `docs/enterprise-adoption.md`, + `docs/oss-repository-governance-audit.md`, governance design spec, + `changelog.md`, `improvements.md`. + +## Live GitHub changes applied + +- Synced 25 labels without deleting unrelated labels. +- Enabled Discussions, Dependabot security updates/alerts, secret scanning and + push protection, private vulnerability reporting, and delete-branch-on-merge. +- Re-verified active branch ruleset `22101891` and tag ruleset `22102322`. + +## Next step + +1. Merge PR #17 (`chore/sync-develop-with-v1.0.1`) first because this branch is + based on it. +2. Fetch the updated `develop` and merge it into `feature/oss-governance-hardening` + before opening the governance PR, because `origin/develop` also contains + newer adapter work. +3. Push `feature/oss-governance-hardening` and open a PR to `develop`. +4. After its new Python 3.10/lint/audit checks have run, update ruleset + `22101891`: remove the two Python 3.9 contexts; add all three Python 3.10 + contexts plus `lint` and `dependency-audit`; query effective rules again. ## Verification -`python3 -m pytest tests/unit tests/integration -q` (550 tests passing) and -`python3 scripts/sync_version.py --check` passed cleanly. +- CI-equivalent pytest + branch coverage: 558 passed, 92.74% coverage. +- `ruff check .`: passed. +- scoped `mypy --strict`: passed. +- `sync_version.py --check` and `verify_examples.py --check`: passed. +- `pip-audit --strict` in a clean dev-tool environment: no known vulnerabilities. +- actionlint v1.7.12: passed after fixing PR-title expression injection. +- Python package build: wheel and sdist built successfully; emitted only the + tracked PyPA license-metadata deprecation warning. +- Antigravity 1.1.27 artifact: official SHA-512 matched and archive contained + the expected `antigravity` binary. ## Open risks -- The ReDoS runtime timeout (`rules/conflict.py`) is POSIX-only. -- `supersede.py` guarantees single-file atomicity, but true two-phase multi-file commit across pair updates is scoped out. +- Until the governance PR is merged and ruleset contexts are updated, the live + ruleset still names removed Python 3.9 checks and does not require the new + `lint`/`dependency-audit` jobs. +- `pypa/gh-action-pypi-publish` remains `continue-on-error: true`, so a release + can partially succeed; tracked in `improvements.md`. +- Project/milestone/stale automation is deliberately deferred until issue/PR + volume meets the triggers in the audit report. diff --git a/improvements.md b/improvements.md index 9739f74..f18ffe0 100644 --- a/improvements.md +++ b/improvements.md @@ -22,6 +22,10 @@ another worktree's. - [ ] *(전제조건: qualified maintainer 2명 이상)* **CODEOWNERS 독립 승인 활성화** — 현재 1인 운영 상태에서 필수 code-owner review를 켜면 운영을 막거나 형식적 self-review만 만든다고 보고서 자체가 명시적으로 경고함. 인원 조건 충족 전엔 시작하지 않음. (enterprise-adoption.md §4, §9 "지금 구현하지 않을 것") - [ ] *(전제조건: 저장소 2개 이상)* **조직 단위 ruleset/reusable workflow/audit export/taxonomy** — 여러 저장소가 같은 운영 문제를 반복할 때 설계 시작. 지금은 저장소가 1개뿐이라 시작 조건 미충족. (enterprise-adoption.md §6, §8 항목 5) +- [ ] *(다음 governance PR merge 직후)* **required-check context 동기화** — ruleset `22101891`에서 Python 3.9 context를 제거하고 Python 3.10 matrix, `lint`, `dependency-audit`를 required로 추가한 뒤 effective-rules API로 재검증한다. +- [ ] *(동일 drift 재발 시)* **ruleset 설정 검증 자동화** — classic branch-protection API와 repository ruleset API를 혼동한 감사 오류 및 CI check-name drift가 다시 발생하면 ruleset-as-code 또는 read-only verification script를 도입한다. +- [ ] **PyPI publish fail-closed 재검토** — Trusted Publisher가 안정화되면 release workflow의 `continue-on-error: true`를 제거해 GitHub Release와 PyPI가 부분 성공으로 갈라지지 않게 한다. +- [ ] **PyPA license metadata 현대화** — 2027-02-18 이전에 deprecated `project.license` table과 license classifier를 SPDX expression / `license-files`로 전환하고 최소 setuptools 버전을 맞춘다. ## Done diff --git a/project-roadmap.md b/project-roadmap.md index 60f5827..2b02bbf 100644 --- a/project-roadmap.md +++ b/project-roadmap.md @@ -19,10 +19,10 @@ before implementation. Concrete selected work belongs in `improvements.md`. - Extend `harness-parity` coverage beyond `preflight`/`init`/`validate` to `check`, `search`, `graph`, and `create` once a real regression in one of those commands under a specific harness demonstrates the gap matters. -- Automate the Antigravity CLI (`agy`) adapter the same way once it has a - package-registry distribution a CI runner can install non-interactively; - today it has none, so `adapters/antigravity/README.md`'s manual - verification is the only signal. +- **Automate the Antigravity CLI (`agy`) adapter install-and-run verification** + — **Done (2026-09-05).** `harness-parity` downloads a versioned Linux release + artifact, verifies its official SHA-512 digest, and exercises the adapter. + The mutable `curl | bash` bootstrapper is intentionally not executed in CI. - **Harness-specific hook support beyond Claude Code SessionStart when equivalent stable extension points exist** — **Evaluated, not pursued (2026-08-31).** The precondition is now true: Codex CLI has a config-driven `SessionStart`/`UserPromptSubmit` hook system (`~/.codex/hooks.json`), and Gemini CLI ships `gemini hooks migrate` specifically to port Claude Code hooks over. But ADR Toolkit doesn't use a hook even on Claude Code today (it relies entirely on skill auto-discovery), and a hook that fires on every session regardless of relevance cuts against this project's own restraint principle (max 3 questions, judge what's significant, minimize interruption). The plausible use cases (nudge about an unfinished draft ADR, warn about a governed path) are already covered by deliberately invoking `discover` and `check` rather than an always-on hook. Revisit only if real usage shows people miss something that `discover`/`check` can't catch without a session-start nudge -- not just because the extension points now exist. ## ADR navigation and scale @@ -43,9 +43,10 @@ before implementation. Concrete selected work belongs in `improvements.md`. ## Public and enterprise governance -- After the repository becomes public, apply and API-verify branch/tag - protections, required checks, conversation resolution, force-push/deletion - controls, and a documented bypass policy. +- **Apply and API-verify repository branch/tag rulesets** — **Done + (2026-09-02; re-verified 2026-09-06).** Active rulesets protect + `master`/`develop`/`release/*` and `v*`; required-check names must be updated + whenever the CI matrix changes. - Add CODEOWNERS and mandatory independent review when the contributor model can actually satisfy it. - Organization-level rulesets, reusable workflows, RBAC, audit export, diff --git a/pyproject.toml b/pyproject.toml index 92daaf4..f284080 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,9 @@ dev = [ "pytest-cov==7.1.0", "mypy==1.19.1", "ruff==0.16.6", + "PyYAML==6.0.3", + "tomli==2.4.0; python_version < '3.11'", + "build==1.4.4", ] [tool.ruff] diff --git a/scripts/export_dev_requirements.py b/scripts/export_dev_requirements.py new file mode 100644 index 0000000..c329db1 --- /dev/null +++ b/scripts/export_dev_requirements.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +"""Export the pinned ``dev`` extra from pyproject.toml as requirements.""" + +from __future__ import annotations + +import argparse +from pathlib import Path +from typing import Sequence + +try: + import tomllib +except ModuleNotFoundError: # Python 3.10 + import tomli as tomllib # type: ignore[no-redef] + + +def dev_requirements(pyproject: Path) -> list[str]: + with pyproject.open("rb") as stream: + document = tomllib.load(stream) + return list(document["project"]["optional-dependencies"]["dev"]) + + +def main(argv: Sequence[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--pyproject", type=Path, default=Path("pyproject.toml")) + args = parser.parse_args(argv) + print(*dev_requirements(args.pyproject), sep="\n") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/unit/test_github_governance.py b/tests/unit/test_github_governance.py new file mode 100644 index 0000000..75092df --- /dev/null +++ b/tests/unit/test_github_governance.py @@ -0,0 +1,102 @@ +from pathlib import Path +import subprocess +import sys + +import yaml + + +ROOT = Path(__file__).resolve().parents[2] + + +def load_yaml(relative_path: str) -> object: + with (ROOT / relative_path).open(encoding="utf-8") as stream: + return yaml.safe_load(stream) + + +def test_dependabot_updates_merge_through_develop() -> None: + config = load_yaml(".github/dependabot.yml") + + assert isinstance(config, dict) + updates = config["updates"] + assert all(update["target-branch"] == "develop" for update in updates) + + +def test_cli_paths_receive_the_cli_area_label() -> None: + config = load_yaml(".github/labeler.yml") + + assert isinstance(config, dict) + cli_rules = config["area:cli"] + patterns = cli_rules[0]["changed-files"][0]["any-glob-to-any-file"] + assert "skills/adr-toolkit/scripts/adr.py" in patterns + assert "skills/adr-toolkit/scripts/commands/**" in patterns + + +def test_ci_does_not_pipe_remote_installers_to_a_shell() -> None: + workflow = (ROOT / ".github/workflows/test.yml").read_text(encoding="utf-8") + + assert "install.sh | bash" not in workflow + + +def test_dependency_audit_installs_dev_requirements_from_pyproject() -> None: + workflow = (ROOT / ".github/workflows/test.yml").read_text(encoding="utf-8") + + assert "scripts/export_dev_requirements.py" in workflow + + +def test_dev_requirement_export_reads_the_requested_extra(tmp_path: Path) -> None: + pyproject = tmp_path / "pyproject.toml" + pyproject.write_text( + '[project.optional-dependencies]\n' + 'dev = ["pytest==9.1.1", "ruff==0.16.6"]\n' + 'docs = ["mkdocs==1.6.1"]\n', + encoding="utf-8", + ) + + result = subprocess.run( + [ + sys.executable, + str(ROOT / "scripts/export_dev_requirements.py"), + "--pyproject", + str(pyproject), + ], + check=False, + capture_output=True, + text=True, + ) + + assert result.returncode == 0, result.stderr + assert result.stdout == "pytest==9.1.1\nruff==0.16.6\n" + + +def test_release_uses_pinned_dependencies_and_action_commits() -> None: + workflow = (ROOT / ".github/workflows/release.yml").read_text(encoding="utf-8") + + assert 'pip install -e ".[dev]"' in workflow + assert "pip install pytest build" not in workflow + for line in workflow.splitlines(): + if "uses:" in line: + ref = line.split("uses:", 1)[1].strip().split("#", 1)[0].strip() + revision = ref.rsplit("@", 1)[1] + assert len(revision) == 40 + assert all(character in "0123456789abcdef" for character in revision) + + +def test_pr_title_is_not_interpolated_into_a_shell_script() -> None: + workflow = (ROOT / ".github/workflows/test.yml").read_text(encoding="utf-8") + + assert 'PR_TITLE: ${{ github.event.pull_request.title }}' in workflow + assert 'TITLE="${{ github.event.pull_request.title }}"' not in workflow + + +def test_labeler_write_permissions_are_scoped_per_job() -> None: + workflow = load_yaml(".github/workflows/labeler.yml") + + assert isinstance(workflow, dict) + assert workflow["permissions"] == {"contents": "read"} + assert workflow["jobs"]["label-pr"]["permissions"] == { + "contents": "read", + "pull-requests": "write", + } + assert workflow["jobs"]["triage-issue"]["permissions"] == { + "issues": "write" + } From 8dc3fd90c9b5f44249f31c3732849f7c683033ef Mon Sep 17 00:00:00 2001 From: shcommit Date: Sun, 6 Sep 2026 01:26:43 +0900 Subject: [PATCH 14/15] feat(github): add type:epic label to taxonomy --- .github/labels.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/labels.yml b/.github/labels.yml index 5d18388..4c77684 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -19,6 +19,9 @@ - name: "type:test" color: "bfdadc" description: "Test-only change" +- name: "type:epic" + color: "3fb950" + description: "Umbrella/tracking issue that groups related work" # --- area: which part of the repo --- - name: "area:core" From e8468e9b47506207dbd2858a4a77f7772f01bc6b Mon Sep 17 00:00:00 2001 From: shcommit Date: Sun, 6 Sep 2026 01:46:02 +0900 Subject: [PATCH 15/15] docs(handoff): reflect develop merge and superseded PR #17 --- handoff.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/handoff.md b/handoff.md index 8d2483d..0093b16 100644 --- a/handoff.md +++ b/handoff.md @@ -19,22 +19,22 @@ GitHub assumptions and closed additional supply-chain/configuration gaps. ## Live GitHub changes applied -- Synced 25 labels without deleting unrelated labels. +- Synced labels without deleting unrelated labels; added `type:epic` this session. - Enabled Discussions, Dependabot security updates/alerts, secret scanning and push protection, private vulnerability reporting, and delete-branch-on-merge. - Re-verified active branch ruleset `22101891` and tag ruleset `22102322`. ## Next step -1. Merge PR #17 (`chore/sync-develop-with-v1.0.1`) first because this branch is - based on it. -2. Fetch the updated `develop` and merge it into `feature/oss-governance-hardening` - before opening the governance PR, because `origin/develop` also contains - newer adapter work. -3. Push `feature/oss-governance-hardening` and open a PR to `develop`. -4. After its new Python 3.10/lint/audit checks have run, update ruleset - `22101891`: remove the two Python 3.9 contexts; add all three Python 3.10 - contexts plus `lint` and `dependency-audit`; query effective rules again. +1. Push `feature/oss-governance-hardening` and open a PR to `develop`. This + branch already merged `origin/develop` (Cline adapter + improvements + backlog) and carries the v1.0.1 sync from PR #17. +2. Close PR #17 (`chore/sync-develop-with-v1.0.1`) as superseded — its head + `508b860` is already an ancestor of this branch. +3. After the PR's new Python 3.10/lint/audit checks run green, merge and then + update ruleset `22101891`: remove the two Python 3.9 contexts; add all three + Python 3.10 contexts plus `lint` and `dependency-audit`; query effective + rules again. ## Verification