Skip to content

feat(bash): markdownlint-cli2 shim uses the commit gate's config - #458

Merged
twistedmelonman merged 1 commit into
mainfrom
claude/feat-markdownlint-shim
Oct 22, 2026
Merged

twistedmelonman merged 1 commit into
mainfrom
claude/feat-markdownlint-shim

Conversation

@twistedmelonman

Copy link
Copy Markdown
Member

Why

markdownlint-cli2 reads config only from its cwd and below. It never reads a parent dir or ~. In a repo with no config of its own, a bare markdownlint-cli2 file.md lints under markdownlint's defaults (MD013 on, and more). The commit gate and standards-check use the canonical config instead. An agent ran the bare binary, saw findings the gate never reports, and acted on them.

What

  • Shim bash/markdownlint-cli2-wrapper.sh, installed as ~/.local/bin/markdownlint-cli2. install.sh section 4 links it the same way it links gh and gpush: ~/.local/bin/markdownlint-cli2 -> ~/.config/bash/markdownlint-cli2-wrapper.sh. The gitignore and the symlink exclusions need no change, because bash/ is already allow-listed and a known config dir.

    1. No args, or --help: run the real binary unchanged.
    2. --config X or --config=X: unchanged.
    3. A repo config in the cwd: unchanged, because cli2 reads it.
    4. Canonical config present (${MARKDOWNLINT_CANONICAL_CONFIG:-~/.config/markdownlint-cli/.markdownlint.json}): prepend --config <canonical>.
    5. Otherwise: one stderr notice that defaults are in use, then unchanged.

    The real binary is the first one on PATH whose realpath is not the shim. If there is none, the shim exits 127. If a second copy of the shim calls this one again, it exits 126.

  • One list. git/hooks/lib-markdownlint-config.sh holds the config filename list. lint-markdown.sh and the shim both source it, each from beside its own realpath, so a pull works before install.sh --sync. run-standards.sh in github-workflows keeps its own copy, and the drift test checks its order.

  • CI. bash-tests.yml points the drift test at the github-workflows clone it already makes for yamllint (MARKDOWNLINT_UPSTREAM_DIR).

After install

Run hash -r in each open shell, or open a new one. Bash caches the old /opt/homebrew/bin/markdownlint-cli2 path, so until then a plain markdownlint-cli2 skips the shim. which markdownlint-cli2 shows the PATH result; hash -t markdownlint-cli2 shows the cached one.

Tests

bash/tests/test-markdownlint-cli2-shim.sh uses a stub "real" binary that records argv. Cases: no config gets canonical added; .markdownlint-cli2.jsonc, .markdownlint.json and .markdownlintrc leave args unchanged; --config X and --config=X leave args unchanged; a missing canonical leaves args unchanged and prints a notice; no args and --help pass through; self-skip with the shim dir on PATH twice plus a second symlink dir; no real binary exits 127.

Known-bad runs (MDL_SHIM=<bad shim>):

Bad shim FAILs
always passes through 5: canonical added, missing-canonical notice, self-skip argv, no-real message
always prepends --config 11: every "unchanged" case
skips itself by path string, not realpath 12, including self-skip killed at the 10 s cap (exec loop)

bash/tests/test-markdownlint-config-drift.sh:

  1. The canonical config equals github-workflows' standards/markdownlint.json (jq -S).
  2. The lib's list matches run-standards.sh's for c in ... order.
  3. lint-markdown.sh and the shim source the lib and carry no copy of the list.

Upstream source, in order: MARKDOWNLINT_UPSTREAM_DIR, then ~/Developer/github-workflows (may be stale), then raw.githubusercontent.com. If none works, the test fails; it never skips.

Known-bad runs: MD013 flipped in the upstream copy fails check 1. Two list entries swapped fails check 2. A list copy appended to the shim fails check 3. A bad explicit MARKDOWNLINT_UPSTREAM_DIR fails. No sibling clone with the network blocked fails.

Local run: run-tests.sh markdown gives 5/5 (the three existing lint-markdown tests plus the two new ones). The symlink-exclusions and install tests pass.

The mirror test is smartwatermelon/github-workflows#202.

markdownlint-cli2 reads config only from its cwd and below, never
from a parent dir or ~. In a repo with no config of its own, a bare
run lints under markdownlint's defaults (MD013 on, and more), while
the commit gate and standards-check use the canonical config. An
agent ran the bare binary, saw findings the gate never reports, and
acted on them.

New ~/.local/bin/markdownlint-cli2 shim (bash/markdownlint-cli2-
wrapper.sh, linked by install.sh like gh and gpush). With no
--config and no repo config in the cwd, it prepends
--config <canonical>. Otherwise it runs the real binary unchanged,
and warns when the canonical file is missing. It finds the real
binary by realpath, skipping itself, and exits 127 if there is none.

The repo config filename list moves to git/hooks/lib-markdownlint-
config.sh, which lint-markdown.sh and the shim both source.

Tests:
- test-markdownlint-cli2-shim.sh: argv-recording stub; each case
  fails against a passthrough, always-prepend, or path-string
  self-skip shim.
- test-markdownlint-config-drift.sh: the canonical config equals
  github-workflows' standards/markdownlint.json (jq -S), and the
  filename list matches run-standards.sh's order. Fails, never
  skips, when no upstream copy is reachable. CI points it at the
  clone bash-tests.yml already makes.
@twistedmelonman
twistedmelonman merged commit ac5dc8f into main Oct 22, 2026
5 checks passed
@twistedmelonman
twistedmelonman deleted the claude/feat-markdownlint-shim branch October 22, 2026 05:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant