Commit aeba0de
gate(GATE-CONFLICT-MARKERS): refuse a tracked file that carries a merge conflict marker (#1450)
Four record gates return rc 0 on a `docs/STATUS.md` carrying a spliced
merge
conflict. Each measures its own budget, and none asks whether a merge
tool wrote
into the file at all, so a keyed row that is half one branch and half
another
satisfies every budget it is measured against. An earlier revision of
#1414's
branch carried exactly that file, the full record gate set ran, and it
reported
clean; a person caught it by reading `git diff --stat`.
This adds one tree-scoped checker, in one place rather than four, and
wires it
where the pre-push record gates already run.
## The rule, and why the separator is conditional
A line that starts with seven `<` and a space opens a hunk, and a line
that
starts with seven `>` and a space closes one. Either fails, anywhere in
a tracked
text file. A line of exactly seven `=` fails only when a start marker
opened a
hunk above it.
The separator stays conditional because a bare row of `=` is legal
markdown: it
is the setext heading underline and it is a horizontal rule. Two
independent
narrowings keep the gate off ordinary text, and both were measured
rather than
assumed. **Exactly seven:** five shipped files already carry a rule of
eight or
more `=` (three logs under
`docs/bench-evidence/gdn-replayssm-w0-20260818/` and
two tokenizer corpora under `tests/parity/goldens/`), so a `^=+` rule
would have
been red on arrival. **Adjacency:** even a line of exactly seven is
silent unless
a real marker opened a hunk.
The diff3 `|||||||` marker is deliberately absent. A diff3 conflict
still carries
the start and end markers, so nothing escapes, and a line of seven `|`
is a
plausible empty row in a repository whose records are wide markdown
tables.
## No allowlist, and no per-change registration
Every pattern is built by character repetition rather than written as a
literal,
so the checker and its suite carry no marker at column 0 and need no
exemption
from the gate they implement. The suite builds its fixtures in temporary
git
repositories. Nothing in the repository has to register itself with this
gate,
and no file accumulates a list that every change must append to.
AGENTS.md
§ *Records* forbids that surface: if N concurrent pull requests edit
file F, F is
a lock.
A document that quotes a conflict marker on purpose indents it. That
remedy is
printed in the failure message, and it is per instance and in the text,
not a
registry entry.
## RED-BEFORE
The defect was re-derived rather than quoted, on a detached scratch
worktree at
`b537a5344`. Five lines were spliced into the `docs/STATUS.md`
capability table:
a start marker, a duplicated keyed row, a separator, the same row again,
and an
end marker. The mutation asserted its own application against the file's
sha256
before and after, and `git diff --stat` read `docs/STATUS.md | 5 +++++`.
```
=== check-public-doc-tables rc=0 ===
OK: docs/BENCHMARKS.md and docs/FEATURES.md are human-readable keyed tables, docs/STATUS.md is inside its size ratchet, and .agents/benchmark-record.md carries the append-only record.
=== check-agent-record rc=0 ===
agent record OK: ENGINE=168 MODEL=377 QUANT=84 KERNEL=52 BACKEND=85 ANCHOR-ROT=38
=== check-doc-checkpoint --base b537a53 --head HEAD rc=0 ===
OK: public documents match the claims this change makes.
=== check-issue-index-append-only --base b537a53 --head HEAD rc=0 ===
OK: issue index append-only
```
The two range-scoped checkers ran over a detached scratch **commit**,
not over
the working tree. A working-tree mutation of a commit-reading checker
returns 0
because the checker never reads the mutated bytes, and that 0 looks
exactly like
a gate that cannot detect the defect.
The suite was written before the checker existed and was seen failing:
`Ran 17 tests`, `FAILED (failures=17)`, rc 1.
## GREEN-AFTER
Same tree, same splice, the new gate:
```
root: /dev/shm/red-1417
docs/STATUS.md:129: conflict start marker: <<<<<<< HEAD
docs/STATUS.md:131: conflict separator inside the hunk opened at line 129: =======
docs/STATUS.md:133: conflict end marker: >>>>>>> origin/main
conflict markers: 3 findings in 1 file; examined 3728 tracked text files (1077 binary, 1 symlink, 0 absent skipped, 4806 tracked paths)
Resolve the merge before committing. A document that quotes a conflict marker on purpose must indent it, so that it does not start at column 0.
rc=1
```
On the clean tree of this branch:
```
root: /home/mudler/_git/vllm.cpp/.claude/worktrees/fix-1417
conflict markers: 0 findings in 0 files; examined 3733 tracked text files (1077 binary, 1 symlink, 0 absent skipped, 4811 tracked paths)
rc=0
```
**3739 tracked text files examined**, out of 4817 tracked paths: 1077
binary, 1
symlink and 0 absent were skipped, and each class is counted and printed
rather
than dropped in silence. (It read 3733 of 4811 before the last merge of
`main`;
both figures are the same run on different trees.) A gate that cannot
say how
many things it examined has not reported, so an examined count of zero
exits 2
instead of printing a clean verdict, and a file the run could not READ
exits 2
as well rather than being called clean. The report also names the
resolved root,
because a checker resolves its root from its own path and a run from a
linked
worktree can otherwise print OK about the shared checkout.
The clean-tree case does not assert a floor. It asserts EQUALITY with
git's own
tracked text set, derived at read time from `git grep -I --name-only -e
''` plus
the tracked files that have no lines at all. Both sides are re-derived
on every
run, so nothing is stored and nothing drifts; the two agreed byte for
byte.
The suite: `Ran 21 tests`, `OK`, `cases run: 21`, rc 0. The count is
asserted by
the runner itself, so a suite that ran zero cases exits 2 rather than
printing
OK.
Cost: 0.29 s of CPU for the whole tree, against 0.9 s before the scan
was moved
off decoding 142 MB of text to bytes. Wall time on this box ranged 1.17
s to
4.60 s under load from two sibling sessions, which is why the spec's
stop
condition bounds CPU and not wall.
## The gate is armed, proven by mutation
Each mutation was applied in a scratch worktree under `/dev/shm`, never
in the
reviewed tree. Each asserted that its bytes actually changed, printed
the new
sha256, compiled the result before running anything, and restored the
file
byte-identically afterwards.
| Mutation | Suite |
|---|---|
| M1 the detection core deleted (`scan_text` returns nothing) | **RED**,
8 failures |
| M2 the separator adjacency guard `open_at is not None` dropped |
**RED**, 1 failure |
| M3 the examined count deleted from the report | **RED**, 5 failures |
| M5 the index dedupe removed (`return names`) | **RED**, 1 failure |
| M6 an unreadable file filed as a finding again | **RED**, 1 failure |
| M7 the file count split on the first colon again | **RED**, 1 failure
|
| M4 the whole-buffer reject deleted | GREEN, and expected |
Each mutation reds the case that owns it. M1 to M3 were re-run after the
refactor, so no earlier commit's mutation proof was silently disarmed by
a later
one.
**M2 is the finding this pull request is most worth reading for.** On
the first
run it stayed GREEN: the adjacency guard was in the source and no case
could
tell whether it was there. A file with no marker leaves the scan early,
so the
guard never decided anything in any fixture, and a new gate carrying a
branch no
mutation can falsify has the same defect it was added to fix.
`test_a_separator_after_the_hunk_closes_is_not_named` was written to
close it. It
gives a file a closed hunk and then a legal setext underline below it;
the exit
code is 1 either way, so the case asserts the report, and it carries its
own
positive control (three named lines) before it claims a fourth is
absent.
M4 is green on purpose and is recorded as such. The whole-buffer reject
is a pure
optimization that decides nothing the per-line pass would decide
differently, and
its docstring says so, so a later reader cannot mistake it for the
semantic
guard.
## The four existing checkers are unchanged
Byte-identical to `origin/main`, verified by sha256 rather than by eye:
```
scripts/check-public-doc-tables.py same=YES
scripts/check-agent-record.py same=YES
scripts/check-doc-checkpoint.py same=YES
scripts/check-issue-index-append-only.py same=YES
```
All four still pass on this tree, tree-scoped and range-scoped, at rc 0.
This
adds a gate; it alters no existing gate's semantics.
## Repairs from the fresh review
The review returned PASS with two LOW findings. Both are repaired here,
because
both bear on the principle this change elevates.
**An unmerged index triple-counted.** `git ls-files` emits stages 1, 2
and 3 for
a conflicted path, so during a live merge the checker read one file
three times
and reported `9 findings in 1 file; examined 3 tracked text files`. The
verdict
stayed 1, so nothing was ever missed, but the count was wrong in exactly
the
state the gate exists for, and no fixture built an unmerged index.
`tracked_paths` now deduplicates with `dict.fromkeys`, which keeps git's
order.
`test_an_unmerged_index_is_counted_once` makes `git merge` genuinely
fail and
asserts the three stage entries before it asserts anything else, so the
dedupe
is falsifiable rather than merely present.
**The clean-tree floor was 27% of the real count.** `> 1000` against a
real 3733
would have stayed green over a scan that collapsed to markdown alone. It
is now
equality with git's own text set, as described above.
Three accuracy repairs came with them. An unreadable file was filed as a
finding, so an `OSError` exited 1 and printed "Resolve the merge before
committing"; it now takes its own list and exits 2, because a file this
run
could not read is a file it cannot call clean. The summary file count
split each
report line on its first colon and merged two tracked paths sharing a
prefix.
And the creation-mutation comments in `check-pr-size.py` claimed the
disabled
stub fails *every* case that runs the checker: measured, it fails **16
of 21**,
and the five survivors assert only that an ordinary document exits 0, or
never
run the checker at all. The mutation is still detected; the sentence was
wrong,
and it is corrected in both places.
The spec's `## Risks` now records the unmerged index as **not seen**
when the
spec was written, rather than as a trade-off that was weighed and
dropped.
## Where it runs
`CHECKERS` in `scripts/agent-preflight.sh`, deliberately first, so a
reader who
sees it fail knows to stop reading the verdicts below it; `SUITES` for
the
mutation suite; and a step in the `agent-record` job of
`.github/workflows/ci.yml`, so a pull request is gated even when
preflight was
never run. The workflow was re-parsed with a loader that rejects
duplicate
mapping keys: 17 jobs, one registered step. PyYAML accepts workflows
GitHub
rejects, and a duplicate key yields zero jobs.
## One in-flow repair, tracked separately as #1448
Registering a new checker requires an entry in `CREATION_MUTATIONS`, so
this
change necessarily edits `scripts/check-pr-size.py`, and a change to
that file
must prove its own HEAD checker/test pair passes. It could not:
`test_every_tracked_and_current_change_path_is_classified` sweeps every
tracked
path, and ten files of `docs/bench-evidence/gdn-replayssm-w0-20260818/`
have been
unclassified since 2026-08-18, because `BENCH_EVIDENCE` matches exactly
one path
segment. That suite is RED on `origin/main`, verified in a detached
worktree at
`9ecaf1bb3`, and it is in no preflight `SUITES` entry, so preflight is
green over
it. It surfaces only as `ERROR: HEAD checker/test pair failed for
'scripts/check-pr-size.py'`, which reads as a defect in the change under
review.
Repaired here, per AGENTS.md § *Every change starts from an issue*: a
`BENCH_EVIDENCE_RUN` pattern that names the surface rather than widening
a rule,
restricted to the extensions the directory carries and deliberately
excluding
`.md` and `.json`, which already classify as `public_document` and would
be
silently reclassified because the `evidence` arm is tested first.
Classification
of all 4811 tracked paths was captured before and after: exactly those
ten move,
and no other path changes class. Issue #1448 tracks it and is not closed
by this
body's keyword, so it can be closed by hand after review confirms the
repair.
Fourth instance of that class after #856, #668 and #989.
## Gates
All re-run after the last merge of `origin/main` `0adeb8b0e`, so the
base is an
ancestor of HEAD and the trailer gates EXECUTE rather than skip.
- `python3 scripts/check-conflict-markers.py` — rc 0, 3739 files
examined.
- `python3 tests/scripts/test_check_conflict_markers.py` — rc 0, 21
cases, `OK`.
- `python3 scripts/check-agent-record.py` — rc 0, re-run after the
merge.
- `python3 scripts/check-public-doc-tables.py` — rc 0.
- `python3 scripts/check-doc-checkpoint.py --base origin/main --head
HEAD` — rc 0.
- `python3 scripts/check-issue-index-append-only.py --base origin/main
--head HEAD` — rc 0.
- `python3 scripts/check-pr-size.py --base origin/main --head HEAD` — rc
0,
`OK: every explicit path class is within its review budget.`
- `python3 scripts/check-commit-trailers.py --range origin/main..HEAD` —
rc 0
over 7 commits, EXECUTED rather than skipped.
- `python3 scripts/check-commit-style.py --range origin/main..HEAD` — rc
0, same
range.
One accounted-for red, and it is not this change's:
`test_cpu_x86_llamacpp_floor` reports `NO_QUIET_WINDOW` exit 4 where the
case
expects 2. That is #618's load dependence, measured on this box at load
average
122-129 and later 157-225, driven by sibling agent sessions. This branch
touches
no benchmark harness, and the same suite re-run on the same sources at
load
average 2.5 reported `Ran 10 tests`, `OK`, rc 0.
The full `scripts/agent-preflight.sh` on this head, after the merge:
**85 gates
`ok`, ZERO skipped**, and rc 1 for that one suite alone.
`commit-trailers` and
`commit-style` both read `ok` rather than `SKIP`, which is the point of
merging
`main` first: a skipped gate reports nothing about the tree, and the
harness
refuses the green banner over one.
`windows-msvc-cpu` and `windows-msvc-vulkan` are red on every pull
request
(#584, #968) with no `main` baseline. This diff carries no C++.
Closes #1417
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
---------
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>1 parent 0adeb8b commit aeba0de
8 files changed
Lines changed: 1078 additions & 0 deletions
File tree
- .agents
- specs
- .github/workflows
- scripts
- tests/scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
479 | 479 | | |
480 | 480 | | |
481 | 481 | | |
| 482 | + | |
| 483 | + | |
482 | 484 | | |
0 commit comments