docs(skills): admit hand-authored reference files under references/ in the catalog anatomy - #14786

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy
Sep 3, 2026
Merged

docs(skills): admit hand-authored reference files under references/ in the catalog anatomy#14786
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy

Conversation

@os-litant

@os-litantos-litant commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14655

What this changes

One line of skills/README.md, inside the "Skill anatomy" tree: the references/ entry
now carries a comment admitting hand-authored reference files.

-├── references/+├── references/ # may also hold hand-authored reference files linked from SKILL.md

The lane decision: option 2 (amend the anatomy, do not move the files)

The card offered two honest resolutions. The skills-lane seat chose option 2 in its
claim comment on #14655, and this PR implements exactly that.

The anatomy reserved references/ for the generated _index.md and assigned every
hand-authored per-topic file to rules/. Three published packages already keep
hand-authored anchors under references/:

packagefilelinked from its own SKILL.md
objectstack-datareferences/data-hooks.mdSKILL.md:50, :173, :522
objectstack-platformreferences/plugin-hooks.mdSKILL.md:657, :790

Both are cited by path from sibling packages as well, so option 1 (move to rules/) is a
multi-package rename carrying pointer updates, a check:skill-identifier-liveness Leg-2
binding re-check (bindings are keyed by file path plus heading) and ceiling-row renames in
scripts/check-skills-token-ratchet.mjs — for zero reader value, since the reader reaches
the same text either way. Amending the sentence that describes the catalog is the cheaper
and truer half of the pair.

Why it is one block, and one line inside it. The permission is stated where the layout
is defined, so a reader authoring a new skill sees it at the moment the question arises,
and the requirement travels with the permission: a hand-authored reference file must be
linked from SKILL.md. Everything else in the tree is byte-identical — SKILL.md,
the two _index.md comment lines (still "generated pointers", still "do not hand-edit",
still the pnpm --filter @objectstack/spec gen:skill-refs pointer), rules/ with its
per-topic role, contracts/ and evals/.

One spelling change in the two blocks below, declared. The tree's first line is a
placeholder for the skill directory, written in the file with angle brackets. GitHub's
body sanitizer deletes angle-bracket fragments from a PR body — it did exactly that on
this body's first write, leaving skills// — so the placeholder is spelled
SKILL-NAME here. That line is untouched by this PR either way; the changed line
contains no brackets and is quoted verbatim above.

Before

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

After

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/ # may also hold hand-authored reference files linked from SKILL.md
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

The illustrative examples the card sketched for the new comment (event lists, payload
tables, hook references) were left out deliberately: they are flavour, not contract, and
the block is under a shrink-or-neutral budget. The contract — optional, hand-authored,
linked from SKILL.md — is stated in full.

Token delta

skills/README.md, measured as ceil(utf8_bytes / 4):

utf8 bytestokens
before (df657d9df)79411986
after (3b9bf9435)80182005
delta+77+19

Net positive, and stated as such rather than bought back by degrading a neighbouring
sentence. The paid-for alternative was folding the two _index.md comment lines into one,
which would have cost the reader the runnable pnpm --filter @objectstack/spec gen:skill-refs command on the catalog's front page — a worse trade than +19 tokens.
skills/README.md is outside the token-ratchet population
(scripts/check-skills-token-ratchet.mjs:133, self-test at :790), so no ceiling row
exists for it and none moved; the gate run below confirms that.

Premise correction (premise_false)

The card's closing sentence — "Either way the generator-owned skills/README.md is
regenerated, never hand-edited"
— is false for this block, and the seat corrected it
at dispatch. Measured here at 3b9bf9435:

  • packages/spec/scripts/build-skill-docs.ts:12-13 states the contract in its own header:
    derived listings are "rewritten between BEGIN/END GENERATED: skills markers; prose
    outside the markers is preserved"
    .
  • The generated Index table ends at the END marker on skills/README.md:44. The "Skill
    anatomy" heading is at :50 and the tree at :52-62 — after it, in preserved prose.
  • So there is no generator source to amend: the block is hand-authored, and this PR edits
    it by hand. That the generated region was not touched is proved by
    check:skill-docs staying green, and by the reverse verification below showing that the
    same gate does go red for an edit inside the region.

Second, smaller correction: the seat's claim comment located the block at :44-58. At
this branch point it is the END marker that sits at :44; the anatomy heading is :50
and the tree :52-62. No effect on the decision.

Changeset

None, and the skip-changeset label is applied. This diff releases nothing from any
package — scripts/check-empty-changeset.mjs:359-361 enumerates that case by name:

* It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like) -> delete the changeset and apply the 'skip-changeset' label (route 2). The label is a gate-level exemption: it produces NO input for changesets/action.

The diff is exactly one file, skills/README.md, which is inside the enumerated skills/.

Gates

Head 3b9bf9435. The list is the union re-derived after the last edit with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(16 families, all matched via skills/README.md), plus the three the dispatch named
directly. Exit codes captured by redirect before any pipe; every run went through
scripts/pm/os-verify-lock.sh.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 130 declared cross-package glob(s) (92 unique) are covered by core or crosspkg, …
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-test-completeness.mjs3NOT MEASUREDPREREQUISITE NOT MET — this gate grades a saved turbo run test log, and no log was named. The gate's own text: "the local reading for this gate is NOT MEASURED. ⛔ It is not a red, and there is nothing here to fix."
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. (first run exited 3 on unbuilt prerequisites; measured after building @objectstack/formula then @objectstack/lint)
pnpm --filter @objectstack/spec run check:skill-docs0✓ skills/README.md · ✓ content/docs/ai/skills-reference.mdx · ✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5698 bare -- token(s) · …
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0All 117 self-test cases passed. · OK: 25 package(s) read outside themselves, all declared…
pnpm check:doc-authoring0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references.
pnpm check:merge-driver0✓ check-regen-pending self-test passed.
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions …
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: 4 copies of the decision frame are structurally isomorphic across 3 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) … Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(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-skills-token-ratchet.mjs --self-test0✓ check-skills-token-ratchet self-test: 64 cases pass.
node scripts/check-nul-bytes.mjs0check-nul-bytes: OK (scanned 8053 text file(s) … no raw ASCII control bytes).

18 measured green, 1 recorded NOT MEASURED (CI-only input), 0 red.

Reverse verification of check:skill-docs

The gate that proves the generated region was left alone is only worth quoting if it can
fail. Run from the committed state, under the verify lock, by a script with a trap on
EXIT, INT and TERM that re-checks the file out, and absolute paths throughout. No rebuild
leg is owed: the gate is tsx scripts/build-skill-docs.ts --check, which reads the
markdown sources from disk — no compiled artifact sits between the mutation and the
measurement.

legon-disk proofgate exitverdict line
mutated (one character inserted inside the generated region: the Formula row's expression domain cell)anchor count before 1 / after 0, injected count before 0 / after 1; blob 2940ce52…e023126d…1✗ skills/README.md is out of date — run pnpm --filter @objectstack/spec gen:skill-docs
restored (git checkout HEAD -- skills/README.md)blob back to 2940ce52… = the HEAD blob; git diff HEAD 0 bytes; git status --porcelain 0 lines0✅ Skill docs in sync

The predicted direction was red naming skills/README.md, and that is what it did.

ESLint

Narrowed to the changed file, and the narrowing is a measurement rather than a skip:

  1. Population, read from eslint.config.mjs itself: the base block at :971 is
    files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'], and every later block (:1015,
    :1054, :1103, :1172, :1212, :1238) narrows within those extensions. No block
    admits .md.
  2. Count, from --format json: one entry for skills/README.md, 0 errors, 1 warning,
    and the warning is File ignored because no matching configuration was supplied.
    ESLint exited 0.
  3. Invariance for untouched files: the config declares no parserOptions.project and
    no projectService anywhere (its own comment at :328 says so — "no
    parserOptions.project, no typed @typescript-eslint rules"), so no verdict on any
    file is a function of another file's contents. A markdown-only diff cannot move a lint
    result anywhere in the tree.

Follow-up, not this PR

  • The card's "require the link" half is stated here as text, not enforced by a gate.
    Adding one was explicitly out of scope. A gate is plausible — the shape would be "every
    non-_index.md file under a published skills/*/references/ is referenced by that
    skill's SKILL.md" — but it needs the lane's decision, and there is a third file to
    triage first (below).
  • skills/objectstack-ui/references/react-blocks.md is a generated file (its own
    header: "GENERATED by packages/spec/scripts/build-react-blocks-contract.ts — do not
    edit") living under references/ beside _index.md, and grep finds no link to it from
    objectstack-ui/SKILL.md. It is neither the generated _index.md the anatomy names nor
    the hand-authored, linked material this PR admits, so the amended sentence does not cover
    it either. Reported to the lane seat for dedup against Seam (spec lane, ruled): gen:react-blocks stops emitting skills/objectstack-ui/contracts/react-blocks.contract.json — the markdown rendering becomes the single AI-facing output (#14296 item 3 = A) #14559 rather than filed from
    here.

Merge

Draft, and it stays draft — governed skills/** surface (Prime Directive #14): the
maintainer's human merge is the review record. No reviewers requested, no auto-merge, no
ready flip.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

The catalog anatomy in `skills/README.md` reserved `references/` for the
generated `_index.md` alone and assigned every hand-authored per-topic file to
`rules/`. Three published skills already keep hand-authored anchors under
`references/` (`objectstack-data/references/data-hooks.md`,
`objectstack-platform/references/plugin-hooks.md`), each linked directly from
its own `SKILL.md` and cited by path from sibling packages, so the anatomy
described a layout the catalog does not have.
Amend the anatomy rather than move the files: the reader reaches the same text
either way, and a move would be a multi-package rename with binding checks and
ceiling-row renames for zero reader value. `_index.md` keeps its "generated —
do not hand-edit" line and its `gen:skill-refs` pointer, byte-identical;
`rules/` keeps its per-topic role. The requirement travels with the permission:
a hand-authored reference file must be linked from `SKILL.md`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
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): admit hand-authored reference files under references/ in the catalog anatomy - #14786

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy
Sep 3, 2026
Merged

docs(skills): admit hand-authored reference files under references/ in the catalog anatomy#14786
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy

Conversation

@os-litant

@os-litantos-litant commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14655

What this changes

One line of skills/README.md, inside the "Skill anatomy" tree: the references/ entry
now carries a comment admitting hand-authored reference files.

-├── references/+├── references/ # may also hold hand-authored reference files linked from SKILL.md

The lane decision: option 2 (amend the anatomy, do not move the files)

The card offered two honest resolutions. The skills-lane seat chose option 2 in its
claim comment on #14655, and this PR implements exactly that.

The anatomy reserved references/ for the generated _index.md and assigned every
hand-authored per-topic file to rules/. Three published packages already keep
hand-authored anchors under references/:

packagefilelinked from its own SKILL.md
objectstack-datareferences/data-hooks.mdSKILL.md:50, :173, :522
objectstack-platformreferences/plugin-hooks.mdSKILL.md:657, :790

Both are cited by path from sibling packages as well, so option 1 (move to rules/) is a
multi-package rename carrying pointer updates, a check:skill-identifier-liveness Leg-2
binding re-check (bindings are keyed by file path plus heading) and ceiling-row renames in
scripts/check-skills-token-ratchet.mjs — for zero reader value, since the reader reaches
the same text either way. Amending the sentence that describes the catalog is the cheaper
and truer half of the pair.

Why it is one block, and one line inside it. The permission is stated where the layout
is defined, so a reader authoring a new skill sees it at the moment the question arises,
and the requirement travels with the permission: a hand-authored reference file must be
linked from SKILL.md. Everything else in the tree is byte-identical — SKILL.md,
the two _index.md comment lines (still "generated pointers", still "do not hand-edit",
still the pnpm --filter @objectstack/spec gen:skill-refs pointer), rules/ with its
per-topic role, contracts/ and evals/.

One spelling change in the two blocks below, declared. The tree's first line is a
placeholder for the skill directory, written in the file with angle brackets. GitHub's
body sanitizer deletes angle-bracket fragments from a PR body — it did exactly that on
this body's first write, leaving skills// — so the placeholder is spelled
SKILL-NAME here. That line is untouched by this PR either way; the changed line
contains no brackets and is quoted verbatim above.

Before

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

After

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/ # may also hold hand-authored reference files linked from SKILL.md
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

The illustrative examples the card sketched for the new comment (event lists, payload
tables, hook references) were left out deliberately: they are flavour, not contract, and
the block is under a shrink-or-neutral budget. The contract — optional, hand-authored,
linked from SKILL.md — is stated in full.

Token delta

skills/README.md, measured as ceil(utf8_bytes / 4):

utf8 bytestokens
before (df657d9df)79411986
after (3b9bf9435)80182005
delta+77+19

Net positive, and stated as such rather than bought back by degrading a neighbouring
sentence. The paid-for alternative was folding the two _index.md comment lines into one,
which would have cost the reader the runnable pnpm --filter @objectstack/spec gen:skill-refs command on the catalog's front page — a worse trade than +19 tokens.
skills/README.md is outside the token-ratchet population
(scripts/check-skills-token-ratchet.mjs:133, self-test at :790), so no ceiling row
exists for it and none moved; the gate run below confirms that.

Premise correction (premise_false)

The card's closing sentence — "Either way the generator-owned skills/README.md is
regenerated, never hand-edited"
— is false for this block, and the seat corrected it
at dispatch. Measured here at 3b9bf9435:

  • packages/spec/scripts/build-skill-docs.ts:12-13 states the contract in its own header:
    derived listings are "rewritten between BEGIN/END GENERATED: skills markers; prose
    outside the markers is preserved"
    .
  • The generated Index table ends at the END marker on skills/README.md:44. The "Skill
    anatomy" heading is at :50 and the tree at :52-62 — after it, in preserved prose.
  • So there is no generator source to amend: the block is hand-authored, and this PR edits
    it by hand. That the generated region was not touched is proved by
    check:skill-docs staying green, and by the reverse verification below showing that the
    same gate does go red for an edit inside the region.

Second, smaller correction: the seat's claim comment located the block at :44-58. At
this branch point it is the END marker that sits at :44; the anatomy heading is :50
and the tree :52-62. No effect on the decision.

Changeset

None, and the skip-changeset label is applied. This diff releases nothing from any
package — scripts/check-empty-changeset.mjs:359-361 enumerates that case by name:

* It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like) -> delete the changeset and apply the 'skip-changeset' label (route 2). The label is a gate-level exemption: it produces NO input for changesets/action.

The diff is exactly one file, skills/README.md, which is inside the enumerated skills/.

Gates

Head 3b9bf9435. The list is the union re-derived after the last edit with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(16 families, all matched via skills/README.md), plus the three the dispatch named
directly. Exit codes captured by redirect before any pipe; every run went through
scripts/pm/os-verify-lock.sh.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 130 declared cross-package glob(s) (92 unique) are covered by core or crosspkg, …
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-test-completeness.mjs3NOT MEASUREDPREREQUISITE NOT MET — this gate grades a saved turbo run test log, and no log was named. The gate's own text: "the local reading for this gate is NOT MEASURED. ⛔ It is not a red, and there is nothing here to fix."
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. (first run exited 3 on unbuilt prerequisites; measured after building @objectstack/formula then @objectstack/lint)
pnpm --filter @objectstack/spec run check:skill-docs0✓ skills/README.md · ✓ content/docs/ai/skills-reference.mdx · ✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5698 bare -- token(s) · …
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0All 117 self-test cases passed. · OK: 25 package(s) read outside themselves, all declared…
pnpm check:doc-authoring0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references.
pnpm check:merge-driver0✓ check-regen-pending self-test passed.
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions …
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: 4 copies of the decision frame are structurally isomorphic across 3 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) … Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(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-skills-token-ratchet.mjs --self-test0✓ check-skills-token-ratchet self-test: 64 cases pass.
node scripts/check-nul-bytes.mjs0check-nul-bytes: OK (scanned 8053 text file(s) … no raw ASCII control bytes).

18 measured green, 1 recorded NOT MEASURED (CI-only input), 0 red.

Reverse verification of check:skill-docs

The gate that proves the generated region was left alone is only worth quoting if it can
fail. Run from the committed state, under the verify lock, by a script with a trap on
EXIT, INT and TERM that re-checks the file out, and absolute paths throughout. No rebuild
leg is owed: the gate is tsx scripts/build-skill-docs.ts --check, which reads the
markdown sources from disk — no compiled artifact sits between the mutation and the
measurement.

legon-disk proofgate exitverdict line
mutated (one character inserted inside the generated region: the Formula row's expression domain cell)anchor count before 1 / after 0, injected count before 0 / after 1; blob 2940ce52…e023126d…1✗ skills/README.md is out of date — run pnpm --filter @objectstack/spec gen:skill-docs
restored (git checkout HEAD -- skills/README.md)blob back to 2940ce52… = the HEAD blob; git diff HEAD 0 bytes; git status --porcelain 0 lines0✅ Skill docs in sync

The predicted direction was red naming skills/README.md, and that is what it did.

ESLint

Narrowed to the changed file, and the narrowing is a measurement rather than a skip:

  1. Population, read from eslint.config.mjs itself: the base block at :971 is
    files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'], and every later block (:1015,
    :1054, :1103, :1172, :1212, :1238) narrows within those extensions. No block
    admits .md.
  2. Count, from --format json: one entry for skills/README.md, 0 errors, 1 warning,
    and the warning is File ignored because no matching configuration was supplied.
    ESLint exited 0.
  3. Invariance for untouched files: the config declares no parserOptions.project and
    no projectService anywhere (its own comment at :328 says so — "no
    parserOptions.project, no typed @typescript-eslint rules"), so no verdict on any
    file is a function of another file's contents. A markdown-only diff cannot move a lint
    result anywhere in the tree.

Follow-up, not this PR

  • The card's "require the link" half is stated here as text, not enforced by a gate.
    Adding one was explicitly out of scope. A gate is plausible — the shape would be "every
    non-_index.md file under a published skills/*/references/ is referenced by that
    skill's SKILL.md" — but it needs the lane's decision, and there is a third file to
    triage first (below).
  • skills/objectstack-ui/references/react-blocks.md is a generated file (its own
    header: "GENERATED by packages/spec/scripts/build-react-blocks-contract.ts — do not
    edit") living under references/ beside _index.md, and grep finds no link to it from
    objectstack-ui/SKILL.md. It is neither the generated _index.md the anatomy names nor
    the hand-authored, linked material this PR admits, so the amended sentence does not cover
    it either. Reported to the lane seat for dedup against Seam (spec lane, ruled): gen:react-blocks stops emitting skills/objectstack-ui/contracts/react-blocks.contract.json — the markdown rendering becomes the single AI-facing output (#14296 item 3 = A) #14559 rather than filed from
    here.

Merge

Draft, and it stays draft — governed skills/** surface (Prime Directive #14): the
maintainer's human merge is the review record. No reviewers requested, no auto-merge, no
ready flip.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

The catalog anatomy in `skills/README.md` reserved `references/` for the
generated `_index.md` alone and assigned every hand-authored per-topic file to
`rules/`. Three published skills already keep hand-authored anchors under
`references/` (`objectstack-data/references/data-hooks.md`,
`objectstack-platform/references/plugin-hooks.md`), each linked directly from
its own `SKILL.md` and cited by path from sibling packages, so the anatomy
described a layout the catalog does not have.
Amend the anatomy rather than move the files: the reader reaches the same text
either way, and a move would be a multi-package rename with binding checks and
ceiling-row renames for zero reader value. `_index.md` keeps its "generated —
do not hand-edit" line and its `gen:skill-refs` pointer, byte-identical;
`rules/` keeps its per-topic role. The requirement travels with the permission:
a hand-authored reference file must be linked from `SKILL.md`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
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): admit hand-authored reference files under references/ in the catalog anatomy - #14786

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy
Sep 3, 2026
Merged

docs(skills): admit hand-authored reference files under references/ in the catalog anatomy#14786
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy

Conversation

@os-litant

@os-litantos-litant commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14655

What this changes

One line of skills/README.md, inside the "Skill anatomy" tree: the references/ entry
now carries a comment admitting hand-authored reference files.

-├── references/+├── references/ # may also hold hand-authored reference files linked from SKILL.md

The lane decision: option 2 (amend the anatomy, do not move the files)

The card offered two honest resolutions. The skills-lane seat chose option 2 in its
claim comment on #14655, and this PR implements exactly that.

The anatomy reserved references/ for the generated _index.md and assigned every
hand-authored per-topic file to rules/. Three published packages already keep
hand-authored anchors under references/:

packagefilelinked from its own SKILL.md
objectstack-datareferences/data-hooks.mdSKILL.md:50, :173, :522
objectstack-platformreferences/plugin-hooks.mdSKILL.md:657, :790

Both are cited by path from sibling packages as well, so option 1 (move to rules/) is a
multi-package rename carrying pointer updates, a check:skill-identifier-liveness Leg-2
binding re-check (bindings are keyed by file path plus heading) and ceiling-row renames in
scripts/check-skills-token-ratchet.mjs — for zero reader value, since the reader reaches
the same text either way. Amending the sentence that describes the catalog is the cheaper
and truer half of the pair.

Why it is one block, and one line inside it. The permission is stated where the layout
is defined, so a reader authoring a new skill sees it at the moment the question arises,
and the requirement travels with the permission: a hand-authored reference file must be
linked from SKILL.md. Everything else in the tree is byte-identical — SKILL.md,
the two _index.md comment lines (still "generated pointers", still "do not hand-edit",
still the pnpm --filter @objectstack/spec gen:skill-refs pointer), rules/ with its
per-topic role, contracts/ and evals/.

One spelling change in the two blocks below, declared. The tree's first line is a
placeholder for the skill directory, written in the file with angle brackets. GitHub's
body sanitizer deletes angle-bracket fragments from a PR body — it did exactly that on
this body's first write, leaving skills// — so the placeholder is spelled
SKILL-NAME here. That line is untouched by this PR either way; the changed line
contains no brackets and is quoted verbatim above.

Before

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

After

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/ # may also hold hand-authored reference files linked from SKILL.md
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

The illustrative examples the card sketched for the new comment (event lists, payload
tables, hook references) were left out deliberately: they are flavour, not contract, and
the block is under a shrink-or-neutral budget. The contract — optional, hand-authored,
linked from SKILL.md — is stated in full.

Token delta

skills/README.md, measured as ceil(utf8_bytes / 4):

utf8 bytestokens
before (df657d9df)79411986
after (3b9bf9435)80182005
delta+77+19

Net positive, and stated as such rather than bought back by degrading a neighbouring
sentence. The paid-for alternative was folding the two _index.md comment lines into one,
which would have cost the reader the runnable pnpm --filter @objectstack/spec gen:skill-refs command on the catalog's front page — a worse trade than +19 tokens.
skills/README.md is outside the token-ratchet population
(scripts/check-skills-token-ratchet.mjs:133, self-test at :790), so no ceiling row
exists for it and none moved; the gate run below confirms that.

Premise correction (premise_false)

The card's closing sentence — "Either way the generator-owned skills/README.md is
regenerated, never hand-edited"
— is false for this block, and the seat corrected it
at dispatch. Measured here at 3b9bf9435:

  • packages/spec/scripts/build-skill-docs.ts:12-13 states the contract in its own header:
    derived listings are "rewritten between BEGIN/END GENERATED: skills markers; prose
    outside the markers is preserved"
    .
  • The generated Index table ends at the END marker on skills/README.md:44. The "Skill
    anatomy" heading is at :50 and the tree at :52-62 — after it, in preserved prose.
  • So there is no generator source to amend: the block is hand-authored, and this PR edits
    it by hand. That the generated region was not touched is proved by
    check:skill-docs staying green, and by the reverse verification below showing that the
    same gate does go red for an edit inside the region.

Second, smaller correction: the seat's claim comment located the block at :44-58. At
this branch point it is the END marker that sits at :44; the anatomy heading is :50
and the tree :52-62. No effect on the decision.

Changeset

None, and the skip-changeset label is applied. This diff releases nothing from any
package — scripts/check-empty-changeset.mjs:359-361 enumerates that case by name:

* It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like) -> delete the changeset and apply the 'skip-changeset' label (route 2). The label is a gate-level exemption: it produces NO input for changesets/action.

The diff is exactly one file, skills/README.md, which is inside the enumerated skills/.

Gates

Head 3b9bf9435. The list is the union re-derived after the last edit with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(16 families, all matched via skills/README.md), plus the three the dispatch named
directly. Exit codes captured by redirect before any pipe; every run went through
scripts/pm/os-verify-lock.sh.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 130 declared cross-package glob(s) (92 unique) are covered by core or crosspkg, …
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-test-completeness.mjs3NOT MEASUREDPREREQUISITE NOT MET — this gate grades a saved turbo run test log, and no log was named. The gate's own text: "the local reading for this gate is NOT MEASURED. ⛔ It is not a red, and there is nothing here to fix."
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. (first run exited 3 on unbuilt prerequisites; measured after building @objectstack/formula then @objectstack/lint)
pnpm --filter @objectstack/spec run check:skill-docs0✓ skills/README.md · ✓ content/docs/ai/skills-reference.mdx · ✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5698 bare -- token(s) · …
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0All 117 self-test cases passed. · OK: 25 package(s) read outside themselves, all declared…
pnpm check:doc-authoring0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references.
pnpm check:merge-driver0✓ check-regen-pending self-test passed.
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions …
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: 4 copies of the decision frame are structurally isomorphic across 3 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) … Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(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-skills-token-ratchet.mjs --self-test0✓ check-skills-token-ratchet self-test: 64 cases pass.
node scripts/check-nul-bytes.mjs0check-nul-bytes: OK (scanned 8053 text file(s) … no raw ASCII control bytes).

18 measured green, 1 recorded NOT MEASURED (CI-only input), 0 red.

Reverse verification of check:skill-docs

The gate that proves the generated region was left alone is only worth quoting if it can
fail. Run from the committed state, under the verify lock, by a script with a trap on
EXIT, INT and TERM that re-checks the file out, and absolute paths throughout. No rebuild
leg is owed: the gate is tsx scripts/build-skill-docs.ts --check, which reads the
markdown sources from disk — no compiled artifact sits between the mutation and the
measurement.

legon-disk proofgate exitverdict line
mutated (one character inserted inside the generated region: the Formula row's expression domain cell)anchor count before 1 / after 0, injected count before 0 / after 1; blob 2940ce52…e023126d…1✗ skills/README.md is out of date — run pnpm --filter @objectstack/spec gen:skill-docs
restored (git checkout HEAD -- skills/README.md)blob back to 2940ce52… = the HEAD blob; git diff HEAD 0 bytes; git status --porcelain 0 lines0✅ Skill docs in sync

The predicted direction was red naming skills/README.md, and that is what it did.

ESLint

Narrowed to the changed file, and the narrowing is a measurement rather than a skip:

  1. Population, read from eslint.config.mjs itself: the base block at :971 is
    files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'], and every later block (:1015,
    :1054, :1103, :1172, :1212, :1238) narrows within those extensions. No block
    admits .md.
  2. Count, from --format json: one entry for skills/README.md, 0 errors, 1 warning,
    and the warning is File ignored because no matching configuration was supplied.
    ESLint exited 0.
  3. Invariance for untouched files: the config declares no parserOptions.project and
    no projectService anywhere (its own comment at :328 says so — "no
    parserOptions.project, no typed @typescript-eslint rules"), so no verdict on any
    file is a function of another file's contents. A markdown-only diff cannot move a lint
    result anywhere in the tree.

Follow-up, not this PR

  • The card's "require the link" half is stated here as text, not enforced by a gate.
    Adding one was explicitly out of scope. A gate is plausible — the shape would be "every
    non-_index.md file under a published skills/*/references/ is referenced by that
    skill's SKILL.md" — but it needs the lane's decision, and there is a third file to
    triage first (below).
  • skills/objectstack-ui/references/react-blocks.md is a generated file (its own
    header: "GENERATED by packages/spec/scripts/build-react-blocks-contract.ts — do not
    edit") living under references/ beside _index.md, and grep finds no link to it from
    objectstack-ui/SKILL.md. It is neither the generated _index.md the anatomy names nor
    the hand-authored, linked material this PR admits, so the amended sentence does not cover
    it either. Reported to the lane seat for dedup against Seam (spec lane, ruled): gen:react-blocks stops emitting skills/objectstack-ui/contracts/react-blocks.contract.json — the markdown rendering becomes the single AI-facing output (#14296 item 3 = A) #14559 rather than filed from
    here.

Merge

Draft, and it stays draft — governed skills/** surface (Prime Directive #14): the
maintainer's human merge is the review record. No reviewers requested, no auto-merge, no
ready flip.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

The catalog anatomy in `skills/README.md` reserved `references/` for the
generated `_index.md` alone and assigned every hand-authored per-topic file to
`rules/`. Three published skills already keep hand-authored anchors under
`references/` (`objectstack-data/references/data-hooks.md`,
`objectstack-platform/references/plugin-hooks.md`), each linked directly from
its own `SKILL.md` and cited by path from sibling packages, so the anatomy
described a layout the catalog does not have.
Amend the anatomy rather than move the files: the reader reaches the same text
either way, and a move would be a multi-package rename with binding checks and
ceiling-row renames for zero reader value. `_index.md` keeps its "generated —
do not hand-edit" line and its `gen:skill-refs` pointer, byte-identical;
`rules/` keeps its per-topic role. The requirement travels with the permission:
a hand-authored reference file must be linked from `SKILL.md`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
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): admit hand-authored reference files under references/ in the catalog anatomy - #14786

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy
Sep 3, 2026
Merged

docs(skills): admit hand-authored reference files under references/ in the catalog anatomy#14786
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy

Conversation

@os-litant

@os-litantos-litant commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14655

What this changes

One line of skills/README.md, inside the "Skill anatomy" tree: the references/ entry
now carries a comment admitting hand-authored reference files.

-├── references/+├── references/ # may also hold hand-authored reference files linked from SKILL.md

The lane decision: option 2 (amend the anatomy, do not move the files)

The card offered two honest resolutions. The skills-lane seat chose option 2 in its
claim comment on #14655, and this PR implements exactly that.

The anatomy reserved references/ for the generated _index.md and assigned every
hand-authored per-topic file to rules/. Three published packages already keep
hand-authored anchors under references/:

packagefilelinked from its own SKILL.md
objectstack-datareferences/data-hooks.mdSKILL.md:50, :173, :522
objectstack-platformreferences/plugin-hooks.mdSKILL.md:657, :790

Both are cited by path from sibling packages as well, so option 1 (move to rules/) is a
multi-package rename carrying pointer updates, a check:skill-identifier-liveness Leg-2
binding re-check (bindings are keyed by file path plus heading) and ceiling-row renames in
scripts/check-skills-token-ratchet.mjs — for zero reader value, since the reader reaches
the same text either way. Amending the sentence that describes the catalog is the cheaper
and truer half of the pair.

Why it is one block, and one line inside it. The permission is stated where the layout
is defined, so a reader authoring a new skill sees it at the moment the question arises,
and the requirement travels with the permission: a hand-authored reference file must be
linked from SKILL.md. Everything else in the tree is byte-identical — SKILL.md,
the two _index.md comment lines (still "generated pointers", still "do not hand-edit",
still the pnpm --filter @objectstack/spec gen:skill-refs pointer), rules/ with its
per-topic role, contracts/ and evals/.

One spelling change in the two blocks below, declared. The tree's first line is a
placeholder for the skill directory, written in the file with angle brackets. GitHub's
body sanitizer deletes angle-bracket fragments from a PR body — it did exactly that on
this body's first write, leaving skills// — so the placeholder is spelled
SKILL-NAME here. That line is untouched by this PR either way; the changed line
contains no brackets and is quoted verbatim above.

Before

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

After

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/ # may also hold hand-authored reference files linked from SKILL.md
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

The illustrative examples the card sketched for the new comment (event lists, payload
tables, hook references) were left out deliberately: they are flavour, not contract, and
the block is under a shrink-or-neutral budget. The contract — optional, hand-authored,
linked from SKILL.md — is stated in full.

Token delta

skills/README.md, measured as ceil(utf8_bytes / 4):

utf8 bytestokens
before (df657d9df)79411986
after (3b9bf9435)80182005
delta+77+19

Net positive, and stated as such rather than bought back by degrading a neighbouring
sentence. The paid-for alternative was folding the two _index.md comment lines into one,
which would have cost the reader the runnable pnpm --filter @objectstack/spec gen:skill-refs command on the catalog's front page — a worse trade than +19 tokens.
skills/README.md is outside the token-ratchet population
(scripts/check-skills-token-ratchet.mjs:133, self-test at :790), so no ceiling row
exists for it and none moved; the gate run below confirms that.

Premise correction (premise_false)

The card's closing sentence — "Either way the generator-owned skills/README.md is
regenerated, never hand-edited"
— is false for this block, and the seat corrected it
at dispatch. Measured here at 3b9bf9435:

  • packages/spec/scripts/build-skill-docs.ts:12-13 states the contract in its own header:
    derived listings are "rewritten between BEGIN/END GENERATED: skills markers; prose
    outside the markers is preserved"
    .
  • The generated Index table ends at the END marker on skills/README.md:44. The "Skill
    anatomy" heading is at :50 and the tree at :52-62 — after it, in preserved prose.
  • So there is no generator source to amend: the block is hand-authored, and this PR edits
    it by hand. That the generated region was not touched is proved by
    check:skill-docs staying green, and by the reverse verification below showing that the
    same gate does go red for an edit inside the region.

Second, smaller correction: the seat's claim comment located the block at :44-58. At
this branch point it is the END marker that sits at :44; the anatomy heading is :50
and the tree :52-62. No effect on the decision.

Changeset

None, and the skip-changeset label is applied. This diff releases nothing from any
package — scripts/check-empty-changeset.mjs:359-361 enumerates that case by name:

* It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like) -> delete the changeset and apply the 'skip-changeset' label (route 2). The label is a gate-level exemption: it produces NO input for changesets/action.

The diff is exactly one file, skills/README.md, which is inside the enumerated skills/.

Gates

Head 3b9bf9435. The list is the union re-derived after the last edit with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(16 families, all matched via skills/README.md), plus the three the dispatch named
directly. Exit codes captured by redirect before any pipe; every run went through
scripts/pm/os-verify-lock.sh.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 130 declared cross-package glob(s) (92 unique) are covered by core or crosspkg, …
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-test-completeness.mjs3NOT MEASUREDPREREQUISITE NOT MET — this gate grades a saved turbo run test log, and no log was named. The gate's own text: "the local reading for this gate is NOT MEASURED. ⛔ It is not a red, and there is nothing here to fix."
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. (first run exited 3 on unbuilt prerequisites; measured after building @objectstack/formula then @objectstack/lint)
pnpm --filter @objectstack/spec run check:skill-docs0✓ skills/README.md · ✓ content/docs/ai/skills-reference.mdx · ✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5698 bare -- token(s) · …
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0All 117 self-test cases passed. · OK: 25 package(s) read outside themselves, all declared…
pnpm check:doc-authoring0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references.
pnpm check:merge-driver0✓ check-regen-pending self-test passed.
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions …
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: 4 copies of the decision frame are structurally isomorphic across 3 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) … Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(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-skills-token-ratchet.mjs --self-test0✓ check-skills-token-ratchet self-test: 64 cases pass.
node scripts/check-nul-bytes.mjs0check-nul-bytes: OK (scanned 8053 text file(s) … no raw ASCII control bytes).

18 measured green, 1 recorded NOT MEASURED (CI-only input), 0 red.

Reverse verification of check:skill-docs

The gate that proves the generated region was left alone is only worth quoting if it can
fail. Run from the committed state, under the verify lock, by a script with a trap on
EXIT, INT and TERM that re-checks the file out, and absolute paths throughout. No rebuild
leg is owed: the gate is tsx scripts/build-skill-docs.ts --check, which reads the
markdown sources from disk — no compiled artifact sits between the mutation and the
measurement.

legon-disk proofgate exitverdict line
mutated (one character inserted inside the generated region: the Formula row's expression domain cell)anchor count before 1 / after 0, injected count before 0 / after 1; blob 2940ce52…e023126d…1✗ skills/README.md is out of date — run pnpm --filter @objectstack/spec gen:skill-docs
restored (git checkout HEAD -- skills/README.md)blob back to 2940ce52… = the HEAD blob; git diff HEAD 0 bytes; git status --porcelain 0 lines0✅ Skill docs in sync

The predicted direction was red naming skills/README.md, and that is what it did.

ESLint

Narrowed to the changed file, and the narrowing is a measurement rather than a skip:

  1. Population, read from eslint.config.mjs itself: the base block at :971 is
    files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'], and every later block (:1015,
    :1054, :1103, :1172, :1212, :1238) narrows within those extensions. No block
    admits .md.
  2. Count, from --format json: one entry for skills/README.md, 0 errors, 1 warning,
    and the warning is File ignored because no matching configuration was supplied.
    ESLint exited 0.
  3. Invariance for untouched files: the config declares no parserOptions.project and
    no projectService anywhere (its own comment at :328 says so — "no
    parserOptions.project, no typed @typescript-eslint rules"), so no verdict on any
    file is a function of another file's contents. A markdown-only diff cannot move a lint
    result anywhere in the tree.

Follow-up, not this PR

  • The card's "require the link" half is stated here as text, not enforced by a gate.
    Adding one was explicitly out of scope. A gate is plausible — the shape would be "every
    non-_index.md file under a published skills/*/references/ is referenced by that
    skill's SKILL.md" — but it needs the lane's decision, and there is a third file to
    triage first (below).
  • skills/objectstack-ui/references/react-blocks.md is a generated file (its own
    header: "GENERATED by packages/spec/scripts/build-react-blocks-contract.ts — do not
    edit") living under references/ beside _index.md, and grep finds no link to it from
    objectstack-ui/SKILL.md. It is neither the generated _index.md the anatomy names nor
    the hand-authored, linked material this PR admits, so the amended sentence does not cover
    it either. Reported to the lane seat for dedup against Seam (spec lane, ruled): gen:react-blocks stops emitting skills/objectstack-ui/contracts/react-blocks.contract.json — the markdown rendering becomes the single AI-facing output (#14296 item 3 = A) #14559 rather than filed from
    here.

Merge

Draft, and it stays draft — governed skills/** surface (Prime Directive #14): the
maintainer's human merge is the review record. No reviewers requested, no auto-merge, no
ready flip.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

The catalog anatomy in `skills/README.md` reserved `references/` for the
generated `_index.md` alone and assigned every hand-authored per-topic file to
`rules/`. Three published skills already keep hand-authored anchors under
`references/` (`objectstack-data/references/data-hooks.md`,
`objectstack-platform/references/plugin-hooks.md`), each linked directly from
its own `SKILL.md` and cited by path from sibling packages, so the anatomy
described a layout the catalog does not have.
Amend the anatomy rather than move the files: the reader reaches the same text
either way, and a move would be a multi-package rename with binding checks and
ceiling-row renames for zero reader value. `_index.md` keeps its "generated —
do not hand-edit" line and its `gen:skill-refs` pointer, byte-identical;
`rules/` keeps its per-topic role. The requirement travels with the permission:
a hand-authored reference file must be linked from `SKILL.md`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
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): admit hand-authored reference files under references/ in the catalog anatomy - #14786

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy
Sep 3, 2026
Merged

docs(skills): admit hand-authored reference files under references/ in the catalog anatomy#14786
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy

Conversation

@os-litant

@os-litantos-litant commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14655

What this changes

One line of skills/README.md, inside the "Skill anatomy" tree: the references/ entry
now carries a comment admitting hand-authored reference files.

-├── references/+├── references/ # may also hold hand-authored reference files linked from SKILL.md

The lane decision: option 2 (amend the anatomy, do not move the files)

The card offered two honest resolutions. The skills-lane seat chose option 2 in its
claim comment on #14655, and this PR implements exactly that.

The anatomy reserved references/ for the generated _index.md and assigned every
hand-authored per-topic file to rules/. Three published packages already keep
hand-authored anchors under references/:

packagefilelinked from its own SKILL.md
objectstack-datareferences/data-hooks.mdSKILL.md:50, :173, :522
objectstack-platformreferences/plugin-hooks.mdSKILL.md:657, :790

Both are cited by path from sibling packages as well, so option 1 (move to rules/) is a
multi-package rename carrying pointer updates, a check:skill-identifier-liveness Leg-2
binding re-check (bindings are keyed by file path plus heading) and ceiling-row renames in
scripts/check-skills-token-ratchet.mjs — for zero reader value, since the reader reaches
the same text either way. Amending the sentence that describes the catalog is the cheaper
and truer half of the pair.

Why it is one block, and one line inside it. The permission is stated where the layout
is defined, so a reader authoring a new skill sees it at the moment the question arises,
and the requirement travels with the permission: a hand-authored reference file must be
linked from SKILL.md. Everything else in the tree is byte-identical — SKILL.md,
the two _index.md comment lines (still "generated pointers", still "do not hand-edit",
still the pnpm --filter @objectstack/spec gen:skill-refs pointer), rules/ with its
per-topic role, contracts/ and evals/.

One spelling change in the two blocks below, declared. The tree's first line is a
placeholder for the skill directory, written in the file with angle brackets. GitHub's
body sanitizer deletes angle-bracket fragments from a PR body — it did exactly that on
this body's first write, leaving skills// — so the placeholder is spelled
SKILL-NAME here. That line is untouched by this PR either way; the changed line
contains no brackets and is quoted verbatim above.

Before

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

After

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/ # may also hold hand-authored reference files linked from SKILL.md
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

The illustrative examples the card sketched for the new comment (event lists, payload
tables, hook references) were left out deliberately: they are flavour, not contract, and
the block is under a shrink-or-neutral budget. The contract — optional, hand-authored,
linked from SKILL.md — is stated in full.

Token delta

skills/README.md, measured as ceil(utf8_bytes / 4):

utf8 bytestokens
before (df657d9df)79411986
after (3b9bf9435)80182005
delta+77+19

Net positive, and stated as such rather than bought back by degrading a neighbouring
sentence. The paid-for alternative was folding the two _index.md comment lines into one,
which would have cost the reader the runnable pnpm --filter @objectstack/spec gen:skill-refs command on the catalog's front page — a worse trade than +19 tokens.
skills/README.md is outside the token-ratchet population
(scripts/check-skills-token-ratchet.mjs:133, self-test at :790), so no ceiling row
exists for it and none moved; the gate run below confirms that.

Premise correction (premise_false)

The card's closing sentence — "Either way the generator-owned skills/README.md is
regenerated, never hand-edited"
— is false for this block, and the seat corrected it
at dispatch. Measured here at 3b9bf9435:

  • packages/spec/scripts/build-skill-docs.ts:12-13 states the contract in its own header:
    derived listings are "rewritten between BEGIN/END GENERATED: skills markers; prose
    outside the markers is preserved"
    .
  • The generated Index table ends at the END marker on skills/README.md:44. The "Skill
    anatomy" heading is at :50 and the tree at :52-62 — after it, in preserved prose.
  • So there is no generator source to amend: the block is hand-authored, and this PR edits
    it by hand. That the generated region was not touched is proved by
    check:skill-docs staying green, and by the reverse verification below showing that the
    same gate does go red for an edit inside the region.

Second, smaller correction: the seat's claim comment located the block at :44-58. At
this branch point it is the END marker that sits at :44; the anatomy heading is :50
and the tree :52-62. No effect on the decision.

Changeset

None, and the skip-changeset label is applied. This diff releases nothing from any
package — scripts/check-empty-changeset.mjs:359-361 enumerates that case by name:

* It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like) -> delete the changeset and apply the 'skip-changeset' label (route 2). The label is a gate-level exemption: it produces NO input for changesets/action.

The diff is exactly one file, skills/README.md, which is inside the enumerated skills/.

Gates

Head 3b9bf9435. The list is the union re-derived after the last edit with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(16 families, all matched via skills/README.md), plus the three the dispatch named
directly. Exit codes captured by redirect before any pipe; every run went through
scripts/pm/os-verify-lock.sh.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 130 declared cross-package glob(s) (92 unique) are covered by core or crosspkg, …
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-test-completeness.mjs3NOT MEASUREDPREREQUISITE NOT MET — this gate grades a saved turbo run test log, and no log was named. The gate's own text: "the local reading for this gate is NOT MEASURED. ⛔ It is not a red, and there is nothing here to fix."
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. (first run exited 3 on unbuilt prerequisites; measured after building @objectstack/formula then @objectstack/lint)
pnpm --filter @objectstack/spec run check:skill-docs0✓ skills/README.md · ✓ content/docs/ai/skills-reference.mdx · ✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5698 bare -- token(s) · …
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0All 117 self-test cases passed. · OK: 25 package(s) read outside themselves, all declared…
pnpm check:doc-authoring0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references.
pnpm check:merge-driver0✓ check-regen-pending self-test passed.
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions …
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: 4 copies of the decision frame are structurally isomorphic across 3 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) … Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(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-skills-token-ratchet.mjs --self-test0✓ check-skills-token-ratchet self-test: 64 cases pass.
node scripts/check-nul-bytes.mjs0check-nul-bytes: OK (scanned 8053 text file(s) … no raw ASCII control bytes).

18 measured green, 1 recorded NOT MEASURED (CI-only input), 0 red.

Reverse verification of check:skill-docs

The gate that proves the generated region was left alone is only worth quoting if it can
fail. Run from the committed state, under the verify lock, by a script with a trap on
EXIT, INT and TERM that re-checks the file out, and absolute paths throughout. No rebuild
leg is owed: the gate is tsx scripts/build-skill-docs.ts --check, which reads the
markdown sources from disk — no compiled artifact sits between the mutation and the
measurement.

legon-disk proofgate exitverdict line
mutated (one character inserted inside the generated region: the Formula row's expression domain cell)anchor count before 1 / after 0, injected count before 0 / after 1; blob 2940ce52…e023126d…1✗ skills/README.md is out of date — run pnpm --filter @objectstack/spec gen:skill-docs
restored (git checkout HEAD -- skills/README.md)blob back to 2940ce52… = the HEAD blob; git diff HEAD 0 bytes; git status --porcelain 0 lines0✅ Skill docs in sync

The predicted direction was red naming skills/README.md, and that is what it did.

ESLint

Narrowed to the changed file, and the narrowing is a measurement rather than a skip:

  1. Population, read from eslint.config.mjs itself: the base block at :971 is
    files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'], and every later block (:1015,
    :1054, :1103, :1172, :1212, :1238) narrows within those extensions. No block
    admits .md.
  2. Count, from --format json: one entry for skills/README.md, 0 errors, 1 warning,
    and the warning is File ignored because no matching configuration was supplied.
    ESLint exited 0.
  3. Invariance for untouched files: the config declares no parserOptions.project and
    no projectService anywhere (its own comment at :328 says so — "no
    parserOptions.project, no typed @typescript-eslint rules"), so no verdict on any
    file is a function of another file's contents. A markdown-only diff cannot move a lint
    result anywhere in the tree.

Follow-up, not this PR

  • The card's "require the link" half is stated here as text, not enforced by a gate.
    Adding one was explicitly out of scope. A gate is plausible — the shape would be "every
    non-_index.md file under a published skills/*/references/ is referenced by that
    skill's SKILL.md" — but it needs the lane's decision, and there is a third file to
    triage first (below).
  • skills/objectstack-ui/references/react-blocks.md is a generated file (its own
    header: "GENERATED by packages/spec/scripts/build-react-blocks-contract.ts — do not
    edit") living under references/ beside _index.md, and grep finds no link to it from
    objectstack-ui/SKILL.md. It is neither the generated _index.md the anatomy names nor
    the hand-authored, linked material this PR admits, so the amended sentence does not cover
    it either. Reported to the lane seat for dedup against Seam (spec lane, ruled): gen:react-blocks stops emitting skills/objectstack-ui/contracts/react-blocks.contract.json — the markdown rendering becomes the single AI-facing output (#14296 item 3 = A) #14559 rather than filed from
    here.

Merge

Draft, and it stays draft — governed skills/** surface (Prime Directive #14): the
maintainer's human merge is the review record. No reviewers requested, no auto-merge, no
ready flip.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

The catalog anatomy in `skills/README.md` reserved `references/` for the
generated `_index.md` alone and assigned every hand-authored per-topic file to
`rules/`. Three published skills already keep hand-authored anchors under
`references/` (`objectstack-data/references/data-hooks.md`,
`objectstack-platform/references/plugin-hooks.md`), each linked directly from
its own `SKILL.md` and cited by path from sibling packages, so the anatomy
described a layout the catalog does not have.
Amend the anatomy rather than move the files: the reader reaches the same text
either way, and a move would be a multi-package rename with binding checks and
ceiling-row renames for zero reader value. `_index.md` keeps its "generated —
do not hand-edit" line and its `gen:skill-refs` pointer, byte-identical;
`rules/` keeps its per-topic role. The requirement travels with the permission:
a hand-authored reference file must be linked from `SKILL.md`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
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): admit hand-authored reference files under references/ in the catalog anatomy - #14786

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy
Sep 3, 2026
Merged

docs(skills): admit hand-authored reference files under references/ in the catalog anatomy#14786
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy

Conversation

@os-litant

@os-litantos-litant commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14655

What this changes

One line of skills/README.md, inside the "Skill anatomy" tree: the references/ entry
now carries a comment admitting hand-authored reference files.

-├── references/+├── references/ # may also hold hand-authored reference files linked from SKILL.md

The lane decision: option 2 (amend the anatomy, do not move the files)

The card offered two honest resolutions. The skills-lane seat chose option 2 in its
claim comment on #14655, and this PR implements exactly that.

The anatomy reserved references/ for the generated _index.md and assigned every
hand-authored per-topic file to rules/. Three published packages already keep
hand-authored anchors under references/:

packagefilelinked from its own SKILL.md
objectstack-datareferences/data-hooks.mdSKILL.md:50, :173, :522
objectstack-platformreferences/plugin-hooks.mdSKILL.md:657, :790

Both are cited by path from sibling packages as well, so option 1 (move to rules/) is a
multi-package rename carrying pointer updates, a check:skill-identifier-liveness Leg-2
binding re-check (bindings are keyed by file path plus heading) and ceiling-row renames in
scripts/check-skills-token-ratchet.mjs — for zero reader value, since the reader reaches
the same text either way. Amending the sentence that describes the catalog is the cheaper
and truer half of the pair.

Why it is one block, and one line inside it. The permission is stated where the layout
is defined, so a reader authoring a new skill sees it at the moment the question arises,
and the requirement travels with the permission: a hand-authored reference file must be
linked from SKILL.md. Everything else in the tree is byte-identical — SKILL.md,
the two _index.md comment lines (still "generated pointers", still "do not hand-edit",
still the pnpm --filter @objectstack/spec gen:skill-refs pointer), rules/ with its
per-topic role, contracts/ and evals/.

One spelling change in the two blocks below, declared. The tree's first line is a
placeholder for the skill directory, written in the file with angle brackets. GitHub's
body sanitizer deletes angle-bracket fragments from a PR body — it did exactly that on
this body's first write, leaving skills// — so the placeholder is spelled
SKILL-NAME here. That line is untouched by this PR either way; the changed line
contains no brackets and is quoted verbatim above.

Before

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

After

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/ # may also hold hand-authored reference files linked from SKILL.md
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

The illustrative examples the card sketched for the new comment (event lists, payload
tables, hook references) were left out deliberately: they are flavour, not contract, and
the block is under a shrink-or-neutral budget. The contract — optional, hand-authored,
linked from SKILL.md — is stated in full.

Token delta

skills/README.md, measured as ceil(utf8_bytes / 4):

utf8 bytestokens
before (df657d9df)79411986
after (3b9bf9435)80182005
delta+77+19

Net positive, and stated as such rather than bought back by degrading a neighbouring
sentence. The paid-for alternative was folding the two _index.md comment lines into one,
which would have cost the reader the runnable pnpm --filter @objectstack/spec gen:skill-refs command on the catalog's front page — a worse trade than +19 tokens.
skills/README.md is outside the token-ratchet population
(scripts/check-skills-token-ratchet.mjs:133, self-test at :790), so no ceiling row
exists for it and none moved; the gate run below confirms that.

Premise correction (premise_false)

The card's closing sentence — "Either way the generator-owned skills/README.md is
regenerated, never hand-edited"
— is false for this block, and the seat corrected it
at dispatch. Measured here at 3b9bf9435:

  • packages/spec/scripts/build-skill-docs.ts:12-13 states the contract in its own header:
    derived listings are "rewritten between BEGIN/END GENERATED: skills markers; prose
    outside the markers is preserved"
    .
  • The generated Index table ends at the END marker on skills/README.md:44. The "Skill
    anatomy" heading is at :50 and the tree at :52-62 — after it, in preserved prose.
  • So there is no generator source to amend: the block is hand-authored, and this PR edits
    it by hand. That the generated region was not touched is proved by
    check:skill-docs staying green, and by the reverse verification below showing that the
    same gate does go red for an edit inside the region.

Second, smaller correction: the seat's claim comment located the block at :44-58. At
this branch point it is the END marker that sits at :44; the anatomy heading is :50
and the tree :52-62. No effect on the decision.

Changeset

None, and the skip-changeset label is applied. This diff releases nothing from any
package — scripts/check-empty-changeset.mjs:359-361 enumerates that case by name:

* It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like) -> delete the changeset and apply the 'skip-changeset' label (route 2). The label is a gate-level exemption: it produces NO input for changesets/action.

The diff is exactly one file, skills/README.md, which is inside the enumerated skills/.

Gates

Head 3b9bf9435. The list is the union re-derived after the last edit with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(16 families, all matched via skills/README.md), plus the three the dispatch named
directly. Exit codes captured by redirect before any pipe; every run went through
scripts/pm/os-verify-lock.sh.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 130 declared cross-package glob(s) (92 unique) are covered by core or crosspkg, …
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-test-completeness.mjs3NOT MEASUREDPREREQUISITE NOT MET — this gate grades a saved turbo run test log, and no log was named. The gate's own text: "the local reading for this gate is NOT MEASURED. ⛔ It is not a red, and there is nothing here to fix."
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. (first run exited 3 on unbuilt prerequisites; measured after building @objectstack/formula then @objectstack/lint)
pnpm --filter @objectstack/spec run check:skill-docs0✓ skills/README.md · ✓ content/docs/ai/skills-reference.mdx · ✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5698 bare -- token(s) · …
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0All 117 self-test cases passed. · OK: 25 package(s) read outside themselves, all declared…
pnpm check:doc-authoring0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references.
pnpm check:merge-driver0✓ check-regen-pending self-test passed.
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions …
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: 4 copies of the decision frame are structurally isomorphic across 3 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) … Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(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-skills-token-ratchet.mjs --self-test0✓ check-skills-token-ratchet self-test: 64 cases pass.
node scripts/check-nul-bytes.mjs0check-nul-bytes: OK (scanned 8053 text file(s) … no raw ASCII control bytes).

18 measured green, 1 recorded NOT MEASURED (CI-only input), 0 red.

Reverse verification of check:skill-docs

The gate that proves the generated region was left alone is only worth quoting if it can
fail. Run from the committed state, under the verify lock, by a script with a trap on
EXIT, INT and TERM that re-checks the file out, and absolute paths throughout. No rebuild
leg is owed: the gate is tsx scripts/build-skill-docs.ts --check, which reads the
markdown sources from disk — no compiled artifact sits between the mutation and the
measurement.

legon-disk proofgate exitverdict line
mutated (one character inserted inside the generated region: the Formula row's expression domain cell)anchor count before 1 / after 0, injected count before 0 / after 1; blob 2940ce52…e023126d…1✗ skills/README.md is out of date — run pnpm --filter @objectstack/spec gen:skill-docs
restored (git checkout HEAD -- skills/README.md)blob back to 2940ce52… = the HEAD blob; git diff HEAD 0 bytes; git status --porcelain 0 lines0✅ Skill docs in sync

The predicted direction was red naming skills/README.md, and that is what it did.

ESLint

Narrowed to the changed file, and the narrowing is a measurement rather than a skip:

  1. Population, read from eslint.config.mjs itself: the base block at :971 is
    files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'], and every later block (:1015,
    :1054, :1103, :1172, :1212, :1238) narrows within those extensions. No block
    admits .md.
  2. Count, from --format json: one entry for skills/README.md, 0 errors, 1 warning,
    and the warning is File ignored because no matching configuration was supplied.
    ESLint exited 0.
  3. Invariance for untouched files: the config declares no parserOptions.project and
    no projectService anywhere (its own comment at :328 says so — "no
    parserOptions.project, no typed @typescript-eslint rules"), so no verdict on any
    file is a function of another file's contents. A markdown-only diff cannot move a lint
    result anywhere in the tree.

Follow-up, not this PR

  • The card's "require the link" half is stated here as text, not enforced by a gate.
    Adding one was explicitly out of scope. A gate is plausible — the shape would be "every
    non-_index.md file under a published skills/*/references/ is referenced by that
    skill's SKILL.md" — but it needs the lane's decision, and there is a third file to
    triage first (below).
  • skills/objectstack-ui/references/react-blocks.md is a generated file (its own
    header: "GENERATED by packages/spec/scripts/build-react-blocks-contract.ts — do not
    edit") living under references/ beside _index.md, and grep finds no link to it from
    objectstack-ui/SKILL.md. It is neither the generated _index.md the anatomy names nor
    the hand-authored, linked material this PR admits, so the amended sentence does not cover
    it either. Reported to the lane seat for dedup against Seam (spec lane, ruled): gen:react-blocks stops emitting skills/objectstack-ui/contracts/react-blocks.contract.json — the markdown rendering becomes the single AI-facing output (#14296 item 3 = A) #14559 rather than filed from
    here.

Merge

Draft, and it stays draft — governed skills/** surface (Prime Directive #14): the
maintainer's human merge is the review record. No reviewers requested, no auto-merge, no
ready flip.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

The catalog anatomy in `skills/README.md` reserved `references/` for the
generated `_index.md` alone and assigned every hand-authored per-topic file to
`rules/`. Three published skills already keep hand-authored anchors under
`references/` (`objectstack-data/references/data-hooks.md`,
`objectstack-platform/references/plugin-hooks.md`), each linked directly from
its own `SKILL.md` and cited by path from sibling packages, so the anatomy
described a layout the catalog does not have.
Amend the anatomy rather than move the files: the reader reaches the same text
either way, and a move would be a multi-package rename with binding checks and
ceiling-row renames for zero reader value. `_index.md` keeps its "generated —
do not hand-edit" line and its `gen:skill-refs` pointer, byte-identical;
`rules/` keeps its per-topic role. The requirement travels with the permission:
a hand-authored reference file must be linked from `SKILL.md`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
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): admit hand-authored reference files under references/ in the catalog anatomy - #14786

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy
Sep 3, 2026
Merged

docs(skills): admit hand-authored reference files under references/ in the catalog anatomy#14786
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy

Conversation

@os-litant

@os-litantos-litant commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14655

What this changes

One line of skills/README.md, inside the "Skill anatomy" tree: the references/ entry
now carries a comment admitting hand-authored reference files.

-├── references/+├── references/ # may also hold hand-authored reference files linked from SKILL.md

The lane decision: option 2 (amend the anatomy, do not move the files)

The card offered two honest resolutions. The skills-lane seat chose option 2 in its
claim comment on #14655, and this PR implements exactly that.

The anatomy reserved references/ for the generated _index.md and assigned every
hand-authored per-topic file to rules/. Three published packages already keep
hand-authored anchors under references/:

packagefilelinked from its own SKILL.md
objectstack-datareferences/data-hooks.mdSKILL.md:50, :173, :522
objectstack-platformreferences/plugin-hooks.mdSKILL.md:657, :790

Both are cited by path from sibling packages as well, so option 1 (move to rules/) is a
multi-package rename carrying pointer updates, a check:skill-identifier-liveness Leg-2
binding re-check (bindings are keyed by file path plus heading) and ceiling-row renames in
scripts/check-skills-token-ratchet.mjs — for zero reader value, since the reader reaches
the same text either way. Amending the sentence that describes the catalog is the cheaper
and truer half of the pair.

Why it is one block, and one line inside it. The permission is stated where the layout
is defined, so a reader authoring a new skill sees it at the moment the question arises,
and the requirement travels with the permission: a hand-authored reference file must be
linked from SKILL.md. Everything else in the tree is byte-identical — SKILL.md,
the two _index.md comment lines (still "generated pointers", still "do not hand-edit",
still the pnpm --filter @objectstack/spec gen:skill-refs pointer), rules/ with its
per-topic role, contracts/ and evals/.

One spelling change in the two blocks below, declared. The tree's first line is a
placeholder for the skill directory, written in the file with angle brackets. GitHub's
body sanitizer deletes angle-bracket fragments from a PR body — it did exactly that on
this body's first write, leaving skills// — so the placeholder is spelled
SKILL-NAME here. That line is untouched by this PR either way; the changed line
contains no brackets and is quoted verbatim above.

Before

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

After

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/ # may also hold hand-authored reference files linked from SKILL.md
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

The illustrative examples the card sketched for the new comment (event lists, payload
tables, hook references) were left out deliberately: they are flavour, not contract, and
the block is under a shrink-or-neutral budget. The contract — optional, hand-authored,
linked from SKILL.md — is stated in full.

Token delta

skills/README.md, measured as ceil(utf8_bytes / 4):

utf8 bytestokens
before (df657d9df)79411986
after (3b9bf9435)80182005
delta+77+19

Net positive, and stated as such rather than bought back by degrading a neighbouring
sentence. The paid-for alternative was folding the two _index.md comment lines into one,
which would have cost the reader the runnable pnpm --filter @objectstack/spec gen:skill-refs command on the catalog's front page — a worse trade than +19 tokens.
skills/README.md is outside the token-ratchet population
(scripts/check-skills-token-ratchet.mjs:133, self-test at :790), so no ceiling row
exists for it and none moved; the gate run below confirms that.

Premise correction (premise_false)

The card's closing sentence — "Either way the generator-owned skills/README.md is
regenerated, never hand-edited"
— is false for this block, and the seat corrected it
at dispatch. Measured here at 3b9bf9435:

  • packages/spec/scripts/build-skill-docs.ts:12-13 states the contract in its own header:
    derived listings are "rewritten between BEGIN/END GENERATED: skills markers; prose
    outside the markers is preserved"
    .
  • The generated Index table ends at the END marker on skills/README.md:44. The "Skill
    anatomy" heading is at :50 and the tree at :52-62 — after it, in preserved prose.
  • So there is no generator source to amend: the block is hand-authored, and this PR edits
    it by hand. That the generated region was not touched is proved by
    check:skill-docs staying green, and by the reverse verification below showing that the
    same gate does go red for an edit inside the region.

Second, smaller correction: the seat's claim comment located the block at :44-58. At
this branch point it is the END marker that sits at :44; the anatomy heading is :50
and the tree :52-62. No effect on the decision.

Changeset

None, and the skip-changeset label is applied. This diff releases nothing from any
package — scripts/check-empty-changeset.mjs:359-361 enumerates that case by name:

* It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like) -> delete the changeset and apply the 'skip-changeset' label (route 2). The label is a gate-level exemption: it produces NO input for changesets/action.

The diff is exactly one file, skills/README.md, which is inside the enumerated skills/.

Gates

Head 3b9bf9435. The list is the union re-derived after the last edit with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(16 families, all matched via skills/README.md), plus the three the dispatch named
directly. Exit codes captured by redirect before any pipe; every run went through
scripts/pm/os-verify-lock.sh.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 130 declared cross-package glob(s) (92 unique) are covered by core or crosspkg, …
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-test-completeness.mjs3NOT MEASUREDPREREQUISITE NOT MET — this gate grades a saved turbo run test log, and no log was named. The gate's own text: "the local reading for this gate is NOT MEASURED. ⛔ It is not a red, and there is nothing here to fix."
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. (first run exited 3 on unbuilt prerequisites; measured after building @objectstack/formula then @objectstack/lint)
pnpm --filter @objectstack/spec run check:skill-docs0✓ skills/README.md · ✓ content/docs/ai/skills-reference.mdx · ✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5698 bare -- token(s) · …
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0All 117 self-test cases passed. · OK: 25 package(s) read outside themselves, all declared…
pnpm check:doc-authoring0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references.
pnpm check:merge-driver0✓ check-regen-pending self-test passed.
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions …
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: 4 copies of the decision frame are structurally isomorphic across 3 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) … Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(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-skills-token-ratchet.mjs --self-test0✓ check-skills-token-ratchet self-test: 64 cases pass.
node scripts/check-nul-bytes.mjs0check-nul-bytes: OK (scanned 8053 text file(s) … no raw ASCII control bytes).

18 measured green, 1 recorded NOT MEASURED (CI-only input), 0 red.

Reverse verification of check:skill-docs

The gate that proves the generated region was left alone is only worth quoting if it can
fail. Run from the committed state, under the verify lock, by a script with a trap on
EXIT, INT and TERM that re-checks the file out, and absolute paths throughout. No rebuild
leg is owed: the gate is tsx scripts/build-skill-docs.ts --check, which reads the
markdown sources from disk — no compiled artifact sits between the mutation and the
measurement.

legon-disk proofgate exitverdict line
mutated (one character inserted inside the generated region: the Formula row's expression domain cell)anchor count before 1 / after 0, injected count before 0 / after 1; blob 2940ce52…e023126d…1✗ skills/README.md is out of date — run pnpm --filter @objectstack/spec gen:skill-docs
restored (git checkout HEAD -- skills/README.md)blob back to 2940ce52… = the HEAD blob; git diff HEAD 0 bytes; git status --porcelain 0 lines0✅ Skill docs in sync

The predicted direction was red naming skills/README.md, and that is what it did.

ESLint

Narrowed to the changed file, and the narrowing is a measurement rather than a skip:

  1. Population, read from eslint.config.mjs itself: the base block at :971 is
    files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'], and every later block (:1015,
    :1054, :1103, :1172, :1212, :1238) narrows within those extensions. No block
    admits .md.
  2. Count, from --format json: one entry for skills/README.md, 0 errors, 1 warning,
    and the warning is File ignored because no matching configuration was supplied.
    ESLint exited 0.
  3. Invariance for untouched files: the config declares no parserOptions.project and
    no projectService anywhere (its own comment at :328 says so — "no
    parserOptions.project, no typed @typescript-eslint rules"), so no verdict on any
    file is a function of another file's contents. A markdown-only diff cannot move a lint
    result anywhere in the tree.

Follow-up, not this PR

  • The card's "require the link" half is stated here as text, not enforced by a gate.
    Adding one was explicitly out of scope. A gate is plausible — the shape would be "every
    non-_index.md file under a published skills/*/references/ is referenced by that
    skill's SKILL.md" — but it needs the lane's decision, and there is a third file to
    triage first (below).
  • skills/objectstack-ui/references/react-blocks.md is a generated file (its own
    header: "GENERATED by packages/spec/scripts/build-react-blocks-contract.ts — do not
    edit") living under references/ beside _index.md, and grep finds no link to it from
    objectstack-ui/SKILL.md. It is neither the generated _index.md the anatomy names nor
    the hand-authored, linked material this PR admits, so the amended sentence does not cover
    it either. Reported to the lane seat for dedup against Seam (spec lane, ruled): gen:react-blocks stops emitting skills/objectstack-ui/contracts/react-blocks.contract.json — the markdown rendering becomes the single AI-facing output (#14296 item 3 = A) #14559 rather than filed from
    here.

Merge

Draft, and it stays draft — governed skills/** surface (Prime Directive #14): the
maintainer's human merge is the review record. No reviewers requested, no auto-merge, no
ready flip.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

The catalog anatomy in `skills/README.md` reserved `references/` for the
generated `_index.md` alone and assigned every hand-authored per-topic file to
`rules/`. Three published skills already keep hand-authored anchors under
`references/` (`objectstack-data/references/data-hooks.md`,
`objectstack-platform/references/plugin-hooks.md`), each linked directly from
its own `SKILL.md` and cited by path from sibling packages, so the anatomy
described a layout the catalog does not have.
Amend the anatomy rather than move the files: the reader reaches the same text
either way, and a move would be a multi-package rename with binding checks and
ceiling-row renames for zero reader value. `_index.md` keeps its "generated —
do not hand-edit" line and its `gen:skill-refs` pointer, byte-identical;
`rules/` keeps its per-topic role. The requirement travels with the permission:
a hand-authored reference file must be linked from `SKILL.md`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
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): admit hand-authored reference files under references/ in the catalog anatomy - #14786

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy
Sep 3, 2026
Merged

docs(skills): admit hand-authored reference files under references/ in the catalog anatomy#14786
os-zhuang merged 1 commit into
mainfrom
claude/issue-14655-references-anatomy

Conversation

@os-litant

@os-litantos-litant commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14655

What this changes

One line of skills/README.md, inside the "Skill anatomy" tree: the references/ entry
now carries a comment admitting hand-authored reference files.

-├── references/+├── references/ # may also hold hand-authored reference files linked from SKILL.md

The lane decision: option 2 (amend the anatomy, do not move the files)

The card offered two honest resolutions. The skills-lane seat chose option 2 in its
claim comment on #14655, and this PR implements exactly that.

The anatomy reserved references/ for the generated _index.md and assigned every
hand-authored per-topic file to rules/. Three published packages already keep
hand-authored anchors under references/:

packagefilelinked from its own SKILL.md
objectstack-datareferences/data-hooks.mdSKILL.md:50, :173, :522
objectstack-platformreferences/plugin-hooks.mdSKILL.md:657, :790

Both are cited by path from sibling packages as well, so option 1 (move to rules/) is a
multi-package rename carrying pointer updates, a check:skill-identifier-liveness Leg-2
binding re-check (bindings are keyed by file path plus heading) and ceiling-row renames in
scripts/check-skills-token-ratchet.mjs — for zero reader value, since the reader reaches
the same text either way. Amending the sentence that describes the catalog is the cheaper
and truer half of the pair.

Why it is one block, and one line inside it. The permission is stated where the layout
is defined, so a reader authoring a new skill sees it at the moment the question arises,
and the requirement travels with the permission: a hand-authored reference file must be
linked from SKILL.md. Everything else in the tree is byte-identical — SKILL.md,
the two _index.md comment lines (still "generated pointers", still "do not hand-edit",
still the pnpm --filter @objectstack/spec gen:skill-refs pointer), rules/ with its
per-topic role, contracts/ and evals/.

One spelling change in the two blocks below, declared. The tree's first line is a
placeholder for the skill directory, written in the file with angle brackets. GitHub's
body sanitizer deletes angle-bracket fragments from a PR body — it did exactly that on
this body's first write, leaving skills// — so the placeholder is spelled
SKILL-NAME here. That line is untouched by this PR either way; the changed line
contains no brackets and is quoted verbatim above.

Before

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

After

skills/SKILL-NAME/
├── SKILL.md # frontmatter + prose guide
├── references/ # may also hold hand-authored reference files linked from SKILL.md
│ └── _index.md # generated pointers into @objectstack/spec sources
│ # (pnpm --filter @objectstack/spec gen:skill-refs — do not hand-edit)
├── rules/ # (optional) detailed per-topic rule files linked from SKILL.md
├── contracts/ # (optional) generated machine-readable contracts (e.g. react-blocks)
└── evals/ # skill eval fixtures — used by maintainers to score the skill,
# inert (but harmless) in consumer installs

The illustrative examples the card sketched for the new comment (event lists, payload
tables, hook references) were left out deliberately: they are flavour, not contract, and
the block is under a shrink-or-neutral budget. The contract — optional, hand-authored,
linked from SKILL.md — is stated in full.

Token delta

skills/README.md, measured as ceil(utf8_bytes / 4):

utf8 bytestokens
before (df657d9df)79411986
after (3b9bf9435)80182005
delta+77+19

Net positive, and stated as such rather than bought back by degrading a neighbouring
sentence. The paid-for alternative was folding the two _index.md comment lines into one,
which would have cost the reader the runnable pnpm --filter @objectstack/spec gen:skill-refs command on the catalog's front page — a worse trade than +19 tokens.
skills/README.md is outside the token-ratchet population
(scripts/check-skills-token-ratchet.mjs:133, self-test at :790), so no ceiling row
exists for it and none moved; the gate run below confirms that.

Premise correction (premise_false)

The card's closing sentence — "Either way the generator-owned skills/README.md is
regenerated, never hand-edited"
— is false for this block, and the seat corrected it
at dispatch. Measured here at 3b9bf9435:

  • packages/spec/scripts/build-skill-docs.ts:12-13 states the contract in its own header:
    derived listings are "rewritten between BEGIN/END GENERATED: skills markers; prose
    outside the markers is preserved"
    .
  • The generated Index table ends at the END marker on skills/README.md:44. The "Skill
    anatomy" heading is at :50 and the tree at :52-62 — after it, in preserved prose.
  • So there is no generator source to amend: the block is hand-authored, and this PR edits
    it by hand. That the generated region was not touched is proved by
    check:skill-docs staying green, and by the reverse verification below showing that the
    same gate does go red for an edit inside the region.

Second, smaller correction: the seat's claim comment located the block at :44-58. At
this branch point it is the END marker that sits at :44; the anatomy heading is :50
and the tree :52-62. No effect on the decision.

Changeset

None, and the skip-changeset label is applied. This diff releases nothing from any
package — scripts/check-empty-changeset.mjs:359-361 enumerates that case by name:

* It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like) -> delete the changeset and apply the 'skip-changeset' label (route 2). The label is a gate-level exemption: it produces NO input for changesets/action.

The diff is exactly one file, skills/README.md, which is inside the enumerated skills/.

Gates

Head 3b9bf9435. The list is the union re-derived after the last edit with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(16 families, all matched via skills/README.md), plus the three the dispatch named
directly. Exit codes captured by redirect before any pipe; every run went through
scripts/pm/os-verify-lock.sh.

gateexitits own verdict line
node scripts/check-ci-filter-parity.mjs0OK: all 130 declared cross-package glob(s) (92 unique) are covered by core or crosspkg, …
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-test-completeness.mjs3NOT MEASUREDPREREQUISITE NOT MET — this gate grades a saved turbo run test log, and no log was named. The gate's own text: "the local reading for this gate is NOT MEASURED. ⛔ It is not a red, and there is nothing here to fix."
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. (first run exited 3 on unbuilt prerequisites; measured after building @objectstack/formula then @objectstack/lint)
pnpm --filter @objectstack/spec run check:skill-docs0✓ skills/README.md · ✓ content/docs/ai/skills-reference.mdx · ✅ Skill docs in sync
pnpm check:agent-test-spelling0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5698 bare -- token(s) · …
pnpm check:corpus-claim-drift0check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
pnpm check:cross-package-test-inputs0All 117 self-test cases passed. · OK: 25 package(s) read outside themselves, all declared…
pnpm check:doc-authoring0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references.
pnpm check:merge-driver0✓ check-regen-pending self-test passed.
pnpm check:pm-governed-merges0✓ check-governed-merges --self-test: 243 assertions …
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: 4 copies of the decision frame are structurally isomorphic across 3 files
pnpm check:skill-identifier-liveness0check-skill-identifier-liveness OK — Leg 1: 465 citation(s) over 46 published file(s) … Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(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-skills-token-ratchet.mjs --self-test0✓ check-skills-token-ratchet self-test: 64 cases pass.
node scripts/check-nul-bytes.mjs0check-nul-bytes: OK (scanned 8053 text file(s) … no raw ASCII control bytes).

18 measured green, 1 recorded NOT MEASURED (CI-only input), 0 red.

Reverse verification of check:skill-docs

The gate that proves the generated region was left alone is only worth quoting if it can
fail. Run from the committed state, under the verify lock, by a script with a trap on
EXIT, INT and TERM that re-checks the file out, and absolute paths throughout. No rebuild
leg is owed: the gate is tsx scripts/build-skill-docs.ts --check, which reads the
markdown sources from disk — no compiled artifact sits between the mutation and the
measurement.

legon-disk proofgate exitverdict line
mutated (one character inserted inside the generated region: the Formula row's expression domain cell)anchor count before 1 / after 0, injected count before 0 / after 1; blob 2940ce52…e023126d…1✗ skills/README.md is out of date — run pnpm --filter @objectstack/spec gen:skill-docs
restored (git checkout HEAD -- skills/README.md)blob back to 2940ce52… = the HEAD blob; git diff HEAD 0 bytes; git status --porcelain 0 lines0✅ Skill docs in sync

The predicted direction was red naming skills/README.md, and that is what it did.

ESLint

Narrowed to the changed file, and the narrowing is a measurement rather than a skip:

  1. Population, read from eslint.config.mjs itself: the base block at :971 is
    files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'], and every later block (:1015,
    :1054, :1103, :1172, :1212, :1238) narrows within those extensions. No block
    admits .md.
  2. Count, from --format json: one entry for skills/README.md, 0 errors, 1 warning,
    and the warning is File ignored because no matching configuration was supplied.
    ESLint exited 0.
  3. Invariance for untouched files: the config declares no parserOptions.project and
    no projectService anywhere (its own comment at :328 says so — "no
    parserOptions.project, no typed @typescript-eslint rules"), so no verdict on any
    file is a function of another file's contents. A markdown-only diff cannot move a lint
    result anywhere in the tree.

Follow-up, not this PR

  • The card's "require the link" half is stated here as text, not enforced by a gate.
    Adding one was explicitly out of scope. A gate is plausible — the shape would be "every
    non-_index.md file under a published skills/*/references/ is referenced by that
    skill's SKILL.md" — but it needs the lane's decision, and there is a third file to
    triage first (below).
  • skills/objectstack-ui/references/react-blocks.md is a generated file (its own
    header: "GENERATED by packages/spec/scripts/build-react-blocks-contract.ts — do not
    edit") living under references/ beside _index.md, and grep finds no link to it from
    objectstack-ui/SKILL.md. It is neither the generated _index.md the anatomy names nor
    the hand-authored, linked material this PR admits, so the amended sentence does not cover
    it either. Reported to the lane seat for dedup against Seam (spec lane, ruled): gen:react-blocks stops emitting skills/objectstack-ui/contracts/react-blocks.contract.json — the markdown rendering becomes the single AI-facing output (#14296 item 3 = A) #14559 rather than filed from
    here.

Merge

Draft, and it stays draft — governed skills/** surface (Prime Directive #14): the
maintainer's human merge is the review record. No reviewers requested, no auto-merge, no
ready flip.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1

The catalog anatomy in `skills/README.md` reserved `references/` for the
generated `_index.md` alone and assigned every hand-authored per-topic file to
`rules/`. Three published skills already keep hand-authored anchors under
`references/` (`objectstack-data/references/data-hooks.md`,
`objectstack-platform/references/plugin-hooks.md`), each linked directly from
its own `SKILL.md` and cited by path from sibling packages, so the anatomy
described a layout the catalog does not have.
Amend the anatomy rather than move the files: the reader reaches the same text
either way, and a move would be a multi-package rename with binding checks and
ceiling-row renames for zero reader value. `_index.md` keeps its "generated —
do not hand-edit" line and its `gen:skill-refs` pointer, byte-identical;
`rules/` keeps its per-topic role. The requirement travels with the permission:
a hand-authored reference file must be linked from `SKILL.md`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
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