Commit Graph

74 Commits

Author SHA1 Message Date
arnaudne b4067c7685 fix(duplicate-day): extract userId before closure to satisfy TS strict null check
session.user is narrowed inside the early-return guard but TypeScript
does not carry that narrowing into the Prisma transaction callback.
Extract to a const immediately after the guard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 08:41:56 +02:00
arnaudne d3f1753555 feat(timeline): duplicate a day's events onto another day
POST /api/events/duplicate-day — copies all events from sourceDate to
targetDate, shifting timestamps by the day offset while preserving
time-of-day, duration, notes, and metadata. Ownership-gated per family.

In the timeline, each day header gains a copy icon button. Clicking it
expands an inline date picker; confirming triggers the API and
invalidates the events query.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 07:35:59 +02:00
arnaudne f2cc181ece feat: per-baby breastfeeding and pump feature toggles
Add isBreastfed and hasPump flags to Baby model. When disabled,
hide breastfeeding/pump options from quick-add FAB, dashboard
counters and quick-log groups, and the milk stock nav link.
Settings page gains toggle UI to configure per baby.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 11:51:41 +02:00
arnaudne 319d70c3a4 fix(growth): round X-axis week labels to 1 decimal
1.7142857142857142sem → 1.7sem

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 11:24:24 +02:00
arnaudne 6dc09fb696 fix: UX audit — code-review findings
- Photo client-side limit: 5MB → 30MB (matches server)
- OTHER event shows notes preview in dashboard + timeline summaries
- Stats: add "Activités par jour" chart (Bain/Balade/Plat ventre/Autre)
- Combined diaper (wet→stool): show color/amount pickers, pass metadata
- Quick-add FAB: 4-col → 5-col grid (14 types in 3 rows instead of 4)
- Stool color buttons: force dark text, readable on light bg in dark mode
- Reminders: mark taken (✓) sets lastSentAt, ↺ clears it; shows elapsed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 11:06:54 +02:00
arnaudne da60efd7c1 feat(milk): add stock editing and lot assembly
- Inline edit form per stock (volume, location, expiry, notes)
- Assembly mode: select 2+ lots, merge into one with combined volume
  and earliest expiry date, deletes originals
- POST /api/milk now accepts expiresAt override directly
- Fix OTHER event type icon (add FileText to EventIcon map)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:10:39 +02:00
arnaudne 6f2336ffae feat: improve event logging UX
- Remove quick-template feature from dashboard
- Increase photo upload limit from 5MB to 30MB
- Add combined diaper toggle (wet + stool in one save) to event modal
- Show all event types in quick-add FAB and dashboard quick-log grid
- Add OTHER generic event type for freeform notes (schema + migration)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:01:05 +02:00
arnaudne 2d0c44e21d chore(api-docs): use default swagger-ui theme
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:34:38 +02:00
arnaudne 1dac9690f5 security: SSRF prevention, ownership gaps, host header injection, date validation
- webhooks: block SSRF — reject localhost/127.x/169.254.x/10.x/192.168.x
  URLs at creation and silently skip at dispatch time
- medication-profiles/last-intakes: add family ownership check on babyId
- invite/send-email: build invite URL from NEXTAUTH_URL env only;
  drop req.headers.get("origin") to prevent host header injection
- baby POST: validate birthDate is a real date; add required-fields check
- milk POST: validate storedAt before new Date()
- journal GET: validate date filter before new Date()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:33:45 +02:00
arnaudne 432feed88f chore: remove accidentally committed worktree submodule, ignore .claude/worktrees/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:29:35 +02:00
arnaudne 0a12cdfbcc fix: sanitize v1 API pagination inputs and validate growth date
- v1/events GET: offset now clamped ≥0, limit fallback prevents NaN
  being passed to Prisma take/skip
- v1/growth POST: validate date is a valid ISO date before new Date()
  to avoid Invalid Date silently propagating into DB

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:27:27 +02:00
arnaudne 9e32766046 security: second-pass ownership checks + input validation fixes
- medication-profiles/[id]: verify familyId ownership before PATCH/DELETE
- event-templates/[id]: verify familyId ownership before PATCH/DELETE
- notify/push: verify baby.familyId matches session family before push
- events GET: validate type against ALL_EVENT_TYPES allowlist; sanitize
  limit (1–500) and offset (≥0) to prevent NaN/unbounded queries
- v1/summary: fix operator precedence bug in feeds count calculation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:25:34 +02:00
arnaudne 169309af05 security: enforce family ownership on all baby-data API routes
All collection routes (events, growth, doctor-notes, journal, milestones,
vaccinations, milk, reminders, teeth, search, export) now verify the requested
babyId belongs to the authenticated user's family before querying or writing.
All [id] mutation routes verify record ownership via nested baby→familyId
before any PATCH/DELETE. Additional fixes: admin config masks sensitive
secrets in GET response, invite send-email enforces PARENT role, photo
serving requires authentication, baby PATCH restricted to own-family PARENT.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:22:44 +02:00
arnaudne a844af9c68 fix(events): correct timezone double-offset in default duration calc
When endedAt is omitted, the computed end time was stored as a UTC
string then passed through toUTC() which re-parsed it as local time,
applying the timezone offset twice. Express resolvedEndedAt as a local
datetime string to match other form fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:22:06 +02:00
arnaudne 41170d9155 feat: UX improvements, webhooks, invite management
- Photo lightbox: ArrowLeft/Right/Escape keyboard navigation
- Growth chart: persistent WHO percentile legend (not hover-only)
- Sleep stats: configurable window selector (18h/19h/20h/21h)
- Quick-add FAB: now visible on desktop (bottom-right corner)
- Invite code reset button in settings (non-admin, PARENT role only)
- Pending email invites: tracked per-token with 7-day expiry, revocable
  from settings UI; new /auth/invite/t/[token] accept page
- Webhooks: Prisma model, CRUD API, HMAC-SHA256 dispatcher wired into
  event.created / growth.created / doctor_note.created; settings UI
- Docker cron service: medication reminders every 15min, milk-expiry
  and daily-digest daily; secured with CRON_SECRET
- Photo uploads persist across redeploys via Docker named volume

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:14:28 +02:00
arnaudne 3f08313bc2 fix(photos): persist uploads via Docker named volume
Photos were lost on redeploy because they wrote to public/uploads
inside the container with no volume mount. Now stored in uploads_data
named volume via UPLOAD_DIR env var (already supported by upload route).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 22:59:31 +02:00
arnaudne 2b38879888 chore: ignore .pnpm-store directory 2026-06-15 22:46:49 +02:00
arnaudne f5e0f581af feat: doctor visit log, schedule prediction, chart PDF, natural digest
- Doctor notes: add doctorName, reason, prescriptions, nextAppointmentAt fields (schema + API + UI)
- Dashboard: baby schedule prediction widget (avg nap/bedtime/first-feed over 14 days)
- Growth PDF: embed weight chart as image (SVG→Canvas capture) before data table
- Weekly digest: natural language push notification instead of bullet stats

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 22:46:22 +02:00
arnaudne f470120add fix(photos): delete photo uses local event metadata, not a missing GET route
The deletePhoto function fetched /api/events/:id which has no GET handler
(404). This gave undefined metadata, so the PATCH would set metadata to
{photo: null} only, losing other fields — and on a 404 it likely didn't
execute correctly at all. Now reads metadata from the already-loaded
allEvents array and deletes the photo key before PATCHing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 22:38:01 +02:00
arnaudne c28fa8188f fix: dark mode event colors, sidebar height, calendar default, photo badge
- event-config: all bgClass/borderClass get dark:bg-*-950/50 + dark:border-*-800 variants — fixes bright pastel backgrounds in dark mode
- nav: h-screen → h-[100dvh] — fixes sidebar bottom buttons hidden behind mobile browser chrome
- calendar: selectedDay defaults to today, never null — events shown immediately on load
- timeline + dashboard: photo indicator (ImageIcon) on events with metadata.photo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 22:22:32 +02:00
arnaudne cd3efa98fc feat: teeth tracker, feeding analysis, sidebar groups, timeline polish
Features:
- Teeth tracker: Tooth model + migration, API routes (GET/POST upsert/DELETE),
  page at /teeth with 20-tooth grid, progress bar, inline mark/unmark flow
- Dashboard feeding analysis: avg interval + last feed + next expected time widget
- Fix dashboard baby?.name crash (selectedBaby?.name)

UI/UX:
- Sidebar: grouped into Suivi / Santé / Vie du bébé / Outils sections
- Nav: active highlight works on child routes (startsWith)
- Timeline: filter chips sticky on mobile; skeleton cards replace spinner
- Stats: 500-event limit warning banner
- Medications: 3 states — "Dans Xh" (orange), "Disponible" (green), "En retard de Xh" (amber)
- Notes: skip auto-save on empty/whitespace blur

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 21:51:32 +02:00
arnaudne 8bb048ff56 feat: finish remaining improvements — search filters, stats zero-fill, notes nav warning
- search: date range (7j/30j/90j/Tout) + type (Repas/Sommeil/Couche/Santé) filter chips
- stats: missing-day gaps filled with 0 — lines no longer break on no-data days
- notes: beforeunload handler warns before closing tab with unsaved changes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 21:02:31 +02:00
arnaudne 8170ef9996 docs(improvements): update checkboxes — all items done except 3 minor ones 2026-06-15 20:59:47 +02:00
arnaudne b40694d691 feat: finish all UI/UX improvements from backlog
- nav: aria-label on baby switcher select
- dashboard: active timers show baby name
- growth: PDF export includes percentile band per weight row
- settings: invite link note (permanent, reset if compromised)
- timeline: select mode toggle shows "Annuler" when active
- photos: delete photo from lightbox (PATCHes event metadata)
- notes: invisible date input overlay for date-jump via calendar picker
- IMPROVEMENTS.md: updated checkboxes, 8 remaining → 0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 20:58:57 +02:00
arnaudne 8f68eebeb0 feat: UI/UX improvements pass + fix photo 404 in Docker
Photos:
- Serve uploads via /api/photos/[filename] from UPLOAD_DIR env var
- Default UPLOAD_DIR=/data/uploads (mount as Docker volume for persistence)
- Upload route creates dir on demand, no root-permission issue
- Dockerfile creates /data/uploads owned by nextjs user

Growth:
- Show chart with single measurement (was hidden until 2+ points)
- PC label expanded to "PC — Périmètre crânien (cm)"
- WHO percentile ⓘ tooltip explaining P50/P3-P97
- Date field defaults to today in add-measurement form

Stats:
- Heatmap legend shows actual counts (0 1 2 3 4+) not just Moins/Plus
- Sleep timeline labeled "Fenêtre 19h – 11h"

Timeline:
- Filter type persisted in URL param (?type=...) — survives navigation
- Inline note save shows ✓ flash after blur
- Photo thumbnails open in new tab

Photos:
- Lightbox wraps: next on last → first, prev on first → last

Search:
- Result count header "X événements · Y notes"
- Event results link to /timeline?date=...&type=...
- Note results link to /notes?date=...

Notes:
- Unsaved indicator (amber dot) while textarea dirty, green ✓ after save

Milestones:
- Sort by date ascending within each month group

Settings:
- Pushover key field has eye toggle (show/hide)
- API key delete shows confirmation with key name
- All sections wrapped in collapsible SectionCard accordion

Dashboard:
- Empty state callout for new users with no events today

Event modal:
- Milk lot selection shows "Sélectionnez un lot — il sera marqué comme utilisé"
- Photo upload validates 5MB client-side before sending
- Timer resume shows "Reprise du chronomètre" when loaded from localStorage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 20:44:45 +02:00
arnaudne f7ab467385 fix(notifications): include baby name in all push/browser notification titles and bodies 2026-06-15 20:08:15 +02:00
arnaudne 716a85db84 feat(events): default duration when saving without timer — per-type fallback (15m feed, 45m nap, 8h sleep, etc.) 2026-06-15 20:04:32 +02:00
arnaudne 4a50d125c6 fix(event-icon): add Activity icon for SYMPTOM type 2026-06-15 20:01:43 +02:00
arnaudne 4bf6486e0f fix(growth): connectNulls on WHO curves — baby measurement rows break percentile lines 2026-06-15 19:35:11 +02:00
arnaudne f1998d32c1 fix(api-docs): full dark theme for Swagger UI — override all light sections 2026-06-15 19:31:54 +02:00
arnaudne e5acf75cac fix(api-docs): use relative spec URL — req.url returns internal Docker address
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 18:29:54 +02:00
arnaudne 5d7ef5fa3b fix(api-docs): rename openapi.json route dir to spec (dot breaks Next.js routing)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 18:14:01 +02:00
arnaudne 61f80e5fe4 fix(api-docs): load swagger-ui-standalone-preset.js for StandaloneLayout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:41:11 +02:00
arnaudne f59a6c5d8a feat: REST API v1 + API key management + Swagger docs
**API system**
- ApiKey model: SHA-256-hashed tokens (gw_<hex>), per-family, scoped
- Migration: 20260615210000_api_keys
- src/lib/api-auth.ts: verifyApiKey(), hasScope(), generateApiKey(), prepareKey()

**V1 endpoints** (all require Bearer gw_ token):
- GET  /api/v1/babies              — list family babies (any read scope)
- GET  /api/v1/events              — query events (events:read), babyId/type/from/to/limit/offset
- POST /api/v1/events              — log event (events:write), full metadata support
- GET  /api/v1/growth              — growth logs (growth:read)
- POST /api/v1/growth              — add measurement (growth:write), weight in grams
- GET  /api/v1/summary             — today's counts + sleep + last feed (summary:read)
- GET  /api/v1/milk                — stock lots + totalMl (milk:read)

**API key management**
- GET  /api/api-keys               — list keys (session auth)
- POST /api/api-keys               — create key, returns raw token once (session auth)
- DELETE /api/api-keys/[id]        — revoke key (session auth)

**Documentation**
- GET /api/v1/openapi.json         — OpenAPI 3.0 spec (CORS open)
- GET /api-docs                    — Swagger UI (CDN, dark themed)

**Settings UI** — "Clés API" section: create key with scope checkboxes, copy token banner (shown once), revoke, link to /api-docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:33:25 +02:00
arnaudne 009090b381 feat: running timers, symptom log, search, PDF export
- dashboard: ActiveTimers widget — live h:mm:ss for any in-progress timed event (breastfeed, nap, sleep, walk…), Terminer button patches endedAt
- dashboard: reorganised quick-log groups; Santé group (Température, Traitement, Symptôme)
- events: SYMPTOM type — tag chips (Fièvre, Reflux, Colique, Éruption, Toux, Congestion, Diarrhée, Vomissement, Irritabilité, Pleurs excessifs), stored in metadata.tags
- prisma: migration adding SYMPTOM to EventType enum
- search: GET /api/search (events by notes ILIKE, journal by content ILIKE), /search page with debounced input + keyword highlight
- nav: Recherche link added to LINKS + More drawer
- growth: PDF export button — generates formatted table with all measurements, zebra rows, downloads croissance-{name}.pdf

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:27:01 +02:00
arnaudne cd677d9244 fix(growth): show distinct points for same-week measurements
Use fractional week values (float) for actual data points instead of
floor'd integers. Two measurements on different days within the same
week now appear at distinct x positions. XAxis set to type="number"
with continuous domain so the chart renders them correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:11:08 +02:00
arnaudne e27f42942c feat: feeding heatmap, diaper streak, quick note, handoff summary, weight percentile alert, growth curve labels
- stats: 7×24 feed density heatmap (Mon–Sun × 24h, indigo intensity, dark mode)
- stats: diaper streak card — current consecutive days ≥4 couches + best streak for period
- dashboard: QuickNoteWidget — inline journal note (sky card) without leaving dashboard
- dashboard: HandoffWidget — localStorage "since last visit" digest (feeds/diapers/sleep) shown after 2h gap
- growth: replace S0/S1/S2 x-axis with human labels (Naiss., 1sem, 2sem, 1m, 2m … 1 an)
- growth: tooltip shows age in full words (e.g. "3 mois 1 sem.")
- cron: POST /api/cron/weight-percentile-alert — alerts when baby weight exits configurable WHO band (weight_percentile_min / weight_percentile_max SystemConfig keys, deduped per measurement)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:05:04 +02:00
arnaudne b417eb648c fix(build): disable webpack build workers to reduce memory
Next.js spawns per-worker webpack processes — each has its own heap.
On low-RAM servers total usage exceeds available memory → SIGKILL (silent).
webpackBuildWorker: false runs compilation in main process only.
Also lower JS heap cap to 512MB to leave room for OS + pnpm.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:21:01 +02:00
arnaudne 1c837a3aae fix(build): use webpack instead of Turbopack for production build
Turbopack (Next.js 16 default) OOM-kills on low-memory servers — BuildKit
loses the connection and reports EOF. Webpack uses significantly less RAM.
Also bumped NODE_OPTIONS heap limit to 2048MB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:10:49 +02:00
arnaudne 18074524e8 fix(docker): cap Node.js heap to 1536MB during build
Prevents OOM kill on low-memory servers during Next.js + Workbox build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:05:31 +02:00
arnaudne 6714e26716 fix(build): remove sharp devDep — breaks Docker --ignore-scripts install
sharp uses postinstall to download native binaries. Docker build uses
pnpm install --ignore-scripts which skips this → build fails on server.
PNG icons already generated and committed, sharp no longer needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:55:25 +02:00
arnaudne ef2ee3cf7a fix(pwa): add turbopack config to silence next-pwa webpack warning
next-pwa registers a webpack config; Next.js 16 requires turbopack: {}
when a webpack config exists and no turbopack config is set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:52:22 +02:00
arnaudne 95e02aca57 feat(pwa): full PWA setup with service worker and PNG icons
- Add @ducanh2912/next-pwa with NetworkFirst runtime caching (24h, 200 entries)
- Disabled in development to avoid SW interference
- Generate icon-192.png, icon-512.png, apple-touch-icon.png from SVG
- Update manifest.json to use PNG icons (required for Android install prompt)
- Add apple-touch-icon + appleWebApp metadata to layout
- Add /offline fallback page shown when network unavailable

