# If the Export tab still crashes — how to pin the exact control in 2 minutes

The crash is a macOS SwiftUI **layout oscillation**, not a logic bug. The
console line that confirms it:

> The window has been marked as needing another Update Constraints in Window
> pass, but it has already had more Update Constraints passes than there are
> views in the window.

…followed by `min == max` fractional-size complaints from
`AppKitProgressView`, `PlatformTextFieldAdaptor`, `AppKitStepper`, etc. Those
adaptor names tell you which *kind* of control is unstable.

This build already replaced the most likely trigger (the vertical-growing
`TextField` entry-message field) with a fixed-height `TextEditor`, pinned the
currency fields to a fixed width, and removed the spinner placeholder. If it
STILL crashes, bisect the Form to find the remaining offender.

## Fast bisection

In `EntryExportView.exportForm`, temporarily comment out sections one at a
time and rebuild. The form is:

```swift
Form {
    entrySettingsSection      // DatePicker + Picker
    feesSection               // currency TextFields (now fixed width)
    entryRulesSection         // Steppers + Toggles
    perEventLimitsSection     // Steppers
    entryMessageSection       // TextEditor (now fixed height)
    validationSection
    exportActionsSection
    summarySection
}
```

Comment out the bottom half first (`entryMessageSection` … `summarySection`).
- If the crash stops → the offender is in that half; re-add one at a time.
- If it persists → it's in the top half (likely a Stepper or the DatePicker).

## Known-good fallbacks for each control type on macOS grouped Forms

- **Stepper**: if Steppers are the cause, wrap the row in an explicit
  `LabeledContent { Stepper("", value:…, in:…) } label: { Text(label) }`
  and/or give the row `.fixedSize(horizontal: false, vertical: true)`.
- **DatePicker**: add `.datePickerStyle(.field)` to force a fixed-size field
  instead of the graphical/stepper styles that can renegotiate.
- **Any text field**: prefer a fixed `.frame(width:)`, never `maxWidth`, and
  add `.textFieldStyle(.roundedBorder)`.
- **Nuclear option** that almost always stops oscillation: replace
  `.formStyle(.grouped)` with a plain `ScrollView { VStack(alignment:.leading,
  spacing:16) { … } .padding() }` and lay out rows manually. You lose the
  grouped chrome but gain a layout engine that cannot loop.

## Why .task (not onAppear) for the config creation

Creating the SwiftData config (`EntrySettings`/`ScoringConfig`/`TimingConfig`)
in `onAppear` mutates the model *during* AppKit's layout commit, which forces a
re-entrant layout pass. `.task` runs on the next main-actor turn, after layout,
so the mutation can't reenter the current pass. Keep it in `.task`.
