3 plans across 3 waves: - 15-01: markdownlint-cli2 config/script/step + fix 13 baseline violations (SC-4) - 15-02: ci.yml changes job + conditional api/harness + always-running gate (SC-1/2, SC-3 YAML) - 15-03: operator branch-protection checkpoint + publish.yml comment (SC-3)
9.8 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 15-ci-skip-api-harness-jobs-for-doc-only-prs | 03 | execute | 3 |
|
|
false |
|
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.
<execution_context> @$HOME/.claude/gsd-core/workflows/execute-plan.md @$HOME/.claude/gsd-core/templates/summary.md </execution_context>
@.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.
<artifacts_produced> 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
mainchanged toCI / fast-checks+CI / gate(Gitea admin state, not a repo file) - publish.yml safety-gate comment updated to name the new required checks </artifacts_produced>
<threat_model>
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. |
| </threat_model> |
<success_criteria>
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.)
</success_criteria>