Skip to content

Fix Permission custom pivots and relation scope safety - #491

Merged
binaryfire merged 14 commits into
0.4from
audit/permission-correctness-parity-lifecycle
Aug 8, 2026
Merged

Fix Permission custom pivots and relation scope safety#491
binaryfire merged 14 commits into
0.4from
audit/permission-correctness-parity-lifecycle

Conversation

@binaryfire

@binaryfire binaryfire commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

This change completes the targeted Permission correctness and extension maintenance while preserving Hypervel's partitioned, denied-permission, and coroutine-safe design.

The main changes are:

  • retain and group relation-owned pivot predicates across custom pivot select, save, delete, detach, and update operations;
  • honor the documented using(CustomPivot::class) extension path for permission and role assignments, including deferred assignments, casts, events, and guarded pivot attributes;
  • synchronize role replacements by exact edge differences so retained custom pivot rows keep their attributes, timestamps, identity, and lifecycle hooks;
  • restore complete replacement-event payloads and ordering while avoiding event work when no matching listener exists;
  • build complete, correctly oriented pivots for cached direct and role-derived permissions without adding queries to authorization checks;
  • preserve exact team identifiers and return truthful command status when setup publication fails;
  • restore package discovery, direct split-package dependencies, and executable metadata coverage;
  • update the Permission guide, audit records, and implementation plan.

Design

Permission's warm authorization and permission-name paths remain on the compact assignment cache. They do not construct relations or add database queries.

Saved writes resolve the model's public roles() or permissions() relation once and use its captured team and partition context through comparison, mutation, and invalidation. Stock pivots retain set-based writes. Applications that explicitly configure a custom pivot use native per-row Eloquent operations where casts and lifecycle hooks require them.

The shared Database relation layer now owns destructive predicate retention. Relation constraints are grouped beneath the parent identity, including range constraints, so an or predicate cannot escape to another parent or partition. Explicit custom-pivot attributes bypass mass-assignment filtering while continuing through casts, mutators, timestamps, and events.

Role replacement reads the current scoped IDs once, computes the exact difference, and writes only changed edges. The same read supplies detached-event IDs when that event is enabled, avoiding a second payload query. No-op replacements preserve warm caches and perform no writes.

Compatibility and performance

Supported Spatie-facing method names, named arguments, relation overrides, event payloads, command options, pivot events, and custom casts remain compatible or are restored.

The normal authorization path gains no query, relation construction, lock, yield, serialization, or retained worker state. Stock pivots keep bulk operations. Custom pivots pay per-row model work only when an application opts into a custom pivot class. Replacement-event hydration remains listener-gated.

Validation

  • composer fix
  • complete Permission and Database package coverage
  • focused partition, custom-pivot, event, command, and cache regressions
  • root and split Composer manifest validation
  • package discovery and dependency consistency checks

For more details, see: docs/plans/2026-08-08-0059-permission-correctness-extension-parity-and-relation-scope-safety.md

Summary by CodeRabbit

  • New Features

    • Added support for custom pivot models in permission and role assignments, including deferred operations and team-aware relationships.
    • Improved permission and role synchronization, replacement events, revocations, and partition isolation.
    • Added reliable handling for explicit team IDs, including 0, and clearer setup-command failure reporting.
    • Registered Horizon, Permission, and Wayfinder providers for automatic discovery.
  • Bug Fixes

    • Preserved pivot constraints during updates, saves, and deletes.
    • Prevented unnecessary cache resets when permissions remain unchanged.
  • Documentation

    • Expanded permission and custom pivot guidance, including events, performance, and relationship behavior.

Retain every relation-owned pivot predicate, including between clauses, and replay them as one grouped condition so boolean-or scopes cannot escape the relation identity during explicit pivot writes.

Carry those constraints onto hydrated Pivot and MorphPivot instances, preserve primary-key authority, and route current-pivot hydration through the relation's canonical constructor. Explicit custom-pivot attributes now use coroutine-safe forceFill semantics so casts, mutators, timestamps, and model events run without applying request mass-assignment policy.

Add counterfactual coverage for grouped scope replay, range predicates, cross-parent isolation, primary-key behavior, guarded custom attributes, morph identity, and stock/custom pivot events.
Resolve saved writes through the public roles and permissions relations, preserve their captured partition/team context, and retain stock set-based mutations while allowing configured custom pivots to run native casts and lifecycle hooks. Deferred assignments now retain their selected pivot class without storing request models or adding coroutine state.

Complete warm direct and role-derived pivot metadata, constraints, and orientation; return fresh public via-role pivots so callers cannot mutate cached authorization edges. Replacement events publish the complete pre-operation payload after successful writes, while no-op permission syncs preserve warm caches and role sync mutates only changed edges so retained custom pivot rows survive.

Add coverage for custom and partitioned pivots, event ordering and failure paths, warm-pivot save/delete isolation, catalog alias protection, no-op cache retention, exact role-diff writes, deferred assignments, and stock bulk-update retention.
Treat only null and empty team options as absent so the string zero remains a valid team identifier, restore the caller's prior team context after command execution, and report migration publication failures through the command exit status.

Declare Permission's direct split-package dependencies, remove runtime class guards for those installed dependencies, and register Permission, Horizon, and Wayfinder providers in the root manifest. Add a repository-wide split-provider discoverability invariant and exact Permission metadata parity coverage.

Expand command regressions for disabled teams, zero identifiers, global-role warnings, existing migrations, copy failures, context restoration, and About output.
Cover guard resolution without a provider, LDAP-backed model discovery, missing Passport clients, model-valued team identifiers, missing role IDs, exact pipe-delimited role checks, current-team reverse assignments, invalid wildcard implementations, and blank wildcard subparts.

These are supported current Spatie behaviors that Hypervel already implements; the tests make that compatibility executable without adding production seams or compatibility machinery.
Add the direct Symfony HttpKernel, Carbon, and Composer runtime requirements already used by Broadcasting, Concurrency, Contracts, DI, Notifications, Passkeys, Process, and Telescope.

Pin each split manifest to the root constraint with focused metadata tests so subtree packages remain independently installable and dependency drift fails in CI.
Define permissions and roles in Laravel-style terms, correct the revocation example, and document saved replacement event payloads, listener gating, transaction ordering, and no-op cache behavior.

Show the supported trait-alias custom-pivot extension pattern, explain which model-returning APIs load the real relation, preserve the compact authorization cache contract, and state the reverse arbitrary-model boundary and exact performance costs.
Route permission-06 through permission-18, database-29 through database-33, and the related discovery and metadata findings through the core dependency index.

Add the final ledger assessment covering lifecycle ownership, custom-pivot semantics, replacement events, no-op cache preservation, differential role sync, performance boundaries, cross-package revalidation, rejected machinery, verification, and independent review status while leaving the later fresh Permission audit optional.
Record the agreed Database pivot ownership, Permission custom-pivot and replacement-event contracts, warm-pivot construction, command and metadata corrections, supported upstream coverage, documentation scope, rejected overengineering, and complete verification strategy.

The plan includes the core audit's exact anti-overengineering guidance and the final permission-06 through permission-18 design so implementation intent and performance boundaries remain durable across future maintenance.
…ctness-parity-lifecycle

# Conflicts:
#	docs/plans/2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.md
#	tests/Passkeys/PackageMetadataTest.php
Require new root package dependencies to use the latest compatible stable release published on Packagist.\n\nDocument that the root Composer lock is intentionally untracked, that local dependencies should be refreshed after dependency changes, and that stale local lock state is not a repository defect.
Update the role replacement query-count regression for the diff-based synchronization path. Initial assignment now asserts one scoped pivot read followed by one bulk insert, without expecting an unnecessary delete.\n\nClarify that detached-event payloads reuse the mandatory role read and require both role and permission pivot-read regressions to pin the SELECT statement type.
…ctness-parity-lifecycle

# Conflicts:
#	docs/plans/2026-07-12-0900-framework-coroutine-state-lifecycle-audit.md
#	docs/plans/2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.md
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f6ddf95-7edb-41a2-8db1-35129aea50c5

📥 Commits

Reviewing files that changed from the base of the PR and between 4198040 and 51f752d.

📒 Files selected for processing (58)
  • AGENTS.md
  • composer.json
  • docs/plans/2026-07-12-0900-framework-coroutine-state-lifecycle-audit.md
  • docs/plans/2026-07-12-0915-framework-coroutine-state-lifecycle-audit-ledger.md
  • docs/plans/2026-08-08-0059-permission-correctness-extension-parity-and-relation-scope-safety.md
  • src/boost/docs/permission.md
  • src/broadcasting/composer.json
  • src/concurrency/composer.json
  • src/contracts/composer.json
  • src/database/src/Eloquent/Relations/BelongsToMany.php
  • src/database/src/Eloquent/Relations/Concerns/AsPivot.php
  • src/database/src/Eloquent/Relations/Concerns/InteractsWithPivotTable.php
  • src/database/src/Eloquent/Relations/MorphToMany.php
  • src/di/composer.json
  • src/notifications/composer.json
  • src/permission/composer.json
  • src/permission/src/Commands/AssignRoleCommand.php
  • src/permission/src/Commands/CreateRoleCommand.php
  • src/permission/src/Commands/UpgradeForTeamsCommand.php
  • src/permission/src/PermissionRegistrar.php
  • src/permission/src/PermissionServiceProvider.php
  • src/permission/src/Support/Config.php
  • src/permission/src/Traits/BuildsPermissionRelations.php
  • src/permission/src/Traits/EnforcesPermissionPartition.php
  • src/permission/src/Traits/HasPermissions.php
  • src/permission/src/Traits/HasRoles.php
  • src/process/composer.json
  • src/telescope/composer.json
  • tests/Broadcasting/PackageMetadataTest.php
  • tests/Composer/PackageManifestConsistencyTest.php
  • tests/Concurrency/PackageMetadataTest.php
  • tests/Contracts/PackageMetadataTest.php
  • tests/Database/Eloquent/Relations/BelongsToManyPivotEventsTest.php
  • tests/Database/Eloquent/Relations/MorphToManyPivotEventsTest.php
  • tests/Database/Eloquent/Relations/migrations/2025_01_01_000000_create_pivot_events_test_tables.php
  • tests/Di/PackageMetadataTest.php
  • tests/Notifications/PackageMetadataTest.php
  • tests/Permission/Commands/CommandTest.php
  • tests/Permission/Commands/TeamCommandTest.php
  • tests/Permission/CustomPivotTest.php
  • tests/Permission/Events/EventTest.php
  • tests/Permission/Events/PartitionEventTest.php
  • tests/Permission/GuardTest.php
  • tests/Permission/Integration/CacheTest.php
  • tests/Permission/Integration/PartitionQueryCountTest.php
  • tests/Permission/Integration/PermissionRegistrarTest.php
  • tests/Permission/Models/RoleTest.php
  • tests/Permission/PackageMetadataTest.php
  • tests/Permission/PartitionCustomPivotTest.php
  • tests/Permission/PartitionRelationsTest.php
  • tests/Permission/PartitionTeamsTest.php
  • tests/Permission/Traits/HasPermissionsTest.php
  • tests/Permission/Traits/HasRolesTest.php
  • tests/Permission/Traits/TeamHasAssignedModelsTest.php
  • tests/Permission/Traits/TeamHasPermissionsTest.php
  • tests/Permission/Traits/WildcardHasPermissionsTest.php
  • tests/Process/PackageMetadataTest.php
  • tests/Telescope/PackageMetadataTest.php

📝 Walkthrough

Walkthrough

Changes

Permission correctness and package parity

Layer / File(s) Summary
Database pivot constraint safety
src/database/src/Eloquent/Relations/..., tests/Database/Eloquent/Relations/...
Pivot constraints now persist through hydration, selection, updates, and deletion. Custom pivot writes use forceFill, and range predicates constrain destructive queries.
Permission relation and pivot context
src/permission/src/PermissionRegistrar.php, src/permission/src/Traits/*, tests/Permission/CustomPivotTest.php, tests/Permission/Partition*
Permission and role operations preserve custom pivot classes, captured relation contexts, partition and team metadata, warm-pivot identity, and role-derived pivot isolation.
Synchronization and command outcomes
src/permission/src/Commands/*, src/permission/src/Traits/HasPermissions.php, src/permission/src/Traits/HasRoles.php, tests/Permission/Events/*, tests/Permission/Commands/*
Replacement synchronization dispatches detached and attached events, avoids cache resets for no-op changes, preserves explicit team ID 0, and reports migration-copy failures.
Package metadata and documentation
composer.json, src/*/composer.json, tests/*/PackageMetadataTest.php, docs/plans/*, src/boost/docs/permission.md
Split-package dependencies and providers are registered and checked against root metadata. Audit records, implementation plans, and Permission documentation describe the updated behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PermissionModel
  participant PermissionRegistrar
  participant Relation
  participant Pivot
  participant PermissionEvents
  PermissionModel->>Relation: capture relation context
  PermissionRegistrar->>Relation: resolve pivot class
  Relation->>Pivot: hydrate constrained pivot
  PermissionModel->>PermissionRegistrar: synchronize assignments
  PermissionRegistrar->>PermissionEvents: dispatch detached and attached events
  PermissionRegistrar->>PermissionModel: invalidate cache after actual changes
Loading

Possibly related PRs

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch audit/permission-correctness-parity-lifecycle

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@binaryfire
binaryfire merged commit d80d05a into 0.4 Aug 8, 2026
36 of 37 checks passed
@binaryfire

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Pull request is closed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant