Commit Graph
377 Commits
Author SHA1 Message Date
Marc-André Bombeck 3a241dbe35 feat(ui): extract <Textarea> primitive with iOS-zoom + WCAG defences
Six textarea call-sites had been copy-pasting the same className with
the text-base sm:text-sm iOS-zoom defence; the audit flagged it as a
drift risk. New shadcn-style primitive in src/components/ui/textarea.tsx
mirrors <Input>'s shape and bakes in the defence, the WCAG 2.5.5 tap-
target floor (min-h-11 sm:min-h-9), autoCapitalize="sentences" /
spellCheck defaults for prose, and the autoComplete="off" + password-
manager ignore posture from <Input>.

Migrates four call-sites onto the primitive (bugreport, medications
JSON import, side-effect notes, admin feedback note) — saves ~16 LOC
of inline copy-paste and ~850 chars of repeated className strings.
The coach-input textarea stays as-is: it's an auto-grow composer with
a transparent bg wrapped in an outer "fake input" div, so migrating
would require nullifying nearly every primitive default.

11 new unit tests lock the contract (iOS zoom class, tap-target floor,
forwardRef sentinel, override semantics, attribute pass-through).
2026-05-22 08:54:49 +02:00
Marc-André Bombeck 76a7cbcff8 docs(planning): v1.4.43 W10 QA round findings reports
Six axes — code-reviewer, security, design, senior-dev, product-lead, simplifier — output to .planning/round-v1443-QA-<axis>-findings.md. Verdicts:

- code-reviewer: APPROVE_WITH_FIXES (1C closed, 1H = version bump)
- security: APPROVE (0C/0H)
- design: APPROVE_WITH_FIXES (1C closed, 1H closed)
- senior-dev: APPROVE (0C/0H + 1M closed)
- product-lead: SHIP (0C/1H closed)
- simplifier: CHANGES_REQUESTED (1C + 2H all closed)
2026-05-21 23:06:20 +02:00
Marc-André Bombeck a8e2310fb0 docs(planning): W6-ZOD-ROLLOUT v1.4.43 phase report 2026-05-21 22:37:10 +02:00
Marc-André Bombeck c8f15589b1 docs(planning): W12-QOL-RESIDUAL phase report 2026-05-21 22:32:02 +02:00
Marc-André Bombeck 2679894277 docs(planning): W13-SECURITY-RESIDUAL v1.4.43 phase report 2026-05-21 22:19:57 +02:00
Marc-André Bombeck 46e2ac255c docs(planning): W14-WITHINGS-EXTENSION v1.4.43 phase report 2026-05-21 22:19:29 +02:00
Marc-André Bombeck 937daaad04 docs(planning): W11-MOBILE-UI-RESIDUAL v1.4.43 phase report 2026-05-21 22:09:12 +02:00
Marc-André Bombeck 7e0f4afeeb docs(planning): v1.4.43 audit findings + HAR gitignore
Four audit reports from the v1.4.43 discovery round: analytics 9s root cause, mobile-UI WCAG sweep, QoL copy gaps, security PII + log-growth.

Also adds *.har to gitignore so production HAR captures (which carry auth headers and cookies) do not land in the repo by accident.
2026-05-21 22:05:05 +02:00
Marc-André Bombeck ade2c10e0d docs(planning): W8-OPS v1.4.43 phase report 2026-05-21 21:46:12 +02:00
Marc-André Bombeck 825284c724 docs(planning): W7-WITHINGS-RELIABILITY v1.4.43 phase report
B3 (typed-classification catch-block migration) applied to both
activity + sleep sync paths. B4 (IntegrationStatus surface
unification) and B7 (IntegrationStatus row freshness audit) are
explicitly deferred to v1.4.44 with the architecturally-correct
recipe restated so the next wave can pick them up cleanly.
2026-05-21 21:46:12 +02:00
Marc-André Bombeck f2cd4f6d59 docs(planning): W4-QoL-COPY v1.4.43 phase report 2026-05-21 21:45:46 +02:00
Marc-André Bombeck 2c68a48d21 docs(planning): v1.4.43 marathon handoff for fresh session
Orchestrator hit 78% context with 9 implementation waves dispatched
and running in worktrees. Fresh session picks up at cherry-pick +
W10 QA round + reconcile + release endgame.

Handoff doc inventories every dispatched wave with its agent id and
scope, the post-merge develop+main rebase pattern that recurred in
PR #193, the Marc-voice + PII cleanup checklist for cherry-pick
review, and the docker BuildKit cache verify-loop per the v1.4.42
post-deploy lesson.

Estimated 2-3h fresh session for the closure work.
2026-05-21 21:26:28 +02:00
Marc-André Bombeck 6f62193b99 docs(planning): v1.4.42 QA round findings reports
Carry-over from the v1.4.42 marathon W10 reconcile — the six W10
reviewer reports (code-reviewer, security, design, senior-dev,
product-lead, simplifier-residual) for the release-trail audit-input
that gated the v1.4.42 tag.
2026-05-21 19:31:04 +02:00
Marc-André Bombeck cb0a3b29f2 docs(planning): W1-KNIP v1.4.42 phase report
Captures the gate-flip outcome: 35 unused exports → 0, 52 unused types
→ 0, exit code 0 on the final `pnpm knip` run. Documents the
`ignoreExportsUsedInFile` + `ignoreIssues` ignore-block rationale,
inventories every dropped symbol, and flags the two W3 / W4 reconcile
callouts (`describeInjectionSite` re-export, `listSupportedTimezones`
re-export) along with the merge-order constraint they imply.
2026-05-21 19:06:22 +02:00
Marc-André Bombeck 3b006b7ccb docs(planning): W3-QUERYKEY-LONGTAIL v1.4.42 phase report 2026-05-21 19:04:22 +02:00
Marc-André Bombeck 6850818271 docs(planning): W6 v1.4.42 phase report 2026-05-21 19:01:15 +02:00
Marc-André Bombeck cb601fcadd docs(planning): W5-IOS-WORKOUTS-DEDUP + DESIGN-POLISH v1.4.42 phase report 2026-05-21 18:55:53 +02:00
Marc-André Bombeck 43f10118b7 docs(planning): W4-ARCH-HYGIENE v1.4.42 phase report 2026-05-21 18:55:14 +02:00
Marc-André Bombeck bc50d3ce8c docs(planning): W2-ZOD-MULTI-ISSUE v1.4.42 phase report
Records the helper landing + widgets-route swap and stages the
v1.4.43 rollout queue (41 sites in src/app/api/** still on the
issues[0].message pattern). Includes the rewrite recipe + iOS-contract
prioritisation so the follow-up wave can pick up without re-discovery.
2026-05-21 18:52:41 +02:00
Marc-André Bombeck d3d6010407 docs(planning): v1.4.41 QA round findings reports
Carry-over from the v1.4.41 marathon reconcile — the six W10 reviewer
reports (code-reviewer, security, design, senior-dev, product-lead,
simplifier-residual) were not committed before the squash-merge to
main. Lands them on develop so the next marathon's audit-trail has
the inputs that gated the v1.4.41 tag in one place.
2026-05-21 18:42:55 +02:00
Marc-André Bombeck 182d15747d docs(planning): W-RECONCILE-INSIGHTS v1.4.41 phase report
Captures the helper extract + 6 sibling-route wire + cache-shape
reconcile + test pins. Notes the medication-compliance stub-row
recogniser fix and the pulse-status-timeout assertion flip as
risk callouts.
2026-05-21 18:18:53 +02:00
Marc-André Bombeck cd2a579cc3 Revert "perf(insights): persist timeout stub for blood-pressure and weight status"
This reverts commit 27f3bec1f4.
2026-05-21 18:18:48 +02:00
Marc-André Bombeck 0aa2f14935 docs(planning): W-SIMPLIFIER v1.4.41 phase report 2026-05-21 17:52:01 +02:00
Marc-André Bombeck 27f3bec1f4 perf(insights): persist timeout stub for blood-pressure and weight status
Mirror the bmi-status (v1.4.37) timeout-stub persist into the two
sibling endpoints. On a 20 s provider stall the route now writes a
sentinel auditLog row keyed to today's Berlin day so the next mount
short-circuits at the cache lookup instead of re-racing the same
provider call for the rest of the day.

This eliminates the ~14 s warm response on every iOS dashboard open
when the provider has stalled once that day. Response envelope
unchanged; iOS v0.5.4 contract preserved.

Tests cover both arms: the persist-on-timeout path (one create with
timeout:true, model:"timeout-stub", today's dateKey) and the
short-circuit-on-stub path (subsequent mount returns cached text,
provider never invoked).
2026-05-21 17:38:20 +02:00
Marc-André Bombeck a1bfc3b461 docs(planning): v1.4.41 marathon handoff for fresh session 2026-05-21 17:17:53 +02:00
Marc-André Bombeck ac5ecd39f1 docs(planning): W-FRONTEND-FACTORY + W-ORG v1.4.41 phase reports 2026-05-21 17:17:13 +02:00
Marc-André BombeckandClaude Opus 4.7 70c5026812 docs(operator): pg.Pool max scaling guidance for multi-container deploys
Append a Postgres connection-pool sizing section to the self-hosting
scaling guide. Documents the v1.4.40 W-POOL default (20 slots per
container), the container_count × DATABASE_POOL_MAX × max_connections
envelope (with a 1-5 container ready-reckoner table), the env-var
override path, and PgBouncer as the escape hatch for deployments that
outgrow the per-process pool model. Cross-references src/lib/db.ts and
the empirical trace that motivated the bump.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 17:16:04 +02:00
Marc-André Bombeck 8a56f48259 refactor(prompts): unify src/lib/insights/prompt*.ts into src/lib/ai/prompts/
Pre-v1.4.41 prompts lived in two homes — `src/lib/ai/prompts/` (per-
metric system prompts + safety contracts) and `src/lib/insights/prompt
*.ts` (user-prompt builder + comparison snapshot + the `compactSections`
helper). A new reader could not tell from filenames which file to
extend; both directories looked equally authoritative. Audit rec #3
flagged the split as the only undiscoverable seam in the AI layer.

Move both files into `src/lib/ai/prompts/`:
  - `lib/insights/prompt.ts` → `lib/ai/prompts/insight-system-prompt.ts`
  - `lib/insights/prompt-compact.ts` → `lib/ai/prompts/compact-sections.ts`
  - co-located tests follow

Three importers updated: `app/api/insights/generate/route.ts`,
`lib/ai/coach/snapshot.ts`, the generate route test (the
`vi.mock('@/lib/insights/prompt')` path moved too). Prompts now have
ONE home and ONE mental model.
2026-05-21 17:14:01 +02:00
Marc-André Bombeck 59befdca73 docs(planning): W-IOS-COORD v1.4.41 closure report
Captures SB-7 follow-up (check-user endpoint shipped), SB-8 (merge
semantics documented, no code change), SB-9 (already retired, no
streak surface exists), and the dashboard-widgets PUT 422 root cause:
the Zod validator is correct and additive-safe; the live 422s from
iOS v0.5.4 are an iOS-side payload bug (likely an unknown widget id
or an out-of-range order value). No server fix viable.
2026-05-21 17:13:49 +02:00
Marc-André Bombeck dde79b041c chore(streak): formalise deprecation status per SB-9
ls src/app/api/streak returns No such file or directory. No web
frontend code paths reference /api/streak/...; the only streak
references in the API tree are derived fields inside
/api/insights/targets (streakDays, streakHighlight).

Decision: nothing to deprecate. The route family either never existed
or was removed before the audit. iOS can safely treat /api/streak/*
as 404. Document filed so a future audit does not re-open the
question.
2026-05-21 17:13:49 +02:00
Marc-André Bombeck 6c2eadb342 docs(dashboard-layout): document merge semantics for SB-8 audit gap
The dashboard layout resolver in src/lib/dashboard-layout.ts is
append-only on read: new widget ids added to DASHBOARD_WIDGET_IDS
automatically appear for every existing user on next GET, with
visible / tileVisible / order taken from DEFAULT_DASHBOARD_LAYOUT. No
database migration is ever required when introducing a new widget.

Documents the merge contract so a future widget-introduction does not
ship a one-shot SQL migration that would clobber every user's
customisation. PUT preservation rules captured: chartOverlayPrefs is
preserved-on-omit, comparisonBaseline is not. Investigation-only — no
code change.
2026-05-21 17:13:49 +02:00
Marc-André Bombeck 4cca62dce9 docs(planning): v1.4.40 marathon closure row, QA findings, perf-verify
Adds the v1.4.40 row + the four prior v1.4.39.x stacked rows to
.planning/round-audit-marathon-closure.md (v1.4.39.1/.2/.3/.4 were
documented inline in the corresponding memory files but the table
hadn't picked them up).

Commits the six v1.4.40 QA reviewer findings files (senior, security,
specialist, UX, product-lead, simplifier) so the audit trail stays
self-contained.

Adds round-v1440-perf-verify.md anchored on the live deploy commit
1114cf7e (squash-merge), Coolify deploy timestamp 2026-05-21T12:55:02Z,
docker workflow 26221332323. Includes the AP-2 .p8 install runbook for
SB-5 live effect.
2026-05-21 13:00:03 +02:00
Marc-André Bombeck 8e9a7891ce docs(planning): W-INFRA v1.4.40 phase report 2026-05-21 12:27:49 +02:00
Marc-André Bombeck 29bfcc674b refactor(rollups): move measurement-rollups under src/lib/rollups umbrella
Creates the new src/lib/rollups/ aggregator directory and relocates
the main measurement rollup module:

- src/lib/measurements/rollups.ts → src/lib/rollups/measurement-rollups.ts
- src/lib/measurements/__tests__/rollups.test.ts →
  src/lib/rollups/__tests__/measurement-rollups.test.ts

Every importer of @/lib/measurements/rollups is rewritten to
@/lib/rollups/measurement-rollups. The orphan sibling reference inside
src/lib/measurements/rollup-read.ts (still in its old home until the
next batch) is promoted to the @/lib alias so the chain keeps
typechecking through the in-flight move.

Pure relocation — no behaviour change, no exported-surface change. The
umbrella exists so the rollup contract is discoverable from a single
directory before the iOS sprint locks the boundary.
2026-05-21 12:19:45 +02:00
Marc-André Bombeck 6901b0791d docs(planning): W-GHOSTS v1.4.40 phase report 2026-05-21 12:14:22 +02:00
Marc-André Bombeck e9a9e79edb docs(planning): W-WMY-WIRE v1.4.40 phase report 2026-05-21 12:07:24 +02:00
Marc-André Bombeck e8a0947638 docs(planning): W-RSC v1.4.40 phase report 2026-05-21 11:52:50 +02:00
Marc-André Bombeck 594cd22ff5 docs(planning): W-DELETED v1.4.40 report — soft-delete invisibility across reader tiers
Inventory of touched files, test matrix, and commit map for the
v1.4.40 soft-delete-filter sweep that closes Senior Infra+DB audit
Critical Finding #3.
2026-05-21 11:52:29 +02:00
Marc-André BombeckandClaude Opus 4.7 6ab3440336 test(apns,notifications-status): pin payload shape and aggregate behaviour
Pins the SB-5 (apns time-sensitive interruption-level) and SB-6
(/api/notifications/status events map) contracts.

Note: the SB-6 route + test changes landed inside an earlier
`fix(dashboard-summary)` commit (1bcaae47) due to a cross-wave
pre-commit-hook race that auto-folded my staged changes; this
commit ships the wave report + records the proper SB-5 + SB-6
intent in the log for the release-notes sweep to pick up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 11:45:47 +02:00
Marc-André Bombeck 014593a2a1 test(aasa): pin payload shape and content-type
Five assertions on the route handler:
  - 200 on an unauthenticated GET.
  - Content-Type is exactly application/json with no charset parameter
    (Apple's swcd/aasa-validator refuse a charset and the failure mode
    is silent — Universal Links just stop firing).
  - Cache-Control allows public CDN mirroring.
  - JSON shape matches the SB-4 spec exactly.
  - applinks App ID matches webcredentials App ID so a rotated
    Team ID never half-lands.

Includes the v1.4.40 phase report.
2026-05-21 11:38:58 +02:00
Marc-André Bombeck 64e1b74373 docs(planning): v1.4.39 architecture-QA reports and empirical trace
Five planning artefacts produced during the v1.4.39 review pass:
frontend, ghosts (dead-code drift), infra-db, organization, and a
cross-cutting empirical trace. Kept under .planning/ rather than the
versioned docs site because they are working notes scoped to a single
release window.
2026-05-21 11:19:57 +02:00
Marc-André Bombeck 50b74be208 docs(planning): v1.4.39.1 closure row 2026-05-21 09:12:57 +02:00
Marc-André Bombeck 805e495731 docs(planning): v1.4.39 closure rows and post-deploy perf-verify
Closes the audit-marathon closure series with rows for v1.4.38.5
through v1.4.38.8 (the four-hotfix chain that landed between v1.4.38.4
and v1.4.39) plus v1.4.39 itself.

Adds round-v1439-perf-verify.md anchored on the live deploy
(commit hash d84922cd, deploy uuid ujgsxpa46esyzybjldgxjeox) and the
public-surface 10x burst median 86 ms. Authenticated heavy-endpoint
verification awaits the first browser session against the live deploy.
2026-05-21 07:50:32 +02:00
Marc-André Bombeck 5329943e09 docs(planning): v1.4.40 backlog captures deferred QA findings
Six QA reviewers (senior / security / specialist / ux / product-lead /
simplifier) ran on the v1.4.39 diff; their finding docs land here for
traceability. The v1.4.40 backlog captures every Medium + Low the
reconcile deliberately deferred — F-SEC-M-02 RLS (v1.5), F-M-01
long-window consumer (v1.5), F-M-02 unused tx parameter, F-M-03 partial
index post-convergence, F-M-05 RollupBucketRow consolidation, F-M-06
per-agent worktree process note, UX-M1-3 compliance + mood polish,
Specialist-M01 UNION arm cleanup, Simplifier dead-exports + duplicate
tz helpers, plus low-severity observability polish. UX-M4 is marked
obsolete (Specialist-H2 widen closed it).
2026-05-18 13:00:01 +02:00
Marc-André Bombeck bbcb09e879 docs(planning): W-WMY v1.4.39 phase report 2026-05-18 12:32:34 +02:00
Marc-André Bombeck 4deaa18fd5 docs(planning): W-MED v1.4.39 phase report 2026-05-18 12:32:01 +02:00
Marc-André Bombeck dfdf6be598 docs(planning): W-MOOD v1.4.39 phase report 2026-05-18 12:30:33 +02:00
Marc-André Bombeck 1c6264dfd3 docs(planning): W-SUM v1.4.39 phase report
Captures the cumulative-metric sum_value rollup work — writer +
boot-backfill discovery widening + consumer swaps in
`/api/measurements?groupBy=day` and `/api/dashboard/summary` +
new `rollup-read-cumulative.ts` helpers. Documents the
`/api/analytics` A2 skip-cumulative deferral to v1.4.40 (file
owned by W-SINCE in this phase) and the per-source W8c deferral
to v1.5. Surfaces the cross-agent commit-message drift recurrence
for the marathon retrospective.
2026-05-18 12:23:31 +02:00
Marc-André Bombeck 823764a914 perf(analytics): cap live-fallback findMany at trailing 90 days
The per-type loop inside `buildAnalyticsResponse` calls
`fetchMeasurementSeriesChunked` without a `since` filter, so a single
default-slice request pulls every measurement ever for the user across
15 chunked rounds. The v1.4.38.8 per-type fast-path gate made this
unreachable in the common case, but a regression on those gates would
re-trigger Marc's 74.6 s cold full-slice mount.

- Add `ANALYTICS_LIVE_WINDOW_DAYS = 90` + `liveSince` constant.
- Thread `since: liveSince` into every per-type chunked read.
- Annotate `meta.analytics.bp_aggregate.live_since` (ISO) so the next
  perf-verify can prove the cap is in effect on a live request.

Defense-in-depth only — the rollup fast-path is unchanged because it
never reaches the per-type loop. Worst-case bound drops from ~347 k
rows to ~5 k rows on Marc's tenant (matching the
comprehensive-aggregator + summaries-slice 90-day windows).

Trade-off: `summarize().avg30LastYear` returns null on the fall-through
path; the dashboard's year-over-year overlay shows "no prior data"
until the rollup tier warms. Acceptable on a degraded path that
v1.4.38.8 already made rare.

Tests: new `src/__tests__/api/analytics/since-cap.test.ts` pins the
90-day floor on every per-type findMany, pins the `live_since`
annotate, and pins that the slim slice is untouched. Existing chunked
paging integration test reseeded with timestamps inside the 90-day
window so it still exercises the two-chunk boundary.
2026-05-18 12:22:11 +02:00
Marc-André Bombeck 0d33f91d01 feat(rollups): schema scaffolding for mood + medication-compliance + cumulative-sum tiers
Three additive Prisma additions land in one commit so the v1.4.39 wave
agents (W-MOOD, W-MED, W-SUM) build against a stable generated client:

- `mood_entry_rollups`: per-(user, granularity, bucket) mood stats
  (count / mean / min / max / sd). Reuses the existing
  `measurement_rollup_granularity` enum.
- `medication_compliance_rollups`: per-(user, medication, day)
  scheduled / taken / skipped triple. `day` is the user-tz-anchored
  YYYY-MM-DD string so multi-instance reads do not re-derive boundaries.
- `measurement_rollups.sum_value`: nullable column. Cumulative-metric
  read paths consume it directly; boot backfill folds NULLs on first
  reach.

Migrations 0070 / 0071 / 0072 use IF NOT EXISTS + DO $$ EXCEPTION WHEN
duplicate_object guards (mirror 0061 / 0067 / 0069). No ALTER on
existing tables that downgrade row contracts, no DROP. Backward-safe
read paths land in the wave agents that follow.

Includes `.planning/round-v1438-perf-analysis.md` — the read-only audit
that anchors the wave plan.
2026-05-18 12:10:02 +02:00