Skip to content

fix(skill-quality): stop check 21 passing silently under mawk (0.17.1) - #3058

Merged
kyle-sexton merged 3 commits into
mainfrom
claude/pocock-steering-course-00zkvd
Aug 20, 2026
Merged

fix(skill-quality): stop check 21 passing silently under mawk (0.17.1)#3058
kyle-sexton merged 3 commits into
mainfrom
claude/pocock-steering-course-00zkvd

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Closes #3005

Summary

Check 21 (fresh-eyes declaration conformance) silently passed every file it was given on any machine
whose awk is mawk — the Ubuntu/Debian default. Its embedded awk program used ERE interval
expressions, and mawk aborts the program before it emits a single record, so malformed
fresh-eyes-exempt directives that should FAIL were reported as a clean run. Silent-pass is the worst
shape a gate can fail in, and the plugin advertises running "against any repo", so this made that
claim false for a large share of consumers.

Fix

The issue attributed the break to mawk not implementing ERE intervals. That turned out to be the wrong
diagnosis for the container in question, and the correction narrowed the fix. awk there is mawk
1.3.4 20240123
, which does implement intervals. Isolating each construct:

Pattern Result
^ {0,3}> ? — interval + literal compiles
^ {0,3}[abc] — interval + bracket class compiles
^ {0,3}[0-9]{1,9} — interval + interval compiles
^ *([-*+]) — star + group compiles
^ ? ? ?([-*+]) — optionals + group compiles
^ {0,3}([-*+])[ \t]+interval + group panics

The trigger is specific: an ERE interval immediately followed by a parenthesized group panics mawk
1.3.4's regex compiler with REcompile() - panic: values still on machine stack. The panic is fatal,
which is the silent-pass mechanism. Sweeping every .sh file in the repo for that shape finds exactly
two sites, both in this scanner — the list-marker strip and fence detection — which is why check 23,
written interval-free in #2963, was unaffected.

The scanner is now interval-free throughout, not just at the two panicking sites. The two
surviving {0,3} sites compiled fine under 1.3.4, but mawk 1.3.3 implements no intervals at all
and matches the braces as literal text, degrading exactly the same way — a second silent failure mode
for the same regexes on an older but still widely deployed mawk. The rewrite preserves the exact
CommonMark bounds already enforced: the three-space indent cap becomes three optional spaces, the
ordered-marker digit cap one digit plus eight optional ones. Both bounds were verified to still reject
out-of-range input (4 spaces does not match; a 10-digit marker is not stripped).

The two alternatives the issue floated at triage were declined deliberately. A loud gawk required
exit would make the "runs against any repo" claim false on stock Debian/Ubuntu rather than fixing it,
and command -v gawk || awk leaves the silent-pass path live wherever gawk is absent.

The portability guard

A source-level guard assertion fails the suite if an interval returns to any awk-consumed regex. It is
deliberately implementation-aware rather than black-box: gawk compiles intervals happily, so a
reintroduced interval would pass the entire suite on the CI runner and break only for consumers. No
behavioral assertion can observe this class of break on gawk.

Two properties of the guard came out of the review cycle (see the resolved threads):

  • All three interval forms are rejected, {n} included, not just the comma forms. mawk panics on
    a{3}(b) exactly as it does on a{0,3}(b) — verified directly — so a comma-only pattern would have
    left the same silent-failure class open.
  • It is scoped to what awk actually compiles, not to the whole file. Bash is not mawk, and
    check-skill.sh legitimately uses an interval in a [[ =~ ]] regex to date a frontmatter synced:
    value; an unscoped broadening would have failed the suite on correct code. The two surfaces that do
    reach awk are covered: slash-delimited regex literals inside the embedded programs, and
    FRESH_EYES_JUDGE_RE, which is a POSIX ERE awk compiles via -v but which never appears between
    slashes and would be missed by a literal-only or line-range scan.

Verification

Run in the affected environment (awk/usr/bin/mawk, mawk 1.3.4 20240123):

  • Premise reproduced on origin/main @ d9f15d4f firstcheck-skill.test.sh failed 21
    assertions, all Check 21, emitting the REcompile() panic.
  • check-skill.test.sh: 96 → 118 passing, 0 failures, 0 panics. Diffing the baseline and
    post-fix ok sets shows exactly +21 newly passing from the scanner fix, zero assertions that
    regressed from passing to failing, plus the new guard assertion.
  • Guard proven to fail, not just to pass. Reintroducing the original bug drove the suite to 22
    failures — the 21 restored panics plus the guard naming the file and line. After the review
    hardening, each of {n}, {n,} and {n,m} was reintroduced in turn and the guard caught every
    one, while the clean tree still passes with the Bash date regex untouched. Tree restored after each
    probe.
  • scripts/check-changed-skills.test.sh: PASS=13 FAIL=0 — the other suite that exercises
    check-skill.sh, whose fixtures have broken on a checker change before.
  • Behavioral sanity on real skills — the fixed checker now genuinely emits fresh-eyes records
    under mawk (previously none at all), and skill-quality/skills/check, work-items/skills/work
    and source-control/skills/pull-request all still PASS with 0 errors.
  • Full local gate, all green: validate-plugins.sh; generate-catalog.mjs --check and
    generate-cheatsheet.mjs --check; check-changelog-parity.sh in all four modes, including
    --check-preserved origin/main (36 headings compared, all preserved) and --check-bump origin/main; check-changed-skills.sh origin/main; both portability lints; markdownlint-cli2 on
    the touched CHANGELOG (0 issues); typos on the touched plugin (clean); shellcheck on both
    touched scripts (clean).
  • Version collision re-checked against origin/main immediately before each push — main is on
    skill-quality 0.17.0, so 0.17.1 is uncontested.

These counts are reproducible from a clean checkout on any mawk box with
bash plugins/skill-quality/scripts/check-skill.test.sh.

Related


🤖 Generated with Claude Code

https://claude.ai/code/session_01QbfCrj3X9FfGL7VRZYmrn4

Check 21's fresh-eyes scanner used ERE interval expressions, two of them
immediately followed by a group. mawk 1.3.4 panics on that construct with
"REcompile() - panic: values still on machine stack" and dies before emitting a
single record, so on a stock Debian/Ubuntu box every malformed
fresh-eyes-exempt directive PASSed and the check reported a clean run over a
file it never scanned. mawk 1.3.3, which implements no intervals at all,
degrades the same way by matching the braces as literal text.

Isolating the construct shows the trigger is narrow: interval-then-literal,
interval-then-class, interval-then-interval and star/optional-then-group all
compile fine under mawk; only interval-then-group panics. Sweeping every .sh
file in the repo for that shape finds exactly the two sites in this scanner,
which is why check 23 (written interval-free in #2963) was unaffected.

The scanner is now interval-free throughout, preserving the exact CommonMark
bounds it already enforced: the three-space indent cap as three optional
spaces, the ordered-marker digit cap as one digit plus eight optional ones.
gawk behavior is unchanged; on mawk the suite gains 21 passing assertions with
no regressions (96 -> 117 ok).

A source-level guard assertion fails the suite if any interval returns. It is
deliberately implementation-aware rather than black-box, because a gawk CI
runner compiles intervals happily and so cannot observe this class of break in
any behavioral form.

Closes #3005

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbfCrj3X9FfGL7VRZYmrn4
@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 50s —— View job


Security review — no issues found

  • Gather context (PR diff, changed files)
  • Run skip-gate check
  • Review diff for security-relevant surface
  • Post findings

Skip gate: This PR has no security-relevant surface. All four changed files (plugin.json, CHANGELOG.md, check-skill.sh, check-skill.test.sh) are: a version bump, a changelog entry, and a rewrite of embedded ERE regexes in check-skill.sh's fresh-eyes scanner ({0,3}? ? ?, [0-9]{1,9} → repeated [0-9]?) plus a matching source-level guard assertion in the test suite.

Verified there's no injection surface introduced:

  • The awk program is a fixed literal script; the only externalized values (FRESH_EYES_PROXIMITY_LINES, FRESH_EYES_JUDGE_RE) are passed via awk -v, not string-interpolated into the program text, both before and after this change.
  • The file being scanned (fe_file) is passed as an awk positional argument, never spliced into the script.
  • The regex rewrites are behavior-preserving pattern substitutions (interval quantifiers → equivalent optional-repetition chains) with no change to what data flows where or how it's later used — this is a fault-tolerance/portability fix (mawk panic → silent pass), not a change to trust boundaries, authz, secret handling, or any GitHub Actions workflow.
  • The new test assertion (grep -nE guard) only greps the checker's own source file for a forbidden syntax shape; no untrusted input reaches it.

No CRITICAL, IMPORTANT, or SUGGESTION findings.

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

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

@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: c84cb36917

ℹ️ 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/skill-quality/scripts/check-skill.test.sh Outdated
@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 2m 46s —— View job


Code review

  • Gather context (PR body, REVIEW.md, workflow files)
  • Review diff (plugin.json, CHANGELOG.md, check-skill.sh, check-skill.test.sh)
  • Post findings

This repo carries a claude-security-review.yml workflow, so this lane (code-review) is scoped to correctness/maintainability only; security is left to that lane.

Regex-conversion correctness (verified by hand-tracing, not by running the suite — I don't have test-execution access in this lane):

  • ^ {0,3}> ?^ ? ? ?> ?: equivalent — three independently-optional literal spaces before > covers the same 0–3 range as {0,3}, including the boundary case (a 4-space-indented > correctly fails to match either way).
  • [0-9]{1,9}[0-9][0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?: 1 required + 8 optional digits = 1–9 digits, matching the original bound.
  • ^ {0,3}(```+|~~~+)^ ? ? ?(```+|~~~+): same equivalence as the first case.

One finding posted inline on check-skill.test.sh:2988 (Suggestion, non-blocking): the new portability guard's regex only catches comma-form ERE intervals ({n,m}), so a bare exact-count interval like {9} could reintroduce the same mawk-panic class undetected, while the guard's pass message claims the source is fully "free of ERE interval expressions." Widening the regex naively would false-positive on the unrelated bash-level date regex at check-skill.sh:703, so a real fix needs to scope the check to the awk program's line range — noted for awareness, not required for this PR.

Everything else checked out:

  • check-skill.sh:703's own {4}/{2} date regex is bash's [[ =~ ]] engine, not awk, so it's correctly untouched.
  • CHANGELOG entry and 0.17.00.17.1 patch bump are consistent with the fix's scope and the plugin's existing changelog format.
  • The PR's own scope note (declining to touch check-changelog-parity.sh and markdown-format.test.sh, which use intervals that compile under mawk 1.3.4 but would degrade under 1.3.3) is a reasonable, explicitly-documented boundary — not something this diff needed to absorb.

No other correctness issues found. Test-run counts and CI results in the PR description are author-reported and were not independently re-executed here.

…ount form

Review on #3058 found two gaps in the portability guard, both verified against
the tree before fixing.

The pattern required a comma inside the braces, so an exact-count interval slipped
through. That is the same silent-failure class the guard exists to prevent:
mawk 1.3.4 panics on a{3}(b) exactly as it does on a{0,3}(b), confirmed by
direct reproduction. All three forms are now rejected.

Broadening alone would have introduced a false positive. check-skill.sh:703
dates a frontmatter value with a Bash [[ =~ ]] regex using ^[0-9]{4}-[0-9]{2},
which is legal there because Bash is not mawk. The guard is therefore scoped to
what awk actually compiles rather than to the whole file: slash-delimited regex
literals inside the embedded programs, plus the judge regex handed across with
-v, which is a POSIX ERE awk compiles even though it is written as a shell
string and never appears between slashes.

Verified by reintroducing each of the three interval forms in turn and
confirming the guard names the file and line for every one, while the clean
tree still passes with the Bash date regex in place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbfCrj3X9FfGL7VRZYmrn4
Comment thread plugins/skill-quality/scripts/check-skill.test.sh
@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 52s —— View job


Security review — no issues found

  • Gather context (PR diff since last-reviewed head)
  • Run skip-gate check
  • Review diff for security-relevant surface
  • Post findings

Skip gate: No security-relevant surface in the commits since the last-reviewed head (c84cb36). This push (8f464e2) only scopes the mawk-portability guard to awk-consumed regexes and adds coverage for exact-count {n} intervals, responding to the Codex review comment on the prior revision, plus the matching CHANGELOG update. check-skill.sh itself is unchanged in this push.

Verified no new risk was introduced:

  • Both grep -nE patterns only scan the checker's own source file ($SUT, a fixed local path) for a forbidden syntax shape — no untrusted input reaches them, and no new externalized/interpolated value was added.
  • The interval regex (\{[0-9]+(,[0-9]*)?\}) and the two scoping greps are static literals; nothing here touches trust boundaries, secrets, or workflow permissions.

No CRITICAL, IMPORTANT, or SUGGESTION findings.

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

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

@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

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


Code review — /review:code-review

  • Gather context (PR diff, changed files)
  • Run skip-gate check
  • Review diff for correctness/maintainability
  • Post findings

Skip gate: PR is open, not draft, and the diff (regex rewrite + new source-level guard) has real review surface — proceeded with a full review.

Scope: correctness and maintainability of the two commits currently on this branch (c84cb368f464e2), which together rewrite check-skill.sh's fresh-eyes awk program to be interval-free and add a source-level portability guard in check-skill.test.sh. Security is out of scope for this lane (already cleared by the security-review lane on c84cb36).

What I checked

  • Manually traced each rewritten regex against its {m,n} original for behavioral equivalence (not just re-stated the PR's own claims):
    • ^ {0,3}> ?^ ? ? ?> ? (blockquote depth, check-skill.sh:1039)
    • [0-9]{1,9}[.)][0-9][0-9]?×8[.)] (ordered-marker digit cap, check-skill.sh:1093)
    • ^ {0,3}(```+|~~~+)^ ? ? ?(```+|~~~+) (fence detection, check-skill.sh:1104)
    • All three: rejecting a 4-space indent / 10-digit marker still fails to match at position 0 the same way the original {0,3}/{1,9} did (POSIX leftmost-longest, no backtracking surprises) — the CommonMark bounds are preserved, not just visually similar.
  • Verified the new guard's scope is correct, not just present: swept every awk invocation in check-skill.sh (lines 702, 1021, 1299) and confirmed none outside the fresh-eyes program contain an interval today, and that the guard's two grep surfaces (slash-delimited regex literals + FRESH_EYES_JUDGE_RE=...) would catch a reintroduced interval in any of them.
  • Confirmed the guard correctly leaves check-skill.sh:703's Bash [[ "$SYNCED_VAL" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] alone (neither grep surface matches it — no slashes, not FRESH_EYES_JUDGE_RE), which is the intended awk-only scoping, not a gap.
  • Confirmed this commit already addresses the Codex P2 finding on the prior commit ({n} exact-count intervals are now covered — INTERVAL_RE='\{[0-9]+(,[0-9]*)?\}' matches with or without a comma).
  • Checked the new guard's own regex for false-positive risk against the comment prose it's embedded next to (which itself contains literal {0,3} / {1,9} text) — the comment lines have only one / each, so the guard's two-slash-bracketing pattern can't match them, and the comment-line exclusion (grep -vE '^[0-9]+:[[:space:]]*#') would filter them either way.
  • CHANGELOG entry and version bump are consistent with repo convention (Keep a Changelog, no dates on other entries either) and accurately describe the final (post-guard-fix) state, not a stale description of the first commit.

I was not able to execute awk/bash -n/the test suite myself in this sandbox (Bash access in this lane is restricted to gh pr diff/view/comment/review), so the above is static/manual verification, not a re-run of the author's test results — I'm not vouching for the PR's own "96 → 117 passing" / "22 failures on regression" numbers, only for the regex equivalence and guard-scoping reasoning I traced myself.

Findings

One inline nit posted, no blocking issues:

  • P3 (nit) check-skill.test.sh:2993 — the guard's comment says "the frontmatter date check below," but that check lives in a different file (check-skill.sh:703) and earlier in it, not below this comment. Purely a misleading pointer for a future reader; the guard's actual behavior toward that check is correct.

No correctness issues found in the regex rewrites or the new guard's scoping logic.
(branch: claude/pocock-steering-course-00zkvd)

Comment thread plugins/skill-quality/scripts/check-skill.test.sh Outdated
The scoping rationale said "the frontmatter date check below", but that check
lives in check-skill.sh, not below this comment in the test file. Name the file
and what the check does instead, and describe it rather than citing a line
number that would rot on the next edit to that file.

Comment only; the guard's behavior is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbfCrj3X9FfGL7VRZYmrn4
@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 37s —— View job


Security review — no issues found

  • Gather context (PR diff since last-reviewed head)
  • Run skip-gate check
  • Review diff for security-relevant surface
  • Post findings

Skip gate: No security-relevant surface in the commits since the last-reviewed head (8f464e2). This push (88d0227) contains a single one-line-of-substance change — a comment-text correction in check-skill.test.sh's mawk-portability guard, replacing a misleading pointer ("the frontmatter date check below") with an accurate description of where that check lives. This is the same nit flagged inline on the prior revision.

Verified no functional change accompanies it:

  • The guard's regex (INTERVAL_RE='\{[0-9]+(,[0-9]*)?\}') and its scoping logic are byte-identical to the previously-reviewed revision — only the comment prose above it changed.
  • No new externalized input, trust boundary, or workflow permission is touched by a comment edit.

No CRITICAL, IMPORTANT, or SUGGESTION findings.
(branch: claude/pocock-steering-course-00zkvd)

@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 57s —— View job


Code review — /review:code-review

  • Gather context (PR diff, changed files, prior review comments)
  • Run skip-gate check
  • Review diff for correctness/maintainability
  • Post findings

Skip gate: Not skipped — reviewing the diff introduced since the last-reviewed head. PR is open, not draft.

Scope: Only 8f464e288d0227 is new since this lane's last full pass (already covered c84cb36 and 8f464e2). That commit touches a single file, check-skill.test.sh, +3/-2, comment text only — no code, no test logic, no assertion behavior changed.

What changed: The scoping-rationale comment above the mawk-portability guard previously said the Bash [[ =~ ]] date regex it intentionally exempts is "the frontmatter date check below" — misleading, since that check lives in a different file (check-skill.sh) and earlier in it, not below this comment in this file. This was flagged independently by both this lane's own prior pass and a human-authored inline comment at check-skill.test.sh:2993 on the prior revision. This commit fixes it by naming the file and describing what the check does instead of citing a location.

Verified by hand:

  • The new wording — "check-skill.sh dates a frontmatter synced: value with one" — accurately describes check-skill.sh:703, [[ "$SYNCED_VAL" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]], which does check a vendor-sync frontmatter synced: field via a Bash interval regex.
  • Describing by content rather than by line number is also more durable — the prior "below" pointer broke because of file/line drift, and this phrasing doesn't have a line number to rot.
  • No functional lines changed; the guard's INTERVAL_RE, its two grep surfaces, and the fail/pass logic are untouched.

No correctness or maintainability issues found in this commit. All findings from earlier passes on this PR were either already fixed by the author (exact-count {n} coverage, awk-scoping) or are the exact nit this commit resolves.
(branch: claude/pocock-steering-course-00zkvd)

@kyle-sexton
kyle-sexton merged commit 53b1cbc into main Aug 20, 2026
51 checks passed
@kyle-sexton
kyle-sexton deleted the claude/pocock-steering-course-00zkvd branch August 20, 2026 21:52
kyle-sexton pushed a commit that referenced this pull request Aug 20, 2026
…on collisions

Main landed six commits while this branch was open, three of which bumped plugins
this branch also bumped, to the same numbers. Resolved by stacking this branch's
entry above main's released one:

  claude-ops    0.34.0 (both) -> mine becomes 0.35.0, main's 0.34.0 kept
  session-flow  0.29.0 (both) -> mine becomes 0.30.0, main's 0.29.0 kept
  skill-quality 0.17.1 (both) -> mine becomes 0.17.2, main's 0.17.1 kept

The two manifest conflicts were NOT the version field. Bumping with `jq` had
rewritten each whole file, re-encoding main's — escapes as literal UTF-8
em-dashes in the description string. Resolved by taking main's exact bytes and
editing only the version line, so no manifest carries an incidental encoding
change.

Also: main's audit-skill-visibility SKILL.md sat at exactly the 200-line soft
target, so this branch's inline pair-cooccurrence section pushed it to 234 and
tripped a warning. The detail moves to reference/pair-cooccurrence.md — the
progressive-disclosure spoke the check recommends — and the pointer folds into
the existing scope-boundary row rather than adding one, landing back at 200 with
0 errors and 0 warnings.

Verified on the merged tree: 33/33 co-occurrence cases, changelog parity, shell
portability (56 files), cross-plugin drift, fleet doc-grammar, fleet finding
coverage, silent skips, markdownlint on every conflicted file, and check-skill
on audit-skill-visibility. Main's mawk fix (#3058) also cleared the 21 assertion
failures check-skill.test.sh was reporting in this tree; it now passes fully.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CnzwTKoTa6xNY7iyEzMYpm
kyle-sexton added a commit that referenced this pull request Aug 20, 2026
…-interval sites (#3062)

Closes #3000

## Summary

Materializes the project glossary that lane 6's term adoption deferred,
and — at the maintainer's direction rather than as a filed follow-up —
closes the two remaining ERE-interval sites in awk regexes that PR #3058
scoped out. Two commits, kept separate so neither is buried.

Documentation and test-probe hardening only; no runtime behavior change.
`markdown-format` 0.11.21 → 0.11.22.

## Fix

**1. The glossary (#3000).** `curate-language`'s convention ladder found
no existing glossary and more than one plausible home, so it deferred
rather than inventing one — that was the entire blocker. The maintainer
confirmed the flat `docs/GLOSSARY.md` placement, beside `CATALOG.md` and
`SKILL-CHEAT-SHEET.md`, satisfying the ladder's step 4 ("ask when two or
more plausible choices remain").

- **Seven ADOPTED terms**, each a 1–2 sentence what-it-IS definition:
AFK criterion, asset rush, context load, cognitive load, navigation
pointer, phase boundary, primary source, secondary source, smart zone.
- **The REJECT rows recorded as rejected synonyms**, each mapped to the
term or doctrine that owns the concept — `design concept` → shared
understanding, `highway / stale highway` → navigation pointer, `cache`
(doc-restating sense) → audit-derivability's doctrine, `sediment`, `push
vs point` → point-don't-copy, `grill-execute-clear`, and `sycophancy`
(owned by nothing — recorded so it is not reintroduced as project
vocabulary, with free-prose use explicitly unaffected).
- **Vocabulary only**, per the glossary contract: no decisions, specs,
or open questions. The reasoning stays in the lane 6 table, which the
glossary cites and which now links back — the issue's third acceptance
criterion.
- **Single language context**, so no context map. Stated in the file so
the absence reads as a decision rather than an omission.

**2. The two latent mawk-interval sites.** Both **compile** under mawk
1.3.4 — each interval precedes a literal or a bracket class, not a
group, so neither hits the panic that broke check 21 — but mawk
**1.3.3** implements no intervals at all and matches the braces as
literal text. Each then degrades *silently*:

| Site | Unfixed behavior on mawk 1.3.3 |
|---|---|
| `scripts/check-changelog-parity.sh` — fence probe `^ {0,3}` | stops
recognizing code fences, so the scanner segments every changelog it
reads wrongly |
| `markdown-format.test.sh` — heading probe `#{1,6}` | stops recognizing
headings, so the MD024 assertion passes vacuously — a test that cannot
fail |

Both rewritten interval-free with identical bounds: three optional
spaces, and one hash plus five optional ones.

Swept the repo for the same shape rather than trusting the two names.
`ai-slop`'s `PATTERN_RULES` carry `[^.]{0,80}(...)` — the
interval-then-group form that actually panics — but they reach `grep
-E`, not awk, and that script's awk programs already avoid intervals
deliberately. No change needed there; confirmed by reading its
consumption sites.

## Verification

- **Bounds verified in both directions** under this container's mawk
1.3.4: 0/1/2/3 leading spaces match and 4 does not; 1–6 hashes match and
7 does not. A rewrite that stopped panicking while quietly widening the
bound would be the same class of silent defect these fix.
- `plugins/markdown-format/hooks/markdown-format.test.sh` — **PASS=149
FAIL=0**
- `scripts/check-changelog-parity.test.sh` — **PASS=82 FAIL=0**
- `check-changelog-parity.sh` all four modes, re-run **after
committing** so `--check-preserved` had something to compare: 83
changelogs newest-first, and **50 headings compared, all preserved**
- `scripts/validate-plugins.sh`; `generate-catalog.mjs --check` /
`generate-cheatsheet.mjs --check` — in sync
- `check-changed-skills.sh origin/main` — no changed skills under
`plugins/*/skills/`
- `markdownlint-cli2` (3 files) — 0 issues; `typos` clean; `shellcheck`
and `shfmt -d` clean on both touched scripts
- `scripts/check-changelog-parity.sh` is a repo-level script with no
plugin manifest, so it takes no version bump.

One self-inflicted defect caught and fixed before pushing: the new
changelog entry initially replaced the `## [0.11.21]` heading instead of
sitting above it, absorbing that release's notes. `grep '^## \['` now
reads 0.11.22 → 0.11.21 → 0.11.20 → 0.11.19, and `--check-preserved`
confirms it.

## Related

- Refs #2904 (lane 6, the term-adoption decisions),
`docs/upstream/aihero-course.md` "Term adoption"
- Refs #3005 / PR #3058 — established the interval-free shape and named
these two sites as out of its scope
- Refs #2994 — the AI Hero course roadmap this queue item belongs to

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01QbfCrj3X9FfGL7VRZYmrn4

---
_Generated by [Claude
Code](https://claude.ai/code/session_01QbfCrj3X9FfGL7VRZYmrn4)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
kyle-sexton pushed a commit that referenced this pull request Aug 20, 2026
…l CSV-injection fix

Lanes 5 and 10 of the cursor/plugins pstack port.

LANE 5 (`show-me-your-work`) — absorbed into one consumer, no skill, no
convention.

Upstream keeps a reviewable decision trail for long or unattended work:
`ts | phase | decision | why | evidence | result`, append-only, plus a log
helper, a self-audit against the transcript, a mandatory cross-model review, and
a standing per-reply section.

The lane opened proposing to absorb this into session-flow:running-retro. An
adversarial audit destroyed that premise correctly: running-retro's ledger is
defect-shaped over five session-process categories, written by a
transcript-parsing subagent after the fact, in a file whose cumulative-chain
identity lives in YAML frontmatter a TSV cannot carry, and the skill "does not
run builds, tests, or a code review" so it cannot produce a `result` cell at
all. Two artifacts, not one. session-flow is also barred from the contract tier
by its own binding, and topic-docs already records `history.md` (append-only
decision log) as deliberately absent.

The audit then argued the opposite verdict: ship a shared capture format that
the fleet's "eight" existing audit-trail surfaces route into. That count was
checked surface by surface and does not hold. One is a decision trail —
implement-dispatch's DEVIATIONS.md. One is related but differently shaped
(handoff's settled-decisions and abandoned-approaches sections, synthesized at
pause time rather than appended at decision time). The other six are not
decision trails: a change inventory, loop counters, a hook-wired notification
record on a governed schema, a status comment edited in place, OTel spans whose
contract explicitly forbids a parallel schema, and a return record whose first
line reads "capturing RETURN — not activity". One consumer is below Rule of
Three, so a marketplace convention would be the same speculative generality that
killed `arena`. The full classification table is in the provenance file.

So DEVIATIONS.md gains what it was missing: append-and-supersede rather than
edit, evidence as a pointer with a preference for what a committed script
produced, an explicit outcome that says `unverified` rather than reading as
settled, and one-entry-is-one-decision. The unverified-vs-omitted rule cites the
grounding discipline work-loop and babysit-loop already carry verbatim instead
of restating it a third time.

The formula-injection guard was the highest-value single item in the upstream
file, and looking for somewhere to apply it found a live exposure rather than a
hypothetical one. claude-ops:audit-install-state wrote scanned `relpath` and
five other tree-derived fields raw through `csv.writer` into an artifact the
skill tells the reader to open row by row — and a plugin, project, or worktree
directory under `~/.claude` may be named anything, including a formula. Fixed
with `csv_safe`, covering the leading tab, CR and LF a spreadsheet strips before
deciding. `TestCsvFormulaInjection` was confirmed discriminating: 8 subtest
failures with the guard disabled in memory, 0 with it restored.

Rejected: the mandatory different-model-family review (the same unconditional
cross-vendor demand already rejected for `arena`, against 15+ presence-gated
sites); the standing per-reply "Attention" section (a session-wide output
posture is a declared species here with one member, and the instruction-economy
rule wants observed repeated stumbles); and the log-vs-transcript self-audit,
which already ships verbatim in two lanes.

LANE 10 (`bro`) — omitted, nothing absorbed.

Seven lines upstream, two of them body, no mechanism. The capability ships twice
already: education:explain's empty argument resolves to the previous assistant
response by anaphora with 'rephrase that' among its triggers, and
discipline:wait-what is the interjection-fired re-pitch. The brevity pressure
that is `bro`'s only distinguishing note ships a third time as
discipline:tighten-your-output, and adhd:clarify already states the routing
between them by name. Shipping it would also need a second entry on the naming
grammar's closed exception list, carrying the argument already spent on
wait-what. Recorded at length only so a later reader sees the file was read
rather than skipped for being short.

Also merges origin/main, which independently landed the same check-21 mawk fix
this branch carried (#3058). Main's is the faithful translation — it preserves
the original 1-9 digit bound where this branch had widened it to unbounded — and
its test suite already asserts the positive liveness case, so this branch's
version was dropped entirely in favour of main's. `review` renumbered 0.24.0 to
0.25.0 after a collision with main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtvBfWQRz7w6kgRnJm4qgj
kyle-sexton added a commit that referenced this pull request Aug 21, 2026
…ued absorb/omit verdicts (#3065)

No linked issue

## Summary

Ports the ten skills of cursor/plugins' `pstack` collection (MIT, pinned
at `main@60c641e4`) into this marketplace — one lane at a time, each
through interview → adversarial audit → plan → implement. Every lane is
a **reauthor**, not a fork: substance kept where it earns its place,
wrapper adapted to this marketplace's conventions, prose rewritten.
Provenance lives in `docs/upstream/cursor-pstack.md` and the plugin
CHANGELOGs, never in skill bodies.

**Two skills ship. Eight lanes absorb or omit, each with a written
argument.** That ratio is the point: an adversarial fresh-context
validator ran over every lane's decision set with the rationale
withheld, and overturned the majority of the recommendations on most
lanes. Where it was right, the plan changed; where it was wrong on the
facts, the record says so and why.

| Lane | Upstream | Verdict |
|---|---|---|
| 1 | `why` | **Ships** `discovery:trace-intent` — intent archaeology
from records, five evidence tiers |
| 2 | `blast-radius` | **Ships** `review:quality-gate downstream` — the
outward-looking review lens |
| 3 | `arena` | Omitted; 4 ideas into `architecture:improve`,
`prototype`, `naming` |
| 4 | `technical-writing` | **Ships** `docs-hygiene:write-for-humans` —
lane-2 shaped |
| 5 | `show-me-your-work` | Absorbed into `implement-dispatch`; found a
live CSV-injection bug |
| 6 | `recall` | Omitted, absorbed nothing — every candidate already had
an owner |
| 7 | `teach` | 2 rules into `education:teach`'s lesson contract |
| 8 | `tdd` | 2 rules into `testing:write` and `debugging:debug` |
| 9 | `reflect` | Omitted; one routing gap closed in both retro skills |
| 10 | `bro` | Omitted, nothing absorbed — capability already ships
twice |

## Fix

**Lane 1 — `discovery:trace-intent`.** Recovers *why* a change was made
from review threads, tickets and design docs. Five intent-evidence
tiers, renamed from upstream's "confidence" because the axis measures
inferential distance, not certainty — ICD 203 forbids conflating them.
Adds a per-citation source-reliability note that annotates without
routing, because every comparable scheme (ICD 203, GRADE, Admiralty)
separates directness from source quality. **One deliberate departure:**
upstream admits labelled code-shape inference; this forbids it outright,
on operational grounds — code is the one source always present and free
to consult, so a weak-but-admissible rung for it gets filled exactly
when the real record is thin.

**Lane 2 — `review:quality-gate downstream`.** The whole review lane was
diff-scoped and nothing looked outward; verified by reading each
incumbent rather than assumed. Rejected upstream's five-rung proof
ladder, which would have been this fleet's *ninth* evidence ladder.

**Lane 4 — `docs-hygiene:write-for-humans`.** The sibling
`write-for-agents` excludes human-facing prose in both its description
and its own "does NOT do", and nothing else claimed it. Ships the
Diátaxis mode picker, a rhythm section against machine-cadence prose,
one sentence-rules spoke, and drift-stamped sources. **The re-posture is
the port:** upstream ships Google style / ASD-STE100 / Global English as
house rules; here they are a *named, replaceable default set*, applied
only after a search for the consuming project's own style guide comes
back empty. The plan's own draft contained the disproof — a decision
existed solely to delete two rules because they conflicted with this
repo's em-dash ruling, and a standard that must be pre-edited to stop
fighting its home repo is not a value that "cannot conflict in any
repo".

**Lanes 3, 5, 6, 7, 8, 9, 10 — absorbed or omitted.** Highlights:
`arena` omitted on Rule of Three (two consumers, not three, and they
disagree about everything a shared runner would fix), with its
rejected-shapes field, graft ledger and read-the-spread discipline
absorbed. `recall` produced nothing and says so. `bro` contributes two
sentences and no mechanism against three incumbents.

**One real bug, found rather than ported.** Hunting for somewhere to
apply upstream's formula-injection guard turned up a live exposure:
`claude-ops:audit-install-state` wrote scanned `relpath` and five other
tree-derived fields raw through `csv.writer` into an artifact the skill
tells the reader to open row by row — and a plugin, project, or worktree
directory under `~/.claude` may be named `=HYPERLINK(...)`. Fixed with
`csv_safe`, covering the leading tab/CR/LF a spreadsheet strips before
deciding.

## Verification

All gates run locally against `origin/main` at the branch tip, all
green:

- `check-changed-skills` — 15 skills checked, 0 failed. Both new skills
PASS with **0 errors, 0 warnings**.
- `validate-plugins`, `validate-plugin-contracts` (48 setup skills, 2816
files)
- `check-changelog-parity` — all four modes (`--check`, `--check-bump`,
`--check-order`, `--check-preserved`; 13 changed changelogs, 329
headings compared)
- `check-contract-slice-prune` — both modes; the change set leaves no
path under `docs/topics/`
- `check-skill-portability`, `check-skill-count-claims`,
`check-skill-leaf-names`, `check-cross-plugin-source-drift`,
`check-plugin-manifest-presence`, `check-orphaned-fixtures`,
`check-silent-skips`, `check-fleet-audit-doc-grammar`,
`check-lane-coverage`, `check-contract-clause-coverage`,
`check-stale-base-overlap`
- `check-evals-quality` on both new eval suites — PASS, 0 warnings
- `markdownlint-cli2` on every touched markdown file — 0 issues
- `install_state` Python suite — 50 tests OK, including the new
`TestCsvFormulaInjection`

**The CSV-injection test was confirmed discriminating**, not just
passing: with `csv_safe` disabled in memory its cases fail 8 subtests,
and pass with it restored.

## Related

- `docs/upstream/cursor-pstack.md` — the single source of truth for
everything derived from this upstream: eight attribution-table rows plus
prose sections for the two lanes that shipped nothing, each with its
argument and a recheck trigger.
- ADR 0016 — the always-listed description budget the two new skills are
measured against.
- ADR 0004 — the incumbent-first gate every lane ran before
recommending.
- `docs/PLUGIN-PHILOSOPHY.md` — the two-lane convention posture that
reshaped lane 4, and the instruction-economy rule that declined several
otherwise-novel absorbs.
- `docs/MIGRATION-PLAYBOOK.md` — the skill-split and Rule-of-Three rules
behind the omissions.
- #3058 — main's independent fix for the same check-21 mawk defect this
branch had carried; main's version was taken wholesale (see below).

## Notes for the reviewer

- **Where the audit changed the plan.** Lane 4 was re-postured from
house rules to resolvable defaults. Lane 5's absorb target moved twice —
the plan's `running-retro` was the wrong artifact, and the audit's
counter-proposal of a marketplace convention was checked surface by
surface and found to have **one** genuine adopter, not the eight
claimed. Lane 7's two rules were re-homed off `visualization:visualize`
(a form router that "is not a craft teacher"). Lane 8's cost branch
moved to `testing:write`'s existing decline list. Lane 9's fix widened
to cover `retro` as well as `running-retro`. Two of my own factual
claims were caught and corrected in the record, including a grep that
used `\|` without `-E` and so found nothing.

- **One dissent is recorded, not smoothed over.** The audit argued for
absorbing `reflect`'s three orthogonal review lenses, and correctly
caught that the plan had miscited `arena`'s reasoning. They stay
declined on the instruction-economy rule, and `cursor-pstack.md` says so
plainly so a later reader who disagrees knows where to start.

- **check-21 collision with main.** Main landed the same mawk fix
independently while this branch was in flight. Main's is the faithful
translation — it preserves the original 1–9 digit bound where this
branch had widened it to unbounded — and its suite already asserts the
positive liveness case, so this branch's version was dropped entirely.
`review` renumbered 0.24.0 → 0.25.0 after a version collision.

- **Pre-existing local test failures are not from this branch.**
`scripts/run-plugin-tests.sh` fails 31 checks across four suites
(`claude-config` ×3, `claude-ops:morning-brief`,
`discovery:check-coverage-complete`). Every one reproduces identically
on a clean `origin/main` worktree; the cause is the container running as
uid 0, so `chmod 000` fixtures stay readable. Most suites SKIP for
exactly this; these four hard-FAIL instead. CI runs non-root.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01EtvBfWQRz7w6kgRnJm4qgj)_

---------

Co-authored-by: Claude <noreply@anthropic.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.

skill-quality: check-skill.sh fresh-eyes scanner breaks under mawk — ERE interval expressions unsupported

2 participants