<!--
  Parsec Sdn. Bhd. · AMIR
  Generated by the Parsec Sdn. Bhd. AI Development Framework v2
  © 2026 Parsec Sdn. Bhd. All rights reserved.
  Internal use only. Unauthorised reproduction or use outside of
  Parsec Sdn. Bhd.-authorised projects is prohibited.
-->

# AMIR — Developer Guide
> How we build, how we work, and how to get started.

**Stack:** Laravel 12 + PHP 8.3 + PostgreSQL 16 + Redis + Inertia/React 18 + Tailwind 3
**Last updated:** 2026-05-06
**Framework:** Parsec Sdn. Bhd. AI Development Framework v2
**Development Level:** 3 (Manual) — promoting to L4/L5 after Sprint 1 retrospective

---

## 1. What We're Building

AMIR is an AI-native SME accounting SaaS for Malaysia. The product handles double-entry bookkeeping, GST/SST tax management, and Malaysia-specific compliance (LHDN MyInvois e-invoicing, PDPA 2010 amended 2024, BNM-MPS Shariah rulings). It ships with a **Koperasi pack** for cooperative societies — member management, cooperative loans, and Ar-Rahnu (Shariah-compliant pawn) — to win the SKM tender (S005/2026, deadline 21 May 2026).

The core insight: SMEs in Malaysia today choose between expensive enterprise tools (SAP, Microsoft Dynamics) and unsuitable foreign SaaS (Xero, QuickBooks — neither speaks LHDN, MyInvois, or Bahasa Malaysia natively). AMIR is built for the local accounting reality from Day 1: tax codes, e-invoicing, PDPA, Koperasi-specific accounting, and natural language that mixes English and BM.

---

## 2. Tech Stack

| Layer | Technology | Version |
|-------|------------|---------|
| Backend | Laravel | 12.x (PHP 8.3) |
| Frontend | Inertia + React | React 18 |
| Styling | Tailwind CSS | 3.x |
| Database | PostgreSQL | 16 |
| Cache / Queue | Redis + Laravel Horizon | 7.x / Horizon 5 |
| Auth | Sanctum (stateful SPA + token) + Fortify | bundled |
| Roles & Permissions | Spatie Permission | v6 |
| Audit Log | spatie/laravel-activitylog | v4 |
| Testing | Pest + Playwright (E2E) | Pest 3, Playwright 1 |
| Static Analysis | PHPStan + Larastan | latest |
| Code Style | Laravel Pint (PER preset) | latest |
| Error Tracking | Sentry | SDK latest |
| Hosting | Laravel Forge → AWS (Singapore region for staging/prod, future Malaysia region) | — |
| CI/CD | GitHub Actions | — |
| LLM | Anthropic API (Claude Sonnet for production AI features, Haiku for routing) | API |

---

## 3. Local Environment Setup

### Prerequisites

- **PHP 8.3+** with extensions: `pdo_pgsql`, `redis`, `mbstring`, `intl`, `bcmath`, `gd`, `sodium`, `openssl`
- **Composer 2.x**
- **Node.js 22+** + **npm 10+**
- **PostgreSQL 16** (local or Docker)
- **Redis 7** (local or Docker)
- **Git** with worktree support (any modern version)
- **Claude Code** (`npm install -g @anthropic-ai/claude-code`)

### First-Time Setup

```bash
# 1. Clone and enter the repo
git clone git@github.com:parsec-my/amir.git
cd amir
git checkout dev

# 2. Install dependencies
composer install
npm install

# 3. Environment configuration
cp .env.example .env
php artisan key:generate

# Edit .env — required variables:
# DB_CONNECTION=pgsql
# DB_HOST=127.0.0.1
# DB_PORT=5432
# DB_DATABASE=amir
# DB_USERNAME=amir
# DB_PASSWORD=...
# REDIS_HOST=127.0.0.1
# REDIS_PORT=6379
# QUEUE_CONNECTION=redis
# CACHE_DRIVER=redis
# SESSION_DRIVER=redis
# ANTHROPIC_API_KEY=...                  # for AI features (later)
# AWS_KMS_KEY_ID=...                     # for E3 envelope encryption (later)
# MYINVOIS_CLIENT_ID=... (sandbox)       # LHDN preprod (Sprint S08)
# WHATSAPP_API_TOKEN=...                 # 360dialog or Meta (Sprint S10)

# 4. Database setup
php artisan migrate:fresh --seed

# 5. Build frontend assets
npm run build       # production build
# OR for development with hot reload:
npm run dev         # in a separate terminal

# 6. Verify the setup
php artisan test
# All green? You're ready.

# 7. Run the dev server
php artisan serve   # http://localhost:8000
# OR (preferred — concurrent dev server + queue + vite + log tail):
composer dev        # starts everything via concurrently
```

### Laravel Boost (MCP — Required for AI-Assisted Development)

Laravel Boost gives Claude Code live access to the database schema, registered routes, recent log errors, and package documentation. It runs as a local MCP server when you run `claude`.

```bash
# Already installed by bootstrap.sh — verify it's connected
claude mcp list   # must show "laravel-boost"

# If not showing, run the installer
php artisan boost:install
```

If Boost is not connected, agents will hallucinate schema and routes. Always verify before starting a session.

---

## 4. Project Structure

```
app/
  Domain/                       ← All business logic, grouped by domain
    Tenant/                       ← Multi-tenant scaffolding (D28)
      Actions/                    ← One class per operation (e.g. CreateTenant)
      Events/                     ← Domain events (TenantCreated)
      Models/                     ← Eloquent models — relationships and scopes only
      Enums/                      ← Status enums, type enums (PHP backed enums)
      Resources/                  ← API response transformers
      Requests/                   ← Form request validation classes
    Auth/
    COA/                          ← Chart of Accounts
    Journal/                      ← General ledger (immutable once posted)
    Invoice/                      ← Sales invoices
    Bill/                         ← Purchase bills
    Payment/
    Bank/                         ← Bank reconciliation
    Tax/                          ← SST + MyInvois e-invoicing
    Reporting/                    ← P&L, Balance Sheet, Trial Balance
    Member/                       ← Koperasi members (Sprint S15)
    Loan/                         ← Koperasi loans (Sprint S16)
    ArRahnu/                      ← Shariah-compliant pawn — Tawarruq (Sprint S20)
    Notification/                 ← WhatsApp + Email
    PDPA/                         ← Data subject rights, breach reporting

  Http/
    Controllers/
      Api/V1/                     ← API controllers — every route prefixed /api/v1
      Web/                        ← Inertia controllers (server-rendered SPA)
    Middleware/
      EnsureTenantContext.php
      IdempotencyMiddleware.php
    Resources/                    ← (alternatively in Domain/X/Resources/)

  Casts/
    MoneyCast.php                 ← Integer cents ↔ Money value object
    EncryptedCast.php             ← AWS KMS envelope encryption (E3)

  Models/
    Concerns/
      BelongsToTenant.php         ← Trait — auto-applies TenantScope (per D28)
      HasUuidV7.php               ← Trait — UUID v7 PK generation (per D15-R1)

  Scopes/
    TenantScope.php               ← Global scope — auto-filters by current tenant

config/
  amir.php                        ← AMIR-specific config (feature flags, integrations)

resources/
  js/
    Pages/                        ← Inertia pages (one per route)
    Components/                   ← Reusable React components
    Layouts/                      ← App shell, auth shell
    lib/                          ← formatMoney, formatDate, enums.ts (mirrors PHP)
  views/                          ← Blade for emails + minimal SSR

routes/
  api.php                         ← API routes — all under /api/v1/*
  web.php                         ← Inertia routes
  channels.php                    ← Broadcasting (later)

database/
  migrations/                     ← Additive only (per CLAUDE.md DO NOT)
  factories/                      ← One per model
  seeders/                        ← DemoSeeder for dev, ProdSeeder for prod data

tests/
  Unit/Domain/                    ← Action class unit tests
  Feature/Api/V1/                 ← Integration tests with AssertsQueryPerformance
  Feature/Invariants/             ← Cross-domain invariant tests (tenant isolation, journal balance)
  Browser/                        ← Playwright E2E

.ai/guidelines/                   ← Boost auto-loaded architecture rules
  project-architecture.md           ← Action class pattern, controller rules, event pattern
  data-conventions.md               ← Money, UUID, phone E.164, IC encryption (E3)
  testing-standards.md              ← Pest patterns, AssertsQueryPerformance trait

docs/
  living/                         ← Updated every sprint via /sprint-close
    CODEBASE_MAP.md
    DATA_MODEL.md
    API_REFERENCE.md
    DOMAIN_GUIDE.md
    INTEGRATION_LOG.md
    DEVIATION_LOG.md
    VELOCITY_LOG.md
  ARCHITECTURE.md                 ← Original design (Phase 6)
  USER_STORIES.md                 ← Stories with ACs (Phase 4)
  DECISIONS_LOG.md                ← D1–D33+ technical decisions (Phase 5)
  BUSINESS_FLOWS.md               ← Mermaid flows (Phase 3)
  ACCOUNTING_INVARIANTS.md        ← Bookkeeping integrity rules
```

**`.claude/` folder:**

| Path | What it is |
|------|-----------|
| `.claude/settings.json` | Pre-approved agent permissions + lifecycle hooks config |
| `.claude/commands/` | Slash commands — `/plan`, `/verify`, `/commit-push-pr`, etc. |
| `.claude/rules/` | Stack best-practices + AGENT_GUIDE.md — **auto-read every session start**. Do not move. |
| `.claude/hooks/` | Lifecycle scripts — Pint formatter, commit guard, session logger, tenant-scope check, enum sync check |
| `.claude/skills/` | On-demand modules — referenced by slash commands (writing-tests, writing-migrations, writing-pr-descriptions, reviewing-code, writing-journal-entries, writing-myinvois-integration, writing-ar-rahnu, writing-pdpa-handlers) |
| `.claude/agents/` | *(L5+)* Sub-agent definitions — team-lead, backend-dev, frontend-dev, test-writer, code-reviewer |

---

## 5. Key Documents

| Document | Where | What It Contains |
|----------|-------|-----------------|
| `CLAUDE.md` | Repo root | Architecture rules, patterns, data conventions, DO NOT list. **Read this before writing any code.** |
| `ARCHITECTURE.md` | `docs/` | Full system design, schema, API routes, infrastructure |
| `USER_STORIES.md` | `docs/` | Every story with acceptance criteria. Your task ACs come from here. |
| `DECISIONS_LOG.md` | `docs/` | D1–D33+ technical decisions with rationale. Check before questioning a choice. |
| `BUSINESS_FLOWS.md` | `docs/` | Mermaid diagrams for every user and system flow. Reference for edge cases. |
| `ACCOUNTING_INVARIANTS.md` | `docs/` | Bookkeeping integrity rules — debit=credit, posting state machine, etc. |
| `.sprint-backlog.json` | Repo root | Current sprint tasks. Your full task brief is here. |
| `DEVELOPER_GUIDE.md` | Repo root | This file. |
| `PREFLIGHT_CHECKLIST.md` | Repo root | Day-1 third-party accounts, domain, infrastructure setup. Run in parallel with development. |
| `.ai/guidelines/project-architecture.md` | `.ai/guidelines/` | Action class pattern, controllers, events. Auto-loaded by Boost. |
| `.ai/guidelines/data-conventions.md` | `.ai/guidelines/` | Money cents, UUID v7, IC encryption. Auto-loaded by Boost. |
| `.ai/guidelines/testing-standards.md` | `.ai/guidelines/` | Pest patterns, query assertion trait. Auto-loaded by Boost. |

**Living docs in `docs/living/`** — read all 7 before planning or troubleshooting. They reflect actual current state, not the original plan. If a planning doc and a living doc conflict, the living doc wins.

---

## 6. How We Work

### The AI-Assisted Model

We build with Claude Code as a coding agent, not a code-suggestion tool:

- **You are the architect and reviewer.** You decide what gets built, review what the agent produces, and keep the patterns accurate.
- **Agents write the code.** They follow `CLAUDE.md` and self-verify before committing.
- **Tasks are precise briefs.** Every task in `.sprint-backlog.json` contains full context, ACs, and explicit pattern requirements. Brief quality determines output quality.

### The Two Phases

**Phase 1 — Learning (First 2 sprints — S00 demo + S01 production)**
Work manually, one task at a time, one agent session at a time. This is not slower — it's how you learn what Claude does well, where it needs guardrails, and build up the `CLAUDE.md` DO NOT list with real mistakes from real work. Do not skip this phase.

