Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,30 @@ jobs:
- name: Run check-tick-history-order
run: tools/hygiene/check-tick-history-order.sh

lint-no-conflict-markers:
# Fail if any committed file contains git merge-conflict markers
# (`<<<<<<<`, `=======`, `>>>>>>>`). Aaron 2026-04-26 ask:
# *"maybe we should hygene for <<<<<<< HEAD and >>>>>>> ======= things
# like that in our files incase we ever accidently botch a merge,
# happens to the best of us humans too."*
Comment on lines +349 to +352
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

P1 (repo convention): This workflow comment introduces direct contributor-name attribution (“Aaron …”) on a current-state CI surface. Per docs/AGENT-BEST-PRACTICES.md “No name attribution…”, please rewrite to role-refs (e.g., “human maintainer request (2026-04-26)”) and keep names to the enumerated history surfaces only.

Copilot uses AI. Check for mistakes.
#
# Per Otto-339 anywhere-means-anywhere: conflict markers in
# substrate would shift weights wrongly when read by AI. Per
# Otto-341 mechanism-not-vigilance: CI catches this regardless
# of which agent / human / harness produced the merge. The
# script self-allowlists files that legitimately document
# merge-conflict resolution.
name: lint (no conflict markers)
timeout-minutes: 2
runs-on: ubuntu-22.04

steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Run check-no-conflict-markers
run: tools/hygiene/check-no-conflict-markers.sh

lint-no-empty-dirs:
# Fail if a committed directory has no files — almost always a
# forgotten artefact (an agent-created skill folder without a
Expand Down
119 changes: 119 additions & 0 deletions tools/hygiene/check-no-conflict-markers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/usr/bin/env bash
#
# tools/hygiene/check-no-conflict-markers.sh — fails the build if
# any committed file contains git merge-conflict markers
# (`<<<<<<<`, `=======`, `>>>>>>>`).
#
# Why this exists (Aaron 2026-04-26):
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

P1 (repo convention): This comment adds direct contributor-name attribution on a current-state code surface. docs/AGENT-BEST-PRACTICES.md requires role-refs in code/workflows and reserves names for the closed list of history surfaces; please rewrite these references accordingly.

Suggested change
# Why this exists (Aaron 2026-04-26):
# Why this exists (human contributor note, 2026-04-26):

Copilot uses AI. Check for mistakes.
# "maybe we should hygene for <<<<<<< HEAD and >>>>>>> ======= things
# like that in our files incase we ever accidently botch a merge,
# happens to the best of us humans too."
#
# Botched merges leak conflict markers into committed files. Per
# Otto-339 anywhere-means-anywhere: those markers in substrate
# would shift weights wrongly when read by AI. Per Otto-341
# mechanism-not-vigilance: a CI check catches this regardless of
# which agent / human / harness produced the merge.
#
# What this checks:
# - All tracked files (via `git ls-files`) for the three conflict
# marker patterns at line-start
# - Reports first violations with file + line number
# - Exits non-zero if any are found
#
# What this does NOT check:
# - Other tools' specific conflict markers (Mercurial, etc.) —
# repo is git, only git markers matter
# - File content within blocks — only the markers at line-start
# - Untracked files — only committed/staged matters
Comment on lines +24 to +28
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

P2: The doc says “Untracked files — only committed/staged matters”, but the implementation reads the working tree content of git ls-files paths (so it won’t necessarily reflect what’s staged in the index for partial stages). Suggest rewording to “tracked working tree files” or explicitly scanning the index if staged-only detection is intended.

Copilot uses AI. Check for mistakes.
#
# Composes with:
# - tools/hygiene/check-tick-history-order.sh (other substrate
# integrity check)
# - .github/workflows/gate.yml (wired as a lint job)
#
# Self-test:
# $ tools/hygiene/check-no-conflict-markers.sh
# → exit 0 if clean
# → exit 1 with diagnostic if conflict markers found

set -euo pipefail

REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
cd "$REPO_ROOT"

# This script itself documents the conflict marker patterns. Skip
# self-match by excluding this file from the search.
SELF_PATH="tools/hygiene/check-no-conflict-markers.sh"

# Files where conflict-marker discussion is legitimate (e.g.,
# documentation explaining merge resolution, this script, or
# substrate captures of conflict-resolution work). Add here as
# needed; keep the list short and explicit.
ALLOWLIST=(
"$SELF_PATH"
# Substrate / research files documenting merge-conflict resolution
# discipline. They legitimately contain the marker tokens as
# examples. Allowed because the file body is meta-discussion not
# accidental marker leakage.
"memory/feedback_otto_341_lint_suppression_is_self_deception_noise_signal_or_underlying_fix_greenfield_large_refactors_welcome_training_data_human_shortcut_bias_2026_04_26.md"
Comment on lines +55 to +59
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

P1: ALLOWLIST contains a hard-coded path to a memory file that isn’t present in the repo. This dead entry makes it unclear what is actually exempt; either remove it until the file exists or replace it with the correct existing path that legitimately contains marker examples.

Suggested change
# Substrate / research files documenting merge-conflict resolution
# discipline. They legitimately contain the marker tokens as
# examples. Allowed because the file body is meta-discussion not
# accidental marker leakage.
"memory/feedback_otto_341_lint_suppression_is_self_deception_noise_signal_or_underlying_fix_greenfield_large_refactors_welcome_training_data_human_shortcut_bias_2026_04_26.md"

Copilot uses AI. Check for mistakes.
)

is_allowed() {
local path="$1"
for allowed in "${ALLOWLIST[@]}"; do
if [[ "$path" == "$allowed" ]]; then
return 0
fi
done
return 1
}

# Search at line-start (^) — accidental conflict markers are always
# at column 1. This also avoids false positives in legitimate
# content that mentions the marker tokens inline (in prose).
PATTERN='^(<<<<<<<[[:space:]]|=======$|>>>>>>>[[:space:]])'

violations=0
first_hit=""

while IFS= read -r file; do
if is_allowed "$file"; then
continue
fi
if [[ ! -f "$file" ]]; then
continue
fi
# Use grep -E with line numbers; binary files quietly skipped.
if hits=$(grep -nE "$PATTERN" "$file" 2>/dev/null); then
while IFS= read -r line; do
violations=$((violations + 1))
if [[ -z "$first_hit" ]]; then
first_hit="$file:$line"
fi
echo "VIOLATION: $file:$line" >&2
done <<< "$hits"
fi
done < <(git ls-files)

Comment on lines +80 to +98
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

P1: This runs grep separately for every tracked file, which will also scan large/binary tracked artifacts (e.g., the PDFs under docs/). Consider using git grep with binary ignored or otherwise skipping known-binary/large files so the check stays reliably fast under the 2-minute CI timeout.

Suggested change
while IFS= read -r file; do
if is_allowed "$file"; then
continue
fi
if [[ ! -f "$file" ]]; then
continue
fi
# Use grep -E with line numbers; binary files quietly skipped.
if hits=$(grep -nE "$PATTERN" "$file" 2>/dev/null); then
while IFS= read -r line; do
violations=$((violations + 1))
if [[ -z "$first_hit" ]]; then
first_hit="$file:$line"
fi
echo "VIOLATION: $file:$line" >&2
done <<< "$hits"
fi
done < <(git ls-files)
git_grep_args=(git grep -nI -E "$PATTERN" -- .)
for allowed in "${ALLOWLIST[@]}"; do
git_grep_args+=(":(exclude)$allowed")
done
git_grep_args+=(":(exclude)references/**")
# Search tracked text files in one pass; `-I` skips binary content.
if hits="$("${git_grep_args[@]}" 2>/dev/null)"; then
while IFS= read -r hit; do
violations=$((violations + 1))
if [[ -z "$first_hit" ]]; then
first_hit="$hit"
fi
echo "VIOLATION: $hit" >&2
done <<< "$hits"
fi

Copilot uses AI. Check for mistakes.
if [[ $violations -gt 0 ]]; then
echo "" >&2
echo "FAIL: $violations git merge-conflict marker line(s) found in committed files" >&2
echo "" >&2
echo "First hit: $first_hit" >&2
echo "" >&2
echo "How to fix:" >&2
echo " - Open each flagged file" >&2
echo " - Resolve the conflict (pick one side, both sides, or" >&2
echo " re-merge manually); REMOVE all marker lines" >&2
echo " - Verify by re-running this script (exit 0 = clean)" >&2
echo "" >&2
echo "If a file legitimately discusses these markers (docs about" >&2
echo "merge resolution, this script itself, or substrate files" >&2
echo "documenting merge-conflict-resolution work), add the path" >&2
echo "to the ALLOWLIST in this script." >&2
exit 1
fi

echo "OK: no git merge-conflict markers found in committed files"
exit 0
Loading