refactor(models): split Site into SiteConfig + SiteState #109

Merged
lerko merged 1 commits from refactor/site-split into main 2026-06-11 21:21:08 +00:00
Owner

Summary

Phase 5c — the root-cause architectural fix. models.Site fused 22 persistent config fields with 11 ephemeral runtime state fields in one struct, causing the lost-update race (Phase 3), the 11-line field-by-field copy in UpdateSiteConfig, and ~12 manual sync points per new field.

Design: embedded composition via type Site struct { SiteConfig; SiteState }. Field access unchanged — site.Name and site.Status still work via promotion.

Changes:

  • Store layer deals exclusively in SiteConfig — DB never sees runtime state (compiler-enforced)
  • UpdateSiteConfig reduced from 11-line field-by-field copy to existing.SiteConfig = cfg
  • RunCheck takes SiteConfig — statically prevented from reading/writing state
  • Backup.Sites changed to []SiteConfig — exports no longer carry zero-valued runtime fields
  • Import backward-compatible (json ignores unknown fields)
  • Engine liveState keeps full Site composites with both config + state

Test plan

  • go test -race ./... — all pass
  • golangci-lint — 0 issues
  • UpdateSiteConfig body is 3 lines instead of 14
  • Store methods accept/return SiteConfig only
  • RunCheck takes SiteConfig, not Site
## Summary Phase 5c — the root-cause architectural fix. `models.Site` fused 22 persistent config fields with 11 ephemeral runtime state fields in one struct, causing the lost-update race (Phase 3), the 11-line field-by-field copy in `UpdateSiteConfig`, and ~12 manual sync points per new field. **Design:** embedded composition via `type Site struct { SiteConfig; SiteState }`. Field access unchanged — `site.Name` and `site.Status` still work via promotion. **Changes:** - Store layer deals exclusively in `SiteConfig` — DB never sees runtime state (compiler-enforced) - `UpdateSiteConfig` reduced from 11-line field-by-field copy to `existing.SiteConfig = cfg` - `RunCheck` takes `SiteConfig` — statically prevented from reading/writing state - `Backup.Sites` changed to `[]SiteConfig` — exports no longer carry zero-valued runtime fields - Import backward-compatible (json ignores unknown fields) - Engine `liveState` keeps full `Site` composites with both config + state ## Test plan - [x] `go test -race ./...` — all pass - [x] `golangci-lint` — 0 issues - [x] `UpdateSiteConfig` body is 3 lines instead of 14 - [x] Store methods accept/return `SiteConfig` only - [x] `RunCheck` takes `SiteConfig`, not `Site`
lerko added 1 commit 2026-06-11 21:15:18 +00:00
refactor(models): split Site into SiteConfig + SiteState
CI / test (pull_request) Successful in 1m58s
CI / lint (pull_request) Successful in 1m21s
CI / vulncheck (pull_request) Successful in 1m2s
52ccd7ad91
Site now embeds SiteConfig (22 persistent fields) and SiteState
(11 ephemeral runtime fields). Field access unchanged via promotion
— site.Name and site.Status still work.

Store layer deals exclusively in SiteConfig — the DB never sees
runtime state. Engine's liveState keeps full Site composites.
UpdateSiteConfig reduced from 11-line field-by-field copy to
`existing.SiteConfig = cfg`.

RunCheck takes SiteConfig (only needs config fields). Checker is
now statically prevented from reading/writing runtime state.

Backup.Sites changed to []SiteConfig — exports no longer carry
zero-valued runtime fields. Import backward-compatible (json
ignores unknown fields).
lerko merged commit 52ccd7ad91 into main 2026-06-11 21:21:08 +00:00
lerko deleted branch refactor/site-split 2026-06-11 21:21:08 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: lerkolabs/uptop#109