diff --git a/.github/scripts/pr-docs.cjs b/.github/scripts/pr-docs.cjs index 312c72e63e..d919df1c38 100644 --- a/.github/scripts/pr-docs.cjs +++ b/.github/scripts/pr-docs.cjs @@ -256,6 +256,9 @@ function pathExemption(pathname) { if (pathname.startsWith('apps/web/e2e/')) { return 'web end-to-end test'; } + if (/^\.github\/(?:workflows|scripts|actions)\//.test(pathname)) { + return 'CI infrastructure path'; + } const basename = POSIX_PATH.basename(pathname); const extension = POSIX_PATH.extname(basename).slice(1).toLowerCase(); diff --git a/.github/scripts/pr-docs.test.cjs b/.github/scripts/pr-docs.test.cjs index 0f152c06e8..14ad613255 100644 --- a/.github/scripts/pr-docs.test.cjs +++ b/.github/scripts/pr-docs.test.cjs @@ -92,10 +92,57 @@ test('recognized harness configuration paths are exempt', () => { ]); }); +// @covers AC-CI-PR-DOCS-001.8 +test('CI infrastructure paths under .github are exempt', () => { + const result = validator.classifyChangedFiles([ + { filename: '.github/workflows/release.yml', status: 'modified' }, + { filename: '.github/scripts/pr-walkthrough-workflow-contract_test.py', status: 'modified' }, + { filename: '.github/actions/setup-opencode/action.yml', status: 'modified' }, + ]); + + assert.equal(result.requiresCoverage, false); + assert.deepEqual(result.triggeringPaths, []); + assert.deepEqual(result.exemptPaths, [ + '.github/workflows/release.yml', + '.github/scripts/pr-walkthrough-workflow-contract_test.py', + '.github/actions/setup-opencode/action.yml', + ]); +}); + +// @covers AC-CI-PR-DOCS-001.8 +test('a CI path combined with a non-exempt path still requires coverage', () => { + const result = validator.classifyChangedFiles([ + { filename: '.github/workflows/release.yml', status: 'modified' }, + { filename: 'apps/backend/runtime.go', status: 'modified' }, + ]); + + assert.equal(result.requiresCoverage, true); + assert.deepEqual(result.exemptPaths, ['.github/workflows/release.yml']); + assert.deepEqual(result.triggeringPaths, ['apps/backend/runtime.go']); +}); + +// @covers AC-CI-PR-DOCS-001.8 +test('non-CI files under .github and CI paths outside .github require coverage', () => { + const result = validator.classifyChangedFiles([ + { filename: '.github/dependabot.yml', status: 'modified' }, + { filename: '.github/release-signing-key.asc', status: 'modified' }, + { filename: 'scripts/build.sh', status: 'modified' }, + { filename: 'workflows/ci.yml', status: 'modified' }, + ]); + + assert.equal(result.requiresCoverage, true); + assert.deepEqual(result.exemptPaths, []); + assert.deepEqual(result.triggeringPaths, [ + '.github/dependabot.yml', + '.github/release-signing-key.asc', + 'scripts/build.sh', + 'workflows/ci.yml', + ]); +}); + // @covers AC-CI-PR-DOCS-001.3 test('shipped files and unsupported test-like paths require coverage', () => { const result = validator.classifyChangedFiles([ - { filename: '.github/workflows/release.yml', status: 'modified' }, { filename: 'apps/web/package.json', status: 'modified' }, { filename: 'apps/web/lib/fixture.test.json', status: 'modified' }, { filename: 'apps/backend/worker_test.go.txt', status: 'modified' }, @@ -106,7 +153,6 @@ test('shipped files and unsupported test-like paths require coverage', () => { assert.equal(result.requiresCoverage, true); assert.deepEqual(result.triggeringPaths, [ - '.github/workflows/release.yml', 'apps/web/package.json', 'apps/web/lib/fixture.test.json', 'apps/backend/worker_test.go.txt', diff --git a/.github/scripts/pr-walkthrough-workflow-contract_test.py b/.github/scripts/pr-walkthrough-workflow-contract_test.py index 2645862c97..3eb6fe7ff2 100644 --- a/.github/scripts/pr-walkthrough-workflow-contract_test.py +++ b/.github/scripts/pr-walkthrough-workflow-contract_test.py @@ -183,7 +183,7 @@ class PRWalkthroughWorkflowContractTest(unittest.TestCase): self.assertIn("arbitrary Git or shell commands", self.skill) def test_generation_uses_requested_model_native_high_reasoning_variant(self) -> None: - model = "opencode-go/muse-spark-1.3-contributor" + model = "opencode/muse-spark-1.3-contributor-free" variant = "high" self.assertIn(f"PR_WALKTHROUGH_MODEL: {model}", self.workflow) self.assertIn(f"PR_WALKTHROUGH_VARIANT: {variant}", self.workflow) diff --git a/.github/workflows/pr-walkthrough.yml b/.github/workflows/pr-walkthrough.yml index 739f754a65..499efb2b5b 100644 --- a/.github/workflows/pr-walkthrough.yml +++ b/.github/workflows/pr-walkthrough.yml @@ -8,7 +8,11 @@ on: types: [opened, ready_for_review, reopened, synchronize, labeled] env: - PR_WALKTHROUGH_MODEL: opencode-go/muse-spark-1.3-contributor + # Free model, accepted explicitly: it may use prompts and completions to + # train future Meta models, so PR patches and prepared head context are + # subject to that policy. To revoke, restore a non-training model such as + # opencode-go/muse-spark-1.3-contributor here and in the agent block below. + PR_WALKTHROUGH_MODEL: opencode/muse-spark-1.3-contributor-free PR_WALKTHROUGH_VARIANT: high concurrency: @@ -152,7 +156,7 @@ jobs: --- description: Generate and render a visual pull request walkthrough. mode: primary - model: "opencode-go/muse-spark-1.3-contributor" + model: "opencode/muse-spark-1.3-contributor-free" temperature: 0.1 permission: "*": deny diff --git a/docs/decisions/2026-09-10-pr-documentation-coverage.md b/docs/decisions/2026-09-10-pr-documentation-coverage.md index 6466e4e5b1..6c8744d12f 100644 --- a/docs/decisions/2026-09-10-pr-documentation-coverage.md +++ b/docs/decisions/2026-09-10-pr-documentation-coverage.md @@ -23,6 +23,8 @@ The override persists while the label is present and affects only this check. The exact `plugin-registry/plugins.yaml` source is exempt when it is the only non-documentation path changed. The exemption does not include other files under `plugin-registry/`; a pull request that also changes any non-exempt path still requires linked delivery context. +CI infrastructure paths under `.github/workflows/**`, `.github/scripts/**`, and `.github/actions/**` are exempt because they change the delivery pipeline, not shipped product behavior, and are already governed by workflow contract tests. The exemption is directory-scoped; it does not cover configuration, keys, or other files under `.github/`, nor workflows, scripts, or actions outside `.github/`. A pull request that also changes any non-exempt path still requires linked delivery context. + Harness configuration and definitions are exempt when they use the repository's recognized harness formats: Codex agent or config TOML, Claude settings JSON, or Cursor rule MDC. Markdown harness files are covered by the general Markdown exemption. This avoids requiring delivery records for contributor tooling changes while keeping arbitrary JSON, YAML, and other files subject to normal evaluation. CI owns this policy. It does not require use of a particular agent or prove that plans were committed before code. diff --git a/docs/plans/pr-documentation-coverage/plan.md b/docs/plans/pr-documentation-coverage/plan.md index 92b79597a7..0c768a0bd9 100644 --- a/docs/plans/pr-documentation-coverage/plan.md +++ b/docs/plans/pr-documentation-coverage/plan.md @@ -1,6 +1,6 @@ --- created: 2026-09-10 -updated: 2026-09-16 +updated: 2026-09-21 status: done requirements: - REQ-CI-PR-DOCS-001 @@ -65,6 +65,7 @@ Live smoke tests and required-check activation are deployment steps, not complet - [x] [Task 02: Report pull request coverage](task-02-pr-workflow.md) - [x] [Task 03: Support merge queue coverage](task-03-queue-coverage.md) - [x] [Task 04: Exempt the canonical plugin registry source](task-04-exempt-plugin-registry-source.md) +- [x] [Task 05: Exempt CI infrastructure paths](task-05-exempt-ci-paths.md) Execute sequentially. No subagents are authorized. @@ -116,6 +117,26 @@ Task 04 verification completed on 2026-09-16: - `zizmor .github/workflows/pr-docs.yml` reported no findings. - The specification catalog and linter passed; `git diff --check` passed. +## Amendment: CI infrastructure paths + +The original four work orders remain complete. This amendment exempts the CI +infrastructure paths `.github/workflows/**`, `.github/scripts/**`, and +`.github/actions/**` from requiring a delivery package. CI pipeline changes do +not ship product behavior and are already covered by workflow contract tests, +so requiring delivery context for them adds process without design value. The +exemption is directory-scoped; workflows, scripts, or actions outside `.github/` +still require coverage. + +Task 05 implements the exemption and updates the path classifier tests, +requirement, system design, decision record, and plan. + +Task 05 verification completed on 2026-09-21: + +- 80 validator tests, 7 workflow contract tests, and 9 action-pinning tests passed. +- All 24 workflow files passed action-pinning lint. +- `zizmor .github/workflows/pr-docs.yml` reported no findings. +- The specification catalog and linter passed; `git diff --check` passed. + ## Risks - Conservative defaults require labels for some small fixes, refactors, and dependency manifest changes. diff --git a/docs/plans/pr-documentation-coverage/task-05-exempt-ci-paths.md b/docs/plans/pr-documentation-coverage/task-05-exempt-ci-paths.md new file mode 100644 index 0000000000..5367999cd8 --- /dev/null +++ b/docs/plans/pr-documentation-coverage/task-05-exempt-ci-paths.md @@ -0,0 +1,119 @@ +--- +id: "05-exempt-ci-paths" +title: "Exempt CI infrastructure paths" +status: done +wave: 5 +depends_on: + - "04-exempt-plugin-registry-source" +plan: "plan.md" +requirements: + - REQ-CI-PR-DOCS-001 +acceptance_criteria: + - AC-CI-PR-DOCS-001.8 +system_design: + - ../../specs/ci/system-design/pull-request-documentation-coverage.md +--- + +# Task 05: Exempt CI infrastructure paths + +## Summary + +Allow a pull request that changes only CI infrastructure under +`.github/workflows/**`, `.github/scripts/**`, or `.github/actions/**` to pass +documentation coverage without a linked delivery package. Keep every other +path, including workflows, scripts, and actions outside `.github/`, covered. + +## In scope + +- Add the `.github/workflows/**`, `.github/scripts/**`, and `.github/actions/**` + path families to the classifier exemption list. +- Add regression coverage for the CI-only and mixed-path cases, including a + workflow, script, or action outside `.github/` that still triggers coverage. +- Update the existing validator test that asserts `.github/workflows/release.yml` + requires coverage. +- Reconcile the CI requirement, system design, decision, and plan records. + +## Out of scope + +- Exempting the full `.github/` tree, including configuration, keys, or docker + files under `.github/`. +- Exempting workflows, scripts, or actions outside `.github/`. +- Changing the label override, rulesets, or merge enforcement. + +## Acceptance + +- A change containing only paths under `.github/workflows/**`, + `.github/scripts/**`, or `.github/actions/**` is classified as exempt and does + not require delivery artifacts. +- A CI change combined with any non-exempt path still requires coverage. +- A workflow, script, or action outside `.github/` still triggers coverage. +- Existing path exemptions and generic YAML classification remain unchanged. + +## Verification + +Run from the repository root: + +```bash +node --test .github/scripts/pr-docs.test.cjs +python3 .github/scripts/pr-docs-workflow-contract_test.py +python3 .github/scripts/lint-action-pinning_test.py +python3 .github/scripts/lint-action-pinning.py +zizmor .github/workflows/pr-docs.yml +python3 scripts/list-docs.py validate +python3 scripts/lint-spec-files.py --all +git diff --check +``` + +## Files likely touched + +- `.github/scripts/pr-docs.cjs` +- `.github/scripts/pr-docs.test.cjs` +- `docs/specs/ci/requirements/pull-request-documentation-coverage.md` +- `docs/specs/ci/system-design/pull-request-documentation-coverage.md` +- `docs/decisions/2026-09-10-pr-documentation-coverage.md` +- `docs/plans/pr-documentation-coverage/plan.md` + +## Dependencies + +Task 04. The existing coverage validator and workflow must remain the +implementation boundary. + +## Risks + +- A directory-prefix exemption could accidentally cover `.github/` files outside + the three CI path families. Use directory-scoped matching and a negative test + for a non-CI `.github/` file. +- Broader than the registry exemption: three path families instead of one exact + path. Keep the scope confined to `.github/` CI infrastructure. + +## Parallelism + +`sequential` + +## Inputs + +- [Requirements](../../specs/ci/requirements/pull-request-documentation-coverage.md) +- [System design](../../specs/ci/system-design/pull-request-documentation-coverage.md) +- [Plan](plan.md) +- [Decision](../../decisions/2026-09-10-pr-documentation-coverage.md) +- `.github/AGENTS.md` and the existing validator tests. + +## Results + +Implemented the `.github/workflows/**`, `.github/scripts/**`, and +`.github/actions/**` exemptions in the path classifier and added positive, +mixed-path, and negative regression coverage. The previous +`.github/workflows/release.yml` coverage assertion moved to the CI exemption +test. Non-CI `.github/` files and CI paths outside `.github/` still trigger +coverage. + +Verification: + +- `node --test .github/scripts/pr-docs.test.cjs`: 80 tests passed. +- `python3 .github/scripts/pr-docs-workflow-contract_test.py`: 7 tests passed. +- `python3 .github/scripts/lint-action-pinning_test.py`: 9 tests passed. +- `python3 .github/scripts/lint-action-pinning.py`: 24 workflows passed. +- `zizmor .github/workflows/pr-docs.yml`: no findings. +- `python3 scripts/list-docs.py validate`: 294 decisions and 1066 specifications validated. +- `python3 scripts/lint-spec-files.py --all`: passed. +- `git diff --check`: passed. \ No newline at end of file diff --git a/docs/plans/pr-walkthrough/plan.md b/docs/plans/pr-walkthrough/plan.md index 6445eb7519..2b2c74a904 100644 --- a/docs/plans/pr-walkthrough/plan.md +++ b/docs/plans/pr-walkthrough/plan.md @@ -61,10 +61,16 @@ decoupled from the code-review App token. The job runs for `opened`, runs when the label is `generate-pr-walkthrough`. Use the shared base-controlled setup action for OpenCode. Select -`opencode-go/muse-spark-1.2-contributor` with `--model`. Select its built-in +`opencode/muse-spark-1.3-contributor-free` with `--model`. Select its built-in `high` reasoning variant with `--variant`. The pinned OpenCode 1.17.7 model catalog declares this variant, so no custom provider override is necessary. +This model is free but may use prompts and completions to train future Meta +models. This is an accepted, explicit decision: the PR patch and prepared +PR-head context are subject to that data policy. To revoke, restore a +non-training model such as `opencode-go/muse-spark-1.3-contributor` in +`.github/workflows/pr-walkthrough.yml`. + The job will: 1. Check out `github.event.pull_request.head.sha` with full history and no diff --git a/docs/specs/ci/requirements/pull-request-documentation-coverage.md b/docs/specs/ci/requirements/pull-request-documentation-coverage.md index eb45534cc6..6f54c4e45f 100644 --- a/docs/specs/ci/requirements/pull-request-documentation-coverage.md +++ b/docs/specs/ci/requirements/pull-request-documentation-coverage.md @@ -2,7 +2,7 @@ status: draft system: ci created: 2026-09-10 -updated: 2026-09-16 +updated: 2026-09-21 owners: - kandev --- @@ -29,12 +29,13 @@ Contributors can write the artifacts manually; use of the repository harness is #### Acceptance criteria - **AC-CI-PR-DOCS-001.1:** Every open pull request shall receive a documentation coverage result, including drafts and forks. -- **AC-CI-PR-DOCS-001.2:** A pull request containing only recognized documentation, tests, translation catalogs, dependency lock, or harness changes shall pass without a delivery package. +- **AC-CI-PR-DOCS-001.2:** A pull request containing only recognized documentation, tests, translation catalogs, dependency lock, harness, or CI workflow, script, or action changes shall pass without a delivery package. - **AC-CI-PR-DOCS-001.3:** Other changes shall require an added or modified work order, its plan, and linked requirements and system designs. Changing the title to `fix`, `chore`, or `refactor` shall not exempt the change. - **AC-CI-PR-DOCS-001.4:** Existing plans and specifications shall qualify when the work order references them and they exist in the proposed revision. Contributors shall update contracts when behavior changes, but shall not need meaningless edits to unchanged contracts. - **AC-CI-PR-DOCS-001.5:** A deleted artifact, empty file, unresolved reference, unrelated unlinked document, or work order without requirement and acceptance references shall not satisfy coverage. - **AC-CI-PR-DOCS-001.6:** The result shall identify triggering paths, accepted references, missing artifacts, and corrective steps. Structural coverage shall not claim semantic completeness or prove that planning preceded coding. - **AC-CI-PR-DOCS-001.7:** A pull request that changes only `plugin-registry/plugins.yaml` and other already exempt paths shall pass without a delivery package. Any additional non-exempt path shall continue to require coverage. +- **AC-CI-PR-DOCS-001.8:** A pull request that changes only recognized CI infrastructure paths under `.github/workflows/**`, `.github/scripts/**`, or `.github/actions/**` shall pass without a delivery package. Any additional non-exempt path shall continue to require coverage. ### REQ-CI-PR-DOCS-002: Explicit documentation exception diff --git a/docs/specs/ci/system-design/pull-request-documentation-coverage.md b/docs/specs/ci/system-design/pull-request-documentation-coverage.md index 37a17a1fec..29de93af87 100644 --- a/docs/specs/ci/system-design/pull-request-documentation-coverage.md +++ b/docs/specs/ci/system-design/pull-request-documentation-coverage.md @@ -1,6 +1,6 @@ --- status: draft -last_updated: 2026-09-16 +last_updated: 2026-09-21 system: ci requirements: - REQ-CI-PR-DOCS-001 @@ -46,9 +46,11 @@ Initial exemptions: - Exact canonical registry source `plugin-registry/plugins.yaml`. - Exact lock basenames `pnpm-lock.yaml`, `package-lock.json`, `yarn.lock`, `go.sum`, and `Cargo.lock`. - Recognized non-Markdown harness files: `.codex/agents/*.toml`, `.codex/config.toml`, `.claude/settings.json`, and `.cursor/rules/*.mdc`. +- CI infrastructure paths `.github/workflows/**`, `.github/scripts/**`, and `.github/actions/**`. These change the delivery pipeline, not shipped product behavior, and are already governed by workflow contract tests. Keep other `plugin-registry/**` paths subject to normal coverage. Do not exempt all JSON, YAML, assets, scripts, package manifests, Rust files, workflows, generated directories, or files containing the word `test`. -These can change shipped behavior or repository contracts. Add exemptions later only with concrete fixtures. +These can change shipped behavior or repository contracts. The `.github/` CI exemption is a directory-scoped rule; a workflow, script, or action outside `.github/` still requires coverage. +Add exemptions later only with concrete fixtures. This conservative policy creates false positives for small runtime fixes and refactors. The explicit label is their escape hatch. ## Artifact contract diff --git a/docs/specs/ui/requirements/pr-walkthrough.md b/docs/specs/ui/requirements/pr-walkthrough.md index 6ca00b57b8..6526f05fca 100644 --- a/docs/specs/ui/requirements/pr-walkthrough.md +++ b/docs/specs/ui/requirements/pr-walkthrough.md @@ -92,9 +92,9 @@ the dedicated Cloudflare R2 walkthrough bucket. Lifecycle controls retention. variable. The trusted, non-generating repair path for edited descriptions is in `.github/workflows/pr-walkthrough-reconcile.yml`. It does not share the `OPENCODE_REVIEW_ENABLED` code-review gate. -- The initial runner uses `opencode-go/muse-spark-1.3-contributor` and its - built-in `high` reasoning variant. The workflow passes these values with the - OpenCode 1.17.7 `--model` and `--variant` options. +- The runner uses `opencode/muse-spark-1.3-contributor-free` with the `high` + variant (`--model`/`--variant`). Free tier may train on prompts (accepted). + Revoke: `opencode-go/muse-spark-1.3-contributor`. - The workflow preserves the generated JSON and HTML as CI artifacts and uploads only the HTML to the `kandev-pr-walkthroughs` R2 bucket. - Each published object uses the key