---
phase: 15
slug: ci-skip-api-harness-jobs-for-doc-only-prs
status: draft
nyquist_compliant: true
wave_0_complete: false
created: 2026-06-12
---

# Phase 15 — Validation Strategy

> Per-phase validation contract for feedback sampling during execution.

This phase is CI workflow config + tool config + mechanical markdown fixes. There is
no application business logic; the gate-job result logic is validated by static YAML
checks plus real CI behavior on doc-only vs code PRs (not a unit-test framework).

---

## Test Infrastructure

| Property | Value |
|----------|-------|
| **Framework** | Static (markdownlint-cli2 + Prettier + `yq`/`grep`) for local checks; Gitea Actions CI (behavioral) for SC-1/2/3 |
| **Config file** | `.markdownlint-cli2.jsonc` (new, Plan 01); `.gitea/workflows/ci.yml` (modified, Plans 01/02) |
| **Quick run command** | `pnpm md:lint && pnpm format:check` |
| **Full suite command** | `pnpm md:lint && pnpm format:check` locally; then a doc-only PR + a code PR observed in Gitea Actions |
| **Estimated runtime** | local ~3s; CI doc-only PR ~30s; CI code PR ~5min |

---

## Sampling Rate

- **After every task commit:** Run `pnpm md:lint` (Plan 01) or `yq -e '.jobs.<job>' .gitea/workflows/ci.yml` (Plan 02).
- **After every plan wave:** Run `pnpm md:lint && pnpm format:check`; for Plan 02, parse ci.yml with `yq` and grep the gate's per-job result checks.
- **Before `/gsd-verify-work`:** local checks green; CI behavioral checks observed on throwaway PRs (Plan 03 checkpoint).
- **Max feedback latency:** ~3s local; CI behavioral confirmation is the operator checkpoint in Plan 03.

---

## Per-Task Verification Map

| Task ID | Plan | Wave | Success Criterion | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------------|------------|-----------------|-----------|-------------------|-------------|--------|
| 15-01-01 | 01 | 1 | SC-4 (setup) | T-15-01/02/03 | Pinned devDep; `.planning/**` excluded from lint scope | static | `test -f .markdownlint-cli2.jsonc && node -e "require('./package.json').scripts['md:lint']"` | ❌ W0 (config new) | ⬜ pending |
| 15-01-02 | 01 | 1 | SC-4 | T-15-03 | Gate can fail on a real violation; baseline green; no Prettier conflict | static | `pnpm md:lint && pnpm format:check` | ✅ (after fixes) | ⬜ pending |
| 15-02-01 | 02 | 2 | SC-1, SC-2 | T-15-05/06 | Positive `code` filter — ambiguous files default to full gate; action pinned @v4 | static | `yq -e '.jobs.changes.outputs.code' ci.yml && grep "needs.changes.outputs.code == 'true'" ci.yml` | ❌ W0 (jobs new) | ⬜ pending |
| 15-02-02 | 02 | 2 | SC-1, SC-2, SC-3(YAML) | T-15-04/07 | Gate fails on fast-checks!=success; individual `needs.X.result` (no broken wildcard) | static | `yq -e '.jobs.gate.if == "always()"' ci.yml && grep 'needs.fast-checks.result' ci.yml && ! grep 'needs.\*.result' ci.yml` | ❌ W0 (gate new) | ⬜ pending |
| 15-03-01 | 03 | 3 | SC-3 (docs) | T-15-10 | publish.yml comment matches new required checks | static | `grep 'CI / gate' .gitea/workflows/publish.yml` | ✅ (publish.yml exists) | ⬜ pending |
| 15-03-02 | 03 | 3 | SC-1, SC-2, SC-3 | T-15-08/09 | Drop api/harness required; require gate; only after `CI / gate` reports | manual (operator) | Gitea branch-protection UI/API + throwaway doc-only & code PRs | Manual | ⬜ pending |

*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*

---

## Wave 0 Requirements

These artifacts do not exist before this phase and are created within it (no separate test scaffold needed — the "tests" are the static checks above):

- [ ] `.markdownlint-cli2.jsonc` — new config (Plan 01 Task 1); required before `pnpm md:lint` runs
- [ ] root `md:lint` script + `markdownlint-cli2` devDep (Plan 01 Task 1)
- [ ] 13 baseline markdown violations fixed (Plan 01 Task 2) — needed for `pnpm md:lint` to pass
- [ ] `changes` + `gate` jobs in ci.yml (Plan 02) — needed before any `yq`/grep gate check passes
- [ ] Gitea branch-protection update — manual operator step (Plan 03 Task 2), only after `CI / gate` has reported at least once

---

## Manual-Only Verifications

| Behavior | Success Criterion | Why Manual | Test Instructions |
|----------|-------------------|------------|-------------------|
| Branch protection requires `CI / fast-checks` + `CI / gate`, not `CI / api`/`CI / harness` | SC-3 | Gitea admin config — cannot be set in a workflow YAML file (RESEARCH Gitea-Specific Note #4) | Gitea → Settings → Branches → edit `main` rule; set required contexts to exactly `CI / fast-checks` + `CI / gate`; remove `CI / api` + `CI / harness` (Plan 03 Task 2) |
| Doc-only PR is mergeable with api/harness skipped | SC-1, SC-3 | Requires a live PR run + the live protection rule | Open a throwaway `*.md`-only PR; confirm api/harness skipped, `CI / gate` passes, PR mergeable |
| Code PR runs and gates on all jobs | SC-2 | Requires a live PR run | Open a throwaway `*.ts` PR; confirm fast-checks/api/harness/gate all run; PR mergeable only when green |

---

## Validation Sign-Off

- [x] All tasks have `<automated>`/static verify or are the designated manual operator checkpoint
- [x] Sampling continuity: no 3 consecutive code tasks without automated verify (the only manual task is the unavoidable Gitea-admin checkpoint)
- [x] Wave 0 covers all newly-created artifacts
- [x] No watch-mode flags
- [x] Feedback latency ~3s local
- [x] `nyquist_compliant: true` set in frontmatter

**Approval:** pending
