Skip to content

fix(skills): make doc-get-exact an exact allow-list of the getz recipe - #576

Merged
ThomasK33 merged 5 commits into
mainfrom
fix/566-doc-get-exact-gaps
Nov 13, 2026
Merged

ThomasK33 merged 5 commits into
mainfrom
fix/566-doc-get-exact-gaps

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Why

#566 made the doc-get-exact case of proof-static.sh flag a documented read of a run path that cuts the newline at the end of the path. That check parsed shell with regexes and then with an awk tokenizer. Each review round found one more shape that the parser missed: a nested substitution, a <( with two commands, a read in a group or a loop, and a quoted 'get' word. No doc or script has any of those shapes. The parser did not converge, so this PR removes it.

What changed

All changes are in .agents/skills/verify-claudecode-nvim/: scripts/proof-static.sh and SKILL.md. scripts/vcn.sh and both launch proofs are unchanged.

doc-get-exact now reads text and parses no shell. The one line that reads a path whole is the getz() recipe of features/README.md, which doc-recipe already runs. So in a code block or a single-backtick inline span that code_units reads, a plain shell get of a path word passes only as that line. The shapes that the check misses, read or not, are listed under Stated limits.

For each line of a code block or an inline span (code_units output), the check does five steps.

  1. It joins a line that ends in a backslash to the next line, as the shell does.
  2. It deletes every single quote, double quote and backslash. The shell does the same to g"et", O'UT' and \get.
  3. It finds each word get, an optional -z, and the word after them. -z never counts as that word. A get of a name that holds no path does not end the search on its line.
  4. That word passes when it is PORT, SID, NVIM_PID, TUI_PID, NVIM_08 or a name that starts with CLIENT_PID (none holds a path), a lowercase word (for claude mcp get nvim), or the placeholder NAME or <NAME>.
  5. For any other word, also one that starts with a $, the whole original line must equal the recipe line of features/README.md, byte for byte. So the copy in SKILL.md must equal the copy in the README.

After the doc loop, the case checks that it read every .md file of the skill (13 today) and saw the recipe line at least twice across those docs. A loop that drops a doc before that count, or reads only the units that source a run.env, fails that check. A doc that the loop skips after the count is not caught. Any other .md file in the skill, a dot-named one included, also makes the check fail, and the message does not print the count that find got.

The proof-static.sh header, the ok doc-get-exact line and the SKILL.md bullet say what the scan checks. None of them mentions the doc count yet, and the header and the bullet still use the wider wording that this body narrows. The bullet no longer holds a code span that would fail the check.

What a doc author sees

In the code blocks and spans that the check reads, a plain shell line can read OUT or WORK only with the one recipe line. The shapes under Stated limits pass. Any other line there with get OUT, get -z OUT, get WORK and so on fails, in a comment or in a string too. The failure names the doc and the line. The old allowed form IFS= read -r -d '' OUT < <("$V" get -z OUT) now fails in a doc.

Stated limits

These shapes pass although they hold a get of a path. Main's previous version missed them too.

  • A double-backtick inline span that holds a backtick substitution. code_units reads only single-backtick spans.
  • A name that xargs passes, as in OUT=$(printf OUT | xargs "$V" get).
  • A redirection glued to get, as in get</dev/null OUT. Bash runs it as get OUT.
  • An inline <code> span.
  • An argument list in a Lua or Python block, such as { "get", "OUT" }.
  • A recipe copy split by a backslash and a newline inside single quotes.

These shapes make the check fail on a copy of the recipe that markdown renders the same way but that differs in bytes. The check fails closed.

  • A copy in a fenced block inside a list item, because the block keeps the list indent.
  • A copy in a span that wraps across lines inside a list item.
  • A copy in a double-backtick span that CommonMark pads with spaces.
  • A copy with CRLF line ends.
  • A copy with one trailing space.

Other limits:

  • A get word that a shell expansion builds is not seen: brace expansion ({get,}), a variable or an alias that holds the word, or an eval. A name that a $'..' or ${..} expansion builds is flagged, because the word then starts with a $.
  • The recipe line of features/README.md is the anchor and is trusted as written. doc-recipe runs it, but nothing flags an extra get that someone adds to it.
  • The case reads the docs (SKILL.md and features/*.md). It does not read the scripts. The five journeys define their own get() helper with the safe read, and proof-agent-tty-resolution.sh tests get itself.
  • The limits of the other cases are unchanged: an indented code block, <pre>, a fence that markdown would not open, and four shapes that no file of the skill has (a source whose run.env is on the next line after a backslash, a quoted command word, BASH_ENV, and a use of PORT, SID, NVIM_PID or NODE_DIR without a $).
  • These limits of the previous version are gone, because this check makes them wrong: a get in a pipe or a file that feeds read, a get whose name is a variable, a get on the next line of a substitution, and a backtick substitution of get in a code block.

Evidence

Gate on 1e2057d5: mise run clean && mise run all exits 0 in 669 s on this host. luacheck: 200 files, 0 warnings, 0 errors. Busted: 1908 successes, 0 failures, 0 errors, 36 pending. mise run format-check: 458 files, 0 changed. proof-static.sh on the real files, run on its own: CASES 5, exit 0.

Self-tests in doc-get-exact: 73 lines must fail and 17 lines must pass, plus the recipe line itself. The 73 are the 35 lines of the previous version, 9 for quoted get and -z words, 13 quote, backslash, $'..' and case-pattern shapes, 1 for a get of a name that holds no path before a get of a path on one line, 9 multi-line, tab and unit-marker samples, and 6 changed copies of the recipe (a comment added before or after it, a command added, -d '' removed, IFS= removed, -z removed). All hold on the real files.

Scan. The case reads 13 docs (SKILL.md and 12 features/*.md) and finds 0 hits. I also ran the rule on every line of the scripts. The hits are expected: the get() helper of each of the five journeys (3 lines each), 13 lines of proof-agent-tty-resolution.sh (tests of get itself), the self-test samples of proof-static.sh, and 3 usage lines of vcn.sh.

Mutants. Each row changes one rule of the proof (b01 to b25) or one doc (d1 to d13) and runs proof-static.sh. All 38 runs fail at doc-get-exact. The three cases before it print ok.

Rule Mutants What fails
Pass list b01 adds OUT, b02 drops PORT, b03 drops the CLIENT_PID rule, b04 drops the lowercase rule, b05 drops the <NAME> brackets b01 misses OUT=$($V get OUT). b02 to b05 flag a line that reads no path
Word handling b06 takes the whole token, b07 does not skip -z, b08 counts an empty word as a miss, b09 has no word-start guard false positives on $($V get PORT), get -z CLIENT_PID_a, get -z and forget OUT
Word loop b21 returns and b22 breaks at a pass word both miss the line with a get of PORT before a get of OUT
Byte equality b10 flags nothing, b11 accepts a line that contains getz, b12 accepts a prefix, b13 never accepts b10 misses. b11 and b12 pass a changed recipe. b13 flags the recipe
Quote stripping b14 keeps single quotes, b15 keeps double quotes, b16 keeps backslashes misses 'get' OUT, "get" OUT and ge\t OUT
Line join b17 no join, b18 joins with a space, b19 no flush at the end, b20 drops a pending line at a unit end misses the two-line, g plus et and trailing-backslash samples
Doc scan b23 drops SKILL.md, b24 drops features/*.md, b25 reads only the units that source a run.env the count check: 12 docs, 1 doc and 0 recipe lines are not every doc with the recipe in two
Docs d1 and d13 change the recipe copy in SKILL.md, d2 adds a substitution, d3 the old allowed form, d4 a quoted 'get', d5 a nested substitution, d6 two joined lines, d7 a get on the next line of a substitution, d8 a group that feeds read, d9 read -d '' without IFS=, d10 get $'OUT', d11 a backtick substitution in a code block, d12 a pipe each fails with the doc and the line

Six doc breaks (quoted 'get', joined lines, get on the next line of a substitution, $'OUT', backticks in a code block, a pipe) ran green on the previous head 55e68244. The same breaks (d4, d6, d7, d10, d11, d12) fail on this head, so this check closes shapes that the previous version missed or listed as limits.

Not changed

Nothing outside the skill changes.


Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh

…et-exact

The doc-get-exact case of proof-static.sh stopped a command substitution at its first ")" and read a "<(" only when one simple
pattern matched. A get of OUT or WORK that cut the newline at the end of the path passed in these shapes: a nested
substitution before the get, a "<(" with a cd or a second command, a read that a group or a loop wraps, and a read with no IFS=.

path_reads is now a small awk scanner that tracks quotes and a stack of open "$(", "<(" and "(" groups. It flags a get of a path
name in any open "$(" and in every "<(" except IFS= read -r -d '' NAME < <(COMMAND get -z NAME). The header, the ok line and the
SKILL.md bullet say what is checked and what is not.

_Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-13T11:39:06.308834Z 1e2057d Manual request
🔒 Security Review ✅ Completed 2026-11-13T11:38:10.137576Z 1e2057d Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 55e6824434

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@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: 55e6824434

ℹ️ 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".

The awk scanner of doc-get-exact parsed quotes and parentheses, and each review round found one more shape it missed
(a nested substitution, a "<(" with two commands, a read in a group, a quoted 'get' word). No doc or script has any of them.

The check now reads text and parses no shell. It joins a line that ends in a backslash to the next one, deletes every quote mark
and backslash, and finds each "get [-z] WORD". WORD passes when it is PORT, SID, NVIM_PID, TUI_PID, NVIM_08, CLIENT_PID*, a
lowercase word or the placeholder NAME or <NAME>. For any other WORD the whole line must equal the getz() line of
features/README.md byte for byte, so the copy in SKILL.md must equal it too. The header, the ok line and the SKILL.md bullet say
what is checked. They drop the limits that this check makes wrong and keep one: a get or a name that a shell expansion builds.

_Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
_Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
@ThomasK33 ThomasK33 changed the title fix(skills): follow nested parentheses and every <(...) form in doc-get-exact fix(skills): make doc-get-exact an exact allow-list of the getz recipe Nov 13, 2026
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: 64569bbbdb

ℹ️ 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".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 64569bbbdb

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…get-exact

A mutant of the word loop that stops at a pass word survived every self-test, because no sample had a get of a name that holds no
path before a get of a path on one line. The sample PORT=$("$V" get PORT); OUT=$("$V" get OUT) must be flagged now.

The scan of the real docs also stayed green when the loop dropped SKILL.md, dropped features/*.md or read only the units that
source a run.env. The case now counts the docs it reads against every .md file of the skill and counts the recipe lines it sees,
which must be at least two (SKILL.md and features/README.md).

_Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
_Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 1e2057d548

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@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: 1e2057d548

ℹ️ 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".

@ThomasK33

Copy link
Copy Markdown
Member Author

Maintainer decision (ThomasK33, 2026-11-13, in the agent chat): option B for this PR.

Waiver recorded before the merge: merge 1e2057d5 now with the Codex finding on this head declined. The verifier ran the split form and bash ends the command at the newline, so vcn.sh prints "get needs one name" and no newline is cut. The three small doc-check items go into #603 before the merge: the doc count counts loop passes, not what the scan reads. The rule that any other .md file fails the check is not written in the script or the skill. Two comments still use the wider wording that the body narrows.


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high

@ThomasK33
ThomasK33 added this pull request to the merge queue Nov 13, 2026
Merged via the queue into main with commit 08e7aa6 Nov 13, 2026
8 checks passed
@ThomasK33
ThomasK33 deleted the fix/566-doc-get-exact-gaps branch November 13, 2026 12:47
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.

1 participant