Skip to content

fix(ai-slop, claude-config): make emit-findings esc() idempotent and resolve repo-root spellings (0.3.6, 0.39.4) - #3242

Merged
kyle-sexton merged 3 commits into
mainfrom
cursor/3203-emit-findings-esc-cfcf
Aug 23, 2026
Merged

fix(ai-slop, claude-config): make emit-findings esc() idempotent and resolve repo-root spellings (0.3.6, 0.39.4)#3242
kyle-sexton merged 3 commits into
mainfrom
cursor/3203-emit-findings-esc-cfcf

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

Summary

#3202 fixed two latent defects in docs-hygiene:audit-noise emit-findings.sh only. Each producer carries its own copy, so the same defects were still live in ai-slop and claude-config:audit-instructions.

Defect 1 — esc() is not idempotent. A naive gsub(/\|/, "\\|") turns a \| b into a \\| b. GFM reads that as a literal backslash followed by a live delimiter, so the row splits and the fix action misreads it. This repo writes literal \| in its own tables.

Defect 2 — repo-root spelling mismatch. git rev-parse --show-toplevel can answer C:/Users/… while the caller is at /tmp/… (Git Bash). Consequences differed per producer and were confirmed individually:

  • ai-slop failed open — Location stayed absolute and nothing reported it, because an absolute path is still a well-formed cell.
  • claude-config failed closed — a path it could not prove was under the root was declined as outside-repo-root, so an in-repo finding was silently missed.

Fix

esc() now parks an already-escaped pipe on a sentinel first, matching #3202. Root resolution prefers the caller pwd minus git --show-prefix, with git's two spellings as fallbacks, matching the #3180 form that #3202 did not actually land.

Shared code was considered and declined: plugins are portable and there is no existing cross-plugin emit-findings library. The three copies now agree on the same two helpers instead.

ai-slop 0.3.6 (0.3.5 is #3179), claude-config 0.39.3.

docs-hygiene residual (not fixed here): #3202 shipped the idempotent esc() but not the three-spelling root. That producer still resolves root via git rev-parse --show-toplevel only.

Verification

  • plugins/ai-slop/skills/audit/scripts/detect.test.sh — 98/98 (idempotent esc + symlink spelling mismatch)
  • plugins/claude-config/skills/audit-instructions/scripts/emit-findings.test.sh — 81/81 (same two cases; fail-closed fence still declines a true out-of-repo path)
  • scripts/check-changelog-parity.sh --check-bump origin/main — pass
  • markdownlint on both changelogs — 0 issues

Related

Closes #3203

Refs #3180, #3202

@claude

claude Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 4s —— View job


Security review (/review:security-review)

  • Gather context (PR diff, changed files, CLAUDE.md instructions)
  • Invoke /review:security-review skill
  • Post findings

Skip gate hit: no security-relevant surface.

This PR touches only plugins/ai-slop/skills/audit/scripts/emit-findings.sh, plugins/claude-config/skills/audit-instructions/scripts/emit-findings.sh, their test scripts, plugin.json version bumps, and CHANGELOG entries. No .github/workflows/** files changed.

I read both emit-findings.sh diffs in full:

Both changes are internal path-normalization and awk string-escaping fixes for a documentation-audit pipeline:

  • The esc() idempotency fix (sentinel-park already-escaped \|, then re-escape) operates only on strings already destined for a Markdown table cell — no new eval, no shell interpolation of untrusted content, no injection surface introduced.
  • The repo-root spelling reconciliation (REPO_ROOT / REPO_ROOT_ALT / REPO_ROOT_PWD) extends the existing prefix-match root check to three known spellings but preserves each producer's existing trust posture (ai-slop fails open on no match, claude-config fails closed via declined_outofrepo) — this is the same class of check as before the PR, just resolving more spellings of the same trusted git rev-parse output, not accepting new untrusted input.
  • No new dependency pins, no permission or trigger changes, no secrets/token handling, no command construction from external input.

No CRITICAL, IMPORTANT, or SUGGESTION security findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 826ec21174

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/claude-config/skills/audit-instructions/scripts/emit-findings.sh Outdated
@github-actions

Copy link
Copy Markdown

Last security-reviewed head: 826ec211745d475dd0832c8dd100328a5ab4ed55. On the next push, the relevance gate compares only the commits since this SHA; delete this comment to force a full re-review.

@cursor
cursor Bot force-pushed the cursor/3203-emit-findings-esc-cfcf branch from 826ec21 to 250be7b Compare August 23, 2026 22:32
kyle-sexton and others added 3 commits August 23, 2026 22:33
…resolve repo-root spellings (0.3.6, 0.39.3)

Both sibling emit-findings.sh copies still carried the two defects #3202
fixed only in docs-hygiene. A naive gsub double-escapes a pipe the source
already escaped, which GFM reads as a live delimiter. esc() now parks
already-escaped pipes on a sentinel first.

git rev-parse --show-toplevel can spell the same directory differently
than the caller pwd (Git Bash). ai-slop failed open (Location stayed
absolute, nothing reported it). claude-config failed closed (in-repo
hits were declined as outside-repo-root). Root resolution now prefers
the caller pwd minus git --show-prefix, with git's two spellings as
fallbacks. Shared code was considered and declined: plugins are
portable and there is no existing cross-plugin emit-findings library.

Closes #3203

Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
Walk the full backslash run before each pipe so a\\|b stays one GFM
cell, and drop Windows user-path literals from comments/changelog.

Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
…39.4 entry

Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
@cursor
cursor Bot force-pushed the cursor/3203-emit-findings-esc-cfcf branch from ffa22b4 to 89ed48c Compare August 23, 2026 22:33
@kyle-sexton kyle-sexton changed the title fix(ai-slop, claude-config): make emit-findings esc() idempotent and resolve repo-root spellings (0.3.6, 0.39.3) fix(ai-slop, claude-config): make emit-findings esc() idempotent and resolve repo-root spellings (0.3.6, 0.39.4) Aug 23, 2026
@kyle-sexton
kyle-sexton enabled auto-merge (squash) August 23, 2026 22:34
@kyle-sexton
kyle-sexton merged commit 7bb3073 into main Aug 23, 2026
55 checks passed
@kyle-sexton
kyle-sexton deleted the cursor/3203-emit-findings-esc-cfcf branch August 23, 2026 22:44
@claude

claude Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Claude finished @cursor[bot]'s task in 5m 39s —— View job


Code review (/review:code-review)

  • Gather context (PR diff, changed files, CLAUDE.md instructions)
  • Invoke /review:code-review skill criteria
  • Review changed files for correctness/maintainability
  • Post findings

Security is out of scope for this lane (this repo carries a dedicated security-review workflow; that lane already posted a skip on this PR). Reviewed both emit-findings.sh diffs (esc() idempotency + repo-root spelling reconciliation), both test files, and the version/changelog bumps.

1 suggestion, posted inline:

Investigated, no live bug: I hand-traced esc()'s bs % 2 == 0 branch for bs=2 against Codex's scenario. The walk collects the 2-backslash run, sees the trailing |, bumps bs to 3 (odd), and emits \\\|. In GFM that decodes as \\ → literal \, then \| → literal | with the delimiter still escaped — which matches how the source's \\| would have rendered before landing in a table cell, and it doesn't split the row. So the parity logic in the version at 89ed48c4 (the head SHA under review) is correct for this case; Codex's comment reads like it was reviewing an earlier commit (826ec21174) with a different esc() implementation. The finding above is about locking that in with a test, not a correctness defect.

Everything else checked out:

  • REPO_ROOT / REPO_ROOT_ALT / REPO_ROOT_PWD prefix matching always appends / before comparing, so there's no false-prefix collision (e.g. /tmp/foo vs /tmp/foobar).
  • ai-slop's fail-open (relativize) vs claude-config's fail-closed (relativize_in_repo) postures are each preserved from before the PR — only the set of root spellings each checks against grew.
  • plugin.json version bumps (ai-slop 0.3.6, claude-config 0.39.4) match their CHANGELOG.md headers.
  • New script-level variables are all initialized before use under set -euo pipefail, and the new symlink-based tests are a reasonable way to exercise the Windows-spelling scenario on Linux CI.

Comment on lines +473 to +483
ALREADY="$TEST_TMPDIR/already-esc.txt"
cat >"$ALREADY" <<'EOF'
Finding: rule=ai-slop/audit/rule-em-dash file=doc.md line=1 fired=a\|b excerpt=text
Summary rule=ai-slop/audit/rule-em-dash findings=1 declined=0 disabled=0
EOF
ALREADY_OUT="$TEST_TMPDIR/findings/already-esc.md"
bash "$EMIT" --from "$ALREADY" --out "$ALREADY_OUT" --branch test-branch >/dev/null 2>&1
already_row="$(LC_ALL=C grep -m1 '^| 1 ' "$ALREADY_OUT")"
assert_not_contains "emit: an already-escaped pipe is not double-escaped" "$already_row" '\\\|'
assert_contains "emit: and survives as a single-escaped literal" "$already_row" 'a\|b'
already_delims="$(printf '%s' "$already_row" | sed 's/\\|//g' | awk -F'|' '{print NF - 1}')"

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.

Test-coverage gap in the new esc() idempotency case.

This case only exercises bs=1 (source already carries a single, correctly-escaped \|). The parity branch that actually decides correctness — an even, non-zero backslash run before a pipe (e.g. source excerpt containing a\\|b — two backslashes then a pipe) — has no test in either copy of this suite.

I hand-traced esc() for bs=2: the walk collects bs=2, sees the trailing |, and since 2 % 2 == 0 bumps it to 3, emitting \\\|. Rendered in GFM that's \\ → literal \, then \| → literal | (and the delimiter stays escaped), which matches the pre-table rendering of the two-backslash-then-pipe source. So the logic is correct as merged — but that's exactly the branch Codex flagged as suspect on an earlier revision, and it's the one branch nothing pins down with a regression test today.

Worth adding a sibling case here (and in emit-findings.test.sh) with a source excerpt containing an even, non-zero backslash run before | (e.g. fired=a\\|b), asserting it comes out as a\\\|b and still parses as the expected column count. Cheap insurance against a future edit to esc() silently breaking the one non-obvious parity branch.

Fix this →

Comment on lines +409 to +419
PIPEF2="$CRLFREPO/already-pipe.md"
# shellcheck disable=SC2016 # backticks and \| are fixture content, not a subshell
printf 'CRITICAL: run `a \| b` before pushing.\n' >"$PIPEF2"
# Feed a synthetic scan row whose excerpt (the source line) already contains \|.
printf '%s\n' "$PIPEF2:1:I28-a" >"$TEST_TMPDIR/already-pipe.txt"
(cd "$CRLFREPO" && bash "$EMIT" --from "$TEST_TMPDIR/already-pipe.txt" \
--out "$TEST_TMPDIR/already-pipe-out.md" --branch testbranch >/dev/null 2>&1)
ALREADY_ROW=$(LC_ALL=C grep -m1 '^| [0-9]' "$TEST_TMPDIR/already-pipe-out.md")
assert_not_contains "an already-escaped pipe is not double-escaped" "$ALREADY_ROW" '\\\|'
assert_contains "and survives as a single-escaped literal" "$ALREADY_ROW" '\|'
ALREADY_COLS=$(printf '%s\n' "$ALREADY_ROW" | sed 's/\\|//g' | awk -F'|' '{print NF}')

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.

Same gap as the ai-slop copy (see comment on detect.test.sh): this case only covers bs=1 (source already has a single \|). The bs % 2 == 0 branch for a non-zero even backslash run before a pipe — the exact shape Codex's review questioned — isn't exercised here either. I traced it by hand and it's correct as merged (a\\|ba\\\|b, which GFM renders back to literal \|), but a regression test for that branch is cheap and would settle the question for good instead of relying on a manual trace.

@github-actions

Copy link
Copy Markdown

Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count.

kyle-sexton added a commit that referenced this pull request Aug 24, 2026
…3270)

Closes #3266

## Summary

`detect.sh` expands a directory target to the markdown files beneath it.
The prefix came from `git rev-parse --show-toplevel` and the filter from
`pwd`. On hosts where those spellings differ (Git Bash: `D:/repo` vs
`/d/repo`), no prefixed candidate survived the filter, so `|| find`
silently replaced the tracked-files listing. The walk includes untracked
and ignored markdown.

## Fix

Directory expansion now runs `git ls-files` with `-C <dir>`, which is
already restricted to that directory's subtree and answers in paths
relative to it. The caller's own spelling of the directory is the only
anchor. The branch is chosen up front from `--is-inside-work-tree`, so a
filesystem walk is only ever the answer for a directory genuinely
outside a checkout. Inside one, a listing that fails reports on stderr
instead of degrading into a different set of files.

ai-slop 0.3.8.

## Verification

- `plugins/ai-slop/skills/audit/scripts/detect.test.sh` — 132/132
- `dir target in git repo: only the tracked file counts` — PASS
- `scripts/check-changelog-parity.sh --check-bump origin/main` — pass
- `--check-preserved origin/main` — 0.3.7 heading kept
- `--check-order` — pass

## Related

Refs #3242

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
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.

fix(fleet): detector-findings producers share two latent defects in their own copies of esc() and repo-root resolution

1 participant