chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358) - #7407

Merged
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root
Sep 2, 2026
Merged

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358)#7407
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#7358

SCAN_ROOT was one string, skills. #7251 moved the two contributor-only guides into .claude/skills/objectui-contributor/ and 55 of 93 stated paths left the checked surface in one commit with nothing turning red — the gate simply stopped looking. This makes it a list, ['skills', '.claude/skills'], clears the red the wider surface brings, and restores the pins #7251 had to weaken.

Head: 471589f. Every number below was measured on that commit unless it names another.

The measurement the docblock asks for

The script's own header requires this: "Widen it on purpose, with the measurement re-run, not as a rider." Re-run, before and after, with node scripts/check-skills-paths.mjs and --list:

before (0614b6df1)after (471589f)
files scanned1620
stated paths checked2788
resolve2787 (+1 baselined)
pattern tokens excluded14
console-development.md— (unscanned)49
no-touch-zones.md— (unscanned)6
objectui-contributor/SKILL.md— (unscanned)5
verify/SKILL.md— (unscanned)1

Per root, which the gate now prints on every green run:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
skills/ — 27/27 resolve across 16 file(s)
.claude/skills/ — 60/61 resolve across 4 file(s)

The card's headline number is 93, and the honest re-measurement is 88. Both the card's own figures cannot hold at once: it records 18 to 16 files and 93 to 27 assertions, a loss of 66, while the two moved guides carry 55 between them. The missing 11 are assertions #7251 also removed when it rewrote the published guides it kept. So widening restores 61 assertions under the new root — the 55 that left, plus 6 in two files that were never under skills at all — for 88, not 93. The PR title carries the measured number rather than the card's.

The red the widened surface brought: exactly one, and it is not rot

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:381 — apps/console/src/context/

Coordinates corrected at source: zero. Not for want of looking — the other 60 in the two moved guides all resolved on arrival. They were maintained by hand through #3713 and #3730 and had not yet gone stale, which is the case for gating them, not against.

The one non-resolver is the deliberate negative sentence in the Key contexts section, which exists to correct the recurring wrong guess that the five contexts live in the console app:

All five live in @object-ui/app-shell; there is no apps/console/src/context/ directory at all.

Evidence it is a true statement and not a stale coordinate:

$ ls -d apps/console/src/context/
ls: cannot access 'apps/console/src/context/': No such file or directory

This is the entry #7251 was forced to delete. The baseline is keyed by guide file, so the move retired it through the ratchet's second direction — the gate demanded it in its own words, "1 baseline entry the scan never met ... Delete the entry". Widening the root makes the scan meet the same sentence again, so the entry returns under the new key, granted against the gate's own prescription, quoted verbatim in the entry's reason:

If the path is deliberately named as NOT existing — a sentence whose whole point is "there is no such directory" — add it to scripts/skills-path-baseline.json with a reason, and expect the gate to go red again the day that path appears on disk.

Dated objectui#3735 (re-keyed under the moved guide path by objectui#7358, 2026-09-02). It is the only entry in the file, as it was before the move: one exemption for 88 assertions.

The lesson the miss itself taught: emptiness is judged per root

The empty-verdict trap in main() used to ask whether the whole surface read zero. That question could not have caught #7251: 16 files and 27 assertions still under skills is a healthy-looking total while a second tree of 55 sits unscanned. It now asks per root, and a root that judges nothing is named and red — either a move nobody threaded through, or a root that should be deleted from SCAN_ROOTS, and both are decisions rather than a pass. scan() returns a perRoot row per root to make that answerable at all.

The pins #7251 left to be reversed

All three were named in advance by the comments beside them.

  1. The checked floor, 20 back to 50. With the arithmetic restored, as Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 left it: 88 checked today, of which the two moved guides carry 55 (49 + 6). If .claude/skills ever falls out of SCAN_ROOTS the reading drops to 33 and the floor is red — the assertion Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 could not make.
  2. A per-root floor beside it, because the one above is a total: 27 under skills alone clears a floor of 20, which is exactly how the 55 left unnoticed.
  3. The docs(skills): console-development.md 指路的 7 个 console 页面里 5 个不存在,并整节教用已删除的 MetadataManagerPage #3713 / docs(skills): console-development.md 的 Key contexts / Key hooks / UnifiedSidebar 三处把 13 个真实符号指到 apps/console 下已不存在的目录 #3730 block asserts result.missing through scan(repoRoot) again, on the guide's current path, and carries its exemption pin again.
  4. The "is NOT covered by the gate today" assertion is DELETED, as it was written to be. Its replacement is the inverse claim rather than nothing: inGuide(result.missing) is empty both when the guide is clean and when nothing scans it, so the guide's own stated-path density is now tied to its root's checked count and the two stop being confusable.

Five fixture cases cover the two-root surface: the declared roots, a dead path found under .claude/skills, the perRoot breakdown, a root that reads nothing showing as a zero row rather than an absence, and a baseline entry keyed under the second root. 31 to 37 cases.

Ablation — the widened root really is checked

One coordinate in the newly covered guide, mutated on the committed tree and restored, both legs proved on disk. No build or dist is involved: the gate is a node script reading markdown, so there is no artifact between the edit and the reading.

Mutation landed (packages/app-shell/src/layout/UnifiedSidebar.tsx to ...SidebarGONE.tsx, injected-token count 1, original-token count 0, on-disk hash 0cd9feb against HEAD blob 27e5f46), gate exit 1:

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:268 — packages/app-shell/src/layout/UnifiedSidebarGONE.tsx

Restore proved, not assumed — git checkout HEAD -- ABSOLUTE_PATH under a trap ... EXIT INT TERM, then on-disk hash 27e5f46 equal to the HEAD blob, git diff HEAD --name-only empty, injected-token count 0, original-token count 1. Gate exit 0:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).

Premises that did not hold

  • premise_false: the gate has a --self-test mode — the dispatch expected node scripts/check-skills-paths.mjs to run "self-test + live". It has no such flag; grep -n 'self-test\|selfTest' over the script returns nothing, and its only mode besides the live run is --list. The self-test is the vitest suite, and that is where the two-root cases were added.
  • premise_false: 93 stated paths come back under the gate — 88, for the arithmetic given above.
  • premise_false: the queue guard returns exit 3 GOVERNED — it does so when .claude/skills/** is in the diff, and nothing there needed correcting, so the diff is three files under scripts/. Its own verdict: ✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched. Draft and human-merged either way, as the dispatch asks.
  • premise_false: the widened surface brings stale coordinates to correct at source — 60 of 61 resolve; the single non-resolver is the deliberate negative sentence.

Gates, at 471589f

Exit codes captured by redirecting first (cmd > file 2>&1; EXIT=$?), never read across a pipe. Each row quotes the gate's own verdict line.

gateexitits own verdict
node scripts/check-skills-paths.mjs0✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
pnpm exec vitest run scripts/__tests__/check-skills-paths.test.ts0Test Files 1 passed (1) / Tests 37 passed (37)
pnpm type-check:scripts0tsc -p tsconfig.scripts.json, silent
node scripts/check-doc-links.mjs0Links are valid across 17 scan roots.
node scripts/check-control-bytes.mjs0✅ check-control-bytes: OK (scanned 6103 tracked text file(s); skipped 85 binary).
node scripts/check-shell-escape-residue.mjs0✅ check-shell-escape-residue: OK (4/4 root(s) resolved ...)
node scripts/check-changeset-presence.mjs0✅ No source or published contract of a released package changed in this range, so no changeset is owed.
node scripts/check-governed-queue-guard.mjs --self-test0OK check-governed-queue-guard self-test: 132 cases pass
node scripts/check-governed-queue-guard.mjs --test (3 changed paths)0✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched.
pnpm exec eslint (changed files)01 file linted, 0 errors, 0 warnings

No changeset, on the gate's own verdict, not on judgement: 3 file(s) changed, 0 of them published source of a package the release covers. No skip-changeset label, which this repo does not use as a mechanism, and no empty-frontmatter changeset either — the gate says none is owed.

type-check:scripts genuinely covers the edited test file, rather than excluding it and reporting a clean run about other files: tsc -p tsconfig.scripts.json --listFiles lists both scripts/check-skills-paths.mjs and scripts/__tests__/check-skills-paths.test.ts, one occurrence each.

eslint was narrowed, and the narrowing is measured rather than assumed. Population, read from eslint.config.js and not guessed: every rule block is scoped files: ['**/*.{ts,tsx}'], and no block declares an .mjs, .cjs or .json glob — so of the three changed files only the .ts test is inside the linted population at all. File count read from --format json: 1 result object, errorCount 0, warningCount 0. Invariance: the config declares neither parserOptions.project nor projectService, so linting is not type-aware and nothing in this diff can move the verdict on a file it did not touch. The full-repo pnpm lint is CI's run.

Scope

Three files, all under scripts/. Nothing under skills/objectui/**, nothing under packages/**, no other gate touched, and — because nothing needed correcting — nothing under .claude/skills/** either.

Two findings of the same class were filed unassigned rather than ridden in here; neither is addressed by this PR:

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…#7358)
SCAN_ROOT was one string, `skills`. objectui#7251 moved the two
contributor-only guides into `.claude/skills/objectui-contributor/` and
55 of 93 stated paths, 59%, left the checked surface in one commit with
nothing turning red — the gate simply stopped looking.
Make it a list, `['skills', '.claude/skills']`, and thread it through
`scan()` (a `perRoot` breakdown beside the totals), the OK line, the
`--list` footer and the empty-verdict trap.
That trap is now judged PER ROOT, which is the lesson the miss itself
taught: summed over roots, "one root reads nothing" is invisible — a
whole-surface floor stayed comfortably green on the 16 files still under
`skills` while a second tree of 55 assertions sat unscanned.
Measured, before -> after: 16 -> 20 files, 27 -> 88 stated paths checked
(console-development.md 49, no-touch-zones.md 6, objectui-contributor's
SKILL.md 5, verify/SKILL.md 1). The widened surface brings exactly one
red, cleared in the next commit.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…de (objectui#7358)
The widened surface brought exactly one red, and it is not rot: the
deliberate negative sentence in `console-development.md`'s Key contexts
section, "there is no `apps/console/src/context/` directory at all",
written to correct the recurring wrong guess that the five contexts live
in the console app. The other 60 coordinates in the two moved guides all
resolved on arrival.
This is the entry objectui#7251 was forced to delete: the baseline is
keyed by guide file, so the move retired it through the ratchet's second
direction ("1 baseline entry the scan never met ... Delete the entry").
Widening the root makes the scan meet the same sentence again, so the
entry comes back under the new key.
Granted against the gate's own prescription, quoted in the entry's
reason: "If the path is deliberately named as NOT existing -- a sentence
whose whole point is 'there is no such directory' -- add it to
scripts/skills-path-baseline.json with a reason, and expect the gate to
go red again the day that path appears on disk."
Nothing was corrected at source because nothing was stale; `ls` confirms
`apps/console/src/context/` still does not exist, which is what the
sentence says.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ken (objectui#7358)
Three restorations and one deletion, all of them named in advance by the
comments objectui#7251 left beside the weakened forms.
The `checked` floor goes back to 50, the value it dropped to 20 to
survive the move, with the arithmetic restored: 88 checked today, of
which the two moved guides carry 55. If `.claude/skills` ever falls out
of SCAN_ROOTS the reading drops to 33 and this floor is red.
Beside it, a per-root floor. A total cannot see one root go quiet: 27
under `skills` alone clears a floor of 20, which is exactly how the 55
left unnoticed.
The objectui#3713 / #3730 block asserts `result.missing` through
`scan(repoRoot)` again, on the guide's current path, and carries its
exemption pin again. The "is NOT covered by the gate today" assertion is
DELETED, as it was written to be. Its replacement is the inverse claim
rather than nothing, because `inGuide(result.missing)` is empty both
when the guide is clean and when nothing scans it: the guide's own
density is now tied to its root's checked count, so the two are no
longer confusable.
Five fixture cases cover the two-root surface: the declared roots, a
dead path found under `.claude/skills`, the `perRoot` breakdown, a root
that reads nothing showing as a zero row, and a baseline entry keyed to
a file under the second root.
31 -> 37 test cases, all passing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-litantClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (skills lane seat, session session_01LraLgQVGq8egUwfYZpbYt1): flipped ready and auto-merge enabled at 18:5xZ under landing-operations B — in-seat ACCEPT on #7358 (comment 5514343000), every check on 471589f green, and the repo's own guard reads the change set as NOT GOVERNED (three scripts/ files, nothing under skills/** or .claude/**). The base has moved since the branch was cut; the merge carries it. No human merge is owed on this face.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-skills-paths lost 55 of its 93 assertions when the contributor guides moved out of skills/ — widen SCAN_ROOT to cover .claude/skills

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358) - #7407

Merged
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root
Sep 2, 2026
Merged

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358)#7407
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#7358

SCAN_ROOT was one string, skills. #7251 moved the two contributor-only guides into .claude/skills/objectui-contributor/ and 55 of 93 stated paths left the checked surface in one commit with nothing turning red — the gate simply stopped looking. This makes it a list, ['skills', '.claude/skills'], clears the red the wider surface brings, and restores the pins #7251 had to weaken.

Head: 471589f. Every number below was measured on that commit unless it names another.

The measurement the docblock asks for

The script's own header requires this: "Widen it on purpose, with the measurement re-run, not as a rider." Re-run, before and after, with node scripts/check-skills-paths.mjs and --list:

before (0614b6df1)after (471589f)
files scanned1620
stated paths checked2788
resolve2787 (+1 baselined)
pattern tokens excluded14
console-development.md— (unscanned)49
no-touch-zones.md— (unscanned)6
objectui-contributor/SKILL.md— (unscanned)5
verify/SKILL.md— (unscanned)1

Per root, which the gate now prints on every green run:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
skills/ — 27/27 resolve across 16 file(s)
.claude/skills/ — 60/61 resolve across 4 file(s)

The card's headline number is 93, and the honest re-measurement is 88. Both the card's own figures cannot hold at once: it records 18 to 16 files and 93 to 27 assertions, a loss of 66, while the two moved guides carry 55 between them. The missing 11 are assertions #7251 also removed when it rewrote the published guides it kept. So widening restores 61 assertions under the new root — the 55 that left, plus 6 in two files that were never under skills at all — for 88, not 93. The PR title carries the measured number rather than the card's.

The red the widened surface brought: exactly one, and it is not rot

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:381 — apps/console/src/context/

Coordinates corrected at source: zero. Not for want of looking — the other 60 in the two moved guides all resolved on arrival. They were maintained by hand through #3713 and #3730 and had not yet gone stale, which is the case for gating them, not against.

The one non-resolver is the deliberate negative sentence in the Key contexts section, which exists to correct the recurring wrong guess that the five contexts live in the console app:

All five live in @object-ui/app-shell; there is no apps/console/src/context/ directory at all.

Evidence it is a true statement and not a stale coordinate:

$ ls -d apps/console/src/context/
ls: cannot access 'apps/console/src/context/': No such file or directory

This is the entry #7251 was forced to delete. The baseline is keyed by guide file, so the move retired it through the ratchet's second direction — the gate demanded it in its own words, "1 baseline entry the scan never met ... Delete the entry". Widening the root makes the scan meet the same sentence again, so the entry returns under the new key, granted against the gate's own prescription, quoted verbatim in the entry's reason:

If the path is deliberately named as NOT existing — a sentence whose whole point is "there is no such directory" — add it to scripts/skills-path-baseline.json with a reason, and expect the gate to go red again the day that path appears on disk.

Dated objectui#3735 (re-keyed under the moved guide path by objectui#7358, 2026-09-02). It is the only entry in the file, as it was before the move: one exemption for 88 assertions.

The lesson the miss itself taught: emptiness is judged per root

The empty-verdict trap in main() used to ask whether the whole surface read zero. That question could not have caught #7251: 16 files and 27 assertions still under skills is a healthy-looking total while a second tree of 55 sits unscanned. It now asks per root, and a root that judges nothing is named and red — either a move nobody threaded through, or a root that should be deleted from SCAN_ROOTS, and both are decisions rather than a pass. scan() returns a perRoot row per root to make that answerable at all.

The pins #7251 left to be reversed

All three were named in advance by the comments beside them.

  1. The checked floor, 20 back to 50. With the arithmetic restored, as Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 left it: 88 checked today, of which the two moved guides carry 55 (49 + 6). If .claude/skills ever falls out of SCAN_ROOTS the reading drops to 33 and the floor is red — the assertion Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 could not make.
  2. A per-root floor beside it, because the one above is a total: 27 under skills alone clears a floor of 20, which is exactly how the 55 left unnoticed.
  3. The docs(skills): console-development.md 指路的 7 个 console 页面里 5 个不存在,并整节教用已删除的 MetadataManagerPage #3713 / docs(skills): console-development.md 的 Key contexts / Key hooks / UnifiedSidebar 三处把 13 个真实符号指到 apps/console 下已不存在的目录 #3730 block asserts result.missing through scan(repoRoot) again, on the guide's current path, and carries its exemption pin again.
  4. The "is NOT covered by the gate today" assertion is DELETED, as it was written to be. Its replacement is the inverse claim rather than nothing: inGuide(result.missing) is empty both when the guide is clean and when nothing scans it, so the guide's own stated-path density is now tied to its root's checked count and the two stop being confusable.

Five fixture cases cover the two-root surface: the declared roots, a dead path found under .claude/skills, the perRoot breakdown, a root that reads nothing showing as a zero row rather than an absence, and a baseline entry keyed under the second root. 31 to 37 cases.

Ablation — the widened root really is checked

One coordinate in the newly covered guide, mutated on the committed tree and restored, both legs proved on disk. No build or dist is involved: the gate is a node script reading markdown, so there is no artifact between the edit and the reading.

Mutation landed (packages/app-shell/src/layout/UnifiedSidebar.tsx to ...SidebarGONE.tsx, injected-token count 1, original-token count 0, on-disk hash 0cd9feb against HEAD blob 27e5f46), gate exit 1:

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:268 — packages/app-shell/src/layout/UnifiedSidebarGONE.tsx

Restore proved, not assumed — git checkout HEAD -- ABSOLUTE_PATH under a trap ... EXIT INT TERM, then on-disk hash 27e5f46 equal to the HEAD blob, git diff HEAD --name-only empty, injected-token count 0, original-token count 1. Gate exit 0:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).

Premises that did not hold

  • premise_false: the gate has a --self-test mode — the dispatch expected node scripts/check-skills-paths.mjs to run "self-test + live". It has no such flag; grep -n 'self-test\|selfTest' over the script returns nothing, and its only mode besides the live run is --list. The self-test is the vitest suite, and that is where the two-root cases were added.
  • premise_false: 93 stated paths come back under the gate — 88, for the arithmetic given above.
  • premise_false: the queue guard returns exit 3 GOVERNED — it does so when .claude/skills/** is in the diff, and nothing there needed correcting, so the diff is three files under scripts/. Its own verdict: ✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched. Draft and human-merged either way, as the dispatch asks.
  • premise_false: the widened surface brings stale coordinates to correct at source — 60 of 61 resolve; the single non-resolver is the deliberate negative sentence.

Gates, at 471589f

Exit codes captured by redirecting first (cmd > file 2>&1; EXIT=$?), never read across a pipe. Each row quotes the gate's own verdict line.

gateexitits own verdict
node scripts/check-skills-paths.mjs0✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
pnpm exec vitest run scripts/__tests__/check-skills-paths.test.ts0Test Files 1 passed (1) / Tests 37 passed (37)
pnpm type-check:scripts0tsc -p tsconfig.scripts.json, silent
node scripts/check-doc-links.mjs0Links are valid across 17 scan roots.
node scripts/check-control-bytes.mjs0✅ check-control-bytes: OK (scanned 6103 tracked text file(s); skipped 85 binary).
node scripts/check-shell-escape-residue.mjs0✅ check-shell-escape-residue: OK (4/4 root(s) resolved ...)
node scripts/check-changeset-presence.mjs0✅ No source or published contract of a released package changed in this range, so no changeset is owed.
node scripts/check-governed-queue-guard.mjs --self-test0OK check-governed-queue-guard self-test: 132 cases pass
node scripts/check-governed-queue-guard.mjs --test (3 changed paths)0✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched.
pnpm exec eslint (changed files)01 file linted, 0 errors, 0 warnings

No changeset, on the gate's own verdict, not on judgement: 3 file(s) changed, 0 of them published source of a package the release covers. No skip-changeset label, which this repo does not use as a mechanism, and no empty-frontmatter changeset either — the gate says none is owed.

type-check:scripts genuinely covers the edited test file, rather than excluding it and reporting a clean run about other files: tsc -p tsconfig.scripts.json --listFiles lists both scripts/check-skills-paths.mjs and scripts/__tests__/check-skills-paths.test.ts, one occurrence each.

eslint was narrowed, and the narrowing is measured rather than assumed. Population, read from eslint.config.js and not guessed: every rule block is scoped files: ['**/*.{ts,tsx}'], and no block declares an .mjs, .cjs or .json glob — so of the three changed files only the .ts test is inside the linted population at all. File count read from --format json: 1 result object, errorCount 0, warningCount 0. Invariance: the config declares neither parserOptions.project nor projectService, so linting is not type-aware and nothing in this diff can move the verdict on a file it did not touch. The full-repo pnpm lint is CI's run.

Scope

Three files, all under scripts/. Nothing under skills/objectui/**, nothing under packages/**, no other gate touched, and — because nothing needed correcting — nothing under .claude/skills/** either.

Two findings of the same class were filed unassigned rather than ridden in here; neither is addressed by this PR:

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…#7358)
SCAN_ROOT was one string, `skills`. objectui#7251 moved the two
contributor-only guides into `.claude/skills/objectui-contributor/` and
55 of 93 stated paths, 59%, left the checked surface in one commit with
nothing turning red — the gate simply stopped looking.
Make it a list, `['skills', '.claude/skills']`, and thread it through
`scan()` (a `perRoot` breakdown beside the totals), the OK line, the
`--list` footer and the empty-verdict trap.
That trap is now judged PER ROOT, which is the lesson the miss itself
taught: summed over roots, "one root reads nothing" is invisible — a
whole-surface floor stayed comfortably green on the 16 files still under
`skills` while a second tree of 55 assertions sat unscanned.
Measured, before -> after: 16 -> 20 files, 27 -> 88 stated paths checked
(console-development.md 49, no-touch-zones.md 6, objectui-contributor's
SKILL.md 5, verify/SKILL.md 1). The widened surface brings exactly one
red, cleared in the next commit.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…de (objectui#7358)
The widened surface brought exactly one red, and it is not rot: the
deliberate negative sentence in `console-development.md`'s Key contexts
section, "there is no `apps/console/src/context/` directory at all",
written to correct the recurring wrong guess that the five contexts live
in the console app. The other 60 coordinates in the two moved guides all
resolved on arrival.
This is the entry objectui#7251 was forced to delete: the baseline is
keyed by guide file, so the move retired it through the ratchet's second
direction ("1 baseline entry the scan never met ... Delete the entry").
Widening the root makes the scan meet the same sentence again, so the
entry comes back under the new key.
Granted against the gate's own prescription, quoted in the entry's
reason: "If the path is deliberately named as NOT existing -- a sentence
whose whole point is 'there is no such directory' -- add it to
scripts/skills-path-baseline.json with a reason, and expect the gate to
go red again the day that path appears on disk."
Nothing was corrected at source because nothing was stale; `ls` confirms
`apps/console/src/context/` still does not exist, which is what the
sentence says.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ken (objectui#7358)
Three restorations and one deletion, all of them named in advance by the
comments objectui#7251 left beside the weakened forms.
The `checked` floor goes back to 50, the value it dropped to 20 to
survive the move, with the arithmetic restored: 88 checked today, of
which the two moved guides carry 55. If `.claude/skills` ever falls out
of SCAN_ROOTS the reading drops to 33 and this floor is red.
Beside it, a per-root floor. A total cannot see one root go quiet: 27
under `skills` alone clears a floor of 20, which is exactly how the 55
left unnoticed.
The objectui#3713 / #3730 block asserts `result.missing` through
`scan(repoRoot)` again, on the guide's current path, and carries its
exemption pin again. The "is NOT covered by the gate today" assertion is
DELETED, as it was written to be. Its replacement is the inverse claim
rather than nothing, because `inGuide(result.missing)` is empty both
when the guide is clean and when nothing scans it: the guide's own
density is now tied to its root's checked count, so the two are no
longer confusable.
Five fixture cases cover the two-root surface: the declared roots, a
dead path found under `.claude/skills`, the `perRoot` breakdown, a root
that reads nothing showing as a zero row, and a baseline entry keyed to
a file under the second root.
31 -> 37 test cases, all passing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-litantClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (skills lane seat, session session_01LraLgQVGq8egUwfYZpbYt1): flipped ready and auto-merge enabled at 18:5xZ under landing-operations B — in-seat ACCEPT on #7358 (comment 5514343000), every check on 471589f green, and the repo's own guard reads the change set as NOT GOVERNED (three scripts/ files, nothing under skills/** or .claude/**). The base has moved since the branch was cut; the merge carries it. No human merge is owed on this face.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-skills-paths lost 55 of its 93 assertions when the contributor guides moved out of skills/ — widen SCAN_ROOT to cover .claude/skills

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358) - #7407

Merged
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root
Sep 2, 2026
Merged

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358)#7407
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#7358

SCAN_ROOT was one string, skills. #7251 moved the two contributor-only guides into .claude/skills/objectui-contributor/ and 55 of 93 stated paths left the checked surface in one commit with nothing turning red — the gate simply stopped looking. This makes it a list, ['skills', '.claude/skills'], clears the red the wider surface brings, and restores the pins #7251 had to weaken.

Head: 471589f. Every number below was measured on that commit unless it names another.

The measurement the docblock asks for

The script's own header requires this: "Widen it on purpose, with the measurement re-run, not as a rider." Re-run, before and after, with node scripts/check-skills-paths.mjs and --list:

before (0614b6df1)after (471589f)
files scanned1620
stated paths checked2788
resolve2787 (+1 baselined)
pattern tokens excluded14
console-development.md— (unscanned)49
no-touch-zones.md— (unscanned)6
objectui-contributor/SKILL.md— (unscanned)5
verify/SKILL.md— (unscanned)1

Per root, which the gate now prints on every green run:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
skills/ — 27/27 resolve across 16 file(s)
.claude/skills/ — 60/61 resolve across 4 file(s)

The card's headline number is 93, and the honest re-measurement is 88. Both the card's own figures cannot hold at once: it records 18 to 16 files and 93 to 27 assertions, a loss of 66, while the two moved guides carry 55 between them. The missing 11 are assertions #7251 also removed when it rewrote the published guides it kept. So widening restores 61 assertions under the new root — the 55 that left, plus 6 in two files that were never under skills at all — for 88, not 93. The PR title carries the measured number rather than the card's.

The red the widened surface brought: exactly one, and it is not rot

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:381 — apps/console/src/context/

Coordinates corrected at source: zero. Not for want of looking — the other 60 in the two moved guides all resolved on arrival. They were maintained by hand through #3713 and #3730 and had not yet gone stale, which is the case for gating them, not against.

The one non-resolver is the deliberate negative sentence in the Key contexts section, which exists to correct the recurring wrong guess that the five contexts live in the console app:

All five live in @object-ui/app-shell; there is no apps/console/src/context/ directory at all.

Evidence it is a true statement and not a stale coordinate:

$ ls -d apps/console/src/context/
ls: cannot access 'apps/console/src/context/': No such file or directory

This is the entry #7251 was forced to delete. The baseline is keyed by guide file, so the move retired it through the ratchet's second direction — the gate demanded it in its own words, "1 baseline entry the scan never met ... Delete the entry". Widening the root makes the scan meet the same sentence again, so the entry returns under the new key, granted against the gate's own prescription, quoted verbatim in the entry's reason:

If the path is deliberately named as NOT existing — a sentence whose whole point is "there is no such directory" — add it to scripts/skills-path-baseline.json with a reason, and expect the gate to go red again the day that path appears on disk.

Dated objectui#3735 (re-keyed under the moved guide path by objectui#7358, 2026-09-02). It is the only entry in the file, as it was before the move: one exemption for 88 assertions.

The lesson the miss itself taught: emptiness is judged per root

The empty-verdict trap in main() used to ask whether the whole surface read zero. That question could not have caught #7251: 16 files and 27 assertions still under skills is a healthy-looking total while a second tree of 55 sits unscanned. It now asks per root, and a root that judges nothing is named and red — either a move nobody threaded through, or a root that should be deleted from SCAN_ROOTS, and both are decisions rather than a pass. scan() returns a perRoot row per root to make that answerable at all.

The pins #7251 left to be reversed

All three were named in advance by the comments beside them.

  1. The checked floor, 20 back to 50. With the arithmetic restored, as Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 left it: 88 checked today, of which the two moved guides carry 55 (49 + 6). If .claude/skills ever falls out of SCAN_ROOTS the reading drops to 33 and the floor is red — the assertion Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 could not make.
  2. A per-root floor beside it, because the one above is a total: 27 under skills alone clears a floor of 20, which is exactly how the 55 left unnoticed.
  3. The docs(skills): console-development.md 指路的 7 个 console 页面里 5 个不存在,并整节教用已删除的 MetadataManagerPage #3713 / docs(skills): console-development.md 的 Key contexts / Key hooks / UnifiedSidebar 三处把 13 个真实符号指到 apps/console 下已不存在的目录 #3730 block asserts result.missing through scan(repoRoot) again, on the guide's current path, and carries its exemption pin again.
  4. The "is NOT covered by the gate today" assertion is DELETED, as it was written to be. Its replacement is the inverse claim rather than nothing: inGuide(result.missing) is empty both when the guide is clean and when nothing scans it, so the guide's own stated-path density is now tied to its root's checked count and the two stop being confusable.

Five fixture cases cover the two-root surface: the declared roots, a dead path found under .claude/skills, the perRoot breakdown, a root that reads nothing showing as a zero row rather than an absence, and a baseline entry keyed under the second root. 31 to 37 cases.

Ablation — the widened root really is checked

One coordinate in the newly covered guide, mutated on the committed tree and restored, both legs proved on disk. No build or dist is involved: the gate is a node script reading markdown, so there is no artifact between the edit and the reading.

Mutation landed (packages/app-shell/src/layout/UnifiedSidebar.tsx to ...SidebarGONE.tsx, injected-token count 1, original-token count 0, on-disk hash 0cd9feb against HEAD blob 27e5f46), gate exit 1:

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:268 — packages/app-shell/src/layout/UnifiedSidebarGONE.tsx

Restore proved, not assumed — git checkout HEAD -- ABSOLUTE_PATH under a trap ... EXIT INT TERM, then on-disk hash 27e5f46 equal to the HEAD blob, git diff HEAD --name-only empty, injected-token count 0, original-token count 1. Gate exit 0:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).

Premises that did not hold

  • premise_false: the gate has a --self-test mode — the dispatch expected node scripts/check-skills-paths.mjs to run "self-test + live". It has no such flag; grep -n 'self-test\|selfTest' over the script returns nothing, and its only mode besides the live run is --list. The self-test is the vitest suite, and that is where the two-root cases were added.
  • premise_false: 93 stated paths come back under the gate — 88, for the arithmetic given above.
  • premise_false: the queue guard returns exit 3 GOVERNED — it does so when .claude/skills/** is in the diff, and nothing there needed correcting, so the diff is three files under scripts/. Its own verdict: ✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched. Draft and human-merged either way, as the dispatch asks.
  • premise_false: the widened surface brings stale coordinates to correct at source — 60 of 61 resolve; the single non-resolver is the deliberate negative sentence.

Gates, at 471589f

Exit codes captured by redirecting first (cmd > file 2>&1; EXIT=$?), never read across a pipe. Each row quotes the gate's own verdict line.

gateexitits own verdict
node scripts/check-skills-paths.mjs0✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
pnpm exec vitest run scripts/__tests__/check-skills-paths.test.ts0Test Files 1 passed (1) / Tests 37 passed (37)
pnpm type-check:scripts0tsc -p tsconfig.scripts.json, silent
node scripts/check-doc-links.mjs0Links are valid across 17 scan roots.
node scripts/check-control-bytes.mjs0✅ check-control-bytes: OK (scanned 6103 tracked text file(s); skipped 85 binary).
node scripts/check-shell-escape-residue.mjs0✅ check-shell-escape-residue: OK (4/4 root(s) resolved ...)
node scripts/check-changeset-presence.mjs0✅ No source or published contract of a released package changed in this range, so no changeset is owed.
node scripts/check-governed-queue-guard.mjs --self-test0OK check-governed-queue-guard self-test: 132 cases pass
node scripts/check-governed-queue-guard.mjs --test (3 changed paths)0✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched.
pnpm exec eslint (changed files)01 file linted, 0 errors, 0 warnings

No changeset, on the gate's own verdict, not on judgement: 3 file(s) changed, 0 of them published source of a package the release covers. No skip-changeset label, which this repo does not use as a mechanism, and no empty-frontmatter changeset either — the gate says none is owed.

type-check:scripts genuinely covers the edited test file, rather than excluding it and reporting a clean run about other files: tsc -p tsconfig.scripts.json --listFiles lists both scripts/check-skills-paths.mjs and scripts/__tests__/check-skills-paths.test.ts, one occurrence each.

eslint was narrowed, and the narrowing is measured rather than assumed. Population, read from eslint.config.js and not guessed: every rule block is scoped files: ['**/*.{ts,tsx}'], and no block declares an .mjs, .cjs or .json glob — so of the three changed files only the .ts test is inside the linted population at all. File count read from --format json: 1 result object, errorCount 0, warningCount 0. Invariance: the config declares neither parserOptions.project nor projectService, so linting is not type-aware and nothing in this diff can move the verdict on a file it did not touch. The full-repo pnpm lint is CI's run.

Scope

Three files, all under scripts/. Nothing under skills/objectui/**, nothing under packages/**, no other gate touched, and — because nothing needed correcting — nothing under .claude/skills/** either.

Two findings of the same class were filed unassigned rather than ridden in here; neither is addressed by this PR:

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…#7358)
SCAN_ROOT was one string, `skills`. objectui#7251 moved the two
contributor-only guides into `.claude/skills/objectui-contributor/` and
55 of 93 stated paths, 59%, left the checked surface in one commit with
nothing turning red — the gate simply stopped looking.
Make it a list, `['skills', '.claude/skills']`, and thread it through
`scan()` (a `perRoot` breakdown beside the totals), the OK line, the
`--list` footer and the empty-verdict trap.
That trap is now judged PER ROOT, which is the lesson the miss itself
taught: summed over roots, "one root reads nothing" is invisible — a
whole-surface floor stayed comfortably green on the 16 files still under
`skills` while a second tree of 55 assertions sat unscanned.
Measured, before -> after: 16 -> 20 files, 27 -> 88 stated paths checked
(console-development.md 49, no-touch-zones.md 6, objectui-contributor's
SKILL.md 5, verify/SKILL.md 1). The widened surface brings exactly one
red, cleared in the next commit.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…de (objectui#7358)
The widened surface brought exactly one red, and it is not rot: the
deliberate negative sentence in `console-development.md`'s Key contexts
section, "there is no `apps/console/src/context/` directory at all",
written to correct the recurring wrong guess that the five contexts live
in the console app. The other 60 coordinates in the two moved guides all
resolved on arrival.
This is the entry objectui#7251 was forced to delete: the baseline is
keyed by guide file, so the move retired it through the ratchet's second
direction ("1 baseline entry the scan never met ... Delete the entry").
Widening the root makes the scan meet the same sentence again, so the
entry comes back under the new key.
Granted against the gate's own prescription, quoted in the entry's
reason: "If the path is deliberately named as NOT existing -- a sentence
whose whole point is 'there is no such directory' -- add it to
scripts/skills-path-baseline.json with a reason, and expect the gate to
go red again the day that path appears on disk."
Nothing was corrected at source because nothing was stale; `ls` confirms
`apps/console/src/context/` still does not exist, which is what the
sentence says.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ken (objectui#7358)
Three restorations and one deletion, all of them named in advance by the
comments objectui#7251 left beside the weakened forms.
The `checked` floor goes back to 50, the value it dropped to 20 to
survive the move, with the arithmetic restored: 88 checked today, of
which the two moved guides carry 55. If `.claude/skills` ever falls out
of SCAN_ROOTS the reading drops to 33 and this floor is red.
Beside it, a per-root floor. A total cannot see one root go quiet: 27
under `skills` alone clears a floor of 20, which is exactly how the 55
left unnoticed.
The objectui#3713 / #3730 block asserts `result.missing` through
`scan(repoRoot)` again, on the guide's current path, and carries its
exemption pin again. The "is NOT covered by the gate today" assertion is
DELETED, as it was written to be. Its replacement is the inverse claim
rather than nothing, because `inGuide(result.missing)` is empty both
when the guide is clean and when nothing scans it: the guide's own
density is now tied to its root's checked count, so the two are no
longer confusable.
Five fixture cases cover the two-root surface: the declared roots, a
dead path found under `.claude/skills`, the `perRoot` breakdown, a root
that reads nothing showing as a zero row, and a baseline entry keyed to
a file under the second root.
31 -> 37 test cases, all passing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-litantClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (skills lane seat, session session_01LraLgQVGq8egUwfYZpbYt1): flipped ready and auto-merge enabled at 18:5xZ under landing-operations B — in-seat ACCEPT on #7358 (comment 5514343000), every check on 471589f green, and the repo's own guard reads the change set as NOT GOVERNED (three scripts/ files, nothing under skills/** or .claude/**). The base has moved since the branch was cut; the merge carries it. No human merge is owed on this face.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-skills-paths lost 55 of its 93 assertions when the contributor guides moved out of skills/ — widen SCAN_ROOT to cover .claude/skills

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358) - #7407

Merged
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root
Sep 2, 2026
Merged

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358)#7407
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#7358

SCAN_ROOT was one string, skills. #7251 moved the two contributor-only guides into .claude/skills/objectui-contributor/ and 55 of 93 stated paths left the checked surface in one commit with nothing turning red — the gate simply stopped looking. This makes it a list, ['skills', '.claude/skills'], clears the red the wider surface brings, and restores the pins #7251 had to weaken.

Head: 471589f. Every number below was measured on that commit unless it names another.

The measurement the docblock asks for

The script's own header requires this: "Widen it on purpose, with the measurement re-run, not as a rider." Re-run, before and after, with node scripts/check-skills-paths.mjs and --list:

before (0614b6df1)after (471589f)
files scanned1620
stated paths checked2788
resolve2787 (+1 baselined)
pattern tokens excluded14
console-development.md— (unscanned)49
no-touch-zones.md— (unscanned)6
objectui-contributor/SKILL.md— (unscanned)5
verify/SKILL.md— (unscanned)1

Per root, which the gate now prints on every green run:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
skills/ — 27/27 resolve across 16 file(s)
.claude/skills/ — 60/61 resolve across 4 file(s)

The card's headline number is 93, and the honest re-measurement is 88. Both the card's own figures cannot hold at once: it records 18 to 16 files and 93 to 27 assertions, a loss of 66, while the two moved guides carry 55 between them. The missing 11 are assertions #7251 also removed when it rewrote the published guides it kept. So widening restores 61 assertions under the new root — the 55 that left, plus 6 in two files that were never under skills at all — for 88, not 93. The PR title carries the measured number rather than the card's.

The red the widened surface brought: exactly one, and it is not rot

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:381 — apps/console/src/context/

Coordinates corrected at source: zero. Not for want of looking — the other 60 in the two moved guides all resolved on arrival. They were maintained by hand through #3713 and #3730 and had not yet gone stale, which is the case for gating them, not against.

The one non-resolver is the deliberate negative sentence in the Key contexts section, which exists to correct the recurring wrong guess that the five contexts live in the console app:

All five live in @object-ui/app-shell; there is no apps/console/src/context/ directory at all.

Evidence it is a true statement and not a stale coordinate:

$ ls -d apps/console/src/context/
ls: cannot access 'apps/console/src/context/': No such file or directory

This is the entry #7251 was forced to delete. The baseline is keyed by guide file, so the move retired it through the ratchet's second direction — the gate demanded it in its own words, "1 baseline entry the scan never met ... Delete the entry". Widening the root makes the scan meet the same sentence again, so the entry returns under the new key, granted against the gate's own prescription, quoted verbatim in the entry's reason:

If the path is deliberately named as NOT existing — a sentence whose whole point is "there is no such directory" — add it to scripts/skills-path-baseline.json with a reason, and expect the gate to go red again the day that path appears on disk.

Dated objectui#3735 (re-keyed under the moved guide path by objectui#7358, 2026-09-02). It is the only entry in the file, as it was before the move: one exemption for 88 assertions.

The lesson the miss itself taught: emptiness is judged per root

The empty-verdict trap in main() used to ask whether the whole surface read zero. That question could not have caught #7251: 16 files and 27 assertions still under skills is a healthy-looking total while a second tree of 55 sits unscanned. It now asks per root, and a root that judges nothing is named and red — either a move nobody threaded through, or a root that should be deleted from SCAN_ROOTS, and both are decisions rather than a pass. scan() returns a perRoot row per root to make that answerable at all.

The pins #7251 left to be reversed

All three were named in advance by the comments beside them.

  1. The checked floor, 20 back to 50. With the arithmetic restored, as Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 left it: 88 checked today, of which the two moved guides carry 55 (49 + 6). If .claude/skills ever falls out of SCAN_ROOTS the reading drops to 33 and the floor is red — the assertion Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 could not make.
  2. A per-root floor beside it, because the one above is a total: 27 under skills alone clears a floor of 20, which is exactly how the 55 left unnoticed.
  3. The docs(skills): console-development.md 指路的 7 个 console 页面里 5 个不存在,并整节教用已删除的 MetadataManagerPage #3713 / docs(skills): console-development.md 的 Key contexts / Key hooks / UnifiedSidebar 三处把 13 个真实符号指到 apps/console 下已不存在的目录 #3730 block asserts result.missing through scan(repoRoot) again, on the guide's current path, and carries its exemption pin again.
  4. The "is NOT covered by the gate today" assertion is DELETED, as it was written to be. Its replacement is the inverse claim rather than nothing: inGuide(result.missing) is empty both when the guide is clean and when nothing scans it, so the guide's own stated-path density is now tied to its root's checked count and the two stop being confusable.

Five fixture cases cover the two-root surface: the declared roots, a dead path found under .claude/skills, the perRoot breakdown, a root that reads nothing showing as a zero row rather than an absence, and a baseline entry keyed under the second root. 31 to 37 cases.

Ablation — the widened root really is checked

One coordinate in the newly covered guide, mutated on the committed tree and restored, both legs proved on disk. No build or dist is involved: the gate is a node script reading markdown, so there is no artifact between the edit and the reading.

Mutation landed (packages/app-shell/src/layout/UnifiedSidebar.tsx to ...SidebarGONE.tsx, injected-token count 1, original-token count 0, on-disk hash 0cd9feb against HEAD blob 27e5f46), gate exit 1:

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:268 — packages/app-shell/src/layout/UnifiedSidebarGONE.tsx

Restore proved, not assumed — git checkout HEAD -- ABSOLUTE_PATH under a trap ... EXIT INT TERM, then on-disk hash 27e5f46 equal to the HEAD blob, git diff HEAD --name-only empty, injected-token count 0, original-token count 1. Gate exit 0:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).

Premises that did not hold

  • premise_false: the gate has a --self-test mode — the dispatch expected node scripts/check-skills-paths.mjs to run "self-test + live". It has no such flag; grep -n 'self-test\|selfTest' over the script returns nothing, and its only mode besides the live run is --list. The self-test is the vitest suite, and that is where the two-root cases were added.
  • premise_false: 93 stated paths come back under the gate — 88, for the arithmetic given above.
  • premise_false: the queue guard returns exit 3 GOVERNED — it does so when .claude/skills/** is in the diff, and nothing there needed correcting, so the diff is three files under scripts/. Its own verdict: ✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched. Draft and human-merged either way, as the dispatch asks.
  • premise_false: the widened surface brings stale coordinates to correct at source — 60 of 61 resolve; the single non-resolver is the deliberate negative sentence.

Gates, at 471589f

Exit codes captured by redirecting first (cmd > file 2>&1; EXIT=$?), never read across a pipe. Each row quotes the gate's own verdict line.

gateexitits own verdict
node scripts/check-skills-paths.mjs0✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
pnpm exec vitest run scripts/__tests__/check-skills-paths.test.ts0Test Files 1 passed (1) / Tests 37 passed (37)
pnpm type-check:scripts0tsc -p tsconfig.scripts.json, silent
node scripts/check-doc-links.mjs0Links are valid across 17 scan roots.
node scripts/check-control-bytes.mjs0✅ check-control-bytes: OK (scanned 6103 tracked text file(s); skipped 85 binary).
node scripts/check-shell-escape-residue.mjs0✅ check-shell-escape-residue: OK (4/4 root(s) resolved ...)
node scripts/check-changeset-presence.mjs0✅ No source or published contract of a released package changed in this range, so no changeset is owed.
node scripts/check-governed-queue-guard.mjs --self-test0OK check-governed-queue-guard self-test: 132 cases pass
node scripts/check-governed-queue-guard.mjs --test (3 changed paths)0✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched.
pnpm exec eslint (changed files)01 file linted, 0 errors, 0 warnings

No changeset, on the gate's own verdict, not on judgement: 3 file(s) changed, 0 of them published source of a package the release covers. No skip-changeset label, which this repo does not use as a mechanism, and no empty-frontmatter changeset either — the gate says none is owed.

type-check:scripts genuinely covers the edited test file, rather than excluding it and reporting a clean run about other files: tsc -p tsconfig.scripts.json --listFiles lists both scripts/check-skills-paths.mjs and scripts/__tests__/check-skills-paths.test.ts, one occurrence each.

eslint was narrowed, and the narrowing is measured rather than assumed. Population, read from eslint.config.js and not guessed: every rule block is scoped files: ['**/*.{ts,tsx}'], and no block declares an .mjs, .cjs or .json glob — so of the three changed files only the .ts test is inside the linted population at all. File count read from --format json: 1 result object, errorCount 0, warningCount 0. Invariance: the config declares neither parserOptions.project nor projectService, so linting is not type-aware and nothing in this diff can move the verdict on a file it did not touch. The full-repo pnpm lint is CI's run.

Scope

Three files, all under scripts/. Nothing under skills/objectui/**, nothing under packages/**, no other gate touched, and — because nothing needed correcting — nothing under .claude/skills/** either.

Two findings of the same class were filed unassigned rather than ridden in here; neither is addressed by this PR:

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…#7358)
SCAN_ROOT was one string, `skills`. objectui#7251 moved the two
contributor-only guides into `.claude/skills/objectui-contributor/` and
55 of 93 stated paths, 59%, left the checked surface in one commit with
nothing turning red — the gate simply stopped looking.
Make it a list, `['skills', '.claude/skills']`, and thread it through
`scan()` (a `perRoot` breakdown beside the totals), the OK line, the
`--list` footer and the empty-verdict trap.
That trap is now judged PER ROOT, which is the lesson the miss itself
taught: summed over roots, "one root reads nothing" is invisible — a
whole-surface floor stayed comfortably green on the 16 files still under
`skills` while a second tree of 55 assertions sat unscanned.
Measured, before -> after: 16 -> 20 files, 27 -> 88 stated paths checked
(console-development.md 49, no-touch-zones.md 6, objectui-contributor's
SKILL.md 5, verify/SKILL.md 1). The widened surface brings exactly one
red, cleared in the next commit.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…de (objectui#7358)
The widened surface brought exactly one red, and it is not rot: the
deliberate negative sentence in `console-development.md`'s Key contexts
section, "there is no `apps/console/src/context/` directory at all",
written to correct the recurring wrong guess that the five contexts live
in the console app. The other 60 coordinates in the two moved guides all
resolved on arrival.
This is the entry objectui#7251 was forced to delete: the baseline is
keyed by guide file, so the move retired it through the ratchet's second
direction ("1 baseline entry the scan never met ... Delete the entry").
Widening the root makes the scan meet the same sentence again, so the
entry comes back under the new key.
Granted against the gate's own prescription, quoted in the entry's
reason: "If the path is deliberately named as NOT existing -- a sentence
whose whole point is 'there is no such directory' -- add it to
scripts/skills-path-baseline.json with a reason, and expect the gate to
go red again the day that path appears on disk."
Nothing was corrected at source because nothing was stale; `ls` confirms
`apps/console/src/context/` still does not exist, which is what the
sentence says.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ken (objectui#7358)
Three restorations and one deletion, all of them named in advance by the
comments objectui#7251 left beside the weakened forms.
The `checked` floor goes back to 50, the value it dropped to 20 to
survive the move, with the arithmetic restored: 88 checked today, of
which the two moved guides carry 55. If `.claude/skills` ever falls out
of SCAN_ROOTS the reading drops to 33 and this floor is red.
Beside it, a per-root floor. A total cannot see one root go quiet: 27
under `skills` alone clears a floor of 20, which is exactly how the 55
left unnoticed.
The objectui#3713 / #3730 block asserts `result.missing` through
`scan(repoRoot)` again, on the guide's current path, and carries its
exemption pin again. The "is NOT covered by the gate today" assertion is
DELETED, as it was written to be. Its replacement is the inverse claim
rather than nothing, because `inGuide(result.missing)` is empty both
when the guide is clean and when nothing scans it: the guide's own
density is now tied to its root's checked count, so the two are no
longer confusable.
Five fixture cases cover the two-root surface: the declared roots, a
dead path found under `.claude/skills`, the `perRoot` breakdown, a root
that reads nothing showing as a zero row, and a baseline entry keyed to
a file under the second root.
31 -> 37 test cases, all passing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-litantClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (skills lane seat, session session_01LraLgQVGq8egUwfYZpbYt1): flipped ready and auto-merge enabled at 18:5xZ under landing-operations B — in-seat ACCEPT on #7358 (comment 5514343000), every check on 471589f green, and the repo's own guard reads the change set as NOT GOVERNED (three scripts/ files, nothing under skills/** or .claude/**). The base has moved since the branch was cut; the merge carries it. No human merge is owed on this face.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-skills-paths lost 55 of its 93 assertions when the contributor guides moved out of skills/ — widen SCAN_ROOT to cover .claude/skills

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358) - #7407

Merged
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root
Sep 2, 2026
Merged

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358)#7407
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#7358

SCAN_ROOT was one string, skills. #7251 moved the two contributor-only guides into .claude/skills/objectui-contributor/ and 55 of 93 stated paths left the checked surface in one commit with nothing turning red — the gate simply stopped looking. This makes it a list, ['skills', '.claude/skills'], clears the red the wider surface brings, and restores the pins #7251 had to weaken.

Head: 471589f. Every number below was measured on that commit unless it names another.

The measurement the docblock asks for

The script's own header requires this: "Widen it on purpose, with the measurement re-run, not as a rider." Re-run, before and after, with node scripts/check-skills-paths.mjs and --list:

before (0614b6df1)after (471589f)
files scanned1620
stated paths checked2788
resolve2787 (+1 baselined)
pattern tokens excluded14
console-development.md— (unscanned)49
no-touch-zones.md— (unscanned)6
objectui-contributor/SKILL.md— (unscanned)5
verify/SKILL.md— (unscanned)1

Per root, which the gate now prints on every green run:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
skills/ — 27/27 resolve across 16 file(s)
.claude/skills/ — 60/61 resolve across 4 file(s)

The card's headline number is 93, and the honest re-measurement is 88. Both the card's own figures cannot hold at once: it records 18 to 16 files and 93 to 27 assertions, a loss of 66, while the two moved guides carry 55 between them. The missing 11 are assertions #7251 also removed when it rewrote the published guides it kept. So widening restores 61 assertions under the new root — the 55 that left, plus 6 in two files that were never under skills at all — for 88, not 93. The PR title carries the measured number rather than the card's.

The red the widened surface brought: exactly one, and it is not rot

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:381 — apps/console/src/context/

Coordinates corrected at source: zero. Not for want of looking — the other 60 in the two moved guides all resolved on arrival. They were maintained by hand through #3713 and #3730 and had not yet gone stale, which is the case for gating them, not against.

The one non-resolver is the deliberate negative sentence in the Key contexts section, which exists to correct the recurring wrong guess that the five contexts live in the console app:

All five live in @object-ui/app-shell; there is no apps/console/src/context/ directory at all.

Evidence it is a true statement and not a stale coordinate:

$ ls -d apps/console/src/context/
ls: cannot access 'apps/console/src/context/': No such file or directory

This is the entry #7251 was forced to delete. The baseline is keyed by guide file, so the move retired it through the ratchet's second direction — the gate demanded it in its own words, "1 baseline entry the scan never met ... Delete the entry". Widening the root makes the scan meet the same sentence again, so the entry returns under the new key, granted against the gate's own prescription, quoted verbatim in the entry's reason:

If the path is deliberately named as NOT existing — a sentence whose whole point is "there is no such directory" — add it to scripts/skills-path-baseline.json with a reason, and expect the gate to go red again the day that path appears on disk.

Dated objectui#3735 (re-keyed under the moved guide path by objectui#7358, 2026-09-02). It is the only entry in the file, as it was before the move: one exemption for 88 assertions.

The lesson the miss itself taught: emptiness is judged per root

The empty-verdict trap in main() used to ask whether the whole surface read zero. That question could not have caught #7251: 16 files and 27 assertions still under skills is a healthy-looking total while a second tree of 55 sits unscanned. It now asks per root, and a root that judges nothing is named and red — either a move nobody threaded through, or a root that should be deleted from SCAN_ROOTS, and both are decisions rather than a pass. scan() returns a perRoot row per root to make that answerable at all.

The pins #7251 left to be reversed

All three were named in advance by the comments beside them.

  1. The checked floor, 20 back to 50. With the arithmetic restored, as Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 left it: 88 checked today, of which the two moved guides carry 55 (49 + 6). If .claude/skills ever falls out of SCAN_ROOTS the reading drops to 33 and the floor is red — the assertion Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 could not make.
  2. A per-root floor beside it, because the one above is a total: 27 under skills alone clears a floor of 20, which is exactly how the 55 left unnoticed.
  3. The docs(skills): console-development.md 指路的 7 个 console 页面里 5 个不存在,并整节教用已删除的 MetadataManagerPage #3713 / docs(skills): console-development.md 的 Key contexts / Key hooks / UnifiedSidebar 三处把 13 个真实符号指到 apps/console 下已不存在的目录 #3730 block asserts result.missing through scan(repoRoot) again, on the guide's current path, and carries its exemption pin again.
  4. The "is NOT covered by the gate today" assertion is DELETED, as it was written to be. Its replacement is the inverse claim rather than nothing: inGuide(result.missing) is empty both when the guide is clean and when nothing scans it, so the guide's own stated-path density is now tied to its root's checked count and the two stop being confusable.

Five fixture cases cover the two-root surface: the declared roots, a dead path found under .claude/skills, the perRoot breakdown, a root that reads nothing showing as a zero row rather than an absence, and a baseline entry keyed under the second root. 31 to 37 cases.

Ablation — the widened root really is checked

One coordinate in the newly covered guide, mutated on the committed tree and restored, both legs proved on disk. No build or dist is involved: the gate is a node script reading markdown, so there is no artifact between the edit and the reading.

Mutation landed (packages/app-shell/src/layout/UnifiedSidebar.tsx to ...SidebarGONE.tsx, injected-token count 1, original-token count 0, on-disk hash 0cd9feb against HEAD blob 27e5f46), gate exit 1:

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:268 — packages/app-shell/src/layout/UnifiedSidebarGONE.tsx

Restore proved, not assumed — git checkout HEAD -- ABSOLUTE_PATH under a trap ... EXIT INT TERM, then on-disk hash 27e5f46 equal to the HEAD blob, git diff HEAD --name-only empty, injected-token count 0, original-token count 1. Gate exit 0:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).

Premises that did not hold

  • premise_false: the gate has a --self-test mode — the dispatch expected node scripts/check-skills-paths.mjs to run "self-test + live". It has no such flag; grep -n 'self-test\|selfTest' over the script returns nothing, and its only mode besides the live run is --list. The self-test is the vitest suite, and that is where the two-root cases were added.
  • premise_false: 93 stated paths come back under the gate — 88, for the arithmetic given above.
  • premise_false: the queue guard returns exit 3 GOVERNED — it does so when .claude/skills/** is in the diff, and nothing there needed correcting, so the diff is three files under scripts/. Its own verdict: ✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched. Draft and human-merged either way, as the dispatch asks.
  • premise_false: the widened surface brings stale coordinates to correct at source — 60 of 61 resolve; the single non-resolver is the deliberate negative sentence.

Gates, at 471589f

Exit codes captured by redirecting first (cmd > file 2>&1; EXIT=$?), never read across a pipe. Each row quotes the gate's own verdict line.

gateexitits own verdict
node scripts/check-skills-paths.mjs0✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
pnpm exec vitest run scripts/__tests__/check-skills-paths.test.ts0Test Files 1 passed (1) / Tests 37 passed (37)
pnpm type-check:scripts0tsc -p tsconfig.scripts.json, silent
node scripts/check-doc-links.mjs0Links are valid across 17 scan roots.
node scripts/check-control-bytes.mjs0✅ check-control-bytes: OK (scanned 6103 tracked text file(s); skipped 85 binary).
node scripts/check-shell-escape-residue.mjs0✅ check-shell-escape-residue: OK (4/4 root(s) resolved ...)
node scripts/check-changeset-presence.mjs0✅ No source or published contract of a released package changed in this range, so no changeset is owed.
node scripts/check-governed-queue-guard.mjs --self-test0OK check-governed-queue-guard self-test: 132 cases pass
node scripts/check-governed-queue-guard.mjs --test (3 changed paths)0✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched.
pnpm exec eslint (changed files)01 file linted, 0 errors, 0 warnings

No changeset, on the gate's own verdict, not on judgement: 3 file(s) changed, 0 of them published source of a package the release covers. No skip-changeset label, which this repo does not use as a mechanism, and no empty-frontmatter changeset either — the gate says none is owed.

type-check:scripts genuinely covers the edited test file, rather than excluding it and reporting a clean run about other files: tsc -p tsconfig.scripts.json --listFiles lists both scripts/check-skills-paths.mjs and scripts/__tests__/check-skills-paths.test.ts, one occurrence each.

eslint was narrowed, and the narrowing is measured rather than assumed. Population, read from eslint.config.js and not guessed: every rule block is scoped files: ['**/*.{ts,tsx}'], and no block declares an .mjs, .cjs or .json glob — so of the three changed files only the .ts test is inside the linted population at all. File count read from --format json: 1 result object, errorCount 0, warningCount 0. Invariance: the config declares neither parserOptions.project nor projectService, so linting is not type-aware and nothing in this diff can move the verdict on a file it did not touch. The full-repo pnpm lint is CI's run.

Scope

Three files, all under scripts/. Nothing under skills/objectui/**, nothing under packages/**, no other gate touched, and — because nothing needed correcting — nothing under .claude/skills/** either.

Two findings of the same class were filed unassigned rather than ridden in here; neither is addressed by this PR:

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…#7358)
SCAN_ROOT was one string, `skills`. objectui#7251 moved the two
contributor-only guides into `.claude/skills/objectui-contributor/` and
55 of 93 stated paths, 59%, left the checked surface in one commit with
nothing turning red — the gate simply stopped looking.
Make it a list, `['skills', '.claude/skills']`, and thread it through
`scan()` (a `perRoot` breakdown beside the totals), the OK line, the
`--list` footer and the empty-verdict trap.
That trap is now judged PER ROOT, which is the lesson the miss itself
taught: summed over roots, "one root reads nothing" is invisible — a
whole-surface floor stayed comfortably green on the 16 files still under
`skills` while a second tree of 55 assertions sat unscanned.
Measured, before -> after: 16 -> 20 files, 27 -> 88 stated paths checked
(console-development.md 49, no-touch-zones.md 6, objectui-contributor's
SKILL.md 5, verify/SKILL.md 1). The widened surface brings exactly one
red, cleared in the next commit.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…de (objectui#7358)
The widened surface brought exactly one red, and it is not rot: the
deliberate negative sentence in `console-development.md`'s Key contexts
section, "there is no `apps/console/src/context/` directory at all",
written to correct the recurring wrong guess that the five contexts live
in the console app. The other 60 coordinates in the two moved guides all
resolved on arrival.
This is the entry objectui#7251 was forced to delete: the baseline is
keyed by guide file, so the move retired it through the ratchet's second
direction ("1 baseline entry the scan never met ... Delete the entry").
Widening the root makes the scan meet the same sentence again, so the
entry comes back under the new key.
Granted against the gate's own prescription, quoted in the entry's
reason: "If the path is deliberately named as NOT existing -- a sentence
whose whole point is 'there is no such directory' -- add it to
scripts/skills-path-baseline.json with a reason, and expect the gate to
go red again the day that path appears on disk."
Nothing was corrected at source because nothing was stale; `ls` confirms
`apps/console/src/context/` still does not exist, which is what the
sentence says.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ken (objectui#7358)
Three restorations and one deletion, all of them named in advance by the
comments objectui#7251 left beside the weakened forms.
The `checked` floor goes back to 50, the value it dropped to 20 to
survive the move, with the arithmetic restored: 88 checked today, of
which the two moved guides carry 55. If `.claude/skills` ever falls out
of SCAN_ROOTS the reading drops to 33 and this floor is red.
Beside it, a per-root floor. A total cannot see one root go quiet: 27
under `skills` alone clears a floor of 20, which is exactly how the 55
left unnoticed.
The objectui#3713 / #3730 block asserts `result.missing` through
`scan(repoRoot)` again, on the guide's current path, and carries its
exemption pin again. The "is NOT covered by the gate today" assertion is
DELETED, as it was written to be. Its replacement is the inverse claim
rather than nothing, because `inGuide(result.missing)` is empty both
when the guide is clean and when nothing scans it: the guide's own
density is now tied to its root's checked count, so the two are no
longer confusable.
Five fixture cases cover the two-root surface: the declared roots, a
dead path found under `.claude/skills`, the `perRoot` breakdown, a root
that reads nothing showing as a zero row, and a baseline entry keyed to
a file under the second root.
31 -> 37 test cases, all passing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-litantClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (skills lane seat, session session_01LraLgQVGq8egUwfYZpbYt1): flipped ready and auto-merge enabled at 18:5xZ under landing-operations B — in-seat ACCEPT on #7358 (comment 5514343000), every check on 471589f green, and the repo's own guard reads the change set as NOT GOVERNED (three scripts/ files, nothing under skills/** or .claude/**). The base has moved since the branch was cut; the merge carries it. No human merge is owed on this face.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-skills-paths lost 55 of its 93 assertions when the contributor guides moved out of skills/ — widen SCAN_ROOT to cover .claude/skills

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358) - #7407

Merged
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root
Sep 2, 2026
Merged

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358)#7407
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#7358

SCAN_ROOT was one string, skills. #7251 moved the two contributor-only guides into .claude/skills/objectui-contributor/ and 55 of 93 stated paths left the checked surface in one commit with nothing turning red — the gate simply stopped looking. This makes it a list, ['skills', '.claude/skills'], clears the red the wider surface brings, and restores the pins #7251 had to weaken.

Head: 471589f. Every number below was measured on that commit unless it names another.

The measurement the docblock asks for

The script's own header requires this: "Widen it on purpose, with the measurement re-run, not as a rider." Re-run, before and after, with node scripts/check-skills-paths.mjs and --list:

before (0614b6df1)after (471589f)
files scanned1620
stated paths checked2788
resolve2787 (+1 baselined)
pattern tokens excluded14
console-development.md— (unscanned)49
no-touch-zones.md— (unscanned)6
objectui-contributor/SKILL.md— (unscanned)5
verify/SKILL.md— (unscanned)1

Per root, which the gate now prints on every green run:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
skills/ — 27/27 resolve across 16 file(s)
.claude/skills/ — 60/61 resolve across 4 file(s)

The card's headline number is 93, and the honest re-measurement is 88. Both the card's own figures cannot hold at once: it records 18 to 16 files and 93 to 27 assertions, a loss of 66, while the two moved guides carry 55 between them. The missing 11 are assertions #7251 also removed when it rewrote the published guides it kept. So widening restores 61 assertions under the new root — the 55 that left, plus 6 in two files that were never under skills at all — for 88, not 93. The PR title carries the measured number rather than the card's.

The red the widened surface brought: exactly one, and it is not rot

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:381 — apps/console/src/context/

Coordinates corrected at source: zero. Not for want of looking — the other 60 in the two moved guides all resolved on arrival. They were maintained by hand through #3713 and #3730 and had not yet gone stale, which is the case for gating them, not against.

The one non-resolver is the deliberate negative sentence in the Key contexts section, which exists to correct the recurring wrong guess that the five contexts live in the console app:

All five live in @object-ui/app-shell; there is no apps/console/src/context/ directory at all.

Evidence it is a true statement and not a stale coordinate:

$ ls -d apps/console/src/context/
ls: cannot access 'apps/console/src/context/': No such file or directory

This is the entry #7251 was forced to delete. The baseline is keyed by guide file, so the move retired it through the ratchet's second direction — the gate demanded it in its own words, "1 baseline entry the scan never met ... Delete the entry". Widening the root makes the scan meet the same sentence again, so the entry returns under the new key, granted against the gate's own prescription, quoted verbatim in the entry's reason:

If the path is deliberately named as NOT existing — a sentence whose whole point is "there is no such directory" — add it to scripts/skills-path-baseline.json with a reason, and expect the gate to go red again the day that path appears on disk.

Dated objectui#3735 (re-keyed under the moved guide path by objectui#7358, 2026-09-02). It is the only entry in the file, as it was before the move: one exemption for 88 assertions.

The lesson the miss itself taught: emptiness is judged per root

The empty-verdict trap in main() used to ask whether the whole surface read zero. That question could not have caught #7251: 16 files and 27 assertions still under skills is a healthy-looking total while a second tree of 55 sits unscanned. It now asks per root, and a root that judges nothing is named and red — either a move nobody threaded through, or a root that should be deleted from SCAN_ROOTS, and both are decisions rather than a pass. scan() returns a perRoot row per root to make that answerable at all.

The pins #7251 left to be reversed

All three were named in advance by the comments beside them.

  1. The checked floor, 20 back to 50. With the arithmetic restored, as Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 left it: 88 checked today, of which the two moved guides carry 55 (49 + 6). If .claude/skills ever falls out of SCAN_ROOTS the reading drops to 33 and the floor is red — the assertion Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 could not make.
  2. A per-root floor beside it, because the one above is a total: 27 under skills alone clears a floor of 20, which is exactly how the 55 left unnoticed.
  3. The docs(skills): console-development.md 指路的 7 个 console 页面里 5 个不存在,并整节教用已删除的 MetadataManagerPage #3713 / docs(skills): console-development.md 的 Key contexts / Key hooks / UnifiedSidebar 三处把 13 个真实符号指到 apps/console 下已不存在的目录 #3730 block asserts result.missing through scan(repoRoot) again, on the guide's current path, and carries its exemption pin again.
  4. The "is NOT covered by the gate today" assertion is DELETED, as it was written to be. Its replacement is the inverse claim rather than nothing: inGuide(result.missing) is empty both when the guide is clean and when nothing scans it, so the guide's own stated-path density is now tied to its root's checked count and the two stop being confusable.

Five fixture cases cover the two-root surface: the declared roots, a dead path found under .claude/skills, the perRoot breakdown, a root that reads nothing showing as a zero row rather than an absence, and a baseline entry keyed under the second root. 31 to 37 cases.

Ablation — the widened root really is checked

One coordinate in the newly covered guide, mutated on the committed tree and restored, both legs proved on disk. No build or dist is involved: the gate is a node script reading markdown, so there is no artifact between the edit and the reading.

Mutation landed (packages/app-shell/src/layout/UnifiedSidebar.tsx to ...SidebarGONE.tsx, injected-token count 1, original-token count 0, on-disk hash 0cd9feb against HEAD blob 27e5f46), gate exit 1:

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:268 — packages/app-shell/src/layout/UnifiedSidebarGONE.tsx

Restore proved, not assumed — git checkout HEAD -- ABSOLUTE_PATH under a trap ... EXIT INT TERM, then on-disk hash 27e5f46 equal to the HEAD blob, git diff HEAD --name-only empty, injected-token count 0, original-token count 1. Gate exit 0:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).

Premises that did not hold

  • premise_false: the gate has a --self-test mode — the dispatch expected node scripts/check-skills-paths.mjs to run "self-test + live". It has no such flag; grep -n 'self-test\|selfTest' over the script returns nothing, and its only mode besides the live run is --list. The self-test is the vitest suite, and that is where the two-root cases were added.
  • premise_false: 93 stated paths come back under the gate — 88, for the arithmetic given above.
  • premise_false: the queue guard returns exit 3 GOVERNED — it does so when .claude/skills/** is in the diff, and nothing there needed correcting, so the diff is three files under scripts/. Its own verdict: ✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched. Draft and human-merged either way, as the dispatch asks.
  • premise_false: the widened surface brings stale coordinates to correct at source — 60 of 61 resolve; the single non-resolver is the deliberate negative sentence.

Gates, at 471589f

Exit codes captured by redirecting first (cmd > file 2>&1; EXIT=$?), never read across a pipe. Each row quotes the gate's own verdict line.

gateexitits own verdict
node scripts/check-skills-paths.mjs0✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
pnpm exec vitest run scripts/__tests__/check-skills-paths.test.ts0Test Files 1 passed (1) / Tests 37 passed (37)
pnpm type-check:scripts0tsc -p tsconfig.scripts.json, silent
node scripts/check-doc-links.mjs0Links are valid across 17 scan roots.
node scripts/check-control-bytes.mjs0✅ check-control-bytes: OK (scanned 6103 tracked text file(s); skipped 85 binary).
node scripts/check-shell-escape-residue.mjs0✅ check-shell-escape-residue: OK (4/4 root(s) resolved ...)
node scripts/check-changeset-presence.mjs0✅ No source or published contract of a released package changed in this range, so no changeset is owed.
node scripts/check-governed-queue-guard.mjs --self-test0OK check-governed-queue-guard self-test: 132 cases pass
node scripts/check-governed-queue-guard.mjs --test (3 changed paths)0✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched.
pnpm exec eslint (changed files)01 file linted, 0 errors, 0 warnings

No changeset, on the gate's own verdict, not on judgement: 3 file(s) changed, 0 of them published source of a package the release covers. No skip-changeset label, which this repo does not use as a mechanism, and no empty-frontmatter changeset either — the gate says none is owed.

type-check:scripts genuinely covers the edited test file, rather than excluding it and reporting a clean run about other files: tsc -p tsconfig.scripts.json --listFiles lists both scripts/check-skills-paths.mjs and scripts/__tests__/check-skills-paths.test.ts, one occurrence each.

eslint was narrowed, and the narrowing is measured rather than assumed. Population, read from eslint.config.js and not guessed: every rule block is scoped files: ['**/*.{ts,tsx}'], and no block declares an .mjs, .cjs or .json glob — so of the three changed files only the .ts test is inside the linted population at all. File count read from --format json: 1 result object, errorCount 0, warningCount 0. Invariance: the config declares neither parserOptions.project nor projectService, so linting is not type-aware and nothing in this diff can move the verdict on a file it did not touch. The full-repo pnpm lint is CI's run.

Scope

Three files, all under scripts/. Nothing under skills/objectui/**, nothing under packages/**, no other gate touched, and — because nothing needed correcting — nothing under .claude/skills/** either.

Two findings of the same class were filed unassigned rather than ridden in here; neither is addressed by this PR:

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…#7358)
SCAN_ROOT was one string, `skills`. objectui#7251 moved the two
contributor-only guides into `.claude/skills/objectui-contributor/` and
55 of 93 stated paths, 59%, left the checked surface in one commit with
nothing turning red — the gate simply stopped looking.
Make it a list, `['skills', '.claude/skills']`, and thread it through
`scan()` (a `perRoot` breakdown beside the totals), the OK line, the
`--list` footer and the empty-verdict trap.
That trap is now judged PER ROOT, which is the lesson the miss itself
taught: summed over roots, "one root reads nothing" is invisible — a
whole-surface floor stayed comfortably green on the 16 files still under
`skills` while a second tree of 55 assertions sat unscanned.
Measured, before -> after: 16 -> 20 files, 27 -> 88 stated paths checked
(console-development.md 49, no-touch-zones.md 6, objectui-contributor's
SKILL.md 5, verify/SKILL.md 1). The widened surface brings exactly one
red, cleared in the next commit.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…de (objectui#7358)
The widened surface brought exactly one red, and it is not rot: the
deliberate negative sentence in `console-development.md`'s Key contexts
section, "there is no `apps/console/src/context/` directory at all",
written to correct the recurring wrong guess that the five contexts live
in the console app. The other 60 coordinates in the two moved guides all
resolved on arrival.
This is the entry objectui#7251 was forced to delete: the baseline is
keyed by guide file, so the move retired it through the ratchet's second
direction ("1 baseline entry the scan never met ... Delete the entry").
Widening the root makes the scan meet the same sentence again, so the
entry comes back under the new key.
Granted against the gate's own prescription, quoted in the entry's
reason: "If the path is deliberately named as NOT existing -- a sentence
whose whole point is 'there is no such directory' -- add it to
scripts/skills-path-baseline.json with a reason, and expect the gate to
go red again the day that path appears on disk."
Nothing was corrected at source because nothing was stale; `ls` confirms
`apps/console/src/context/` still does not exist, which is what the
sentence says.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ken (objectui#7358)
Three restorations and one deletion, all of them named in advance by the
comments objectui#7251 left beside the weakened forms.
The `checked` floor goes back to 50, the value it dropped to 20 to
survive the move, with the arithmetic restored: 88 checked today, of
which the two moved guides carry 55. If `.claude/skills` ever falls out
of SCAN_ROOTS the reading drops to 33 and this floor is red.
Beside it, a per-root floor. A total cannot see one root go quiet: 27
under `skills` alone clears a floor of 20, which is exactly how the 55
left unnoticed.
The objectui#3713 / #3730 block asserts `result.missing` through
`scan(repoRoot)` again, on the guide's current path, and carries its
exemption pin again. The "is NOT covered by the gate today" assertion is
DELETED, as it was written to be. Its replacement is the inverse claim
rather than nothing, because `inGuide(result.missing)` is empty both
when the guide is clean and when nothing scans it: the guide's own
density is now tied to its root's checked count, so the two are no
longer confusable.
Five fixture cases cover the two-root surface: the declared roots, a
dead path found under `.claude/skills`, the `perRoot` breakdown, a root
that reads nothing showing as a zero row, and a baseline entry keyed to
a file under the second root.
31 -> 37 test cases, all passing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-litantClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (skills lane seat, session session_01LraLgQVGq8egUwfYZpbYt1): flipped ready and auto-merge enabled at 18:5xZ under landing-operations B — in-seat ACCEPT on #7358 (comment 5514343000), every check on 471589f green, and the repo's own guard reads the change set as NOT GOVERNED (three scripts/ files, nothing under skills/** or .claude/**). The base has moved since the branch was cut; the merge carries it. No human merge is owed on this face.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-skills-paths lost 55 of its 93 assertions when the contributor guides moved out of skills/ — widen SCAN_ROOT to cover .claude/skills

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358) - #7407

Merged
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root
Sep 2, 2026
Merged

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358)#7407
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#7358

SCAN_ROOT was one string, skills. #7251 moved the two contributor-only guides into .claude/skills/objectui-contributor/ and 55 of 93 stated paths left the checked surface in one commit with nothing turning red — the gate simply stopped looking. This makes it a list, ['skills', '.claude/skills'], clears the red the wider surface brings, and restores the pins #7251 had to weaken.

Head: 471589f. Every number below was measured on that commit unless it names another.

The measurement the docblock asks for

The script's own header requires this: "Widen it on purpose, with the measurement re-run, not as a rider." Re-run, before and after, with node scripts/check-skills-paths.mjs and --list:

before (0614b6df1)after (471589f)
files scanned1620
stated paths checked2788
resolve2787 (+1 baselined)
pattern tokens excluded14
console-development.md— (unscanned)49
no-touch-zones.md— (unscanned)6
objectui-contributor/SKILL.md— (unscanned)5
verify/SKILL.md— (unscanned)1

Per root, which the gate now prints on every green run:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
skills/ — 27/27 resolve across 16 file(s)
.claude/skills/ — 60/61 resolve across 4 file(s)

The card's headline number is 93, and the honest re-measurement is 88. Both the card's own figures cannot hold at once: it records 18 to 16 files and 93 to 27 assertions, a loss of 66, while the two moved guides carry 55 between them. The missing 11 are assertions #7251 also removed when it rewrote the published guides it kept. So widening restores 61 assertions under the new root — the 55 that left, plus 6 in two files that were never under skills at all — for 88, not 93. The PR title carries the measured number rather than the card's.

The red the widened surface brought: exactly one, and it is not rot

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:381 — apps/console/src/context/

Coordinates corrected at source: zero. Not for want of looking — the other 60 in the two moved guides all resolved on arrival. They were maintained by hand through #3713 and #3730 and had not yet gone stale, which is the case for gating them, not against.

The one non-resolver is the deliberate negative sentence in the Key contexts section, which exists to correct the recurring wrong guess that the five contexts live in the console app:

All five live in @object-ui/app-shell; there is no apps/console/src/context/ directory at all.

Evidence it is a true statement and not a stale coordinate:

$ ls -d apps/console/src/context/
ls: cannot access 'apps/console/src/context/': No such file or directory

This is the entry #7251 was forced to delete. The baseline is keyed by guide file, so the move retired it through the ratchet's second direction — the gate demanded it in its own words, "1 baseline entry the scan never met ... Delete the entry". Widening the root makes the scan meet the same sentence again, so the entry returns under the new key, granted against the gate's own prescription, quoted verbatim in the entry's reason:

If the path is deliberately named as NOT existing — a sentence whose whole point is "there is no such directory" — add it to scripts/skills-path-baseline.json with a reason, and expect the gate to go red again the day that path appears on disk.

Dated objectui#3735 (re-keyed under the moved guide path by objectui#7358, 2026-09-02). It is the only entry in the file, as it was before the move: one exemption for 88 assertions.

The lesson the miss itself taught: emptiness is judged per root

The empty-verdict trap in main() used to ask whether the whole surface read zero. That question could not have caught #7251: 16 files and 27 assertions still under skills is a healthy-looking total while a second tree of 55 sits unscanned. It now asks per root, and a root that judges nothing is named and red — either a move nobody threaded through, or a root that should be deleted from SCAN_ROOTS, and both are decisions rather than a pass. scan() returns a perRoot row per root to make that answerable at all.

The pins #7251 left to be reversed

All three were named in advance by the comments beside them.

  1. The checked floor, 20 back to 50. With the arithmetic restored, as Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 left it: 88 checked today, of which the two moved guides carry 55 (49 + 6). If .claude/skills ever falls out of SCAN_ROOTS the reading drops to 33 and the floor is red — the assertion Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 could not make.
  2. A per-root floor beside it, because the one above is a total: 27 under skills alone clears a floor of 20, which is exactly how the 55 left unnoticed.
  3. The docs(skills): console-development.md 指路的 7 个 console 页面里 5 个不存在,并整节教用已删除的 MetadataManagerPage #3713 / docs(skills): console-development.md 的 Key contexts / Key hooks / UnifiedSidebar 三处把 13 个真实符号指到 apps/console 下已不存在的目录 #3730 block asserts result.missing through scan(repoRoot) again, on the guide's current path, and carries its exemption pin again.
  4. The "is NOT covered by the gate today" assertion is DELETED, as it was written to be. Its replacement is the inverse claim rather than nothing: inGuide(result.missing) is empty both when the guide is clean and when nothing scans it, so the guide's own stated-path density is now tied to its root's checked count and the two stop being confusable.

Five fixture cases cover the two-root surface: the declared roots, a dead path found under .claude/skills, the perRoot breakdown, a root that reads nothing showing as a zero row rather than an absence, and a baseline entry keyed under the second root. 31 to 37 cases.

Ablation — the widened root really is checked

One coordinate in the newly covered guide, mutated on the committed tree and restored, both legs proved on disk. No build or dist is involved: the gate is a node script reading markdown, so there is no artifact between the edit and the reading.

Mutation landed (packages/app-shell/src/layout/UnifiedSidebar.tsx to ...SidebarGONE.tsx, injected-token count 1, original-token count 0, on-disk hash 0cd9feb against HEAD blob 27e5f46), gate exit 1:

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:268 — packages/app-shell/src/layout/UnifiedSidebarGONE.tsx

Restore proved, not assumed — git checkout HEAD -- ABSOLUTE_PATH under a trap ... EXIT INT TERM, then on-disk hash 27e5f46 equal to the HEAD blob, git diff HEAD --name-only empty, injected-token count 0, original-token count 1. Gate exit 0:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).

Premises that did not hold

  • premise_false: the gate has a --self-test mode — the dispatch expected node scripts/check-skills-paths.mjs to run "self-test + live". It has no such flag; grep -n 'self-test\|selfTest' over the script returns nothing, and its only mode besides the live run is --list. The self-test is the vitest suite, and that is where the two-root cases were added.
  • premise_false: 93 stated paths come back under the gate — 88, for the arithmetic given above.
  • premise_false: the queue guard returns exit 3 GOVERNED — it does so when .claude/skills/** is in the diff, and nothing there needed correcting, so the diff is three files under scripts/. Its own verdict: ✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched. Draft and human-merged either way, as the dispatch asks.
  • premise_false: the widened surface brings stale coordinates to correct at source — 60 of 61 resolve; the single non-resolver is the deliberate negative sentence.

Gates, at 471589f

Exit codes captured by redirecting first (cmd > file 2>&1; EXIT=$?), never read across a pipe. Each row quotes the gate's own verdict line.

gateexitits own verdict
node scripts/check-skills-paths.mjs0✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
pnpm exec vitest run scripts/__tests__/check-skills-paths.test.ts0Test Files 1 passed (1) / Tests 37 passed (37)
pnpm type-check:scripts0tsc -p tsconfig.scripts.json, silent
node scripts/check-doc-links.mjs0Links are valid across 17 scan roots.
node scripts/check-control-bytes.mjs0✅ check-control-bytes: OK (scanned 6103 tracked text file(s); skipped 85 binary).
node scripts/check-shell-escape-residue.mjs0✅ check-shell-escape-residue: OK (4/4 root(s) resolved ...)
node scripts/check-changeset-presence.mjs0✅ No source or published contract of a released package changed in this range, so no changeset is owed.
node scripts/check-governed-queue-guard.mjs --self-test0OK check-governed-queue-guard self-test: 132 cases pass
node scripts/check-governed-queue-guard.mjs --test (3 changed paths)0✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched.
pnpm exec eslint (changed files)01 file linted, 0 errors, 0 warnings

No changeset, on the gate's own verdict, not on judgement: 3 file(s) changed, 0 of them published source of a package the release covers. No skip-changeset label, which this repo does not use as a mechanism, and no empty-frontmatter changeset either — the gate says none is owed.

type-check:scripts genuinely covers the edited test file, rather than excluding it and reporting a clean run about other files: tsc -p tsconfig.scripts.json --listFiles lists both scripts/check-skills-paths.mjs and scripts/__tests__/check-skills-paths.test.ts, one occurrence each.

eslint was narrowed, and the narrowing is measured rather than assumed. Population, read from eslint.config.js and not guessed: every rule block is scoped files: ['**/*.{ts,tsx}'], and no block declares an .mjs, .cjs or .json glob — so of the three changed files only the .ts test is inside the linted population at all. File count read from --format json: 1 result object, errorCount 0, warningCount 0. Invariance: the config declares neither parserOptions.project nor projectService, so linting is not type-aware and nothing in this diff can move the verdict on a file it did not touch. The full-repo pnpm lint is CI's run.

Scope

Three files, all under scripts/. Nothing under skills/objectui/**, nothing under packages/**, no other gate touched, and — because nothing needed correcting — nothing under .claude/skills/** either.

Two findings of the same class were filed unassigned rather than ridden in here; neither is addressed by this PR:

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…#7358)
SCAN_ROOT was one string, `skills`. objectui#7251 moved the two
contributor-only guides into `.claude/skills/objectui-contributor/` and
55 of 93 stated paths, 59%, left the checked surface in one commit with
nothing turning red — the gate simply stopped looking.
Make it a list, `['skills', '.claude/skills']`, and thread it through
`scan()` (a `perRoot` breakdown beside the totals), the OK line, the
`--list` footer and the empty-verdict trap.
That trap is now judged PER ROOT, which is the lesson the miss itself
taught: summed over roots, "one root reads nothing" is invisible — a
whole-surface floor stayed comfortably green on the 16 files still under
`skills` while a second tree of 55 assertions sat unscanned.
Measured, before -> after: 16 -> 20 files, 27 -> 88 stated paths checked
(console-development.md 49, no-touch-zones.md 6, objectui-contributor's
SKILL.md 5, verify/SKILL.md 1). The widened surface brings exactly one
red, cleared in the next commit.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…de (objectui#7358)
The widened surface brought exactly one red, and it is not rot: the
deliberate negative sentence in `console-development.md`'s Key contexts
section, "there is no `apps/console/src/context/` directory at all",
written to correct the recurring wrong guess that the five contexts live
in the console app. The other 60 coordinates in the two moved guides all
resolved on arrival.
This is the entry objectui#7251 was forced to delete: the baseline is
keyed by guide file, so the move retired it through the ratchet's second
direction ("1 baseline entry the scan never met ... Delete the entry").
Widening the root makes the scan meet the same sentence again, so the
entry comes back under the new key.
Granted against the gate's own prescription, quoted in the entry's
reason: "If the path is deliberately named as NOT existing -- a sentence
whose whole point is 'there is no such directory' -- add it to
scripts/skills-path-baseline.json with a reason, and expect the gate to
go red again the day that path appears on disk."
Nothing was corrected at source because nothing was stale; `ls` confirms
`apps/console/src/context/` still does not exist, which is what the
sentence says.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ken (objectui#7358)
Three restorations and one deletion, all of them named in advance by the
comments objectui#7251 left beside the weakened forms.
The `checked` floor goes back to 50, the value it dropped to 20 to
survive the move, with the arithmetic restored: 88 checked today, of
which the two moved guides carry 55. If `.claude/skills` ever falls out
of SCAN_ROOTS the reading drops to 33 and this floor is red.
Beside it, a per-root floor. A total cannot see one root go quiet: 27
under `skills` alone clears a floor of 20, which is exactly how the 55
left unnoticed.
The objectui#3713 / #3730 block asserts `result.missing` through
`scan(repoRoot)` again, on the guide's current path, and carries its
exemption pin again. The "is NOT covered by the gate today" assertion is
DELETED, as it was written to be. Its replacement is the inverse claim
rather than nothing, because `inGuide(result.missing)` is empty both
when the guide is clean and when nothing scans it: the guide's own
density is now tied to its root's checked count, so the two are no
longer confusable.
Five fixture cases cover the two-root surface: the declared roots, a
dead path found under `.claude/skills`, the `perRoot` breakdown, a root
that reads nothing showing as a zero row, and a baseline entry keyed to
a file under the second root.
31 -> 37 test cases, all passing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-litantClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (skills lane seat, session session_01LraLgQVGq8egUwfYZpbYt1): flipped ready and auto-merge enabled at 18:5xZ under landing-operations B — in-seat ACCEPT on #7358 (comment 5514343000), every check on 471589f green, and the repo's own guard reads the change set as NOT GOVERNED (three scripts/ files, nothing under skills/** or .claude/**). The base has moved since the branch was cut; the merge carries it. No human merge is owed on this face.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-skills-paths lost 55 of its 93 assertions when the contributor guides moved out of skills/ — widen SCAN_ROOT to cover .claude/skills

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358) - #7407

Merged
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root
Sep 2, 2026
Merged

chore(scripts): check-skills-paths scans .claude/skills too — 27 to 88 stated paths under the gate, the #7251 pins restored (#7358)#7407
os-litant merged 3 commits into
mainfrom
claude/issue-7358-skills-paths-scan-root

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#7358

SCAN_ROOT was one string, skills. #7251 moved the two contributor-only guides into .claude/skills/objectui-contributor/ and 55 of 93 stated paths left the checked surface in one commit with nothing turning red — the gate simply stopped looking. This makes it a list, ['skills', '.claude/skills'], clears the red the wider surface brings, and restores the pins #7251 had to weaken.

Head: 471589f. Every number below was measured on that commit unless it names another.

The measurement the docblock asks for

The script's own header requires this: "Widen it on purpose, with the measurement re-run, not as a rider." Re-run, before and after, with node scripts/check-skills-paths.mjs and --list:

before (0614b6df1)after (471589f)
files scanned1620
stated paths checked2788
resolve2787 (+1 baselined)
pattern tokens excluded14
console-development.md— (unscanned)49
no-touch-zones.md— (unscanned)6
objectui-contributor/SKILL.md— (unscanned)5
verify/SKILL.md— (unscanned)1

Per root, which the gate now prints on every green run:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
skills/ — 27/27 resolve across 16 file(s)
.claude/skills/ — 60/61 resolve across 4 file(s)

The card's headline number is 93, and the honest re-measurement is 88. Both the card's own figures cannot hold at once: it records 18 to 16 files and 93 to 27 assertions, a loss of 66, while the two moved guides carry 55 between them. The missing 11 are assertions #7251 also removed when it rewrote the published guides it kept. So widening restores 61 assertions under the new root — the 55 that left, plus 6 in two files that were never under skills at all — for 88, not 93. The PR title carries the measured number rather than the card's.

The red the widened surface brought: exactly one, and it is not rot

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:381 — apps/console/src/context/

Coordinates corrected at source: zero. Not for want of looking — the other 60 in the two moved guides all resolved on arrival. They were maintained by hand through #3713 and #3730 and had not yet gone stale, which is the case for gating them, not against.

The one non-resolver is the deliberate negative sentence in the Key contexts section, which exists to correct the recurring wrong guess that the five contexts live in the console app:

All five live in @object-ui/app-shell; there is no apps/console/src/context/ directory at all.

Evidence it is a true statement and not a stale coordinate:

$ ls -d apps/console/src/context/
ls: cannot access 'apps/console/src/context/': No such file or directory

This is the entry #7251 was forced to delete. The baseline is keyed by guide file, so the move retired it through the ratchet's second direction — the gate demanded it in its own words, "1 baseline entry the scan never met ... Delete the entry". Widening the root makes the scan meet the same sentence again, so the entry returns under the new key, granted against the gate's own prescription, quoted verbatim in the entry's reason:

If the path is deliberately named as NOT existing — a sentence whose whole point is "there is no such directory" — add it to scripts/skills-path-baseline.json with a reason, and expect the gate to go red again the day that path appears on disk.

Dated objectui#3735 (re-keyed under the moved guide path by objectui#7358, 2026-09-02). It is the only entry in the file, as it was before the move: one exemption for 88 assertions.

The lesson the miss itself taught: emptiness is judged per root

The empty-verdict trap in main() used to ask whether the whole surface read zero. That question could not have caught #7251: 16 files and 27 assertions still under skills is a healthy-looking total while a second tree of 55 sits unscanned. It now asks per root, and a root that judges nothing is named and red — either a move nobody threaded through, or a root that should be deleted from SCAN_ROOTS, and both are decisions rather than a pass. scan() returns a perRoot row per root to make that answerable at all.

The pins #7251 left to be reversed

All three were named in advance by the comments beside them.

  1. The checked floor, 20 back to 50. With the arithmetic restored, as Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 left it: 88 checked today, of which the two moved guides carry 55 (49 + 6). If .claude/skills ever falls out of SCAN_ROOTS the reading drops to 33 and the floor is red — the assertion Skills optimization flight — skills/objectui: RESTRUCTURE (≈ −21,600 tok of 64,022, −34%) — ~19% of the package is addressed at a contributor to this repo (console-development.md, no-touch-zones.md, half of architecture.md), one rule stated up to four times, 11 vestigial guide frontmatters, and three evals that fail correct answers #7251 could not make.
  2. A per-root floor beside it, because the one above is a total: 27 under skills alone clears a floor of 20, which is exactly how the 55 left unnoticed.
  3. The docs(skills): console-development.md 指路的 7 个 console 页面里 5 个不存在,并整节教用已删除的 MetadataManagerPage #3713 / docs(skills): console-development.md 的 Key contexts / Key hooks / UnifiedSidebar 三处把 13 个真实符号指到 apps/console 下已不存在的目录 #3730 block asserts result.missing through scan(repoRoot) again, on the guide's current path, and carries its exemption pin again.
  4. The "is NOT covered by the gate today" assertion is DELETED, as it was written to be. Its replacement is the inverse claim rather than nothing: inGuide(result.missing) is empty both when the guide is clean and when nothing scans it, so the guide's own stated-path density is now tied to its root's checked count and the two stop being confusable.

Five fixture cases cover the two-root surface: the declared roots, a dead path found under .claude/skills, the perRoot breakdown, a root that reads nothing showing as a zero row rather than an absence, and a baseline entry keyed under the second root. 31 to 37 cases.

Ablation — the widened root really is checked

One coordinate in the newly covered guide, mutated on the committed tree and restored, both legs proved on disk. No build or dist is involved: the gate is a node script reading markdown, so there is no artifact between the edit and the reading.

Mutation landed (packages/app-shell/src/layout/UnifiedSidebar.tsx to ...SidebarGONE.tsx, injected-token count 1, original-token count 0, on-disk hash 0cd9feb against HEAD blob 27e5f46), gate exit 1:

❌ check-skills-paths: 1 stated path does not exist
• .claude/skills/objectui-contributor/guides/console-development.md:268 — packages/app-shell/src/layout/UnifiedSidebarGONE.tsx

Restore proved, not assumed — git checkout HEAD -- ABSOLUTE_PATH under a trap ... EXIT INT TERM, then on-disk hash 27e5f46 equal to the HEAD blob, git diff HEAD --name-only empty, injected-token count 0, original-token count 1. Gate exit 0:

✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).

Premises that did not hold

  • premise_false: the gate has a --self-test mode — the dispatch expected node scripts/check-skills-paths.mjs to run "self-test + live". It has no such flag; grep -n 'self-test\|selfTest' over the script returns nothing, and its only mode besides the live run is --list. The self-test is the vitest suite, and that is where the two-root cases were added.
  • premise_false: 93 stated paths come back under the gate — 88, for the arithmetic given above.
  • premise_false: the queue guard returns exit 3 GOVERNED — it does so when .claude/skills/** is in the diff, and nothing there needed correcting, so the diff is three files under scripts/. Its own verdict: ✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched. Draft and human-merged either way, as the dispatch asks.
  • premise_false: the widened surface brings stale coordinates to correct at source — 60 of 61 resolve; the single non-resolver is the deliberate negative sentence.

Gates, at 471589f

Exit codes captured by redirecting first (cmd > file 2>&1; EXIT=$?), never read across a pipe. Each row quotes the gate's own verdict line.

gateexitits own verdict
node scripts/check-skills-paths.mjs0✅ check-skills-paths: OK (87/88 stated path(s) resolve across 20 guide file(s); 1 baselined).
pnpm exec vitest run scripts/__tests__/check-skills-paths.test.ts0Test Files 1 passed (1) / Tests 37 passed (37)
pnpm type-check:scripts0tsc -p tsconfig.scripts.json, silent
node scripts/check-doc-links.mjs0Links are valid across 17 scan roots.
node scripts/check-control-bytes.mjs0✅ check-control-bytes: OK (scanned 6103 tracked text file(s); skipped 85 binary).
node scripts/check-shell-escape-residue.mjs0✅ check-shell-escape-residue: OK (4/4 root(s) resolved ...)
node scripts/check-changeset-presence.mjs0✅ No source or published contract of a released package changed in this range, so no changeset is owed.
node scripts/check-governed-queue-guard.mjs --self-test0OK check-governed-queue-guard self-test: 132 cases pass
node scripts/check-governed-queue-guard.mjs --test (3 changed paths)0✅ NOT GOVERNED — 3 path(s) checked against 5 governed surface(s); none matched.
pnpm exec eslint (changed files)01 file linted, 0 errors, 0 warnings

No changeset, on the gate's own verdict, not on judgement: 3 file(s) changed, 0 of them published source of a package the release covers. No skip-changeset label, which this repo does not use as a mechanism, and no empty-frontmatter changeset either — the gate says none is owed.

type-check:scripts genuinely covers the edited test file, rather than excluding it and reporting a clean run about other files: tsc -p tsconfig.scripts.json --listFiles lists both scripts/check-skills-paths.mjs and scripts/__tests__/check-skills-paths.test.ts, one occurrence each.

eslint was narrowed, and the narrowing is measured rather than assumed. Population, read from eslint.config.js and not guessed: every rule block is scoped files: ['**/*.{ts,tsx}'], and no block declares an .mjs, .cjs or .json glob — so of the three changed files only the .ts test is inside the linted population at all. File count read from --format json: 1 result object, errorCount 0, warningCount 0. Invariance: the config declares neither parserOptions.project nor projectService, so linting is not type-aware and nothing in this diff can move the verdict on a file it did not touch. The full-repo pnpm lint is CI's run.

Scope

Three files, all under scripts/. Nothing under skills/objectui/**, nothing under packages/**, no other gate touched, and — because nothing needed correcting — nothing under .claude/skills/** either.

Two findings of the same class were filed unassigned rather than ridden in here; neither is addressed by this PR:

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…#7358)
SCAN_ROOT was one string, `skills`. objectui#7251 moved the two
contributor-only guides into `.claude/skills/objectui-contributor/` and
55 of 93 stated paths, 59%, left the checked surface in one commit with
nothing turning red — the gate simply stopped looking.
Make it a list, `['skills', '.claude/skills']`, and thread it through
`scan()` (a `perRoot` breakdown beside the totals), the OK line, the
`--list` footer and the empty-verdict trap.
That trap is now judged PER ROOT, which is the lesson the miss itself
taught: summed over roots, "one root reads nothing" is invisible — a
whole-surface floor stayed comfortably green on the 16 files still under
`skills` while a second tree of 55 assertions sat unscanned.
Measured, before -> after: 16 -> 20 files, 27 -> 88 stated paths checked
(console-development.md 49, no-touch-zones.md 6, objectui-contributor's
SKILL.md 5, verify/SKILL.md 1). The widened surface brings exactly one
red, cleared in the next commit.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…de (objectui#7358)
The widened surface brought exactly one red, and it is not rot: the
deliberate negative sentence in `console-development.md`'s Key contexts
section, "there is no `apps/console/src/context/` directory at all",
written to correct the recurring wrong guess that the five contexts live
in the console app. The other 60 coordinates in the two moved guides all
resolved on arrival.
This is the entry objectui#7251 was forced to delete: the baseline is
keyed by guide file, so the move retired it through the ratchet's second
direction ("1 baseline entry the scan never met ... Delete the entry").
Widening the root makes the scan meet the same sentence again, so the
entry comes back under the new key.
Granted against the gate's own prescription, quoted in the entry's
reason: "If the path is deliberately named as NOT existing -- a sentence
whose whole point is 'there is no such directory' -- add it to
scripts/skills-path-baseline.json with a reason, and expect the gate to
go red again the day that path appears on disk."
Nothing was corrected at source because nothing was stale; `ls` confirms
`apps/console/src/context/` still does not exist, which is what the
sentence says.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ken (objectui#7358)
Three restorations and one deletion, all of them named in advance by the
comments objectui#7251 left beside the weakened forms.
The `checked` floor goes back to 50, the value it dropped to 20 to
survive the move, with the arithmetic restored: 88 checked today, of
which the two moved guides carry 55. If `.claude/skills` ever falls out
of SCAN_ROOTS the reading drops to 33 and this floor is red.
Beside it, a per-root floor. A total cannot see one root go quiet: 27
under `skills` alone clears a floor of 20, which is exactly how the 55
left unnoticed.
The objectui#3713 / #3730 block asserts `result.missing` through
`scan(repoRoot)` again, on the guide's current path, and carries its
exemption pin again. The "is NOT covered by the gate today" assertion is
DELETED, as it was written to be. Its replacement is the inverse claim
rather than nothing, because `inGuide(result.missing)` is empty both
when the guide is clean and when nothing scans it: the guide's own
density is now tied to its root's checked count, so the two are no
longer confusable.
Five fixture cases cover the two-root surface: the declared roots, a
dead path found under `.claude/skills`, the `perRoot` breakdown, a root
that reads nothing showing as a zero row, and a baseline entry keyed to
a file under the second root.
31 -> 37 test cases, all passing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-litantClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (skills lane seat, session session_01LraLgQVGq8egUwfYZpbYt1): flipped ready and auto-merge enabled at 18:5xZ under landing-operations B — in-seat ACCEPT on #7358 (comment 5514343000), every check on 471589f green, and the repo's own guard reads the change set as NOT GOVERNED (three scripts/ files, nothing under skills/** or .claude/**). The base has moved since the branch was cut; the merge carries it. No human merge is owed on this face.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-skills-paths lost 55 of its 93 assertions when the contributor guides moved out of skills/ — widen SCAN_ROOT to cover .claude/skills

2 participants

@os-litant@claude