Skip to content

Exclude node_modules From Markdown Lint and Docker Lint Discovery - #1474

Merged
ptr727 merged 2 commits into
developfrom
feature/markdownlint-ignore-node-modules
Sep 9, 2026
Merged

ptr727 merged 2 commits into
developfrom
feature/markdownlint-ignore-node-modules

Conversation

@ptr727

@ptr727 ptr727 commented Sep 9, 2026 •

Copy link
Copy Markdown
Owner

Addresses #1466.

What this changes

  • .markdownlint-cli2.jsonc gains "ignores": ["**/node_modules/**"] and "globs": ["**/*.md"].
  • scripts/docker_lint.py drops any path under node_modules from target discovery, with three tests.
  • GOVERNANCE.md, OPERATIONS.md, and scripts/README.md say so where each already describes that discovery.
  • .gitignore gains node_modules/, for this repository's own tree.

The measurements

Run against markdownlint-cli2 v0.23.2 on a scratch git repository holding a real node_modules, a nested sub/node_modules, a gitignored dist/, and two authored files.

Case Result
Canonical config, **/*.md 1711 errors under node_modules
ignores: ["**/node_modules/**"], .gitignore without node_modules node_modules excluded, dist/ still excluded by gitignore: true
ignores: ["node_modules"] or ["node_modules/"] root copy excluded, sub/node_modules still linted
ignores applied to explicitly named file arguments filtered, Linting: 1 file of 3 named
ignores with no globs, no command-line glob Linting: 0 files, exit 0
Canonical config, no globs, no command-line glob usage text, exit 2
Config globs plus command-line **/*.md union, same file set
Config globs plus command-line !docs/** negation still wins

The fifth and sixth rows are why globs is declared alongside ignores rather than after it: an ignores entry with no positive glob to subtract from turns a loud usage error into a silent success. The last two rows are why a repository narrowing the Lint Markdown step's own globs: input is unaffected.

Why the wrapper is in scope

docker_lint.py discovers targets with git ls-files --others --exclude-standard, so an untracked, unignored node_modules/**/*.sh became a shellcheck and shfmt target. No linter config reaches that, because neither tool has one. Measured on the same scratch repository: git ls-files --others --exclude-standard -- '*.sh' returned node_modules/somepkg/bin/run.sh, and returns nothing once the exclude pathspec is appended.

editorconfig-checker and cspell are immune, also measured rather than assumed. editorconfig-checker --dry-run on that tree listed neither node_modules copy, because it excludes them by default. cspell's two patterns are literal root paths that no nested file matches.

:(glob,exclude) rather than :(exclude), because without glob magic the leading **/ does not match zero directories and the root copy leaked. Each of the three tests fails with the change reverted.

Why not .gitignore alone

#1466 offered that as the alternative, on the premise that .gitignore is not a carried file. It is carried, at presence fidelity, so hub content does not propagate and a carrier would keep the defect. Raising the fidelity is not the fix either: intent never judges content, and a .gitignore legitimately varies by language, which is why it sits at presence. It is added here for this repository's own tree, where it is what keeps a local install out of a git add.

The Financial-Modeling outlier

#1466 asked whether that repository's ignores block is the fix to promote. It is not: its entries are data/** and notes/forms-decisions.md, a committed document archive and the owner's working notes, neither of them node_modules. It is genuinely modified against this canonical and needs its own disposition. Its comment did already identify the silent-zero-files hazard above, which is where the globs half of this change comes from.

Verification

ruff format --check, ruff check, mypy, prose_lint.py --diff HEAD, repo_gate.py --check eol, spec/validate.py, all 15 scripts/tests suites, and docker_lint.py --linter markdownlint --linter editorconfig-checker over this tree, which reported 182 Markdown files and 0 issues with a node_modules present.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Linting now consistently excludes third-party files in node_modules directories at any depth, preventing locally installed packages from being treated as project files.
  • Documentation

    • Updated governance, operations, and script documentation to clarify target discovery and node_modules exclusions.
    • Updated Markdown linting configuration to recognize Markdown files while ignoring node_modules paths.
  • Chores

    • Added node_modules/ to ignored files so local dependencies are not included in version control.

## What this changes

`.markdownlint-cli2.jsonc` gains `ignores` and `globs`.
`scripts/docker_lint.py` drops `node_modules` from target discovery, and
`GOVERNANCE.md`, `OPERATIONS.md`, and `scripts/README.md` say so where each
already describes that discovery. `.gitignore` gains `node_modules/`.

## Why

Per #1466, `"gitignore": true` was the config's only ignore source and no
fleet `.gitignore` listed `node_modules`, so a checkout that had ever run a
Node tool locally linted third-party Markdown. Measured on a scratch repo
against markdownlint-cli2 v0.23.2: 1711 errors under `node_modules`, 0 after
the `ignores` entry, with the entry filtering explicitly named file arguments
as well as globs.

`**/node_modules/**` rather than `node_modules`, because the bare form matched
the root copy only and left a nested workspace copy linted.

`globs` is declared alongside it because an `ignores` entry with no positive
glob to subtract from lints no files and exits 0, where the same invocation
before this change exited 2 on a usage error. A command-line glob still unions
with it and a command-line negation still wins, so a repo narrowing the Lint
Markdown step's own `globs:` input is unaffected.

## Why the wrapper is in scope too

`docker_lint.py` discovers targets with `git ls-files --others
--exclude-standard`, so an untracked, unignored `node_modules/**/*.sh` became
a shellcheck and shfmt target, which no linter config can reach because
neither tool has one. editorconfig-checker and cspell are immune, measured:
the first excludes `node_modules` by default, the second takes two literal
root paths.

## Why not only `.gitignore`

#1466 offered that as the alternative, on the premise that `.gitignore` is not
a carried file. It is carried, at `presence` fidelity, so hub content does not
propagate and a carrier would keep the defect. Raising the fidelity is not the
fix either, since `intent` never judges content and `.gitignore` legitimately
varies by language. It is added here for this repository's own tree, where it
is what keeps a local install out of `git add`.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 9, 2026 15:23
@coderabbitai

coderabbitai Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

Warning

Review limit reached

Next included review available in 49 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 13db41e1-9b95-4f94-b0bb-53a36c928e3d

📥 Commits

Reviewing files that changed from the base of the PR and between b907c83 and ff6cd41.

📒 Files selected for processing (2)
  • .markdownlint-cli2.jsonc
  • reports/canonical-review.json

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 67ba865d-917c-4208-9a7f-ad4c31a2d70d

📥 Commits

Reviewing files that changed from the base of the PR and between cf7be2b and b907c83.

📒 Files selected for processing (8)
  • .gitignore
  • .markdownlint-cli2.jsonc
  • GOVERNANCE.md
  • OPERATIONS.md
  • reports/canonical-review.json
  • scripts/README.md
  • scripts/docker_lint.py
  • scripts/tests/test_docker_lint.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The lint configuration and Docker runner now exclude node_modules paths at any depth. Tests cover Markdown, pattern-free, and shell-script target discovery. Documentation and review metadata reflect the updated behavior.

Changes

Lint target exclusion

Layer / File(s) Summary
Configure excluded lint paths
.gitignore, .markdownlint-cli2.jsonc
The repository ignores node_modules, and Markdown linting excludes those paths at any depth.
Filter discovered lint targets
scripts/docker_lint.py, scripts/tests/test_docker_lint.py
ls_files always applies NODE_MODULES_EXCLUDE. Tests verify the exclusion for Markdown, editorconfig-checker, and shellcheck targets.
Document and record the exclusion
GOVERNANCE.md, OPERATIONS.md, scripts/README.md, reports/canonical-review.json
Project documentation describes the exclusion, and the canonical review report records the configuration review pass.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to b907c

Lint target discovery now skips node_modules at any depth while retaining repository targets, with matching configuration, documentation, and regression coverage. No current merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 2 files. (6 skipped: 6… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: excluding node_modules from Markdown and Docker lint discovery.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 2 files. (6 skipped: 6 unsupported.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/markdownlint-ignore-node-modules

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ptr727

ptr727 commented Sep 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 9, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The changes are cohesive and well-tested; only a minor wording fix in a new JSONC comment was found.

Pull request overview

Updates the Markdown and Docker lint target-discovery logic to avoid linting third-party content under node_modules/, and documents that behavior where discovery is described.

Changes:

  • Add node_modules exclusion and explicit globs to .markdownlint-cli2.jsonc to prevent linting third-party Markdown and avoid silent zero-target runs.
  • Exclude any **/node_modules/** paths from scripts/docker_lint.py target discovery (with unit tests covering nested and pattern-free cases).
  • Update runbooks/docs to state that Docker lint discovery excludes node_modules, and ignore node_modules/ locally via .gitignore.
File summaries
File Description
scripts/docker_lint.py Adds a git pathspec exclusion for **/node_modules/** during target discovery.
scripts/tests/test_docker_lint.py Adds regression tests ensuring node_modules is excluded for markdownlint/shellcheck and doesn’t break pattern-free linters.
.markdownlint-cli2.jsonc Adds globs and ignores to avoid linting node_modules content and prevent silent zero-target behavior.
scripts/README.md Documents that the Docker lint runner excludes node_modules from discovery.
GOVERNANCE.md Updates the wrapper description to note node_modules exclusion during discovery.
OPERATIONS.md Updates the local verification runbook to note node_modules exclusion during discovery.
.gitignore Ignores node_modules/ for this repository’s working tree.
reports/canonical-review.json Records the canonical-review entry for the .markdownlint-cli2.jsonc unit.
Review details
  • Files reviewed: 7/8 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .markdownlint-cli2.jsonc Resolved
Copilot read "nothing positive to subtract from lints no files" as making
"lints" a noun, which it does. The claim is unchanged: the ignores entry
only subtracts, so a run with no positive glob lints nothing and exits 0.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 9, 2026 15:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The exclusion logic is correctly implemented, covered by targeted tests, and consistently documented across the referenced runbooks.

Review details
  • Files reviewed: 7/8 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@ptr727

ptr727 commented Sep 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 9, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ptr727
ptr727 merged commit e7cb1cf into develop Sep 9, 2026
9 checks passed
@ptr727
ptr727 deleted the feature/markdownlint-ignore-node-modules branch September 9, 2026 20:14
ptr727 added a commit that referenced this pull request Sep 10, 2026
…g, and the Release-Trigger Retirement (#1489)

Promotes thirteen merged pull requests from `develop` to `main`.

## What this promotes

- **#1486** State the `host-tools.json` carry rule and stop the
empty-overlay
warning. `spec/files.json` now requires the declaration unconditionally,
and
  `host_gate.py` no longer warns on an empty one.
- **#1484** Fan the validator's pytest leg out over a `python-versions`
input,
so a caller with more than one interpreter no longer silently loses its
other
legs to a pinned 3.13, with an entry guard that refuses a value
`fromJson`
  would otherwise admit.
- **#1476** Retire the publish-on-merge release trigger, which
`spec/files.json`
applied to an interface contract whose task has no publish-on-merge
path.
- **#1479** Ship the release caller snippet and retire the
not-yet-released
  claim.
- **#1474** Exclude `node_modules` from markdownlint and Docker lint
discovery
  at any depth.
- **#1468** Move the carried-surface cluster off decision, recording the
  measurements and what stays unmeasured on the issues that hold them.
- **#1498** Correct the release-caller snippet's publish-on-merge claim,
raised by CodeRabbit against this promotion and fixed on its own branch,
  since a promotion PR's head is `develop`.
- **#1494** Settle the markdown lint exclusion seam as a nested config.
- **#1492** Check declared deployment environments and fix the
carried-payload
  rule.
- **#1501** Move the session handoff to a chain of labeled issues. Two
of its
units are verbatim fidelity, `AGENTS.md` "Session Scope" and
`GOVERNANCE.md`
"Communicating with the User", so every fleet repo picks them up on its
next
  resync.
- **#1505** Refuse a bare handoff link rather than reading it as no
successor,
  answering a finding raised against this promotion.
- **#1504** Reject a deployment environment name or branch carrying
surrounding
  whitespace, answering two more.
- **#1506** Encode an environment name before it becomes a path segment,
delete
a branch that cannot fire, and dedupe a registry name by case, answering
two
  more and declining one on a reproduction.

## Closing references

A `develop`-targeted pull request registers no closing reference, so the
issues
those pull requests settled close with this promotion.

#1470 is the one that needs a word. Two of its seven acceptance items
are not
met as literally written, and the maintainer's decision to close it here
anyway,
with the basis for both, is recorded on the issue itself.

Closes #1483
Closes #1473
Closes #1487
Closes #1482
Closes #1475
Closes #1470

## Left open deliberately

- **#1499**, **#1500** follow-ups #1501 filed rather than fixed.
- **#1503** the validator-looser-than-its-consumer class, filed from
findings on
this promotion and deliberately not closed by it. Five instances
recorded, and
  the fix is a design decision rather than a check.
- **#1497** the Docker delta list never says to remove the `push:`
block.
- **#1472** the fleet-level pin call, deliberately not pre-empted by
#1479.
- **#1321**, **#1464** carry #1468's decision record and remain in their
own
  scope.

## Verification

`origin/main..origin/develop` merges clean, no end-of-line conflict.
Each
promoted pull request ran the fleet gate set on its own branch before
merging;
this promotion adds no content of its own.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01817rckKywjfoaEV5WF9TKs
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.

2 participants