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

feat(verify): extract dogfood engine into public @objectstack/verify + CLI#2041
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 appcrm_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-tenant8 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>
@os-zhuang
os-zhuang merged commit fd2e1a2 into main Jun 19, 2026
16 of 17 checks passed
@os-zhuang
os-zhuang deleted the feat/verify-package branch June 19, 2026 00:16
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