Skip to content

test(spec): downstream-consumer contract — backward-compat gate for third parties (#2035)#2089

Merged
xuyushun441-sys merged 1 commit into
mainfrom
spec/external-consumer-gate
Jun 21, 2026
Merged

test(spec): downstream-consumer contract — backward-compat gate for third parties (#2035)#2089
xuyushun441-sys merged 1 commit into
mainfrom
spec/external-consumer-gate

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Follow-up to #2035 / #2088. Builds recommendation #1 from that discussion: the missing gate that catches a spec change breaking a lagging third-party consumer before release.

The gap this closes

Every spec consumer the framework currently tests — the example apps, @objectstack/dogfood — lives in this monorepo and co-evolves with the spec in the same commit. When a spec change would break them, the same PR just fixes them. So none of them can catch a change that breaks a real third party (e.g. hotcrm) that is pinned to a published release and authors metadata independently. grep hotcrm .github/ → 0 hits today.

What this adds

@objectstack/downstream-contract — a frozen fixture authored the way an external consumer does: the ObjectSchema.create builder, the defineX factories, and bare metadata literals (: ActionInput / : ReportInput / : PageInput — the pre-#2035 pattern real third parties used). It is governed by a freeze contract (see the package README):

A spec change that requires editing the fixtures to stay green is, by definition, a breaking change for third parties.

Two signals, both riding existing CI jobs (no new workflow):

Signal Catches Wired into
typecheck type-surface breaks (removed/narrowed export — the #2023 class) lint.yml → TypeScript Type Check
test runtime schema + cross-reference breaks turbo run test → Test Core

The test runs each bare-literal fixture through its schema's .parse() and assembles all metadata via defineStack (schema + cross-reference validation — the metadata core of objectstack validate).

Grounding (done for real, not theory)

Before writing this I overlaid the merged spec build into a local checkout of hotcrm (real external consumer on published 9.11.0) and ran its own gates: tsc --noEmit → 0 errors, objectstack validate → ✓ passed (15 objects / 15 pages / 10 reports / 11 actions / 17 flows). hotcrm also proved the original thesis — it uses defineView() but authors pages/reports/actions/flows as bare literals (incl. : Action, the output-type footgun), exactly because the factories were missing. This package is the deterministic, in-CI floor under that manual check.

Verification (local)

  • pnpm --filter @objectstack/downstream-contract typecheck → exit 0
  • pnpm --filter @objectstack/downstream-contract test → 2 passed
  • turbo run test --filter=@objectstack/downstream-contract → picked up ✓
  • Teeth confirmed: ActionSchema.parse rejects a broken action type (not a no-op)

Follow-ups (not in this PR)

  • A spec public-API surface snapshot (api-extractor) for an even tighter type-break signal.
  • Optional release-pipeline step that runs hotcrm's live validate && typecheck && build against the unreleased spec.

🤖 Generated with Claude Code

…2035)

Adds `@objectstack/downstream-contract` — a frozen, representative third-party
consumer of `@objectstack/spec`, and the CI gate that runs it.

Why: every other spec consumer the framework tests (example apps,
`@objectstack/dogfood`) lives in this repo and co-evolves with the spec in the
same commit, so none can catch a change that breaks a *real* third party (e.g.
hotcrm) pinned to a published release. This fixture is authored the way an
external project does — builder + `defineX` factories + bare metadata literals
(the pre-#2035 pattern) — and is FROZEN: a spec change that requires editing it
to stay green is, by definition, breaking.

Two signals, both wired into existing CI jobs (no new workflow):
  • typecheck — fixtures are typed with real spec types (`ActionInput`,
    `ReportInput`, `PageInput`, …); a removed/narrowed export fails (the #2023
    class). Added to lint.yml's TypeScript Type Check job.
  • test — each bare-literal fixture is run through its schema's `.parse()`, and
    everything is assembled via `defineStack` (schema + cross-reference
    validation, the metadata core of `objectstack validate`). Rides
    `turbo run test` (Test Core).

Verified locally: typecheck + test green; confirmed the schema parse has teeth
(rejects a broken action `type`). README documents the freeze contract.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 21, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 21, 2026 5:14am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation ci/cd dependencies Pull requests that update a dependency file tests tooling size/m labels Jun 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 1 changed package(s). ✅

@xuyushun441-sys
xuyushun441-sys merged commit 92647c1 into main Jun 21, 2026
17 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the spec/external-consumer-gate branch June 21, 2026 05:18
xuyushun441-sys added a commit that referenced this pull request Jun 21, 2026
…2035) (#2093)

The last layer of the third-party validation strategy from #2035: a live ceiling
above the deterministic in-repo floor (@objectstack/downstream-contract, #2089).

`scripts/downstream-smoke.sh` clones objectstack-ai/hotcrm at a pinned tag
(v1.2.0), installs it against the PUBLISHED @objectstack/* packages, overlays the
freshly-built — unreleased — @objectstack/spec dist, and runs hotcrm's own
`typecheck` + `objectstack validate`. If the about-to-publish spec breaks a real,
independently-authored third-party consumer, the release is blocked.

Wired into release.yml between the build and the changesets publish step, so it
runs only in the release pipeline (never reddens a feature PR). Mirrors the
existing console-SPA step that already clones an external repo at a pinned ref.

Verified end-to-end locally: clone hotcrm@v1.2.0 → install → overlay current
spec → typecheck + validate both pass (15 objects / 15 pages / 10 reports / 11
actions / 17 flows). Override the pinned ref with HOTCRM_REF.

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
xuyushun441-sys added a commit that referenced this pull request Jun 21, 2026
) (#2095)

The depth gate (#2089) only exercised object/view/action/report/page. Adds a
frozen bare-literal fixture for the remaining domains — datasource, connector,
policy, sharing rule, role, permission set, email template, webhook, object
extension, cube, mapping, theme, translation bundle — typed with the spec's own
input aliases and parsed against each domain's schema.

Now a narrowed/removed schema property on ANY writable domain breaks the
contract (authoring it surfaced two already: EmailTemplate requires `bodyHtml`,
Cube requires `dimensions`). 15 cases, typecheck + test green.

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
xuyushun441-sys added a commit that referenced this pull request Jun 21, 2026
…2097)

The #2088 guard only covered examples/**. Broadens it to the platform's own apps
(account/setup/studio) so the "author metadata via defineX, never a bare
`: DomainType` literal" convention holds across every authoring surface, not just
the example corpus. Explicitly excludes packages/downstream-contract, whose bare
literals are an intentional frozen backward-compat fixture (#2089).

No migrations — apps author no bare-literal metadata today; this is future-
proofing. Verified the guard fires on a probe under packages/apps and that
`pnpm lint` is green.

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
xuyushun441-sys added a commit that referenced this pull request Jun 21, 2026
…ates (#2035) (#2098)

Documents the layered strategy built across #2088/#2089/#2092/#2093/#2095/#2097
in one authoritative place: why in-repo consumers can't witness backward compat
(they co-evolve with the spec), the six gates and each one's job, and — crucially
— the FREEZE CONTRACT: a change that requires editing the fixtures or removing a
snapshot entry is by definition breaking (bump major), never a mechanical
test-update. Also formalizes `objectstack validate` as the third party's
authoritative self-gate.

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants