docs(skills): stop promising os validate checks a model-registry entry - #14953

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines
Sep 3, 2026
Merged

docs(skills): stop promising os validate checks a model-registry entry#14953
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14836

skills/objectstack-ai/SKILL.md told an AI author to run os validate after authoring "a model-registry entry", and then said the gate confirms "skill / tool / model metadata". A model-registry entry is not an input os validate can receive — and the same file already says so, 64 lines above. Two deletions, shrink-only, nothing else moved.

The two sentences, before and after

Before, at the base commit b1d49b3940:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts or a model-registry entry, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool / model metadata conforms to the protocol.

After, at this head:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool metadata conforms to the protocol.

Untouched, deliberately: the os validate fence comment at :400, the remaining CEL sentence at :404-407, the central-registration row at :332, skills/README.md, and every other file in the tree. No replacement validator is named and no "not currently validated" caveat is added — the fix is to stop promising, not to explain the gap.

Premise re-verified at this head — the three reads the card names

  1. packages/spec/src/stack.zod.ts — the AI collections of ObjectStackDefinitionSchema are exactly agents, tools, skills (the three z.array(...) rows under the ADR-0063 doc block). A case-insensitive grep of that file for modelRegist, knowledge or promptTemplate exits 1 with no output.
  2. ModelRegistrySchema is referenced in exactly two files across packages/**/*.ts: packages/spec/src/ai/model-registry.zod.ts (3 occurrences) and packages/spec/src/ai/model-registry.test.ts (5). No stack key, no lint rule, no CLI command reaches it.
  3. packages/cli/src/commands/validate.ts parses ObjectStackDefinitionSchemasafeParse at :187, with the two authoring-lint passes at :184-185 reading the same schema.

The contradiction partner is still present verbatim at :332: "agents / tools / skills are the only AI stack collections — knowledge sources have none, agents are platform-supplied, and tools is not the default path".

Measurement — this file, before and after

measurebeforeafterdelta
lines of skills/objectstack-ai/SKILL.md4174170
tokens, same file54755467-8
ceiling for this file, unedited680668060
headroom13311339+8
published bundle total156199156191-8

Line count is unchanged because both deletions are intra-line. The token numbers are node scripts/check-skills-token-ratchet.mjs's own output for this path:

  • before — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5475 tokens (ceiling 6806; headroom 1331).
  • after — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5467 tokens (ceiling 6806; headroom 1339).

The ceiling map is not edited: this is a shrink under an unchanged ceiling.

Judgement calls

1. No re-flow, and the wrap was measured rather than assumed. This file wraps prose at 80 columns. Line :396 shrinks from 79 to 53 characters while :397 keeps five words ("author-time gate before reporting done:", 39 characters) — not an orphan, so nothing is re-wrapped. Line :404 shrinks from 76 to 68 with a full 80-character line behind it at :405. Pulling a word up in either place would edit a line this card did not name and would cascade through the rest of the paragraph.

2. The os validate fence comment at :400 stays — it is shared wording, not this file's claim. Measured: the identical line, byte for byte, is at skills/objectstack-api/SKILL.md:411, so it describes the gate rather than this domain; and the sentence immediately after it here already tells the reader that this domain has no CEL site. Editing it in one of the two files would fork wording the published catalog shares. Recorded as an observation, not a change.

3. Nothing else in the catalog claims a model-registry entry is validated. Swept skills/** for model-registry, model registry and ModelRegistry: the remaining mentions are :301 here (a note that ModelProviderSchema accepts a wider provider set), :405 here (the template dialect sentence), skills/objectstack-formula/SKILL.md:422 (the same template dialect fact) and the generated reference pointer at skills/objectstack-ai/references/_index.md:18. None of them promises a check, so the deletion leaves no instruction stranded and no author without a route.

4. No changeset. This diff publishes nothing from any released package — one markdown file in the published catalog — so it carries skip-changeset rather than an empty changeset. check-changeset-presence states the outcome in its own verdict line on this PR.

Reverse verification

Run from the committed state, under a trap restoring the absolute path, with the mutation proved on disk before anything was measured:

  • Mutation leg — "or a model-registry entry" re-inserted at :396; grep -c on that phrase moved 0 to 1 and git hash-object moved to 3cfae14e1c, away from the HEAD blob 9f5231126e. Only then was the ratchet re-read.
  • What it shows — the file's measured count rises from 5467 to 5473 tokens, the six tokens this half of the change removes. The ratchet itself stays GREEN in that state (5473 is far under the 6806 ceiling), and that is the honest reading: this gate prices size, it does not detect the false claim. The number that moves is the file's measured token count, not the gate's verdict. Nothing in the local union can red on the sentence itself — see NOT MEASURED below.
  • Restore leggit checkout HEAD -- on the absolute path; git hash-object returns 9f5231126eab1f93aa5d1a142d4a78867cf8275f, equal to the HEAD blob, and git diff HEAD is empty. The ratchet then re-reads 5467 tokens, and git status --porcelain is clean.

Gates

The union was derived at this head rather than from a hand-written list:

dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 741e8e7071 (/home/user/objectstack-14836).
dispatch-gates: change set derived from git — 1 path(s) vs merge base b1d49b394 of 'origin/main' and HEAD
(committed 1, working tree 0, untracked 0; three-dot semantics, never 'origin/main..HEAD')
· skills/objectstack-ai/SKILL.md
dispatch-gates --commands: 21 command(s) — 14 pnpm, 7 direct node (16 matched by path, 0 by change KIND, 5 declared WHOLE-TREE and named on every card).

Exit codes were captured by redirecting stdout and stderr into a per-gate log file and reading $? on the next statement — never through a pipe, which would report the pipe's status instead. Each row quotes the gate's own verdict line.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 133 declared cross-package glob(s) (93 unique) are covered by core or crosspkg, every crosspkg entry still covers one, and the test job's if: still names both filters.
node scripts/check-closing-keyword-parity.mjs0check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 8133 tracked file(s), all registered).
node scripts/check-comment-mask-corpus.mjs0✓ comment-mask corpus sweep: 5808 files, 0 disagree, 0 unparseable, 49.9s (comparator self-test: 12 cases pass).
node scripts/check-cross-package-test-inputs.mjs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
node scripts/check-shard-attestation.mjs0✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).
node scripts/check-skills-token-ratchet.mjs0✓ check-skills-token-ratchet: 36 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.
node scripts/check-test-completeness.mjs3PREREQUISITE NOT MET — NOT MEASURED, see below.
pnpm --filter @objectstack/lint run check:doc-formula-expressions0✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. (two further ✓ legs: spec TSDoc 9 examples clean, field-level predicates 14 clean / 6 undeterminable)
pnpm --filter @objectstack/spec run check:skill-docs0✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5809 bare -- token(s) · 1388 launcher-rooted run(s) · 9 separator(s) JUDGED · 5 vitest-backed script name(s) derived from 81 manifest(s)
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
pnpm check:doc-authoring0✓ doc authoring guard: sibling-package prose ids hold the baseline — 831 pinned site(s) across 231 file(s), 83548 string(s) read in 1132 parsed source(s), no growth, no burn-down unrecorded.
pnpm check:nul-bytes0check-nul-bytes: OK (scanned 8126 text file(s) -- 8126 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions (the full enumeration is long; the run's closing line is) live: the real generator declared 9 output(s) and certified this tree
pnpm check:refd-timer-probe0OK check-refd-timer-probe: 5803 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else.
pnpm check:role-word0check-role-word: OK, no new occurrences of the reserved word.
pnpm check:skill-compatibility0✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 79 workspace packages
pnpm check:skill-frame-sync0✓ check-skill-frame-sync: 2 copies of the decision frame are structurally isomorphic across 2 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) checked against 94209 implementation word tokens (3 ledgered exemption(s)); Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s).
pnpm check:watch-hint-literal0✓ check-watch-hint-literal: 47 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 31, ROOT_FILE_WATCH_HINTS 9, ROOT_WATCH_HINTS 2, DECLARED_WATCH_HINTS 5 -- every one an array of quoted literals inside its own statement.

Two readings needed a prerequisite before they meant anything: check:doc-formula-expressions first answered PREREQUISITE NOT MET twice — @objectstack/formula not built, then @objectstack/lint not built — and both builds went through the shared verify lock (os-verify-lock: VERDICT command-exit 0, 190s and 12s held). Its exit 0 above is the reading after that, not the first one.

NOT MEASURED

  • node scripts/check-test-completeness.mjs — exit 3, PREREQUISITE NOT MET. The gate grades a saved turbo run test log; it does not run tests and cannot produce one. Its own text: "Arrived here from the gate family scripts/pm/dispatch-gates.mjs derives? That list names this script with NO argument, which is this branch. There is no local log to hand it, so the local reading for this gate is NOT MEASURED. It is not a red, and there is nothing here to fix." Supplying that prerequisite means the repo-wide test farm, which is CI's run, not this flight's. Read as neither pass nor fail.
  • No gate in the union can red on the deleted claim itself. The sentence was false about os validate in a way no checker models: check:skill-identifier-liveness grades whether cited identifiers exist (ModelRegistrySchema does exist), and the token ratchet grades size. The reverse verification above demonstrates this rather than papering over it — the mutated tree is green everywhere. The correctness of this change rests on the three source reads, not on a gate.
  • The always-runs CI tail is outside this table.dispatch-gates --commands says so in its own closing line: workflows with no path filter are not in the local union, and are read from this PR's checks.
  • Nothing was executed from this file. It is published prose; there is no runtime behaviour to exercise and no test to add or change, so the entire reading is gate-shaped by nature.

Landing

Governed surface (skills/**): this PR stays a draft. Not flipped ready, not enqueued, no auto-merge, no reviewers requested by this seat, no approving review. Labels: skip-changeset, and needs:contract-review because the deleted words are a claim about what os validate checks.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

`skills/objectstack-ai/SKILL.md` told the author to run the author-time gate
after "a model-registry entry" and said the gate confirms "skill / tool / model
metadata". Neither is a check `os validate` can run: the AI collections of
`ObjectStackDefinitionSchema` are exactly `agents` / `tools` / `skills`
(`packages/spec/src/stack.zod.ts`), `ModelRegistrySchema` is referenced only by
its own file and its own test, and `os validate` parses
`ObjectStackDefinitionSchema` (`packages/cli/src/commands/validate.ts`). The
same file already says so at its central-registration row.
Two deletions, shrink-only: "or a model-registry entry" and "/ model". No
re-wrap, no replacement validator named, no other sentence touched.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 14:01
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 5784526Sep 3, 2026
43 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14836-ai-skill-dup-lines branch September 3, 2026 14:22
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 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

docs(skills): stop promising os validate checks a model-registry entry - #14953

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines
Sep 3, 2026
Merged

docs(skills): stop promising os validate checks a model-registry entry#14953
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14836

skills/objectstack-ai/SKILL.md told an AI author to run os validate after authoring "a model-registry entry", and then said the gate confirms "skill / tool / model metadata". A model-registry entry is not an input os validate can receive — and the same file already says so, 64 lines above. Two deletions, shrink-only, nothing else moved.

The two sentences, before and after

Before, at the base commit b1d49b3940:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts or a model-registry entry, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool / model metadata conforms to the protocol.

After, at this head:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool metadata conforms to the protocol.

Untouched, deliberately: the os validate fence comment at :400, the remaining CEL sentence at :404-407, the central-registration row at :332, skills/README.md, and every other file in the tree. No replacement validator is named and no "not currently validated" caveat is added — the fix is to stop promising, not to explain the gap.

Premise re-verified at this head — the three reads the card names

  1. packages/spec/src/stack.zod.ts — the AI collections of ObjectStackDefinitionSchema are exactly agents, tools, skills (the three z.array(...) rows under the ADR-0063 doc block). A case-insensitive grep of that file for modelRegist, knowledge or promptTemplate exits 1 with no output.
  2. ModelRegistrySchema is referenced in exactly two files across packages/**/*.ts: packages/spec/src/ai/model-registry.zod.ts (3 occurrences) and packages/spec/src/ai/model-registry.test.ts (5). No stack key, no lint rule, no CLI command reaches it.
  3. packages/cli/src/commands/validate.ts parses ObjectStackDefinitionSchemasafeParse at :187, with the two authoring-lint passes at :184-185 reading the same schema.

The contradiction partner is still present verbatim at :332: "agents / tools / skills are the only AI stack collections — knowledge sources have none, agents are platform-supplied, and tools is not the default path".

Measurement — this file, before and after

measurebeforeafterdelta
lines of skills/objectstack-ai/SKILL.md4174170
tokens, same file54755467-8
ceiling for this file, unedited680668060
headroom13311339+8
published bundle total156199156191-8

Line count is unchanged because both deletions are intra-line. The token numbers are node scripts/check-skills-token-ratchet.mjs's own output for this path:

  • before — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5475 tokens (ceiling 6806; headroom 1331).
  • after — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5467 tokens (ceiling 6806; headroom 1339).

The ceiling map is not edited: this is a shrink under an unchanged ceiling.

Judgement calls

1. No re-flow, and the wrap was measured rather than assumed. This file wraps prose at 80 columns. Line :396 shrinks from 79 to 53 characters while :397 keeps five words ("author-time gate before reporting done:", 39 characters) — not an orphan, so nothing is re-wrapped. Line :404 shrinks from 76 to 68 with a full 80-character line behind it at :405. Pulling a word up in either place would edit a line this card did not name and would cascade through the rest of the paragraph.

2. The os validate fence comment at :400 stays — it is shared wording, not this file's claim. Measured: the identical line, byte for byte, is at skills/objectstack-api/SKILL.md:411, so it describes the gate rather than this domain; and the sentence immediately after it here already tells the reader that this domain has no CEL site. Editing it in one of the two files would fork wording the published catalog shares. Recorded as an observation, not a change.

3. Nothing else in the catalog claims a model-registry entry is validated. Swept skills/** for model-registry, model registry and ModelRegistry: the remaining mentions are :301 here (a note that ModelProviderSchema accepts a wider provider set), :405 here (the template dialect sentence), skills/objectstack-formula/SKILL.md:422 (the same template dialect fact) and the generated reference pointer at skills/objectstack-ai/references/_index.md:18. None of them promises a check, so the deletion leaves no instruction stranded and no author without a route.

4. No changeset. This diff publishes nothing from any released package — one markdown file in the published catalog — so it carries skip-changeset rather than an empty changeset. check-changeset-presence states the outcome in its own verdict line on this PR.

Reverse verification

Run from the committed state, under a trap restoring the absolute path, with the mutation proved on disk before anything was measured:

  • Mutation leg — "or a model-registry entry" re-inserted at :396; grep -c on that phrase moved 0 to 1 and git hash-object moved to 3cfae14e1c, away from the HEAD blob 9f5231126e. Only then was the ratchet re-read.
  • What it shows — the file's measured count rises from 5467 to 5473 tokens, the six tokens this half of the change removes. The ratchet itself stays GREEN in that state (5473 is far under the 6806 ceiling), and that is the honest reading: this gate prices size, it does not detect the false claim. The number that moves is the file's measured token count, not the gate's verdict. Nothing in the local union can red on the sentence itself — see NOT MEASURED below.
  • Restore leggit checkout HEAD -- on the absolute path; git hash-object returns 9f5231126eab1f93aa5d1a142d4a78867cf8275f, equal to the HEAD blob, and git diff HEAD is empty. The ratchet then re-reads 5467 tokens, and git status --porcelain is clean.

Gates

The union was derived at this head rather than from a hand-written list:

dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 741e8e7071 (/home/user/objectstack-14836).
dispatch-gates: change set derived from git — 1 path(s) vs merge base b1d49b394 of 'origin/main' and HEAD
(committed 1, working tree 0, untracked 0; three-dot semantics, never 'origin/main..HEAD')
· skills/objectstack-ai/SKILL.md
dispatch-gates --commands: 21 command(s) — 14 pnpm, 7 direct node (16 matched by path, 0 by change KIND, 5 declared WHOLE-TREE and named on every card).

Exit codes were captured by redirecting stdout and stderr into a per-gate log file and reading $? on the next statement — never through a pipe, which would report the pipe's status instead. Each row quotes the gate's own verdict line.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 133 declared cross-package glob(s) (93 unique) are covered by core or crosspkg, every crosspkg entry still covers one, and the test job's if: still names both filters.
node scripts/check-closing-keyword-parity.mjs0check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 8133 tracked file(s), all registered).
node scripts/check-comment-mask-corpus.mjs0✓ comment-mask corpus sweep: 5808 files, 0 disagree, 0 unparseable, 49.9s (comparator self-test: 12 cases pass).
node scripts/check-cross-package-test-inputs.mjs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
node scripts/check-shard-attestation.mjs0✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).
node scripts/check-skills-token-ratchet.mjs0✓ check-skills-token-ratchet: 36 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.
node scripts/check-test-completeness.mjs3PREREQUISITE NOT MET — NOT MEASURED, see below.
pnpm --filter @objectstack/lint run check:doc-formula-expressions0✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. (two further ✓ legs: spec TSDoc 9 examples clean, field-level predicates 14 clean / 6 undeterminable)
pnpm --filter @objectstack/spec run check:skill-docs0✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5809 bare -- token(s) · 1388 launcher-rooted run(s) · 9 separator(s) JUDGED · 5 vitest-backed script name(s) derived from 81 manifest(s)
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
pnpm check:doc-authoring0✓ doc authoring guard: sibling-package prose ids hold the baseline — 831 pinned site(s) across 231 file(s), 83548 string(s) read in 1132 parsed source(s), no growth, no burn-down unrecorded.
pnpm check:nul-bytes0check-nul-bytes: OK (scanned 8126 text file(s) -- 8126 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions (the full enumeration is long; the run's closing line is) live: the real generator declared 9 output(s) and certified this tree
pnpm check:refd-timer-probe0OK check-refd-timer-probe: 5803 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else.
pnpm check:role-word0check-role-word: OK, no new occurrences of the reserved word.
pnpm check:skill-compatibility0✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 79 workspace packages
pnpm check:skill-frame-sync0✓ check-skill-frame-sync: 2 copies of the decision frame are structurally isomorphic across 2 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) checked against 94209 implementation word tokens (3 ledgered exemption(s)); Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s).
pnpm check:watch-hint-literal0✓ check-watch-hint-literal: 47 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 31, ROOT_FILE_WATCH_HINTS 9, ROOT_WATCH_HINTS 2, DECLARED_WATCH_HINTS 5 -- every one an array of quoted literals inside its own statement.

Two readings needed a prerequisite before they meant anything: check:doc-formula-expressions first answered PREREQUISITE NOT MET twice — @objectstack/formula not built, then @objectstack/lint not built — and both builds went through the shared verify lock (os-verify-lock: VERDICT command-exit 0, 190s and 12s held). Its exit 0 above is the reading after that, not the first one.

NOT MEASURED

  • node scripts/check-test-completeness.mjs — exit 3, PREREQUISITE NOT MET. The gate grades a saved turbo run test log; it does not run tests and cannot produce one. Its own text: "Arrived here from the gate family scripts/pm/dispatch-gates.mjs derives? That list names this script with NO argument, which is this branch. There is no local log to hand it, so the local reading for this gate is NOT MEASURED. It is not a red, and there is nothing here to fix." Supplying that prerequisite means the repo-wide test farm, which is CI's run, not this flight's. Read as neither pass nor fail.
  • No gate in the union can red on the deleted claim itself. The sentence was false about os validate in a way no checker models: check:skill-identifier-liveness grades whether cited identifiers exist (ModelRegistrySchema does exist), and the token ratchet grades size. The reverse verification above demonstrates this rather than papering over it — the mutated tree is green everywhere. The correctness of this change rests on the three source reads, not on a gate.
  • The always-runs CI tail is outside this table.dispatch-gates --commands says so in its own closing line: workflows with no path filter are not in the local union, and are read from this PR's checks.
  • Nothing was executed from this file. It is published prose; there is no runtime behaviour to exercise and no test to add or change, so the entire reading is gate-shaped by nature.

Landing

Governed surface (skills/**): this PR stays a draft. Not flipped ready, not enqueued, no auto-merge, no reviewers requested by this seat, no approving review. Labels: skip-changeset, and needs:contract-review because the deleted words are a claim about what os validate checks.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

`skills/objectstack-ai/SKILL.md` told the author to run the author-time gate
after "a model-registry entry" and said the gate confirms "skill / tool / model
metadata". Neither is a check `os validate` can run: the AI collections of
`ObjectStackDefinitionSchema` are exactly `agents` / `tools` / `skills`
(`packages/spec/src/stack.zod.ts`), `ModelRegistrySchema` is referenced only by
its own file and its own test, and `os validate` parses
`ObjectStackDefinitionSchema` (`packages/cli/src/commands/validate.ts`). The
same file already says so at its central-registration row.
Two deletions, shrink-only: "or a model-registry entry" and "/ model". No
re-wrap, no replacement validator named, no other sentence touched.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 14:01
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 5784526Sep 3, 2026
43 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14836-ai-skill-dup-lines branch September 3, 2026 14:22
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@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

docs(skills): stop promising os validate checks a model-registry entry - #14953

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines
Sep 3, 2026
Merged

docs(skills): stop promising os validate checks a model-registry entry#14953
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14836

skills/objectstack-ai/SKILL.md told an AI author to run os validate after authoring "a model-registry entry", and then said the gate confirms "skill / tool / model metadata". A model-registry entry is not an input os validate can receive — and the same file already says so, 64 lines above. Two deletions, shrink-only, nothing else moved.

The two sentences, before and after

Before, at the base commit b1d49b3940:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts or a model-registry entry, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool / model metadata conforms to the protocol.

After, at this head:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool metadata conforms to the protocol.

Untouched, deliberately: the os validate fence comment at :400, the remaining CEL sentence at :404-407, the central-registration row at :332, skills/README.md, and every other file in the tree. No replacement validator is named and no "not currently validated" caveat is added — the fix is to stop promising, not to explain the gap.

Premise re-verified at this head — the three reads the card names

  1. packages/spec/src/stack.zod.ts — the AI collections of ObjectStackDefinitionSchema are exactly agents, tools, skills (the three z.array(...) rows under the ADR-0063 doc block). A case-insensitive grep of that file for modelRegist, knowledge or promptTemplate exits 1 with no output.
  2. ModelRegistrySchema is referenced in exactly two files across packages/**/*.ts: packages/spec/src/ai/model-registry.zod.ts (3 occurrences) and packages/spec/src/ai/model-registry.test.ts (5). No stack key, no lint rule, no CLI command reaches it.
  3. packages/cli/src/commands/validate.ts parses ObjectStackDefinitionSchemasafeParse at :187, with the two authoring-lint passes at :184-185 reading the same schema.

The contradiction partner is still present verbatim at :332: "agents / tools / skills are the only AI stack collections — knowledge sources have none, agents are platform-supplied, and tools is not the default path".

Measurement — this file, before and after

measurebeforeafterdelta
lines of skills/objectstack-ai/SKILL.md4174170
tokens, same file54755467-8
ceiling for this file, unedited680668060
headroom13311339+8
published bundle total156199156191-8

Line count is unchanged because both deletions are intra-line. The token numbers are node scripts/check-skills-token-ratchet.mjs's own output for this path:

  • before — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5475 tokens (ceiling 6806; headroom 1331).
  • after — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5467 tokens (ceiling 6806; headroom 1339).

The ceiling map is not edited: this is a shrink under an unchanged ceiling.

Judgement calls

1. No re-flow, and the wrap was measured rather than assumed. This file wraps prose at 80 columns. Line :396 shrinks from 79 to 53 characters while :397 keeps five words ("author-time gate before reporting done:", 39 characters) — not an orphan, so nothing is re-wrapped. Line :404 shrinks from 76 to 68 with a full 80-character line behind it at :405. Pulling a word up in either place would edit a line this card did not name and would cascade through the rest of the paragraph.

2. The os validate fence comment at :400 stays — it is shared wording, not this file's claim. Measured: the identical line, byte for byte, is at skills/objectstack-api/SKILL.md:411, so it describes the gate rather than this domain; and the sentence immediately after it here already tells the reader that this domain has no CEL site. Editing it in one of the two files would fork wording the published catalog shares. Recorded as an observation, not a change.

3. Nothing else in the catalog claims a model-registry entry is validated. Swept skills/** for model-registry, model registry and ModelRegistry: the remaining mentions are :301 here (a note that ModelProviderSchema accepts a wider provider set), :405 here (the template dialect sentence), skills/objectstack-formula/SKILL.md:422 (the same template dialect fact) and the generated reference pointer at skills/objectstack-ai/references/_index.md:18. None of them promises a check, so the deletion leaves no instruction stranded and no author without a route.

4. No changeset. This diff publishes nothing from any released package — one markdown file in the published catalog — so it carries skip-changeset rather than an empty changeset. check-changeset-presence states the outcome in its own verdict line on this PR.

Reverse verification

Run from the committed state, under a trap restoring the absolute path, with the mutation proved on disk before anything was measured:

  • Mutation leg — "or a model-registry entry" re-inserted at :396; grep -c on that phrase moved 0 to 1 and git hash-object moved to 3cfae14e1c, away from the HEAD blob 9f5231126e. Only then was the ratchet re-read.
  • What it shows — the file's measured count rises from 5467 to 5473 tokens, the six tokens this half of the change removes. The ratchet itself stays GREEN in that state (5473 is far under the 6806 ceiling), and that is the honest reading: this gate prices size, it does not detect the false claim. The number that moves is the file's measured token count, not the gate's verdict. Nothing in the local union can red on the sentence itself — see NOT MEASURED below.
  • Restore leggit checkout HEAD -- on the absolute path; git hash-object returns 9f5231126eab1f93aa5d1a142d4a78867cf8275f, equal to the HEAD blob, and git diff HEAD is empty. The ratchet then re-reads 5467 tokens, and git status --porcelain is clean.

Gates

The union was derived at this head rather than from a hand-written list:

dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 741e8e7071 (/home/user/objectstack-14836).
dispatch-gates: change set derived from git — 1 path(s) vs merge base b1d49b394 of 'origin/main' and HEAD
(committed 1, working tree 0, untracked 0; three-dot semantics, never 'origin/main..HEAD')
· skills/objectstack-ai/SKILL.md
dispatch-gates --commands: 21 command(s) — 14 pnpm, 7 direct node (16 matched by path, 0 by change KIND, 5 declared WHOLE-TREE and named on every card).

Exit codes were captured by redirecting stdout and stderr into a per-gate log file and reading $? on the next statement — never through a pipe, which would report the pipe's status instead. Each row quotes the gate's own verdict line.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 133 declared cross-package glob(s) (93 unique) are covered by core or crosspkg, every crosspkg entry still covers one, and the test job's if: still names both filters.
node scripts/check-closing-keyword-parity.mjs0check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 8133 tracked file(s), all registered).
node scripts/check-comment-mask-corpus.mjs0✓ comment-mask corpus sweep: 5808 files, 0 disagree, 0 unparseable, 49.9s (comparator self-test: 12 cases pass).
node scripts/check-cross-package-test-inputs.mjs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
node scripts/check-shard-attestation.mjs0✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).
node scripts/check-skills-token-ratchet.mjs0✓ check-skills-token-ratchet: 36 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.
node scripts/check-test-completeness.mjs3PREREQUISITE NOT MET — NOT MEASURED, see below.
pnpm --filter @objectstack/lint run check:doc-formula-expressions0✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. (two further ✓ legs: spec TSDoc 9 examples clean, field-level predicates 14 clean / 6 undeterminable)
pnpm --filter @objectstack/spec run check:skill-docs0✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5809 bare -- token(s) · 1388 launcher-rooted run(s) · 9 separator(s) JUDGED · 5 vitest-backed script name(s) derived from 81 manifest(s)
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
pnpm check:doc-authoring0✓ doc authoring guard: sibling-package prose ids hold the baseline — 831 pinned site(s) across 231 file(s), 83548 string(s) read in 1132 parsed source(s), no growth, no burn-down unrecorded.
pnpm check:nul-bytes0check-nul-bytes: OK (scanned 8126 text file(s) -- 8126 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions (the full enumeration is long; the run's closing line is) live: the real generator declared 9 output(s) and certified this tree
pnpm check:refd-timer-probe0OK check-refd-timer-probe: 5803 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else.
pnpm check:role-word0check-role-word: OK, no new occurrences of the reserved word.
pnpm check:skill-compatibility0✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 79 workspace packages
pnpm check:skill-frame-sync0✓ check-skill-frame-sync: 2 copies of the decision frame are structurally isomorphic across 2 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) checked against 94209 implementation word tokens (3 ledgered exemption(s)); Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s).
pnpm check:watch-hint-literal0✓ check-watch-hint-literal: 47 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 31, ROOT_FILE_WATCH_HINTS 9, ROOT_WATCH_HINTS 2, DECLARED_WATCH_HINTS 5 -- every one an array of quoted literals inside its own statement.

Two readings needed a prerequisite before they meant anything: check:doc-formula-expressions first answered PREREQUISITE NOT MET twice — @objectstack/formula not built, then @objectstack/lint not built — and both builds went through the shared verify lock (os-verify-lock: VERDICT command-exit 0, 190s and 12s held). Its exit 0 above is the reading after that, not the first one.

NOT MEASURED

  • node scripts/check-test-completeness.mjs — exit 3, PREREQUISITE NOT MET. The gate grades a saved turbo run test log; it does not run tests and cannot produce one. Its own text: "Arrived here from the gate family scripts/pm/dispatch-gates.mjs derives? That list names this script with NO argument, which is this branch. There is no local log to hand it, so the local reading for this gate is NOT MEASURED. It is not a red, and there is nothing here to fix." Supplying that prerequisite means the repo-wide test farm, which is CI's run, not this flight's. Read as neither pass nor fail.
  • No gate in the union can red on the deleted claim itself. The sentence was false about os validate in a way no checker models: check:skill-identifier-liveness grades whether cited identifiers exist (ModelRegistrySchema does exist), and the token ratchet grades size. The reverse verification above demonstrates this rather than papering over it — the mutated tree is green everywhere. The correctness of this change rests on the three source reads, not on a gate.
  • The always-runs CI tail is outside this table.dispatch-gates --commands says so in its own closing line: workflows with no path filter are not in the local union, and are read from this PR's checks.
  • Nothing was executed from this file. It is published prose; there is no runtime behaviour to exercise and no test to add or change, so the entire reading is gate-shaped by nature.

Landing

Governed surface (skills/**): this PR stays a draft. Not flipped ready, not enqueued, no auto-merge, no reviewers requested by this seat, no approving review. Labels: skip-changeset, and needs:contract-review because the deleted words are a claim about what os validate checks.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

`skills/objectstack-ai/SKILL.md` told the author to run the author-time gate
after "a model-registry entry" and said the gate confirms "skill / tool / model
metadata". Neither is a check `os validate` can run: the AI collections of
`ObjectStackDefinitionSchema` are exactly `agents` / `tools` / `skills`
(`packages/spec/src/stack.zod.ts`), `ModelRegistrySchema` is referenced only by
its own file and its own test, and `os validate` parses
`ObjectStackDefinitionSchema` (`packages/cli/src/commands/validate.ts`). The
same file already says so at its central-registration row.
Two deletions, shrink-only: "or a model-registry entry" and "/ model". No
re-wrap, no replacement validator named, no other sentence touched.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 14:01
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 5784526Sep 3, 2026
43 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14836-ai-skill-dup-lines branch September 3, 2026 14:22
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@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 > 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

docs(skills): stop promising os validate checks a model-registry entry - #14953

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines
Sep 3, 2026
Merged

docs(skills): stop promising os validate checks a model-registry entry#14953
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14836

skills/objectstack-ai/SKILL.md told an AI author to run os validate after authoring "a model-registry entry", and then said the gate confirms "skill / tool / model metadata". A model-registry entry is not an input os validate can receive — and the same file already says so, 64 lines above. Two deletions, shrink-only, nothing else moved.

The two sentences, before and after

Before, at the base commit b1d49b3940:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts or a model-registry entry, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool / model metadata conforms to the protocol.

After, at this head:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool metadata conforms to the protocol.

Untouched, deliberately: the os validate fence comment at :400, the remaining CEL sentence at :404-407, the central-registration row at :332, skills/README.md, and every other file in the tree. No replacement validator is named and no "not currently validated" caveat is added — the fix is to stop promising, not to explain the gap.

Premise re-verified at this head — the three reads the card names

  1. packages/spec/src/stack.zod.ts — the AI collections of ObjectStackDefinitionSchema are exactly agents, tools, skills (the three z.array(...) rows under the ADR-0063 doc block). A case-insensitive grep of that file for modelRegist, knowledge or promptTemplate exits 1 with no output.
  2. ModelRegistrySchema is referenced in exactly two files across packages/**/*.ts: packages/spec/src/ai/model-registry.zod.ts (3 occurrences) and packages/spec/src/ai/model-registry.test.ts (5). No stack key, no lint rule, no CLI command reaches it.
  3. packages/cli/src/commands/validate.ts parses ObjectStackDefinitionSchemasafeParse at :187, with the two authoring-lint passes at :184-185 reading the same schema.

The contradiction partner is still present verbatim at :332: "agents / tools / skills are the only AI stack collections — knowledge sources have none, agents are platform-supplied, and tools is not the default path".

Measurement — this file, before and after

measurebeforeafterdelta
lines of skills/objectstack-ai/SKILL.md4174170
tokens, same file54755467-8
ceiling for this file, unedited680668060
headroom13311339+8
published bundle total156199156191-8

Line count is unchanged because both deletions are intra-line. The token numbers are node scripts/check-skills-token-ratchet.mjs's own output for this path:

  • before — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5475 tokens (ceiling 6806; headroom 1331).
  • after — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5467 tokens (ceiling 6806; headroom 1339).

The ceiling map is not edited: this is a shrink under an unchanged ceiling.

Judgement calls

1. No re-flow, and the wrap was measured rather than assumed. This file wraps prose at 80 columns. Line :396 shrinks from 79 to 53 characters while :397 keeps five words ("author-time gate before reporting done:", 39 characters) — not an orphan, so nothing is re-wrapped. Line :404 shrinks from 76 to 68 with a full 80-character line behind it at :405. Pulling a word up in either place would edit a line this card did not name and would cascade through the rest of the paragraph.

2. The os validate fence comment at :400 stays — it is shared wording, not this file's claim. Measured: the identical line, byte for byte, is at skills/objectstack-api/SKILL.md:411, so it describes the gate rather than this domain; and the sentence immediately after it here already tells the reader that this domain has no CEL site. Editing it in one of the two files would fork wording the published catalog shares. Recorded as an observation, not a change.

3. Nothing else in the catalog claims a model-registry entry is validated. Swept skills/** for model-registry, model registry and ModelRegistry: the remaining mentions are :301 here (a note that ModelProviderSchema accepts a wider provider set), :405 here (the template dialect sentence), skills/objectstack-formula/SKILL.md:422 (the same template dialect fact) and the generated reference pointer at skills/objectstack-ai/references/_index.md:18. None of them promises a check, so the deletion leaves no instruction stranded and no author without a route.

4. No changeset. This diff publishes nothing from any released package — one markdown file in the published catalog — so it carries skip-changeset rather than an empty changeset. check-changeset-presence states the outcome in its own verdict line on this PR.

Reverse verification

Run from the committed state, under a trap restoring the absolute path, with the mutation proved on disk before anything was measured:

  • Mutation leg — "or a model-registry entry" re-inserted at :396; grep -c on that phrase moved 0 to 1 and git hash-object moved to 3cfae14e1c, away from the HEAD blob 9f5231126e. Only then was the ratchet re-read.
  • What it shows — the file's measured count rises from 5467 to 5473 tokens, the six tokens this half of the change removes. The ratchet itself stays GREEN in that state (5473 is far under the 6806 ceiling), and that is the honest reading: this gate prices size, it does not detect the false claim. The number that moves is the file's measured token count, not the gate's verdict. Nothing in the local union can red on the sentence itself — see NOT MEASURED below.
  • Restore leggit checkout HEAD -- on the absolute path; git hash-object returns 9f5231126eab1f93aa5d1a142d4a78867cf8275f, equal to the HEAD blob, and git diff HEAD is empty. The ratchet then re-reads 5467 tokens, and git status --porcelain is clean.

Gates

The union was derived at this head rather than from a hand-written list:

dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 741e8e7071 (/home/user/objectstack-14836).
dispatch-gates: change set derived from git — 1 path(s) vs merge base b1d49b394 of 'origin/main' and HEAD
(committed 1, working tree 0, untracked 0; three-dot semantics, never 'origin/main..HEAD')
· skills/objectstack-ai/SKILL.md
dispatch-gates --commands: 21 command(s) — 14 pnpm, 7 direct node (16 matched by path, 0 by change KIND, 5 declared WHOLE-TREE and named on every card).

Exit codes were captured by redirecting stdout and stderr into a per-gate log file and reading $? on the next statement — never through a pipe, which would report the pipe's status instead. Each row quotes the gate's own verdict line.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 133 declared cross-package glob(s) (93 unique) are covered by core or crosspkg, every crosspkg entry still covers one, and the test job's if: still names both filters.
node scripts/check-closing-keyword-parity.mjs0check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 8133 tracked file(s), all registered).
node scripts/check-comment-mask-corpus.mjs0✓ comment-mask corpus sweep: 5808 files, 0 disagree, 0 unparseable, 49.9s (comparator self-test: 12 cases pass).
node scripts/check-cross-package-test-inputs.mjs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
node scripts/check-shard-attestation.mjs0✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).
node scripts/check-skills-token-ratchet.mjs0✓ check-skills-token-ratchet: 36 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.
node scripts/check-test-completeness.mjs3PREREQUISITE NOT MET — NOT MEASURED, see below.
pnpm --filter @objectstack/lint run check:doc-formula-expressions0✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. (two further ✓ legs: spec TSDoc 9 examples clean, field-level predicates 14 clean / 6 undeterminable)
pnpm --filter @objectstack/spec run check:skill-docs0✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5809 bare -- token(s) · 1388 launcher-rooted run(s) · 9 separator(s) JUDGED · 5 vitest-backed script name(s) derived from 81 manifest(s)
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
pnpm check:doc-authoring0✓ doc authoring guard: sibling-package prose ids hold the baseline — 831 pinned site(s) across 231 file(s), 83548 string(s) read in 1132 parsed source(s), no growth, no burn-down unrecorded.
pnpm check:nul-bytes0check-nul-bytes: OK (scanned 8126 text file(s) -- 8126 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions (the full enumeration is long; the run's closing line is) live: the real generator declared 9 output(s) and certified this tree
pnpm check:refd-timer-probe0OK check-refd-timer-probe: 5803 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else.
pnpm check:role-word0check-role-word: OK, no new occurrences of the reserved word.
pnpm check:skill-compatibility0✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 79 workspace packages
pnpm check:skill-frame-sync0✓ check-skill-frame-sync: 2 copies of the decision frame are structurally isomorphic across 2 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) checked against 94209 implementation word tokens (3 ledgered exemption(s)); Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s).
pnpm check:watch-hint-literal0✓ check-watch-hint-literal: 47 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 31, ROOT_FILE_WATCH_HINTS 9, ROOT_WATCH_HINTS 2, DECLARED_WATCH_HINTS 5 -- every one an array of quoted literals inside its own statement.

Two readings needed a prerequisite before they meant anything: check:doc-formula-expressions first answered PREREQUISITE NOT MET twice — @objectstack/formula not built, then @objectstack/lint not built — and both builds went through the shared verify lock (os-verify-lock: VERDICT command-exit 0, 190s and 12s held). Its exit 0 above is the reading after that, not the first one.

NOT MEASURED

  • node scripts/check-test-completeness.mjs — exit 3, PREREQUISITE NOT MET. The gate grades a saved turbo run test log; it does not run tests and cannot produce one. Its own text: "Arrived here from the gate family scripts/pm/dispatch-gates.mjs derives? That list names this script with NO argument, which is this branch. There is no local log to hand it, so the local reading for this gate is NOT MEASURED. It is not a red, and there is nothing here to fix." Supplying that prerequisite means the repo-wide test farm, which is CI's run, not this flight's. Read as neither pass nor fail.
  • No gate in the union can red on the deleted claim itself. The sentence was false about os validate in a way no checker models: check:skill-identifier-liveness grades whether cited identifiers exist (ModelRegistrySchema does exist), and the token ratchet grades size. The reverse verification above demonstrates this rather than papering over it — the mutated tree is green everywhere. The correctness of this change rests on the three source reads, not on a gate.
  • The always-runs CI tail is outside this table.dispatch-gates --commands says so in its own closing line: workflows with no path filter are not in the local union, and are read from this PR's checks.
  • Nothing was executed from this file. It is published prose; there is no runtime behaviour to exercise and no test to add or change, so the entire reading is gate-shaped by nature.

Landing

Governed surface (skills/**): this PR stays a draft. Not flipped ready, not enqueued, no auto-merge, no reviewers requested by this seat, no approving review. Labels: skip-changeset, and needs:contract-review because the deleted words are a claim about what os validate checks.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

`skills/objectstack-ai/SKILL.md` told the author to run the author-time gate
after "a model-registry entry" and said the gate confirms "skill / tool / model
metadata". Neither is a check `os validate` can run: the AI collections of
`ObjectStackDefinitionSchema` are exactly `agents` / `tools` / `skills`
(`packages/spec/src/stack.zod.ts`), `ModelRegistrySchema` is referenced only by
its own file and its own test, and `os validate` parses
`ObjectStackDefinitionSchema` (`packages/cli/src/commands/validate.ts`). The
same file already says so at its central-registration row.
Two deletions, shrink-only: "or a model-registry entry" and "/ model". No
re-wrap, no replacement validator named, no other sentence touched.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 14:01
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 5784526Sep 3, 2026
43 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14836-ai-skill-dup-lines branch September 3, 2026 14:22
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@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

docs(skills): stop promising os validate checks a model-registry entry - #14953

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines
Sep 3, 2026
Merged

docs(skills): stop promising os validate checks a model-registry entry#14953
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14836

skills/objectstack-ai/SKILL.md told an AI author to run os validate after authoring "a model-registry entry", and then said the gate confirms "skill / tool / model metadata". A model-registry entry is not an input os validate can receive — and the same file already says so, 64 lines above. Two deletions, shrink-only, nothing else moved.

The two sentences, before and after

Before, at the base commit b1d49b3940:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts or a model-registry entry, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool / model metadata conforms to the protocol.

After, at this head:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool metadata conforms to the protocol.

Untouched, deliberately: the os validate fence comment at :400, the remaining CEL sentence at :404-407, the central-registration row at :332, skills/README.md, and every other file in the tree. No replacement validator is named and no "not currently validated" caveat is added — the fix is to stop promising, not to explain the gap.

Premise re-verified at this head — the three reads the card names

  1. packages/spec/src/stack.zod.ts — the AI collections of ObjectStackDefinitionSchema are exactly agents, tools, skills (the three z.array(...) rows under the ADR-0063 doc block). A case-insensitive grep of that file for modelRegist, knowledge or promptTemplate exits 1 with no output.
  2. ModelRegistrySchema is referenced in exactly two files across packages/**/*.ts: packages/spec/src/ai/model-registry.zod.ts (3 occurrences) and packages/spec/src/ai/model-registry.test.ts (5). No stack key, no lint rule, no CLI command reaches it.
  3. packages/cli/src/commands/validate.ts parses ObjectStackDefinitionSchemasafeParse at :187, with the two authoring-lint passes at :184-185 reading the same schema.

The contradiction partner is still present verbatim at :332: "agents / tools / skills are the only AI stack collections — knowledge sources have none, agents are platform-supplied, and tools is not the default path".

Measurement — this file, before and after

measurebeforeafterdelta
lines of skills/objectstack-ai/SKILL.md4174170
tokens, same file54755467-8
ceiling for this file, unedited680668060
headroom13311339+8
published bundle total156199156191-8

Line count is unchanged because both deletions are intra-line. The token numbers are node scripts/check-skills-token-ratchet.mjs's own output for this path:

  • before — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5475 tokens (ceiling 6806; headroom 1331).
  • after — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5467 tokens (ceiling 6806; headroom 1339).

The ceiling map is not edited: this is a shrink under an unchanged ceiling.

Judgement calls

1. No re-flow, and the wrap was measured rather than assumed. This file wraps prose at 80 columns. Line :396 shrinks from 79 to 53 characters while :397 keeps five words ("author-time gate before reporting done:", 39 characters) — not an orphan, so nothing is re-wrapped. Line :404 shrinks from 76 to 68 with a full 80-character line behind it at :405. Pulling a word up in either place would edit a line this card did not name and would cascade through the rest of the paragraph.

2. The os validate fence comment at :400 stays — it is shared wording, not this file's claim. Measured: the identical line, byte for byte, is at skills/objectstack-api/SKILL.md:411, so it describes the gate rather than this domain; and the sentence immediately after it here already tells the reader that this domain has no CEL site. Editing it in one of the two files would fork wording the published catalog shares. Recorded as an observation, not a change.

3. Nothing else in the catalog claims a model-registry entry is validated. Swept skills/** for model-registry, model registry and ModelRegistry: the remaining mentions are :301 here (a note that ModelProviderSchema accepts a wider provider set), :405 here (the template dialect sentence), skills/objectstack-formula/SKILL.md:422 (the same template dialect fact) and the generated reference pointer at skills/objectstack-ai/references/_index.md:18. None of them promises a check, so the deletion leaves no instruction stranded and no author without a route.

4. No changeset. This diff publishes nothing from any released package — one markdown file in the published catalog — so it carries skip-changeset rather than an empty changeset. check-changeset-presence states the outcome in its own verdict line on this PR.

Reverse verification

Run from the committed state, under a trap restoring the absolute path, with the mutation proved on disk before anything was measured:

  • Mutation leg — "or a model-registry entry" re-inserted at :396; grep -c on that phrase moved 0 to 1 and git hash-object moved to 3cfae14e1c, away from the HEAD blob 9f5231126e. Only then was the ratchet re-read.
  • What it shows — the file's measured count rises from 5467 to 5473 tokens, the six tokens this half of the change removes. The ratchet itself stays GREEN in that state (5473 is far under the 6806 ceiling), and that is the honest reading: this gate prices size, it does not detect the false claim. The number that moves is the file's measured token count, not the gate's verdict. Nothing in the local union can red on the sentence itself — see NOT MEASURED below.
  • Restore leggit checkout HEAD -- on the absolute path; git hash-object returns 9f5231126eab1f93aa5d1a142d4a78867cf8275f, equal to the HEAD blob, and git diff HEAD is empty. The ratchet then re-reads 5467 tokens, and git status --porcelain is clean.

Gates

The union was derived at this head rather than from a hand-written list:

dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 741e8e7071 (/home/user/objectstack-14836).
dispatch-gates: change set derived from git — 1 path(s) vs merge base b1d49b394 of 'origin/main' and HEAD
(committed 1, working tree 0, untracked 0; three-dot semantics, never 'origin/main..HEAD')
· skills/objectstack-ai/SKILL.md
dispatch-gates --commands: 21 command(s) — 14 pnpm, 7 direct node (16 matched by path, 0 by change KIND, 5 declared WHOLE-TREE and named on every card).

Exit codes were captured by redirecting stdout and stderr into a per-gate log file and reading $? on the next statement — never through a pipe, which would report the pipe's status instead. Each row quotes the gate's own verdict line.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 133 declared cross-package glob(s) (93 unique) are covered by core or crosspkg, every crosspkg entry still covers one, and the test job's if: still names both filters.
node scripts/check-closing-keyword-parity.mjs0check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 8133 tracked file(s), all registered).
node scripts/check-comment-mask-corpus.mjs0✓ comment-mask corpus sweep: 5808 files, 0 disagree, 0 unparseable, 49.9s (comparator self-test: 12 cases pass).
node scripts/check-cross-package-test-inputs.mjs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
node scripts/check-shard-attestation.mjs0✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).
node scripts/check-skills-token-ratchet.mjs0✓ check-skills-token-ratchet: 36 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.
node scripts/check-test-completeness.mjs3PREREQUISITE NOT MET — NOT MEASURED, see below.
pnpm --filter @objectstack/lint run check:doc-formula-expressions0✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. (two further ✓ legs: spec TSDoc 9 examples clean, field-level predicates 14 clean / 6 undeterminable)
pnpm --filter @objectstack/spec run check:skill-docs0✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5809 bare -- token(s) · 1388 launcher-rooted run(s) · 9 separator(s) JUDGED · 5 vitest-backed script name(s) derived from 81 manifest(s)
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
pnpm check:doc-authoring0✓ doc authoring guard: sibling-package prose ids hold the baseline — 831 pinned site(s) across 231 file(s), 83548 string(s) read in 1132 parsed source(s), no growth, no burn-down unrecorded.
pnpm check:nul-bytes0check-nul-bytes: OK (scanned 8126 text file(s) -- 8126 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions (the full enumeration is long; the run's closing line is) live: the real generator declared 9 output(s) and certified this tree
pnpm check:refd-timer-probe0OK check-refd-timer-probe: 5803 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else.
pnpm check:role-word0check-role-word: OK, no new occurrences of the reserved word.
pnpm check:skill-compatibility0✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 79 workspace packages
pnpm check:skill-frame-sync0✓ check-skill-frame-sync: 2 copies of the decision frame are structurally isomorphic across 2 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) checked against 94209 implementation word tokens (3 ledgered exemption(s)); Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s).
pnpm check:watch-hint-literal0✓ check-watch-hint-literal: 47 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 31, ROOT_FILE_WATCH_HINTS 9, ROOT_WATCH_HINTS 2, DECLARED_WATCH_HINTS 5 -- every one an array of quoted literals inside its own statement.

Two readings needed a prerequisite before they meant anything: check:doc-formula-expressions first answered PREREQUISITE NOT MET twice — @objectstack/formula not built, then @objectstack/lint not built — and both builds went through the shared verify lock (os-verify-lock: VERDICT command-exit 0, 190s and 12s held). Its exit 0 above is the reading after that, not the first one.

NOT MEASURED

  • node scripts/check-test-completeness.mjs — exit 3, PREREQUISITE NOT MET. The gate grades a saved turbo run test log; it does not run tests and cannot produce one. Its own text: "Arrived here from the gate family scripts/pm/dispatch-gates.mjs derives? That list names this script with NO argument, which is this branch. There is no local log to hand it, so the local reading for this gate is NOT MEASURED. It is not a red, and there is nothing here to fix." Supplying that prerequisite means the repo-wide test farm, which is CI's run, not this flight's. Read as neither pass nor fail.
  • No gate in the union can red on the deleted claim itself. The sentence was false about os validate in a way no checker models: check:skill-identifier-liveness grades whether cited identifiers exist (ModelRegistrySchema does exist), and the token ratchet grades size. The reverse verification above demonstrates this rather than papering over it — the mutated tree is green everywhere. The correctness of this change rests on the three source reads, not on a gate.
  • The always-runs CI tail is outside this table.dispatch-gates --commands says so in its own closing line: workflows with no path filter are not in the local union, and are read from this PR's checks.
  • Nothing was executed from this file. It is published prose; there is no runtime behaviour to exercise and no test to add or change, so the entire reading is gate-shaped by nature.

Landing

Governed surface (skills/**): this PR stays a draft. Not flipped ready, not enqueued, no auto-merge, no reviewers requested by this seat, no approving review. Labels: skip-changeset, and needs:contract-review because the deleted words are a claim about what os validate checks.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

`skills/objectstack-ai/SKILL.md` told the author to run the author-time gate
after "a model-registry entry" and said the gate confirms "skill / tool / model
metadata". Neither is a check `os validate` can run: the AI collections of
`ObjectStackDefinitionSchema` are exactly `agents` / `tools` / `skills`
(`packages/spec/src/stack.zod.ts`), `ModelRegistrySchema` is referenced only by
its own file and its own test, and `os validate` parses
`ObjectStackDefinitionSchema` (`packages/cli/src/commands/validate.ts`). The
same file already says so at its central-registration row.
Two deletions, shrink-only: "or a model-registry entry" and "/ model". No
re-wrap, no replacement validator named, no other sentence touched.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 14:01
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 5784526Sep 3, 2026
43 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14836-ai-skill-dup-lines branch September 3, 2026 14:22
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@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

docs(skills): stop promising os validate checks a model-registry entry - #14953

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines
Sep 3, 2026
Merged

docs(skills): stop promising os validate checks a model-registry entry#14953
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14836

skills/objectstack-ai/SKILL.md told an AI author to run os validate after authoring "a model-registry entry", and then said the gate confirms "skill / tool / model metadata". A model-registry entry is not an input os validate can receive — and the same file already says so, 64 lines above. Two deletions, shrink-only, nothing else moved.

The two sentences, before and after

Before, at the base commit b1d49b3940:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts or a model-registry entry, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool / model metadata conforms to the protocol.

After, at this head:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool metadata conforms to the protocol.

Untouched, deliberately: the os validate fence comment at :400, the remaining CEL sentence at :404-407, the central-registration row at :332, skills/README.md, and every other file in the tree. No replacement validator is named and no "not currently validated" caveat is added — the fix is to stop promising, not to explain the gap.

Premise re-verified at this head — the three reads the card names

  1. packages/spec/src/stack.zod.ts — the AI collections of ObjectStackDefinitionSchema are exactly agents, tools, skills (the three z.array(...) rows under the ADR-0063 doc block). A case-insensitive grep of that file for modelRegist, knowledge or promptTemplate exits 1 with no output.
  2. ModelRegistrySchema is referenced in exactly two files across packages/**/*.ts: packages/spec/src/ai/model-registry.zod.ts (3 occurrences) and packages/spec/src/ai/model-registry.test.ts (5). No stack key, no lint rule, no CLI command reaches it.
  3. packages/cli/src/commands/validate.ts parses ObjectStackDefinitionSchemasafeParse at :187, with the two authoring-lint passes at :184-185 reading the same schema.

The contradiction partner is still present verbatim at :332: "agents / tools / skills are the only AI stack collections — knowledge sources have none, agents are platform-supplied, and tools is not the default path".

Measurement — this file, before and after

measurebeforeafterdelta
lines of skills/objectstack-ai/SKILL.md4174170
tokens, same file54755467-8
ceiling for this file, unedited680668060
headroom13311339+8
published bundle total156199156191-8

Line count is unchanged because both deletions are intra-line. The token numbers are node scripts/check-skills-token-ratchet.mjs's own output for this path:

  • before — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5475 tokens (ceiling 6806; headroom 1331).
  • after — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5467 tokens (ceiling 6806; headroom 1339).

The ceiling map is not edited: this is a shrink under an unchanged ceiling.

Judgement calls

1. No re-flow, and the wrap was measured rather than assumed. This file wraps prose at 80 columns. Line :396 shrinks from 79 to 53 characters while :397 keeps five words ("author-time gate before reporting done:", 39 characters) — not an orphan, so nothing is re-wrapped. Line :404 shrinks from 76 to 68 with a full 80-character line behind it at :405. Pulling a word up in either place would edit a line this card did not name and would cascade through the rest of the paragraph.

2. The os validate fence comment at :400 stays — it is shared wording, not this file's claim. Measured: the identical line, byte for byte, is at skills/objectstack-api/SKILL.md:411, so it describes the gate rather than this domain; and the sentence immediately after it here already tells the reader that this domain has no CEL site. Editing it in one of the two files would fork wording the published catalog shares. Recorded as an observation, not a change.

3. Nothing else in the catalog claims a model-registry entry is validated. Swept skills/** for model-registry, model registry and ModelRegistry: the remaining mentions are :301 here (a note that ModelProviderSchema accepts a wider provider set), :405 here (the template dialect sentence), skills/objectstack-formula/SKILL.md:422 (the same template dialect fact) and the generated reference pointer at skills/objectstack-ai/references/_index.md:18. None of them promises a check, so the deletion leaves no instruction stranded and no author without a route.

4. No changeset. This diff publishes nothing from any released package — one markdown file in the published catalog — so it carries skip-changeset rather than an empty changeset. check-changeset-presence states the outcome in its own verdict line on this PR.

Reverse verification

Run from the committed state, under a trap restoring the absolute path, with the mutation proved on disk before anything was measured:

  • Mutation leg — "or a model-registry entry" re-inserted at :396; grep -c on that phrase moved 0 to 1 and git hash-object moved to 3cfae14e1c, away from the HEAD blob 9f5231126e. Only then was the ratchet re-read.
  • What it shows — the file's measured count rises from 5467 to 5473 tokens, the six tokens this half of the change removes. The ratchet itself stays GREEN in that state (5473 is far under the 6806 ceiling), and that is the honest reading: this gate prices size, it does not detect the false claim. The number that moves is the file's measured token count, not the gate's verdict. Nothing in the local union can red on the sentence itself — see NOT MEASURED below.
  • Restore leggit checkout HEAD -- on the absolute path; git hash-object returns 9f5231126eab1f93aa5d1a142d4a78867cf8275f, equal to the HEAD blob, and git diff HEAD is empty. The ratchet then re-reads 5467 tokens, and git status --porcelain is clean.

Gates

The union was derived at this head rather than from a hand-written list:

dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 741e8e7071 (/home/user/objectstack-14836).
dispatch-gates: change set derived from git — 1 path(s) vs merge base b1d49b394 of 'origin/main' and HEAD
(committed 1, working tree 0, untracked 0; three-dot semantics, never 'origin/main..HEAD')
· skills/objectstack-ai/SKILL.md
dispatch-gates --commands: 21 command(s) — 14 pnpm, 7 direct node (16 matched by path, 0 by change KIND, 5 declared WHOLE-TREE and named on every card).

Exit codes were captured by redirecting stdout and stderr into a per-gate log file and reading $? on the next statement — never through a pipe, which would report the pipe's status instead. Each row quotes the gate's own verdict line.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 133 declared cross-package glob(s) (93 unique) are covered by core or crosspkg, every crosspkg entry still covers one, and the test job's if: still names both filters.
node scripts/check-closing-keyword-parity.mjs0check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 8133 tracked file(s), all registered).
node scripts/check-comment-mask-corpus.mjs0✓ comment-mask corpus sweep: 5808 files, 0 disagree, 0 unparseable, 49.9s (comparator self-test: 12 cases pass).
node scripts/check-cross-package-test-inputs.mjs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
node scripts/check-shard-attestation.mjs0✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).
node scripts/check-skills-token-ratchet.mjs0✓ check-skills-token-ratchet: 36 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.
node scripts/check-test-completeness.mjs3PREREQUISITE NOT MET — NOT MEASURED, see below.
pnpm --filter @objectstack/lint run check:doc-formula-expressions0✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. (two further ✓ legs: spec TSDoc 9 examples clean, field-level predicates 14 clean / 6 undeterminable)
pnpm --filter @objectstack/spec run check:skill-docs0✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5809 bare -- token(s) · 1388 launcher-rooted run(s) · 9 separator(s) JUDGED · 5 vitest-backed script name(s) derived from 81 manifest(s)
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
pnpm check:doc-authoring0✓ doc authoring guard: sibling-package prose ids hold the baseline — 831 pinned site(s) across 231 file(s), 83548 string(s) read in 1132 parsed source(s), no growth, no burn-down unrecorded.
pnpm check:nul-bytes0check-nul-bytes: OK (scanned 8126 text file(s) -- 8126 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions (the full enumeration is long; the run's closing line is) live: the real generator declared 9 output(s) and certified this tree
pnpm check:refd-timer-probe0OK check-refd-timer-probe: 5803 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else.
pnpm check:role-word0check-role-word: OK, no new occurrences of the reserved word.
pnpm check:skill-compatibility0✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 79 workspace packages
pnpm check:skill-frame-sync0✓ check-skill-frame-sync: 2 copies of the decision frame are structurally isomorphic across 2 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) checked against 94209 implementation word tokens (3 ledgered exemption(s)); Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s).
pnpm check:watch-hint-literal0✓ check-watch-hint-literal: 47 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 31, ROOT_FILE_WATCH_HINTS 9, ROOT_WATCH_HINTS 2, DECLARED_WATCH_HINTS 5 -- every one an array of quoted literals inside its own statement.

Two readings needed a prerequisite before they meant anything: check:doc-formula-expressions first answered PREREQUISITE NOT MET twice — @objectstack/formula not built, then @objectstack/lint not built — and both builds went through the shared verify lock (os-verify-lock: VERDICT command-exit 0, 190s and 12s held). Its exit 0 above is the reading after that, not the first one.

NOT MEASURED

  • node scripts/check-test-completeness.mjs — exit 3, PREREQUISITE NOT MET. The gate grades a saved turbo run test log; it does not run tests and cannot produce one. Its own text: "Arrived here from the gate family scripts/pm/dispatch-gates.mjs derives? That list names this script with NO argument, which is this branch. There is no local log to hand it, so the local reading for this gate is NOT MEASURED. It is not a red, and there is nothing here to fix." Supplying that prerequisite means the repo-wide test farm, which is CI's run, not this flight's. Read as neither pass nor fail.
  • No gate in the union can red on the deleted claim itself. The sentence was false about os validate in a way no checker models: check:skill-identifier-liveness grades whether cited identifiers exist (ModelRegistrySchema does exist), and the token ratchet grades size. The reverse verification above demonstrates this rather than papering over it — the mutated tree is green everywhere. The correctness of this change rests on the three source reads, not on a gate.
  • The always-runs CI tail is outside this table.dispatch-gates --commands says so in its own closing line: workflows with no path filter are not in the local union, and are read from this PR's checks.
  • Nothing was executed from this file. It is published prose; there is no runtime behaviour to exercise and no test to add or change, so the entire reading is gate-shaped by nature.

Landing

Governed surface (skills/**): this PR stays a draft. Not flipped ready, not enqueued, no auto-merge, no reviewers requested by this seat, no approving review. Labels: skip-changeset, and needs:contract-review because the deleted words are a claim about what os validate checks.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

`skills/objectstack-ai/SKILL.md` told the author to run the author-time gate
after "a model-registry entry" and said the gate confirms "skill / tool / model
metadata". Neither is a check `os validate` can run: the AI collections of
`ObjectStackDefinitionSchema` are exactly `agents` / `tools` / `skills`
(`packages/spec/src/stack.zod.ts`), `ModelRegistrySchema` is referenced only by
its own file and its own test, and `os validate` parses
`ObjectStackDefinitionSchema` (`packages/cli/src/commands/validate.ts`). The
same file already says so at its central-registration row.
Two deletions, shrink-only: "or a model-registry entry" and "/ model". No
re-wrap, no replacement validator named, no other sentence touched.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 14:01
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 5784526Sep 3, 2026
43 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14836-ai-skill-dup-lines branch September 3, 2026 14:22
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@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

docs(skills): stop promising os validate checks a model-registry entry - #14953

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines
Sep 3, 2026
Merged

docs(skills): stop promising os validate checks a model-registry entry#14953
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14836

skills/objectstack-ai/SKILL.md told an AI author to run os validate after authoring "a model-registry entry", and then said the gate confirms "skill / tool / model metadata". A model-registry entry is not an input os validate can receive — and the same file already says so, 64 lines above. Two deletions, shrink-only, nothing else moved.

The two sentences, before and after

Before, at the base commit b1d49b3940:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts or a model-registry entry, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool / model metadata conforms to the protocol.

After, at this head:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool metadata conforms to the protocol.

Untouched, deliberately: the os validate fence comment at :400, the remaining CEL sentence at :404-407, the central-registration row at :332, skills/README.md, and every other file in the tree. No replacement validator is named and no "not currently validated" caveat is added — the fix is to stop promising, not to explain the gap.

Premise re-verified at this head — the three reads the card names

  1. packages/spec/src/stack.zod.ts — the AI collections of ObjectStackDefinitionSchema are exactly agents, tools, skills (the three z.array(...) rows under the ADR-0063 doc block). A case-insensitive grep of that file for modelRegist, knowledge or promptTemplate exits 1 with no output.
  2. ModelRegistrySchema is referenced in exactly two files across packages/**/*.ts: packages/spec/src/ai/model-registry.zod.ts (3 occurrences) and packages/spec/src/ai/model-registry.test.ts (5). No stack key, no lint rule, no CLI command reaches it.
  3. packages/cli/src/commands/validate.ts parses ObjectStackDefinitionSchemasafeParse at :187, with the two authoring-lint passes at :184-185 reading the same schema.

The contradiction partner is still present verbatim at :332: "agents / tools / skills are the only AI stack collections — knowledge sources have none, agents are platform-supplied, and tools is not the default path".

Measurement — this file, before and after

measurebeforeafterdelta
lines of skills/objectstack-ai/SKILL.md4174170
tokens, same file54755467-8
ceiling for this file, unedited680668060
headroom13311339+8
published bundle total156199156191-8

Line count is unchanged because both deletions are intra-line. The token numbers are node scripts/check-skills-token-ratchet.mjs's own output for this path:

  • before — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5475 tokens (ceiling 6806; headroom 1331).
  • after — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5467 tokens (ceiling 6806; headroom 1339).

The ceiling map is not edited: this is a shrink under an unchanged ceiling.

Judgement calls

1. No re-flow, and the wrap was measured rather than assumed. This file wraps prose at 80 columns. Line :396 shrinks from 79 to 53 characters while :397 keeps five words ("author-time gate before reporting done:", 39 characters) — not an orphan, so nothing is re-wrapped. Line :404 shrinks from 76 to 68 with a full 80-character line behind it at :405. Pulling a word up in either place would edit a line this card did not name and would cascade through the rest of the paragraph.

2. The os validate fence comment at :400 stays — it is shared wording, not this file's claim. Measured: the identical line, byte for byte, is at skills/objectstack-api/SKILL.md:411, so it describes the gate rather than this domain; and the sentence immediately after it here already tells the reader that this domain has no CEL site. Editing it in one of the two files would fork wording the published catalog shares. Recorded as an observation, not a change.

3. Nothing else in the catalog claims a model-registry entry is validated. Swept skills/** for model-registry, model registry and ModelRegistry: the remaining mentions are :301 here (a note that ModelProviderSchema accepts a wider provider set), :405 here (the template dialect sentence), skills/objectstack-formula/SKILL.md:422 (the same template dialect fact) and the generated reference pointer at skills/objectstack-ai/references/_index.md:18. None of them promises a check, so the deletion leaves no instruction stranded and no author without a route.

4. No changeset. This diff publishes nothing from any released package — one markdown file in the published catalog — so it carries skip-changeset rather than an empty changeset. check-changeset-presence states the outcome in its own verdict line on this PR.

Reverse verification

Run from the committed state, under a trap restoring the absolute path, with the mutation proved on disk before anything was measured:

  • Mutation leg — "or a model-registry entry" re-inserted at :396; grep -c on that phrase moved 0 to 1 and git hash-object moved to 3cfae14e1c, away from the HEAD blob 9f5231126e. Only then was the ratchet re-read.
  • What it shows — the file's measured count rises from 5467 to 5473 tokens, the six tokens this half of the change removes. The ratchet itself stays GREEN in that state (5473 is far under the 6806 ceiling), and that is the honest reading: this gate prices size, it does not detect the false claim. The number that moves is the file's measured token count, not the gate's verdict. Nothing in the local union can red on the sentence itself — see NOT MEASURED below.
  • Restore leggit checkout HEAD -- on the absolute path; git hash-object returns 9f5231126eab1f93aa5d1a142d4a78867cf8275f, equal to the HEAD blob, and git diff HEAD is empty. The ratchet then re-reads 5467 tokens, and git status --porcelain is clean.

Gates

The union was derived at this head rather than from a hand-written list:

dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 741e8e7071 (/home/user/objectstack-14836).
dispatch-gates: change set derived from git — 1 path(s) vs merge base b1d49b394 of 'origin/main' and HEAD
(committed 1, working tree 0, untracked 0; three-dot semantics, never 'origin/main..HEAD')
· skills/objectstack-ai/SKILL.md
dispatch-gates --commands: 21 command(s) — 14 pnpm, 7 direct node (16 matched by path, 0 by change KIND, 5 declared WHOLE-TREE and named on every card).

Exit codes were captured by redirecting stdout and stderr into a per-gate log file and reading $? on the next statement — never through a pipe, which would report the pipe's status instead. Each row quotes the gate's own verdict line.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 133 declared cross-package glob(s) (93 unique) are covered by core or crosspkg, every crosspkg entry still covers one, and the test job's if: still names both filters.
node scripts/check-closing-keyword-parity.mjs0check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 8133 tracked file(s), all registered).
node scripts/check-comment-mask-corpus.mjs0✓ comment-mask corpus sweep: 5808 files, 0 disagree, 0 unparseable, 49.9s (comparator self-test: 12 cases pass).
node scripts/check-cross-package-test-inputs.mjs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
node scripts/check-shard-attestation.mjs0✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).
node scripts/check-skills-token-ratchet.mjs0✓ check-skills-token-ratchet: 36 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.
node scripts/check-test-completeness.mjs3PREREQUISITE NOT MET — NOT MEASURED, see below.
pnpm --filter @objectstack/lint run check:doc-formula-expressions0✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. (two further ✓ legs: spec TSDoc 9 examples clean, field-level predicates 14 clean / 6 undeterminable)
pnpm --filter @objectstack/spec run check:skill-docs0✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5809 bare -- token(s) · 1388 launcher-rooted run(s) · 9 separator(s) JUDGED · 5 vitest-backed script name(s) derived from 81 manifest(s)
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
pnpm check:doc-authoring0✓ doc authoring guard: sibling-package prose ids hold the baseline — 831 pinned site(s) across 231 file(s), 83548 string(s) read in 1132 parsed source(s), no growth, no burn-down unrecorded.
pnpm check:nul-bytes0check-nul-bytes: OK (scanned 8126 text file(s) -- 8126 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions (the full enumeration is long; the run's closing line is) live: the real generator declared 9 output(s) and certified this tree
pnpm check:refd-timer-probe0OK check-refd-timer-probe: 5803 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else.
pnpm check:role-word0check-role-word: OK, no new occurrences of the reserved word.
pnpm check:skill-compatibility0✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 79 workspace packages
pnpm check:skill-frame-sync0✓ check-skill-frame-sync: 2 copies of the decision frame are structurally isomorphic across 2 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) checked against 94209 implementation word tokens (3 ledgered exemption(s)); Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s).
pnpm check:watch-hint-literal0✓ check-watch-hint-literal: 47 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 31, ROOT_FILE_WATCH_HINTS 9, ROOT_WATCH_HINTS 2, DECLARED_WATCH_HINTS 5 -- every one an array of quoted literals inside its own statement.

Two readings needed a prerequisite before they meant anything: check:doc-formula-expressions first answered PREREQUISITE NOT MET twice — @objectstack/formula not built, then @objectstack/lint not built — and both builds went through the shared verify lock (os-verify-lock: VERDICT command-exit 0, 190s and 12s held). Its exit 0 above is the reading after that, not the first one.

NOT MEASURED

  • node scripts/check-test-completeness.mjs — exit 3, PREREQUISITE NOT MET. The gate grades a saved turbo run test log; it does not run tests and cannot produce one. Its own text: "Arrived here from the gate family scripts/pm/dispatch-gates.mjs derives? That list names this script with NO argument, which is this branch. There is no local log to hand it, so the local reading for this gate is NOT MEASURED. It is not a red, and there is nothing here to fix." Supplying that prerequisite means the repo-wide test farm, which is CI's run, not this flight's. Read as neither pass nor fail.
  • No gate in the union can red on the deleted claim itself. The sentence was false about os validate in a way no checker models: check:skill-identifier-liveness grades whether cited identifiers exist (ModelRegistrySchema does exist), and the token ratchet grades size. The reverse verification above demonstrates this rather than papering over it — the mutated tree is green everywhere. The correctness of this change rests on the three source reads, not on a gate.
  • The always-runs CI tail is outside this table.dispatch-gates --commands says so in its own closing line: workflows with no path filter are not in the local union, and are read from this PR's checks.
  • Nothing was executed from this file. It is published prose; there is no runtime behaviour to exercise and no test to add or change, so the entire reading is gate-shaped by nature.

Landing

Governed surface (skills/**): this PR stays a draft. Not flipped ready, not enqueued, no auto-merge, no reviewers requested by this seat, no approving review. Labels: skip-changeset, and needs:contract-review because the deleted words are a claim about what os validate checks.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

`skills/objectstack-ai/SKILL.md` told the author to run the author-time gate
after "a model-registry entry" and said the gate confirms "skill / tool / model
metadata". Neither is a check `os validate` can run: the AI collections of
`ObjectStackDefinitionSchema` are exactly `agents` / `tools` / `skills`
(`packages/spec/src/stack.zod.ts`), `ModelRegistrySchema` is referenced only by
its own file and its own test, and `os validate` parses
`ObjectStackDefinitionSchema` (`packages/cli/src/commands/validate.ts`). The
same file already says so at its central-registration row.
Two deletions, shrink-only: "or a model-registry entry" and "/ model". No
re-wrap, no replacement validator named, no other sentence touched.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 14:01
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 5784526Sep 3, 2026
43 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14836-ai-skill-dup-lines branch September 3, 2026 14:22
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@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

docs(skills): stop promising os validate checks a model-registry entry - #14953

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines
Sep 3, 2026
Merged

docs(skills): stop promising os validate checks a model-registry entry#14953
os-zhuang merged 1 commit into
mainfrom
claude/issue-14836-ai-skill-dup-lines

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14836

skills/objectstack-ai/SKILL.md told an AI author to run os validate after authoring "a model-registry entry", and then said the gate confirms "skill / tool / model metadata". A model-registry entry is not an input os validate can receive — and the same file already says so, 64 lines above. Two deletions, shrink-only, nothing else moved.

The two sentences, before and after

Before, at the base commit b1d49b3940:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts or a model-registry entry, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool / model metadata conforms to the protocol.

After, at this head:

  • :396-397 — After authoring a *.skill.ts / *.tool.ts, run the author-time gate before reporting done:
  • :404 — It confirms the skill / tool metadata conforms to the protocol.

Untouched, deliberately: the os validate fence comment at :400, the remaining CEL sentence at :404-407, the central-registration row at :332, skills/README.md, and every other file in the tree. No replacement validator is named and no "not currently validated" caveat is added — the fix is to stop promising, not to explain the gap.

Premise re-verified at this head — the three reads the card names

  1. packages/spec/src/stack.zod.ts — the AI collections of ObjectStackDefinitionSchema are exactly agents, tools, skills (the three z.array(...) rows under the ADR-0063 doc block). A case-insensitive grep of that file for modelRegist, knowledge or promptTemplate exits 1 with no output.
  2. ModelRegistrySchema is referenced in exactly two files across packages/**/*.ts: packages/spec/src/ai/model-registry.zod.ts (3 occurrences) and packages/spec/src/ai/model-registry.test.ts (5). No stack key, no lint rule, no CLI command reaches it.
  3. packages/cli/src/commands/validate.ts parses ObjectStackDefinitionSchemasafeParse at :187, with the two authoring-lint passes at :184-185 reading the same schema.

The contradiction partner is still present verbatim at :332: "agents / tools / skills are the only AI stack collections — knowledge sources have none, agents are platform-supplied, and tools is not the default path".

Measurement — this file, before and after

measurebeforeafterdelta
lines of skills/objectstack-ai/SKILL.md4174170
tokens, same file54755467-8
ceiling for this file, unedited680668060
headroom13311339+8
published bundle total156199156191-8

Line count is unchanged because both deletions are intra-line. The token numbers are node scripts/check-skills-token-ratchet.mjs's own output for this path:

  • before — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5475 tokens (ceiling 6806; headroom 1331).
  • after — ✓ check-skills-token-ratchet: skills/objectstack-ai/SKILL.md is 5467 tokens (ceiling 6806; headroom 1339).

The ceiling map is not edited: this is a shrink under an unchanged ceiling.

Judgement calls

1. No re-flow, and the wrap was measured rather than assumed. This file wraps prose at 80 columns. Line :396 shrinks from 79 to 53 characters while :397 keeps five words ("author-time gate before reporting done:", 39 characters) — not an orphan, so nothing is re-wrapped. Line :404 shrinks from 76 to 68 with a full 80-character line behind it at :405. Pulling a word up in either place would edit a line this card did not name and would cascade through the rest of the paragraph.

2. The os validate fence comment at :400 stays — it is shared wording, not this file's claim. Measured: the identical line, byte for byte, is at skills/objectstack-api/SKILL.md:411, so it describes the gate rather than this domain; and the sentence immediately after it here already tells the reader that this domain has no CEL site. Editing it in one of the two files would fork wording the published catalog shares. Recorded as an observation, not a change.

3. Nothing else in the catalog claims a model-registry entry is validated. Swept skills/** for model-registry, model registry and ModelRegistry: the remaining mentions are :301 here (a note that ModelProviderSchema accepts a wider provider set), :405 here (the template dialect sentence), skills/objectstack-formula/SKILL.md:422 (the same template dialect fact) and the generated reference pointer at skills/objectstack-ai/references/_index.md:18. None of them promises a check, so the deletion leaves no instruction stranded and no author without a route.

4. No changeset. This diff publishes nothing from any released package — one markdown file in the published catalog — so it carries skip-changeset rather than an empty changeset. check-changeset-presence states the outcome in its own verdict line on this PR.

Reverse verification

Run from the committed state, under a trap restoring the absolute path, with the mutation proved on disk before anything was measured:

  • Mutation leg — "or a model-registry entry" re-inserted at :396; grep -c on that phrase moved 0 to 1 and git hash-object moved to 3cfae14e1c, away from the HEAD blob 9f5231126e. Only then was the ratchet re-read.
  • What it shows — the file's measured count rises from 5467 to 5473 tokens, the six tokens this half of the change removes. The ratchet itself stays GREEN in that state (5473 is far under the 6806 ceiling), and that is the honest reading: this gate prices size, it does not detect the false claim. The number that moves is the file's measured token count, not the gate's verdict. Nothing in the local union can red on the sentence itself — see NOT MEASURED below.
  • Restore leggit checkout HEAD -- on the absolute path; git hash-object returns 9f5231126eab1f93aa5d1a142d4a78867cf8275f, equal to the HEAD blob, and git diff HEAD is empty. The ratchet then re-reads 5467 tokens, and git status --porcelain is clean.

Gates

The union was derived at this head rather than from a hand-written list:

dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 741e8e7071 (/home/user/objectstack-14836).
dispatch-gates: change set derived from git — 1 path(s) vs merge base b1d49b394 of 'origin/main' and HEAD
(committed 1, working tree 0, untracked 0; three-dot semantics, never 'origin/main..HEAD')
· skills/objectstack-ai/SKILL.md
dispatch-gates --commands: 21 command(s) — 14 pnpm, 7 direct node (16 matched by path, 0 by change KIND, 5 declared WHOLE-TREE and named on every card).

Exit codes were captured by redirecting stdout and stderr into a per-gate log file and reading $? on the next statement — never through a pipe, which would report the pipe's status instead. Each row quotes the gate's own verdict line.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 133 declared cross-package glob(s) (93 unique) are covered by core or crosspkg, every crosspkg entry still covers one, and the test job's if: still names both filters.
node scripts/check-closing-keyword-parity.mjs0check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 8133 tracked file(s), all registered).
node scripts/check-comment-mask-corpus.mjs0✓ comment-mask corpus sweep: 5808 files, 0 disagree, 0 unparseable, 49.9s (comparator self-test: 12 cases pass).
node scripts/check-cross-package-test-inputs.mjs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
node scripts/check-shard-attestation.mjs0✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).
node scripts/check-skills-token-ratchet.mjs0✓ check-skills-token-ratchet: 36 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.
node scripts/check-test-completeness.mjs3PREREQUISITE NOT MET — NOT MEASURED, see below.
pnpm --filter @objectstack/lint run check:doc-formula-expressions0✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. (two further ✓ legs: spec TSDoc 9 examples clean, field-level predicates 14 clean / 6 undeterminable)
pnpm --filter @objectstack/spec run check:skill-docs0✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5809 bare -- token(s) · 1388 launcher-rooted run(s) · 9 separator(s) JUDGED · 5 vitest-backed script name(s) derived from 81 manifest(s)
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0OK: 25 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
pnpm check:doc-authoring0✓ doc authoring guard: sibling-package prose ids hold the baseline — 831 pinned site(s) across 231 file(s), 83548 string(s) read in 1132 parsed source(s), no growth, no burn-down unrecorded.
pnpm check:nul-bytes0check-nul-bytes: OK (scanned 8126 text file(s) -- 8126 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions (the full enumeration is long; the run's closing line is) live: the real generator declared 9 output(s) and certified this tree
pnpm check:refd-timer-probe0OK check-refd-timer-probe: 5803 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else.
pnpm check:role-word0check-role-word: OK, no new occurrences of the reserved word.
pnpm check:skill-compatibility0✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 79 workspace packages
pnpm check:skill-frame-sync0✓ check-skill-frame-sync: 2 copies of the decision frame are structurally isomorphic across 2 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) checked against 94209 implementation word tokens (3 ledgered exemption(s)); Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s).
pnpm check:watch-hint-literal0✓ check-watch-hint-literal: 47 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 31, ROOT_FILE_WATCH_HINTS 9, ROOT_WATCH_HINTS 2, DECLARED_WATCH_HINTS 5 -- every one an array of quoted literals inside its own statement.

Two readings needed a prerequisite before they meant anything: check:doc-formula-expressions first answered PREREQUISITE NOT MET twice — @objectstack/formula not built, then @objectstack/lint not built — and both builds went through the shared verify lock (os-verify-lock: VERDICT command-exit 0, 190s and 12s held). Its exit 0 above is the reading after that, not the first one.

NOT MEASURED

  • node scripts/check-test-completeness.mjs — exit 3, PREREQUISITE NOT MET. The gate grades a saved turbo run test log; it does not run tests and cannot produce one. Its own text: "Arrived here from the gate family scripts/pm/dispatch-gates.mjs derives? That list names this script with NO argument, which is this branch. There is no local log to hand it, so the local reading for this gate is NOT MEASURED. It is not a red, and there is nothing here to fix." Supplying that prerequisite means the repo-wide test farm, which is CI's run, not this flight's. Read as neither pass nor fail.
  • No gate in the union can red on the deleted claim itself. The sentence was false about os validate in a way no checker models: check:skill-identifier-liveness grades whether cited identifiers exist (ModelRegistrySchema does exist), and the token ratchet grades size. The reverse verification above demonstrates this rather than papering over it — the mutated tree is green everywhere. The correctness of this change rests on the three source reads, not on a gate.
  • The always-runs CI tail is outside this table.dispatch-gates --commands says so in its own closing line: workflows with no path filter are not in the local union, and are read from this PR's checks.
  • Nothing was executed from this file. It is published prose; there is no runtime behaviour to exercise and no test to add or change, so the entire reading is gate-shaped by nature.

Landing

Governed surface (skills/**): this PR stays a draft. Not flipped ready, not enqueued, no auto-merge, no reviewers requested by this seat, no approving review. Labels: skip-changeset, and needs:contract-review because the deleted words are a claim about what os validate checks.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

`skills/objectstack-ai/SKILL.md` told the author to run the author-time gate
after "a model-registry entry" and said the gate confirms "skill / tool / model
metadata". Neither is a check `os validate` can run: the AI collections of
`ObjectStackDefinitionSchema` are exactly `agents` / `tools` / `skills`
(`packages/spec/src/stack.zod.ts`), `ModelRegistrySchema` is referenced only by
its own file and its own test, and `os validate` parses
`ObjectStackDefinitionSchema` (`packages/cli/src/commands/validate.ts`). The
same file already says so at its central-registration row.
Two deletions, shrink-only: "or a model-registry entry" and "/ model". No
re-wrap, no replacement validator named, no other sentence touched.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 14:01
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 5784526Sep 3, 2026
43 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14836-ai-skill-dup-lines branch September 3, 2026 14:22
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@claude