Skip to content

Commit 71f9cd1

Browse files
os-litantclaude
andauthored
docs(spec): strip the internal issue-ids projected into the published skill catalog (#12097)
Fourteen `#NNNN` references survived the 2026-08-23 strip of the published skill corpus because they were never authored in `skills/**`: they are projected there from `.describe()` / TSDoc text in `packages/spec/src/**` by `gen:skill-refs` and `gen:react-blocks`. Six source sites are rewritten to keep the teaching without the citation, and the artifacts are regenerated. The doc-authoring gate's path exemption for the generated artifacts is dropped in the same change — it existed only because those files still carried projected ids, and an exemption over a clean surface is where the next regeneration would smuggle one back in. Its self-test now pins the generated artifacts as IN scope and reds on an id planted in one. Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0fd4899 commit 71f9cd1

16 files changed

Lines changed: 94 additions & 51 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
docs(spec): strip the internal issue-id references that were projected into the published skill catalog
6+
7+
The 2026-08-23 ruling stripped internal `#NNNN` citations from the published
8+
skill corpus, but 14 of them were not authored in `skills/**` at all — they were
9+
projected there from `.describe()` / TSDoc text in `packages/spec/src/**` by
10+
`gen:skill-refs` and `gen:react-blocks`, so a hand-edit of the corpus could not
11+
reach them and a regeneration would have put them straight back.
12+
13+
Six source sites are rewritten to say the same thing without the citation, and
14+
the artifacts are regenerated: the module summaries of `data/driver/common`,
15+
`data/driver/config-registry`, `data/driver/turso`, `shared/retry-policy` and
16+
`system/translation`, plus the `ListView.objectName` / `ListView.viewType`
17+
deprecation notes and the `<Block>` summary in `ui/react-blocks`. The teaching in
18+
each is kept, per the standing ruling of 2026-08-12, verbatim and untranslated:
19+
「处理 issue 时犯的错应该总结成经验,保留 issue id没有意义」.
20+
21+
Customer-facing text changes in three places from the one source edit: the
22+
published catalog (`skills/*/references/_index.md`,
23+
`skills/objectstack-ui/references/react-blocks.md` and its sibling
24+
`contracts/react-blocks.contract.json`), and the docs site
25+
(`content/docs/references/data/driver-common.mdx`, `driver-turso.mdx`). No
26+
schema shape, no `.describe()` used for validation, and no accept/reject
27+
behaviour changes — the edits are comment and documentation text only.
28+
29+
The doc-authoring gate's path exemption for the generated artifacts is removed
30+
in the same change: it existed only because those files still carried projected
31+
ids, and an exemption over a surface that no longer needs one is where the next
32+
regeneration would smuggle one back in.

content/docs/references/data/driver-common.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description: Driver Common protocol schemas
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
77

8-
Shared building blocks for the per-driver `datasource.config` shapes (#4410).
8+
Shared building blocks for the per-driver `datasource.config` shapes.
99

1010
Every schema under `data/driver/` describes ONE driver's `config` slot — the
1111
keys an author may write and the platform actually reads. They are the

content/docs/references/data/driver-turso.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description: Driver Turso protocol schemas
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
77

8-
Turso / libSQL Driver Protocol (#6345).
8+
Turso / libSQL Driver Protocol.
99

1010
## Why this arrives late, and what it closes
1111

packages/spec/src/data/driver/common.zod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { z } from 'zod';
44

55
/**
6-
* Shared building blocks for the per-driver `datasource.config` shapes (#4410).
6+
* Shared building blocks for the per-driver `datasource.config` shapes.
77
*
88
* Every schema under `data/driver/` describes ONE driver's `config` slot — the
99
* keys an author may write and the platform actually reads. They are the

packages/spec/src/data/driver/config-registry.zod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
import { getTursoConfigJsonSchema, TursoConfigSchema } from './turso.zod';
1616

1717
/**
18-
* The driver-id → `datasource.config` shape registry (#4410).
18+
* The driver-id → `datasource.config` shape registry.
1919
*
2020
* ## Why this exists
2121
*

packages/spec/src/data/driver/turso.zod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
} from './common.zod';
1818

1919
/**
20-
* Turso / libSQL Driver Protocol (#6345).
20+
* Turso / libSQL Driver Protocol.
2121
*
2222
* ## Why this arrives late, and what it closes
2323
*

packages/spec/src/shared/retry-policy.zod.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
/**
44
* @module shared/retry-policy
55
*
6-
* The **single declaration** of the exponential-backoff retry policy (#4661,
7-
* the #4535 C8 dual-source cluster; completed for the anonymous inline blocks
8-
* by #4964 / #4962).
6+
* The **single declaration** of the exponential-backoff retry policy.
7+
*
8+
* Converged in 17.0.0 in two passes: first the two same-named exports a
9+
* dual-source scan could see, then the anonymous inline blocks it could not.
910
*
1011
* Until 17 this shape existed twice — `automation/control-flow.zod.ts` (the
1112
* `try_catch` node's `retry` region) and `system/job.zod.ts` (`job.retryPolicy`)

packages/spec/src/system/translation.zod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const LocaleSchema = lazySchema(() => z.string().describe('BCP-47 Languag
1313
export type Locale = z.input<typeof LocaleSchema>;
1414

1515
/**
16-
* Shared history sentence for every shape in this file (#4001).
16+
* Shared history sentence for every shape in this file.
1717
*
1818
* Translation data has the most literal version of the silent-strip failure in
1919
* the whole spec: a misspelled group or key is dropped, the bundle saves or

packages/spec/src/ui/react-blocks.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ export const REACT_BLOCKS: ReactBlockDef[] = [
275275
replacedBy: 'data',
276276
note: "Write the metadata-tier data source instead: data={{ provider: 'object', object: '…' }} — the same spelling a metadata list view authors. objectName keeps working during the deprecation window.",
277277
},
278-
description: "[DEPRECATED → `data={{ provider: 'object', object }}`] The object this block binds to (server-connected). Converging on the metadata-tier spelling (#11284); this alias is removed after the deprecation window.",
278+
description: "[DEPRECATED → `data={{ provider: 'object', object }}`] The object this block binds to (server-connected). Converging on the metadata-tier spelling; this alias is removed after the deprecation window.",
279279
},
280280
{
281281
name: 'viewType',
@@ -285,7 +285,7 @@ export const REACT_BLOCKS: ReactBlockDef[] = [
285285
replacedBy: 'type',
286286
note: 'Write type="kanban" (ListViewSchema\'s own `type`, the metadata-tier view kind) instead. viewType keeps working during the deprecation window.',
287287
},
288-
description: '[DEPRECATED → `type`] Which visualization to render (default grid). Converging on the metadata-tier spelling (#11284): write `type`, the same key a metadata list view authors.',
288+
description: '[DEPRECATED → `type`] Which visualization to render (default grid). Converging on the metadata-tier spelling: write `type`, the same key a metadata list view authors.',
289289
},
290290
{ name: 'filters', type: "FilterArray e.g. ['status','=','active']", kind: 'controlled', description: 'ObjectQL base filter; drive from React state for tabbed/searched lists. ([field, op, value]; ops =, !=, >, <, contains, in; compound: [\"and\", […], […]]).' },
291291
{ name: 'navigation', type: "{ mode: 'page' | 'drawer' | 'modal' | 'split' | 'none' }", kind: 'binding', description: 'What a row click does. Use { mode: \"none\" } when you handle clicks via onRowClick.' },
@@ -344,7 +344,7 @@ export const REACT_BLOCKS: ReactBlockDef[] = [
344344
{
345345
tag: 'Block',
346346
schemaType: '(any)',
347-
summary: 'Escape hatch — render any registered component by type. <Block type="object-kanban" objectName="task" /> etc. Not a way back to the record:* family: those need a record page\'s record context and are rejected here too (#4413).',
347+
summary: 'Escape hatch — render any registered component by type. <Block type="object-kanban" objectName="task" /> etc. Not a way back to the record:* family: those need a record page\'s record context and are rejected here too.',
348348
interactions: [
349349
{ name: 'type', type: 'string', kind: 'binding', required: true, description: 'The registered component type to render.' },
350350
],

scripts/check-doc-authoring.mjs

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -263,17 +263,13 @@ const FENCE_CLOSE = /^```\s*$/;
263263
// guard: the exact failure this file's header opens with, one rule over.
264264
const PUBLISHED_SKILLS_ROOT = 'skills';
265265

266-
// Generated artifacts under `skills/**`. Their ids are not authored here — they
267-
// are projected from `.describe()` / TSDoc in `packages/spec`, so the fix for
268-
// one is a spec-source edit plus a regeneration, on a surface with its own
269-
// gates. Flagging them here would red a file no author can legally hand-edit
270-
// ("do not edit" is in their own headers) and point the remedy at the wrong
271-
// repo layer. They are exempt from THIS rule, not absolved: the spec-side ids
272-
// are tracked separately.
273-
const GENERATED_SKILL_ARTIFACTS = [
274-
/\/references\/_index\.md$/,
275-
/\/references\/react-blocks\.md$/,
276-
];
266+
// There is deliberately NO exemption for the generated artifacts under
267+
// `skills/**` (`references/_index.md`, `references/react-blocks.md`). The first
268+
// cut carried one, because those files still held ids projected from TSDoc in
269+
// `packages/spec/src/**`. Those source lines are stripped now, and an exemption
270+
// over a surface that no longer needs one is where the next regeneration would
271+
// smuggle one back in. A red here is fixed AT THE SPEC SOURCE, never by hand-
272+
// editing the artifact — the failure text below prescribes exactly that.
277273

278274
// There is deliberately NO per-passage allowlist here, and adding one is not a
279275
// remedy this gate offers.
@@ -429,11 +425,11 @@ function collectFiles() {
429425
}
430426

431427
/**
432-
* Every hand-authored Markdown file in the PUBLISHED catalog.
428+
* Every Markdown file in the PUBLISHED catalog — generated artifacts included.
433429
*
434430
* Its own walk, for the reason argued at {@link PUBLISHED_SKILLS_ROOT}: the
435-
* `collectFiles()` walk skips `references/`, where hand-authored companions
436-
* live. Generated artifacts are dropped by path.
431+
* `collectFiles()` walk skips `references/`, where both the hand-authored
432+
* companions and the generated `_index.md` files live.
437433
*
438434
* Empty is a hard error here for the same reason it is in `collectFiles`
439435
* (#4932): "the catalog is clean" and "the catalog was never opened" are the
@@ -460,9 +456,8 @@ function collectPublishedSkillFiles(root = PUBLISHED_SKILLS_ROOT) {
460456
else if (/\.mdx?$/.test(e)) files.push(posix(p));
461457
}
462458
})(root);
463-
const kept = files.filter((p) => !GENERATED_SKILL_ARTIFACTS.some((re) => re.test(p)));
464-
if (kept.length === 0) throw new EmptyRootError([root], 0);
465-
return kept.sort();
459+
if (files.length === 0) throw new EmptyRootError([root], 0);
460+
return files.sort();
466461
}
467462

468463
/** Bare internal issue-id references in one published file's source. */
@@ -663,9 +658,9 @@ function selfTest() {
663658
'skills/objectstack-demo/SKILL.md': 'The `cursor` key was removed in protocol 17.',
664659
'skills/objectstack-demo/references/data-hooks.md': 'Hooks fire per row.',
665660
'skills/objectstack-demo/rules/indexing.md': '`type` was retired.',
666-
// Generated artifacts — exempt: their ids come from packages/spec TSDoc.
667-
'skills/objectstack-demo/references/_index.md': 'Driver registry (#4410).',
668-
'skills/objectstack-ui/references/react-blocks.md': 'Converging on the metadata tier (#11284).',
661+
// Generated artifacts — IN scope and clean; their text comes from spec TSDoc.
662+
'skills/objectstack-demo/references/_index.md': 'Driver registry.',
663+
'skills/objectstack-ui/references/react-blocks.md': 'Converging on the metadata tier.',
669664
// The internal roots are NOT this rule's business.
670665
'.claude/agents/os-dev.md': 'Lesson learned while fixing #4286.',
671666
'docs/adr/0049-enforce-or-remove.md': 'Superseded by #5248.',
@@ -686,14 +681,14 @@ function selfTest() {
686681
// GREEN: the corpus as stripped.
687682
expect('a clean published corpus is green', scan().length, 0);
688683

689-
// Scope: the collector reaches references/, and drops the generated files.
684+
// Scope: the collector reaches references/, generated artifacts included.
690685
const seen = collectPublishedSkillFiles();
691686
expect('the id scan reaches hand-authored references/ (the other rule\'s walk does not)',
692687
seen.includes('skills/objectstack-demo/references/data-hooks.md'), true);
693-
expect('generated references/_index.md is exempt',
694-
seen.includes('skills/objectstack-demo/references/_index.md'), false);
695-
expect('the generated react-blocks contract page is exempt',
696-
seen.includes('skills/objectstack-ui/references/react-blocks.md'), false);
688+
expect('the generated references/_index.md is IN scope (no exemption)',
689+
seen.includes('skills/objectstack-demo/references/_index.md'), true);
690+
expect('the generated react-blocks contract page is IN scope (no exemption)',
691+
seen.includes('skills/objectstack-ui/references/react-blocks.md'), true);
697692
expect('the id scan does not reach .claude/', seen.some((f) => f.startsWith('.claude/')), false);
698693
expect('the id scan does not reach docs/', seen.some((f) => f.startsWith('docs/')), false);
699694
expect('the id scan does not reach content/', seen.some((f) => f.startsWith('content/')), false);
@@ -706,6 +701,17 @@ function selfTest() {
706701
expect('the red names the file', red[0]?.file, 'skills/objectstack-demo/SKILL.md');
707702
expect('the red names the id', red[0]?.ids?.join(','), '#4286');
708703

704+
// ...and in a GENERATED artifact — listing the file proves collection,
705+
// this proves it is SCANNED, which is what dropping the exemption bought.
706+
const regen = join(idDir, 'skills', 'objectstack-demo', 'references', '_index.md');
707+
writeFileSync(planted, 'The `cursor` key was removed in protocol 17.');
708+
writeFileSync(regen, 'Driver registry (#4410).');
709+
red = scan();
710+
expect('an id a regeneration put back into a generated artifact is RED', red.length, 1);
711+
expect('the red names the generated file', red[0]?.file, 'skills/objectstack-demo/references/_index.md');
712+
writeFileSync(regen, 'Driver registry.');
713+
writeFileSync(planted, 'The `cursor` key was removed in protocol 17 (#4286).');
714+
709715
// ...and in a comment inside a code fence, which is where half the
710716
// measured population lived.
711717
writeFileSync(planted, ['```ts', " cursor: 'abc', // removed in #4286", '```'].join('\n'));
@@ -918,6 +924,10 @@ function main() {
918924
+ `\nresolvable anchor where one exists — a protocol version, an ADR number, a lint rule id.`
919925
+ `\n\nWriting a usage example that needs an issue number? Use the placeholder \`#<n>\`.`
920926
+ `\nIt teaches the same syntax and is unmistakable to a customer reading it.`
927+
+ `\n\nFlagged file says "Auto-generated — do not edit"? Then the id is not authored there:`
928+
+ `\nit is projected from a \`.describe()\` / TSDoc string in \`packages/spec/src/**\`. Strip it`
929+
+ `\nAT THE SOURCE and regenerate (\`gen:skill-refs\`, \`gen:react-blocks\`, \`gen:docs\`) — the`
930+
+ `\nartifact is not exempt, because an exemption there is where the next one would land.`
921931
+ `\n\nThere is no per-passage exemption to reach for, by design: this rule has none.`
922932
+ `\n\nMaintainer ruling 2026-08-12, verbatim: 「处理 issue 时犯的错应该总结成经验,保留 issue id没有意义」\n`,
923933
);

0 commit comments

Comments
 (0)