# Cidadãos Persistentes — Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Each admin-approved user gets a persistent human-like character (needs, jobs, housing, family, chat) in a multi-city world that progresses via an always-on scheduler without requiring anyone online. **Architecture:** Laravel 13 + MariaDB hold all character/world state. `city:tick` (every minute) applies need decay, salaries, building progress and simple offline AI. The existing Three.js viewer becomes a client for the caller's character (throttled position sync) plus action HUD/UI. Admin manages citizen approval, territory expansion and new cities. **Tech Stack:** Laravel 13, PHP 8.3+, MariaDB, Sanctum (existing), Three.js viewer (existing), PHPUnit, Playwright MCP. No new production dependencies. **Spec:** `docs/superpowers/specs/2026-10-18-citizens-persistence-design.md` ## Global Constraints - PHP 8.3+; follow existing conventions (Form Requests, `#[Fillable]`, English PHPDoc, Pint). - All user-visible strings in `lang/pt_BR.json`, `lang/en.json`, `lang/es.json`. - LGPD protections, CAPTCHA, CSRF unchanged; no secrets in repo. - `php artisan test` and `vendor/bin/pint --test` must pass before considering a task done. - Registration must leave users `pending` until admin approval. - World must progress with zero users online (`city:tick` + `schedule:run`). - Character death is not simulated; needs floor at 0 with energy penalties only. ## Review Focus 1. **Unapproved login:** user with `status=pending` must not enter dashboard/city; expect friendly wait screen + logout. 2. **Tick idempotency:** running `city:tick` twice in one minute must not double-decay needs or double-pay salary (guard by `last_tick_at`). 3. **Ownership:** actions (eat/build/sleep/position) must reject another user's character ID (403/422). 4. **Build occupancy:** two characters must not complete houses on the same footprint (unique site check by AABB). 5. **City bounds:** movement and building must clamp/fail outside active city bounds; new city layout must derive from `cities.bounds`, not only store positions. 6. **Family graph:** spouse requires accepted friendship; child requires both spouses; child NPC has `user_id=null` and never logs in. 7. **Migration safety:** existing `is_active` users become `active` and can still log in after the `status` column ships. --- ### Task 1: Schema + seeders (cities, characters, buildings, relationships, inventory, transactions, jobs, messages) **Files:** - Create: `database/migrations/2026_10_18_000100_add_status_to_users_table.php` - Create: `database/migrations/2026_10_18_000200_create_cities_table.php` - Create: `database/migrations/2026_10_18_000300_create_characters_table.php` - Create: `database/migrations/2026_10_18_000400_create_buildings_table.php` - Create: `database/migrations/2026_10_18_000500_create_relationships_table.php` - Create: `database/migrations/2026_10_18_000600_create_inventory_items_table.php` - Create: `database/migrations/2026_10_18_000700_create_transactions_table.php` - Create: `database/migrations/2026_10_18_000800_create_jobs_table.php` - Create: `database/migrations/2026_10_18_000900_create_character_messages_table.php` - Create: `app/Models/City.php`, `Character.php`, `Building.php`, `Relationship.php`, `InventoryItem.php`, `Transaction.php`, `Job.php`, `CharacterMessage.php` - Create: `database/factories/CharacterFactory.php`, `CityFactory.php` - Create: `database/seeders/CityWorldSeeder.php` (City10 + jobs; backfill users status; ensure characters for active users) - Modify: `database/seeders/DatabaseSeeder.php` (register seeder) - Modify: `app/Models/User.php` (status const/cast/relation) **Steps:** - [ ] Write migrations/models/factories/seeder per spec §3 - [ ] `php artisan migrate` in container - [ ] Feature test: seeder creates City10 + character for active user; `users.status` backfilled - [ ] Run `php artisan test --filter=CityWorld` and Pint - [ ] Commit ### Task 2: Registration pending + admin citizens approval **Files:** - Create: `app/Http/Controllers/Web/Admin/CitizenAdminController.php` - Create: `resources/views/admin/citizens/index.blade.php` - Create: `app/Http/Requests/...` if needed - Modify: `app/Http/Controllers/Web/AuthController.php` (register → pending; login branch for pending) - Create: `resources/views/auth/pending.blade.php` - Modify: `routes/web.php` (admin citizens routes; pending view route or inline response) - Modify: `app/Http/Middleware/EnsureUserIsActive.php` if needed - Modify: lang files (pending/approve/ban strings) - Modify: `resources/views/dashboard` or layout nav (Admin → Cidadãos link) **Steps:** - [ ] Failing tests: register creates pending; login as pending redirects to wait screen; admin approve activates + creates character; banned cannot login - [ ] Implement controller/views/routes - [ ] `php artisan test --filter=Citizen` green + Pint - [ ] Commit ### Task 3: World ticker `city:tick` + schedule **Files:** - Create: `app/Console/Commands/CityTick.php` - Modify: `routes/console.php` (everyMinute) - Create: `tests/Feature/CityTickTest.php` **Steps:** - [ ] Failing test: needs decay once per call; second immediate call no-ops; sleep recovers energy; work pays; building progress completes at 100 - [ ] Implement command + offline AI rules (spec §4) - [ ] Tests green + Pint; `php artisan schedule:list` shows entry - [ ] Commit ### Task 4: City data payload + layout from bounds + character position sync **Files:** - Modify: `app/Http/Controllers/Web/CityViewerController.php` (city param, characters/buildings/jobs/bounds; layout from city bounds) - Modify: `app/Http/Controllers/Api/...` if mirrored - Create: `app/Http/Controllers/Web/CityActionController.php` (position + actions stubs for this task: position only) - Modify: `routes/web.php` - Create: `tests/Feature/CityDataTest.php`, `CityPositionTest.php` **Steps:** - [ ] Failing tests: `/city/data` includes own character + bounds; layout covers full bounds; position POST persists and clamps to bounds; cannot set another character - [ ] Implement - [ ] Tests green + Pint - [ ] Commit ### Task 5: Actions API (eat/drink/sleep/wake/work/study/buy/sell/build) **Files:** - Modify: `app/Http/Controllers/Web/CityActionController.php` - Create: Form Requests under `app/Http/Requests/` - Create: `tests/Feature/CityActionsTest.php` - Modify: lang files (action labels/errors) **Steps:** - [ ] Failing tests per action happy path + key rejects (no house, no job, no money, occupied site, insufficient materials) - [ ] Implement actions with DB transactions where money/inventory move - [ ] Tests green + Pint - [ ] Commit ### Task 6: Social (friends, spouse, child, character chat) **Files:** - Modify: `app/Http/Controllers/Web/CitySocialController.php` (create) - Create: `app/Http/Controllers/Web/CharacterChatController.php` or extend `ChatController` - Create: `tests/Feature/CitySocialTest.php` - Modify: lang files **Steps:** - [ ] Failing tests: friend request/accept; marry requires accepted friend + no existing spouse; child creates NPC character with parent links; message POST persists and is readable by recipient - [ ] Implement - [ ] Tests green + Pint - [ ] Commit ### Task 7: Admin territory expand + multi-city **Files:** - Create: `app/Http/Controllers/Web/Admin/CityAdminController.php` - Create: `resources/views/admin/cities/*` (index + create + expand form) - Modify: `routes/web.php` - Create: `tests/Feature/CityExpansionTest.php` - Modify: lang files **Steps:** - [ ] Failing tests: create city; expand bounds +40 up to cap; layout for new city uses its bounds - [ ] Implement - [ ] Tests green + Pint - [ ] Commit ### Task 8: Viewer frontend — own character, HUD needs, action menu, citizens, buildings **Files:** - Modify: `resources/views/city/viewer.blade.php` (HUD + action panel markup) - Modify: `public/js/city/app.js` (own character control, needs HUD poll, action fetches, render characters/buildings from payload, bounds clamp, position throttle) - Modify: lang files **Steps:** - [ ] Manual/Playwright: after Task 2–7, load `/city`, see own avatar, move persists (reload keeps position), action buttons update needs/money, second character visible, build marker - [ ] No console errors; Pint N/A (JS); `node --check app.js` - [ ] Commit ### Task 9: End-to-end Playwright + bugfix pass **Files:** - Screenshots under `.playwright-mcp/` - Fix any bugs found in previous tasks **Steps:** - [ ] Full journey: register → pending screen → admin approves → login → accept terms if needed → `/city` → move → eat/work/sleep → build house → friend with second user → chat → admin expand → new city - [ ] Console clean; PHPUnit full suite green; Pint green - [ ] Final commit