Skip to content

feat(verify): extract dogfood engine into public @objectstack/verify + CLI - #2041

Merged
os-zhuang merged 3 commits into
mainfrom
feat/verify-package
Jun 19, 2026
Merged

os-zhuang merged 3 commits into
mainfrom
feat/verify-package

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jun 18, 2026 •

Copy link
Copy Markdown
Contributor

Why

ObjectStack is a platform where third parties have an AI author arbitrary metadata. The real risk isn't "a platform change broke the CRM example" — it's "the AI used a valid primitive the examples don't exercise, and it silently breaks at runtime" (the #2018 timezone-bucketing class: green on build / 900+ unit tests / spec-liveness / CodeQL, only visible when the real engine + services + HTTP run together).

The internal @objectstack/dogfood package already contained an app-agnostic objectstack verify engine in embryo (its derive/verify comments literally say "point it at any app … a third-party app like hotcrm"). This PR productizes that engine so third-party and template authors can run the same runtime proofs against their own apps.

What changed

New public library @objectstack/verify (packages/verify)

  • bootStack (was bootDogfoodStack), deriveCrudCases, runCrudVerification, runRlsProofs + report types — moved & generalized out of packages/dogfood/src.
  • tsup ESM+CJS+DTS, publishConfig.access: public, third-party-facing README.

New objectstack verify CLI command (packages/cli/src/commands/verify.ts)

  • oclif command reusing the existing loadConfig; flags --app/--rls/--multi-tenant/--json.
  • Exit-code contract (CI-usable): create-failed / read-failed / fidelity-gaps / rls-hole → exit 1; inconclusive verdicts (needs-fixture / skipped / member-visible) warn and exit 0.

@objectstack/dogfood kept, slimmed (single source of truth)

Release plumbing

  • @objectstack/verify added to the changeset fixed group (@objectstack/dogfood stays private/excluded); check-changeset-fixed.mjs passes (73 packages in sync). Changeset added for verify + cli.

Validated end-to-end on a real third-party app (hotcrm)

Ran the built CLI against ../hotcrm (a standalone marketplace CRM on published @objectstack@^9.9.1, with real sharing / territory / role-hierarchy rules — not in this repo):

  • Booted hotcrm's full 14-object stack through the real HTTP surface and verified data fidelity.
  • Demonstrated the exit-1 red path on a real app — crm_product.sku reads back uppercased (an intentional normalization hook), correctly flagged as a fidelity-gaps mismatch → exit 1.
  • fix(security)[P0]: enforce RLS on by-id writes — close member-edits-others'-records hole (#1985) #1994 authorization invariant holds on hotcrm: --rls --multi-tenant → 8 consistent / 0 holes (a fresh member can neither read 404 nor mutate 403 cross-org records) across its real sharing rules.

The exercise also surfaced two @objectstack/verify tool-quality issues (not hotcrm bugs), both addressed here:

  1. Fixed — the CLI ran fidelity + RLS proofs on the same in-process stack, so the RLS phase's admin-creates collided with the fidelity phase's rows on unique-constrained fields (a 409 that silently skipped those objects). The RLS phase now boots a separate pristine stack (hotcrm --rls --multi-tenant: 5-consistent/3-skipped → 8-consistent/0-skipped-by-409).
  2. Documented — fields normalized on write (uppercase/trim hooks, canonicalizing formulas) read back as fidelity-gaps even when working as designed; the report shows the wrote → read diff. A declared-transform allowlist is a planned enhancement.

Validated across the template corpus (9 apps)

Ran the CLI across all 9 apps in ../templates (the AI-authored starter corpus: expense, contracts, content, compliance, todo, project, procurement, hr, helpdesk), on an older @objectstack than this branch — a cross-version run:

Coverage caveat: the auto-derive skips objects whose required fields it can't synthesize (lookups / master-detail), so relational-heavy templates have ~half their objects skipped — "0 holes" is over the auto-exercised subset. Auto-synthesizing related records in topological order to reach those objects is a planned enhancement.

Verification

  • @objectstack/verify builds (ESM+CJS+DTS).
  • dogfood: 5 files / 57 tests green after the extraction and after rebasing onto the latest main (which carries spec/RLS schema changes) — zero behavior change.
  • objectstack verify against the framework examples: crm 4 verified / 2 skipped / exit 0, crm --rls 0 holes, showcase exit 0; clean process exit (fixed a hang: the in-process stack leaves handles open after stop(), so the command forces this.exit()).
  • objectstack verify against hotcrm (real third-party): boots, verifies, proves exit-1 and the fix(security)[P0]: enforce RLS on by-id writes — close member-edits-others'-records hole (#1985) #1994 invariant (above).
  • canonical lint, check-changeset-fixed.mjs, and ci.yml YAML all green/valid.

Fix found along the way

The harness used a port-less loopback inject origin (http://localhost) which fails better-auth's default dev trusted-origins (http://localhost:*) → objectstack verify got a 403 on dev-admin sign-in in a bare node CLI (the dogfood tests passed only because turbo's strict-env run masked it). Switched to a ported loopback base so sign-in is deterministic across the node CLI and the test runner.

🤖 Generated with Claude Code

…+ CLI

Productize the internal dogfood regression engine as a published library and an
`objectstack verify` CLI so third-party and template authors can run the same
runtime proofs (auto-derived CRUD round-trip fidelity + the cross-owner RLS
invariant) against their own apps.

- new public @objectstack/verify: bootStack / deriveCrudCases /
  runCrudVerification / runRlsProofs, moved+generalized from packages/dogfood/src
  (tsup ESM+CJS+DTS, publishConfig public)
- new `objectstack verify` oclif command (--app/--rls/--multi-tenant/--json);
  exits 1 on create-failed/read-failed/fidelity-gaps/rls-hole
- dogfood now consumes @objectstack/verify; drop 3 redundant auto-verify tests
  (replaced by an `objectstack verify` CI step over the example apps); keep the
  hand-written golden regressions (#2018 tz, #1994 RLS, #2004 field fidelity)
- fix: the harness used a port-less loopback inject origin (http://localhost)
  that fails better-auth's default dev trusted-origins (http://localhost:*);
  use a ported base so dev-admin sign-in passes in a bare node CLI and the test
  runner alike
- add @objectstack/verify to the changeset `fixed` group; changeset for verify+cli

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

vercel Bot commented Jun 18, 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 19, 2026 12:12am

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/l labels Jun 18, 2026
@github-actions

github-actions Bot commented Jun 18, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/cli, @objectstack/dogfood, @objectstack/verify.

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

  • content/docs/concepts/cloud-artifact-api.mdx (via packages/cli)
  • content/docs/concepts/implementation-status.mdx (via @objectstack/cli)
  • content/docs/concepts/packages.mdx (via @objectstack/cli)
  • content/docs/getting-started/cli.mdx (via @objectstack/cli)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/cli)
  • content/docs/guides/authentication.mdx (via @objectstack/cli)
  • content/docs/guides/client-sdk.mdx (via @objectstack/cli)
  • content/docs/guides/hook-bodies.mdx (via packages/cli)
  • content/docs/guides/packages.mdx (via @objectstack/cli)
  • content/docs/guides/project-scoping.mdx (via @objectstack/cli)
  • content/docs/guides/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/guides/runtime-services/index.mdx (via packages/cli)
  • content/docs/guides/skills.mdx (via packages/cli)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/cli)
  • content/docs/protocol/objectos/realtime-protocol.mdx (via @objectstack/cli)

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.

…delity gaps

Surfaced by running `objectstack verify` against the real third-party hotcrm app:

- The CLI ran data-fidelity and RLS proofs against the SAME in-process stack, so
  the RLS phase's admin-creates collided with rows the fidelity phase had already
  written on unique-constrained fields (e.g. a unique `sku`/`account_number`) →
  a 409 that silently skipped those objects' authorization check. Boot a separate
  pristine stack for the RLS phase. hotcrm `--rls --multi-tenant` goes from
  5-consistent/3-skipped(409) to 8-consistent/0-holes.
- Document the known limitation that fields normalized on write (uppercase/trim
  hooks, canonicalizing formulas) read back as `fidelity-gaps` even when working
  as designed (hotcrm's `sku`/`account_number` uppercase hooks).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ctstack/verify

ADR-0054 assigned the prove-it-runs mechanism to @objectstack/dogfood; this PR
extracted that engine into the published @objectstack/verify. Append an Update
addendum (and list it as a consumer) mapping the package to the ADR's phases —
dogfood stays the gate (hand-written golden proofs) and now runs on the verify
engine; Phase 1's field/RLS matrix is the published derive/verify/rls; Phase 3's
generative pass has deriveCrudCases as its seed — and stating the honest scope of
the auto-derived path (scalar round-trip + by-id RLS only) so it doesn't read as
subsuming the golden proofs. Decision unchanged.

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

This branch was successfully deployed

1 active deployment
Preview — f9d495fc Deployed Jun 19, 2026 by vercel[bot]
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/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant