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:

Check Result
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-zhuang os-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Jul 17, 2026
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Building Building Preview, Comment Jul 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 main Jul 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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant