Skip to content

refactor(spec): build-docs uses the shared generated-output sink - #3145

Merged
os-zhuang merged 1 commit into
mainfrom
claude/converge-docs-sink
Jul 17, 2026
Merged

refactor(spec): build-docs uses the shared generated-output sink#3145
os-zhuang merged 1 commit into
mainfrom
claude/converge-docs-sink

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Collects the debt #3138 deliberately left behind.

#3138 extracted the emit() / manageDir() / flush() sink to lib/generated-output.ts and moved build-skill-references + build-react-blocks-contract onto it — but left build-docs.ts carrying the inline original, because #3126 was rewriting the same file at the time and the migration would have conflicted. #3126 has landed (a6058728d), so the conflict is gone.

Why this duplication in particular is worth removing

Two copies of a check/write sink is the one duplication this design cannot afford. The sink's entire claim is that --check and write are the same code — and that claim holds per copy. A fix to one (say the empty-dir prune #3138 added) silently leaves the other behind, and the failure mode isn't cosmetic: it's a gate that passes on output a real run wouldn't produce. Exactly the false-green the gate exists to prevent.

wasEmitted() added to the shared sink

build-docs is the first caller whose later output depends on its earlier output — each category index.mdx links only the pages that were actually generated (a .zod.ts whose schemas are all unrepresentable in JSON Schema produces no page, and carding it would be a dangling 404).

That question belongs to the sink: it already owns the emitted map, and having the caller keep a second copy would fork the very state the sink exists to consolidate. Under --check it also must come from the sink — nothing is written, so the disk still holds the stale tree and reading it would make the check diverge from the run it models.

New export, no signature change; the other two callers are untouched.

The guard moves from an inline managedDirs.size === 0 to flush()'s guard hook, same meaning: json-schema/ is gitignored, so a fresh checkout that skips gen:schema emits almost nothing and "nothing differs" would read as success.

Verified, not assumed

Net −58 lines. A refactor of gate machinery is exactly where "it's only a refactor" gets you a gate that no longer bites, so the full red-test suite was re-run rather than skipped:

CheckResult
Regenerate → byte-identical output✅ all 258 files, tree stays clean
In-sync tree✅ exit 0
Stale content (#3076 replayed)✅ exit 1 — ~ … (out of date)
Missing page✅ exit 1 — + … (missing — spec adds it)
Stale leftover page✅ exit 1 — - … (stale — …)
No-schema guard✅ exit 1, not a vacuous pass
check:skill-refs / check:react-blocks regression✅ both exit 0, skills/ stays clean
#3138's reverse test: hand-written .md in skills/*/references/✅ gate stays green and the file survives a write

tsc --noEmit clean.

🤖 Generated with Claude Code

#3138 extracted the emit()/manageDir()/flush() sink to lib/generated-output.ts and
moved build-skill-references + build-react-blocks-contract onto it, but left
build-docs.ts carrying the inline original: #3126 was rewriting the same file at the
time and the migration would have conflicted. #3126 has landed, so collect the debt.
Two copies of a check/write sink is the one duplication this design cannot afford —
its whole claim is that --check and write are the same code, and that claim is per
copy. A fix to one (say the empty-dir prune #3138 added) silently leaves the other
behind, and the failure mode is a gate that passes on output a real run would not
produce.
Adds wasEmitted() to the shared sink. build-docs is the first caller whose later
output depends on its earlier output — each category index links only the pages that
got generated — and that question belongs to the sink: it already owns the emitted
map, and having the caller keep a second copy would fork the state the sink exists to
consolidate. The other two callers are unaffected (new export, no signature change).
The guard moves from an inline `managedDirs.size === 0` to flush()'s guard hook,
keeping the same meaning: json-schema/ is gitignored, so a fresh checkout that skips
gen:schema emits almost nothing and "nothing differs" would read as success.
Pure refactor, verified rather than assumed:
- regenerating produces byte-identical output — all 258 files, tree stays clean
- all five drift classes still fail: stale content, missing page, stale leftover,
no-schema guard, plus in-sync staying green
- both existing sink callers regress clean, including #3138's reverse test that a
hand-written .md in skills/*/references/ keeps the gate green and survives a write
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@os-zhuangos-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Jul 17, 2026
@vercel

vercelBot commented Jul 17, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specBuildingBuildingPreview, CommentJul 17, 2026 1:59pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

102 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/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/cli.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/validating-metadata.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/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 packages/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/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.

@os-zhuang
os-zhuang merged commit 87d216e into mainJul 17, 2026
19 of 20 checks passed
@os-zhuang
os-zhuang deleted the claude/converge-docs-sink branch July 17, 2026 14:16
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gatetooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@os-zhuang