# Verify-completion audit — NAN-453 — 2026-05-21

## Ticket
Linear: https://linear.app/nanostreet/issue/NAN-453/mobile-balance-sheet-consume-new-assets-liabilities-equity-response
Title: Mobile: Balance sheet — consume new {assets, liabilities, equity} response shape (NAN-444 mobile)
Status: In Progress

## Backend PR #545 state
`gh pr view 545 --repo NanoStreetApp/backend` →
- **state: MERGED** — merged 2026-05-18T14:58:21Z
- **title:** [Backend] NAN-444: balance sheet 3-bucket grouping

> ⚠️ The ticket's coordination requirement ("backend blocked on this mobile work landing first") was violated: backend merged 2026-05-18 while mobile is still In Progress as of 2026-05-21. The production API is already returning `{assets, liabilities, equity}` shape.

---

## Acceptance criteria

### AC1 — New `BalanceSheetDto` schema parses the new backend shape
**[DONE]**
Evidence: `src/features/stock-detail/data/dto/financialsDto.ts:90-108`
```
export const balanceSheetDtoSchema = z.object({
  ticker, period, cached, periods,
  assets: z.array(financialRowDtoSchema),
  liabilities: z.array(financialRowDtoSchema),
  equity: z.array(financialRowDtoSchema),
}).refine(...)
```
The schema validates `assets / liabilities / equity` as arrays of `FinancialRowDto`, with a `.refine` that checks all three buckets' row values match `periods.length`. The old `{rows: FinancialRow[]}` schema (`financialStatementDtoSchema`) is kept intact for income statement and cash-flow — no regression there.
Type: `src/features/stock-detail/data/dto/financialsDto.ts:108` — `export type BalanceSheetDto = z.infer<typeof balanceSheetDtoSchema>`.

---

### AC2 — `BalanceSheetSection` renders 3 expandable sections mapping to bucket keys
**[PARTIAL]**

Two distinct views consume the `BalanceSheet` shape; neither implements the full "3 expandable sections" spec:

**`BalanceSheetSection.tsx`** (in-page overview component):
- Evidence: `src/features/stock-detail/presentation/components/financials/BalanceSheetSection.tsx:23-26` — `SUB_TABS` contains only `assets` and `liabilities`; `equity` bucket is not exposed as a tab.
- Evidence: `src/features/stock-detail/presentation/components/financials/BalanceSheetSection.tsx:153` — renders `<FinancialSubTabs>` (a tab-switcher), not 3 expandable sections.
- The `equity` key from `BalanceSheet` is unused in this component.

**`BalanceSheetBreakdownScreen.tsx`** (the "see more" detail screen):
- Evidence: `src/features/stock-detail/presentation/screens/BalanceSheetBreakdownScreen.tsx:47` — `rows={[...statement.assets, ...statement.liabilities, ...statement.equity]}` — all 3 buckets are consumed and passed to `FinancialBreakdownTable` as one flat concatenated array.
- No bucket-labelled expandable sections; rows from all three buckets merge into a single table.

**What's missing:** No component renders the 3 buckets as separate, labelled, independently-expandable sections. The overview is 2-tab (equity absent). The breakdown screen is 1 flat table (equity present but not demarcated). The AC requires section-per-bucket with expand/collapse.

---

### AC3 — Sections expand/collapse independently
**[NOT DONE]**
Evidence: `src/features/stock-detail/presentation/components/financials/BalanceSheetSection.tsx:145` — `const [activeTab, setActiveTab] = useState<'assets' | 'liabilities'>('assets')` — shared tab state, not per-bucket expand/collapse booleans.
Evidence: `src/features/stock-detail/presentation/screens/BalanceSheetBreakdownScreen.tsx:47` — single flat `FinancialBreakdownTable` with no per-section toggle.
Notes: `ExpandableSection` is used in `BalanceSheetSection.tsx:173` but only wraps the waterfall snapshot card, not the 3 data buckets. No independent per-bucket collapsed/expanded state exists anywhere.

---

### AC4 — `FinancialsRepositoryImpl.getBalanceSheet` returns new typed bucket shape
**[DONE]**
Evidence: `src/features/stock-detail/data/repositories/FinancialsRepositoryImpl.ts:27-31`
```ts
public async getBalanceSheet(ticker, period, asOf): Promise<BalanceSheet> {
  const raw = await this.api.fetchBalanceSheet(ticker, period, asOf);
  const dto = balanceSheetDtoSchema.parse(raw);
  return mapBalanceSheetDtoToDomain(dto);
}
```
Return type is `BalanceSheet` (from `src/features/stock-detail/business/models/FinancialStatement.ts`):
```ts
export type BalanceSheet = {
  readonly ticker: string; readonly period: string; readonly periods: readonly string[];
  readonly assets: readonly FinancialRow[];
  readonly liabilities: readonly FinancialRow[];
  readonly equity: readonly FinancialRow[];
};
```
Mapper: `src/features/stock-detail/data/mappers/financialsMapper.ts:54-63` — `mapBalanceSheetDtoToDomain` maps all 3 buckets through `mapRowDtoToDomain` (recursive child mapping preserved).

---

### AC5 — Tests updated for new shape
**[PARTIAL]**

**Repository test — DONE:**
Evidence: `src/features/stock-detail/data/repositories/FinancialsRepositoryImpl.test.ts` — `describe('FinancialsRepositoryImpl — balance sheet')` block with mock response using `{assets, liabilities, equity}` shape. Asserts `.assets[0].label`, `.liabilities[0].label`, `.equity[0].label`.

**Mapper test — NOT DONE:**
Evidence: `src/features/stock-detail/data/mappers/financialsMapper.test.ts` — file covers `mapOverviewDtoToDomain` and `mapStatementDtoToDomain` only. No test for `mapBalanceSheetDtoToDomain`. The function maps all 3 buckets including recursive children and is untested in isolation.

**DTO test — NOT DONE:**
No `financialsDto.test.ts` exists. The `balanceSheetDtoSchema` refine clause (cross-bucket periods-length check) is not covered by any test.

---

### AC6 — Coordinated merge order with backend #545
**[NOT DONE]**
Backend PR #545 merged 2026-05-18; mobile NAN-453 is still In Progress as of 2026-05-21. The stated lock order (mobile first, then backend) was inverted. The backend API is live with `{assets, liabilities, equity}` shape; the mobile DTO (`balanceSheetDtoSchema`) is already adapted on `main`, so the data layer will not crash, but the UI layers (AC2/AC3) are incomplete — users on staging will see the partially-updated UI against the live new API shape.

---

### AC7 — Smoke test on iOS dev-client against staging
**[SKIPPED — cannot run device from agent]**

---

## Overall: NEEDS WORK

| AC | Status |
|---|---|
| AC1 — BalanceSheetDto schema | DONE |
| AC2 — 3 expandable sections in BalanceSheetSection | PARTIAL |
| AC3 — Independent expand/collapse | NOT DONE |
| AC4 — Repository returns typed bucket shape | DONE |
| AC5 — Tests updated | PARTIAL |
| AC6 — Merge order coordination | NOT DONE (backend merged first) |
| AC7 — iOS smoke test | SKIPPED |

**Minimum before close:** Add equity tab to `BalanceSheetSection.tsx`, convert tab-switcher to 3 independent expandable sections (using existing `ExpandableSection`), add `mapBalanceSheetDtoToDomain` test to `financialsMapper.test.ts`.

## Linear access
MCP failed on first call (deprecated-transport signal); succeeded on retry.
