Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
180 lines
13 KiB
Markdown
180 lines
13 KiB
Markdown
---
|
|
phase: 15-ci-skip-api-harness-jobs-for-doc-only-prs
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- .markdownlint-cli2.jsonc
|
|
- package.json
|
|
- pnpm-lock.yaml
|
|
- .gitea/workflows/ci.yml
|
|
- README.md
|
|
- apps/api/README.md
|
|
- apps/pwa/README.md
|
|
- apps/pwa/e2e/README.md
|
|
- docs/API.md
|
|
- docs/ARCHITECTURE.md
|
|
- docs/DEVELOPMENT.md
|
|
- docs/GETTING-STARTED.md
|
|
autonomous: true
|
|
requirements: []
|
|
must_haves:
|
|
truths:
|
|
- "pnpm md:lint exits 0 on the current repo (13 baseline violations fixed)"
|
|
- "Introducing a bare fenced code block (MD040) makes pnpm md:lint exit non-zero"
|
|
- "The fast-checks CI job runs a Markdown lint step between Format check and Typecheck"
|
|
- "pnpm format:check still exits 0 after the MD031 blank-line fixes (no Prettier conflict)"
|
|
- "markdownlint never scans .planning/** (excluded by config ignores)"
|
|
artifacts:
|
|
- path: ".markdownlint-cli2.jsonc"
|
|
provides: "markdownlint-cli2 config (extends prettier preset; content rules; globs + ignores)"
|
|
contains: "markdownlint/style/prettier"
|
|
- path: "package.json"
|
|
provides: "md:lint script + markdownlint-cli2 devDependency"
|
|
contains: "md:lint"
|
|
- path: ".gitea/workflows/ci.yml"
|
|
provides: "Markdown lint step in fast-checks job"
|
|
contains: "pnpm md:lint"
|
|
key_links:
|
|
- from: ".gitea/workflows/ci.yml"
|
|
to: "package.json md:lint script"
|
|
via: "fast-checks step runs pnpm md:lint"
|
|
pattern: "pnpm md:lint"
|
|
- from: "package.json md:lint script"
|
|
to: ".markdownlint-cli2.jsonc"
|
|
via: "markdownlint-cli2 auto-discovers root config (globs + ignores)"
|
|
pattern: "markdownlint-cli2"
|
|
---
|
|
|
|
<objective>
|
|
Add a real markdown lint gate to the existing `fast-checks` CI job: install `markdownlint-cli2`, create the root `.markdownlint-cli2.jsonc` config (Prettier-compatible preset + content rules + scoped globs that exclude `.planning/**`), add a root `md:lint` script, wire a "Markdown lint" step into `fast-checks`, and fix the 13 baseline violations so the gate starts GREEN.
|
|
|
|
This delivers Success Criterion 4: `fast-checks` runs markdownlint over docs; an introduced violation fails the gate; the existing baseline passes.
|
|
|
|
Purpose: Docs get a fast but real format+lint gate without a separate CI job, scoped so churny `.planning/**` bookkeeping is never linted.
|
|
Output: `.markdownlint-cli2.jsonc`, updated `package.json` (script + devDep), one new `fast-checks` step, 7 doc files fixed (13 violations), green `pnpm md:lint` + `pnpm format:check`.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@$HOME/.claude/gsd-core/workflows/execute-plan.md
|
|
@$HOME/.claude/gsd-core/templates/summary.md
|
|
</execution_context>
|
|
|
|
<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
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="execute">
|
|
<name>Task 1: Install markdownlint-cli2, add md:lint script, create .markdownlint-cli2.jsonc</name>
|
|
<read_first>
|
|
- package.json (root — current scripts block lines 6-16 and devDependencies lines 17-25; mirror the existing `format:check` script + `prettier` devDep placement)
|
|
- .prettierrc (root JSON tool-config structural analog: printWidth 100 — the reason MD013 must stay disabled)
|
|
- .planning/phases/15-ci-skip-api-harness-jobs-for-doc-only-prs/15-RESEARCH.md (Pattern 5 + "Code Examples" → the exact `.markdownlint-cli2.jsonc` body and rule-decision table; Pitfall 5 `#` vs `!` negation; Pitfall 6 .pnpm-store)
|
|
- .planning/phases/15-ci-skip-api-harness-jobs-for-doc-only-prs/15-PATTERNS.md ("`.markdownlint-cli2.jsonc` (new file)" and "package.json (root)" sections — exact insertion points)
|
|
</read_first>
|
|
<action>
|
|
Install `markdownlint-cli2` at version `0.22.1` as a root-workspace devDependency: run `pnpm add -D markdownlint-cli2@0.22.1 --workspace-root` (this updates root package.json devDependencies and pnpm-lock.yaml). Add a root script `"md:lint": "markdownlint-cli2"` immediately after the existing `"format:check"` script — NO glob args on the CLI; globs/ignores live in the config file so `pnpm md:lint` and a bare `npx markdownlint-cli2` resolve identically (RESEARCH Open Question 3 resolved).
|
|
|
|
Create `.markdownlint-cli2.jsonc` at the repo root using the exact body from RESEARCH "Code Examples → .markdownlint-cli2.jsonc" / PATTERNS "`.markdownlint-cli2.jsonc` (new file)". Required keys: `config.extends` = `"markdownlint/style/prettier"`; ENABLE `MD001`, `MD024`, `MD040`, `MD031`, `MD051`, `MD052` (all `true`); DISABLE `MD041`, `MD034`, `MD036` (all `false`) — rationale per the rule-decision table (MD041 because CLAUDE.md starts with `## Project`; MD034 because the version table uses `pkg@version` syntax; MD036 because docs/API.md uses bold response labels). Set `globs` to exactly `["docs/**/*.md", "*.md", "apps/**/*.md"]` and `ignores` to exactly `[".planning/**", "node_modules/**", "**/node_modules/**", ".pnpm-store/**"]`. Do NOT add MD013 (line-length) — it is disabled by the prettier preset and re-enabling it produces 700+ false violations against `.prettierrc` printWidth 100.
|
|
|
|
Do NOT run md:lint to green yet — the 13 baseline violations are fixed in Task 2; this task may leave `pnpm md:lint` red.
|
|
</action>
|
|
<verify>
|
|
<automated>test -f .markdownlint-cli2.jsonc && node -e "const p=require('./package.json'); if(!p.scripts['md:lint']) process.exit(1); if(!p.devDependencies['markdownlint-cli2']) process.exit(1); console.log('script+dep present')" && grep -q 'markdownlint/style/prettier' .markdownlint-cli2.jsonc && grep -q '.planning/' .markdownlint-cli2.jsonc && echo OK</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- package.json `scripts` contains `"md:lint": "markdownlint-cli2"` (no glob args)
|
|
- package.json `devDependencies` contains `markdownlint-cli2` at `0.22.1`; pnpm-lock.yaml updated
|
|
- `.markdownlint-cli2.jsonc` exists at repo root and `config.extends` is `"markdownlint/style/prettier"`
|
|
- `.markdownlint-cli2.jsonc` enables MD001/MD024/MD040/MD031/MD051/MD052 and disables MD041/MD034/MD036
|
|
- `.markdownlint-cli2.jsonc` `ignores` array includes `".planning/**"`, `"node_modules/**"`, `"**/node_modules/**"`, `".pnpm-store/**"`
|
|
- `.markdownlint-cli2.jsonc` `globs` array is exactly `["docs/**/*.md", "*.md", "apps/**/*.md"]`
|
|
</acceptance_criteria>
|
|
<done>markdownlint-cli2@0.22.1 is a root devDep, `pnpm md:lint` is wired to the root config, and the config scopes the lint to docs/repo-root/app markdown while excluding `.planning/**`. md:lint may still be red (fixed next task).</done>
|
|
</task>
|
|
|
|
<task type="execute">
|
|
<name>Task 2: Fix the 13 baseline markdown violations; green md:lint + format:check; wire the fast-checks step</name>
|
|
<read_first>
|
|
- .planning/phases/15-ci-skip-api-harness-jobs-for-doc-only-prs/15-RESEARCH.md ("Baseline Violation Assessment" table — exact file/rule/count; Pitfall 4 — verify MD031 fix does not break Prettier)
|
|
- .planning/phases/15-ci-skip-api-harness-jobs-for-doc-only-prs/15-PATTERNS.md ("Docs content fixes" table + MD040/MD031 fix patterns; "Markdown lint step in fast-checks" insertion slot)
|
|
- .gitea/workflows/ci.yml (fast-checks job lines 8-38 — confirm the real step names: "Format check" line 31-32, "Typecheck" line 34; insert the new step between them)
|
|
- README.md (lines ~78, ~119 — the 2 bare fences)
|
|
- docs/API.md (lines ~517, ~544), docs/ARCHITECTURE.md (lines ~64, ~171), docs/DEVELOPMENT.md (lines ~9, ~22) — 2 bare fences each
|
|
- docs/GETTING-STARTED.md (lines ~52, ~54 — the MD031 fence pair needing blank lines)
|
|
- apps/api/README.md (line ~19), apps/pwa/README.md (line ~45), apps/pwa/e2e/README.md (line ~76) — 1 bare fence each
|
|
</read_first>
|
|
<action>
|
|
Fix all 13 violations confirmed at these exact locations (run `pnpm md:lint` first to re-confirm line numbers, then fix):
|
|
- MD040 (11 bare fences — add a language tag matching the fence content; use `bash` for shell, `text` for plain output, `json`/`yaml`/`ts` as appropriate): apps/api/README.md:19, apps/pwa/e2e/README.md:76, apps/pwa/README.md:45, docs/API.md:517, docs/API.md:544, docs/ARCHITECTURE.md:64, docs/ARCHITECTURE.md:171, docs/DEVELOPMENT.md:9, docs/DEVELOPMENT.md:22, README.md:78, README.md:119.
|
|
- MD031 (1 fence pair — add a blank line before the opening ```` ```bash ```` and after the closing ```` ``` ````): docs/GETTING-STARTED.md:52 and :54.
|
|
|
|
Only change what each rule requires — add a language token after the opening backticks (MD040) or add surrounding blank lines (MD031); do NOT rewrite fence bodies or restructure docs. Do not "fix" any rule that the config disables (MD013/MD034/MD036/MD041) — those are intentionally off.
|
|
|
|
After fixes, add the new step to the `fast-checks` job in ci.yml between the "Format check" step and the "Typecheck" step — a named step `Markdown lint` running `pnpm md:lint` (mirror the one-line `Format check` step pattern exactly). This is the only ci.yml change in this plan; do not touch the api/harness/changes/gate jobs (Plan 02 owns those).
|
|
|
|
Then run BOTH `pnpm md:lint` (must exit 0) AND `pnpm format:check` (must exit 0) — per RESEARCH Pitfall 4, the MD031 blank-line additions must not introduce a Prettier conflict on docs/GETTING-STARTED.md. If format:check newly fails on a file you touched, run `pnpm format` on that file and re-confirm md:lint is still 0.
|
|
</action>
|
|
<verify>
|
|
<automated>pnpm md:lint && pnpm format:check && grep -q 'pnpm md:lint' .gitea/workflows/ci.yml && echo OK</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- `pnpm md:lint` exits 0 (13 baseline violations resolved)
|
|
- `pnpm format:check` exits 0 (no Prettier conflict introduced by the MD031 fix)
|
|
- `.gitea/workflows/ci.yml` fast-checks job contains a step named `Markdown lint` running `pnpm md:lint`, positioned after the `Format check` step and before the `Typecheck` step
|
|
- Re-running md:lint after temporarily inserting a bare ```` ``` ```` fence into any in-scope .md (then reverting) exits non-zero (gate can fail) — proven during execution, not left in the tree
|
|
- No api/harness/changes/gate job was modified by this plan
|
|
</acceptance_criteria>
|
|
<done>The markdown baseline is clean, `fast-checks` runs `pnpm md:lint`, the gate can fail on a real violation, and Prettier and markdownlint do not conflict. SC-4 satisfied.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<artifacts_produced>
|
|
This phase introduces the following new symbols (Plan 01 portion). The plan-review source-grounding pass must treat these as newly-created, not drift:
|
|
|
|
- `.markdownlint-cli2.jsonc` — new root config file
|
|
- `md:lint` — new root `package.json` script
|
|
- `markdownlint-cli2` — new root devDependency (0.22.1)
|
|
- `Markdown lint` — new step name in the `fast-checks` job
|
|
</artifacts_produced>
|
|
|
|
<threat_model>
|
|
## Trust Boundaries
|
|
|
|
| Boundary | Description |
|
|
|----------|-------------|
|
|
| npm registry → repo devDependencies | `markdownlint-cli2` is fetched and runs in CI and on developer machines |
|
|
| markdown content → lint tool | doc files are the input; markdownlint only reads, never executes content |
|
|
|
|
## STRIDE Threat Register
|
|
|
|
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
|
|-----------|----------|-----------|-------------|-----------------|
|
|
| T-15-01 | Tampering (supply chain) | new `markdownlint-cli2` devDependency | mitigate | Pin to exact `0.22.1` (no `^`/`~`); RESEARCH Package Legitimacy Audit verdict OK (~4 yrs, ~3M/wk, DavidAnson/markdownlint-cli2). Audit table present in RESEARCH; no `[ASSUMED]`/`[SUS]` packages so no blocking-human checkpoint required. |
|
|
| T-15-02 | Denial of Service (gate noise) | lint glob accidentally scanning `.planning/**` or `node_modules` | accept→mitigate | `ignores` excludes `.planning/**`, `node_modules/**`, `**/node_modules/**`, `.pnpm-store/**`; verified the glob lints exactly 12 in-scope files, not bookkeeping churn. |
|
|
| T-15-03 | Tampering (false-green) | a disabled content rule silently hides a real doc defect | accept | Rule-disable decisions (MD034/MD036/MD041) are scoped to non-author-controlled patterns documented in RESEARCH; MD040/MD031/MD001/MD024/MD051/MD052 stay enabled to catch broken fences/links. |
|
|
</threat_model>
|
|
|
|
<verification>
|
|
- `pnpm md:lint` exits 0 on the clean tree; exits non-zero when a bare fence is introduced.
|
|
- `pnpm format:check` exits 0 (Prettier/markdownlint do not conflict).
|
|
- `.gitea/workflows/ci.yml` fast-checks job runs `pnpm md:lint` between Format check and Typecheck.
|
|
- markdownlint scans 12 in-scope files and never touches `.planning/**` (confirm "Finding:" line lists the negated ignores).
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
Maps to Phase 15 Success Criterion 4: `fast-checks` runs markdownlint-cli2 over the docs glob; an introduced markdown-lint violation fails the gate; the existing markdown baseline passes (13 violations fixed, rules configured) so the gate starts green.
|
|
</success_criteria>
|
|
|
|
<output>
|
|
Create `.planning/phases/15-ci-skip-api-harness-jobs-for-doc-only-prs/15-01-SUMMARY.md` when done.
|
|
</output>
|