Skip to content

refactor: architectural cleanup -- God object, data layer, dispatch#567

Merged
cpcloud merged 12 commits intomainfrom
refactoring
Mar 2, 2026
Merged

refactor: architectural cleanup -- God object, data layer, dispatch#567
cpcloud merged 12 commits intomainfrom
refactoring

Conversation

@cpcloud
Copy link
Copy Markdown
Collaborator

@cpcloud cpcloud commented Mar 2, 2026

Summary

  • Extract formState (13 fields) and extractState (9 fields) from the 56-field Model God object, reducing it to 34 fields
  • Add generic listQuery[T], getByID[T], and checkDependencies helpers in the data layer, deduplicating 14 list methods, 8 get methods, and 4 delete methods
  • Add createOrUpdate helper to deduplicate 9 submit methods in forms
  • Add buildOptions[T] generic to deduplicate 4 option builder functions
  • Replace unscopedPreload lambda duplication (16 copies) with a plain function
  • Unify the 7-block overlay guard chain in Update into a single dispatchOverlay method; simplify 3 handler signatures from (tea.Model, tea.Cmd) to tea.Cmd
  • Extract shared helpers from earlier in the branch: fuzzyMatch/sortFuzzyScored, waitForStream, formDataAs[T], replaceAssistantWithError, computeNaturalWidths, overlayMaxHeight, countStr, idColumnSpec, quoteRowSpec, transformCells
  • Add coverage tests for all new helpers

Net -248 lines of production code, +339 lines of tests.

Closes #568.

Test plan

  • go test -shuffle=on ./... passes (all packages)
  • nix run '.#pre-commit' clean on every commit
  • CI checks pass

@cpcloud cpcloud force-pushed the refactoring branch 3 times, most recently from 064e69e to d562e98 Compare March 2, 2026 13:13
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 2, 2026

Codecov Report

❌ Patch coverage is 89.94490% with 73 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.21%. Comparing base (5cd94dd) to head (e8877d6).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
internal/app/model.go 74.00% 31 Missing and 8 partials ⚠️
internal/app/forms.go 90.90% 11 Missing and 1 partial ⚠️
internal/app/extraction.go 84.00% 6 Missing and 2 partials ⚠️
internal/app/chat.go 84.78% 7 Missing ⚠️
internal/data/store.go 95.00% 4 Missing and 2 partials ⚠️
internal/app/types.go 94.11% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #567      +/-   ##
==========================================
+ Coverage   74.24%   75.21%   +0.97%     
==========================================
  Files          58       60       +2     
  Lines       17902    17575     -327     
==========================================
- Hits        13291    13219      -72     
+ Misses       3912     3710     -202     
+ Partials      699      646      -53     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

cpcloud added 8 commits March 2, 2026 09:47
- countStr: replace 12 inline count-to-string blocks in tables.go
- idColumnSpec: replace 9 identical ID column spec literals
- formDataAs[T]: generic type assertion replacing 10 form data casts
- fuzzy.go: consolidate fuzzyMatch, sortFuzzyScored, and
  highlightFuzzyPositions from column_finder.go and chat.go
- stream.go: generic waitForStream replacing 4 near-identical
  channel-wait functions across chat.go and extraction.go
- transformCells: shared cell-grid copy loop used by compactMoneyCells
  and magTransformCells
- quoteRowSpec: single builder for quote rows with includeProject /
  includeVendor flags, replacing 3 copy-pasted functions
- overlayMaxHeight: extract shared overlay height clamping from
  buildDashboardOverlay and buildNotePreviewOverlay
- topLevelEmptyHint: replace 8-case switch with map + TabKind.plural()
- computeNaturalWidths: consolidate naturalWidths and
  naturalWidthsIndirect via a column-index mapper function
- replaceAssistantWithError: extract duplicated "remove assistant
  message, append error, refresh" pattern from 3 chat stream handlers
Cover the patch gaps from the refactoring PR: formDataAs error path,
waitForStream open/closed channel, replaceAssistantWithError,
overlayMaxHeight clamp, computeNaturalWidths indirect path and
short-row guard, and the missing tabIncidents/tabDocuments branches
in TestEmptyHintPerTab.
…pers

Exercise the formDataAs error path at every call site by passing the
wrong formData type. Add direct plural() test covering tabQuotes and
tabDocuments (bypassed by emptyHintOverrides). Test waitForExtractProgress,
waitForLLMChunk, waitForSQLChunk, and waitForChunk wrappers with both
open and closed channels.
Move 13 form-related fields into a formState struct held as a value
field (fs) on Model. All references updated from m.fieldName to
m.fs.fieldName. No logic changes -- purely structural reorganization
to reduce the God object surface area.

Part of #568.
Move 9 extraction-related fields into an extractState struct held as
a value field (ex) on Model. pull stays on Model since it is shared
between chat and extraction. All references updated from m.fieldName
to m.ex.fieldName. No logic changes.

Part of #568.
…cation

Replace 16 identical func(q *gorm.DB) *gorm.DB { return q.Unscoped() }
closures in Preload calls with a single package-level var. The 2 special
cases with chained .Preload("ProjectType") are left as-is.

Part of #568.
cpcloud added 4 commits March 2, 2026 09:53
Introduce buildOptions[T] that takes a label+id extractor closure and
handles the make/append/withOrdinals boilerplate. projectTypeOptions,
maintenanceOptions, projectOptions, and applianceOptions now delegate
to it.

Part of #568.

Made-with: Cursor
Add a createOrUpdate helper that handles the repeated pattern of
setting the entity ID from editID on update, or persisting the new
ID back to editID on create. 9 submit methods now delegate to it
(project, quote, maintenance, appliance, vendor, serviceLog,
incident, document, scopedDocument). submitHouseForm is left as-is
since it is a singleton with unique logic.

Also fixes submitIncidentForm which was missing the editID assignment
after create, inconsistent with every other entity.

Part of #568.

Made-with: Cursor
Introduce listQuery[T], getByID[T], and checkDependencies to eliminate
the repeated boilerplate across entity CRUD methods:

- listQuery[T]: replaces 14 list methods' var/prepare/unscoped/find/return
  pattern with a single generic that takes a prepare closure
- getByID[T]: replaces 8 get methods' var/preload/first/return pattern
- checkDependencies: replaces the repeated count-and-fail loop in 4
  delete methods (vendor, project, maintenance, appliance)

Create/Update/Restore left as-is -- they're already compact one-liners
or have too much entity-specific logic (vendor upsert transactions,
document blob omission, polymorphic parent checks).

Part of #568.

Made-with: Cursor
Replace the 7-block overlay guard chain in Update with a single
dispatchOverlay method that determines the active overlay (priority
order), absorbs non-key messages, and dispatches key messages to the
appropriate handler.

As part of this, simplify handleChatKey, handleCalendarKey, and
handleInlineInputKey to return tea.Cmd instead of (tea.Model, tea.Cmd)
since they always return m. Extract helpOverlayKey from the inline
switch that was previously in Update.

Part of #568.

Made-with: Cursor
@cpcloud cpcloud changed the title refactor(app): extract shared helpers and deduplicate patterns refactor: architectural cleanup -- God object, data layer, dispatch Mar 2, 2026
@cpcloud cpcloud merged commit 2584472 into main Mar 2, 2026
17 checks passed
@cpcloud cpcloud deleted the refactoring branch March 2, 2026 18:03
cpcloud added a commit that referenced this pull request Mar 19, 2026
…567)

## Summary

- Extract `formState` (13 fields) and `extractState` (9 fields) from the
56-field `Model` God object, reducing it to 34 fields
- Add generic `listQuery[T]`, `getByID[T]`, and `checkDependencies`
helpers in the data layer, deduplicating 14 list methods, 8 get methods,
and 4 delete methods
- Add `createOrUpdate` helper to deduplicate 9 submit methods in forms
- Add `buildOptions[T]` generic to deduplicate 4 option builder
functions
- Replace `unscopedPreload` lambda duplication (16 copies) with a plain
function
- Unify the 7-block overlay guard chain in `Update` into a single
`dispatchOverlay` method; simplify 3 handler signatures from
`(tea.Model, tea.Cmd)` to `tea.Cmd`
- Extract shared helpers from earlier in the branch:
`fuzzyMatch`/`sortFuzzyScored`, `waitForStream`, `formDataAs[T]`,
`replaceAssistantWithError`, `computeNaturalWidths`, `overlayMaxHeight`,
`countStr`, `idColumnSpec`, `quoteRowSpec`, `transformCells`
- Add coverage tests for all new helpers

Net -248 lines of production code, +339 lines of tests.

Closes #568.

## Test plan

- [x] `go test -shuffle=on ./...` passes (all packages)
- [x] `nix run '.#pre-commit'` clean on every commit
- [x] CI checks pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: architectural refactoring pass (God object, boilerplate, data layer)

1 participant