Offline behavior: cached reads work on all platforms; background sync
for new events not supported (iOS Safari has no Background Sync API).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:50:29 +02:00
arnaudne 78fc31252e fix(settings): guard Notification API for iOS Safari
Notification is undefined on iOS Safari — bare access crashes on render.
Added "Notification" in window check before accessing .permission.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:47:34 +02:00
arnaudne eb1e5ee3fe fix(ui): lift modal above tab bar, remove duplicate last activities
- Modal container pb = 68px + safe-area-inset-bottom so footer always
  above the tab bar on mobile (was hidden behind it)
- Remove "Dernières activités" from dashboard (duplicates today's feed)
- Quick log grid goes full-width, 6 cols on sm+

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:46:18 +02:00
arnaudne 8152c02995 feat(pump): show all available lots for merge, add expiry input
- Replace today-only lot filter with all available lots (fridge + freezer)
- Show lot date (d MMM HH:mm) not just time for clarity
- Add expiry input when creating new lot (hidden when merging)
- Expiry unit label adapts: heures/jours/mois per storage location

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:42:16 +02:00
arnaudne 9fb1a726dd feat(pump): link pump events to milk stock, fix expiry units
- PUMP modal: "Ajouter au stock" toggle with location selector
- Merge option: pick existing today's lot to add volume to (expiry kept)
- On save: POST new MilkStock or PATCH existing volume
- Milk page: expiry input now in hours/days/months per location
- Milk page: "soon" alert threshold scales per location (freezer=7d, fridge=24h, room=2h)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:34:35 +02:00
arnaudne 142d27e85c fix(modal): proper mobile bottom sheet — sticky header/footer, scrollable body
- rounded-t-2xl on mobile, full-width (no side padding)
- maxHeight = 100dvh - safe-area-top - 68px nav - safe-area-bottom
- header and footer flex-shrink-0, body flex-1 overflow-y-auto overflow-x-hidden
- save buttons in pinned footer, larger touch targets (py-3, rounded-xl)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 19:02:23 +02:00
arnaudne 493f6ab7fc fix(compose): drop service_healthy, use service_started + sleep
pg_isready healthchecks broken server-wide (root_db, hoppscotch-db also
unhealthy). Remove healthcheck block, depend on service_started only.
migrate sleeps 5s to give postgres time to accept connections.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 18:38:05 +02:00
arnaudne 750c2c2f60 fix(compose): use CMD form for pg_isready healthcheck, add start_period
CMD-SHELL drops pg_isready from PATH on some Alpine setups. Switch to
explicit CMD array form with -h 127.0.0.1. start_period gives postgres
time to initialize before failures count against retries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 18:02:15 +02:00