**Phase 2 — Automated (Sprint S02+, after `/level-up` to L4)**
Once 3–5 features are shipped manually and `CLAUDE.md` accurately describes real patterns, switch to parallel agent dispatch. Four worktrees run simultaneously, each agent self-verifies before committing, and you review batches.

---

## 7. Install Claude Code

```bash
# Requires Node.js 22+
npm install -g @anthropic-ai/claude-code

# Verify
claude --version
```

---

## 8. Set Up Worktrees

Bootstrap.sh creates four worktrees automatically. If you need to recreate them:

```bash
# Run from inside the main project folder (amir/)
git worktree add -b agent/b ../amir-b dev
git worktree add -b agent/c ../amir-c dev
git worktree add -b agent/d ../amir-d dev
git worktree add -b agent/q ../amir-q dev
```

Shell aliases (added by bootstrap.sh to `~/.zshrc` automatically):

```bash
alias za="cd ~/projects/amir   && claude"
alias zb="cd ~/projects/amir-b && claude"
alias zc="cd ~/projects/amir-c && claude"
alias zd="cd ~/projects/amir-d && claude"
alias zq="cd ~/projects/amir-q && claude"   # analysis only — no commits
```

> **Rule:** Each worktree must be on a different branch. Never run two agents on the same branch or touching the same files.

---

## 8b. Launch Your Workspace

```bash
./tmux-work        # opens 4 agents + preview pane in one command
```

Layout: 2×2 grid of agents on left, shared preview pane on right. From any pane:
```bash
tmux send-keys -t "$TMUX_PREVIEW_PANE" 'q' C-m "preview \"docs/ARCHITECTURE.md\"" C-m
```

> **Note:** `--dangerously-skip-permissions` (used by `tmux-work` for interactive sessions) bypasses `PreToolUse` hooks — separator check, debug-artifact check, Co-Authored-By block. Do not use in `dispatch.sh`. Print mode (`claude -p`) handles permissions differently and does not need the flag.

---

## 9. Daily Rhythm

### Morning (5–10 min)

```bash
./tmux-work        # opens 4 agents + preview pane
/sprint-status     # Haiku → Sonnet (auto). Shows current sprint progress.
```

Check what's pending. Note dependencies. Pick your tasks and dispatch.

### Starting a New Sprint

```bash
/sprint-start [N]  # one command — populates backlog (only if needed) + at L4 reconfigures dispatch.sh
```

For AMIR specifically: the backlog ships with all 605 tasks pre-populated by Phase 11. So `/sprint-start` mainly does the L4 dispatch reconfiguration step (skipped at L3).

### Throughout the Day — Level 3 (current)

Work one task at a time:
```
/plan [task-id]       ← Sonnet — review the plan before approving
/implement            ← Sonnet — executes the approved plan
/verify               ← Sonnet — 13-step self-check (do NOT skip)
/commit-push-pr       ← Sonnet — stages, commits, pushes, opens PR
                      ← review PR on GitHub: diff, CI, ## Assumptions — merge
/task-done [task-id]  ← Haiku — marks done in backlog, returns to Sonnet
```

### Level 4 (after `/level-up` post-Sprint 1)

```bash
./dispatch.sh [batch-name]   # all agents launch on Sonnet via tmux send-keys
/watch                       # status ping every 10 min
# step away — agents work in -p mode, output buffers until done
tail -f logs/agent-*.log     # if you want live visibility

# When all agents in batch finish:
./review.sh [batch-name]
# 1. Read each PR diff on GitHub
# 2. Check CI is green
# 3. Read ## Assumptions — unread assumptions become bugs
# 4. Merge ONE PR at a time — run tests on dev after each merge
# 5. /task-done [task-id] after each merge
# 6. ./dispatch.sh [next-batch-name] — only when dev is clean
```

**Never dispatch the next batch until dev is clean.**

### Level 5 (after second `/level-up`)

```bash
/team-launch [feature-id]   # Team Lead → Backend Dev || Frontend Dev → Test Writer → Code Reviewer
/watch                      # monitor progress
# One PR per feature appears when pipeline completes
/task-done [feature-id]
```

> **Critical:** Sub-agents cannot spawn sub-agents. Only the parent Claude session spawns specialists. The team lead returns a JSON plan and never writes code.

### End of Day (5 min)

```bash
/sprint-status
```

If an agent made a mistake, flag it now:
```bash
/flag-mistake [describe what went wrong and what should have happened]
```

This updates `CLAUDE.md`, `.cursorrules`, and remaining backlog prompts. Do it the same day, not at sprint-end.

### End of Sprint (mandatory)

```bash
za
/sprint-close
```

`/sprint-close` runs the full test suite, checks every performance assertion for regressions across the whole codebase, updates all 7 living docs, and (at L4) reconfigures `dispatch.sh` for the next sprint. **Do not advance the sprint counter until `/sprint-close` passes clean.**

After clean close:
```bash
/sprint-start [N+1]
```

---

## 10. The Standard Task Flow

### Phase 1 — Manual (one task at a time — current at L3)

```
/plan [TASK-ID]
         ↓
   review plan
         ↓
   /implement
         ↓
  /test-and-fix
         ↓
     /verify          ← 13 steps. Do not skip — tests passing ≠ work is correct.
         ↓
/commit-push-pr
         ↓
   merge PR on GitHub
         ↓
   /task-done [TASK-ID]
```

**Why `/verify` matters:** It runs 13 checks — not just whether tests pass. It checks architecture compliance, ACs, security, performance assertions, and that no DO NOT rules were violated.

### Phase 2 — Automated dispatch (L4+, future)

```bash
./dispatch.sh --dry-run     # preview what will be dispatched
./dispatch.sh [batch-name]  # launch agents across worktrees
/watch                      # background monitor — status ping every 10 min
```

Agents run, self-verify, and commit when all checks pass. When ready:

```bash
./review.sh [batch-name]    # per worktree: verification status, test results, diff summary
```

---

## 11. Slash Commands Reference

| Command | Model | When to Use | What It Does |
|---------|-------|-------------|-------------|
| `/sprint-start [N]` | Haiku → Sonnet (auto) | Start of every sprint | Populates backlog from SPRINT_PLAN.md (only if needed for AMIR — already pre-populated). At L4: configures dispatch.sh batch layout. |
| `/plan` | Sonnet | Before writing any code | Maps task → ACs → files → implementation order. Outputs reviewable plan. Does NOT write code. |
| `/implement` | Sonnet | After plan is approved | Executes plan in dependency order. Runs `/simplify` before `/verify`. |
| `/scaffold` | Sonnet | New domain entity | Generates Model + Migration + Action(s) + Request + Resource + Policy + Test + Factory. |
| `/test-and-fix` | Sonnet | Tests are failing | Runs suite, diagnoses failures, fixes code (never weakens tests), loops until green. |
| `/verify` | Sonnet | After implementing, before commit | 13-step self-check. |
| `/commit-push-pr` | Sonnet | Work is verified | Stages, writes Conventional Commits message + Morse separator, pushes, creates PR. |
| `/task-done [id]` | Haiku → Sonnet (auto) | After merging a PR | Marks task done in backlog, switches to dev, pulls latest, returns to branch. |
| `/simplify` | Sonnet | Inside `/implement` | Spawns 3 parallel review agents (code reuse, code quality, efficiency) to remove LLM bloat. |
| `/review-changes` | Sonnet | Optional extra check | Staff engineer review — finds bugs, gaps, architecture violations. Reports only, does not fix. |
| `/flag-mistake` | Sonnet | When Claude makes a mistake | Captures mistake as DO NOT rule in CLAUDE.md and .cursorrules. |
| `/sprint-status` | Haiku → Sonnet (auto) | Morning and end of day | Progress table: done, in progress, blocked. |
| `/watch` | Sonnet | After dispatching agents (L4+) | Background monitor — `/sprint-status` every 10 min. Session-scoped. |
| `/sprint-close` | Sonnet | **End of every sprint** | Full-codebase performance regression gate. Updates living docs. Mandatory. |
| `/level-up` | Sonnet | Exit criteria met | Generates L4 or L5 infrastructure. |
| `/level-down` | Sonnet | When stepping back | Steps down safely — does not delete higher-level infra. |
| `/team-launch [id]` | Sonnet (agents via frontmatter) | L5 — per feature | Team Lead → Backend Dev || Frontend Dev → Test Writer → Code Reviewer pipeline. |

**Model rule:** Sonnet is the default. Haiku runs automatically inside `/sprint-start`, `/sprint-status`, `/task-done`. Opus is not the default — `/model opus` for one turn if Sonnet fails on a problem, then `/model sonnet` to return.

**Per-task sequence:** `/plan` → `/implement` → `/verify` → `/commit-push-pr` → merge PR → `/task-done [id]`
**Sprint start:** `/sprint-start [N]` (one command)
**End-of-sprint:** `/sprint-close` → fix regressions → `/sprint-start [N+1]`

---

## 12. Git Conventions

**Branch naming:**
```
feature/[sprint]-[id]-[short-description]    e.g. feature/s1-001-tenant-create-action
fix/[sprint]-[id]-[short-description]        e.g. fix/s2-015-coa-balance-validation
chore/[short-description]                    e.g. chore/update-pint-config
```

**Conventional Commits format with mandatory Parsec Morse separator:**
```
feat(tenant): add CreateTenant action with TenantCreated event
.--. .- .-. ... . -.-.

[body — what changed, why, any context. Always include the Morse line as line 2.]
```

**Types:** `feat` (new capability), `fix` (bug fix), `refactor` (no behaviour change), `test` (test changes only), `docs` (docs only), `chore` (tooling, deps, config).

**Branch rules:**
- All work goes to `dev` via PR — never commit directly to `dev`, `staging`, `prod`, or `main`
- PRs require passing CI before merge
- One branch per agent session — never share a branch between two running agents
- Merge completed branches before dispatching dependent tasks

**Branch model (4-branch agent-assisted):**
- `main` → merges trigger Forge webhook → **staging** deploy
- `prod` → merges trigger Forge webhook → **production** deploy
- `dev` → all agent work merges here first
- `staging` → human-managed promotion from `main` to `staging`

`pre-commit-guard.sh` blocks: `dd()`, `var_dump()`, `console.log` (committed), `Co-Authored-By:` lines (we work alone), and missing Morse separator. Do not bypass with `--no-verify`.

---

## 13. Code Patterns

> Read `CLAUDE.md` and `.ai/guidelines/project-architecture.md` for the full set of rules. The patterns below are the ones you will use every day.

### Controller pattern — validate → delegate → respond

```php
// app/Http/Controllers/Api/V1/InvoiceController.php
final class InvoiceController extends Controller
{
    public function store(StoreInvoiceRequest $request, CreateInvoice $action): JsonResponse
    {
        $invoice = $action->execute($request->validated());

        return InvoiceResource::make($invoice)
            ->response()
            ->setStatusCode(201);
    }
}
```

Controllers never contain business logic. They validate (via FormRequest), delegate (via Action injection), and respond (via Resource).

### Action class pattern — final class, single execute(), DB::transaction, fires event

```php
// app/Domain/Invoice/Actions/CreateInvoice.php
final class CreateInvoice
{
    public function execute(array $data): Invoice
    {
        return DB::transaction(function () use ($data) {
            $invoice = Invoice::create([
                'tenant_id' => Auth::user()->tenant_id,
                'customer_id' => $data['customer_id'],
                'total_cents' => $this->sumLines($data['lines']),
                'status' => InvoiceStatus::DRAFT,
            ]);

            foreach ($data['lines'] as $line) {
                $invoice->lines()->create($line);
            }

            event(new InvoiceCreated($invoice));

            return $invoice;
        });
    }

    private function sumLines(array $lines): int
    {
        return array_sum(array_column($lines, 'amount_cents'));
    }
}
```

Action classes are `final`, have one public method (`execute()`), wrap multi-step writes in `DB::transaction`, and fire domain events for state changes.

### Test pattern — Pest + AssertsQueryPerformance

```php
// tests/Feature/Api/V1/InvoiceTest.php
uses(AssertsQueryPerformance::class);

it('creates an invoice with lines and fires event', function () {
    Event::fake([InvoiceCreated::class]);
    $tenant = Tenant::factory()->create();
    $user = User::factory()->forTenant($tenant)->create();
    $customer = Customer::factory()->forTenant($tenant)->create();

    $this->assertQueryCountAtMost(8, function () use ($user, $customer) {
        $response = $this->actingAs($user)->postJson('/api/v1/invoices', [
            'customer_id' => $customer->id,
            'lines' => [
                ['description' => 'Service A', 'amount_cents' => 100_00],
                ['description' => 'Service B', 'amount_cents' => 50_00],
            ],
        ]);

        $response->assertCreated()
            ->assertJsonPath('data.total_cents', 150_00)
            ->assertJsonPath('data.status', 'DRAFT');
    });

    Event::assertDispatched(InvoiceCreated::class);
});

it('rejects invoice creation for another tenant', function () {
    // tenant isolation invariant — global scope must filter
});
```

Every integration test asserts query count. Every test that creates models seeds tenant context. Every state-changing test asserts the corresponding event fired.

---

## 14. Data Conventions

> Full list in `CLAUDE.md` and `.ai/guidelines/data-conventions.md`. Non-negotiables:

- **IDs:** UUID v7 (per D15-R1) — sortable, time-prefixed. Never expose internal sequential IDs.
- **Money:** Integer cents — `5000` = RM50.00. Cast via `MoneyCast`. Never `float` or `decimal`.
- **Phone:** E.164 format (per D33) — column name `phone_e164`, type `VARCHAR(20)`. Always with country code, e.g. `+60123456789`.
- **NRIC (IC numbers):** Two-column encryption (per E3) — `ic_no_encrypted` (BYTEA, AWS KMS envelope) + `ic_no_hash` (CHAR(64), HMAC-SHA256 unique). **Never** `ic_number_encrypted` or `ic_number_hash` — those are the rejected names.
- **Tenant:** `tenant_id UUID NOT NULL` on every domain table. Models use `BelongsToTenant` trait. `TenantScope` global scope auto-filters every query (per D28).
- **Timestamps:** `timestampsTz()` — always timezone-aware. Store UTC. Frontend converts to `Asia/Kuala_Lumpur` for display.
- **Status fields:** PHP backed string enums under `app/Domain/[Name]/Enums/`. Frontend mirrors as `ALL-CAPS` string constants in `resources/js/lib/enums.ts`. Sync enforced by `post-edit-enum-sync-check.sh` hook.
- **Migrations:** Additive only. Never `DROP COLUMN`, never `ALTER TABLE ... TYPE`, never rename. Soft-delete obsolete columns by marking nullable + ignored. (Per CLAUDE.md DO NOT list.)

---

## 15. Prompting Patterns That Work

### Give the problem, not the solution

```
"Customers need to be able to dispute an invoice from their account page.
The dispute should pause automated payment reminders and notify the operations team.
Story: STORY-INV-031. Flow: BUSINESS_FLOWS.md#invoice-dispute.
Implement following the domain patterns in CLAUDE.md."
```

### Force verification on complex features

```
"Prove this works by writing tests that:
1. Create a balanced journal entry. Verify both debit and credit lines persist and SUM = 0.
2. Attempt unbalanced entry. Verify rejection with the correct error code.
3. Verify InvoiceCreated event fires. Verify activity log entry exists with correct subject.
Run the suite and show me the output before committing."
```

### Second Claude reviews first Claude

For critical logic (journal posting, e-invoice submission, Tawarruq trail, PDPA handlers):
```
# Worktree A: implements the feature
# Worktree B (read-only `zq`): reviews it

"Review the changes in ../amir as a staff engineer.
Story: STORY-XXX. ACs: [paste].
Find every bug, missing edge case, security issue, and architecture violation.
Do NOT fix — only report. Group by BLOCKER / IMPORTANT / SUGGESTION."
```

### When Claude goes off-track

```
"Stop. Re-read CLAUDE.md, the ARCHITECTURE.md domain section, and this task's ACs.
Re-plan from scratch using the correct patterns.
Explain what went wrong before you start."
```

---

## 16. When Things Go Wrong

| Symptom | Fix |
|---------|-----|
| Claude ignoring architecture rules | `/flag-mistake Claude put business logic in controller` — then: "Re-read CLAUDE.md. Re-plan." |
| Claude weakening failing tests | "The tests are correct. Fix the implementation, not the tests." |
| Business logic in controllers | "Move all logic to an Action class. Controllers only validate, delegate, and respond." |
| Changes piling up without commits | Run `/verify` then `/commit-push-pr`. Small verified commits beat large uncertain ones. |
| Context window filling up | Start a new session. Run `/sprint-status` to reorient. |
| Claude going in circles | "Scrap this approach. Implement the simplest correct solution from scratch." |
| Inconsistent patterns across tasks | Run `/review-changes`. Update CLAUDE.md. Fix it now. |
| Branches diverged too much | Merge completed branches to dev before dispatching more agents. |
| Sprint backlog empty at start | `/sprint-start [N]` — reads SPRINT_PLAN.md and populates. (For AMIR, backlog is pre-populated.) |
| `dispatch.sh` not configured for current sprint | `/sprint-start [N]` — reconfigures dispatch.sh at L4. |
| Hitting usage limits | Confirm `settings.json` has `"model": "sonnet"`. Use `/sprint-start`, `/sprint-status`, `/task-done` — they switch to Haiku for mechanical steps automatically. |
| L4: agents touching the same files | Reorganise the batch — shared-file tasks must be on the same worktree (sequential). Re-run `/sprint-start [N]` to regenerate. |
| L5: team lead missed enum values or locked decisions | `/flag-mistake`. Confirm DECISIONS_LOG entry exists and is locked. |
| Migration conflicts with existing schema | "Use the `database_schema` Boost tool to read the current schema before planning." |
| Agent hallucinated a package API | "Use `search_docs` Boost tool to find the correct API for the version we're running." |
| Unclear test failure | "Use `last_errors` Boost tool to read the full Laravel log before fixing." |
| Duplicate route added | "Use `list_routes` Boost tool to check all registered routes before planning route changes." |
| MyInvois sandbox returns 5xx | Check LHDN preprod status page. Document the outage in `INTEGRATION_LOG.md`. Do NOT retry blindly — preprod has tight rate limits. |
| Tenant scope leak (cross-tenant data visible) | This is a P0. Run the cross-tenant invariant test. `/flag-mistake` immediately. Block the merge. |
| Journal post fails with "imbalanced" | The Action's invariant is working. Check the input — debit + credit lines must sum to 0 cents. |
| Ar-Rahnu Tawarruq trail is incomplete | All 4 legs must post atomically. Check `.claude/skills/writing-ar-rahnu.md` and the Action's `DB::transaction`. |
| PDPA DSAR not delivered within 21 days | Check `pdpa_dsar_requests.due_at` indexing. The scheduled job must scan and alert daily. |

---

## 17. Principles

```
1.  CLAUDE.md is law — if it's not written, agents won't follow it
2.  Plan before implement — every task, every time
3.  Verify before commit — every task, every time
4.  Review before merge — every PR, every time
5.  Read ## Assumptions in every PR — unread assumptions become bugs
6.  Sonnet is the default — Haiku for mechanical, Opus only if Sonnet fails
7.  Model switching is automatic — commands and frontmatter handle it
8.  /sprint-start before any sprint work — one command handles all pre-flight
9.  Performance is asserted in tests, not optimised after launch
10. One branch per agent — no file overlap between concurrent agents
11. /flag-mistake immediately — not at end of sprint
12. Small focused tasks produce better output than large vague ones
13. Second Claude reviewing first Claude catches what you'd miss
14. Merge to dev often — never let branches diverge more than one sprint
15. /sprint-close is mandatory — never advance the sprint counter manually
16. Start fresh sessions per task — don't carry context across unrelated work
17. Tenant isolation is non-negotiable — every query must scope by tenant_id (D28)
18. Money is integer cents always — no exceptions, no shortcuts
19. Audit log every state change — spatie/laravel-activitylog v4 is enabled for a reason
20. Read living docs before planning — they reflect actual current state
```

---

**For new team members:**

Day 1: Read `CLAUDE.md`, this file, and `ARCHITECTURE.md`. Run `bootstrap.sh` if the repo isn't set up yet, otherwise follow Section 3 setup. Run `php artisan test` and confirm green.

Day 2: Read `.claude/rules/AGENT_GUIDE.md`, all 8 files in `.claude/skills/`, and the 7 living docs in `docs/living/`. Pick a `pending` task in `.sprint-backlog.json` and run `/plan [task-id]`.

Day 3+: Follow the standard task flow.

When in doubt: `CLAUDE.md` for rules, `ARCHITECTURE.md` for design, `docs/living/` for current state, `DECISIONS_LOG.md` for history. Living docs win conflicts.
