Skip to content

Settle the Markdown Lint Exclusion Seam as a Nested Config - #1494

Merged
ptr727 merged 3 commits into
developfrom
feature/markdownlint-exclusion-seam
Sep 9, 2026
Merged

ptr727 merged 3 commits into
developfrom
feature/markdownlint-exclusion-seam

Conversation

@ptr727

@ptr727 ptr727 commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Settles #1487.

The decision

#1487 put up three dispositions for ptr727/Financial-Modeling's modified .markdownlint-cli2.jsonc: accept the divergence, re-vendor and erase its two exclusions, or declare a seam. This takes the seam.

A repository excluding a subtree of its own puts a .markdownlint-cli2.jsonc carrying its own ignores beside that content, rather than editing the shared root config. The canonical stays verbatim and fleet-generic, so MD013, MD033, MD046, and MD060 keep byte-level drift detection.

The alternatives cost more. accept leaves that repository permanently reporting as modified fixed content, needing a hand merge on every hub change to the file. Dropping the file to intent, which is what .editorconfig-checker.json does for the same repo-local-exclusions reason, would stop the rule block being judged at all, per spec/fidelity-model.md. A plain re-vendor puts a committed document archive and the owner's freeform notes back under a bare local run, the failure class #1466 was filed about.

Measured, not inferred

Against markdownlint-cli2 0.23.2, the build bundled in the pinned markdownlint-cli2-action v24.2.0:

Behavior Result
Nested ignores Honored, in a bare run, a CI-style glob run, and a run naming files explicitly
Nested pattern base The directory holding the file. **/-prefixed works; a repo-root path matches nothing and reports no error
Nested globs / gitignore Inert, read only from the config in the directory the linter is invoked from
Nested config block Works, and settings merge with those above rather than replacing them
Negated CI glob Still subtracts, even with the root config contributing a positive globs
ignores and no globs Lints 0 files, exits 0. With neither key, usage and exit 2

The explicit-file-argument case matters because that is how scripts/docker_lint.py invokes the linter.

The audit check this would have broken

spec/project-types.json's parity.markdownlint asserted "One .markdownlint-cli2.jsonc", at letter verdict and appliesTo *, with AUDIT.md stating it in prose. The first repository to follow the prescription in spec/divergences.json would have drawn a linter-parity drift finding for doing what CODESTYLE.md now mandates. Both now say the count is of the root config, so a nested one is not a second one.

Two narrower wordings were tried and were wrong. Permitting a nested config "carrying only a subtree exclusion" fails this repository's own host-setup/agent-safety/claude/.markdownlint-cli2.jsonc, which carries an MD041 override and no exclusion. Keying on a subtree "the repository does not author" splits from the scope wording every other surface uses, and from the ledger row in this change.

Second carrier

Blog is owed this too and has not adopted it. It excludes content/** and themes/*/** through the CI input only, and its root config predates the globs key, so its migration is the re-vendor plus one nested file per excluded tree. themes/ takes */** rather than **/*.md so its own authored themes/README.md stays linted, and that placement also sits outside the vendored theme directories, where a re-vendor cannot delete it.

Deliberately not done here

reports/divergences.md is not regenerated. RESYNC.md regenerates it "before using it as a work list", and generating it from this branch bakes in a canonical that exists nowhere yet: the run listed ProjectTemplate itself as a stale carrier of its own file, alongside about thirty lines of unrelated fleet movement from other unpromoted hub work. The ledger entry is the durable record and the report regenerates at the next resync.

Filed rather than fixed

The review passes raised roughly 50 findings that predate this change, none introduced by it, split across two follow-up issues. Four are verified defects in fleet-carried content, including the MD060 comment having compact and tight inverted against the rule's own vocabulary.

Verification

  • prose_lint.py (whole tree), repo_gate.py --check eol, spec/validate.py, build_dist.py --check, spec/audit.py --selftest, workflow_reuse.py --selftest, gh-write-guard.py --selftest, and 1171 unit tests, all clean.
  • docker_lint.py for markdownlint, cspell, and editorconfig-checker, clean over 182 Markdown files.
  • Ten review passes recorded across five canonical units and the branch diff. The final pass returned all three re-read units clean and one finding on the diff, which is fixed in 93b1f0d.

A develop-targeted pull request registers no closing reference, so #1487 closes with the promotion.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KZqEEfFr85oibauzz3GKm3

ptr727 and others added 3 commits September 9, 2026 14:03
Settles #1487, which asked what to do about `ptr727/Financial-Modeling`'s
modified `.markdownlint-cli2.jsonc`. The issue put up three dispositions:
accept the divergence, re-vendor and erase the two exclusions, or declare a
seam. This takes the seam.

## What the seam is

A repository excluding a subtree of its own puts a `.markdownlint-cli2.jsonc`
carrying its own `ignores` beside that content, rather than editing the shared
root config. The canonical stays `verbatim` and fleet-generic, and the rule
block it carries keeps its byte-level drift detection.

Measured against markdownlint-cli2 0.23.2, the version bundled in the pinned
`markdownlint-cli2-action` v24.2.0:

- A nested config's `ignores` is honored; `globs` and `gitignore` are read only
  from the config in the directory the linter is invoked from, so a nested copy
  of either is inert.
- Nested patterns resolve against the directory holding the file. A `**/`-prefixed
  pattern works there because it is depth-relative; a path written from the
  repository root matches nothing and reports no error.
- A nested config's settings merge with those above it, so the fleet rule block
  still governs the files it does not exclude.
- The exclusion filters files even when a run names them explicitly as arguments,
  which is how `scripts/docker_lint.py` invokes the linter.

## Why not the alternatives

`accept` leaves that repository permanently reporting as modified fixed content
and needing a hand merge on every hub change to the file. Dropping the file to
`intent`, which is what `.editorconfig-checker.json` does for the same
repo-local-exclusions reason, would stop `MD013`, `MD033`, `MD046`, and `MD060`
being judged at all, per `spec/fidelity-model.md`. A plain `re-vendor` puts a
committed document archive and the owner's freeform notes back under a bare
local run, which is the failure class #1466 was filed about.

## Second carrier

`Blog` excludes `content/**` and `themes/*/**` through the CI
`markdown-exclude-globs` input only. Once it re-vendors the canonical that
#1474 gave a `globs` key, a bare local run lints the vendored theme and the
WordPress archive. The seam covers it, and `themes/` takes `*/**` rather than
`**/*.md` so its own `themes/README.md` stays linted.

## Filed rather than fixed here

Whole-unit review passes over the four changed canonical units raised 35
findings that predate this change, none introduced by it. Two are verified
defects in fleet-carried content: the `MD060` comment has `compact` and `tight`
inverted, and its stated rationale is false, since the rule's default already
allows both spacings. Two more follow from #1474: the config's `globs` key is
additive to command-line arguments, so a per-file invocation lints the whole
tree, and `globs` covers `.md` but not `.markdown`.

`reports/canonical-review.json` records the passes.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The seam this branch settles is falsified by the audit dimension that
measures the same property. `spec/project-types.json`'s `parity.markdownlint`
asserts "One .markdownlint-cli2.jsonc", at `letter` verdict and `appliesTo`
`*`, and `AUDIT.md` "4. Per-Dimension Checks" states it in prose as "one
config per linter". So the first repository to follow the prescription in
`spec/divergences.json` would have drawn a `linter-parity` drift finding for
doing what `CODESTYLE.md` now mandates.

Both now say a nested `.markdownlint-cli2.jsonc` merges into that config
rather than forking it. The framing is merge semantics rather than what the
nested file carries, because two narrower attempts were both wrong: keying on
a subtree "the repository does not author" splits from the scope wording every
other surface uses, and permitting a nested config "carrying only a subtree
exclusion" would have failed this repository's own
`host-setup/agent-safety/claude/.markdownlint-cli2.jsonc`, which carries an
`MD041` rule override and no exclusion at all.

`docs/reusable-workflows.md` also stops claiming Blog has already migrated.
It has not: both nested paths 404 and its `test-pull-request.yml` still
carries `markdown-exclude-globs`, which the same section reprints as the
CI-only alternative, so the section asserted two contradictory current states
for one repository. The Blog shapes are written as a migration it is owed.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Findings from the branch's own review passes, none of them cosmetic.

`docs/reusable-workflows.md` said "Only `ignores` works from a nested
config", which this repository's own
`host-setup/agent-safety/claude/.markdownlint-cli2.jsonc` falsifies: it
carries an `MD041` override and no exclusion, and a nested `config` block
works precisely because settings merge. The text now states the merge and
names `globs` and `gitignore` as the inert pair.

Two claims about `ptr727/Blog` were false. Its root config predates the
`globs` key, carrying neither `globs` nor `ignores`, so a bare local run
there exits 2 on a usage error rather than linting `content/` and
`themes/`, and its migration needs the re-vendor as well as the nested
files. It was also called the only repository owed the seam, which the
`Financial-Modeling` row in the same change contradicts.

The seam's scope named "a committed data archive or a vendored theme it
does not author" while the ledger row blesses excluding
`notes/forms-decisions.md`, a file that repository's owner writes. The
surfaces now name the category, content a repository does not treat as
authored prose, with the archive, the theme, and a hand-maintained record
as its examples.

`parity.markdownlint` and the `AUDIT.md` sentence said a nested config
"merges into that config rather than forking it", which is true of every
nested config and so can never be false, leaving a `letter`-tier auditor
no predicate. Both now say the count is of the root config, which is a
hand count of one path, and neither extends the editor-parity claim to a
nested `ignores`, the one surface where that parity does not hold.

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

coderabbitai Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 32 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: d73259a9-1df6-4a09-9424-dc2e663f1fdc

📥 Commits

Reviewing files that changed from the base of the PR and between b5fc121 and 93b1f0d.

⛔ Files ignored due to path filters (6)
  • .claude-plugin/fleet-skills/.source-digests/comment-and-doc-style is excluded by !.claude-plugin/fleet-skills/**
  • .claude-plugin/fleet-skills/.source-digests/python-codestyle is excluded by !.claude-plugin/fleet-skills/**
  • .claude-plugin/fleet-skills/skills/comment-and-doc-style/SKILL.md is excluded by !.claude-plugin/fleet-skills/**
  • .claude-plugin/fleet-skills/skills/python-codestyle/references/profiles.md is excluded by !.claude-plugin/fleet-skills/**
  • .github/skills/comment-and-doc-style/SKILL.md is excluded by !.github/skills/**
  • .github/skills/python-codestyle/references/profiles.md is excluded by !.github/skills/**
📒 Files selected for processing (10)
  • .agents/skills/comment-and-doc-style/SKILL.md
  • .agents/skills/python-codestyle/references/profiles.md
  • .markdownlint-cli2.jsonc
  • AUDIT.md
  • CODESTYLE.md
  • docs/reusable-workflows.md
  • reports/canonical-review.json
  • spec/divergences.json
  • spec/fidelity-model.md
  • spec/project-types.json

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.

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 consistently update the spec, audit wording, documentation, and canonical guidance to support nested markdownlint exclusions without introducing behavioral or contract inconsistencies.

Pull request overview

This pull request documents and codifies a fleet-wide seam for repo-local Markdown lint exclusions by using nested .markdownlint-cli2.jsonc files colocated with excluded content, keeping the repository-root config canonical and verbatim for drift detection.

Changes:

  • Updates audit/spec parity language to clarify that the linter-parity check counts only the repository-root .markdownlint-cli2.jsonc, allowing nested configs without failing parity.
  • Records the governed divergence resolution path for ptr727/Financial-Modeling (re-vendor root config, move repo-local ignores into nested configs).
  • Updates linting documentation and the markdownlint canonical config commentary to reflect the nested-config seam and the fleet-wide node_modules ignore.
File summaries
File Description
spec/project-types.json Clarifies markdownlint parity assertion to count only the root config and permit nested configs.
spec/fidelity-model.md Explains why markdownlint remains verbatim with repo-local excludes moved to nested configs.
spec/divergences.json Adds a tracked Financial-Modeling divergence disposition describing the nested-config migration.
reports/canonical-review.json Updates canonical review ledger entries/digests for the affected units.
docs/reusable-workflows.md Documents the nested-config seam as the correct way to exclude unowned/vendored Markdown.
CODESTYLE.md Codifies the nested-config approach for repo-local markdownlint exclusions.
AUDIT.md Updates linter-parity prose to state nested .markdownlint-cli2.jsonc does not count as a second config.
.markdownlint-cli2.jsonc Adds explanatory comments tying the fleet-wide ignores list to the nested-config seam.
.github/skills/python-codestyle/references/profiles.md Removes outdated guidance about markdownlint rule disabling at repo root (generated copy).
.github/skills/comment-and-doc-style/SKILL.md Adds nested-config seam guidance (generated copy).
.claude-plugin/fleet-skills/skills/python-codestyle/references/profiles.md Mirrors the generated skill update for the Claude plugin distribution.
.claude-plugin/fleet-skills/skills/comment-and-doc-style/SKILL.md Mirrors the generated skill update for the Claude plugin distribution.
.claude-plugin/fleet-skills/.source-digests/python-codestyle Updates plugin source digest for the python-codestyle skill distribution.
.claude-plugin/fleet-skills/.source-digests/comment-and-doc-style Updates plugin source digest for the comment-and-doc-style skill distribution.
.agents/skills/python-codestyle/references/profiles.md Removes outdated markdownlint guidance from the authored skill source.
.agents/skills/comment-and-doc-style/SKILL.md Adds the nested-config seam guidance to the authored skill source.
Review details
  • Files reviewed: 16/16 changed files
  • Comments generated: 0
  • Review effort level: Lite

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

@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 a37e8a8 into develop Sep 9, 2026
9 checks passed
@ptr727
ptr727 deleted the feature/markdownlint-exclusion-seam branch September 9, 2026 22:10
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