# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## 6.4.2 — 2026-06-11

### Changed

- **Replace `optional_import` with native dynamic imports.** All optional-peer lazy loads
  (`hazo_pdf`, `hazo_audit`, `hazo_notes`, `hazo_chat`, `hazo_ihelp`, `hazo_ui`) now use
  `import('pkg').catch(() => null)` directly instead of the `optional_import` helper from
  `hazo_core`. Behaviour is identical; this eliminates the Turbopack
  `Can't resolve 'hazo_core'` optional-import probe warnings in the test-app and consuming
  apps that use Turbopack.

### Fixed

- **Test-app turbopack config:** added `hazo_debug → empty stub` alias (per workspace
  CLAUDE.md pattern) and removed `hazo_debug` from `transpilePackages` — the stub is
  the correct fix for the ESM-only hazo_debug Turbopack warning.

## 6.4.1 — 2026-06-03

Patch fixes for the 6.4.0 Google Sheets export feature.

### Fixed

- **Google Sheets hyperlinks now render correctly.** The `=HYPERLINK()` `formulaValue`
  approach sent via the Sheets `spreadsheets.create` API silently rendered as plain text.
  Switched to `textFormatRuns` with `link.uri` — the native format Google Sheets uses to
  store hyperlinks — which shows the file name as a blue underlined clickable link.
- **Removed invalid `textFormatRuns` terminator.** The trailing run at
  `startIndex === text.length` is out-of-bounds per the Sheets API
  (`TextFormatRun.startIndex must be less than the length of the string being formatted`).
  The last run in `textFormatRuns` implicitly extends to end-of-string, so no terminator
  is needed.
- **Test-app demo page:** the sample data uses synthetic file_ids that are not in the
  hazo_files DB, so the `/api/files/external-link` route returned `null` for every file.
  The demo now constructs download URLs client-side to show the hyperlink feature without
  requiring real DB entries.

## 6.4.0 — 2026-06-03

Clickable File Name links in Collected Data exports. Additive — no breaking changes.

### Added

- **`export_file_links` + `get_external_links` props on `ThreadForm` / `CollectedDataView`.**
  When both are set, the File Name cell in the Excel (.xlsx) and Google Sheets exports
  becomes a clickable hyperlink that links directly to the file. CSV always drops the link
  (no hyperlink concept in plain-text CSV).
- **Absolute http(s) guard.** `is_safe_http_url()` (exported from
  `hazo_collab_forms/thread-form`) validates every URL before it is embedded. `blob:`,
  `data:`, relative paths, and `javascript:` are silently skipped — the cell falls back to
  plain text.
- **Excel hyperlink** via SheetJS `cell.l.Target` (applied after `aoa_to_sheet`).
- **Google Sheets hyperlink** via `textFormatRuns` with `link.uri` (native Sheets link
  format — more reliable than `=HYPERLINK()` formula injection via the create API).
- **`LinkCell` type and `SheetCell` union** (`string | number | null | LinkCell`) exported
  from `build_export_sheets.ts`. `SheetSpec.rows` is now `SheetCell[][]`. No change to
  column definitions or the number of columns — the feature is a richer cell in the
  existing File Name column.
- **`cell_value()` helper** (exported from `export_sheets_to_file.ts`) extracts the plain
  display text from any `SheetCell` (used by CSV and xlsx AoA flattening).
- **`to_cell_value()` exported** from `export_sheets_to_google.ts` (previously private).
- **Graceful degradation.** If `get_external_links` throws, a toast is shown and the
  export proceeds without links. Files whose IDs are absent from the resolved map also fall
  back silently to plain text.
- **Test-app:** new `POST /api/files/external-link` route that calls `hazo_files`
  `external_file_links` server-side, and the export demo page (`/thread-form-export`)
  now passes `export_file_links` and `get_external_links` to `CollectedDataView`.

### Requires

- `hazo_files` ≥ 3.0.0 for `external_file_links` (already the peer range).

## 6.3.0 — 2026-06-02

Thread-form Collected Data export. Additive — no breaking changes to the public API.

### Added

- **Export menu in the thread-form Collected Data tab.** Set `enable_export` on
  `ThreadForm` to show a header export control that downloads the selected file items'
  extracted data as **Excel (.xlsx)** or **CSV**, or **opens them directly in Google
  Sheets**. One worksheet per document category; columns = `File Name` + the union of
  `classification_result.extracted_fields` keys (ordered/labelled by the matching
  `available_document_types[].expected_fields`). Mirrors the data shape previously built
  in-app by taxclientdocs.
- **New props on `ThreadForm` / `CollectedDataView`:** `enable_export?: boolean`
  (default false), `export_filename?: string` (default `'collected-data'`),
  `google_client_id?: string` (when set, the "Open in Google Sheets" item appears).
- **Reusable export helpers** exported from `hazo_collab_forms/thread-form`:
  `build_export_sheets()` (pure spec builder), `export_sheets_to_file()` (xlsx/csv
  download), `export_sheets_to_google()` (GIS popup → Sheets REST create), plus
  `safe_cell` / `sanitize_sheet_name` and the `SheetSpec` / `ColumnSpec` types.
- **`xlsx` optional peer dependency** (`^0.18.5`). Loaded via dynamic `import('xlsx')`
  for Excel/CSV generation; if absent the export is a graceful no-op (the menu reports
  "Export unavailable"). No backend required.

### Notes

- **Google Sheets is fully client-side** via Google Identity Services: clicking the
  item pops up Google consent (`drive.file` scope), obtains a short-lived access token,
  and creates a populated multi-tab spreadsheet in the signed-in user's Google account —
  no server route, no stored credentials, no changes to `hazo_auth` / `hazo_files`.
  Consumers must supply a Google OAuth **Web client ID** (with their domain in the
  client's *authorized JavaScript origins*) and enable the Sheets API in their Google
  Cloud project.
- Cell values are formula-injection–protected; sheet names are sanitised to ≤31 chars
  and de-duplicated. Numbers stay numeric in the spreadsheet.

## 6.2.0 — 2026-05-29

Wave 2 workspace standardisation. Additive — no breaking changes to public API.

### Added

- **`hazo_core` peer dep** (`^1.0.0`, required). Powers correlation-ID propagation, HazoError class hierarchy, structured logger, and `optional_import`. See workspace `CLAUDE.md` for the full picture.
- **`src/utils/uuid.ts`** — canonical `generateUUID()` helper (wraps `generateRequestId().slice(4)` from hazo_core; replaces ad-hoc `crypto.randomUUID()` patterns).
- **`src/utils/logger.ts`** — `get_logger() / set_logger()` pattern delegating to `createLogger('hazo_collab_forms')` from hazo_core (auto-injects correlationId + env; falls back to console).
- **`src/lib/with_route_context.ts`** — lightweight route-handler wrapper that opens a hazo_core correlation-ID context for each request. Honours inbound `x-request-id` headers; generates a new ID otherwise. All `src/lib/` route factories (`create_autofill_route`, `create_classification_route`, `create_content_gate_route`, `create_data_route`, `create_resolution_route`, `create_validation_route`) now wrap their handlers, so outbound `fetchWithRequestId()` calls and downstream logs all share the request's correlation ID.
- **`src/lib/config/hazo_collab_forms_config.ts`** — Zod-schema'd config loader delegating to `hazo_core.loadConfig`. Coexists with the existing string-API `src/lib/config.ts`; supports `HAZO_COLLAB_FORMS_<SECTION>_<KEY>` env-var overrides per D-003.
- **`design/feature_requests/INDEX.md`** — workspace standard S16 backfill.

### Changed

- **HazoError adoption in server-side throws.** 12 throw sites in `src/lib/` (autofill_handler, classification_handler, content_gate_handler, create_data_route, periodic_coverage_runner, validation_handler) now throw the appropriate `HazoValidationError` / `HazoExternalError` / `HazoUnavailableError` / `HazoConfigError` subclass with namespaced codes (`HAZO_COLLAB_FORMS_*`). React client-component bare `Error` throws that feed `setError(string)` state are intentionally untouched (Wave 2 precedent).
- **`cn()` consolidation.** Local `src/utils/cn.ts` removed. All 127 internal imports now resolve to `cn` from `hazo_ui` (already a required peer dep). `src/utils/index.ts` re-exports `cn` from `hazo_ui` so external consumers of `hazo_collab_forms/utils` see no API change.
- **`optional_import` canonicalisation.** Local `src/utils/optional_import.ts` (Function-constructor variant, predated `hazo_core`) removed. 18 raw `await import('hazo_*')` sites + every existing local-`optional_import` call site now use `optional_import` from `hazo_core`.
- **Outbound `fetch` → `fetchWithRequestId`** in `autofill_handler.ts` and `validation_handler.ts` for upstream file fetches. Correlation IDs now propagate to downstream services.
- **Peer-dep refresh.** `hazo_ui` bumped to `^3.1.0`. `hazo_api ^2.1.0` added as optional peer (forward-compat; full hazo_api adoption deferred to a future major).

### Deferred to a future major (v7)

- **hazo_api full adoption** — `ok()` / `fail()` response envelopes and `defineRoute()` registration would change the public response shape of every route factory and are reserved for a major bump. The additive `withContext` wrapping landed in 6.2.0 covers the correlation-ID propagation half of the Wave 2 directive.
- **Test-app harness retrofit** — existing Next.js test-app with Playwright e2e tests remains the canonical demo. A `hazo_ui/test-harness` sidebar pass is scoped as standalone work for a future minor.

## 6.1.0 — 2026-05-28

### Added

- **Two-tier admin mode for `HazoValidationRuleEditor`**: pass `include_globals=true` together with a `scope_id` to display global rules (`scope_id IS NULL`) read-only alongside this scope's overrides. Clicking "Override default" on a global calls the new `on_clone_to_override` callback so the consumer can POST to its own clone-to-override endpoint and return the new editable override row. Each rule exposes a computed `origin` field (`'global' | 'override' | 'scope_only'`) the list uses to render "Default" / "Customized" badges. Existing single-tier consumers are unaffected.
- **`use_validation_rules` hook**: new `include_globals` and `on_clone_to_override` options; returns `clone_to_override` + `can_clone_to_override` for UI wiring.

### Notes

- Renaming `rule_id` on an override breaks the override↔global linkage (the override becomes a `scope_only` rule on next fetch). A future enhancement may lock `rule_id` for overrides.

## 6.0.8 — 2026-05-28

### Fixed

- **Group prop forwarding**: `disable_border` and `container_class_name` on a group `FieldConfig` are now correctly forwarded to `HazoCollabFormGroup`. Both props existed on the component but were silently dropped in the group dispatch — setting them in a declarative schema had no effect. `FieldConfig` also gains the `disable_border?: boolean` field in its TypeScript interface.

## 6.0.7 — 2026-05-27

### Added

- **`task_number_offset` prop on `ThreadForm`** (`number`, default `0`): shifts every task's Q-pill display number by a fixed amount. Pass `1` when the KeyInfoDrawer occupies Q1 so the task list renders Q2, Q3, Q4 …. The `aria-label` shifts with the visible text (fixing the accessibility regression from a CSS-overlay workaround), and Send Back "From Q{N}" back-reference chips shift by the same offset automatically.

## 6.0.3 — 2026-05-27

### KeyInfoDrawer fixes

- **`chevron_position` prop** (`'left' | 'right'`, default `'right'`): toggle chevron now defaults to the far-right of the button row, matching `TaskCard`. Pass `'left'` to restore the legacy position.
- **`title` type widened to `React.ReactNode`**: consumers can now pass JSX (e.g. a checkbox + pill) as the drawer title without a TypeScript cast.
- **`aria_label` prop**: explicit `aria-label` override for the toggle button. Falls back to `title` (if it is a string) then `'Key information'`.
- **`disable_section_card` prop** (also on `HazoCollabFormView`): when `true`, `EditSection` renders without `border rounded-lg bg-card shadow-sm`, allowing embedding in an already-styled container.
- **`hide_section_header` prop** (also on `HazoCollabFormView`): when `true`, the section title bar inside `EditSection` is not rendered.

## 6.0.1 — 2026-05-26

### Added
- `field_columns` prop on `HazoCollabFormView` (`1 | 2 | 3`, default `1`) — renders fields in a 2- or 3-column CSS grid in `edit` mode. Wide fields (textareas, data tables, file groups) automatically span the full row via an internal `force_full_row` mechanism. Summary, print, and approval views are unaffected.
- `FieldColumnsContext` and `useFieldColumns` hook exported from the package root for consumers that need to read the column count in custom components.
- `hazo_ui` peer dep bumped to `^2.17.0`.

## 6.0.0 — 2026-05-21

### Added
- Module-level promise caches in `HazoCollabFormSet` for the chat-color config keys (9 keys) and `/api/hazo_auth/me`. Multiple form instances on one page now share a single fetch per resource instead of N × 9 + N requests.

### Removed (BREAKING)
- **HazoFbForm component and all related exports** — never reached a working state in real consumers and is being removed. Drop affects:
  - `src/components/hazo_fb_form/` (entire directory: component, views, hooks, shared/, components/)
  - `src/lib/fb_form_handler.ts` + `create_fb_form_route` / `FbFormRouteOptions` exports
  - `src/types/fb_form_instance.ts` (`FbFormInstance`, `FbInstanceStatus`, `FB_TERMINAL_STATUSES`, `is_fb_terminal_status`)
  - `src/types/fb_form_data_v1.ts` (legacy V1 data types)
  - `src/utils/fb_data_*.ts` (6 files: helpers, mutations, adapter, queries, mutations_v2, adapter_v2) and all `v2_*` re-exports from `src/utils/index.ts`
  - `src/components/shared/json_data_panel/` (debug viewer for fb_form data)
  - All HazoFbForm-related test-app pages: `app/fb-form/*`, `app/api/fb-form/*`, `tests/e2e/fb-form-sendback.spec.ts`
- **Type/file moves:**
  - `src/types/fb_form_data.ts` → `src/types/shared_data.ts` (kept — still consumed by `thread_form`, `clarification`, `lib/resolve_variable`, `lib/resolution_handler`)
  - `src/components/hazo_fb_form/shared/format.ts` → `src/utils/format.ts` (helpers: `format_file_size`, `format_currency_value`, `format_instance_label`, `infer_mime_type`, `get_rule_explanation`)
- Public types still exported from package root (re-routed to `shared_data.ts`): `ResolutionCheck`, `ResolutionDetail`, `ExtractedResponseFields`

## 5.10.0 — 2026-05-21

### Added
- `field_actions` prop on `HazoCollabFormView` and `HazoDataForm`: composable array of `FieldAction` descriptors, sorted by `order`, each error-bounded
- `field_audit` prop: hazo_audit integration — mounts `FieldAuditIcon` per field via the `field_actions` system (requires `hazo_audit` optional peer dep)
- `form_id` prop: forwarded into audit intent payloads
- `FieldAction`, `FieldAuditConfig`, `FieldActionPosition`, `FIELD_ACTION_ORDER` exported from package root
- `FieldActionArraySlot` — composable slot renderer replacing the legacy `FieldActionSlot` for `field_actions` consumers
- Intent events emitted via `POST /api/audit/intent` (best-effort) on: `form_submitted`, `field_confirmed`, `autofill_applied`, `field_revalidated`
- `hazo_audit@^2.0.1` added as optional peer dependency

### Deprecated (slated for a future major)
- `field_action_components` prop — use `field_actions` instead
- `enable_field_audit` prop — use `field_audit` with hazo_audit installed instead
- `audit_debounce_ms` prop
- `src/components/field_audit/` in-session audit module — use hazo_audit instead

(Originally marked "removed in 6.0"; 6.0 only removed `HazoFbForm`. These deprecations remain in place; removal will be carried by a later major.)

## 5.9.1

- **FIX**: `DataFormFieldLayout` (used by `HazoDataForm`) now places the
  `field_action_components.row_end` slot directly after the value column,
  before the file column. Previously the file column (or its 26px
  alignment spacer) sat between the value and the action slot, pushing
  per-field action icons (e.g. `FieldAuditIcon`, raise-question buttons)
  away from the input by ~30–50px. Applies to both `inline` and
  `stacked` layouts.

## 5.9.0

- **NEW**: Built-in `FieldAudit` per-field change history.
  - New `enable_field_audit?: boolean` convenience flag on
    `HazoCollabFormView`, `HazoDataForm` (collab variant), and `ThreadForm`
    (forwarded to the Key-Info drawer's embedded HazoCollabFormView).
    When `true`, the form mounts a `FieldAuditProvider`, auto-records each
    `form_data` change as a `manual` entry, and overlays an inline clock
    icon (`row_end`) + history panel (`row_below`) into every scalar field
    row. Consumer-supplied `field_action_components` win on collision.
  - **Per-field debounce (default 750ms).** Controlled inputs fire on
    every keystroke; the watcher coalesces a typing burst into ONE entry
    whose `prev_value` is the value at the start of the burst and
    `new_value` is the value when typing stopped. Reverting to the
    baseline value within the window cancels the pending entry.
    Tune via `audit_debounce_ms?: number` on each form view.
  - Imperative API for non-manual sources: `useFieldAudit().record_change(
    { field_id, source: 'clarification' | 'manual', new_value, prev_value, user })`.
    When `record_change` is called within ~500ms of an auto-recorded
    `manual` entry for the same value, the existing entry is promoted to
    the more specific source instead of duplicated.
  - In-session storage only — no persistence on reload. Pair with a
    backend write inside `on_field_change` if you need durability.
  - Intended for the tax-agent surface; omit the flag on the client
    portal to hide audit history.
  - New exports from the package root:
    `FieldAuditProvider`, `FieldAuditIcon`, `FieldAuditPanel`,
    `FormDataAuditor`, `useFieldAudit`, `useFieldAuditOptional`,
    plus the `AuditEntry`, `AuditSource`, `RecordChangeInput`,
    `FieldAuditContextValue` types.
- **NEW**: `field_action_components?: FieldActionComponents` and
  `enable_field_audit?: boolean` on `ThreadFormProps`. Both forward
  through to the embedded Key-Info `HazoCollabFormView`. Without the
  Key-Info drawer (`key_info_sections` omitted), neither prop has any
  effect.
- Backward-compatible: when neither flag is set, the rendered output is
  byte-identical to 5.8.0.

## 5.8.0

- **NEW**: `field_action_components?: FieldActionComponents` prop on
  `HazoCollabFormView` and `HazoDataForm`. Lets consumers render per-field
  React components (audit icon, RFI button, custom popover, inline audit
  log) next to every scalar field row.
  - **Two slot positions, independently or together:**
    - `row_end` — inline at the end of the row, after the value column,
      file column, and `UnifiedFieldControls`.
    - `row_below` — full-width block immediately below the row, before the
      next field begins. Use for inline audit logs, validation
      explanations, expandable detail panels.
  - Each slot receives a `FieldActionContext` with `field`, `value`, `mode`,
    `section_id`, and `group_id`. `mode` is widened to
    `ViewMode | 'edit' | 'view'` so the same component handles both forms.
  - Fires in every view mode of `HazoCollabFormView`
    (`edit | summary | print | approval`) and both modes of `HazoDataForm`
    (`edit | view`).
  - Scalar fields only — inputbox, textarea, currency, percentage,
    computed, date, combo, radio, checkbox, masked, static text, summary
    row, and `HazoCollabFormDoc`. Group header rows, data table fields,
    per-cell positions inside tables, and `HazoCollabFormSplitAmount`
    do not fire the slot in this release. The consumer can return `null`
    from either slot to opt out per-field.
  - New helper `<FieldActionSlot />` exported from the package root —
    consumers normally don't import it directly; it's the internal renderer.
  - Backward-compatible: when the prop is omitted the rendered DOM is
    byte-identical to 5.7.0.

## 5.7.0

- **NEW**: `AutofillDropzone` accepts an optional `upload_manager?: ExternalUploadManager` prop. When set, file drops are delegated to the external manager instead of running the inline `upload → extract → conflict` pipeline. `on_autofill_complete` is not called in this mode; the consuming app handles merging via its own `job:completed` subscription. The `OverwriteConfirmDialog` is not rendered in this mode.
- **NEW**: `HazoCollabFormViewProps.upload_manager?` threaded through to `AutofillDropzone`.
- Backward-compatible: when `upload_manager` is absent, behavior is identical to 5.6.1.

## [5.6.0] - 2026-05-12

### Added

- **ThreadForm Key-Info Drawer** — collapsible region above the task list that
  embeds `HazoCollabFormView` (always `mode="edit"`) with H.7 confirm-state
  plumbing. Retires the consumer-side `setInterval` DOM-class overlay shim used
  to bridge prefill state into ThreadForm.
- New `ThreadFormProps` fields: `key_info_sections`, `key_info_form_data`,
  `on_key_info_form_data_change`, `field_metadata`, `on_metadata_change`,
  `show_confirm_action`, `key_info_title`, `key_info_default_expanded`.
- `ThreadFormRef.confirm_all_prefilled()` — bulk-confirms all currently
  prefilled fields in the embedded view; returns transitioned ids.
- `FieldMetadataInput`, `FieldMetadataChange`, and `ConfirmState` re-exported
  from `hazo_collab_forms/thread-form` for single-import ergonomics.
- Collapsed drawer header shows a state-count summary (`3 prefilled • 1 edited
  • 5 confirmed`).

### Changed

- `ThreadForm` is now a `forwardRef` component. Existing call sites (no `ref`)
  continue to work unchanged.

### Notes

- All new props are optional. When `key_info_sections` is omitted, the drawer
  is not rendered and ThreadForm behavior is identical to 5.5.0.

## [5.5.0] - 2026-05-12

### Added

- **H.7 — Confirm-required field state** (`ConfirmState`): first-class field metadata
  for prefill review workflows. Three states: `'prefilled'`, `'confirmed'`, `'edited'`.
- `confirm_state?: ConfirmState` added to `FieldMetadata` and `FieldMetadataInput`.
- CSS classes `hcf-confirm-{state}` applied to both the `[data-field-id]` wrapper div
  and the inner `cls_collab_field_container` for easy per-state styling.
- `show_confirm_action?: boolean` prop on `HazoCollabFormView` (and propagated through
  context to `HazoCollabFormSet`). When `true`, prefilled fields show an inline green
  Confirm (✓) button that fires `on_metadata_change` with `confirm_state: 'confirmed'`.
- Auto-edit detection effect in `EditView`: takes a value snapshot when a field first
  enters `'prefilled'`, then transitions to `'edited'` if the value changes and back to
  `'prefilled'` if it returns to the snapshot.
- `confirm_all_prefilled()` method on `HazoCollabFormViewRef`: bulk-confirms all
  currently prefilled fields, fires `on_metadata_change` for each, returns their IDs.
- `styles/confirm-state.css` published asset with CSS custom properties for all three
  states. Import via `hazo_collab_forms/styles/confirm-state.css`.
- `ConfirmState` re-exported from `hazo_collab_forms`, `hazo_collab_forms/components`,
  and the view-level index.

## [5.4.1] - 2026-05-11

### Changed

- Regenerated `package-lock.json` against the workspace canonical dep
  table. No source changes — purely a lockfile refresh that drops
  ~1.7k stale entries inherited from earlier workspace state.

## [5.4.0] - 2026-05-11

### Added

- `HazoCollabFormSplitAmount`: composite field type for work/personal expense splits.
  Stores value as `{ total: number, work_percent: number }` and renders a read-only
  computed deductible (`total × work_percent / 100`). Accepts `default_work_percent`
  prop. Pairs with the `work_personal_split: true` annotation in consumers' schemas.
- Pure helpers `parse_split_value`, `compute_deductible`, `is_split_value`,
  `clamp_work_percent` and `SplitAmountValue` type exported from `hazo_collab_forms/utils`.
- `"split_amount"` added to the `BaseFieldType` union.

## [5.3.1] - 2026-05-10

### Changed

- **Dependency version pins aligned with workspace canonical table.**
  No API changes — purely a maintenance pass to keep this package on the
  same dep ranges as the rest of the hazo_* ecosystem.
  - `peerDependencies`: `hazo_auth ^5.1.30`, `hazo_config ^2.1.0`,
    `hazo_connect ^2.4.0`, `hazo_ihelp ^2.0.0`,
    `hazo_llm_api ^1.2.0`, `hazo_logs ^1.0.13`, `hazo_pdf ^1.7.0`,
    `lucide-react ^0.553.0`, `sonner ^2.0.7`.
  - `dependencies`: `clsx ^2.1.1`, `tailwind-merge ^3.5.0`.
  - `devDependencies`: `typescript ^5.7.2`, `@types/node ^20.14.10`,
    `@types/react ^18.3.3`, `@types/react-dom ^18.3.0`,
    `lucide-react ^0.553.0`.
- **`CLAUDE.md` rewritten for density.** Same content, ~4× shorter —
  source-of-truth pointers, critical rules, v2-data-model gotchas, and
  conventions condensed so the file stays within sensible context budgets.

### Notes

- `test-app/` was migrated to Tailwind v4 (`@tailwindcss/postcss`,
  `tw-animate-css`, `@import "tailwindcss"`) and added `dropbox` /
  `googleapis` to `serverExternalPackages`. Test-app changes do not
  ship in the published package.

## [5.3.0] - 2026-05-04

### Added

- **`ThreadFormProps.on_selection_change?: (delta: Record<string, boolean>) => void`** —
  dedicated state setter for collected-data Keep selections. Receives a
  chain-expanded delta (only changed keys). Wire to
  `use_thread_form`'s `update_selections` to get the canonical merge
  behavior. The existing `on_form_change` still fires alongside it for
  backward compatibility.
- **Validation rule count + names in `FileBar` progress text** — the
  pipeline-status strip under each file now reads
  `Running N validation rule(s)…` instead of the generic
  `Running validation rules…`. Hover the strip to see the rule names
  via the standard `title` tooltip.
  - New `pipeline_rule_count?` and `pipeline_rule_names?` props on
    `FileBar`.
  - `use_file_pipeline.file_statuses` Map value upgraded from
    `FileStatus` (string) to `FileStatusEntry` (`{ status, total_rules?,
    rule_names? }`). Downstream consumers (`TaskCard`, `ThreadTimeline`)
    updated.

### Changed

- **`CollectedDataView` row visuals** — flat divided rows replaced with
  card rows (white bg, 1 px border, subtle shadow) sitting on a `#FAFBFC`
  bucket strip with a 3 px neutral-gray left bar on each bucket header.
  Pure CSS, no API change.
- **Extracted-fields metadata labels** — `Document date:`, `Confidence:`,
  and inline scalar labels switch from light gray to
  `font-semibold text-[#64748B]` so labels visibly distinguish from
  values. Same 12 px size.
- **`on_form_change` docstring** clarified — it is a debounced
  full-state persistence sink, NOT a state setter. Wiring it in
  isolation (without per-event setters or a `use_thread_form` hook)
  silently drops UI updates from controls that only have the
  full-state path. Selection toggles were the visible symptom of this
  pre-`on_selection_change` and are now fixed.

### Fixed

- **Keep checkbox / "select all" / "unselect all" not updating in UI** —
  consumers wiring `on_form_change` strictly per its docstring (as a
  debounced backend save) saw no visual response when toggling Keep
  on a file row or using the per-message select/unselect-all link.
  The internal handler now also fires the new `on_selection_change`
  setter so local state can update independently of the persistence
  round-trip.

### Notes

- ~10 other handlers in `thread_form.tsx` follow the same
  `on_form_change`-only pattern. Most are fine in practice because
  they fire other state setters in the same handler, but a follow-up
  audit pass is warranted to close the latent bug class.

## [5.2.0] - 2026-05-04

### Added

- **`ExpectedField.array_of`** — declare a field whose value is an array of
  structured rows, e.g. `array_of: { type: 'object', shape: [...] }`. The
  classification prompt builder auto-emits an `(array of objects)` sub-block
  with the nested shape, and `ExtractedFieldsInline` renders the value as
  a table with one column per shape entry. Backwards-compatible (optional).
- **`ThreadFormProps.on_request_reextraction`** — agent-only per-file
  re-extract callback. Signature `(content_id, scope: 'classification' | 'full')
  => Promise<void> | void`. Wired through `ThreadForm` →
  `CollectedDataView` → `ExtractedFieldsInline`.
- **Re-extract icon button** on the extracted-fields meta line, plus a
  Radix Popover **kebab menu** with two scopes (`'classification'` / `'full'`).
  Visible only when `role === 'agent'` AND a callback is provided.
- **Dim-in-place loading state** for the re-extract action. Content area
  gets `opacity-50 pointer-events-none` while the callback is in flight;
  action buttons stay clickable (visibly disabled).
- **Per-row accordion** in `CollectedDataView`. Each file row's extracted-
  fields block collapses behind a chevron; default state is collapsed.
- **Expand all / Collapse all** toggle in the Collected Data tab header.
  Single button that flips based on current state.
- **Info icon (`FileInfoDialog`) on Collected Data file rows** — agent-only
  trigger that opens the same classification + validation + debug-JSON
  dialog already present on `FileBar`. Hidden when there's nothing to
  show.
- **`build_extracted_fields_instruction`** helper exposed via
  `__test_only__` for unit testing the prompt-builder path.
- Renderer now handles **lenient fallbacks** for arrays:
  - Undeclared `Array<object>` → inferred-column table
  - Undeclared `Array<scalar>` → bullet list
  - Declared scalar with `Array<object>` value → inferred-column table
    (uses declared label) — schema upgrade hint without breaking
- **Test-app demo** (`/thread-form-extracted-fields`) updated with
  rental-statement fixture exercising every render path, plus a mock
  re-extract handler that swaps state after a 1.2s delay.

### Changed

- **`render_document_types`** factored into a recursive `render_expected_field`
  helper that emits the nested object-array sub-block. Pure refactor;
  scalar output unchanged.
- **`extracted_fields_instruction`** assembly extracted into the pure
  `build_extracted_fields_instruction(document_types)` helper, replacing
  the inline construction in the route handler. Adds an `array_of`-aware
  clause when at least one doctype declares it.
- **`ExtractedFieldsInline`** restructured around a `partition_entries`
  helper that splits entries into scalars / tables / lists. JSX now
  uses semantic `<table>`/`<thead>`/`<tbody>` with `border-collapse`,
  `scope="col"` on `<th>`, `<caption className="sr-only">`, alternating
  row tint, and `tabular-nums` on numeric/currency columns. Bordered
  card wrapper around each table.
- **Internal `has_anything` → `has_content`** rename in
  `ExtractedFieldsInline` so the existing "confidence alone returns null"
  contract remains pinned even with the new content categories.

### Fixed

- **`parse_classification_response` no longer JSON-stringifies arrays
  in `extracted_fields` extras.** Previously, declared `array_of: object`
  values arrived at the client as JSON-encoded strings (because
  `JSON.stringify(v)` ran on every `typeof v === 'object'` value),
  silently breaking the table render path. Arrays now pass through
  unchanged; plain (non-array) objects still get stringified for the
  scalar fallback.
- **Re-extract click in tests** wrapped in `await act(async () => ...)`
  to flush the trailing `set_is_loading(false)` state update.

### Notes

- All schema/prop additions are optional — no consumer change required to
  upgrade. To take advantage of `array_of` table rendering, opt in by
  adding `array_of: { type: 'object', shape: [...] }` to your doctype's
  `expected_fields`.
- The `LuMoreVertical` icon was substituted with `LuEllipsisVertical`
  (lucide-react renamed it upstream); visually identical.
- Internal: 31 tests in `extracted_fields_inline.test.tsx` cover every
  branch of the partition decision tree plus the loading-state lifecycle.

## [5.1.0] - 2026-05-03

### Added

- **feat(thread_form):** inline `extracted_fields` display in CollectedDataView
  via `show_extracted_fields` and `render_collected_item_details` props.
- **feat(classification):** `parse_classification_response` now passes through
  arbitrary `extracted_fields` keys; `ClassificationDocumentType` accepts
  `expected_fields` to drive both the prompt and the renderer's formatting.
- **feat(classification):** new `{{extracted_fields_instruction}}` prompt variable.
- New exports from `hazo_collab_forms/thread-form`: `ExtractedFieldsInline`,
  `format_extracted_field`, `prettify_key`, `is_iso_date`, `CURRENCY_KEY_REGEX`,
  `ExpectedField`, `ExpectedFieldType`, `ClassificationDocumentType`,
  `FormatExtractedFieldOptions`.

## [5.0.6] - 2026-05-03

### Fixed

- **`hazo_connect_adapter` was silently dropped by all four LLM route handlers.** `create_classification_route`, `create_validation_route`, `create_content_gate_route`, `create_resolution_route` (and `create_llm_commentary_fn`) accept `options.hazo_connect_adapter` and forwarded it to `hazo_llm_api.initialize_llm_api({ hazo_connect_adapter })` — but the `hazo_llm_api` contract is `{ hazo_connect }`. The unknown key was discarded and `hazo_llm_api` fell back to a default SQLite-backed `HazoConnect`, so prompt lookups in PostgREST/Supabase deployments failed with `Prompt … not found in hazo_prompts. Seed it before calling this route.` even when the row clearly existed. Each handler's internal forwarding now passes `{ hazo_connect: hazo_connect_adapter }`. The public consumer-facing `options.hazo_connect_adapter` name is unchanged — no consumer code change required.

## [5.0.5] - 2026-05-03

### Added

- **`HazoValidationRuleEditor` export/import** — the rule-list sidebar now has two icon buttons in the header:
  - **Download (export)**: serialises all loaded rules to a portable JSON file (strips `id`, `created_at`, `changed_at`, `scope_id` so the file is environment-agnostic). Button is disabled when there are no rules.
  - **Upload (import)**: opens a JSON file picker, parses an array of rule objects, and calls `create_rule` for each valid entry. Entries missing `name` or `document_type` are skipped. Duplicate `rule_id` values are resolved by appending a timestamp suffix. Result shown in the sidebar footer for 4 s.
  - Import button is hidden in `read_only` mode; export is always visible.

- **`ThreadMessage.reply_to`** (`{ source_content_id, file_name?, rule_name? }`) — optional field on comment messages created via the inline Reply button on an ACTION REQUIRED card. Lets the consuming app persist and display which send-back issue a comment responds to.

- **Inline Reply on ACTION REQUIRED cards** — agent users can click a "Reply" link on any unresolved `SendBackMessage` issue block to open a compact textarea directly below it. Enter submits, Escape cancels. The resulting comment is added via `on_message_add` with `reply_to` metadata.

- **Reply-to quote block** in `ThreadTimeline` — when a message has `reply_to` set, a small bordered quote showing the linked file name and rule name renders above the message body.

- **"Select all / unselect all" toggle** in `ThreadTimeline` — when an agent-role message contains 2 or more file attachments, a small toggle link appears above the file grid to bulk-check or bulk-uncheck the Keep checkboxes.

### Changed

- **`ThreadComposer` add-question button** is now **agent-only** again — the 5.0.2 change that added it to the client role has been reverted. Clients no longer see the `"+ Question"` / `"Add a question"` button. Use `on_add_question` from the consuming app if you need to surface this action to clients through a different entry point.

- **`SendBackMessage`** accepts a new `on_reply?: (source_content_id, file_name, rule_name, text) => void` prop. When provided, a "Reply" link appears on each unresolved ACTION REQUIRED card header.

- **`TaskCard`** accepts a new `on_reply_to_issue?: (task_id, source_content_id, file_name, rule_name, text) => void` prop, wired through to `ThreadTimeline` and `SendBackMessage`.

- **`ThreadTimeline`** accepts a new `on_reply_to_issue?: (source_content_id, file_name, rule_name, text) => void` prop.

## [5.0.4] - 2026-05-01

### Changed

- **`ThreadForm` "Send to Client" button** is now visible for all non-completed statuses (`draft`, `sent`, `in_progress`) instead of `draft` only. Label adapts: `"Send to Client"` while `draft`, `"Resend Link"` afterwards. Lets agents re-share or re-fetch the client portal URL throughout the form lifecycle without leaving the page. Consumers that want to suppress the button can simply omit `on_send_form` — the button now also opt-out renders only when the callback is supplied (an added safety guard against undefined-handler clicks).

## [5.0.2] - 2026-04-30

### Changed

- **`ThreadComposer` add-question button** is now visible to the `client` role as well as the `agent` role, with the label adapted per role: `"Add a question"` for clients and `"+ Question"` for agents. Lets clients raise a new question on the thread without having to wait for an agent prompt. Wires through the existing `on_add_question` callback — no API change for consumers.

## [5.0.0] - 2026-04-30

### BREAKING CHANGES — Batch Decisions UI for Pending Review

The agent's per-rule "Send Back" button on multi-rule files is replaced by a new **batch decisions panel**: each rule shows a 3-state radio (Pending / Accept / Send Back) and a single **Submit Decisions** button commits the batch. Single-rule files keep today's fast-path UX. Per-rule Send Back clicks for the same parent question now **merge** into one open clarifications task instead of spawning a new task per click. Consecutive `send_back` messages with the same source file render as one panel (file shown once, N stacked "ACTION REQUIRED" boxes). Existing data continues to work — the file-level `accepted` / `sent_back` booleans on `ContentItem` are now derived from the new `rule_actions` map but the booleans are still written for backward compatibility.

### Added

- **`ContentItem.rule_actions?: Record<string, RuleAction>`** — per-rule decision map keyed by `rule_id` (`'accepted' | 'sent_back'`). Drives the file-level pill state (Sent Back wins over Accepted in mixed states) and the panel's visible-rule filter. Absence of an entry means the rule is still Pending.
- **`RuleAction` and `RuleDecision` named types** in `src/components/thread_form/types.ts` — shared across `FileBarProps`, `ThreadTimelineProps`, `TaskCardProps`, and `handle_submit_decisions` so the per-rule decision payload has one source of truth.
- **`ThreadTask.parent_task_id?: string`** — links a clarifications task back to its parent question. Lets new Send Backs find an open clarifications task to merge into instead of spawning a new task per click.
- **`compute_file_decision_state(rule_actions, rules_with_issues)`** in `src/components/thread_form/utils/file_decision_state.ts` — pure helper deriving `{ accepted, sent_back, pending_count }` from a rule_actions map. 9 vitest cases.
- **`merge_send_back_message(tasks, parent_task_id, msg, build_new_task)`** in `src/components/thread_form/utils/merge_send_back.ts` — pure helper that appends a `send_back` message to an open clarifications task (matched by `parent_task_id` + `status !== 'completed'`) or asks the caller to build a fresh task. 5 vitest cases. Closed clarifications tasks are ineligible — the next Send Back starts a fresh round.
- **`RuleDecisionRow` component** at `src/components/thread_form/components/rule_decision_row.tsx` — per-rule row in the batch decisions panel: 3-state radio + comment editor (visible only when Send Back is selected) + display-only `IssueGroupTree` for issue drill-down.
- **`BatchDecisionPanel` sub-component** inside `file_bar.tsx` — owns per-rule decisions and comments, renders one `RuleDecisionRow` per visible rule, and provides header buttons `[Accept all]` + `[Submit Decisions (N)]`. Accept all stamps every visible rule as accepted in one click. Submit is disabled when no rule is decided.
- **`IssueGroupTree.hide_actions?: boolean` prop** — suppresses rule-level and category-level Send Back buttons in agent mode. Used by the batch decisions panel to embed `IssueGroupTree` in display-only mode.
- **`FileBar.show_keep_checkbox?: boolean` prop** and matching **`SendBackMessage.show_keep_checkbox`** — explicit prop-level control over the Keep checkbox. Default behaviour unchanged when undefined; client-side callers now pass `false` so the Keep checkbox is agent-only.
- **`FileBar.on_submit_decisions?` prop** — callback fired when the agent submits a batch of per-rule decisions. Wired through `TaskCard` and `ThreadTimeline` from `thread_form.tsx`.
- **`handle_submit_decisions(item, decisions)`** in `thread_form.tsx` — walks each decision: `'sent_back'` rules build a `send_back` message and route through `merge_send_back_message` so cross-file Q1 batches still consolidate into one clarifications task; `'accepted'` rules just stamp `rule_actions`. Atomic single update for tasks + per-file fields.
- **Render-time consolidation of consecutive `send_back` messages** — `group_messages` in `shared.tsx` merges consecutive send_back messages sharing the same `source_content_id` into one render group. `SendBackMessage.data` accepts `SendBackData | SendBackData[]`; the pinned file row is rendered once and the ACTION REQUIRED block renders once per entry.
- **Clickable `ref` badge** on a clarifications task's pinned file — scrolls smoothly to the original file in the parent question and briefly highlights it. `FileBar` sets `data-content-id` on its root for the lookup.
- **Resolved RFI accordion (client view)** — `file_bar.tsx` switched to `display="collapsed"` on `HazoCollabFormRFIbox`, so once the client submits a response the panel folds into a one-line summary that they can click to expand.
- **`periodic_coverage` validation type** — detects missing files across a period (biweekly rental statements, quarterly council rates, monthly utility bills). Authored in the existing Validation Rule Editor via a new `VALIDATION TYPE` dropdown that swaps the form body between LLM Prompt fields and Coverage Configuration fields (Cadence, Alignment, Period Start, Period End).
- **Variable-chain inputs for period bounds** — Period Start and Period End accept an ordered list of variable references (e.g. `[@tenant_start_date, @Beginning_Of_Tax_Year]`); first non-empty resolved value wins. Rule skips silently when neither resolves.
- **Form-field variables in the rule editor** — `HazoValidationRuleEditor` accepts a new `form_field_variables?: PromptVariable[]` prop. These merge into `prompt_config.variables` and appear in the @-popup alongside session-level constants. Available to both `llm_prompt` and `periodic_coverage` rules.
- **`extracted_data` field on `ValidationRuleResult`** — standardises structured outputs from per-file LLM rules. Today consumed by `periodic_coverage` rules, which read `{ period_start, period_end }` populated by date-extraction LLM rules.
- **Per-group periodic-coverage pass** in `use_file_pipeline.ts` — runs after `run_backoffice_validation`. Groups files by primary classification tag, fetches matching `periodic_coverage` rules, computes gaps deterministically. Returns `GroupCoverageResult[]` for the consuming app to surface.
- **Pure utilities in `src/lib/`** — `compute_coverage_gaps` (deterministic gap arithmetic with calendar-aligned and flexible cadence tiling), `resolve_variable_chain` (form-field + session-variable resolution).

### Changed

- **Pending Review pill** counts only **un-actioned** rules (was: total issues regardless of agent decisions). Once all rules are accepted or sent back the pill disappears; partial state shows the remaining count.
- **`handle_send_back_issues`** routes new `send_back` messages through `merge_send_back_message`. First click creates the new clarifications task and stamps `parent_task_id`; subsequent clicks for the same parent merge into that task while it remains open.
- **`handle_run_backoffice` pending count** excludes files in completed (approved) tasks and files with `accepted: true`. Resolved files no longer inflate the "Run Backoffice (N)" pill.
- **Locked-task affordances** — files in completed tasks now suppress the Document Type pencil, the Remove (×) button, and the Keep checkbox; thread message edit/delete actions are hidden. Tasks are read-only once the agent approves them.
- **`MessageEditor` attached file rows** — replaced the full-width red list with the same compact two-column grid styling used by the regular timeline.
- **`classify_file`** skips classification with a `console.warn` (and `null` return) when the inferred MIME type is `application/octet-stream` (no recognised file extension), instead of POSTing to the API and surfacing a `console.error`. The file still uploads; validation just doesn't run for unknown types.
- **`FbEntryValidation.processed_data_ref`** is now `string | null` to support group-level missing-file validations that have no specific file reference.
- Mock `/api/mock-validate` populates `extracted_data` from filename date patterns so end-to-end coverage testing works without real LLM calls.

### Migration

- No persistence migration required. Existing `ContentItem` records without `rule_actions` are treated as all-pending and continue to work; the file-level `accepted` / `sent_back` booleans are still written for backward compatibility.
- Consumers that previously read `effective_validation_results` directly to render per-rule Send Back buttons in agent mode should rely on the built-in panel — those buttons no longer render in agent mode on multi-rule files.
- `SendBackMessage.data` widened from `SendBackData` to `SendBackData | SendBackData[]`. Existing single-payload callsites work unchanged.
- The verbose validation-prompt LLM output that some consumers were rendering verbatim now has a recommended pattern: produce a tight `client_comment` (max 1–2 sentences, plain English, addressed to the client) on each issue. The agent's audit fields (`description`, `reason`) can stay verbose.

## [4.0.0] - 2026-04-25

### BREAKING CHANGES — Unified Validation Result Shape

Collapses single-issue and multi-issue validation results into one uniform shape. The LLM is now told one schema via one wrapper; the `issues[]` array is always present (empty when the check passed). This simplifies 26 consumer files that previously branched on "does this result have `issues[]`, or should I fall back to `issue_description`?". The `get_issues()` normalizer is gone — consumers iterate `result.issues` directly.

### Changed

- **`ValidationRuleResult` shape** — `issues: ValidationIssue[]` is now **required** (was optional). Deleted top-level `has_issue` and `issue_description`; use `result.issues.length > 0` and `result.summary ?? result.issues[0]?.issue_description`. Added optional rule-level `summary` for pass/fail explanation and rule-level `confidence`.
- **`ValidationIssue` shape** — Promoted `client_comment`, `amount`, `date`, `reason`, `confidence` to first-class optional fields. Deleted the `context` escape hatch (all LLM fields now have typed homes). The agent's send-back message now reads `issue.client_comment` directly.
- **Single-issue vs multi-issue is gone** — Removed `ValidationRule.multi_issue` and `ValidationRuleExecution.multi_issue`. All rules produce an array; a "single-issue" rule just returns 1 entry (or 0 when passing).
- **Prompts DB** — `validation/single_issue_wrapper` row **deleted**. `validation/multi_issue_wrapper` **renamed to `validation/response_wrapper`** with an updated JSON schema including `summary`, per-issue `client_comment` / `amount` / `date` / `reason` / `confidence`, and rule-level `confidence`. No code-level fallback — unseeded wrappers still throw a 500 with a seeding hint.
- **Server execution paths collapsed** — `execute_rule` + `execute_rule_batch` + inline text-mode branch merged into one `run_rule(rule, input, variables, wrappers)` with a tagged `ValidationInput` union (`document | documents | text`). Handler is thinner; parsing is shared.
- **Parser consolidated** — Deleted `parse_validation_response` and `extract_validation_fields`. Kept one parser (`parse_response`) that produces the unified shape.
- **Deleted `get_issues()` normalizer** (`src/utils/validation_result.ts`) — no longer needed.

### Migration

- No persistence migration shipped. Consuming test-apps should reset their SQLite DBs (`test-app/data/prompt_library.sqlite` and any DB that persists `ValidationRuleResult` rows) and re-run `test-app/scripts/seed-prompts.mjs`.
- Rule prompts should **not** instruct the LLM to return extra fields — wrappers own the response shape. Audit rule prompts for stray "return a JSON with field X" language and remove it.

### Files changed
- `src/types/validation.ts` — `ValidationRuleResult` / `ValidationIssue` shape; removed `multi_issue` on rules
- `src/lib/validation_handler.ts` — rewritten: one parser, one `run_rule`, tagged input, loads `response_wrapper` instead of the old two
- `src/utils/validation_result.ts` — deleted `get_issues()`
- `src/utils/fb_data_adapter.ts` / `src/utils/fb_data_adapter_v2.ts` — read `issues.length` / `summary` instead of `has_issue` / `issue_description`
- `src/utils/rule_to_execution.ts` — dropped `multi_issue`
- `src/components/hazo_fb_form/**` (views, hooks, format) — migrated reads
- `src/components/thread_form/**` (file_bar, file_info_dialog, hooks, types, thread_form) — migrated reads, `ContentItem.validation_rule_results` now uses the canonical type
- `src/components/shared/rule_result_card.tsx`, `src/components/shared/file_bar/file_bar_validation_dialog.tsx` — migrated reads
- `test-app/scripts/seed-prompts.mjs` — deleted single_issue_wrapper, renamed + updated response_wrapper
- `test-app/app/api/mock-validate/route.ts` — emits new shape

## [3.2.0] - 2026-04-15

### Fixed

- **Review acceptance not persisting to v2 data model** — `confirm_review_decisions` now calls `set_review_decision()` mutation to persist accept/send_back decisions to `form_data_entries`, updating clarification `status` (→ `accepted`/`sent_back`) and `review` object. Previously, only UI-level state was updated while the data model retained `status: 'responded'` and `review: null`.
- **File status showing "Pending Review" after acceptance** — With the review persistence fix, `get_file_status()` now correctly reads `status === 'accepted'` from clarifications.
- **Process Data button disabled for file-only forms** — `has_front_data` now recognizes `__files_*` and `__private_files_*` keys as valid data, so submissions containing only file uploads enable the button.
- **Process Data button disabled in Tax Data step after review** — Button disabled condition now also checks `skipped_file_ids` and `classification_results`, enabling it when files were accepted in the review step.
- **MIME type `application/octet-stream` for files in Instance 2+** — Added `mime_from_name()` to infer MIME type from file extension as fallback. Also checks `__files_${field_id}` prefix when looking up file attachments in `front_form_data`, since later instances may only have files under the prefixed key.
- **Classification results lost on instance switch** — New restoration effect reconstructs `classification_results` from `form_data_entries` when empty (e.g., Instance 2 after send-back), walking the v2 data model to extract file_id, tags, and document_type.

### Added

- **Autofill deduplication via `FbAutofillRun` tracking** — New `autofill_runs` field on `FbDataEntry` records each autofill execution per file+group with status, fields_populated, timestamp, and form_instance. Prevents duplicate data on page refresh by skipping files already successfully autofilled.
  - New type: `FbAutofillRun` — tracks file_id, file_name, group_id, status, fields_populated, time, form_instance, error
  - New query helpers: `get_autofill_runs_for_file()`, `is_file_autofilled()`, `get_autofilled_file_groups()`
  - New mutation helper: `add_autofill_run()` — records run on the entry containing the file, replaces existing run for same file+group (idempotent)
  - All `route_files_to_back_office` call sites pass autofill history and record results

### Files Changed
- `src/types/fb_form_data.ts` — Added `FbAutofillRun` type and `autofill_runs` field on `FbDataEntry`
- `src/utils/fb_data_queries.ts` — Added autofill query functions
- `src/utils/fb_data_mutations_v2.ts` — Added `add_autofill_run()` mutation
- `src/components/hazo_fb_form/hazo_fb_form.tsx` — Review persistence, classification restore, has_front_data fix, Process Data button enablement
- `src/components/hazo_fb_form/hooks/use_llm_run.ts` — MIME inference, `__files_` prefix fallback, autofill dedup skip logic, autofill run recording

## [3.1.20] - 2026-04-13

### Changed

- **MAJOR: V2 Flattened Data Model — Single Source of Truth Across Instances** — Complete re-architecture of the form data model for improved clarity and cross-instance consistency:
  - Old hierarchical model (v1) with nested activities, validations, and clarifications replaced with flattened structure
  - New model stores validations and clarifications as direct arrays on `FbDataEntry`, not nested in activities
  - **Instance-independent design:** Same JSON output regardless of active instance — data flows forward through the form sequence, not sideways between instances
  - **Thread captures full conversation:** `FbEntryClarification.thread[]` now spans all form instances, showing complete history from issue detection through all client responses and agent reviews
  - **Review decisions linked to thread entries:** `FbClarificationReview.thread_ref` points to the specific client response being reviewed (not just the clarification ID)
  - **Per-instance keyed Maps:** `review_queue_decisions` and `send_back_comments` now stored as `Map<string, T>` keyed by instance ID to prevent data from Instance 1 leaking into Instance 2
  - **Review clears on new input:** When a newer client response arrives after a send-back, the review is set to null (cleared) to reflect that the previous decision no longer applies
  - **New utilities:** `flat_to_data_v2()` converter, `fb_data_queries.ts` read helpers, `fb_data_mutations_v2.ts` immutable update functions
  - **V1 types preserved:** Old types kept in `fb_form_data_v1.ts` for backward compatibility during migration
  - **File status accordion reads from V2:** Rebuilt to use v2 queries instead of parallel v1 state tracking
  - **Review queue items enriched:** Now include full v2 thread data for complete cross-instance conversation context
  - **JSON data panel:** Added copy-to-clipboard button for easier export/debugging
  - **Reconciliation effect:** Clears stale v1 decisions when v2 data shows `review=null`, ensuring consistency after instance switches

### Added

- **E2E test for send-back flow:** `test-app/tests/e2e/fb-form-sendback.spec.ts` — Playwright test covering full multi-instance clarification workflow with thread history validation

### Files Changed
- `src/types/fb_form_data.ts` — Completely rewritten with v2 flattened structure
- `src/types/fb_form_data_v1.ts` — New file preserving v1 types
- `src/utils/fb_data_adapter_v2.ts` — New adapter implementing v2 conversion logic
- `src/utils/fb_data_queries.ts` — New query helpers for reading v2 data
- `src/utils/fb_data_mutations_v2.ts` — New mutation utilities for updating v2 data
- `src/components/hazo_fb_form/hooks/use_instance_state.ts` — Updated to use Map-based decisions
- `src/components/hazo_fb_form/components/review_queue_view.tsx` — Updated to use v2 thread data
- `test-app/tests/e2e/fb-form-sendback.spec.ts` — New Playwright E2E test

### Migration Notes
- Old `flat_to_data()` (v1 adapter) still functional but deprecated. New code should use `flat_to_data_v2()`
- Review queue decisions now accessed via `review_queue_decisions.get(instance_id)` instead of direct property access
- Clarification status now read from `clarification.status` directly instead of inferring from activities array
- Thread history now accessible via `clarification.thread[]` instead of separate activity records

## [3.1.19] - 2026-04-13

### Fixed

- **Client clarification responses reliably persist across page refresh** — `update_clarification_status` now falls back to `file_validation_results.errors` when the clarification item isn't found in `draft_clarifications` (which can be empty due to dedup timing). Previously, if the draft was already deduped against sent_clarifications, the upsert silently failed and the response was lost.

## [3.1.18] - 2026-04-13

### Added

- **File Status Accordion on Tax Data step** — Collapsible panel at the top of Tax Data showing all classified files with their status (Accepted, Sending Back, Pending Review, No Issues). Includes file name, tags, issue counts, and color-coded status badges.

### Fixed

- **Test-app data persists across page refresh** — Added `initialized` guard to prevent localStorage save effects from firing before async init completes. Previously, the empty initial state (`back_form_data: {}`, `form_data: []`) was saved to localStorage on mount, overwriting restored data before the init effect ran.

### Changed

- **RFI card read-only after submit** — After clicking "Submit Response" on a review item, the card becomes grayed out (`opacity-70`) with a "✓ Submitted" badge, all fields become read-only, and the card collapses. The "Submit Response" button is hidden once submitted.

### Fixed

- **Review decisions (accept + send_back) now persist across page reload** — The restoration effect now handles both `'approve'` → accept AND `'skip_process'` → send_back decisions from persisted `sent_clarifications[].review_decision`. Also restores `agent_feedback` as send_back comments. Fixed stepper init effect overwriting restored decisions by checking `has_restored_decisions` before resetting.

## [3.1.15] - 2026-04-12

### Added

- **"Submit Response" button per RFI card in Review Queue** — Each review item card now has its own "Submit Response" button at the bottom (inside the card, not in the footer). The button appears once a decision (Accept/Send Back) is made, is disabled until feedback is provided (for send-back items), and shows "✓ Response submitted" after clicking. Confirms and persists review decisions to the data model per item.

### Fixed

- **Review step clickable after page reload** — Two fixes:
  1. Review step now enabled when `sent_clarifications` has responded items (not just when `review_was_completed` or `effective_review_items` exist)
  2. Active step's `onClick` handler now fires even when `is_clickable` is false — the guard `is_clickable && on_step_click()` blocked the active step from switching the tab view since it's neither completed nor past

## [3.1.10] - 2026-04-12

### Fixed

- **Clarification form no longer grays out when selecting a response option** — The data model enrichment was setting `status: 'responded'` on pending (unconfirmed) responses, which caused `flat_to_data` to produce `input_status: 'complete'`, triggering `is_complete_in_data` in `immediate_clarifications` and forcing `'resolved'` status. Now pending responses preserve the item's original status — only confirmed responses (OK button clicked) set `'responded'`.

- **Clarification responses persist across page reloads** — After client submit + refresh, clarifications now show as resolved with the original response choice and comment. Two fixes:
  1. `immediate_clarifications` now forces `'resolved'` when instance is `client_submitted`/`completed` and the sent clarification has a response — client can't edit after submission anyway
  2. Test-app `handle_submit` now merges submitted clarifications into existing `sent_clarifications` instead of replacing (prevents wiping responses persisted via `on_instance_update`)

### Added

- **Test-app localStorage persistence** — FB Form page now persists instances, back_form_data, form_data, and active_instance_id to localStorage. Survives page refresh. "Clear Data" button resets all state and localStorage. `front_sections` are stripped from saves to prevent quota issues.

## [3.1.8] - 2026-04-12

### Fixed

- **Client RFI responses no longer prematurely forced to 'resolved'** — `immediate_clarifications` was forcing `status: 'resolved'` when `sent_clarifications` had any `response_choice`, even if the agent hadn't reviewed it yet. This grayed out the form immediately after the client selected a response option, preventing them from adding mandatory comments. Now only `is_complete_in_data` (from hierarchical data model) and `is_confirmed` (same-session OK click) force resolved status. Items with a sent response but no agent review remain `'responded'` so clients can still edit.

## [3.1.7] - 2026-04-12

### Fixed

- **Review queue restoration uses sent_clarifications as source of truth** — Replaced fragile file_validation_results-based restoration with direct scan of `sent_clarifications[].review_decision` and `front_form_data.__clarifications[].review_decision`. These are stable, synchronously available from the instance, and use the same IDs as review queue items — eliminating timing issues and file_id mismatch problems from v3.1.5/v3.1.6.

## [3.1.6] - 2026-04-12

### Fixed

- **Review queue restoration now covers client-responded flow** — The v3.1.5 restoration logic only matched `draft_clarifications`, which is empty when items were already sent to the client and de-duped. Now checks all review item sources (drafts, responded sent_clarifications, front_form_data clarifications) matching the same logic as `review_queue_items`.

## [3.1.5] - 2026-04-11

### Fixed

- **Review queue decisions now persist across page reloads** — `review_queue_decisions` and `review_was_completed` are restored from persisted `file_validation_results` on mount. Previously, these were initialized empty on every mount, causing the Review step to show all items as undecided ("0/1") even when the agent had already accepted them.

## [3.1.4] - 2026-04-11

### Added

- **Autofill activity persistence in hierarchical data model** — Autofill results are now recorded as `'autofill'` activities in the `FbDataEntry[]` data model, providing a complete audit trail and surviving page refreshes:
  - New `FbAutofillResult` and `FbAutofillFieldDetail` interfaces for structured autofill data
  - Each per-file autofill (success, error, or empty) writes an activity to the data model
  - On mount, `group_autofill_log` is restored from persisted autofill activities so the UI shows correct state without re-running the pipeline
  - New types exported: `FbAutofillResult`, `FbAutofillFieldDetail`

## [3.1.3] - 2026-04-10

### Fixed

- **Accepted review files no longer re-flagged on Tax Data step** — `confirm_review_decisions()` now marks all `rule_results` as `accepted: true` in `file_validation_results` for accepted files. Previously, only `skipped_file_ids` was updated but validation results were left unmarked, causing `trigger_backoffice_run` to miss resolved rules and `validate_classified_files` to re-flag the same validation issues.

## [3.1.2] - 2026-04-10

### Added

- **Auto-autofill for accepted review items** — When the agent accepts a file in the Review Queue step of the agent stepper, the file now automatically routes to back-office groups and triggers autofill on the Tax Data step without requiring a manual "Next" button click.
  - `confirm_review_decisions()` now returns accepted file IDs (`string[]`) instead of `void`
  - Accepted files added to `skipped_file_ids` for routing
  - New `on_review_accepted?: (accepted_file_ids: string[]) => void` callback prop on HazoFbFormProps
  - `route_skipped_files()` enhanced with queue/autofill UI state (shows spinners in Tax Data)
  - `trigger_backoffice_run` now skips files already routed via review accept (prevents double-processing)
  - Routing triggered on both stepper click and "Next" button in review queue

## [3.0.20] - 2026-04-07

### Added

- **Hierarchical JSON data model** — Complete rewrite of form data serialization (`flat_to_data` adapter):
  - Single `data[]` array combining all form instances (stable across instance switches)
  - Automatic `review_client_input` and `send_back_to_client` activities for back-office review workflows
  - `activity_time` (ISO 8601 timestamp) and `activity_user` fields on all activities
  - Preserved option set history: `original_response_options` vs `send_back_options` vs current `response_options`
  - `processed_data[]` array in client response `client_data` entries for post-processing results
  - JSON data panel with Expand All / Collapse All buttons in test app

- **Configurable response types and sets** — New config editor for managing response options across validation rules:
  - Response Types editor page with sidebar navigation
  - Response Sets editor with response type field linking
  - Dynamic response type/set loading from config API
  - Pass response types/sets to `HazoFbForm` via props for rule-level dropdown selection
  - Tag picker UI for interactive response option management in validation rule editor

- **Interactive client response options editor** — Send-Back view now allows agents to edit response options:
  - Add/remove response options with tag picker UI
  - Option edits persist to both `draft_clarifications` AND `front_form_data.__clarifications`
  - Full response option history preserved in JSON data model

- **Review queue UI improvements** — Review and Send-Back items now use collapsible accordion headers:
  - Pink background for pending items, light blue for actioned items
  - Send-Back items collapse when resolved
  - Accordion titles show status, field reference, and quick decision indicators

- **File layout fix** — File chips in upload zones now use flex-wrap instead of grid-cols-2, allowing files to sit adjacent instead of forcing two-column layout

### Changed

- **Type changes (backward compatible with serialization)**:
  - `FbRfi`: renamed `rfi_ask` → `rfi_issue` (original issue description)
  - `FbRfiDraft`: renamed `rfi_ask` → `rfi_issue`
  - `FbRfi`: added `rfi_comment?: string` (reviewer's feedback)
  - `FbRfiDraft`: added `rfi_comment?: string`
  - `FbActivityInfo`: added `activity_time?: string`, `activity_user?: string`
  - `FbActivity`: added `review_comment?: string`
  - `ClarificationItem`: added `agent_feedback?: string`, `send_back_options?: ClarificationResponseOption[]`, `original_response_options?: ClarificationResponseOption[]`
  - `ClarificationThreadEntry`: added `actor_id?: string`
  - `rfi_status` now uses `"issued"` or `"responded"` (not compound `"issued|responded"`)

- **Review queue styling** — Send Back card styling now matches Review view for consistency
- **Default form config** — Added default response types and sets for standard form workflows

### Fixed

- **Correct hazo_app_config table column names** — Config API now uses correct column names for response types/sets storage
- **Boolean field rendering** — Response type editor now uses select dropdowns (Yes/No) for boolean columns with correct column type mappings
- **Files_mandatory option** — Response types now support optional `files_mandatory` flag for validation rules

### Notes

- **Migration recommended**: Existing form data will continue to work, but new instances will use the hierarchical JSON data model. No schema changes required.
- **Response types configuration**: Update `hazo_collab_forms_config.ini` to configure default response types if needed. See SETUP_CHECKLIST.md for details.

## [3.0.19] - 2026-04-04

### Added

- **Hierarchical data model foundation** — New `flat_to_data` adapter combining all form instances into single `data[]` array with activity tracking
- **JSON viewer** — Interactive panel for inspecting form data model with expand/collapse controls
- **Clarification response flow** — Complete workflow for responding to back-office send-back items with thread history

## [3.0.18] - 2026-04-02

### Fixed

- **Validation clarification responses lost on refresh** — `update_clarification_status` and `batch_update_clarification_responses` now upsert draft clarifications into `sent_clarifications` when the item doesn't already exist there. Previously, validation-generated clarifications (which only existed in `draft_clarifications`) were silently skipped by the `.map()`, so client responses were never persisted to the instance.
- **Clarification response matching by file reference** — `use_fb_form_state` enrichment now matches sent clarification responses by file reference (`doc_references[0].file_id`) in addition to ID, rule_id+target, and rule_name+target, improving restore accuracy on page reload.

## [3.0.17] - 2026-04-02

### Added

- **Clarification thread history** — Full conversation timeline (Input → Issue → Client Response → Agent Rejection) shown in clarification cards. Collapsible "Show N previous" accordion with only the latest entry expanded.
- **Review queue thread display** — SendBackItemCard shows full ClarificationThread when available, with FileBar for document references and human-readable response labels.
- **Instance 2 send-back response flow** — Sourced clarifications in new instances show "Action Required" badge, "Response Required" section, and OK button. Status changes to "Responded" only on explicit OK press.
- **Per-instance agent stepper state** — Switching between instances preserves stepper position, review decisions, and send-back state via snapshot save/restore.
- **Review tab for Instance 2 responses** — Responded sent_clarifications with thread history appear in the review queue, enabling agents to review client responses to send-back items.
- **Completed instance clarification history** — "Clarification History" section shown on completed/terminal instances for read-only review.
- **submit_label prop** — ClarificationResponseForm, ClarificationSection, ClarificationCard, ClarificationGroupCard, and ClarificationItemBody accept custom submit button label.
- **Scenario test harness** — New test page at `/fb-form/scenarios` with mock classify/validate API routes and step-by-step scenario runner for the Date Check Send-Back flow.

### Changed

- **"Conversation History" → "History"** — Thread section renamed for brevity.
- **Thread response labels** — Response choice values now resolve to human-readable labels via `response_options` lookup.
- **Clarification card badge** — Pending items with thread show "Action Required" (amber) instead of no badge.
- **Response section label** — Pending items with thread show "Response Required" instead of "Your Response".
- **Send Back view** — Initializes as read-only ("Sent to Client") for completed instances.
- **Summary & Review** — Blocks submit when sourced clarifications in `front_form_data.__clarifications` have pending status.

### Fixed

- **Stale response data in Instance 2** — `prepare_for_client` now passes cleared items directly to `on_prepare_for_client` callback (3rd argument), bypassing React state batching race conditions.
- **pending_clarification_responses pollution** — Forwarded clarification IDs are cleared from the pending responses map, preventing Instance 1 responses from appearing in Instance 2.
- **Response status on selection change** — Sourced clarifications no longer auto-set status to "responded" when a radio option is selected; status only changes on explicit OK/submit.
- **Client response thread persistence** — `handle_sourced_respond` appends client response entry to thread in both `sent_clarifications` and `front_form_data.__clarifications`.
- **Thread label in SendBackItemCard** — Client response shows human-readable option label instead of raw value.

## [3.0.16] - 2026-03-28

### Fixed

- **Dialog variant type error** — Changed `variant="danger"` to `variant="destructive"` in validation rule editor delete dialog (matching `HazoUiDialog` `DialogVariant` type).
- **test-app: hazo_notes/hazo_ui not transpiled** — Added `hazo_notes` and `hazo_ui` to `transpilePackages` in `test-app/next.config.js`, fixing "Failed to read source code" errors.

## [3.0.15] - 2026-03-27

### Changed

- **BREAKING: `hazo_ui` moved to peer dependency** — Previously a direct dependency, now a required peer dependency (`>=2.7.0`). Consumers must install `hazo_ui` explicitly. This aligns with workspace standards where all hazo packages are peer dependencies.
- **shadcn UI components now sourced from `hazo_ui`** — All 14 `@/components/ui/*` path alias imports (Popover, Calendar, Button, Command, Accordion, HoverCard, DropdownMenu, Dialog, Checkbox) replaced with imports from `hazo_ui`. Consumers no longer need to install shadcn components locally via `npx shadcn@latest add`.
- **Removed duplicate devDependencies** — `hazo_config`, `hazo_ihelp`, `hazo_llm_api`, `hazo_logs`, `hazo_notes`, `lucide-react` removed from devDependencies (workspace handles resolution).

### Fixed

- **Hardcoded MIME type in autofill re-run** — `back_office_view.tsx` used `'application/pdf'` for all files; now uses `infer_mime_type()` to correctly handle non-PDF documents.
- **Debug console.log statements** — Removed 7 `console.log` calls from `front_office_view.tsx` production code.
- **Accessibility: missing focus ring** — Added `focus-visible:ring-2` to `role="button"` group headers in `back_office_view.tsx`.
- **Performance: tag_lookup rebuilt every render** — Memoized `tag_lookup` Map in `draft_clarification_card.tsx` with `useMemo`.

### Added

- **Config sample file** — `config/hazo_collab_forms_config.ini.sample` with documented defaults.
- **AGENTS.md files** — Added `src/AGENTS.md`, `src/components/AGENTS.md`, `test-app/AGENTS.md` per workspace standards.
- **Architecture doc** — `design/architecture.md` documenting component hierarchy, data flow, and entry points.

## [3.0.8] - 2026-03-26

### Fixed

- **Clarification file bar**: Add view (eye) button to referenced document file bars — `on_doc_view` now flows through `ClarificationSection` to `ClarificationCard`/`ClarificationGroupCard` and opens the PDF side panel
- **Clarification file bar width**: Changed from full-width to 50% (`max-w-[50%]`) for better layout
- **ClarificationCard**: Simplified layout — removed redundant header row, delegated fully to `ClarificationItemBody`
- **File validation issues dialog**: Made description `sr-only` for cleaner visual presentation
- **Back office view**: Various workflow improvements

## [3.0.0] - 2026-03-25

### Breaking Changes

- **Dependency restructuring**: Moved `hazo_auth`, `hazo_config`, `hazo_files`, `hazo_notes`, `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/utilities`, `react-resizable-panels` from `dependencies` to `peerDependencies` (all optional). Consumers must now install these explicitly if used. Only `clsx`, `hazo_ui`, `server-only`, and `tailwind-merge` remain as direct dependencies.

### Added

- **HazoFbForm**: New front/back office form component with LLM classification pipeline
  - Multi-instance mode with sidebar instance switching
  - Front office (file upload + stepper), back office (classified data + autofill), clarifications view, interim view, summary/review view
  - LLM-powered file classification with batch support, validation rules, and auto-routing to back-office groups
  - Clarification lifecycle: draft → send → respond → resolve/dismiss, with response file routing
  - Per-instance state isolation (validation results, clarifications, drafts)
  - Components: `HazoFbForm`, `RunButton`, `BackofficeRunButton`, `FrontOfficeStepper`, `TagPill`, `TagPillList`, `FbTagEditor`, `FbDocumentTypeEditor`
  - Views: `FrontOfficeView`, `BackOfficeView`, `InterimView`, `SummaryReviewView`
  - Server route: `create_fb_form_route` (from `hazo_collab_forms/lib`)
  - Types: `HazoFbFormProps`, `FbFormInstance`, `FbClassificationResult`, `FbFileClassification`, `FbRunResult`, `FbRunProgress`, `FbRunMode`, `FbRunLogEntry`, `FbGroupAutofillLog`, `FbAutofillDetail`, `FbFileValidationResult`, `FbUserRole`, `FbTabId`, `FbFormSubmitData`, etc.
- **HazoCollabFormFileTextbox**: New rich file+text field component
  - Block-based content model (text blocks + file blocks)
  - File classification tags, validation status indicators
  - Components: `FileTextboxPanel`, `FileChip`, `FileGroup`, `FileUploadZone`, `TagPills`, `ValidationDialog`, `FileBar`, `FileBarValidation`, `FileBarTag`
  - Types: `FileTextboxBlock`, `FileTextboxValue`, `FileTextboxEntry`
  - Utilities: `normalize_file_textbox_value`, `blocks_to_entries`, `entries_to_blocks`
- **FileBar**: Shared file bar component with validation badges, tag pills, and clickable detail dialog
- **Clarification status config**: `STATUS_CONFIG`, `STATUS_BADGE_STYLES`, `get_worst_status`, `get_issue_text_class`, `get_issue_detail_text_class`
- **Clarification sub-components**: `ClarificationGroupCard`, `ClarificationItemBody` exported for custom clarification UIs
- **Clarification types**: `ClarificationReviewDecision`, `ResponseValidationState`
- **FileTextboxOptions**: Options sub-component for file_textbox field configuration in add-field dialog
- **FbFormInstance type**: Multi-instance state type with status lifecycle (`draft` → `active` → `sent_to_client` → `completed`)
- **Comprehensive pipeline logging**: `use_logger()` integration in `HazoFbForm`, `use_llm_run`, `use_fb_form_state` for debugging classification, validation, routing, and autofill pipeline
- **Section helpers**: `get_section_field_ids`, section traversal utilities
- **Dev file manager**: `create_dev_file_manager` utility for local development
- **Rule execution mapping**: `rule_to_fb_execution` utility for converting validation rules to FB execution format
- **`default_file_accordion_open` prop**: Cascade control for file upload accordion default state (field > group > section > form)

### Fixed

- **Missing exports**: Added `BackofficeRunButton`, `FrontOfficeStepper`, `FileTextboxOptions`, `FbFileClassification`, `FbRunMode`, `FbGroupAutofillLog`, `FbAutofillDetail`, `FbRunLogEntry` to public API
- **hazo_chat duplicate**: Removed from devDependencies (already in peerDependencies)
- **Clarification response validation**: Merge response validation `rule_results` with normal validation results

### Changed

- **Validation rules**: Added `check_type` field (`'immediate' | 'backoffice'`) for controlling when rules execute in the FB Form pipeline
- **Clarification templates**: Updated with response validation support

## [2.2.8] - 2026-03-11

### Fixed

- **Field Library template import**: Removed client-generated `id` from INSERT payload in `create_element`. The `generate_id()` function produced non-UUID IDs (`elt_*`) incompatible with the PostgreSQL `uuid` column (`DEFAULT gen_random_uuid()`), causing `400 Bad Request` errors when importing fields from templates or JSON.

## [2.0.5] - 2026-03-05

### Added

- **Collapsible groups (HazoDataForm)**: New `collapsible_groups`, `collapsed_groups`, and `on_group_toggle` props for form-level group collapse. Per-group overrides via `collapsible` and `initial_collapsed` on `ViewFieldConfig`. Groups with headers show a chevron toggle; ghost form panel mirrors collapse state.
- **TableReferenceAccordion**: New component for rendering array-type reference data below table fields. Displays as a collapsible accordion with prior year rows, column-aware formatting, and automatic subtotals. Exported from `hazo_collab_forms` for standalone use.
- **Array reference values**: `ReferenceValueData.value` now accepts `Record<string, unknown>[]` for table field references. Summary view skips inline reference tags for array values (handled by accordion instead).

## [2.0.3] - 2026-03-05

### Added

- **Data Route Factory**: `create_data_route` (from `hazo_collab_forms/lib`)
  - Backend-agnostic CRUD route factory. Replaces boilerplate REST endpoints (~200 lines) with a single function call.
  - Supports GET, POST, PATCH, DELETE with the standard hazo_connect request format.
  - Optional `allowed_tables` whitelist for security.
  - Works with any hazo_connect backend (SQLite, PostgREST, Supabase).
  - Types exported: `DataRouteOptions` (from `hazo_collab_forms/lib`), `HazoConnectAdapterLike` (from `hazo_collab_forms` and `hazo_collab_forms/types`).
- **hazo_connect_adapter option**: `create_validation_route` and `create_autofill_route` now accept `hazo_connect_adapter` for backend-agnostic prompt lookup (`sqlite_path` deprecated).
- **hazo_connect peer dependency**: Added as optional peer dependency (`>=2.4.0`).
- **HazoDataForm sidebar PDF panel**: New `file_panel_display_mode="sidebar"` mode embeds a `PdfPanel` alongside the form, aligned vertically with the active field using a ghost form layout. New props: `pdf_panel_default_size`, `pdf_panel_min_size`, `pdf_viewer_props`, `on_panel_open`, `on_panel_close`, `on_panel_pop_out`, `file_access_provider`, `on_panel_upload`, `on_panel_file_delete`.
- **PdfPanel component**: Exported standalone (`import { PdfPanel } from 'hazo_collab_forms'`). Dynamically loads `PdfViewer` from optional peer `hazo_pdf`.
- **Clarification click always enabled**: `on_clarification_click` is now always set (not gated on `get_pending_count > 0`) so the click handler fires even before counts load.
- **hazo_pdf optional peer dependency**: Declared in `peerDependenciesMeta` as optional (required only when using `file_panel_display_mode="sidebar"`).

## [2.0.2] - 2026-03-04

### Changed

- **Validation Rule ID separation**: `ValidationRule` now has a separate `rule_id` (human-friendly slug) and `id` (UUID auto-generated by DB). Field-level `validation_rules` arrays reference `rule_id` values. The `is_id_unique()` method on `use_validation_rules` is renamed to `is_rule_id_unique()`.
- **File icon simplification**: Simplified file type icons to PDF vs generic document across all views (file upload, doc view, summary). All icons now use sky-500 color.
- **File manager button colors**: Changed from green to sky color scheme for consistency.
- **Clarification response options**: Added drag-and-drop reordering for response options in the clarification settings editor.

### Migration

**Database schema**: If you have an existing `hazo_collab_form_validations` table, run:

```sql
-- SQLite
ALTER TABLE hazo_collab_form_validations ADD COLUMN rule_id TEXT NOT NULL DEFAULT '';
CREATE UNIQUE INDEX IF NOT EXISTS idx_validations_rule_id ON hazo_collab_form_validations(rule_id);
-- Then populate rule_id from existing id values:
UPDATE hazo_collab_form_validations SET rule_id = id WHERE rule_id = '';

-- PostgreSQL
ALTER TABLE hazo_collab_form_validations ADD COLUMN rule_id TEXT NOT NULL DEFAULT '';
CREATE UNIQUE INDEX IF NOT EXISTS idx_validations_rule_id ON hazo_collab_form_validations(rule_id);
UPDATE hazo_collab_form_validations SET rule_id = id WHERE rule_id = '';
```

## [2.0.0] - 2026-03-04

### Added

- **Clarification System**: New document validation and follow-up workflow
  - `ClarificationCard` - displays a single clarification with issue description, doc references, and response form
  - `ClarificationSection` - renders all clarifications for a form section
  - `ClarificationBadge` - count badge for pending clarifications
  - `ClarificationDocReferenceList` - renders referenced documents in a clarification
  - `ClarificationResponseForm` - structured response UI (radio options + file upload + comment)
  - `ClarificationDialog` - modal wrapper for clarification detail view
  - `use_clarifications` hook - manages clarification state with `get_clarifications` helper
  - `CLARIFICATIONS_KEY` constant for form_data storage key
  - Types: `ClarificationType`, `ClarificationStatus`, `ClarificationDocReference`, `ClarificationResponseOption`, `ClarificationItem`, `ClarificationTemplate`, `ClarificationResponse`, `ClarificationCallbacks`, `ClarificationCounts`
  - Templates: `DEFAULT_CLARIFICATION_TEMPLATES`, `INVALID_DOCUMENT_TEMPLATE`, `ADDITIONAL_INFO_REQUIRED_TEMPLATE` (now exported from `'.'` client entry)

- **Validation Rule Editor**: New UI for managing LLM-based document validation rules
  - `HazoValidationRuleEditor` - full-featured editor with rule list and rule detail panel
  - `ValidationRuleEditorProvider` / `useValidationRuleEditor` - context and hook
  - `use_validation_rules` hook - CRUD operations for validation rules via API
  - `use_file_validation` hook - triggers validation for an uploaded file against applicable rules
  - `ValidationPromptEditor` (`PromptEditor`) - rich prompt editor with slash commands and @variables
  - `ValidationClarificationSettings` (`ClarificationSettings`) - clarification type and response options editor
  - Types: `ValidationRule`, `ValidationRuleRecord`, `PromptCommand`, `PromptVariable`, `PromptEditorConfig`, `ValidationRuleExecution`, `ValidationRequest`, `ValidationRuleResult`, `ValidationResponse`, `ValidationRouteOptions`, `UseFileValidationOptions`, `UseFileValidationReturn`
  - Constants: `DEFAULT_PROMPT_COMMANDS`, `DEFAULT_PROMPT_VARIABLES`, `DEFAULT_PROMPT_EDITOR_CONFIG` (now exported from `'.'` client entry)

- **Server-Side Validation Route Handler**: `create_validation_route` (from `hazo_collab_forms/lib`)
  - Executes validation rules against uploaded documents via LLM
  - Returns `ClarificationItem` array for any rules that find issues
  - Supports both `file_b64` inline data and `download_url` fetch
  - Configurable prompt suffix via `[validation] prompt_suffix` INI setting
  - Types exported: `ValidationRouteOptions`, `ValidationRequest`, `ValidationResponse`, `ValidationRuleExecution`, `ValidationRuleResult`

- **Slugify Utility**: `slugify` function exported from `hazo_collab_forms/utils`

- **Rule Field Map Utility**: `build_rule_field_map` utility and `RuleFieldMapping` type exported from `hazo_collab_forms/utils`

- **New package.json export paths**:
  - `hazo_collab_forms/types` - direct access to type-only exports
  - `hazo_collab_forms/config` - direct access to configuration constants

### Changed

- **Config Constants Promoted to Client Entry**: `DEFAULT_CLARIFICATION_TEMPLATES`, `INVALID_DOCUMENT_TEMPLATE`, `ADDITIONAL_INFO_REQUIRED_TEMPLATE`, `DEFAULT_PROMPT_COMMANDS`, `DEFAULT_PROMPT_VARIABLES`, `DEFAULT_PROMPT_EDITOR_CONFIG` are now accessible from the main `'.'` (client) entry point in addition to `./lib`

### Breaking Changes

This is a major version release. No previously-stable public APIs have been removed, but the version signals significant new capability additions.

## [1.9.2] - 2026-02-25

### Changed

- **File Upload Split**: `accept_files` prop split into `accept_files_public` and `accept_files_private` for independent control of public and private file sections

### Fixed

- **File Manager Prop Flow**: Fixed `file_manager` not being passed through `use_base_form_field` hook to CollabFormFileUploadSection
- **File Icon Crash**: Fixed `get_file_icon` crashing on undefined `mime_type` with defensive null handling

## [1.9.1] - 2026-02-25

### Added

- **hazo_files Integration**: File upload system now uses FileManagerCallbacks for improved architecture
  - New `FileManagerConfig` type with callback-based architecture
  - `file_manager` prop on HazoCollabFormView for centralized file operations
  - New `FormFileAttachment` type replacing legacy FileData in file operations
  - New `FileStatus` type: 'active', 'orphaned', 'soft_deleted', 'missing'
  - New utilities: `use_file_status` hook for polling file status
  - Helper functions: `get_field_attachments`, `get_field_private_attachments`, `is_form_file_attachment`, `attachment_to_file_data`, `file_data_to_attachment`
  - Types exported: `FileManagerConfig`, `FileManagerCallbacks`, `FileUploadOptions`, `FormFileAttachment`, `FileStatus`

### Changed

- **File Upload Architecture**: CollabFormFileUploadSection now requires `file_manager` prop to display file upload UI
  - File upload no longer makes direct API calls
  - Uses `file_manager.callbacks` for upload, remove, get_download_url, get_status, reupload operations
  - Returns empty div when `file_manager` is not provided

## [1.8.14] - 2026-02-02

### Added

- **DOM Props Filter Utility**: New utility to prevent internal props from leaking to DOM elements
  - Added `filter_dom_props()` - splits props into DOM-safe and internal
  - Added `get_dom_safe_props()` - returns only DOM-safe props
  - Filters by prefix (`hazo_chat_*`, `hazo_notes_*`) and specific internal prop names
  - Location: `src/utils/filter_dom_props.ts`
  - Exported from `hazo_collab_forms/utils`

### Fixed

- **React DOM Warnings**: Prevented internal configuration props from being spread onto native HTML elements
  - Components now filter props before spreading to input/textarea elements
  - Eliminates console warnings: "Received `true` for a non-boolean attribute `hazo_chat_hide_references`"
  - Applied to 4 components: inputbox, textarea, checkbox, radio
  - Location: `src/components/hazo_collab_form_inputbox.tsx`, `hazo_collab_form_textarea.tsx`, `hazo_collab_form_checkbox.tsx`, `hazo_collab_form_radio.tsx`

## [1.8.11] - 2026-02-02

### Fixed

- **icons_behaviour Role Resolution**: Fixed TypeError when using `iconsBehaviour` with undefined `activeRole` or missing role configurations
  - Added optional chaining (`?.`) to all control property accesses across components
  - Components now gracefully handle cases where:
    - `activeRole` is undefined
    - `activeRole` doesn't match any role in `iconsBehaviour.roles`
    - Specific controls are not defined for a role
  - Affected files:
    - `src/components/hazo_collab_form_set.tsx` - Field and group level control visibility/enabled checks
    - `src/components/hazo_collab_form_summary/summary_group.tsx` - Group enabled controls and data_ok visibility
    - `src/components/hazo_collab_form_view/views/summary_view.tsx` - Group and field row color checks
  - Pattern changed from `role_controls?.control_XXX.enabled` to `role_controls?.control_XXX?.enabled`
  - Issue reported by consuming apps when passing undefined roles
  - Location: Multiple files listed above

- **Missing control_edit_elements**: Fixed TypeError in UnifiedFieldControls kebab menu
  - Added `control_edit_elements` to the `all_controls` array in `use_control_resolution.ts`
  - Previously missing from control resolution loop, causing undefined control state
  - Error: "Cannot read properties of undefined (reading 'enabled')" when kebab menu accessed edit elements
  - Location: `src/components/shared/unified_field_controls/use_control_resolution.ts`

## [1.8.9] - 2026-02-01

### Added

- **Reference Data Display**: Show comparison values (e.g., prior year tax data) below form fields
  - New `reference_data` prop on HazoCollabFormView accepts `Record<field_id, ReferenceValueData>`
  - Auto-formats currency (numbers >= 100), booleans ("Yes"/"No"), strings (pass-through)
  - Field-level props (`reference_value`, `reference_label`, `reference_tag_background_color`) take precedence
  - Supported in all view modes: edit, summary, print, approval
  - Types exported: `ReferenceValueData`, `ReferenceData`
  - Location: `src/components/hazo_collab_form_view/`, `src/components/hazo_collab_form_base.tsx`, `src/components/hazo_collab_form_set.tsx`
  - Demo page: `test-app/app/reference-data-demo/`

- **Shared Button Component**: Created centralized Button component
  - Location: `src/components/shared/button/`
  - Shadcn-style button with variants: `default`, `outline`, `destructive`, `ghost`, `link`
  - Sizes: `default`, `sm`, `lg`, `icon`
  - Ensures consistent button styling across all dialogs

### Changed

- **Dialog Component Standardization**: Centralized to `shared/dialog/` for consistency
  - All dialog components (HazoAddFieldDialog, HazoAddGroupDialog, HazoEditFieldDialog, HazoFieldSelectorDialog) now use `src/components/shared/dialog/`
  - Ensures consistent portal/overlay behavior across all dialogs
  - DialogFooter default alignment changed to `sm:justify-end` (right-aligned button groups)
  - Consumers can still customize DialogHeader, DialogFooter, DialogTitle, DialogDescription via props
  - Built-in Dialog and DialogContent always used (consumer overrides ignored)
  - Deleted duplicate dialog component: `src/components/hazo_template_generator/components/ui/dialog.tsx`
  - HazoFieldSelectorDialog simplified: removed dynamic component loading, now uses centralized Dialog/Button

### Breaking Changes

- **Control Name Rename**: `control_edit_fields` → `control_add_elements`
  - Affects `icons_behaviour` configurations (icon_order, border_on, always_extracted, role controls)
  - **Migration Required**: Update JSON configs to use `control_add_elements` instead of `control_edit_fields`
  - Prop names unchanged: `enable_edit_fields`, `on_add_field` remain the same for backward compatibility
  - Rationale: Better reflects the control's purpose (adding new elements, not editing existing ones)
  - Files affected: All `icons_behaviour` type definitions, constants, and test data

  **Migration Example**:
  ```diff
  {
    "icon_order": [
      "control_data_ok",
  -   "control_edit_fields",
  +   "control_add_elements",
    ],
    "roles": [{
      "controls": {
  -     "control_edit_fields": { "visible": true, "enabled": true }
  +     "control_add_elements": { "visible": true, "enabled": true }
      }
    }]
  }
  ```

## [1.8.8] - 2026-01-30

### Fixed

- **File Upload field_id**: File uploads now include `field_id` in the FormData request
  - Previously uploads sent: `file`, `files_dir`, `visibility`, `required_permission`
  - Now uploads also include: `field_id` - the ID of the form field the file belongs to
  - Allows server-side APIs to associate uploaded files with their corresponding form fields
  - Location: `src/components/collab_form_file_upload.tsx`

## [1.8.6] - 2026-01-27

### Changed

- **Dependency Upgrades**: Updated hazo ecosystem packages to latest versions
  - `hazo_auth`: ^5.1.9 → ^5.1.24
  - `hazo_config`: ^1.0.0 → ^1.4.2
  - `hazo_chat`: ^4.0.7 → ^4.0.10
  - `hazo_logs`: ^1.0.0 → ^1.0.10 (peer dependency)

- **hazo_config Import Path**: Updated import from `hazo_config` to `hazo_config/server`
  - Affects: `src/lib/config.ts`
  - Migration: If you import `HazoConfig` directly, update to `import { HazoConfig } from 'hazo_config/server'`

## [1.8.5] - 2026-01-27

### Added

- **Data OK Hidden States**: Control which states are available for user selection in multi-state mode
  - New prop `data_ok_hidden_states?: DataOkState[]` on DataOkMultiState component
  - Hidden states still display if current value but cannot be selected via click-cycle or dropdown
  - Cascades from form level to field level (HazoCollabFormSet and HazoCollabFormSummary)
  - Field-level override via `data_ok_hidden_states` in FieldConfig
  - Cascade priority: field-level > form-level > [] (all visible)
  - Use case: Tax agent workflow where agents only select OK/Not OK, never Pending
  - Location: `src/components/data_ok_multi_state.tsx`, `src/components/hazo_collab_form_set.tsx`, `src/components/hazo_collab_form_summary/`

- **HazoCollabFormSet Unified Metadata Sync**: New centralized field metadata management for external control
  - `field_metadata` prop: Controlled sync for all field metadata (visibility, locked, data_ok, deleted)
  - `on_field_metadata_change` callback: Unified handler for all metadata changes (called IN ADDITION to specific callbacks)
  - `FieldMetadataChange` interface: Type for metadata change events
  - `FieldMetadataInput` interface: Consumer-friendly metadata format (supports both `visibility`/`hidden` and `data_ok`/`dataOk` naming)
  - `normalize_field_metadata_input()` function: Normalize consumer formats to canonical format
  - `DataOkState` type: `'ok' | 'not_ok' | 'pending'` for multi-state data OK
  - Solves sync problem when metadata changes externally (no more key-based remounting required)
  - Parent components can control field states without losing scroll position, focus, or animation state
  - Location: `src/components/hazo_collab_form_set.tsx`

- **Group-Level Control Propagation (Inheritance)**: Groups can now set `enable_*` props that automatically cascade to all child fields
  - Cascade priority: field-level > parent-group > form-level > false
  - Supports all 6 control types: `enable_lock`, `enable_visibility_toggle`, `enable_delete`, `enable_data_ok`, `enable_notes`, `enable_chat`
  - New `resolve_field_controls()` utility function exported for manual resolution
  - New `FieldControlSettings` type exported
  - Added `enable_chat` property to `FieldConfig` interface
  - Location: `src/components/hazo_collab_form_summary/utils.ts`

- **HazoCollabFormSummary Inheritance Support**: Summary component now respects group-level control inheritance
  - Child fields inherit `enable_*` settings from parent groups
  - Individual fields can still override inherited settings
  - Works with both inline and popover control display modes

### Fixed

- **Summary View Field-Level Files Not Displayed**: Fixed files not appearing in summary mode for fields with `accept_files: true`
  - Root cause: `SummaryField` component in `summary_view.tsx` was not rendering field-level files
  - Solution: Added file reading and `SummaryFiles` rendering to `SummaryField`, matching the pattern used in `SummaryGroup`
  - Added `accept_files` property to `ViewFieldConfig` interface
  - Location: `src/components/hazo_collab_form_view/views/summary_view.tsx`, `src/components/hazo_collab_form_view/types.ts`

- **Kebab Menu Popover Positioning**: Fixed kebab menu (3-dots) appearing displaced from trigger button
  - Root cause: Manual `getBoundingClientRect()` + `fixed` positioning breaks with parent CSS transforms or scrollable containers
  - Solution: Replaced with Radix Popover which uses Floating UI for transform-aware, scroll-aware positioning
  - Added `@radix-ui/react-popover` as peer dependency
  - Location: `src/components/shared/unified_field_controls/kebab_menu.tsx`

- **enable_chat inheritance**: Fixed issue where `enable_chat` set at group level was not working
  - `chat_config` is now created whenever `chat_group_id` is provided (not dependent on form-level `enable_chat`)
  - Allows groups to enable chat for their children without requiring form-level `enable_chat=true`

- **Add Field Dialog Portal/Overlay Issue**: Fixed dialog appearing inline (side-by-side with form) instead of as modal overlay
  - Root cause: Consumer-provided `dialog_components` (Dialog/DialogContent) may not include proper Radix Portal
  - Solution: Always use built-in Dialog and DialogContent to ensure proper portal/overlay behavior
  - Only DialogHeader, DialogFooter, DialogTitle, DialogDescription can now be customized via `dialog_components`
  - Dialog and DialogContent props in `DialogComponents` interface are now deprecated (ignored)
  - Location: `src/components/hazo_add_field_dialog/`, `src/components/hazo_add_group_dialog/`, `src/components/hazo_edit_field_dialog/`

- **Kebab Menu Notes Not Opening**: Fixed clicking "Notes" in kebab menu not opening the notes panel
  - Root cause: `CollabFormNotesIcon` was wrapped in a Radix Popover, but `HazoNotesIcon` (from hazo_notes) has its own internal popover, creating nested popovers that interfere with each other
  - Solution: Render `CollabFormNotesIcon` directly in a div container (like Chat) instead of wrapping in Popover
  - Location: `src/components/shared/unified_field_controls/kebab_menu.tsx`

### Changed

- **summary_field.tsx**: Updated to use `resolve_field_controls()` for inheritance cascade
- **summary_group.tsx**: Now always passes parent group to child fields for inheritance resolution
- **utils.ts**: Added `FieldControlSettings` interface and `resolve_field_controls()` function
- **index.tsx**: Exports `resolve_field_controls` and `FieldControlSettings` type; fixed chat_config creation logic

## [1.5.3] - 2026-01-07

### Added

- **HazoCollabFormSummary Component**: New read-only summary view component for displaying form data
  - Composable design with individual feature toggles (NO predefined modes)
  - Display form sections, fields, groups, and DataTables in read-only format
  - Optional section icons with custom rendering via `render_icon` callback
  - Optional edit navigation buttons per section
  - Optional field-level controls: data_ok, notes, chat, lock, visibility, delete, needs_correction
  - Row-level controls on DataTables
  - Two control display modes: `inline` (horizontal) or `popover` (dropdown)
  - Data OK highlighting with customizable row colors (green/red/orange)
  - Auto-actions: auto-hide or auto-lock fields when data_ok is set
  - Ref methods: `get_form_data()`, `get_field_metadata()`, `scroll_to_section()`, `scroll_to_field()`
  - Sub-components exported for custom composition: `SummarySection`, `SummaryGroup`, `SummaryField`, `SummaryDataTable`, `SummaryFiles`, `FieldControlsInline`, `FieldControlsPopover`
  - New types: `FormSection`, `FieldMetadata`, `HazoCollabFormSummaryProps`, `HazoCollabFormSummaryRef`
  - Location: `src/components/hazo_collab_form_summary/`
  - Demo pages: `test-app/app/form-summary/`

- **hazo_notes Integration**: Package now integrates with hazo_notes for field-level notes functionality
  - `CollabFormNotesIcon` component dynamically loads `HazoNotesIcon` from hazo_notes package
  - Graceful fallback if hazo_notes is not installed (returns null, no errors)
  - Dynamic loading pattern for Popover components from consuming app
  - Re-exports `NoteEntry` type from hazo_notes
  - hazo_notes added as optional peer dependency in package.json
  - Notes data persists to database via hazo_notes instead of client-side only
  - User attribution and profile integration via hazo_auth

- **DataOkMultiState Component Improvements**:
  - Added explicit color styling using inline styles for reliability
  - OK state: green (#10b981), Not OK state: red (#f87171), Pending state: amber (#f59e0b)
  - Matches DataOkCheckbox color scheme for consistency

- **Test App Configuration Changes**:
  - Config files relocated from `test-app/` root to `test-app/config/` subdirectory
  - Files moved: `hazo_auth_config.ini`, `hazo_chat_config.ini`, `hazo_logs_config.ini`, `hazo_notify_config.ini`
  - New config file: `test-app/config/summary_views.tsx` for summary view configurations
  - Removed obsolete `test-app/package-lock.json` (now uses workspace)

### Changed

- **Base Component Updates**:
  - `hazo_collab_form_base.tsx` - Integrated hazo_notes, updated notes icon rendering
  - Import `NoteEntry` and `HazoNotesIconProps` from hazo_notes
  - `CollabFormNotesIcon` now uses dynamic import for hazo_notes package

- **Component Exports**:
  - Added `HazoCollabFormSummary` and related types to `src/components/index.ts`
  - Added `DataOkMultiState`, `boolean_to_data_ok_state`, `data_ok_state_to_boolean`, `get_default_state_config` exports

- **Package Version**: Bumped to 1.5.3

- **Test App Updates**:
  - Updated sidebar navigation with new summary view links
  - Added form-summary demo page at `test-app/app/form-summary/[mode]/page.tsx`
  - Updated app layout for better navigation structure

### Deprecated

**NOTE**: The following entries from previous unreleased section are now OBSOLETE and superseded by version 1.5.0-1.5.2 releases:

- ~~Advanced Field Control Icons~~ - Released in 1.5.0
- ~~File Validator Callback~~ - Released in 1.5.0
- ~~Mandatory File Indicator~~ - Released in 1.5.0
- ~~Form Set File Properties~~ - Released in 1.5.0

## [1.3.4] - 2025-12-28

### Fixed
- Fixed date component issues

## [1.3.3] - 2025-12-XX

### Fixed
- Fixed table-related issues

## [1.3.2] - 2025-12-XX

### Changed
- Version maintenance update

---

## Migration Notes

### Upgrading to 1.5.3

**No Breaking Changes**: All new features are backward compatible.

**Using HazoCollabFormSummary:**

Basic usage for displaying form data:
```typescript
import { HazoCollabFormSummary, type FormSection } from 'hazo_collab_forms';

const sections: FormSection[] = [
  {
    group_name: "Personal Details",
    icon: "LuUser",  // Optional icon identifier
    field_list: [
      { id: "name", label: "Name", component_type: "HazoCollabFormInputbox", value: "" },
      { id: "email", label: "Email", component_type: "HazoCollabFormInputbox", value: "" },
    ]
  }
];

<HazoCollabFormSummary
  sections={sections}
  form_data={{ name: "John Doe", email: "john@example.com" }}
  render_icon={(name) => iconMap[name]}
/>
```

Reviewer mode with full controls:
```typescript
<HazoCollabFormSummary
  sections={sections}
  form_data={formData}
  field_metadata={metadata}

  // Enable controls
  enable_data_ok={true}
  data_ok_mode="multi_state"
  data_ok_editable={true}
  enable_notes={true}
  enable_chat={true}
  chat_group_id="review-session-123"
  user_role="author"

  // Edit navigation
  show_edit_buttons={true}
  on_edit_section={(index) => router.push(`/edit/${index}`)}

  // Callbacks
  on_data_ok_change={handleDataOkChange}
  on_notes_change={handleNotesChange}
/>
```

**Using hazo_notes Integration:**

Install the hazo_notes package (optional):
```bash
npm install hazo_notes
```

Notes will automatically appear on form fields when enabled. If hazo_notes is not installed, notes functionality will be gracefully disabled with no errors.

**Config File Relocation (Test App Only):**

If you're using the test-app as a reference, note that config files have moved:
- Old: `test-app/hazo_*_config.ini`
- New: `test-app/config/hazo_*_config.ini`

This change only affects the test application, not the package itself.

---

## Release History

- **1.8.6** (2026-01-27) - Dependency upgrades (hazo_auth, hazo_config, hazo_chat, hazo_logs)
- **1.8.5** (2026-01-27) - Data OK hidden states, field metadata sync, group-level control inheritance, bug fixes
- **1.5.3** (2026-01-07) - HazoCollabFormSummary component, hazo_notes integration, DataOkMultiState improvements
- **1.5.2** - Field consolidation and expansion updates
- **1.5.1** - Documentation updates
- **1.5.0** - Hidden and lockable fields, visibility controls
- **1.4.7** - Data OK updates
- **1.4.6** - Data OK field enhancements
- **1.3.4** (2025-12-28) - Date component fixes
- **1.3.3** - Table issue fixes
- **1.3.2** - Maintenance update
