Skip to content

feat: admin panel module with tenant-aware infrastructure#206

Merged
danielhe4rt merged 14 commits into
4.xfrom
feat/base-admin-panel
May 1, 2026
Merged

feat: admin panel module with tenant-aware infrastructure#206
danielhe4rt merged 14 commits into
4.xfrom
feat/base-admin-panel

Conversation

@danielhe4rt
Copy link
Copy Markdown
Contributor

@danielhe4rt danielhe4rt commented May 1, 2026

Summary

  • New module panel-admin (app-modules/panel-admin/) with config, ServiceProvider, Dashboard page, and ApplyTenantScopes middleware
  • AdminPanelProvider in app/Providers/Filament/ — Filament v5 panel with purple theme, tenant-aware via Tenant model with slug URLs (/admin/{tenant-slug}/...), auth middleware, and discovery-based resource loading from domain modules
  • Migration to clean up orphaned tenant_id=2 records in production
  • Seeders refactored — removed ThreeDotsSeeder, updated BaseSeeder with super admin (danielhe4rt) attached to both tenants

Architecture

Resources will live in their domain modules (e.g. identity/src/Filament/Admin/Resources/) and are auto-discovered via config('panel-admin.modules') + the existing discoverResourcesForPanel macro. The panel-admin module stays lean — only infrastructure.

Test plan

  • 3 tests for ApplyTenantScopes middleware (scope applied, skipped when no tenant, empty config)
  • 5 tests for admin panel access (login page, auth redirect, admin access, canAccessPanel, production enforcement)
  • Full test suite passes (50 passed, 3 skipped)
  • PHPStan level 6 clean
  • Pint clean

Summary by CodeRabbit

  • New Features

    • Introduced admin panel with dashboard and multi-tenant support
    • Implemented tenant-scoped model access control to ensure data isolation
    • Added admin authentication and access control system
  • Tests

    • Added feature tests for admin panel access and permissions
    • Added tests for tenant scope application and validation
  • Chores

    • Added database migration to clean up orphaned tenant records
    • Updated database seeding structure to reflect new admin setup

Copy link
Copy Markdown
Contributor

@thalesmengue thalesmengue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Warning

Rate limit exceeded

@danielhe4rt has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 50 minutes and 14 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7cbe3904-5f06-4234-b801-fe7148a4890c

📥 Commits

Reviewing files that changed from the base of the PR and between c3fe517 and 76dc656.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • database/migrations/2026_05_01_200459_delete_orphaned_tenant_records.php
  • phpstan.ignore.neon
📝 Walkthrough

Walkthrough

The PR introduces a new he4rt/panel-admin Composer package that implements a Filament admin panel with multi-tenant support. It includes package configuration, an HTTP middleware for applying tenant-based Eloquent scopes, a Dashboard page, and a service provider. An AdminPanelProvider is registered to configure the Filament admin panel with multi-tenant features using the Tenant model. The package is added as a project dependency. Database seeders are refactored to use an admin user and introduce additional tenants, with a migration cleaning up orphaned tenant records. Feature tests cover admin access and tenant scope functionality.

Suggested reviewers

  • gvieira18
  • thalesmengue
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: admin panel module with tenant-aware infrastructure' directly and accurately summarizes the main changes: a new admin panel module with tenant-scoped infrastructure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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
Review rate limit: 0/1 reviews remaining, refill in 50 minutes and 14 seconds.

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
database/seeders/BaseSeeder.php (1)

23-31: ⚡ Quick win

Consider adding an environment guard to prevent accidental production execution.

BaseSeeder contains no guard against running in production. Given the PR already ships a production migration, a misfire of this seeder in production would create duplicate admin users and tenants, or fail with constraint violations.

🔒️ Suggested guard
 public function run(): void
 {
+    if (app()->isProduction()) {
+        $this->command->warn('BaseSeeder is not intended for production. Skipping.');
+        return;
+    }
+
     $admin = User::factory()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@database/seeders/BaseSeeder.php` around lines 23 - 31, Add an environment
guard to BaseSeeder::run() so it refuses to run in production: check the
application environment (e.g., using App::environment('production') or
app()->environment()) at the start of run() and exit/throw/log a clear message
if running in production; keep the rest of the seeding logic (User::factory(),
tenant creation, etc.) unchanged so the seeder only executes in non-production
environments.
app-modules/panel-admin/tests/Feature/AdminPanelAccessTest.php (2)

31-34: ⚡ Quick win

assertRedirect() without a URL makes the test vacuously pass on a login redirect.

Hitting /admin (panel root without a tenant slug) while authenticated could still redirect to /admin/login or a tenant selector, both of which satisfy assertRedirect(). Since the tenant is created in this test, the assertion should target the tenant-scoped URL and confirm the redirect reaches the dashboard rather than the login page.

♻️ Suggested improvement
     $this
         ->actingAs($user)
-        ->get('/admin')
-        ->assertRedirect();
+        ->get('/admin/'.$tenant->slug)
+        ->assertRedirectContains('/admin/'.$tenant->slug);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app-modules/panel-admin/tests/Feature/AdminPanelAccessTest.php` around lines
31 - 34, Replace the loose redirect assertion so the test verifies it redirects
to the tenant-scoped admin dashboard: where the test currently calls
$this->actingAs($user)->get('/admin')->assertRedirect(),
assertRedirect('/admin/'.$tenant->slug) (or the full dashboard path if you have
one, e.g. '/admin/'.$tenant->slug.'/dashboard'), then perform a follow-up GET to
that exact URL and assert a successful response or that the dashboard content is
present (use the existing $tenant variable and the actingAs/get/assert methods).

37-45: ⚡ Quick win

Test name is misleading — this exercises the non-production path that allows everyone, not admin-specific logic.

Looking at app-modules/identity/src/User/Models/User.php (lines 131-137), in a non-production environment canAccessPanel for the 'admin' panel unconditionally returns true regardless of the username. The config(['he4rt.admins' => 'danielhe4rt']) override on line 40 has no effect on the assertion. The test would pass for User::factory()->create(['username' => 'not-an-admin']) equally.

Either rename the test to reflect what is actually being verified ('any user can access admin panel in non-production'), or force production mode here and rely on the isAdmin() check to confirm the true admin path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app-modules/panel-admin/tests/Feature/AdminPanelAccessTest.php` around lines
37 - 45, The test currently verifies the non-production shortcut (any user can
access the admin panel) rather than admin-specific logic; either rename the test
to reflect that behavior (e.g., "any user can access admin panel in
non-production") or make the test exercise the production/admin path by forcing
production environment (set config(['app.env' => 'production']) or equivalent)
and then assert using the User::isAdmin() logic via canAccessPanel on
Filament::getPanel('admin') with an admin and a non-admin user to confirm
correct behavior; locate the test function name and the panel call (the test
closure, Filament::getPanel('admin'), and User::factory()->create(...)) to
modify accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app-modules/panel-admin/src/Http/Middleware/ApplyTenantScopes.php`:
- Around line 23-28: The current loop in ApplyTenantScopes uses
Model::addGlobalScope which mutates protected static $globalScopes and can leak
tenant scopes across requests in long-running servers; update ApplyTenantScopes
to remove those scopes after the response by implementing a terminate() method
that iterates the same config('panel-admin.tenant_scoped_models', []) list and
cleans up each model (either by unsetting the specific 'tenant' scope from the
model's protected static $globalScopes via a small helper/trait or by calling
the model class's clearBootedModels() to force re-boot), ensuring
addGlobalScope(...) in the middleware is paired with a matching cleanup in
terminate() so per-request tenant scoping is not persisted.

In `@app-modules/panel-admin/tests/Feature/AdminPanelAccessTest.php`:
- Around line 47-57: The test mutates global app environment by calling
app()->detectEnvironment(fn () => 'production'), which can pollute other tests;
change the test to avoid permanent mutation by either mocking the environment
check (e.g., stub Application::isProduction() or the isProduction helper used by
canAccessPanel) or add an afterEach teardown to restore the environment (use
afterEach(fn() => app()->detectEnvironment(fn() => 'testing'))); ensure you keep
the Filament::getPanel('admin') and $user->canAccessPanel($panel) assertions
intact while preventing persistent changes to app()->detectEnvironment.

In `@app-modules/panel-admin/tests/Feature/ApplyTenantScopesTest.php`:
- Around line 13-33: The global scope applied by ApplyTenantScopes persists
across tests; add an afterEach in ApplyTenantScopesTest to clear booted model
state by calling Illuminate\Database\Eloquent\Model::clearBootedModels() (or
EventModel::clearBootedModels()) so the global scope added to EventModel during
the test is removed between tests.

In `@app/Providers/Filament/AdminPanelProvider.php`:
- Around line 37-53: The ApplyTenantScopes middleware is not registered so
tenant global scopes never run; add
He4rt\PanelAdmin\Http\Middleware\ApplyTenantScopes to the Filament tenant
middleware list so it runs after Filament resolves the tenant. Concretely, in
AdminPanelProvider (and likewise in PanelAdminServiceProvider if it registers
Filament middleware) add ApplyTenantScopes::class into the
->tenantMiddleware([...]) array (not ->middleware()) and ensure it is placed
after Filament\Tenant\IdentifyTenant (or the IdentifyTenant entry) so
ApplyTenantScopes can safely call Filament::getTenant().

In `@database/migrations/2026_05_01_200459_delete_orphaned_tenant_records.php`:
- Around line 8-49: The migration currently defines up() but leaves down() empty
(inherited no-op); add an explicit public function down(): void in the anonymous
class and make it throw an exception (e.g., new \RuntimeException or \Exception)
with a clear message like "Irreversible migration: deletes tenant records" to
document that rollback is unsupported; update the class that contains up() to
include this down() override so migrate:rollback surfaces the irreversibility
instead of silently doing nothing.

In `@database/seeders/BaseSeeder.php`:
- Around line 25-31: The seeder currently creates an admin with hardcoded PII
and a weak password; update the User::factory() call in BaseSeeder (the block
that assigns $admin and calls Hash::make('admin')) to use role-based placeholder
data (e.g., username 'admin', name 'Admin User', email 'admin@example.com')
instead of real developer details, and replace the weak literal password with a
secure value sourced from an environment variable or generated at runtime (e.g.,
env('SEED_ADMIN_PASSWORD') or a cryptographic random string) passed through
Hash::make; ensure the chosen approach either logs or outputs the generated
password securely for bootstrap use and document the env var so production runs
don’t create a predictable credential.

---

Nitpick comments:
In `@app-modules/panel-admin/tests/Feature/AdminPanelAccessTest.php`:
- Around line 31-34: Replace the loose redirect assertion so the test verifies
it redirects to the tenant-scoped admin dashboard: where the test currently
calls $this->actingAs($user)->get('/admin')->assertRedirect(),
assertRedirect('/admin/'.$tenant->slug) (or the full dashboard path if you have
one, e.g. '/admin/'.$tenant->slug.'/dashboard'), then perform a follow-up GET to
that exact URL and assert a successful response or that the dashboard content is
present (use the existing $tenant variable and the actingAs/get/assert methods).
- Around line 37-45: The test currently verifies the non-production shortcut
(any user can access the admin panel) rather than admin-specific logic; either
rename the test to reflect that behavior (e.g., "any user can access admin panel
in non-production") or make the test exercise the production/admin path by
forcing production environment (set config(['app.env' => 'production']) or
equivalent) and then assert using the User::isAdmin() logic via canAccessPanel
on Filament::getPanel('admin') with an admin and a non-admin user to confirm
correct behavior; locate the test function name and the panel call (the test
closure, Filament::getPanel('admin'), and User::factory()->create(...)) to
modify accordingly.

In `@database/seeders/BaseSeeder.php`:
- Around line 23-31: Add an environment guard to BaseSeeder::run() so it refuses
to run in production: check the application environment (e.g., using
App::environment('production') or app()->environment()) at the start of run()
and exit/throw/log a clear message if running in production; keep the rest of
the seeding logic (User::factory(), tenant creation, etc.) unchanged so the
seeder only executes in non-production environments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3b2765f6-74bd-4c6d-bedc-09e7209ee58a

📥 Commits

Reviewing files that changed from the base of the PR and between 8fc1159 and c3fe517.

📒 Files selected for processing (19)
  • app-modules/panel-admin/composer.json
  • app-modules/panel-admin/config/panel-admin.php
  • app-modules/panel-admin/database/migrations/.gitkeep
  • app-modules/panel-admin/phpstan.ignore.neon
  • app-modules/panel-admin/phpstan.neon
  • app-modules/panel-admin/resources/views/.gitkeep
  • app-modules/panel-admin/src/Http/Middleware/ApplyTenantScopes.php
  • app-modules/panel-admin/src/Pages/Dashboard.php
  • app-modules/panel-admin/src/PanelAdminServiceProvider.php
  • app-modules/panel-admin/tests/Feature/.gitkeep
  • app-modules/panel-admin/tests/Feature/AdminPanelAccessTest.php
  • app-modules/panel-admin/tests/Feature/ApplyTenantScopesTest.php
  • app/Providers/Filament/AdminPanelProvider.php
  • bootstrap/providers.php
  • composer.json
  • database/migrations/2026_05_01_200459_delete_orphaned_tenant_records.php
  • database/seeders/BaseSeeder.php
  • database/seeders/DatabaseSeeder.php
  • database/seeders/ThreeDotsSeeder.php
💤 Files with no reviewable changes (2)
  • database/seeders/DatabaseSeeder.php
  • database/seeders/ThreeDotsSeeder.php

Comment thread app-modules/panel-admin/src/Http/Middleware/ApplyTenantScopes.php
Comment thread app-modules/panel-admin/tests/Feature/AdminPanelAccessTest.php
Comment thread app-modules/panel-admin/tests/Feature/ApplyTenantScopesTest.php
Comment thread app/Providers/Filament/AdminPanelProvider.php
Comment thread database/seeders/BaseSeeder.php
@danielhe4rt danielhe4rt merged commit c05dfb8 into 4.x May 1, 2026
6 checks passed
@danielhe4rt danielhe4rt deleted the feat/base-admin-panel branch May 1, 2026 23:17
gvieira18 added a commit that referenced this pull request May 2, 2026
## Summary

Sincroniza o feature **Discord ETL** + **upgrade Laravel 12 → 13** da
branch `4.1` para `4.x`, descartando o tooling AI/MCP/Boost individual
que vieram bundled na branch original.

Como 4.x usa squash merges, não havia histórico granular dos 4 commits
originais — solução foi rebuilder via checkout seletivo + ajustes
manuais.

### Categorização dos 4 commits da 4.1

| Commit | Status | Files | Lines | Author |
|---|---|---|---|---|
| `4b4a136` Laravel 12→13 upgrade | KEEP (parcial) | 34 | +768 | Daniel
Reis |
| `84e26cf` Laravel Boost / AI tooling | MIXED — só pieces de Discord
profile | 44 | +5724 | kaster |
| `1449e80` wip console commands | KEEP | 6 | +1808 | Daniel Reis |
| `b0e0f00` Discord ETL feature | KEEP | 55 | +5014 | Daniel Reis |

## What's in

13 commits, +8433/−882 linhas em 94 arquivos:

| Commit | O que |
|---|---|
| `fcff50a` Discord ETL core | 9 migrations,
MembershipEvent/MessageAttachment/MessageEmbed/MessageMention/MessageThread/ModerationEvent/Reaction
models, HasReactions trait, SourceBot enum, 4 Actions, 4 DTOs,
DiscordMessageAdapter, ImportDiscordMessagesCommand, 116 ETL tests |
| `11e5a49` Discord profile ETL | ImportDiscordProfileAction + Command,
ConnectedAccountDTO, DiscordProfileDTO, IdentityProvider expandido com
24 cases (Spotify/Steam/Xbox/etc), ImportDiscordProfileTest |
| `3c31f3e` wip console commands | 6 comandos exploratórios:
discord:fetch-{members,profile,profiles}, discord:import-members,
discord:analyze-profiles, discord:community-report |
| `27d2e6c` Color::Dark → Color::Zinc | Bug do código 4.1 — Filament 5
não tem Color::Dark; crash em runtime quando EpicGames era renderizado |
| `4c5dbc4` ETL upsert + reply resolution | Bug do código 4.1 — Actions
usavam `create()` em vez de `updateOrCreate`, quebrando no unique index
`(tenant_id, provider_message_id)` na 2ª chamada. resolveReplyTargetId
não fazia fallback pra DB quando cache era miss |
| `1471958` PHPStan level 6 cleanup | Resolve 34 erros que vieram com o
transplante (return type narrowing, undefined property docblocks,
match.alwaysTrue, mixed!==null narrows, table-row casts em wip commands)
|
| `f23ffca` Laravel 12→13 upgrade + .gitignore align | composer bumps
(framework 12→13.7, tinker 2→3, backup 9→10), config/cache.php
serializable_classes, rector LARAVEL_130 set + skip 3 attribute
conversions (Fillable/Table/Appends), .gitignore alinhado com sycorax
(AI Agents section completa) |
| `95d75b6` Mantém laracord/bot-discord | Reverte remoção indevida —
fork `danielhe4rt/laracord-framework` + `tinker-zero` já suportam L13.
PR #195 body original mentia ao dizer que removeu |
| `762740a` Cleanup rector | Remove regras LARAVEL_130 redundantes do
`withSets`, drop `LARAVEL_FACTORIES` e outros sets que não rodavam |
| `9eff469` Style — object instantiation | Simplifica `(new
X)->method()` → `new X->method()` com PHP 8.4 syntax |
| `1141f2f` Merge `origin/4.x` | Traz PR #206 (admin panel module com
tenant-aware infrastructure) |
| `fe4f422` Untrack `.ai/mcp/mcp.json` | Config MCP do Laravel Boost com
path absoluto hardcoded — agora ignored pelo .gitignore |
| `a5b227f` Guidelines do sycorax | Adiciona
`.ai/guidelines/{filament,knowledge-base}.blade.php` (exception
`!.ai/guidelines/` no .gitignore) |
| `9a50d5a` Format .gitignore | Header da seção AI Agents corrigido |

## What's out

- **Refactor de 25 models** pra atributos PHP
`#[Fillable]`/`#[Table]`/`#[Appends]` — revertido pra `protected
$fillable` por consistência com 4.x (decisão do user). Rules
`FillablePropertyToFillableAttributeRector`,
`TablePropertyToTableAttributeRector`,
`AppendsPropertyToAppendsAttributeRector` skipped no rector
- **CSRF middleware rename** (`VerifyCsrfToken` →
`PreventRequestForgery`) nos PanelProviders — irrelevante pois 4.x
deletou todos esses providers via PR #203/#204 (admin virou módulo,
guest virou portal Livewire)
- **`.agents/skills/**`** (25 markdown skills do Laravel Boost)
- **`.mcp.json`, `boost.json`, `opencode.json`, `AGENTS.md`,
`CLAUDE.md`** (AI/MCP tooling individual)
- **Http Controllers/Requests removidos pelo PR #203**
(MessagesController, CreateMessageRequest, CreateVoiceMessageRequest) —
não re-adicionados
- **`tests/Feature/NewMessageTest.php`,
`tests/Feature/NewVoiceMessageTest.php`** — dependiam dos Http acima

## Sumário verificação cross-branch

Comparação byte-a-byte entre `sync/from-4.1-discord-etl`, `origin/4.x` e
`origin/4.1`:

| Categoria | Count | Status |
|---|---|---|
| Arquivos da 4.1 trazidos **idênticos** ao sync | **53** | ✅ bulk
checkout limpo (50 ETL + .gitignore + Makefile + BaseSeeder) |
| Arquivos da 4.1 com **diff intencional** no sync | **12** | ✅ 9 models
reverteram L13 attrs + 3 fixes nossos (Color, upsert profile,
upsert/reply message) |
| Arquivos da 4.1 **dropados** | **39** (era 69 — incluiu 25 model attrs
+ 5 PanelProviders + composer/cache/rector/L13 que agora trouxemos) | ✅
tudo no plano original |
| Arquivos no sync **fora da 4.1** | **2** | ✅ ActivityServiceProvider +
IntegrationDiscordServiceProvider em path 4.x (`src/`) |

## Co-authors

| Pessoa | Origem | Email usado |
|---|---|---|
| Daniel Reis | autor PR #195 (L13), #197 (ETL), commit wip |
`danielhe4rt@gmail.com` |
| kaster | autor commit Boost (parts de Discord profile vieram junto) |
`diogokaster@gmail.com` |
| thalesmengue | approved PR #197 |
`102062680+thalesmengue@users.noreply.github.com` |
| 1pride | approved PR #197 | `43507992+1pride@users.noreply.github.com`
|

## Test plan

- [x] `vendor/bin/pint --test --format agent` — pass
- [x] `vendor/bin/phpstan analyse --memory-limit=2G` — **0 errors**
(level 6)
- [x] `vendor/bin/rector --dry-run` — **0 changes**
- [x] `php artisan migrate` — todas 9 migrations aplicaram limpo
- [x] `php artisan test --compact` — **182 tests, 179 passed, 3 skipped,
0 failed** (608 assertions)
  - 70 ImportDiscordMessage tests ✅
  - 13 ImportDiscordProfile tests ✅
  - 46 DiscordMessageAdapter tests ✅
- [x] Laravel framework 13.7.0 instalado e funcional
- [ ] Smoke test `php artisan discord:import-messages` com dump real
- [ ] Smoke test `php artisan discord:import-profiles` com chunks JSON
- [ ] Smoke test backup com `spatie/laravel-backup` v10

## Notas técnicas

### CodeRabbit false positive — `SimpleStrategy.php`

CodeRabbit alertou que `app/Tasks/Cleanup/Strategies/SimpleStrategy.php`
usa classes removidas em `spatie/laravel-backup` v10. **False
positive:**

- `Spatie\Backup\BackupDestination\Backup` ✅ existe em v10.2.1
- `Spatie\Backup\BackupDestination\BackupCollection` ✅ existe em v10.2.1
- `CleanupStrategy::deleteOldBackups(BackupCollection)` ✅ assinatura
inalterada
- Sycorax (template) usa código idêntico byte-a-byte com backup v10

CodeRabbit confundiu mudança de **events** (que viraram primitive data
em v10) com a API de cleanup strategy (sem mudança).

### Estratégia técnica

Cherry-pick não funcionaria por causa do conflito com refactor
`#[Fillable]`/`#[Table]` em models que existem em ambas as branches.
Solução foi `git checkout origin/4.1 -- <paths>` por chunks, depois 4
merges manuais:

- `Message.php` — recriado com novos campos ETL mantendo `protected
$fillable`
- `Voice.php` — idem + comentário sobre coexistência de duas
vocabularies de `state`
- `ActivityServiceProvider.php` — adicionado morphMap mantendo path
`src/` (4.x não moveu pra `src/Providers/`)
- `IntegrationDiscordServiceProvider.php` — registrado novos commands no
path `src/` de 4.x

---------

Co-authored-by: Daniel Reis <danielhe4rt@gmail.com>
Co-authored-by: thalesmengue <102062680+thalesmengue@users.noreply.github.com>
Co-authored-by: 1pride <43507992+1pride@users.noreply.github.com>
Co-authored-by: kaster <diogokaster@gmail.com>
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.

4 participants