Skip to content

fix(docs): FieldSchema.extend() does not exist — the FAQ recommended a call that throws - #3890

Merged
os-zhuang merged 2 commits into
mainfrom
claude/action-undoable-experimental-j8g24i
Jul 28, 2026
Merged

fix(docs): FieldSchema.extend() does not exist — the FAQ recommended a call that throws#3890
os-zhuang merged 2 commits into
mainfrom
claude/action-undoable-experimental-j8g24i

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#3882 brought content/docs under the example type-check gate and left an open question: of the ~600 blocks still unmarked, is any of it genuine rot rather than a fragment? Swept them. One real one.

The rot

content/docs/deployment/troubleshooting.mdx answered "How do I extend a built-in schema?" — a direct FAQ, not an illustrative sketch — with:

import { FieldSchema } from '@objectstack/spec/data';

const CustomFieldSchema = FieldSchema.extend({
  customProperty: z.string().optional()   // …and `z` is never imported
});

FieldSchema carries a .transform() that lowers author-facing sugar at parse time, which makes it a ZodPipe, not a ZodObject. Verified against the built package:

FieldSchema ctor: bound ZodPipe
has .extend?      undefined
has .in?          true  ZodObject
.in.extend WORKS -> {"name":"x","type":"text","customProperty":"hi"}

So anyone following the FAQ got FieldSchema.extend is not a function. Rewritten to FieldSchema.in.extend({ … }), verified to parse, and marked so the gate holds it (196 → 197 checked examples). The answer now also says why — the same applies to ObjectSchema / ActionSchema — and warns that the extended schema no longer runs the transform.

The method matters more than the finding, so it's now in the gate's docstring

Two traps make a naive sweep report a confident "nothing found". Both caught me.

1. tsc stops after syntactic diagnostics and never runs the semantic pass.

I marked all 780 blocks and compiled. Result: 1178 diagnostics, every one a TS1xxx syntax code, zero TS2xxx. That reads exactly like "no rot anywhere" — and I nearly reported it. It's an artefact: ~200 syntactically-broken fragments suppressed type-checking for the entire program, including the 597 blocks that parse fine.

Caught it by injecting a deliberate type error into a known-good block and watching it go unreported. Excluding the syntax failures and re-running turned up 1000+ semantic diagnostics that had been invisible.

This is the same shape as the failure the liveness README documents — "a strong negative claim resting on a search whose result set was silently truncated or filtered" — one layer down.

2. Unimported type names resolve against the DOM lib.

Plugin, Event, Response, Storage, Selection all exist in lib.dom. A block that omits its import reports:

'version' does not exist in type 'Plugin'

…against the browser's Plugin, while the real Plugin contract (contracts/plugin-validator.ts) has version?, init?, destroy? and an index signature. Three of the strongest-looking candidates — plugins/index.mdx, two in kernel/services.mdx — were exactly this. Artefacts, not drift.

What the rest turned out to be

  • Fragmentscolumns: [...] subtrees, YAML in a ts fence, blocks referencing symbols defined in neighbouring blocks. The gate's opt-in design already anticipates these.
  • Unresolvable imports (115)@objectstack/core, zod, hono, relative project paths. A harness limit, not rot: the tsconfig paths map only wires @objectstack/spec. Widening it is a separate, plausible follow-up.
  • protocol/kernel/config-resolution.mdx — its database: / http: keys genuinely aren't on defineStack, but a callout at the top of the page already labels those snippets "design intent, not paste-ready code" and names those very keys. Correctly unmarked; no change.

Verification

  • pnpm --filter @objectstack/spec check:skill-examples✅ 197 prose examples type-check.
  • pnpm check:doc-authoring — 213 files clean.
  • .in.extend verified by executing it against the built package (output above), not just by type-checking.

Generated by Claude Code

…a call that throws

#3882 brought content/docs under the example type-check gate and left the open
question: of the blocks still unmarked, is any GENUINE ROT rather than a fragment?
Swept them. One real one.

deployment/troubleshooting.mdx answered "How do I extend a built-in schema?" with
`FieldSchema.extend({ … })`. FieldSchema carries a .transform() that lowers
author-facing sugar at parse time, so it is a ZodPipe, not a ZodObject —
`.extend` is undefined on it (verified against the built package). Anyone
following the FAQ got a "not a function" throw. The example also used `z` without
importing it. Rewritten to FieldSchema.in.extend({ … }) — `.in` is the input
object the pipe wraps — verified to parse, marked so the gate holds it, and it
now says why (same for ObjectSchema/ActionSchema) and warns that the extended
schema no longer runs the transform.

The sweep's METHOD is recorded in the gate docstring, because two traps make a
naive pass report a confident "nothing found":

  1. tsc stops after syntactic diagnostics and never runs the semantic pass.
     Marking all 780 blocks at once let ~200 broken fragments suppress
     type-checking for every other block; that run returned only TS1xxx codes,
     which reads exactly like "no rot" and proves nothing. Caught by injecting a
     deliberate type error and watching it go unreported.
  2. Unimported type names resolve against the DOM lib — Plugin, Event, Response,
     Storage all exist there. A block missing its import reports "'version' does
     not exist in type 'Plugin'" against lib.dom's Plugin: an artefact, not
     drift. Three of the strongest-looking candidates were exactly this.

After both corrections the rest are fragments, plus config-resolution.mdx, whose
aspirational snippets a callout on the page already labels as design intent.

Gate: 196 -> 197 checked examples.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ajwvrmd1hDC9RBofYBhGuR
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 28, 2026 2:52pm

Request Review

@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

104 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…ch FieldSchema declaration you get

CI rejected the first fix: `FieldSchema.in.extend(…)` passed locally and failed
with "Property 'in' does not exist on type 'ZodObject<…>'" on the runner. The two
builds emit DIFFERENT declarations for the same source — locally
z.ZodPipe<z.ZodObject<…>>, in CI a plain z.ZodObject.

The runtime is unambiguous: after `rm -rf dist && pnpm build`, FieldSchema is a
`bound ZodPipe` with `.extend === undefined` and `.in` a ZodObject. So CI's
declaration contradicts the value it describes — `.extend()` type-checks there and
throws at runtime. That is a real spec-side divergence (likely inference
instability in the DTS bundling of these very large zod types) and deserves its
own investigation; it is not something this docs fix should paper over.

So the checked example now uses only `.parse()`: parse with FieldSchema, validate
the additions alongside it. Correct under either declaration, verified to run, and
better advice anyway — composition keeps the transform, which `.extend()` discards.
`.in.extend()` stays in prose as the merged-schema route with that caveat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ajwvrmd1hDC9RBofYBhGuR
@github-actions github-actions Bot added size/m and removed size/s labels Jul 28, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review July 28, 2026 15:16
@os-zhuang
os-zhuang merged commit 9ea2bc5 into main Jul 28, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/action-undoable-experimental-j8g24i branch July 28, 2026 15:16
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants