Skip to content

docs(scripts): name the surfaces the skill-count gate cannot see - #3050

Merged
kyle-sexton merged 2 commits into
mainfrom
claude/count-gate-surface-scope
Aug 20, 2026
Merged

docs(scripts): name the surfaces the skill-count gate cannot see#3050
kyle-sexton merged 2 commits into
mainfrom
claude/count-gate-surface-scope

Conversation

@kyle-sexton

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

Copy link
Copy Markdown
Contributor

No linked issue

Summary

The skill-count gate's header states which class of stale count it holds
(repository-derivable) and which it does not (document-internal), then asserts
"Nothing else was scoped out" — which reads as completeness the gate does not
have. It never named the surface boundary: the gate reads files in the repo,
so a class-A count written anywhere else is equally derivable and equally
unguarded, because nothing here can see it.

Fix

Adds a SURFACES, not just classes paragraph to the header and qualifies the
"nothing else" sentence to the files the gate actually reads.

The omission has a measured cost, and it is this gate's own introduction that
paid it: PR #3034 carried a hand-written version-and-assertion table in its
body, and that table went stale within the hour when a merge with main
renumbered two plugins. Those counts were class A — plainly derivable from the
tree — and invisible to the gate, because a PR description is not a file in the
repo. A reviewer caught it, which is precisely the control this gate exists to
replace.

So the header now says: prose describing the CURRENT repo that lives on GitHub
rather than on disk — a PR or issue body, a wiki page — derives its counts at
write time or drifts, and a green gate must not be read as covering it.

A second commit narrows that list. The first draft included "a release note",
which contradicted a decision stated 40 lines above it in the same header:
CHANGELOG.md is deliberately not scanned, because a dated entry describes the
release it shipped with and rewriting it to match today's tree falsifies history
rather than fixing a claim. A GitHub release note is that same kind of record.
The header now states that carve-out explicitly as travelling off-disk with the
count, rather than leaving it implied — off-disk surfaces get the same
current-state test as the scanned ones, not a broader one. Raised by an
automated reviewer; the contradiction was real.

Comment-only change throughout. No behavior, no new surface scanned, no
token-list edit.

Verification

  • shellcheck at the repo's own .shellcheckrc (not a laxer severity) — clean
  • shfmt -d — clean
  • scripts/check-skill-count-claims.test.sh — all assertions passed
  • scripts/check-skill-count-claims.sh --check — every claim matches the tree
  • typos, editorconfig-checker — clean
  • scripts/check-changelog-parity.sh --check and --check-bump origin/main
    no version bump required; scripts/ is not a versioned plugin, and this PR
    touches nothing under plugins/
  • Tracker references in the added comment use the bare (#N) form the
    comment-hygiene lane requires, checked before pushing because that lane is
    an external composite action this repo does not vendor and so cannot be run
    locally

All of the above were re-run after the second commit, not just the first.

This body deliberately carries no hand-written counts of assertions, claims, or
versions — the exact failure mode the change documents. The numbers are in the
command output above, where they are regenerated rather than transcribed.

This body was itself corrected once. After the second commit it still
described the superseded wording, which is the same off-disk drift the change
documents, occurring in this PR's own description. Fixed here rather than left
as a demonstration.

Related

Refs #3034 — the PR that introduced this gate, and whose own body supplied the
stale-count evidence cited above.

The gate's header already states which CLASS of stale count it holds
(repository-derivable) and which it does not (document-internal). It said
nothing about SURFACES, and then asserted "Nothing else was scoped out" --
which reads as completeness the gate does not have.

The omission has a measured cost. The PR that introduced this gate (#3034)
carried a hand-written version-and-assertion table in its own body, and that
table went stale within the hour when a merge with main renumbered two
plugins. Those counts are class A -- plainly derivable from the tree -- and
the gate could not see them, because a PR description is not a file in the
repo. A reviewer caught it, which is precisely the control this gate exists
to replace.

So the header now names the surface boundary explicitly and the
"nothing else" sentence is qualified to the files the gate actually reads.
Prose about this repo that lives on GitHub rather than on disk -- a PR or
issue body, a release note, a wiki page -- derives its counts at write time
or drifts, and a green gate must not be read as covering it.

Comment-only change; no behavior, no new surface scanned. Verified:
shellcheck at the repo rcfile, shfmt, the 22 assertions, --check at 24 claims
with 2 exempted, typos, editorconfig, and no version bump required
(scripts/ is not a plugin, confirmed by changelog-parity --check-bump).
Tracker references use the bare (#N) form the comment-hygiene lane requires,
checked before pushing since that lane cannot be run locally.

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

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

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


Code review complete ✅

  • Gather context (PR diff, CLAUDE.md, skill instructions)
  • Apply skip gate
  • Review changed file against criteria
  • Post findings

Summary

This is a comment-only change to the header of scripts/check-skill-count-claims.sh (+15/-4). No executable lines are touched — verified via git diff HEAD~1 HEAD -- scripts/check-skill-count-claims.sh, which shows the diff is confined to the #-prefixed header block (lines 63–77).

The new SURFACES, not just classes paragraph and the qualified "nothing else was scoped out" sentence are factually accurate: I checked the rest of the file and confirmed the gate's actual read surface — README.md, .claude-plugin/plugin.json, and skills/*/SKILL.md per plugin (see L308-L309) — which matches the "files in the repo" framing the new prose asserts. The narrative about PR #3034's stale table is consistent with that PR's own history.

No correctness, maintainability, or test-coverage issues found. Nothing here rises to the high-signal bar for this lane (no behavior change, no new surface, no logic touched) — this docs clarification does what it says.

No inline findings to post.

@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

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


Security review

  • Gather context (PR diff, file contents)
  • Apply skip gate
  • Post result

Skip gate: no security-relevant surface.

The entire diff in scripts/check-skill-count-claims.sh is confined to a comment block (the header docstring, lines 63–77) — a documentation addition explaining the surface scope of the gate, plus a one-word qualifier (WITHIN the files it reads) on an adjacent sentence. No executable code, control flow, file-read logic, permission handling, or Actions workflow config changes.

No security review findings.

Branch: claude/count-gate-surface-scope

@kyle-sexton
kyle-sexton enabled auto-merge (squash) August 20, 2026 05:52
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

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

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

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

@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: 62eb9f3126

ℹ️ 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 scripts/check-skill-count-claims.sh Outdated
…ning

The surfaces paragraph listed "a release note" among off-disk prose whose
counts drift, which contradicted a decision stated 40 lines above it in the
same header: CHANGELOG.md is deliberately not scanned, because a dated entry
describes the release it shipped with and rewriting it to match today's tree
falsifies history rather than fixing a claim.

A GitHub release note is that same kind of record. Its denominator is the tree
at release time, so later divergence is valid history, not stale prose. As
written the paragraph could prompt a maintainer to "fix" an accurate historical
claim -- the opposite of what the changelog carve-out protects.

So the list now names only surfaces that purport to describe the CURRENT repo
(a PR or issue body, a wiki page), and the dated-record carve-out is stated
explicitly as travelling off-disk with the count. Off-disk surfaces get the
same current-state test as the scanned ones, not a broader one.

Raised by an automated reviewer on the PR; the contradiction is real and the
reviewer's reading of the carve-out matches the header's own words, which I
re-read to confirm before changing anything.

Comment-only, as before. Re-verified: shellcheck at the repo rcfile, shfmt,
the assertion suite, --check against the tree, typos, editorconfig. Line
lengths match the file's existing style, and tracker references stay in the
bare (#N) form the comment-hygiene lane requires.

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

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

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


Code review — /review:code-review

  • Gather context (PR diff since last-reviewed head, CLAUDE.md/REVIEW.md, file contents)
  • Apply skip gate
  • Review changes against code-review criteria
  • Post findings

Summary

This lane last reviewed 62eb9f3 (comment above). The new commit e3a24cc — "exempt dated release notes from the off-disk drift warning" — adds one more comment paragraph to the same header block in scripts/check-skill-count-claims.sh. Full diff since the last review: 62eb9f3...e3a24cc.

Still comment-only — verified by reading the whole file: every changed line is #-prefixed (L63–L84), no executable line moved. The new paragraph responds to the Codex P2 comment ("exclude historical release notes from the drift warning") by stating that the CHANGELOG.md dated-record carve-out "travels off-disk with the count," so a GitHub release note's later divergence from the tree is history, not drift. That's consistent with the script's actual behavior: it only ever reads files under plugins/*/ (L310-L318), so a GitHub release note was already outside what this gate can flag — the new prose documents that boundary rather than changing any matching logic, which is the right fix for a comment-only false-positive concern.

I re-read the full file (not just the diff) to check the new prose against the implementation:

  • "this gate reads FILES IN THE REPO" (L63) matches the files=(...) construction at L315-L318 (README.md, plugin.json, SKILL.md per plugin).
  • "Nothing else was scoped out WITHIN the files it reads" (L80) is a correctly narrowed claim — the file-level exclusions (CHANGELOG.md) and surface-level exclusion (off-repo prose) are now both stated, so the sentence no longer overclaims completeness the gate doesn't have.

No correctness or maintainability issues found. Nothing here rises to this lane's high-signal bar (no behavior change, no new surface scanned, no logic touched) — the docs update does what the PR describes.

Verification note: the PR body's shellcheck/shfmt/test-suite output is author-claimed; I did not have tool access to re-run those in this session, so I did not re-verify them independently.

No inline findings to post.

Branch: claude/count-gate-surface-scope

@claude

claude Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

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


Security review

  • Gather context (PR diff since last reviewed head, file contents)
  • Apply skip gate
  • Post result

Skip gate: no security-relevant surface.

Reviewed the full diff on scripts/check-skill-count-claims.sh, including the new commit since the last reviewed head (62eb9f3e3a24cc). That commit adds one more paragraph to the same header docstring (lines 73–78), addressing the Codex bot's suggestion to exempt dated GitHub release notes from the off-disk drift warning. Every added/changed line is #-prefixed prose inside the header block — no executable code, control flow, file-read logic, permission handling, or Actions workflow config is touched.

No security review findings.

Branch: claude/count-gate-surface-scope

@kyle-sexton
kyle-sexton merged commit d1c0935 into main Aug 20, 2026
51 checks passed
@kyle-sexton
kyle-sexton deleted the claude/count-gate-surface-scope branch August 20, 2026 06:03
kyle-sexton added a commit that referenced this pull request Aug 20, 2026
…aky (#3057)

No linked issue

## Summary

`scripts/check-shell-portability.sh --all` was treated as
environmentally
unreliable across four separate attempts, each written off as a command
timeout
or a container restart. That diagnosis was wrong, and it was the
self-perpetuating kind: "it got unlucky" invites retrying the same run,
which
fails the same way and yields no new information. Nothing in the repo
recorded
the real reason.

## Fix

Adds a `COST of --all` block to the script's header.

The cause is structural, not environmental. Per-file cost grows
superlinearly
with file length, so a handful of the longest files dominate the whole
sweep
while the great majority cost almost nothing. It cannot fit a 600s
command
timeout on a tree this size, so it must be detached rather than retried.

Two traps are recorded alongside it, because neither is discoverable
from a
failing run:

- `pgrep -f 'check-shell-portability'` matches the **waiting** shell's
own
command line, so a waiter built on it can never see its condition clear.
Two
waiters were lost to this before it was spotted. Waiting on the pid is
correct.
- A per-file sweep keyed only on the timeout status silently passes over
any file
exiting 1, so its silence reads as a clean audit when nothing was
actually
  checked.

The block also states why CI runs the changed-file mode and never
`--all` — true
of `ci.yml`'s `shell-portability-lint` already, but written down
nowhere.

**On the numbers:** they are stated as a dated observation rather than a
standing
claim, and no file list or file count is hardcoded. The exact figures
move with
the corpus; the shape does not. That is deliberately the same
surfaces-and-derivability discipline the sibling skill-count gate's
header
gained in #3050 — hardcoding "seven files over N lines" here would have
created
precisely the drift that gate exists to catch.

Comment-only. No behavior, no token-list edit, no new construct class.

## Verification

- `shellcheck` at the repo's own `.shellcheckrc` — clean (re-run after
rebase)
- `shfmt -d` — clean (re-run after rebase)
- `scripts/check-shell-portability.test.sh`, which CI runs ahead of the
gate —
  `PASS=333 FAIL=0`
- `typos`, `editorconfig-checker` — clean
- `scripts/check-changelog-parity.sh --check` — no version bump
required;
`scripts/` is not a versioned plugin and nothing under `plugins/` is
touched
- Tracker references use the bare `(#N)` form the `comment-hygiene` lane
requires, checked before pushing because that lane is an external
composite
  action this repo does not vendor and cannot be run locally
- Rebased onto current `main` rather than left on the base it was
written
  against, so `stale-base-overlap-gate` sees a current merge base

The claim this documents was itself measured end to end: `--all`
completed at
exit 0 with `No unexcused GNU-only constructs in 1329 shell file(s)`,
which is
the verification that had been outstanding since #3034.

## Related

Refs #3050 — the sibling gate's surfaces boundary, whose derivability
discipline
this block deliberately follows.
Refs #3034 — the PR whose outstanding `--all` verification this work
completed.


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

---------

Co-authored-by: Claude <noreply@anthropic.com>
kyle-sexton added a commit that referenced this pull request Aug 20, 2026
…erence form (#3061)

No linked issue

## Summary

Two findings from a babysit session, both of which cost real time and
neither of which was written down anywhere. They are unrelated in
mechanism but share a cause: an operator hits them only in CI, with no
local signal.

## Fix

**1. `stuck-checks.md` — checks that never schedule.** That file covered
checks which are *present and never settle*, detected under `UNSTABLE`.
It did not cover the opposite failure with the same surface complaint. A
conflicted PR has no computable merge ref, so `pull_request` workflows
are never created — **absent** rather than pending or failing, and
therefore invisible to `checks.stuck`, which can only classify records
that exist.

What makes it actively misleading is that `pull_request_target` lanes
run against the base and still pass. The PR shows a short all-green list
with no failures anywhere — reading as "passing" or "not started yet" —
while most gates are simply missing. The remedy is merge-or-rebase, not
a CI investigation; diagnosing it as a trigger or App-token problem
spends time on an uninvolved mechanism, which has already happened here
once.

The section **names** the two `pull_request_target` lanes rather than
counting the split, and says to derive the split from the `on:` blocks.
A count in prose goes stale the next time a lane is added — the exact
defect the two recent skill-count-gate changes addressed.

**2. New `docs/conventions/tracker-reference-form`.** What a code
comment may say about an issue. It needs a written home precisely
because the `comment-hygiene` lane **cannot be run locally**: the action
is not vendored, so a violation is discoverable only after pushing.

Written from the action source at the SHA `ci.yml` pins, not from memory
— and that mattered. The belief carried into this change was that `PR
#N` and `owner/repo#N` are simply rejected, but the tree contains
*passing* instances of both. Reading the action resolved the
contradiction: the allowlist is an extension set that excludes Markdown
and YAML, so those instances are out of scope rather than tolerated. The
doc states that scoping explicitly, so the counterexamples don't later
read as evidence the rule is lax.

Documentation only; no executable surface.

## Verification

- `check-changelog-parity.sh --check`, `--check-bump origin/main`,
`--check-order` — all green; `source-control` bumped with a matching
entry
- `check-skill-count-claims.sh --check` — every claim matches the tree
- `check-shell-portability.sh --paths` on the changed skill file — clean
- `markdownlint-cli2` on both docs — 0 issues
- `typos`, `editorconfig-checker`, and `jq` on the manifest — clean

The one rule this PR documents that it cannot verify locally is
`comment-hygiene` itself; the added comment text was grepped for each
rejected shape before pushing, and both changed docs are Markdown, which
the policy does not scan.

## Related

Refs #3050 and #3057 — the two changes whose sessions produced these
findings. This PR closes out the last of them, so nothing from that work
remains unrecorded.


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

---------

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.

2 participants