Skip to content

feat(profiles): tenant_admin gains the org-scoped presentation authority (manage_org_presentation) - #1994

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-1369-tenant-admin-org-presentation-r71
Oct 3, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-1369-tenant-admin-org-presentation-r71

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #1369
Clause-②: no. The change adds one capability that the installed @objectstack/spec 17.6.0 already declares scope: 'org' to the app's own tenant_admin profile. It touches no published schema and no accept set.

Permission boundary: grants one org-scoped capability to tenant_admin

The SaaS composition's tenant_admin (src/sales/profiles/tenant-admin.profile.ts) gains manage_org_presentation in systemPermissions. The community system_admin and the default composition are untouched. This PR changes no package.json or lockfile, because main is already on @objectstack/* 17.6.0.

Readings on the installed @objectstack/spec 17.6.0 (worktree at 25cd8d78, before the edit):

  • The card's Restart-when: probe, run verbatim: exit 0.
  • The PLATFORM_CAPABILITIES entry, verbatim: {"name":"manage_org_presentation","label":"Manage Organization Presentation","description":"Author per-organization presentation overlays — the metadata types whose registry entry declares allowOrgOverride — scoped to the caller’s own organization.","scope":"org"}. That is 1 of 9 entries.
    • Positive control: manage_metadata reads scope: 'platform'.
    • Negative control: manage_org_presentation_zzz reads as absent.
  • The enforcing function's verdict. This was a one-off scratch call to metaWriteCapabilityVerdict from the installed @objectstack/metadata-core, with operation save. It is not a test file, and no test imports that transitive dependency.
    • With today's list, every case is DENIED.
    • With the grant added:
      • view, dashboard, report, translation and email_template saves with an active org are ALLOWED.
      • view, dashboard and report saves without an active org are DENIED.
      • object, flow, app, page, permission and position stay DENIED with an active org, refused with "Saving a metadata item requires the manage_metadata capability."
    • Control: manage_metadata alone on permission is ALLOWED.
  • The override registry, DEFAULT_METADATA_TYPE_REGISTRY in @objectstack/spec/kernel, has 28 types. Exactly five declare allowOrgOverride: true: view, dashboard, report, translation and email_template.

What this proves, and what it does not. It proves the registry's scope for the key, and the platform evaluator's verdict for this exact permission list. It does not prove that a booted SaaS composition threads this profile into a session. No boot was run, and the card does not require one.

Changes

  • src/sales/profiles/tenant-admin.profile.ts
    • One systemPermissions entry, 'manage_org_presentation', with a one-line comment in the file's own style.
    • The docstring paragraph "What is deliberately DROPPED … Blocked-by: Org-scoped presentation customization authority: a tenant org admin authors tier-A overlays without platform-wide manage_metadata objectstack#12702 … this paragraph shrinks to a grant" shrinks to the grant, as it said it would, and the Blocked-by: line is dropped.
    • The paragraph still says why customize_application, manage_profiles and manage_roles stay ungranted. That claim was re-measured on 17.6.0, not carried over: none of the three is in PLATFORM_CAPABILITIES (they are app vocabulary). The types they author (object, app, page, permission, position) are all allowOrgOverride: false, and the verdict above refuses them without manage_metadata.
  • test/saas-composition.test.ts: a positive assertion beside the manage_org_users one. tenant_admin holds manage_org_presentation, and the installed PLATFORM_CAPABILITIES declares it scope: 'org'. The scope is read from the registry, not copied. The existing pin "grants NO platform-scoped capability" is unchanged and stays green with the grant, which is the proof that the grant stays inside one organization.
  • .changeset/1369-tenant-admin-org-presentation.md: 'hotcrm': minor, as the card asks. hotcrm's AGENTS.md has no rule against minor here.

No docs enumerate tenant_admin's permissions. git grep manage_org_users over docs/, content/ and README.md returns 0 hits. As a control, tenant_admin returns 1 hit, docs/ARCHITECTURE.md:229, which names the profile without listing grants. So no doc line changes.

Verification

