--- phase: 15-ci-skip-api-harness-jobs-for-doc-only-prs plan: 03 type: execute wave: 3 depends_on: - 15-02 files_modified: - .gitea/workflows/publish.yml autonomous: false requirements: [] must_haves: truths: - "Branch protection on main requires CI / fast-checks + CI / gate" - "Branch protection on main no longer requires CI / api or CI / harness directly" - "A doc-only PR (which skips api/harness) is mergeable — no missing-required-check deadlock" - "publish.yml's safety-gate comment names the new required checks (fast-checks + gate)" artifacts: - path: ".gitea/workflows/publish.yml" provides: "Updated safety-gate comment naming the new required checks" contains: "CI / gate" key_links: - from: "Gitea branch-protection (main)" to: "ci.yml gate job" via: "required status check 'CI / gate' (always-running aggregate)" pattern: "CI / gate" --- Finalize the gating surface: update Gitea branch protection on `main` to require `CI / fast-checks` + `CI / gate` and DROP the now-skippable `CI / api` and `CI / harness` direct requirements, and update the `publish.yml` safety-gate comment to name the new required checks. This delivers Success Criterion 3 — the only step that makes doc-only PRs actually mergeable without a missing-required-check deadlock. Purpose: With the heavy jobs conditionally skipped (Plan 02), a doc-only PR no longer emits `CI / api` / `CI / harness` statuses; leaving them required would deadlock the merge. The always-running `CI / gate` is the correct gating surface. Output: A Gitea branch-protection change (operator checkpoint — not automatable in YAML) + a one-comment edit to publish.yml. ORDERING HAZARD (read before executing): This plan MUST run only AFTER Plan 02's ci.yml change has merged to `main` and Gitea has emitted at least one `CI / gate` commit-status. Dropping `CI / api` + `CI / harness` before `CI / gate` exists would leave `main` with no valid heavy-job gate, and adding `CI / gate` as required before it has ever reported can itself block PRs. Confirm `CI / gate` has appeared on a recent run before changing branch protection. @$HOME/.claude/gsd-core/workflows/execute-plan.md @$HOME/.claude/gsd-core/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/15-ci-skip-api-harness-jobs-for-doc-only-prs/15-RESEARCH.md @.planning/phases/15-ci-skip-api-harness-jobs-for-doc-only-prs/15-PATTERNS.md Task 1: Update the publish.yml safety-gate comment to name the new required checks - .gitea/workflows/publish.yml (lines 13-17 — the "Safety gate" comment block currently naming "the three required checks (CI / fast-checks, CI / api, CI / harness)") - .planning/phases/15-ci-skip-api-harness-jobs-for-doc-only-prs/15-PATTERNS.md ("publish.yml Comment Update (advisory)" section — exact current text and the required-check change) Edit ONLY the safety-gate comment in publish.yml (lines ~14-17). Change the clause that names "the three required checks (CI / fast-checks, CI / api, CI / harness)" to name the new required checks: `CI / fast-checks` and `CI / gate`. Reflect that `api`/`harness` are now conditionally skipped and gated via the always-running `CI / gate` aggregate rather than being required directly. Do not change any publish.yml job, step, env, or trigger — comment text only. This is the documentation half; the actual protection change is Task 2 (operator). grep -q 'CI / gate' .gitea/workflows/publish.yml && grep -q 'CI / fast-checks' .gitea/workflows/publish.yml && ! grep -qE 'three required checks \(CI / fast-checks, CI / api, CI / harness\)' .gitea/workflows/publish.yml && command -v yq >/dev/null 2>&1 && yq -e '.jobs.publish' .gitea/workflows/publish.yml >/dev/null && echo OK - publish.yml safety-gate comment names `CI / fast-checks` and `CI / gate` as the required checks - publish.yml safety-gate comment no longer asserts `CI / api` and `CI / harness` are required (the old "three required checks" line is replaced) - no publish.yml job/step/env/trigger changed (only comment text); publish.yml still parses as valid YAML publish.yml's safety-gate rationale matches the new branch-protection reality. Task 2: Operator — update Gitea branch protection on main (drop api/harness, require gate) - .planning/phases/15-ci-skip-api-harness-jobs-for-doc-only-prs/15-RESEARCH.md (Gitea-Specific Notes #4 — branch-protection required-check update is a MANUAL Gitea admin step; Pitfall 1 — required-check deadlock from skipped jobs) Plan 02 added an always-running `CI / gate` aggregate job to ci.yml and made `CI / api` / `CI / harness` conditionally skipped on doc-only PRs. Once Plan 02 merged, Gitea emits a `CI / gate` commit-status on every PR run. Branch protection still requires the old `CI / api` + `CI / harness` contexts, which a doc-only PR will never emit — so until you make this change, doc-only PRs deadlock on "missing required checks". This change is a Gitea admin action that cannot be done in a workflow file. PRECONDITION — confirm first: open the most recent PR run in Gitea Actions for this repo and verify a `CI / gate` job ran and reported a status. Do NOT proceed until `CI / gate` has appeared at least once (Plan 02 must already be merged to main). If it has not appeared, stop — the protection change is premature. Then update branch protection: 1. Gitea → this repo → Settings → Branches → edit the protection rule for `main`. 2. Under "Status Check Patterns" / required status checks, set the required contexts to EXACTLY these two: - `CI / fast-checks` - `CI / gate` 3. REMOVE these two from the required list (they are now conditionally skipped and gated via `CI / gate`): - `CI / api` - `CI / harness` 4. Save the protection rule. (Alternative: the `tea` CLI / Gitea API can set branch-protection `status_check_contexts` to `["CI / fast-checks", "CI / gate"]` — login Bergerhouse — if you prefer not to use the UI.) VERIFY the change end-to-end: - Open a throwaway DOC-ONLY PR (edit only a `*.md` under docs/ or the repo root). Expect: `fast-checks` runs, `api`/`harness` show skipped, `CI / gate` passes, and the PR is MERGEABLE (no "missing required checks"). This proves SC-1 + SC-3. - Open a throwaway CODE PR (touch a `*.ts` file). Expect: `fast-checks`, `api`, `harness`, and `CI / gate` all run; the PR is mergeable only when all are green. This proves SC-2. - Close both throwaway PRs without merging. Type "approved" once branch protection requires exactly `CI / fast-checks` + `CI / gate`, `CI / api` and `CI / harness` are no longer required, and the doc-only + code throwaway PRs behaved as described. Or describe what differed. This phase introduces the following new configuration state (Plan 03 portion). The plan-review source-grounding pass must treat these as newly-created, not drift: - Branch-protection required checks on `main` changed to `CI / fast-checks` + `CI / gate` (Gitea admin state, not a repo file) - publish.yml safety-gate comment updated to name the new required checks ## Trust Boundaries | Boundary | Description | |----------|-------------| | operator → Gitea branch-protection config | the required-check set decides whether unreviewed/failing code can merge to main | | skipped heavy job → branch-protection check list | a skipped job may emit no commit-status; a still-required skipped context would deadlock | ## STRIDE Threat Register | Threat ID | Category | Component | Disposition | Mitigation Plan | |-----------|----------|-----------|-------------|-----------------| | T-15-08 | Denial of Service (deadlock) | leaving `CI / api`/`CI / harness` required after the skip logic ships | mitigate | This plan explicitly drops both from required checks and requires the always-running `CI / gate` instead (RESEARCH Pitfall 1 / Gitea-Specific Note #4). | | T-15-09 | Elevation of Privilege (bypass window) | dropping the heavy-job requirements BEFORE `CI / gate` exists leaves main ungated for code | mitigate | Ordering hazard called out: this plan is wave 3, `depends_on: [15-02]`; the checkpoint's PRECONDITION requires confirming `CI / gate` has reported at least once before any required-check edit. | | T-15-10 | Tampering (false sense of gating) | publish.yml comment still claims api/harness are required, misleading a future maintainer | mitigate | Task 1 updates the comment to name `CI / fast-checks` + `CI / gate`. | - Gitea branch protection for `main` requires exactly `CI / fast-checks` + `CI / gate`; `CI / api` and `CI / harness` are not required. - Throwaway doc-only PR is mergeable with api/harness skipped (SC-1/SC-3). - Throwaway code PR runs and gates on all jobs (SC-2). - publish.yml safety-gate comment names the new required checks. Maps to Phase 15 Success Criterion 3: branch protection requires `CI / fast-checks` + an always-running `CI / gate` aggregate; the direct `api`/`harness` requirements are dropped so a skipped heavy job never deadlocks the merge. (SC-1 doc-only mergeability and SC-2 code-PR gating are confirmed end-to-end here against the live protection rule.) Create `.planning/phases/15-ci-skip-api-harness-jobs-for-doc-only-prs/15-03-SUMMARY.md` when done.