Azure / Azure/mpf

task md:lint corrupts the .github/copilot-instructions.md symlink on Windows

Open
#315 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Go
Stars
66
Forks
11
Avg merge
2d 4h
Merged PRs (30d)
12

Description

### What happened

Running the documented `task md:lint` on Windows modifies `.github/copilot-instructions.md`, which is a symlink to `../AGENTS.md`. Committing that change would replace the symlink with a broken target path and silently detach the repository's Copilot instructions from `AGENTS.md`.

### Why it happens

`.github/copilot-instructions.md` is stored as a symlink (mode `120000`) whose blob content is the literal string `../AGENTS.md` with no trailing newline.

Git for Windows defaults to `core.symlinks=false`. Under that setting the entry is checked out as a plain text file containing the target path rather than a real symlink. `markdownlint-cli2` then sees an ordinary `.md` file that does not end in a newline and "fixes" it:

```text
.github/copilot-instructions.md:1:12 error MD047/single-trailing-newline Files should end with a single newline character
```

The resulting diff against a clean tree:

```diff
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index be77ac8..6afa560 120000
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -1 +1 @@
-../AGENTS.md
\ No newline at end of file
+../AGENTS.md
```

The mode stays `120000`, so the entry is still a symlink as far as Git is concerned, but its target becomes `"../AGENTS.md\n"` — a path that does not exist.

### Reproduction

On Windows with `core.symlinks=false` (the default):

```powershell
git clone https://github.com/Azure/mpf.git
cd mpf
task md:tools
task md:lint
git status --short # M .github/copilot-instructions.md
```

Isolated to confirm which tool is responsible:

```powershell
task md:run:markdownlint-cli2:fix --force
git status --short # M .github/copilot-instructions.md
```

`markdown-table-formatter` does not touch the file. `markdownlint-cli2` is the one that rewrites it.

### Why it is easy to miss

The change is a single invisible trailing newline in a one line file. Anyone who runs the documented lint task and then stages with `git add -A` or `git commit -a` will pick it up without noticing, and the diff looks harmless in review. I hit this while fixing an unrelated markdown table in #314 and only caught it because the file was outside the scope of my change.

### Suggested fix

Exclude the symlink from linting in `.github/linters/.markdownlint-cli2.yaml`:

```yaml
ignores:
- .git
- "**/node_modules/**"
- .copilot-tracking/**
- venv/**
- .venv/**
- .github/copilot-instructions.md
```

That keeps the current symlink layout, which is nice because `AGENTS.md` stays the single source of truth.

Alternatives, if you would prefer not to special case a path:

- Disable `MD047` for that file via an inline configuration comment, though that is awkward since any content added to a symlink defeats its purpose.
- Replace the symlink with a small stub file that points readers at `AGENTS.md`, avoiding symlink portability issues altogether at the cost of duplication.

The ignore entry seems like the smallest change.

### Environment

- Windows, Git for Windows with `core.symlinks=false`
- `markdownlint-cli2` v0.23.1 (markdownlint v0.41.1), as pinned by `task md:install:markdownlint-cli2`
- `main` as of 2026-07-27

This is not specific to my checkout. Any Windows contributor with default Git settings who runs the documented lint task will reproduce it.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.