All of the following ran at HEAD 22d1682c, the final commit on this branch. The SHA was printed by the same locked run, before the gates.

  • OS_VERIFY_LOCK_SLOT=hotcrm-issue-1369 bash …/os-verify-lock.sh -c 'git rev-parse --short HEAD && pnpm verify' printed 22d1682c, then os-verify-lock: VERDICT command-exit 0. Along the chain:
    • ✓ Validation passed
    • tsc --noEmit clean
    • objectstack lint: 1 warning, which predates this PR and is unrelated (sales_home_page page:card description)
    • ✓ i18n lint gate: 0 i18n/missing-* issues
    • ✓ source hygiene clean
    • token ratchet all ✓
    • ✓ Build complete
    • Test Files 174 passed (174), Tests 3702 passed | 1 skipped (3703)
  • Targeted run (locked): pnpm exec vitest run --maxWorkers=2 test/saas-composition.test.ts returned Tests 18 passed (18), exit 0.
  • Ablation (locked, one-off, no permanent test), through scripts/ablation-replace.mjs with the change already committed:
    • It deleted the 'manage_org_presentation', entry. The anchor went x1 → x0, and the blob went ac15ca5b8167 → 836edba0ce20.
    • The same file then went red on exactly the new test: × holds the org-scoped presentation-authoring capability (#1369), with AssertionError: expected [ 'view_setup', …(4) ] to include 'manage_org_presentation' and Tests 1 failed | 17 passed (18). The platform-scoped pin stayed green.
    • The restore was proven: blob == HEAD (ac15ca5b8167) and git diff HEAD is empty.

Not run locally: a boot of the SaaS composition (optional for this card).

Token ratchet (node scripts/check-source-token-ratchet.mjs, exit 0 both runs), the src/sales rows before → after:

  • business semantics ~53,433 → ~53,433 tokens (ceiling ~55,000)
  • interaction layer ~27,646 → ~27,646 tokens (ceiling ~31,000)
  • authored total ~96,558 → ~96,566 tokens (ceiling ~100,000; headroom ~3,434). src/sales/profiles counts in the total only.

No ceiling moved.

Acceptance notes


Generated by Claude Code

The SaaS composition's `tenant_admin` shipped with no metadata-authoring
key, because the platform's only one was `manage_metadata` (`scope:
'platform'`). The installed `@objectstack/spec` 17.6.0 declares the
org-scoped subset `manage_org_presentation` (`scope: 'org'`), so the
profile now grants it.

The docstring's "deliberately DROPPED" paragraph shrinks to that grant,
as it said it would, and its `Blocked-by:` line goes. The three app keys
it named (`customize_application`, `manage_profiles`, `manage_roles`)
stay ungranted: on 17.6.0 the types they author (object, app, page,
permission, position) are all `allowOrgOverride: false`, so they still
need the platform-scoped key.

`test/saas-composition.test.ts` adds the positive assertion, with the
scope read from the installed `PLATFORM_CAPABILITIES`. The existing pin
that `tenant_admin` holds no platform-scoped capability is unchanged.

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

vercel Bot commented Oct 3, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated
hotcrm Ignored Ignored Oct 3, 2026 3:44am UTC

Request Review

@github-actions github-actions Bot added ci/cd CI plumbing and the verification pipeline metadata Declarative metadata — schema, security posture, UI surfaces labels Oct 3, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 3, 2026 04:55
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 3, 2026
Merged via the queue into main with commit bea1bf9 Oct 3, 2026
10 checks passed
hotlong pushed a commit that referenced this pull request Oct 3, 2026
… raise, #1994, #1963

No conflicts. #1953 raises the src/sales ceilings to 59,000 / 107,000, the
ruled answer to this branch's measurement. #1994 (tenant_admin profile) and
#1963 (tsx bump, pnpm-lock.yaml) do not touch this branch's file surface.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ER8ntXZhYebyQ66aXWdjfT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd CI plumbing and the verification pipeline metadata Declarative metadata — schema, security posture, UI surfaces

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Grant manage_org_presentation to tenant_admin once the capability ships in a released @objectstack line

2 participants