# Sprint Assumptions Log

This file records assumptions made by agents during autonomous sprint execution.
Each entry is dated and tied to a specific task. Review at sprint-close.

---

## S26-009 — Cancel Invoice Flow (2026-05-09)

### Assumption #1: EInvoiceStatus.canCancel() allows only `validated` state
USER_STORIES.md EINV-TU-03 says "within 72-hour window" but does not specify which statuses are eligible.
**Decision:** Only `validated` invoices can be cancelled (matches MyInvois API behaviour — you can only cancel a LHDN-validated invoice, not a submitted-but-pending one).

### Assumption #2: 72h window measured from `einv_validated_at`, fallback to `einv_submitted_at`
Some invoices validated before the `einv_validated_at` column was added may have `null` in that column.
**Decision:** Fall back to `einv_submitted_at` as the window start timestamp.

### Assumption #3: Journal reversal deferred when accounting period is closed
Acceptance criteria says "reverses original journal entries". The `Reverse` action requires the accounting period to be Open or Reopened. If the period is already closed at cancellation time, the journal reversal is skipped and a warning is logged for manual review.
**Decision:** This is an acceptable trade-off. The MyInvois cancellation succeeds regardless; the journal reversal failure is surfaced in application logs at WARN level with invoice_id and journal_entry_id. A manual reversal entry must be created by the tenant admin. This edge case is uncommon (invoices are typically cancelled within hours, not after period close).

### Assumption #4: `CANCEL_INVOICE` permission reuses the `tenant_admin` role grant
Added `einvoice.invoice.cancel` permission alongside `einvoice.invoice.submit` in `Permissions::seed()`.
Both are granted to `tenant_admin` role automatically.

### Assumption #5: `reason` minimum length of 10 characters
USER_STORIES.md says "mandatory reason text" without specifying minimum length.
**Decision:** 10-character minimum to prevent trivially short reasons like "error". Max 300 characters.

---

## S31-009 — TenantCard (2026-05-08)

### Assumption #1: `surplus_ytd_cents` returns 0
Module M (Surplus/Dividend) is not yet built. The field is included in the TenantCard payload as a placeholder with value 0 so the frontend can render a stub.

### Assumption #2: `transactions_ytd_cents` returns 0
Summing transaction amounts would require joining `transaction_lines` and is not yet implemented. Value is 0 as a placeholder.

### Assumption #3: signal_count reports 1 if any `koperasi_attention_queue` entry exists
The full Signals module (Module N) is not complete. `signal_count` is set to 1 if any entry exists for the tenant, 0 otherwise.
