Skip to content

Commit b8a3602

Browse files
fix(ci): make the markdownlint job lint files again (#4526) (#4584)
* fix(ci): make the markdownlint job lint files again (#4526) The globs were written inside a YAML block scalar, so the quotes around `'**/*.md'` were passed through to markdownlint-cli2 verbatim. It looked for paths beginning with a literal quote, matched nothing, and exited 0. The job has reported success without reading a file since it was added. Unquoting the glob alone turns the job red: it then finds 116 files with 277 violations. So this also clears every one of them. Most were mechanical (blank lines around fences, lists and headings) and came from markdownlint's own --fix. Three groups needed judgment: - Identifiers that markdown reads as emphasis. `__init__.py`, `__SPECKIT_COMMAND_*__` and friends were being rendered as bold in CHANGELOG.md. Running --fix over them rewrites the text itself (`**init**.py`), so they are wrapped in code spans instead, matching how the same tokens are already written elsewhere in the repo. - Fenced blocks without a language. Tagged from their actual content: `markdown` for the blocks that are markdown output, `text` for directory trees, commit messages and log excerpts. - The pillar headings on the docs landing page. They must stay at h3 because main.css styles `.pillar-card h3`, so MD001 is suppressed there with a comment saying why. It is the only suppression added. No prose changed. Verified against markdownlint-cli2 0.23.2, the version the pinned action actually runs. Assisted-by: Claude (model: claude-opus-5, autonomous) * fix(ci): fail the glob check per glob, not on the total The check summed matches across every glob and only failed at zero, so one stale entry among several still passed while leaving that part of the documentation unlinted -- the same silent narrowing as #4526, just partial. Each glob is now checked on its own and every empty one is reported. The unmatched globs are collected in an array rather than a string: nullglob is on for this step, so re-expanding an unquoted list of globs that match nothing erases the list before it can be printed. * fix(ci): exclude CHANGELOG.md from the documentation allowlist Per review: the changelog is generated from git commit messages by the release workflow, so linting it is pointless and the edits this branch made to it were worse than pointless — they rewrote commit-message text that the next release regenerates, making the file diverge from the commits it is built from. Dropped CHANGELOG.md from DOC_GLOBS and reverted the file to its base state. The allowlist now matches 56 files, 0 errors, and the guard step agrees. * fix(docs): drop the MD001 suppression that is no longer needed The disable was added when the pillar cards jumped from the page h1 straight to h3. Main's rewrite put `## Choose your process` above them, so the sequence is h1 -> h2 -> h3 and MD001 does not fire. Linting docs/index.md at the merge base reports 0 issues without the disable, so the comment described a problem that no longer exists. That leaves docs/index.md unchanged by this branch. * test(ci): guard the markdownlint allowlist against the #4526 failure mode The in-workflow guard lives in the file it protects, so a rewrite of lint.yml could drop both together. These assert the same invariants from the test suite: no glob is quoted, every glob matches a file, and the action's globs input still reads DOC_GLOBS.
1 parent ac53c9f commit b8a3602

15 files changed

Lines changed: 131 additions & 34 deletions

File tree

‎.github/workflows/lint.yml‎

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ on:
1010
jobs:
1111
markdownlint:
1212
runs-on: ubuntu-latest
13+
env:
14+
DOC_GLOBS: |
15+
docs/**/*.md
16+
README.md
17+
README.zh-CN.md
18+
CODE_OF_CONDUCT.md
19+
CONTRIBUTING.md
20+
DEVELOPMENT.md
21+
SECURITY.md
22+
SUPPORT.md
23+
spec-driven.md
24+
integrations/*.md
25+
presets/*.md
26+
workflows/*.md
1327
steps:
1428
- name: Checkout
1529
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -36,12 +50,46 @@ jobs:
3650
git diff --check refs/checks/push-before HEAD
3751
fi
3852
53+
# Documentation only. Commands, skills, prompt templates, agent
54+
# instructions (AGENTS.md) and .github/ content are inputs to coding
55+
# agents rather than prose, and are deliberately left unlinted so a
56+
# documentation pass never reformats them. Add new documentation
57+
# paths to the DOC_GLOBS list above.
58+
- name: Verify the documentation globs match files
59+
shell: bash
60+
run: |
61+
set -euo pipefail
62+
shopt -s globstar nullglob
63+
64+
# Checked per glob, not on the total: one stale entry among several
65+
# still leaves that part of the documentation unlinted, which is the
66+
# failure #4526 was about.
67+
count=0
68+
empty=()
69+
while IFS= read -r glob; do
70+
[ -z "$glob" ] && continue
71+
matched=0
72+
for path in $glob; do
73+
[ -f "$path" ] && matched=$((matched + 1))
74+
done
75+
# An array, not a string: nullglob is on, so re-expanding an
76+
# unquoted list of unmatched globs would erase it.
77+
[ "$matched" -eq 0 ] && empty+=("$glob")
78+
count=$((count + matched))
79+
done <<< "$DOC_GLOBS"
80+
81+
echo "documentation files matched: $count"
82+
if [ ${#empty[@]} -gt 0 ]; then
83+
for glob in "${empty[@]}"; do
84+
echo "::error::markdownlint glob matches no files: $glob (see #4526)"
85+
done
86+
exit 1
87+
fi
88+
3989
- name: Run markdownlint-cli2
4090
uses: DavidAnson/markdownlint-cli2-action@21c1be1b93ad9ed58fa840aacc3f279cde2a72ff # v24.2.0
4191
with:
42-
globs: |
43-
'**/*.md'
44-
!extensions/**/*.md
92+
globs: ${{ env.DOC_GLOBS }}
4593

4694
shellcheck:
4795
runs-on: ubuntu-latest

‎CONTRIBUTING.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -278,21 +278,21 @@ Any change that affects a slash command's behavior requires manually testing tha
278278

279279
Paste this into your PR:
280280

281-
~~~markdown
281+
```markdown
282282
## Manual test results
283283

284284
**Agent**: [e.g., GitHub Copilot in VS Code] | **OS/Shell**: [e.g., macOS/zsh]
285285

286286
| Command tested | Notes |
287287
|----------------|-------|
288288
| `/speckit.command` | |
289-
~~~
289+
```
290290

291291
#### Determining which tests to run
292292

293293
Copy this prompt into your agent. Include the agent's response (selected tests plus a brief explanation of the mapping) in your PR.
294294

295-
~~~text
295+
```text
296296
Read CONTRIBUTING.md, then run `git diff --name-only main` to get my changed files.
297297
For each changed file, determine which slash commands it affects by reading
298298
the command templates in templates/commands/ to understand what each command
@@ -324,7 +324,7 @@ Number each test sequentially (T1, T2, ...). List prerequisite tests first.
324324
325325
- T1: /speckit.command — (reason)
326326
- T2: /speckit.command — (reason)
327-
~~~
327+
```
328328

329329
## AI contributions in Spec Kit
330330

‎docs/community/friends.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ Community projects that extend, visualize, or build on Spec Kit:
1717

1818
- **[spectatui](https://github.com/tinesoft/spectatui)** — A terminal UI (TUI) dashboard for Spec Kit that lets you track features, manage specifications, integrations, presets, workflows, and extensions, and monitor AI agent workflows. Attach to existing AI sessions or launch new ones from your terminal. Keyboard and mouse support. Light/dark theme support. Customizable and performance-oriented. Requires the `specify` CLI in your PATH.
1919

20-
- **[spec-kit-copilot](https://github.com/github/spec-kit-copilot)** — _First-party GitHub project._ A GitHub Copilot **skills plugin** that exposes the Spec Kit `specify` CLI to the Copilot agent in both the Copilot CLI and the GitHub Copilot app. It provides a focused skill per `specify` command group — setup, init, check, extensions, presets, bundles, workflows, workflow steps, and self-upgrade — so you can navigate and drive the entire Spec Kit ecosystem through natural language, letting Copilot decide when and how to run the right `specify` commands on your behalf.
20+
- **[spec-kit-copilot](https://github.com/github/spec-kit-copilot)** — *First-party GitHub project.* A GitHub Copilot **skills plugin** that exposes the Spec Kit `specify` CLI to the Copilot agent in both the Copilot CLI and the GitHub Copilot app. It provides a focused skill per `specify` command group — setup, init, check, extensions, presets, bundles, workflows, workflow steps, and self-upgrade — so you can navigate and drive the entire Spec Kit ecosystem through natural language, letting Copilot decide when and how to run the right `specify` commands on your behalf.
2121

2222
- **[Spec Kit Workflow Cockpit](https://github.com/markuswondrak/spec-kit-workflow-cockpit)** — A terminal control panel for running Spec Kit workflows locally. It replaces the fragmented CLI flow with a single live view: see the whole workflow at a glance, follow progress node by node, read and edit feature documents at human review gates without leaving the terminal, and make gate decisions from clearly presented options with confirmation. It also provides a `cockpit-run-context` skill that connects your interactive coding session directly to the current workflow run.

‎docs/install/air-gapped.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ specify init my-project --integration copilot
3838
```
3939

4040
> **Note:** Python 3.11+ is required.
41-
42-
> **Windows note:** Offline scaffolding requires PowerShell 7+ (`pwsh`), not Windows PowerShell 5.x (`powershell.exe`). Install from https://aka.ms/powershell.
41+
>
42+
> **Windows note:** Offline scaffolding requires PowerShell 7+ (`pwsh`), not Windows PowerShell 5.x (`powershell.exe`). Install from <https://aka.ms/powershell>.
4343
4444
## Git Credential Manager on Linux
4545

‎docs/installation.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), [CodeBuddy CLI](https://www.codebuddy.cn/docs/cli/installation), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Pi Coding Agent](https://pi.dev), or [Oh My Pi](https://www.npmjs.com/package/@oh-my-pi/pi-coding-agent)
77
- [uv](https://docs.astral.sh/uv/) for package management (recommended) or [pipx](https://pipx.pypa.io/) for persistent installation
88
- [Python 3.11+](https://www.python.org/downloads/)
9-
- [Git](https://git-scm.com/downloads) _(optional — required only when the git extension is enabled)_
9+
- [Git](https://git-scm.com/downloads) *(optional — required only when the git extension is enabled)*
1010

1111
## Installation
1212

‎docs/reference/authentication.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Create `~/.specify/auth.json` to enable authentication:
2222
```
2323

2424
> **Security:** Restrict the file to owner-only access:
25+
>
2526
> ```bash
2627
> chmod 600 ~/.specify/auth.json
2728
> ```

‎docs/reference/core.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ specify init my-project --integration copilot --preset compliance
5757
| `SPECIFY_FEATURE_NO_PERSIST` | Set to `1` or `true` to stop every core script from writing `.specify/feature.json`, even when it would otherwise persist `SPECIFY_FEATURE_DIRECTORY` on read. Useful when multiple agents run concurrently against the same checkout, each with its own `SPECIFY_FEATURE_DIRECTORY`: without it, each invocation's persist step can overwrite another agent's pinned feature directory. |
5858

5959
> **Two resolution axes.** `SPECIFY_INIT_DIR` selects the **project** (which directory contains `.specify/`); `SPECIFY_FEATURE_DIRECTORY` / `.specify/feature.json` select the **feature** within that project. They are independent — project first, then feature.
60-
60+
>
6161
> **Version control.** `specify init` scaffolds a managed `.specify/.gitignore` that excludes machine-local state — `feature.json` (the current-feature pointer, rewritten on every feature switch) and per-machine extension `extensions/*/local-config.yml` overrides — while leaving everything else under `.specify/` (constitution, templates, scripts, extension config) shareable so teams stay aligned. Like the rest of `.specify/`'s shared scripts and templates, the file is tracked in the shared-infrastructure manifest: your edits are preserved on re-init and `specify init --here --force` restores the managed content. It is intentionally left in place by `specify integration uninstall`, which only removes the uninstalled agent's own files.
62-
62+
>
6363
> **Symlinked project roots.** `SPECIFY_INIT_DIR` relocates *where* the project is, not *how* a command treats symlinks: each command keeps its existing cwd-path stance. Commands that traverse and write project files through broad input paths (`bundle`, `workflow run <file>`) refuse a symlinked `.specify/` to preserve write confinement. Other project-scoped commands keep their existing behavior when `SPECIFY_INIT_DIR` points at a project root, which may include following a symlinked `.specify/`.
6464
6565
## Naming Features with the Helper Scripts

‎docs/reference/extensions.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,12 @@ Catalogs come in two kinds, and the distinction is a **security boundary**, not
126126
> **Do not flip a discovery-only catalog to `install_allowed`.** That defeats the entire point of separating discovery from installation. There are two correct ways to install something you found via `community`:
127127
>
128128
> 1. **Install a single vetted extension directly** with `--from` (no catalog authoring needed). Get the candidate archive URL from `specify extension info <name>` — for a discovery-only entry it prints a "Candidate archive" URL. Review that release archive, then install it:
129+
>
129130
> ```bash
130131
> specify extension info <name> # shows the candidate archive URL
131132
> specify extension add <name> --from <archive-url>
132133
> ```
134+
>
133135
> Treat the URL as untrusted until you have vetted it — it comes from an unvetted catalog.
134136
> 2. **Curate your own catalog** you control and vet, and mark *that* catalog `install_allowed: true` — for when you want a governed, reusable install source (e.g. for an org).
135137
@@ -210,13 +212,15 @@ To set up configuration for a newly installed extension, copy the template:
210212
cp .specify/extensions/<ext>/<ext>-config.template.yml \
211213
.specify/extensions/<ext>/<ext>-config.yml
212214
```
215+
213216
## Project Extension and Hook Configuration
214217
215218
Spec Kit stores project-level extension registration and hook configuration in:
216219
217220
```text
218221
.specify/extensions.yml
219222
```
223+
220224
The file contains installed extensions, global settings, and hooks that are surfaced before or after Spec Kit commands.
221225
222226
```yaml
@@ -264,6 +268,7 @@ Each hook entry supports the following fields:
264268
| `prompt` | Message shown when asking whether to run an optional hook. |
265269
| `description` | Human-readable explanation of what the hook does. |
266270
| `condition` | Optional expression evaluated by `HookExecutor` (using `config.<path>` or `env.<VAR>` with `is set`, `==`, or `!=`). Current command templates do not evaluate conditions and skip hooks with a non-empty condition. |
271+
267272
Hook event names identify when a hook is invoked. They generally use `before_<command>` or `after_<command>`, such as `before_implement`, `after_implement`, `before_tasks`, and `after_tasks`.
268273
269274
Extension manifests reject invalid hook priorities during installation. For existing `.specify/extensions.yml` entries, `HookExecutor.get_hooks_for_event()` sorts with `normalize_priority()`: missing values, booleans, non-numeric values rejected by `int()`, and values less than `1` fall back to `10`; numeric strings and finite floats are coerced with `int()`, while non-finite floats are unsupported and may fail instead of falling back.

‎docs/reference/workflows.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ When an installed workflow is refreshed or reinstalled, project overlays in `.sp
335335
- An overlay that targets a step id that does not exist in the base workflow will raise a validation error when the workflow is resolved.
336336
- Overlays cannot target steps added by other overlays.
337337
- Overlays cannot add new inputs or change the input schema of the base workflow.
338+
338339
## Update Workflows
339340

340341
```bash

‎integrations/CONTRIBUTING.md‎

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -94,22 +94,22 @@ provides:
9494
1. **Fork** the [spec-kit repository](https://github.com/github/spec-kit)
9595
2. **Add your entry** under the `integrations` key in `integrations/catalog.community.json`:
9696

97-
```json
98-
{
99-
"schema_version": "1.0",
100-
"integrations": {
101-
"my-agent": {
102-
"id": "my-agent",
103-
"name": "My Agent",
104-
"version": "1.0.0",
105-
"description": "Integration for My Agent",
106-
"author": "your-name",
107-
"repository": "https://github.com/your-name/speckit-my-agent",
108-
"tags": ["cli"]
109-
}
110-
}
111-
}
112-
```
97+
```json
98+
{
99+
"schema_version": "1.0",
100+
"integrations": {
101+
"my-agent": {
102+
"id": "my-agent",
103+
"name": "My Agent",
104+
"version": "1.0.0",
105+
"description": "Integration for My Agent",
106+
"author": "your-name",
107+
"repository": "https://github.com/your-name/speckit-my-agent",
108+
"tags": ["cli"]
109+
}
110+
}
111+
}
112+
```
113113

114114
3. **Open a pull request** with:
115115
- Your catalog entry

0 commit comments

Comments
 (0)