ledgerrz/GEMINI.md
Daan Meijer 89a48fae16
Some checks failed
linter / quality (push) Failing after 1m5s
tests / ci (8.3) (push) Failing after 49s
tests / ci (8.4) (push) Failing after 1m5s
tests / ci (8.5) (push) Failing after 1m5s
work in progress: predefinedmutations
2026-06-26 10:22:55 +02:00

4.8 KiB

Ledgerrz Project Instructions & Conventions

Welcome to the Ledgerrz codebase! This file defines the persistent guidelines, architectural rules, and context directories loaded in every Gemini session.


1. Context Persistence Mandate

  • Business Logic & Goals (IDEA.md): You MUST read and follow the application concept, features, and user workflow goals outlined in IDEA.md at the start of any new session.
  • Design & Architecture Decisions (DECISIONS.md): You MUST strictly adhere to the established style architecture (BEM methodology with scoped styles and @apply), package deduplication rules, and controller structures documented in DECISIONS.md. Update this file with any new major design decisions made during your session.

2. Key Technology Stack & Conventions

  • PHP/Laravel: PHP 8.4 & Laravel 13. Adhere to typed parameters and return values. Ensure controllers extend properly and use required authorization traits (e.g., AuthorizesRequests).
  • Frontend Styling (BEM): Replaced direct Tailwind inline utility-class markup with BEM (Block, Element, Modifier). All custom component styles must live inside <style scoped> blocks with a relative @reference "../../css/app.css" directive to pull variables without duplications.
  • Real-time Broadcasting: Powered by @laravel/echo-vue with fallback configurations and Vite deduplication rules configured in vite.config.ts.
  • Testing & Isolation: Powered by Pest PHP (v4). Every backend controller, event, or model change must be validated by running tests. To prevent local .env variables from polluting the CLI test execution (causing CSRF/session 419 errors), always run tests in an isolated environment using:
    env -i PATH="$PATH" php artisan test
    
  • Standardized Authorization (can prop): Never write manual role checks (such as pivot.role === 'owner') or hardcoded boolean flags (such as isOwner) inside Vue pages or components. Instead, always leverage Laravel policies on the backend and pass permissions reactively to the frontend as structured can objects (e.g., can: { update: boolean, close: boolean }).
  • Vue-Defined Breadcrumbs Layout: All page-specific breadcrumbs should be declared locally inside the page's .vue file rather than returned from controllers. For dynamic, prop-dependent paths, always use the Inertia v3 layout callback function inside defineOptions:
    defineOptions({
        layout: (props: any) => ({
            breadcrumbs: [
                { title: 'Dynamics', href: route('dynamics.index') },
                { title: props.dynamic.name, href: route('dynamics.show', props.dynamic.id) }
            ]
        })
    });
    

3. Advanced Browser Testing & Architectural Guidelines (Laravel Dusk)

  • Dusk Database Locks on SQLite: Never use the DatabaseTransactions trait in Laravel Dusk tests if the project is backed by a SQLite database file. Since Dusk runs in concurrent, separate processes (the CLI runner and the web server), an active uncommitted database transaction in the CLI will hold a SQLite write lock. This causes the web server to fail with SQLSTATE[HY000]: General error: 5 database is locked. Instead, dynamically create and clean up models during test execution or use sequential DB seeds.
  • Inertia/Vue SPA Testing: Browser tests targeting asynchronously rendered Single Page Applications must always use explicit waitForText() or waitFor() selectors before executing assertions. Direct assertions like assertSee() are immediate and will fail if the JavaScript has not finished compiling and mounting the DOM.
  • Synchronous Queue for WebSockets: Running real-time WebSocket / Echo Dusk tests locally requires setting QUEUE_CONNECTION=sync and BROADCAST_CONNECTION=reverb in your .env file. This ensures broadcast jobs execute immediately on the web server rather than buffering in the database (which fails without a running background queue worker).
  • Automatic ID-to-UUID Serialization: All models carrying a uuid column (such as User, Dynamic, Ledger, Mutation, and PredefinedMutation) must use the App\Concerns\SerializesIdToUuid trait. This guarantees that any raw model or relationship array/JSON conversion automatically replaces the integer id with the secure uuid under the 'id' key.
  • UUID Controller Inputs: When the frontend submits relationship parameters (such as predefined_mutation_id in requests), it only knows and passes the secure UUID. The receiving backend controller must explicitly resolve this UUID to its internal database integer ID before inserting the record (e.g. PredefinedMutation::where('uuid', $uuid)->value('id')), maintaining column type safety and foreign key constraints without exposing integer IDs to the client.