Skip to content

docs(skills): type-check 14 more objectstack-data examples and fix the Field import path - #31618

Merged
os-zhuang merged 2 commits into
mainfrom
claude/project-thread-682ojl
Nov 8, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
claude/project-thread-682ojl

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Refs #31112 (slice 2 of a per-skill split: item 4, objectstack-data; the card stays open)

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:objectstack-data 技能包里又有 14 段 TypeScript 示例接入了示例编译检查(95 段中由 11 段变为 25 段),并修正一处会让照抄者编译失败的示例:Field 原写成从 @objectstack/spec 根入口导入,根入口并不导出它,改为 @objectstack/spec/data。
  • 为什么改:AI 编写元数据时最先照抄这些示例;没接入检查的示例在规范改名后会悄悄失效,这次就查出一处已经失效的导入。
  • 风险与回滚:只改发布技能包的文档文字,不动任何运行时代码;每个文件的 token 上限都未抬高,新增字节靠同文件删减重复句支付。回滚即 revert 本 PR。
  • 先看:skills/objectstack-data/rules/field-types.md(唯一的示例修正)、skills/objectstack-data/rules/indexing.md(8 段新接入、改动最多)、skills/objectstack-data/rules/validation.md(删去两句重复说明以支付额度)。
  • 两分钟验证:pnpm --filter @objectstack/spec check:skill-examples,预期最后一行为 322 段示例全部通过类型检查。
  • 需要你做的:作为受管面审批人审阅并批准本 PR(skills/** 属 Tier H)。

What changes

skills/objectstack-data had 11 of its 95 fenced ts/typescript blocks under check:skill-examples (measured on main 158cc2a, all *.md outside evals/, _index.md excluded). It now has 25. Every new mark is the os:check marker on the line above the fence; wrappers are an import plus a typed const, never more than three lines.

File marked before → after / blocks tokens before → after / ceiling lines before → after
rules/indexing.md 0 → 8 / 10 2284 → 2495 / 3241 236 → 260
references/data-hooks.md 1 → 5 / 24 9905 → 9987 / 10011 942 → 953
rules/lifecycle.md 0 → 1 / 2 1588 → 1590 / 1590 164 → 165
rules/field-types.md 0 → 1 / 8 3199 → 3199 / 3199 252 → 253
rules/validation.md 2 → 2 / 15 2761 → 2754 / 2761 329 → 331
references/examples-objects.md 4 → 4 / 5 1710 → 1723 / 1728 189 → 191
untouched: SKILL.md, seeds.md, datasources.md, relationships.md, security.md 2/4, 0/4, 0/1, 0/13, 2/9 headroom 9, 0, 3, 0, 0 unchanged

Skills bundle reading: no SKILL.md changed, so the sum of every published SKILL.md stays 4592 lines.

  • rules/indexing.md: eight index examples become const indexes: ObjectIndex[] = [ … ]; (or const index: ObjectIndex) with import type { ObjectIndex } from '@objectstack/spec/data'. A retired type / partial key or an unscoped unique: true now fails the gate. The two blocks holding ❌ keys stay unmarked; the opening "Guide for …" line, which carried no rule, now says so.
  • references/data-hooks.md: the full defineHook definition is marked as written; patterns 1 to 3 gain their defineHook import. Pattern 4 stays unmarked (its record: any parameter is a bare any, which the gate refuses), as do the context shapes and fragments; the intro says so once.
  • rules/lifecycle.md: the ObjectSchema.create syntax block is marked as written, paid by "Guide for declaring" becoming "Declaring". The ❌ block sits under the heading "Validation Rules (rejected at parse time)".
  • rules/field-types.md: the Field.* factory block is marked as const fields = { … };.
  • references/examples-objects.md: the protection type excerpt gets one line saying it is an excerpt and unchecked.

Example fixes

  • rules/field-types.md:19 (was line 18): import { Field } from '@objectstack/spec' does not compile. The spec root exports no Field: namespace exports were removed from the root for tree-shaking (packages/spec/src/index.ts, "NAMESPACE EXPORTS — REMOVED"), and every example app imports it from @objectstack/spec/data. Now import { Field } from '@objectstack/spec/data'. The prose at line 12 named the same wrong path, "(@objectstack/spec)"; that parenthetical is removed, because the import line right below it now carries the path and the gate checks it.
  • rules/validation.md:325: the ✅ "Correct — Validation Scoped to Events" example had no name, which every validation rule requires (BaseValidationRuleShape). It gains name: 'no_draft_publish'. The block stays unmarked because it is an object-literal fragment.

Trims that paid the marks (each cut rule and where it survives)

  • rules/validation.md, Uniqueness section: "There is no unique validation type." This survives at "Available Rule Types": "No unique type — enforce uniqueness with a unique index".
  • rules/validation.md, Cross-Field section: "Same inverted semantics as script — the predicate is the failure condition." This survives at "Expression Syntax" ("For script and cross_field, the predicate expresses the failure condition") and in the block's own // ❌ TRUE = invalid comment.
  • rules/field-types.md:11: "overwhelmingly" became "mostly", and the wrong import parenthetical went (see above).
  • rules/indexing.md:3 and rules/lifecycle.md:3: intro wording that carried no rule.

Tests

check:skill-examples, before and after
  • Before (main 158cc2a): ✅ 308 prose examples type-check across 3 surface(s).
  • After (head c4e232f, the same skills tree as the merged head): pnpm --filter @objectstack/spec check:skill-examples exited 0 with ✅ 322 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them (skills + docs surface 274 → 288).
  • Reverse check, run after the commit: planted a retired index type (rules/indexing.md:142), an unknown hook key runAsUser (references/data-hooks.md:747) and the old root Field import (rules/field-types.md:19). The checker exited 1 with TS2353, TS2353 and TS2305 at exactly those lines. Restored with git checkout HEAD -- skills/objectstack-data; git status --porcelain was empty after.

Acceptance notes

  • check:skill-examples is not in the derived gate list for a skills/** diff, so it was run by hand.
  • Blocks left unmarked because the file has no headroom to pay for a wrapper: rules/relationships.md (headroom 0; six ObjectSchema.create blocks, which each need an import, and three of them hold two export defaults), rules/security.md (0; the two rowLevelSecurity lists), references/seeds.md (0; each block names an object defined elsewhere, so it needs a declare const line), rules/datasources.md (3; the federated-object block needs an import of about 18 tokens), SKILL.md (9; the searchableFields object and the indexes list; the same indexes list is checked in rules/indexing.md), and the validations: [ … ] lists in rules/validation.md (about 27 tokens each). The probe that sorted the 95 blocks wrapped each of these with its real type, and every one compiled except the deliberate ❌ examples. So no other example is wrong today; they are just not gated.
  • The fragment note is unpaid in those five untouched files, which have 0 to 9 tokens of headroom and no sentence that could go without losing a fact. This is reported as a deviation.
  • No ceiling was raised or lowered, and the ratchet printed no instruction to lower one.

Verification

Derived gates (dispatch-gates --commands, 45 families) on head c4e232f

44 exited 0. pnpm check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: it needs every package's dist; CI runs it). dispatch-gates --ran exited 0: 45 derived, 44 run, 1 NOT-MEASURED. After merging main (head 1d424c0, which touches no packages/spec or skills/ path), the token ratchet, check:cross-package-test-inputs (whose script main changed), check:skill-links and check-changeset-no-major were re-run, all exit 0.


Generated by Claude Code

…ield import path

Mark 14 more self-contained blocks with the os:check marker (25 of 95),
wrap index lists as ObjectIndex[], add the defineHook imports the pattern
hooks need, and import Field from @objectstack/spec/data, the subpath that
exports it. Paid inside each file by trims; no ceiling moved.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jRhbPVtqq8gkr4md2P
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 8, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · zhuangjianguo · session_01QsR2jULfPnqQFGcZUtKDFH

Contract-review-claim: session_01QsR2jULfPnqQFGcZUtKDFH · head 1d424c0 · 2026-11-08T17:08Z

The lane seat's isolated Fable reviewer runs the CONTRACT_REVIEW_TIER review (maintainer 2026-11-08 16:12Z 「有 fable 车道就可以契约复审啊」); the director seat is off duty.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · zhuangjianguo · session_01QsR2jULfPnqQFGcZUtKDFH

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 1d424c0010ed012b4efa19be6be44313bb939d93
Local-runs: none
Scope: skills/objectstack-data/references/data-hooks.md, skills/objectstack-data/references/examples-objects.md, skills/objectstack-data/rules/field-types.md, skills/objectstack-data/rules/indexing.md, skills/objectstack-data/rules/lifecycle.md, skills/objectstack-data/rules/validation.md + none (the diff holds no implementing code; the gate the marks opt into, packages/spec/scripts/check-skill-examples.ts, and the spec the blocks are compiled against, packages/spec/src/data/{object,field,hook,hook-body,validation}.zod.ts and packages/spec/src/index.ts, were read on main, not reviewed). Remainder of the diff — .changeset/31112-data-skill-examples-checked.md — is the lane seat's ACCEPT at TIER_DEFAULT.

① Derived judgments

  • Accept-set of check:skill-examples widens, 11 → 25 marked blocks in skills/objectstack-data (counted on the head: SKILL.md 2/4, data-hooks 5/24, examples-objects 4/5, seeds 0/4, datasources 0/1, field-types 1/8, indexing 8/10, lifecycle 1/2, relationships 0/13, security 2/9, validation 2/15 — 25/95). Right. Every new mark sits on the line directly above a ```typescript fence (the os:check HTML-comment marker the skills root of SKILLS_DOCS_ROOTS reads); the gate is not in the skills/** derived list but CI runs it on every PR in Type Check · consumer gates (lint.yml "Check skills TypeScript examples compile", pnpm --filter @objectstack/spec run check:skill-examples), so its check-run on this head is the verdict: the step "Check skills TypeScript examples compile" is completed / success in job 128800322733 (Type Check · consumer gates); at the time of this record that job itself and Test Core (1/8) were still in_progress (every other non-skipped check-run on the head: completed / success, Type Check · source gates, Lint · docs & code guards, Check Changeset, Governed Surface Queue Guard and PR-body guards included), neither of which judges a skills/** claim.
  • The nine ObjectIndex wrappers gate what the prose claims. Right. ObjectIndex is the z.input type of IndexSchema (object.zod.ts:4700); IndexSchema is a strictObject of name?, fields, unique? with unique the literal union false | 'global' | 'organization' (DeclaredIndexUniqueScopeSchema, object.zod.ts:531). Under the gate's strict: true profile a retired type/partial key is an excess-property error and unique: true a type error, so "a retired type / partial key or an unscoped unique: true now fails the gate" holds.
  • import { Field } from '@objectstack/spec' → '@objectstack/spec/data' is a correction, not a style choice. Right. packages/spec/src/index.ts ("NAMESPACE EXPORTS — REMOVED", lines 87-96) exports no Field; Field is export const Field in src/data/field.zod.ts:3500, re-exported by src/data/index.ts:193; skills/objectstack-data/SKILL.md:182 already teaches import { ObjectSchema, Field } from '@objectstack/spec/data', so the skill is now self-consistent. No other Field/ObjectSchema/defineHook root import remains anywhere under skills/ on the head (0 hits).
  • The three pattern hooks' new import { defineHook } from '@objectstack/spec/data' resolves. Right: defineHook(config: Hook): ResolvedHook at hook.zod.ts:1713, re-exported via src/data/index.ts:229; 'api.read' / 'api.write' are HookBodyCapability members (hook-body.zod.ts:49-50); beforeDelete, afterInsert, afterUpdate, afterDelete are HookEvent members. P on the root (src/index.ts:286-296 from shared/expression.zod.ts:498) is unchanged.
  • Published surface: @objectstack/skills (18.3.0) ships this prose — its build (packages/skills/scripts/sync-catalog.mjs) copies skills/** into dist/. No SKILL.md, frontmatter, schema, export or runtime path moves; references/_index.md (generator-owned) is untouched and nothing in the diff feeds it (the pointer rows read module doc blocks, not rules files).
  • Token ratchet. Every touched file stays at or under its CEILINGS row, read off ceil(bytes/4) of the head blobs: data-hooks 9987/10011, examples-objects 1723/1728, field-types 3199/3199, indexing 2495/3241, lifecycle 1590/1590, validation 2754/2761 — identical to the PR's table; no ceiling raised or lowered (lowering is optional under the ratchet's discipline). The gate's verdict is the Type Check · source gates check-run (lint.yml "Published skills token ratchet ([finding] the published skills bundle has no length budget — a maintainer ruling now establishes the principle, but nothing enforces it #10473) + its self-test"), completed / success on the head.

② Semver level

'@objectstack/skills': patch with Clause-②: no and Metadata-protocol: none in both the changeset and the PR body. Right: the diff publishes prose only from a released package; no authorable key, value, export or default changes; no narrowing or widening of any spec. Check Changeset and PR-body guards are green on the head. Clause-②: no.

③ Boundary flags

The dev report (card comment 6724448006) lists seven deviations and no open_questions; one more deviation is visible in the tree.

  1. "check:skill-examples is not in the derived list; run by hand" — answered: CI runs it regardless (lint.yml Type Check · consumer gates); the check-run on the head stands in for the hand run, see ①.
  2. "unmarked for token headroom: relationships.md 6, security.md 2, seeds.md 4, datasources.md 1, SKILL.md 2, validation.md validations lists" — accepted: those files are outside this diff or at 0–9 tokens of headroom; the card stays open and names objectstack-data's remaining blocks as the next slice's work. Not escalated.
  3. "fragment note unpaid in SKILL.md, seeds.md, datasources.md, relationships.md, security.md" — accepted deviation from the card's "say so once per file": none of the five is in the diff and each is at ceiling.
  4. "lifecycle.md: the ❌ block's note is its existing heading 'rejected at parse time'" — accepted: the file is at 1590/1590 after the mark and the heading says what the note would.
  5. "no ceiling raised or lowered" — consistent with the tree (①).
  6. "default tier, no Fable build" — superseded by the claim amendment 6724423900 (opus build; the mandated CONTRACT_REVIEW_TIER review is this record).
  7. Undeclared: rules/field-types.md carries no fragment note for its 7 unmarked blocks (the report's list names only the five untouched files). Same cause — the file is at 3199/3199 — and the same disposition: accepted deviation, recorded here so the omission does not read as an oversight. Nit (see ④).

④ Design judgment

Judged on the head tree against the gate (check-skill-examples.ts), the spec on main, the card's eval prompts (skills/objectstack-data/evals/*.json) and the merged slice-1 precedent (#31507, e516cf2).

  • rules/indexing.md — holds. The eight marked blocks compile as ObjectIndex[] / ObjectIndex and teach exactly what eval 9 ("multi-tenant … organization's open tickets, newest first" → { fields: ['organization_id', 'status', 'created_at'] }) and eval 3 (unique: 'organization', type/partial forbidden) expect. The two unmarked blocks are the ones holding unique: true, type and partial ❌ items, correctly left out. Replacing the rule-free "Guide for creating efficient database indexes" line with the per-file note follows the slice-1 precedent (aggregation.md:3, pagination.md:3 did the same). nit — the note's wording: "two blocks holding ❌ keys … check:skill-examples would refuse them". The gate's REFUSE verdict is reserved for a marked block that does not parse; these blocks parse and would fail the semantic pass, and the unique-scope block's ❌ item is a value (unique: true), not a key. "would fail them" (or the precedent's "its ❌ half is wrong on purpose") is the accurate sentence. No effect on the gate or the rule taught.
  • references/data-hooks.md — holds. The full defineHook definition block is marked as written (its two imports resolve; the keys it names — priority, async, condition, onError, timeoutMs, retryPolicy — are judged by the gate, not by this record). Patterns 1–3 gain the one import that makes each a module; their bodies match evals 10 and 12 sentence for sentence (capabilities: ['api.read'] for the read guard, ['api.write'] + multi: true + ctx.previous.id for the cascade). Pattern 4 is left unmarked for its (record: any) — the gate's third anti-idle assertion refuses a bare any — and the intro note says so once. No finding.
  • rules/field-types.md — holds. The import fix is the one real example defect in the slice (①). fields: { → const fields = { makes the block a statement without changing the three spellings it teaches (Field.text(config), Field.select(options, config), Field.lookup(reference, config) match field.zod.ts:3501/3578/3598). Dropping the "(@objectstack/spec)" parenthetical removes the wrong path from prose, and the block now carries the right one. nit — no per-file fragment note for the 7 unmarked blocks (③ item 7); the file has no headroom, so it is an accepted deviation unless a later slice frees tokens.
  • rules/lifecycle.md — holds. The ObjectSchema.create syntax block is marked as written; "Guide for declaring" → "Declaring" pays the 2 tokens and loses no rule. The ❌ block stays unmarked under a heading that already states it is rejected. No finding.
  • rules/validation.md — holds. Both trims survive elsewhere in the same file, verified on the head: "no unique validation type" at Available Rule Types ("No unique type — enforce uniqueness with a unique index"); the inverted-predicate sentence at Expression Syntax ("For script and cross_field, the predicate expresses the failure condition") and in the block's own // ❌ TRUE = invalid. name: 'no_draft_publish' on the ✅ events example is required by BaseValidationRuleShape (validation.zod.ts:143, snake_case regex satisfied). nit — the ❌ half of that pair still has no name, so the ✅/❌ contrast now differs in two keys while its comment names only events; a reader can take the missing name for the lesson. Either add name to the ❌ block (it is unmarked, so the gate is indifferent) or leave it: no effect on correctness.
  • references/examples-objects.md — holds. The protection?: { … } block is a type-member excerpt, not a statement, so marking it would be a parse REFUSE; the one-line note is accurate and the file stays under its ceiling (1723/1728). No finding.

Tried to break it: (a) a wrapper that silently weakens the check — none; every index wrapper is typed to the strict input schema and every hook block goes through defineHook's Hook parameter type, so no block is compiled as an untyped literal; (b) a trim that drops a rule — each of the two validation.md sentences has a surviving home named above; (c) a mark on a block the gate cannot judge — the ❌ blocks, the any block and the type excerpt are all left unmarked; (d) a path that resolves locally but not for a consumer — both specifiers are package exports entries (. and ./data), which is what packagePaths() builds the paths map from.

Findings: three nits (indexing.md note wording; field-types.md missing fragment note, accepted deviation; validation.md ❌/✅ pair asymmetry). No must-fix, no should-fix. The nits may be fixed on the prose face or left as accepted deviations for the card's next data slice.

Implemented-by: claude/project-thread-682ojl
Reviewed-by: session_01QsR2jULfPnqQFGcZUtKDFH

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · zhuangjianguo · session_01QsR2jULfPnqQFGcZUtKDFH

维护者速读

  • 改了什么:objectstack-data 技能里又有 14 段能独立成立的 TypeScript 示例加上 <!-- os:check --> 标记,CI 会把它们当真代码编译,覆盖从 11/95 升到 25/95(indexing 8/10、data-hooks 5/24、lifecycle 1/2、field-types 1/8)。顺手修了两个写错的示例:Field 原来从 @objectstack/spec 导入(根入口并不导出它),改成 @objectstack/spec/data;validation 的 ✅ 示例补上每条校验规则必填的 name。
  • 为什么改:AI 照抄技能示例写对象和钩子,没人编译的示例会悄悄过期,那个错的 Field 导入就是例子。现在键名或导入一错,CI 直接变红(skills: one home per rule — the same rule written in three skills, "moved verbatim" stubs in data and upgrade SKILL.md, an undocumented user-invocable frontmatter key, and <!-- os:check --> coverage of 1/63 (platform) and 0/42 (query) example blocks #31112 第 4 项,data 这一片)。
  • 风险与回滚:只改技能文档,不动运行时,没有抬高任何 token 上限。relationships、security、seeds、datasources 和 SKILL.md 余量只剩 0–9 个 token,包装不下,示例保持不标记。回滚即 revert 本 PR。
  • 先看:skills/objectstack-data/rules/field-types.md 的导入修正,以及 rules/indexing.md 改成带 ObjectIndex[] 类型的写法。
  • 两分钟验证:pnpm --filter @objectstack/spec check:skill-examples 退出 0,统计应为 322 段示例。
  • 需要你做的:批准本 PR(Approve review)。契约复审 PASS 记录是评论 6724581980,Head-sha 1d424c0010ed012b4efa19be6be44313bb939d93;批准后由席位落地。
  • 席位意见:ACCEPT。席位读了 diff:新增的只有标记、类型导入和 const 包装,规则教的写法没变;两处修正都是真错(复审核实根入口不导出 Field)。

席位核对

Head-sha: 1d424c0010ed012b4efa19be6be44313bb939d93

  1. Shape: draft, base main, body Refs #31112 (slice 2 of a per-skill split; the card stays open), changeset '@objectstack/skills': patch. 7 files, +77/−26. Tier H (skills/**).
  2. Premise measured: the slice-1 release note 6721501671 counted data at 11/95; its blockers PR feat(driver-sql,cloud-connection): a declared renamedFrom carries the old column's values on upgrade #31402 and feat(driver-sql): SQL Server as a federation-only dialect #31381 had merged. The dev's reverse check went red at 3 planted lines and came back clean, so the marks are live.
  3. Gates: dev gates 45 derived, 44 exit 0; dual-build-cjs-loads exit 3 (needs a full build; no build input touched). check:skill-examples 308 → 322 exit 0. Check-runs on this head at ACCEPT: 26 success, 13 skipped, 0 failure, 1 still running (re-read before landing).
  4. Accepted deviations: relationships, security, seeds, datasources and SKILL.md untouched for want of token headroom (0–9 tokens; each wrapper costs ~14–27); the dev compiled each of their complete blocks in a scratch probe, so none is wrong today. Review nits, accepted as is: indexing.md:3 says "would refuse" where "would fail" is exact; field-types.md has no once-per-file fragment note (3199/3199 tokens); validation.md's ❌/✅ events pair now differs in name as well as events. Model: opus build under the standing no-Fable-build rule, Fable review (amendment 6724423900).
  5. Out of scope / Skills-docs: items 1–3 of skills: one home per rule — the same rule written in three skills, "moved verbatim" stubs in data and upgrade SKILL.md, an undocumented user-invocable frontmatter key, and <!-- os:check --> coverage of 1/63 (platform) and 0/42 (query) example blocks #31112 and item 4 for formula, automation, ui and platform stay on the card; the data files left unmarked above are recorded there at release. Metadata-protocol: none, so no Skills-docs: line is owed.
  6. Landing: Tier H. pm:awaiting-merge + reviewers os-zhuang, hotlong; stays draft until an authorized approval or a maintainer merge. Refs, not Fixes: the seat releases the card to pm:queue with the remaining counts after the merge.

@objectstack-fleet objectstack-fleet Bot added the pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue label Nov 8, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review November 8, 2026 21:08
@os-zhuang
os-zhuang added this pull request to the merge queue Nov 8, 2026
@objectstack-fleet objectstack-fleet Bot removed the pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue label Nov 8, 2026
Merged via the queue into main with commit 09df571 Nov 8, 2026
50 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-682ojl branch November 8, 2026 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants