Skip to content

ADR-0056 D6: activate declarative roles + sharingRules at runtime (enforce-or-remove) — unblocks role_and_subordinates showcase demo #2077

Description

@os-zhuang

Background

ADR-0056 D6 added the role_and_subordinates sharing-rule recipient (configurable role-hierarchy widening), implemented in @objectstack/plugin-sharing (role-graph.tsRoleGraphService + sharing-rule-service.tsexpandRecipient) and covered by the plugin's own unit tests.

While trying to demonstrate D6 in the showcase (as part of #2076), I found a deeper, pre-existing architectural gap that blocks it from being shown end-to-end.

The gap

Stack-declared roles and sharingRules are never seeded into sys_role / sys_sharing_rule at runtime — neither by the verify harness (bootStack) nor by the real pnpm dev CLI (packages/cli/src/commands/serve.ts).

Evidence:

  • Booting the showcase via bootStack yields sys_role count = 0 and sys_sharing_rule count = 0, even though the showcase declares 3 roles + 2 sharing rules.
  • SharingServicePlugin.start() binds rule hooks from ruleService.listRules(), which reads the sys_sharing_rule table — but nothing populates that table from app metadata. Boot logs show [sharing-rule] hooks bound {"objects":[],"ruleCount":0}.
  • A repo-wide search finds no code path that inserts stack-declared roles/sharingRules into sys_role/sys_sharing_rule (only the runtime createRule API at sharing-rule-service.ts:138 writes the table).

Consequence: the showcase's existing declarative roles + sharing rules (and any app's, e.g. examples/app-crm) are decorative — Studio/authoring metadata that never enforces at runtime. This is a classic enforce-or-remove (ADR-0049) situation.

Note: this is distinct from permission sets, which do resolve at runtime via metadataService.list('permission') / sys_permission_set (permission-evaluator.ts resolvePermissionSets). D7's app-declared default profile was wired through the CLI in #2076 precisely because permission sets resolve; roles + sharing rules do not.

Proposed work

Seed (idempotent upsert) stack-declared rolessys_role and sharingRulessys_sharing_rule at boot, so the existing sharing-rule evaluator (rule-hooks.tsafterInsert/afterUpdate) activates them.

  • Likely homes: SecurityPlugin for roles (sys_role), SharingServicePlugin for sharing rules (sys_sharing_rule), reading from metadataService.list('role' / 'sharingRule').
  • Must be idempotent (re-seed on each boot without duplicating), and reconcile updates/removals.
  • Decide org-scoping behavior (per-tenant seeding under plugin-org-scoping).

⚠️ Blast radius — needs its own ADR + careful review

This changes enforcement behavior for every app that declares roles/sharing rules (examples/app-showcaseandexamples/app-crm today). Activating previously-inert rules can change who-can-see-what. Requires:

  • A full dogfood-suite run across all example apps (currently green precisely because rules are inert).
  • An ADR documenting the enforce-or-remove decision and migration/rollout posture.

Acceptance criteria

  • ADR for "activate declarative roles + sharing rules at runtime".
  • Stack-declared roles + sharing rules seeded idempotently into sys_role / sys_sharing_rule at boot (verify harness + CLI).
  • Full dogfood suite green across all example apps; any behavior changes intentional + documented.
  • Showcase D6 demo: add a role_and_subordinates sharing rule + a conventional role hierarchy (exec → manager → contributor), with an end-to-end dogfood proof that a manager + their subordinates gain access via the widening.

References

  • ADR-0056 (permission model landing verification); D6 section.
  • Merged in feat(app-showcase): ADR-0056 Option A public form + D7 default-profile CLI wiring #2076: Option A public form + D7 default-profile CLI wiring (D6 explicitly deferred here).
  • Code: packages/plugins/plugin-sharing/src/{role-graph,sharing-rule-service,rule-hooks,sharing-plugin}.ts, packages/cli/src/commands/serve.ts, packages/plugins/plugin-security/src/security-plugin.ts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions