Skip to content

feat(cli): preflight installable provider for required capabilities (#3366) - #3385

Merged
os-zhuang merged 2 commits into
mainfrom
claude/preflight-installable-provider-5t7j20
Jul 21, 2026
Merged

feat(cli): preflight installable provider for required capabilities (#3366)#3385
os-zhuang merged 2 commits into
mainfrom
claude/preflight-installable-provider-5t7j20

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes#3366.

Problem

A capability listed in requires: [...] is fail-fast at serve/start time when its provider package is missing — but when the provider has no installable version in the current edition, the generic "not installed, add it to your dependencies" advice is un-followable. Nothing shifted this left: os validate only checks the token against the vocabulary (ADR-0066), and os build never resolves providers or boots the runtime, so a validate && build && test CI script never caught it. It surfaced only as an opaque os start crash — seen upgrading an open-edition app from 14.7 to 16 after @objectstack/service-ai went cloud-only (ADR-0025).

Change

One machine-readable source of truth (spec).@objectstack/spec/kernel now exports PLATFORM_CAPABILITY_PROVIDERS (every vocabulary token → provider package + edition: open / enterprise / cloud) and a pure classifyRequiredCapability(token, isInstalled). This lifts the provider/edition knowledge the serve resolver encoded informally (its CAPABILITY_PROVIDERS map + tier gating) into data a preflight can read before boot.

Shift-left gate (cli). A new capability-preflight.ts util resolves each declared capability's provider the same way serve loads it (host app dir, then the CLI's own deps where the framework @objectstack/* providers live) and renders an edition-aware message. Wired into os build and os validate:

  • No installable version in the active edition (e.g. ai@objectstack/service-ai, cloud-only) → fails fast with the edition-aware message.
  • Absent but installable → advisory pnpm add hint, not a hard error.
  • Satisfied requires → passes unchanged.

Identical boot message (cli). The two os serve fail-fast sites (the AI block and the CAPABILITY_PROVIDERS loop) now render the same classification, so preflight and boot read identically.

Example, from os validate / os build on a requires: ['ai'] app under the open edition:

Capability "ai" resolves to @objectstack/service-ai, which is not available in the open edition (cloud-only since 11.3.0 / ADR-0025). Remove "ai" from requires, or run under a cloud runtime that provides the "ai" tier.

Tests

  • packages/spec/.../platform-capabilities.test.ts — registry shape + classifier (ok / installable / unavailable / unknown; injected resolution, no I/O).
  • packages/cli/test/capability-preflight.test.ts — preflight aggregation, message rendering, missingProviderMessage proving serve and build render identically, and real on-disk resolution.
  • packages/cli/test/serve-capability-vocabulary.test.ts — extended drift guard: the registry stays 1:1 with the vocabulary and agrees with serve's CAPABILITY_PROVIDERS packages; ai/ai-studio are cloud-only.
  • End-to-end verified: ai fails both validate and build; a satisfied list and an absent-but-installable (enterprise) provider behave as specified.

Notes

  • Additive only (new spec exports, new gate) — no authorable spec key / export / config field removed or renamed.
  • Changeset added (@objectstack/spec + @objectstack/cli, minor).

🤖 Generated with Claude Code

https://claude.ai/code/session_013CQ5vX12KRiZ7mn1U4bSwQ


Generated by Claude Code

…3366)
A capability in `requires: [...]` was only checked at serve time, and a
missing provider printed a generic "not installed — add it to your
dependencies" even when the provider has no installable version in the
current edition (e.g. `ai` -> @objectstack/service-ai, cloud-only since
ADR-0025). `os validate` (token vocabulary only) and `os build` (never
resolved providers) both passed, so a validate && build && test CI script
never caught it — it surfaced only as an opaque `os start` crash.
- spec: add `PLATFORM_CAPABILITY_PROVIDERS` (token -> package + edition) and
a pure `classifyRequiredCapability()` — one machine-readable source of
truth for the provider/edition knowledge the serve resolver encoded
informally. A drift test keeps it 1:1 with the vocabulary and in agreement
with serve's CAPABILITY_PROVIDERS packages.
- cli: `capability-preflight.ts` resolves each declared capability's provider
the way serve loads it (host dir, then CLI deps) and renders an
edition-aware message. `os build` and `os validate` fail fast on a
`requires` entry with no installable provider in the active edition; an
absent-but-installable provider is an advisory `pnpm add` hint; a satisfied
list passes unchanged.
- cli: the `os serve` boot error now renders the same classification, so
preflight and boot read identically.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013CQ5vX12KRiZ7mn1U4bSwQ
@vercel

vercelBot commented Jul 21, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specCanceledCanceledJul 21, 2026 2:08pm

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling size/l labels Jul 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/cli, @objectstack/spec.

110 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 packages/cli, @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/cli, @objectstack/spec)
  • content/docs/api/data-flow.mdx(via @objectstack/cli)
  • content/docs/api/environment-routing.mdx(via @objectstack/cli, @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/cli, @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/cli, 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/backup-restore.mdx(via @objectstack/cli)
  • content/docs/deployment/self-hosting.mdx(via @objectstack/cli)
  • 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/cli, @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/cli, @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/data-service.mdx(via packages/cli)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/cli, 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/authentication.mdx(via @objectstack/cli)
  • 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/cli, @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/cli, @objectstack/spec)
  • content/docs/protocol/kernel/realtime-protocol.mdx(via @objectstack/cli)
  • 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/cli, @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/cli, @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.

…ports
`check:api-surface` gates the @objectstack/spec public API. The #3366
additions (PLATFORM_CAPABILITY_PROVIDERS, classifyRequiredCapability, and
their types) are additive (0 breaking, 12 added) — regenerate the committed
snapshot so the gate passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013CQ5vX12KRiZ7mn1U4bSwQ
@os-zhuang
os-zhuang marked this pull request as ready for review July 21, 2026 14:25
@os-zhuang
os-zhuang merged commit 9e45b63 into mainJul 21, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/preflight-installable-provider-5t7j20 branch July 21, 2026 14:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Preflight that required capabilities have an installable provider in the current edition

2 participants

@os-zhuang@claude