Skip to content

feat(app-showcase): ADR-0056 Option A public form + D7 default-profile CLI wiring#2076

Merged
os-zhuang merged 5 commits into
mainfrom
claude/adr-0056-showcase-completion
Jun 20, 2026
Merged

feat(app-showcase): ADR-0056 Option A public form + D7 default-profile CLI wiring#2076
os-zhuang merged 5 commits into
mainfrom
claude/adr-0056-showcase-completion

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Goal

Make the pnpm dev showcase visibly demonstrate the remaining ADR-0056 capabilities — and do the platform fixes so they genuinely take effect, not just sit in metadata.

What's in this PR (both proven end-to-end)

✅ Option A — web-to-lead public form

  • New showcase_inquiry object + a public FormView (allowAnonymous + publicLink: /forms/contact-us) + a guest-defaults hook (status=new, source=web).
  • Authorized purely by the declaration-derived publicFormGrant (create + read-back on showcase_inquiry only) — no guest_portal profile, even under secure-by-default auth.
  • Proof (showcase-public-form.dogfood.test.ts, 3/3): drives the real HTTP routes under requireAuth: true — GET resolves the form + whitelisted schema, POST creates the inquiry anonymously with stamped defaults, and a general anonymous read is still denied.

✅ D7 — app-declared default profile, wired through the CLI

  • Platform gap found & fixed: D7 added isDefault but resolved it only over the SecurityPlugin's built-in sets. The CLI boots new SecurityPlugin() with no options, so an isDefault profile declared in app metadata was silently ignored under pnpm dev.
  • New exported helper appDefaultProfileName(permissions); serve.ts passes the extracted name as fallbackPermissionSet (undefined → built-in default preserved, so non-declaring apps are unaffected). It resolves at request time via the metadata service / sys_permission_set.
  • Showcase declares showcase_member_default (read-mostly baseline) so pnpm dev shows an app-declared default posture.
  • Proofs: appDefaultProfileName unit test (4/4) + showcase-d7-default-profile.dogfood.test.ts (3/3) — a fresh member is governed by the declared default (reads announcements, denied private notes the built-in wildcard would allow).

Full showcase dogfood suite re-run green (25/25) — no regressions.

⚠️ D6 (role_and_subordinates) — deferred, needs a dedicated ADR

Investigating D6 surfaced a broader architectural gap: stack-declared roles and sharingRules are never seeded into sys_role / sys_sharing_rule at runtime — by the harness or pnpm dev. The showcase's existing 3 roles + 2 sharing rules are authoring/Studio metadata that never fire; the sharing-rule evaluator reads the table, which nothing populates from app metadata.

So making D6 "truly take effect" is not a showcase tweak — it's an enforce-or-remove (ADR-0049-style) platform feature: runtime activation of declared roles + sharing rules, which would change enforcement behavior for every app (showcase and CRM). That deserves its own focused ADR + PR rather than riding in here. D6's role_and_subordinates mechanism itself is already covered by plugin-sharing's own tests.

Recommendation: land Option A + D7 here; open a dedicated ADR/PR for "activate declarative roles + sharing rules at runtime" (which D6 then rides on, with a real showcase rule + hierarchy + end-to-end proof).

🤖 Generated with Claude Code


Generated by Claude Code

claude added 2 commits June 20, 2026 11:17
…tion A

Adds a `showcase_inquiry` object + a public FormView (`allowAnonymous` +
`publicLink: /forms/contact-us`) so `pnpm dev` ships a working anonymous
web-to-lead form. The submit route authorizes via the declaration-derived
`publicFormGrant` (create + read-back on `showcase_inquiry` only) — no
`guest_portal` profile, even under secure-by-default auth. A beforeInsert
hook stamps server-controlled defaults (status=new, source=web).

Dogfood proof (`showcase-public-form.dogfood.test.ts`) drives the real
HTTP routes end-to-end under `requireAuth: true`: GET resolves the form +
whitelisted schema, POST creates the inquiry anonymously with stamped
defaults, and a general anonymous read is still denied (the grant is
create + read-back only).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XVdnfUAx85amkerym26vdx
…R-0056 D7)

D7 added `isDefault` to PermissionSet and resolves it in the SecurityPlugin
constructor — but only over the plugin's built-in `defaultPermissionSets`.
The CLI boots `new SecurityPlugin()` with no options, so an `isDefault`
profile declared purely in APP METADATA was silently ignored under
`pnpm dev`. This closes that gap:

- `appDefaultProfileName(permissions)` — exported helper that extracts the
  first `isProfile && isDefault` profile name from a stack's permissions.
- CLI `serve.ts` calls it and passes the name as `fallbackPermissionSet`
  (undefined → built-in default preserved, so non-declaring apps are
  unaffected). At request time the name resolves through the metadata
  service / `sys_permission_set` like any user-defined permission set.
- Showcase declares `showcase_member_default` (isDefault) — a read-mostly
  baseline for fresh sign-ups, so `pnpm dev` demonstrates an app-declared
  default posture instead of the built-in wildcard.

Proofs: unit test for `appDefaultProfileName`, and a showcase dogfood test
that wires the extracted name as the fallback and shows a fresh member is
governed by the declared default (reads announcements, denied private notes
the built-in wildcard would have allowed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XVdnfUAx85amkerym26vdx
@vercel

vercel Bot commented Jun 20, 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 20, 2026 11:54am

Request Review

@github-actions

github-actions Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

19 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, @objectstack/plugin-security)
  • content/docs/concepts/packages.mdx (via @objectstack/cli, @objectstack/plugin-security)
  • content/docs/getting-started/cli.mdx (via @objectstack/cli, @objectstack/plugin-security)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/cli)
  • content/docs/guides/authentication.mdx (via @objectstack/cli)
  • content/docs/guides/cheatsheets/permissions-matrix.mdx (via packages/plugins/plugin-security)
  • content/docs/guides/client-sdk.mdx (via @objectstack/cli)
  • content/docs/guides/hook-bodies.mdx (via packages/cli)
  • content/docs/guides/metadata/dashboard.mdx (via @objectstack/plugin-security)
  • content/docs/guides/packages.mdx (via @objectstack/cli, @objectstack/plugin-security)
  • content/docs/guides/plugins.mdx (via @objectstack/plugin-security)
  • 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/security.mdx (via @objectstack/plugin-security)
  • 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.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling labels Jun 20, 2026
…ry view

defineView infers the object from list.data; FormView has no label field.
These tripped the example-apps typecheck (TS2353).
…lity)

The guest-defaults hook unconditionally set source='web', so the verify
round-trip wrote source='verify-sample' and read back 'web' — a fidelity
gap that failed the Dogfood Regression Gate. Make it a default (only when
absent), matching status. Public submissions never include source (the
form whitelist excludes it), so they still get 'web'; explicit values now
round-trip cleanly.
@os-zhuang
os-zhuang marked this pull request as ready for review June 20, 2026 12:38
@os-zhuang
os-zhuang merged commit 5a5a9fe into main Jun 20, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/adr-0056-showcase-completion branch June 20, 2026 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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