Skip to content

fix: data integrity and persistence locks (DATA-01..17, API-05, SEC-06) - #184

Merged
lnieuwenhuis merged 5 commits into
stagingfrom
fix/audit-data-staging
Sep 17, 2026
Merged

lnieuwenhuis merged 5 commits into
stagingfrom
fix/audit-data-staging

Conversation

@lnieuwenhuis

Copy link
Copy Markdown
Owner

Summary

Independent data-integrity release for DATA-01..17, API-05, SEC-06. Base origin/staging 6a2146a (PR #175). Two cherry-picks ONLY from fix/audit-data-integrity (43be1d03, dae8256c), excluding unrelated dev docs c6160bd. Original worktree untouched. No openfoodfacts.ts changes (API-04/UI-07 later integration preserved). No pagination/version changes (3.5.0 kept, pagination/3.6.0 owners preserved).

Fixes: meal PATCH merges under SELECT FOR UPDATE with group-first FOR SHARE ordering, product snapshot preserved unless nutrition inputs changed, stats eaten_days by entry presence including zero-calorie, HealthKit tzOffsetMinutes local-day clamp, quantity/multiplier/default-serving round-to-precision before positivity, template append_base+index, group delete/create sorted locking, weight stats as-of selectedDate, named unique conflicts to 409, goal strict positivity after round2 with invalid-type rejection, mutationId 128-char cap, missing-serving provenance via source_metadata null, sort-order bigint clamp, optional_f64 fallible, barcode 4-20 shared domain, shopping multiplier projection + builder, admin FOR UPDATE before-image, admin recipe updatedAt projection, BrowserFoodProduct redaction.

Verification

Disposable DB only: macro-tracker-data-staging-pg 127.0.0.1:55452 macro_tracker_data_staging_test, never normal/prod. Fresh release-tree runs:

  • cargo fmt --check PASS, cargo check --locked PASS (rustls 0.23.45, event-listener 5.4.2 from staging)
  • cargo test --locked 193 passed 0 failed (18 data_integrity passed)
  • pnpm install frozen PASS, typecheck PASS, lint PASS, audit:code PASS
  • Bounded hidden backend (target/debug/macro-tracker-backend.exe --features test-faults, PORT 4002, PID 40272 exact path verified, health ready, finally stopped verified not alive): db vitest 10 files 122 passed, web vitest 68 files 448 passed with startup-process excluded (known OPS-03 owned by build worker)
  • gitleaks: only pre-existing mtk_v1_your_token docs examples, no genuine secrets
  • gh user lnieuwenhuis verified

Migrations

None. No fixture/migration files edited. DATA-09 test creates meal_groups_active_default_label_key IF NOT EXISTS as temporary until MIG-01/TEST-01 PR #180 merges, then removable no-op.

Breaking behavior

None. Compatible: PATCH accepts same keys (strips __*), shopping adds servingMultiplier field, BrowserFoodProduct omits 7 internal fields, stats now includes zero-calorie tracked days per held PR177 wording, HealthKit adds optional tzOffsetMinutes default 0.

Risks

MIG PR #180 concurrently open; stopgap harmless after. Contracts PR #177 HOLD pending DATA-03: this branch provides denominator (entry-presence incl zero-calorie) matching its wording. No pagination, no openfoodfacts, no new prod deps.

DATA-01: the API no longer premerges a stale full record; updateMealEntry
merges only the supplied fields under a row lock, so concurrent disjoint
updates survive.
DATA-02: a product-backed entry recalculates only when a nutrition input,
the product or an explicit macro override changed, so group/label edits keep
the stored snapshot while quantity/product changes still recalculate.
DATA-05: linked quantities, serving multipliers and product defaults are
rounded to numeric(8,2) precision before positivity checks and nutrition
maths, and values that would store as zero are rejected.
DATA-06: template application overwrites the template-local order and
appends the block after the day's existing entries in template order.
DATA-07: creates lock the group FOR SHARE and deletes keep group-then-meal
lock order, so no active meal can reference a deleted group.
DATA-11: clientMutationId is capped at 128 characters before persistence.
DATA-13: append allocation clamps against an existing extreme sort_order
instead of overflowing the integer column.
DATA-14: present non-numeric optional numbers are rejected instead of
silently becoming defaults.
DATA-03: stats count eaten days by entry presence, not calories.
DATA-04: HealthKit eligibility and sample clamping use the client's
tzOffsetMinutes local day, documented in the generated OpenAPI.
DATA-08: historical weight stats use only measurements on or before the
selected reference date.
DATA-09: named unique violations map to actionable conflicts (weight date,
default group label).
DATA-10: weight goals are validated strictly positive after rounding and
wrong runtime types no longer clear a goal.
DATA-12: the review queue flags missing source serving size from explicit
provenance instead of the arithmetic fallback.
DATA-15: barcode writes share the 4-20 byte domain both lookups accept.
DATA-17: admin before-images are captured under the product row lock.
API-05: the admin user detail exposes updatedAt without widening the
session recipe payload.

Regressions live in db::tests::data_integrity and the extended existing
suites; verified with cargo test against a disposable PostgreSQL, the live
packages/db suite (122 passed) and the web suite with the Rust backend.
DATA-16: the compact planned-shopping projection carries servingMultiplier
and the shopping builder multiplies serving/count amounts by it, matching
the backend nutrition; gram/ml stay unchanged.
SEC-06: a browser-safe product projection strips internal provenance
(contributor ids, source provider/confidence/metadata, correction links)
from session search results, barcode save echoes and library page props,
while nutrition and source labels stay. The shared db mock keeps real
non-function exports so the projection constant survives module mocking.

Covered by new browser-product and shopping-list unit tests plus the
session-action serialization assertion.
@lnieuwenhuis
lnieuwenhuis merged commit b8d00a0 into staging Sep 17, 2026
7 checks passed
lnieuwenhuis added a commit that referenced this pull request Sep 17, 2026
…-staging

Integrate DATA (#184) after controls (#183), migrations (#180), security (#178), build (#175) and benchmark (#179). Semantic resolution: keep DATA's BrowserFoodProduct types together with the UI-22 per-operation date/idempotency-key logic in food-search-modal.tsx (drop the mount-memoized todayStr/useMemo); all other overlaps auto-merged, including the promise-returning onSaveAsPreset against the controls recipe-builder changes. Preserves controls, security, benchmark and DATA/API-05 patch semantics alongside UI-03 client dirty-draft merging.
lnieuwenhuis added a commit that referenced this pull request Sep 17, 2026
…-staging

Integrate contracts (#177) after DATA (#184); no file overlap with this seat (api-v1/openapi/docs/packages-db types only).
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.

1 participant