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 orpnpm 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

vercelBot commented Jun 20, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJun 20, 2026 11:54am

Request Review

@github-actions

github-actionsBot 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.

…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 mainJun 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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude