Enlantis — utvecklingshistorik

Automatiskt genererad ur git-historiken (2026-01-09 → 2026-08-19). Byggd med npm run history:build.

1 716commits
265pull requests
2medverkande
+2 301 013rader tillagda
−996 862rader borttagna

Aktivitet per vecka (commits)

2026-v02: 8 commits2026-v03: 12 commits2026-v04: 1 commits2026-v06: 4 commits2026-v07: 6 commits2026-v12: 1 commits2026-v16: 6 commits2026-v17: 90 commits2026-v18: 142 commits2026-v19: 285 commits2026-v20: 58 commits2026-v21: 40 commits2026-v22: 38 commits2026-v23: 8 commits2026-v25: 26 commits2026-v26: 9 commits2026-v27: 70 commits2026-v28: 118 commits2026-v29: 119 commits2026-v30: 110 commits2026-v31: 228 commits2026-v32: 109 commits2026-v33: 141 commits2026-v34: 87 commits v02v04v07v16v18v20v22v25v27v29v31v33

Fördelning per kategori (PR + direkt-commits)

Funktioner 409
Buggfixar 260
Infra/verktyg 219
Tester 106
Design/UI 55
i18n 55
Dokumentation 23

Tidslinje

#272 Log B-280…B-282: one 403 root cause, three defects around it

2026-08-20 · Claude (AI) · 1 commits · 1 filer · +129 −0 · Buggfixar B-280 B-282

Detaljer
## Bakgrund Jonas fick ~40 konsolfel på djurdetaljsidan för en gård han själv har skapat (`/app/farms/e6aff2a3-…/animals/c9695117-…`). Utredningen visar att det inte är många fel utan **ett**: `RouteAuth.requireFarmer` avvisar honom på gården — han är varken ägare (`farm.owner_id == user.person_id`) eller aktiv `farmer`-rad. Bekräftat empiriskt i hans konsol: ```js await (await fetch('/api/farms', {headers:{'X-User-Id': localStorage.getItem('user_id')}})).json() // → [] (HTTP 200, tom lista) ``` Att statuskoden är **403 och inte 400** bevisar samtidigt att `X-User-Id` skickas, att användarraden finns och att gården finns — `RouteHelpers.handleRouteError` mappar saknad header, okänd användare och okänd gård till 400. Den här PR:en **loggar bara** de tre defekter som utredningen blottade. Ingen kodfix ingår. ## Ändringar Endast `other/KNOWN_BUGS.md` — tre nya poster. ### B-280 — `CustomFieldRoutes` mappar varje fel till 400 Fem anropsplatser (rad 113, 148, 180, 196, 215) använder fortfarande `.orElse(ZIO.succeed(Response.status(Status.BadRequest)))`, som sväljer allt — inklusive `Forbidden` från `RouteAuth`. Därför ligger två `400 (Bad Request)` mitt bland fyrtiotalet 403 och ser ut som ett separat fel. Exakt B-152:s rotorsak, i en fil som aldrig svepdes. Posten pekar ut det mekaniska svepet som återstår i stället för en punktfix. ### B-281 — appen låter dig navigera i en gård du saknar behörighet till Två oberoende hål: `farmStore.ts:120–127` hydrerar från Dexie och behåller cachade gårdar när serverhydreringen misslyckas (bara en `console.warn`), och det finns **ingen** `farms/[id]/+layout.*` — under `src/routes/app/` finns bara `+layout.svelte` och `admin/+layout.svelte`, så inget ställe kontrollerar gårdsscopet innan barnrutterna monteras. Posten innehåller också den **öppna delfrågan**: varför skaparen inte äger gården. Alla tre gårdsskapande vägar (`FarmRoutes.scala:92`, `SimRoutes.scala:185`, `SimSeedService.scala:330/334`) skriver `ownerId = user.personId`, och `POST /api/users/auth` är nycklad på e-post med UNIQUE-constraint — så koden pekar inte ut sig själv. Kvarvarande hypoteser och en läsbar `/api/users/me`-probe finns i posten. **Bifynd:** `db-patches/db-schema-example.sql:35` deklarerar `FOREIGN KEY (owner_id) REFERENCES user(id)` för `farm`. Det är fel ägarmodell — `db-migration-0094-farm-owner-fk-fix.sql:10` rättade FK:n till `person(id)`, men exempelfilen uppdaterades aldrig. Noterat i posten; kostade tid i den här utredningen. ### B-282 — 403 har ingen hanteringsväg alls i frontend Varje fetch-lager kastar sin egen sträng: `api.ts:91` (`API error: ${statusText}`), `documentStore.ts:45` m.fl. (`HTTP ${status}`). Den enda 401/403-hanteringen, `fetch.ts:48`, är villkorad på aktiv impersonering. Resultatet är att samma fel läses som `HTTP 403` i ett kort och `API error: Forbidden` i nästa. 402 har redan precis den centrala behandling som 403 saknar (`recordBillingGate402`). ## Nummerallokering `bun run bug:next 3` → högsta sedda B-279 (grenen till öppna PR #271). Allokatorn varnade för att `gh` saknas i sandboxen, så öppna PR:ar kontrollerades separat via GitHub-API:t (PR #271 → B-279, #268 → B-270, #16 → lägre) plus en scan av alla fjärrgrenar. `bun run bug:check` grön. ## Testning Dokumentationsändring, ingen kod rörd. ✅ **`check:ci` grön** — 14 gates godkända, 2 överhoppade: | Gate | Utfall | |---|---| | svelte-check, eslint, vitest | ✅ | | i18n coverage + hardcoded-English ratchet | ✅ | | WA-guards (boolean-attr, dialog-hide, variant/size) | ✅ | | a11y-kontrast, token-drift, layout-overflow | ✅ | | UUID/column-type schema guard | ✅ | | bug-number duplicate guard | ✅ 273 rubriker, alla unika | | bundle-size perf budget | ⏭ ingen produktionsbygge | | `sbt test` (backend) | ⏭ `sbt` saknas på PATH i den här sandboxen | Backend-hoppet spelar ingen roll här — inga `.scala`-filer är ändrade. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Sxmnzk2f
  • 5862fe7 Log B-280…B-282: one 403 root cause, three defects around it Claude (AI) · 2026-08-10 · +129 −0

#16 docs: design study — Sabha as internal staff+AI collaboration chat

2026-08-20 · Claude (AI) · 1 commits · 4 filer · +709 −0 · Design/UI

Detaljer
## What Design study (documentation only — no code, schema, or dependency changes) for introducing the MIT-licensed, self-hosted [Sabha](https://github.com/sabha-co/sabha) chat platform as an **internal collaboration tool for employees + the 10 AI colleagues** — explicitly never customer-facing. - **`other/SABHA_INTERNAL_CHAT_PLAN.html`** — self-contained HTML design document (inline CSS + SVG diagrams, light/dark, no external deps; open straight from the repo). Written in Swedish. Covers: - Recommendation: deploy Sabha as a separate self-hosted service rather than growing the AI-Ops chat into a team messenger (decision matrix included) - Access model: new `user.is_staff` flag + `RouteAuth.requireStaff` (root-admins implicitly staff) - SSO: Enlantis backend as DiscourseConnect provider — the only door into the chat, gated on `is_staff` (sequence diagram + payload details) - AI bridge: one Sabha bot per `ai_role`, webhook → `AiAgentDispatcher` wired to `LlmClient.completeWithTools` + KB tools (`search_kb`/`read_wiki_page`/`write_memory`) - Hosting comparison (GCE VM vs Cloud Run+Litestream vs Sabha Cloud) with a GCE-VM recommendation - Security/GDPR notes, 4-stage rollout (~7–10 dev-days), risk register - **`docs/wiki/architecture/ai-ops.md`** — new "Planned" section pointing at the design doc (+ `docs/wiki/log.md` entry) - **`other/ENLANTIS_MASTER_TODO.md`** — new "Internal Tooling (staff + AI)" checklist with the three implementation stages ## Verification - `npm run wiki:lint` — OK, 77 pages, no broken wikilinks - HTML document rendered in headless Chromium in both light and dark mode; all three SVG diagrams, tables, `<details>` sections and stage cards verified 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DpB9LDrXuVrwqNP9sNRnDW --- _Generated by [Claude Code](https://claude.ai/code/session_01DpB9LDrXuVrwqNP9sNRnDW)_
  • 5bf0764 docs: design study — Sabha as internal staff+AI collaboration chat Claude (AI) · 2026-07-06 · +709 −0

#342 B-315 step 2: rebuild the CAP conditionality corpus against Reg 2021/2115 primary text, end the interim decline

2026-08-20 · Jonas · 4 commits · 7 filer · +457 −194 · Buggfixar B-315

Detaljer
## What Rebuilds the CAP conditionality corpus that PR #329 quarantined, so the copilot answers villkorlighet/conditionality questions again instead of declining. Decided by Jonas 2026-08-19 at the merge of #329 — the interim decline was to be as short-lived as possible. - **`src/main/resources/eurlex/cap-conditionality.md`** now holds **eight chunks**, every factual claim verified 2026-08-19 directly against CELLAR-fetched English primary text (`Accept-Language: eng`) of **Reg (EU) 2021/2115** (Annex III + Articles 12/13/31/97) and **Dir 91/676/EEC** (consolidated, for the 170 kg N/ha figure) — not against research memos, which is how the original wrong-regime errors slipped in: Art 12 overview (incl. the explicit *no animal-ID SMR exists* fact), the full SMR 1–11 and GAEC 1–9 lists, animal-welfare SMRs 9/10/11 + hormones-ban SMR 6, nitrates SMR 2, eco-schemes Art 31, and the ≥25% eco-scheme allocation Art 97 (the figure the old corpus mis-cited to Art 109). - **All eight chunks are gated** by `EurLexCorpusHeadingGateSpec`: `32021R2115` (160 article headings + annex titles) added to `_headings.json` via `update-headings.mjs` (new `extractAnnexTitles`, additive-only diff), a bare-annex locator added to `parseLocator` (tried strictly after the article pattern — every pre-existing chunk resolves identically), and the CAP exemption list is now **empty** (`cap-cond-gaec1` included — the old "no CELEX" reason described the chunks' missing anchors, not the regulation, which exists in CELLAR). - **`EurLexQa.ask`'s keyword short-circuit removed** — conditionality questions go through normal retrieval. `EurLexQaSpec` rewritten to pin the post-rebuild behaviour (EN + SV conditionality questions retrieve cap-cond chunks and reach the LLM; non-CAP questions unaffected). - **Stress bank:** `cap-001` restored to `expect:"answer"` + `must_cite:"2021/2115"`; the /code-review pass caught that its Swedish phrasing ("tvärvillkorskontroll", "EU-stöd") had zero token overlap with the corpus, so the art12 chunk now ends with a Swedish terms-of-art line that makes the realistic phrasing retrieve (verified by replicating tokenise/search over the real corpus). New `cap-004` smoke question added after `cap-003`. - **Docs:** B-315 step-2 addendum in KNOWN_BUGS, rebuild note appended to the quarantine doc (kept as diff base per its own instruction), wiki `cap-conditionality` page fixed (the Natura 2000 grassland ban was mislabelled **GAEC 5**; verified Annex III says **GAEC 9**) + `log.md`; `wiki:lint` OK. ## Independent verification A separate Sonnet review agent re-verified every chunk claim against the fetched primary texts and the code diff: verdict **approved with one finding** — an inherited, unbacked "Member States may impose reconversion obligations" sentence in `cap-cond-gaec1` (not in Reg 2021/2115 anywhere). Fixed in the follow-up commit per the handoff's never-guess rule (§5.6): replaced with Article 13-grounded text. ## Test results - `sbt ciTest`: ✅ **4005 passed, 0 failed** (run with `set ThisBuild / exportJars := false` — the git worktree path lacks the Defender exclusion `C:\code\enlantis` has, so `Compile/packageBin`'s jar move hits the known sbt2 AccessDenied crash there; the flag only skips jar export, all tests run). - Targeted specs (`EurLexCorpusSpec` + `EurLexCorpusHeadingGateSpec` + `EurLexQaSpec`): ✅ 15/15. - `FULL_CI=1 check:ci` (frontend): ✅ every step green except the vitest step, where the same two **tree-sweep tests timed out at 5000ms under full pool saturation** (Playwright overflow guard's 21 workers + eslint + svelte-check concurrently, in the un-excluded worktree). Both tests — and the **full vitest suite, 3495/3495** — pass standalone in the same worktree in seconds. The diff touches zero frontend source, so this is a worktree-environment flake, not a regression. - `overflow:check` (FULL_CI-forced): ✅ 129 route+viewport combinations, PASS. ⚠️ Per B-233: rebase onto master and re-run `FULL_CI=1 npm run check:ci`
  • a478951 review-fix: make cap-001's Swedish phrasing actually retrieve, honest note Jonas · 2026-08-19 · +3 −3
  • c05974b review-fix: drop the unbacked reconversion sentence from cap-cond-gaec1 Jonas · 2026-08-19 · +1 −1
  • 22b85d7 docs: record the B-315 CAP corpus rebuild (KNOWN_BUGS, quarantine doc, wiki) Jonas · 2026-08-19 · +54 −2
  • a2a8c2d Rebuild CAP conditionality corpus against Reg 2021/2115 primary text (B-315 step 2) Jonas · 2026-08-19 · +399 −188

#338 Log B-320: root admin sees every farm's economy, ungranted and unlogged

2026-08-20 · Claude (AI) · 1 commits · 1 filer · +44 −0 · Buggfixar B-320

Detaljer
The open question raised on #330 — it was written while that PR was in flight but didn't make the merge, so it lands here instead rather than disappearing with the conversation. **Not a regression, and not a defect.** Root admin is a blanket bypass across the whole of `RouteAuth` — `requireOwner`, `requireFarmer`, `requireRole`, `requireAnyWriter`, and now `requireEconomicAccess` all pass a root admin through unconditionally. For the money gates that means Enlantis staff can read a customer's revenue, costs and margin, plus the four accounting exports, with no grant and with nothing written down anywhere saying they did. It's logged rather than fixed because it's a product decision. The rule for #330 was "Owner and Manager see the economy by default, everyone else needs granted access" — and a root admin isn't a farm role at all, which is about as outside a farm's economy as it gets. But narrowing one gate out of six unilaterally would leave `RouteAuth` inconsistent in a way the next reader trips over. The entry records both viable answers with what each costs, so whoever picks it up isn't starting from scratch: 1. **Operators request a grant like anyone else** — cleanest against the rule, but makes economy-related support cases slower, since root admin stops being a debugging tool for exactly the area someone is calling about. 2. **Keep the bypass, log every use** — each economic hit writes to `admin_user_audit` (migration 0253, already append-only for this exact purpose). Cheaper, and at least supplies the trail that's missing today. `economic_access_grant` (0271) already carries `granted_by`/`granted_at`, so either answer reuses infrastructure that shipped with #330. The entry also notes `impersonation_session` (0247) already logs when an operator acts as a user — precisely the kind of trail absent here. Docs only, one file. `bug:check` green, 309 unique headings. --- _Generated by [Claude Code](https://claude.ai/code/session_01EjegFXjeqmWohfPzPCiU69)_
  • c071ad1 Log B-320 and record its same-evening resolution: economic reads by root admin are now stamped, PR #341 Claude (AI) · 2026-08-19 · +44 −0

#339 Fix B-321: master is red — B-313's own detector test fails in every agent sandbox

2026-08-20 · Claude (AI) · 2 commits · 2 filer · +47 −3 · Buggfixar B-321 B-313

Detaljer
**`npm run check:ci` currently fails on master** in any agent sandbox. Found while gating #330 after rebasing it — everything green except `vitest`, and the failure wasn't in that branch at all: ``` FAIL ../scripts/sbt-launcher-detect.test.mjs > detectSbtShim > returns null when the shim exists but no jar candidate does AssertionError: expected { shim: '/tmp/b313-Gn9BjD/sbt', … } to be null + Received: { jar: "/root/.local/opt/sbt-launch/sbt-launch.jar", … } ``` Confirmed on a clean checkout of `origin/master`: **1 failed | 10 passed**. ## Cause `detectSbtShim` always appends a hardcoded last-resort candidate — `/root/.local/opt/sbt-launch/sbt-launch.jar`, where `bootstrap-backend-toolchain.sh` installs the jar. The test covering "no jar found" only makes the jar the *shim* references missing, not that one. In an agent sandbox that file genuinely exists, so the function returns it instead of `null`. Environment-dependent in the worst direction: it passes on the Windows box (no such path) and fails in every agent session (path present). B-313 exists precisely because the gate must work in agent sandboxes — and its own test made the gate red exactly there. ## Fix The fallback list becomes an injectable third parameter (`jarFallbacks`, defaulting to the exported `DEFAULT_JAR_FALLBACKS`), and the test passes `[]`. **Production behaviour is byte-identical** — the default is the same single-element list as before, and `check-ci.mjs:830` calls `detectSbtShim(findOnPath('sbt'))` with two args, so it picks up the default unchanged. The test simply stops asking the running machine's filesystem about a hardcoded path. Worth keeping in mind generally: a test that reads an absolute path outside its own temp directory isn't hermetic, however clean the assertion looks. This one passed review and merged because the green result came from a machine that happened to lack the path. ## Verification - Targeted: **11/11 passing** (was 10/11). - Full frontend suite: **3 495 passing across 209 files**. - `bug:check` green, 309 unique headings. ## Note on the second commit The B-321 entry was missing from the first commit even though its message described it: the heredoc writing it failed on a stale working directory, and the `npm run bug:check | tail -1` guarding the chain swallowed the non-zero exit — a pipe returns `tail`'s status, not the command's. Same masking trap that bit a rebase earlier in this session. The follow-up commit adds the entry, verified with an explicit exit code rather than a pipe. --- _Generated by [Claude Code](https://claude.ai/code/session_01EjegFXjeqmWohfPzPCiU69)_
  • d450b25 Add the B-321 entry the previous commit's message described Claude (AI) · 2026-08-19 · +33 −0
  • 9904c35 Fix B-321: B-313's own detector test made check:ci red on master Claude (AI) · 2026-08-19 · +14 −3

#341 Root-admin economic reads are access-with-a-trace, migration 0272

2026-08-19 · Jonas · 1 commits · 12 filer · +302 −19 · Infra/verktyg

Detaljer
Verkställer beslutet på PR #330:s öppna fråga: Enlantis-personal behåller ekonomisk åtkomst till kundgårdar, men aldrig mer tyst. - `requireEconomicAccess`:s root-admin-bypass stämplar nu en rad i `admin_economic_read_audit`, migration 0272, som förhandsvillkor. Ingen persisterad rad, ingen åtkomst. Fail-closed även utan installerad sink. - Tabellen är append-only utan FK:er så spåret överlever hard-deletes, samma postur som 0253. Owner-, Manager- och grant-läsningar stämplas aldrig, och owner-grenen prövas före root-admin-grenen så en admin som läser sin egen gård inte loggas som personalinsyn. - Sinken är en modulsingleton, `RouteAuth.installEconomicReadAudit`, samma mönster som impersonation-repot, så gatens miljötyp och dess tio route-filer är orörda. - Lässida: `GET /api/admin/economic-reads`, root-admin-only, nyast först, med gårdsnamn och admin-mejl upplösta. Utan den var spåret skrivbart men oläsbart. - CLAUDE.md-liggaren flyttad till 0272, nästa fria 0273. Wiki `auth-and-roles` uppdaterad, wiki-lint grön. Verifiering: check:ci grönt, frontend + backend, `FULL_CI=1` lokalt. 4032 backend-tester gröna varav tre nya i RouteAuthSpec: stämpeln skrivs, fail-closed nekar vid fallerande sink, owner-som-också-är-admin stämplas inte. AdminUserRoutesAuthSpec gatar nya endpointen, 75/75 i de två specarna. Oberoende review körd i sessionen: tre fynd, alla åtgärdade före denna PR, scaladocen, lässidan, liggaren. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 4ef8b70 feat(auth): root-admin economic reads are access-with-a-trace, migration 0272 Jonas · 2026-08-19 · +302 −19

#340 30s vitest budget for tree-sweeping suites (4 flakes 2026-08-19)

2026-08-19 · Jonas · 1 commits · 1 filer · +11 −1 · Tester

Detaljer
check:ci's vitest step timed out four times on 2026-08-19, always the same two tree-sweeping tests (a11y.test.ts:265, check-contrast.test.mjs:348), always green standalone, always while the gate's own sbt ciTest saturated the CPU. testTimeout moves to 30s globally in frontend/vitest.config.ts since the sweep often lives in the imported module and a per-file hunt misses cases. Verified under the exact flake conditions: FULL_CI=1 check:ci green with vitest (35.1s) running concurrently with sbt ciTest (355.8s). check:ci green (frontend + backend).
  • 8607a88 fix(ci): 30s vitest budget for the tree-sweeping suites (4 flakes 2026-08-19) Jonas · 2026-08-19 · +11 −1

#330 Economic access as a granted permission: money figures are Owner/Manager-only by default

2026-08-19 · Claude (AI) · 4 commits · 38 filer · +1 723 −178 · Buggfixar B-039

Detaljer
Implements Jonas's rule: **Owner and Manager see the economics by default; everyone else needs a granted permission.** Outsiders are kept out of the money by default and can be let in on request, not silently. ## The gate New `RouteAuth.requireEconomicAccess` is the single place every money endpoint goes through. It passes: the farm owner, a root admin, an active `Administration` member ("Manager"), or a member holding a currently-valid grant. Migration **0271** adds `economic_access_grant`, modelled on `employee_role`'s `farmer_id`/`from_date`/`to_date` time-box, plus `granted_by`/`granted_at` for the audit trail. Revoking sets `to_date` — the row is never deleted, so who-had-access-when stays answerable. **Role-mapping caveat, recorded rather than papered over:** the rule names "Manager", but `EmployeeRole` — the enum actually persisted to `employee_role` — is `Administration | Farmer | Vet | ViewOnly`. There is no `Manager` value (`model.farm.Role` documents the aspirational Owner/Manager/Farmhand model but isn't wired to farm membership). `Administration` is the existing full-operational-control role, so it is treated as Manager here — the same mapping `AnimalPnlRoutes` already made for this enum gap. ## Endpoints **Hard gate (403 without access):** `AnalyticsRoutes`, all three `AnimalPnlRoutes`, `ShopAnalyticsRoutes`, and the four accounting exports (Fortnox/Sage/Datev/ExactOnline) — which were previously plain `requireFarmer` and are the most sensitive of the lot. **A backdoor found and closed:** `SavedReportRoutes`' report-builder could reach the `animal_pnl` source directly, bypassing the entire `AnimalPnlRoutes` gate. Gated now, but only for that source — every other report source is operational data and stays open. A permission model with one unguarded path isn't one. Two endpoints get *wider* access, not narrower: two `AnimalPnlRoutes` handlers were owner-only, and now admit Manager and grant-holders too. **Soft degradation (never 403):** the dashboard's `profitability-summary` and `past-optimal-animals` stay on `requireFarmer` and use the non-throwing `hasEconomicAccess` to null the money fields (`Option[Double]`, `None` not `0`, plus an `economicAccess` flag). `FarmAnalyticsSummary`'s `Promise.all` would otherwise blank the whole card grid — including animal counts, high-risk and overdue — over one permission gap. An outsider should lose the money, not the operational picture. ## Two defects found by running the tests, which had never run The agent that wrote this had sbt OOM-killed on every attempt (see B-313), so its 20 new tests were reported as evidence while never having been type-checked. 1. **The suite didn't compile.** `economicLayer` and `runEconomic` were written correctly, but all 12 `requireEconomicAccess` tests called the older `runAuth`, typed for only four repositories — 12 identical "requires services that are not in the environment" errors. A suite that doesn't compile is worse than an absent one: it reads as coverage in the diff while proving nothing. 2. **Once compiled, a real auth inconsistency surfaced.** "Root admin passes even with no farm membership" failed. `RouteAuth` has *two* root-admin predicates — `isRootAdmin` over the config email list, and `isRootAdminUuid` over `cachedRootAdminUuids`, which is the set `requireRootAdmin` itself gates on. This gate checked only the email axis, so it disagreed with `requireRootAdmin` about who is a root admin, and the case was untestable (the email list is config-only). Now checks both, exactly as the impersonation gate at line ~217 already does. ## ⚠️ Open question for Jonas — not decided here **Should an Enlantis operator see a customer farm's revenue, costs and margin at all, with no grant and no record that they looked?** Root admin is a system-level bypass across every `RouteAuth` gate, so today the answer is yes. I kept that rather than narrowing this one gate and surprising the next reader — but it sits awkwardly beside "outsid
  • bd6a7d8 fix(auth): make requireEconomicAccess agree with requireRootAdmin on who is one Claude (AI) · 2026-08-18 · +37 −29
  • 8ce9579 fix(test): point the requireEconomicAccess suite at the runner that has its layers Claude (AI) · 2026-08-18 · +12 −12
  • 5237dbd Renumber the economic-access migration 0270 -> 0271 (collides with B-039's) Claude (AI) · 2026-08-18 · +8 −9
  • d49f26e feat(economics): gate money data behind an explicit economic-access grant Claude (AI) · 2026-08-18 · +1 666 −128

#337 Prose rules for agent text: unslop skill + CLAUDE.md section

2026-08-19 · Claude (AI) · 3 commits · 2 filer · +110 −9 · Funktioner

Detaljer
Adds writing rules for agent-authored text, requested by Jonas after evaluating the pstack `unslop` skill. - Added `.claude/skills/unslop/SKILL.md`: the full 31-pattern catalog (adapted from kentnielsen-droid/cursor-skills), loaded on demand for longer prose deliverables (docs, wiki, marketing copy, release notes, LLM prompt style). Two repo overrides: established repo terms (ratchet gate, outbox) beat the plain-word rule, and everything applies to Swedish too, em dash ban included. - Rewrote the CLAUDE.md section as always-on distilled rules covering chat replies, PR descriptions and commit messages: outcome first, one-line bullets for facts, prose with an opinion for reasoning, specificity over value-words, no em dashes, TL;DR line last on genuinely long replies only. Docs-only change; no code touched, so `check:ci` was not run.
  • 4e1441a Add unslop skill and extend prose rules to PR bodies and commits Claude (AI) · 2026-08-19 · +97 −8
  • f9ef9dd Put chat-reply TL;DR at the end, not the top Claude (AI) · 2026-08-19 · +1 −1
  • 7cf5666 Add chat-reply brevity rules to CLAUDE.md Claude (AI) · 2026-08-19 · +12 −0

#329 B-315: quarantine 5 of 6 wrong-regime CAP conditionality chunks, and decline rather than cite them

2026-08-19 · Claude (AI) · 1 commits · 7 filer · +455 −37 · Buggfixar B-315

Detaljer
The AI corpus file `src/main/resources/eurlex/cap-conditionality.md` described the **pre-2023 cross-compliance regime** while claiming to describe "the new CAP 2023–2027". Copilot was citing it to farmers. Verified against primary text — Reg (EU) 2021/2115 fetched directly from CELLAR (CELEX `32021R2115`, `Accept-Language: eng`), reading Annex III, Article 31 and Article 97 in full, rather than trusting the earlier research memo. | chunk | verdict | evidence | |---|---|---| | `cap-cond-gaec1` (permanent grassland) | ✅ **correct — kept live** | Matches Annex III GAEC 1 verbatim, including the 5 % maximum decrease | | `cap-cond-smc4` (animal ID) | ❌ wrong regime | Current Annex III has **no animal-identification SMR at all** — those were dropped in the 2023 reform | | `cap-cond-gaec9` (livestock density) | ❌ wrong regime | Real GAEC 9 is the ban on converting/ploughing permanent grassland in Natura 2000 sites — nothing about density | | `cap-cond-smc11` (nitrates) | ❌ wrong number | Content right; nitrates is **SMR 2**, under Annex III's "Water" area | | `cap-cond-smc12-smc13` (welfare) | ❌ wrong number + conflation | Calves = **SMR 9**, pigs = **SMR 10**; the antibiotics clause actually belongs to SMR 6, never cited | | `cap-cond-eco` (eco-schemes 25 %) | ❌ wrong citation — **found in this pass** | Art 31 defines eco-schemes, but the ≥25 % allocation is **Art 97**, verbatim: *"At least 25 % of the allocations set out in Annex IX shall be reserved…"*. Art 109 (cited) is unrelated | **5 of 6 wrong.** The sixth, `cap-cond-eco`, is one the research memo had explicitly marked "not verified" — checking it closed that gap, and it was wrong too. ## What this does The five bad chunks are removed from the live corpus and preserved **verbatim** in `other/CAP_CHUNKS_QUARANTINED_2026-08-18.md`, alongside the per-chunk evidence table and a transcription of the real Annex III, so the eventual three-layer rebuild has something to diff against rather than starting from a blank page. `EurLexQa.ask` now detects a conditionality question by keyword (sv: villkorlighet/tvärvillkor/grundvillkor; en: conditionality/cross-compliance) and short-circuits **before** `corpus.search`. That bypasses both previous failure modes: B-287's empty-token `chunks.take(k)` fallback, and confident answers assembled from unrelated overlapping chunks. Citations are empty in both languages — no fabricated legal reference. ## ⚠️ This deliberately conflicts with a stated preference, for now Jonas asked plainly: *"Jag vill inte att copilot ska avböja eller svara fel, den ska kunna hjälpa till."* This ships the **declining** half of that. The reasoning, stated rather than buried: citing the wrong article to a farmer preparing a subsidy dossier is worse than saying "I don't have verified material for this yet — contact Jordbruksverket." A wrong citation is silently trusted; a decline is visibly incomplete. So this is the interim state, not the destination. The destination is the three-layer model in `other/REGELVERK_TIDSMODELL.md` §2, which is what makes Copilot able to answer this properly — and until those layers exist, there is nothing correct to answer *from*. The decline is scoped narrowly to conditionality questions; every other EUR-Lex topic still goes through normal retrieval. `scripts/copilot-stress/questions.json`'s `cap-001` changed `expect: "either"` → `"refuse"` with a note recording why, so the stress suite asserts the interim behaviour instead of drifting past it. ## Tests Red→green proven by forcing the short-circuit condition to `false`: the end-to-end decline test failed as expected on both `citations.isEmpty` and the Jordbruksverket text. Restored, plus a corrected assertion for "an unrelated question still reaches retrieval" — that one does *not* fail the effect, because `LiveEurLexQa.ask`'s `catchAll` degrades gracefully, so the real proof is `text.startsWith("LLM call failed")` with non-empty citations and no decline text. 19/19 across `Eur
  • 094b6bd Quarantine 5 of 6 wrong-regime CAP conditionality chunks (B-315) Claude (AI) · 2026-08-18 · +455 −37

#336 feat(dev): native Linux/WSL start-dev.sh, keeping the PowerShell delegation

2026-08-19 · Claude (AI) · 1 commits · 1 filer · +336 −5 · Funktioner

Detaljer
## What Turns `start-dev.sh` into a real Linux/WSL dev launcher — the POSIX counterpart of `start-dev.ps1` — **without losing what it did before**. ## The existing wrapper is preserved, not replaced `start-dev.sh` was a six-line wrapper that exec'd `start-dev.ps1` through `powershell.exe`, because `.\start-dev.ps1` can't be run from bash. That use case is real: from Git Bash, or from WSL with the repo on a Windows drive, the Windows toolchain is the one that matters. So the script now detects a Windows-owned checkout — `MINGW*`/`MSYS*`/`CYGWIN*` uname, or a `/mnt/<letter>/` repo path — and delegates exactly as before, printing which path it took. `--native` and `--windows` override the detection. Only a checkout on a native Linux filesystem takes the new path. ## The native path keeps the PowerShell version's guards Three of the four aren't Windows trivia: - **Port 5173 is mandatory.** `vite.config.ts` sets `strictPort: true` (Kinde only registers `localhost:5173`; the Playwright e2e config assumes it too), so Vite won't fall back to 5174. A foreign occupant is named, with the `kill` command to free it, instead of starting a dev server that dies immediately. - **Two sbt JVMs against one `target/`** is racy and can corrupt incremental-compile state, so a busy sbt means frontend-only. - **`check:ci` ends in `sbt ciTest`** against that same `target/`, so an overlapping run gets a warning. - **A cold Scala compile takes 2–4 minutes**, so ports are waited on rather than assumed — no success claimed that wasn't observed. Backend and frontend land in two tmux panes, or background processes logging to `.dev-logs/` when tmux is absent or `--no-tmux` is passed. ## Two bugs found by testing, not by review **Ownership detection can't climb the parent chain.** The PowerShell version does, because `bun.exe` carries no useful path on its command line. On Linux that climb is far too permissive: an unrelated `python3 -m http.server` on 5173 was confidently reported as *our* dev server, because the shell that launched it happened to sit in the repo. The process's own `/proc/<pid>/cwd` is both simpler and exact — `cd frontend && bun run dev` leaves the listener with `cwd=<repo>/frontend`, and nothing outside the repo can fake that. **The Java check accepted any JDK that existed.** A box with `JAVA_HOME` set to `java-21-openjdk` had that promoted straight to `JAVA25_HOME` — worse than exporting nothing, since `build.sbt` reads `JAVA25_HOME` and would hand sbt the wrong JDK for a Scala 3.8.3 build. It now verifies the version, matching the whole `java -version` output rather than `head -1`, because a JVM with `JAVA_TOOL_OPTIONS` set prints a `Picked up …` line first — the same trap `scripts/bootstrap-backend-toolchain.sh` already documents. ## Verification Exercised against real listeners rather than reasoned about: | Scenario | Result | |---|---| | Foreign process on 5173 | named, refused, exit 1 | | Foreign on 5173 + something on 8090 | frontend blocked, no false success | | Real `bun run dev` on 5173 | correctly recognised as ours, skipped | | Both ports already served | "nothing to start", exit 0 | | `JAVA_HOME` = Java 21 | rejected with a bootstrap pointer | | Windows-side detection forced, no `powershell.exe` | falls through to native | ## check:ci ⏭ Not run in full — this adds a developer convenience script that no gate covers (`check:ci` does not lint or execute `start-dev.*`). The pre-push quick gate passed. Behaviour was verified directly, per the table above. --- _Generated by [Claude Code](https://claude.ai/code/session_01Jb1fcvSi1SchZgrvFWZBoe)_
  • 842f037 feat(dev): native Linux/WSL start-dev.sh, keeping the PowerShell delegation Claude (AI) · 2026-08-19 · +336 −5

#335 B-313: bypass sandbox sbt shims that override the repo's .jvmopts

2026-08-19 · Jonas · 2 commits · 6 filer · +259 −4 · Buggfixar B-313

Detaljer
## B-313 — sandlådans sbt-shim åsidosätter repots `.jvmopts`; backend-grinden kan inte kompilera kallt Rotorsaken (fastställd i #332): agent-sandlådor löser `sbt` till en ~378-byte-shim som `exec`:ar java med hårdkodat `-Xmx1280m` och läser varken `.jvmopts`, `.sbtopts` eller `SBT_OPTS`. Repots 8 GB blir 1 280 MB och en kall kompilering av 748 källfiler dör i OOM — medan varm cache döljer felet helt. ## Fixen `check-ci.mjs`:s backend-steg upptäcker shimen och kringgår den: - **`scripts/sbt-launcher-detect.mjs`** (ny, enhetstestbar modul): shim = liten fil (<4 KB) + hårdkodad `-Xmx` + inget omnämnande av `.jvmopts` (riktiga launchern är motsatsen på alla tre punkter). Jar-uppslag: env-var som shimen refererar → literal i shimen → bootstrap-platsen `/root/.local/opt/sbt-launch/sbt-launch.jar`. Konservativ åt rätt håll: varje tveksamt fall ⇒ `null` ⇒ dagens beteende. - Vid träff startas **`java <flaggor ur .jvmopts> -jar sbt-launch.jar ciTest`** direkt — exakt den avgränsning B-313-postens fälla kräver: enbart launcher-JVM:en, aldrig `_JAVA_OPTIONS` (som ärvs av de forkade test-JVM:erna som `build.sbt` medvetet kapar till 512 MB). - **Windows går aldrig in i grenen** (`isWin`-vakt; riktiga sbt läser `.jvmopts` — uppmätt via `sbt -v`). - **11 enhetstester** mot fabricerade shims (`scripts/sbt-launcher-detect.test.mjs`). Repo-rotens gate-skript hade ingen testrunner alls — `frontend/vitest.config.ts` inkluderar nu `../scripts/**/*.test.mjs`. - B-313-posten uppdaterad (ÅTGÄRDAD med kvarstående slutbekräftelse), UTFALL-block i `HANDOFF_B313_SBT_COLD_COMPILE.md`. ## Verifiering ✅ check:ci grönt (frontend + backend), `FULL_CI=1` lokalt på Windows-boxen: - Alla steg gröna, inklusive **`sbt ciTest` 324 s genom den nya kodvägen** (Windows-grenen, dvs. oförändrat beteende bekräftat på riktigt). - Detektorns 11 tester gröna; end-to-end-simulering av sandlådevägen (fabricerad shim + jar på PATH) komponerar exakt kommandot `java -Xmx8G -Xms2G -Xss8M ... -jar sbt-launch.jar ciTest`. - **Kvarstående slutbekräftelse** (går inte att göra härifrån): en kall `FULL_CI=1 npm run check:ci` i en färsk sandlåda med borttagen `target/` — instruktion tillagd i handoffens UTFALL-block; först då bockas B-313 av helt. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 777e81d docs: note the B-313 shim bypass in CLAUDE.md's Local CI section Jonas · 2026-08-19 · +1 −1
  • 63fe7d7 fix(ci): B-313 — bypass sandbox sbt shims that override the repo's .jvmopts Jonas · 2026-08-19 · +258 −3

#333 B-318: first-time DPA dialog no longer implies a prior acknowledgement

2026-08-19 · Jonas · 1 commits · 7 filer · +55 −30 · Buggfixar B-318

Detaljer
## B-318 — förstagångs-DPA-dialogen antydde en tidigare bekräftelse `DpaReacknowledgementDialog.svelte` växlar title/lead/body/ackLabel/submit/success på `isFirstTime`, i linje med sitt eget kontrakt ("a keeper who has never acknowledged must never be told the agreement 'changed'") — men diff-panelens rubrik renderades ovillkorligt med `dpaReprompt.diffHeading` ("Vad som ändrats sedan du senast bekräftade"). En användare som aldrig godkänt DPA fick alltså en rubrik som antyder en bekräftelse som aldrig skett. **Fix:** ny nyckel `dpaReprompt.diffHeading.first` ("Versionshistorik" / "Version history") i `misc-2.mjs` + `_order.mjs`; rubriken växlar nu på `isFirstTime`. Panelen visas fortsatt i båda lägena — dok-kommentaren säger uttryckligen att hela historiken är rätt innehåll för en förstagångsanvändare; det var bara rubriken som var fel. Hittad under den lokala slutverifieringen av #327:s DPA-spärr (färsk dev-användare, gård skapad via /welcome steg 1, guiden lämnad före AVTAL-steget). KNOWN_BUGS B-318 loggad i samma commit. ## Verifiering ✅ check:ci grönt (frontend + backend), körd lokalt på Jonas maskin: - Frontend: alla steg gröna utom **flags-guarden, vars fel är en förexisterande CRLF-fantom på Windows-checkouten** — `npm run flags:build` ger tom git-diff (endast radslut), och `flags:check` går grönt direkt efter. Inte relaterad till branchen; separat uppgift flaggad för att göra jämförelsen radslutsokänslig. - Backend: `sbt --no-server test` → **2265 tester gröna, 0 fel, 4 ignorerade** (`[success]`, 298 s). - i18n: `i18n:build` + `i18n:check` gröna. - **Visuellt verifierad** (CLAUDE.md-kravet): färsk dev-användare med gård och utan ack → dialogen öppnas med "Du har inte godkänt vårt personuppgiftsbiträdesavtal (v1) än" och panelrubriken **"Versionshistorik"**; övriga strängar oförändrade. Även kontrollerat att version-bump-läget inte påverkats (nyckeln `dpaReprompt.diffHeading` är kvar orörd). - Pushen gjordes med `SKIP_CHECKS=1` enbart p.g.a. CRLF-fantomen ovan — de fulla sviterna var redan körda. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 11dd69b fix(i18n): B-318 — first-time DPA dialog no longer implies a prior acknowledgement Jonas · 2026-08-19 · +55 −30

#334 B-319: EOL-insensitive generated-file checks + eol=lf attributes (flags:check blocked every Windows push)

2026-08-19 · Jonas · 1 commits · 4 filer · +46 −3 · Buggfixar B-319

Detaljer
## B-319 — `flags:check` föll på radslut, inte på innehåll, och blockerade varje push från en Windows-checkout **Uppmätt 2026-08-19** när pre-push-kroken stoppade pushen av PR #333: `npm run flags:check` → "codes.ts is out of date", `npm run flags:build` skrev om filen — och `git diff` var då **tom** (bara "LF will be replaced by CRLF"-varningen). Enda skillnaden var radslut: generatorn skriver alltid LF (`fs.writeFileSync`), medan `core.autocrlf=true` (aktiv på dev-boxen) checkar ut `codes.ts` med CRLF, och `--check` jämför byte-exakt (`current !== body`). Gaten sitter i både `check:ci` och den snabba pre-push-kroken, så **varje push blockerades** tills `SKIP_CHECKS=1`. ## Ändringar - **`frontend/scripts/build-flag-codes.mjs`**: `--check` normaliserar CRLF→LF på arbetskopie-sidan före jämförelsen. - **`scripts/rules/build-seed-from-draft.mjs`**: samma normalisering för per-area-seed-SQL:en och manifestet — samma fantomklass (byte-exakt jämförelse mot genererade filer), dock inte push-blockerande eftersom den inte är wirad i `check:ci`. - **`scripts/eurlex/update-headings.mjs`**: granskad, behöver inget — den diffar parsad JSON, inte bytes. - **Ny `.gitattributes`**: pinnar `eol=lf` på de genererade filerna (flags `codes.ts`, `frontend/src/lib/i18n/*.json`, rulebook-seed-SQL + manifest) så arbetskopiorna slutar divergera från generatorernas output över huvud taget. Detta pensionerar också den sedan länge kända i18n-"fantomdiffen" (samma klass; tidigare enda botemedel var `git restore`). - **KNOWN_BUGS**: B-319 loggad (allokerad via `bun run bug:next`). Övriga `--check`-skript (`wa-bool-attrs`, `wa-dialog-hide-guard` m.fl.) är skannrar utan genererad-fil-jämförelse och berörs inte. ## Verifiering ✅ check:ci grönt (frontend; backend orörd), körd lokalt på Windows-boxen: - **Fixen verifierad på en arbetskopia med bekräftad CRLF** (`file(1)`: "with CRLF line terminators"): `flags:check` grön, rules `--check` grön, `check:quick` grön. - **End-to-end:** pushen av just denna branch gick igenom `.githooks/pre-push` **utan** `SKIP_CHECKS` — exakt det scenario som tidigare blockerades. - Frontend `check:ci`: alla steg gröna (vitest 3484 gröna; ett första parallellkört pass hade en vitest-flake under samtidig sbt-belastning, omkörning ensam grön — se PR-kommentaren om utfallet skulle avvika). - Backend: 0 Scala-filer rörda; `sbt --no-server test` gav 100 % cache-träff ("No tests to run"), och samma Scala-källor gick 2265 tester gröna tidigare idag (PR #333-körningen). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 7654238 fix(ci): B-319 — EOL-insensitive generated-file checks + eol=lf attributes Jonas · 2026-08-19 · +46 −3

#332 Correct B-313's diagnosis (sandbox sbt shim, not a missing .jvmopts) + local-verification handoff

2026-08-19 · Jonas, Claude (AI) · 2 commits · 2 filer · +185 −38 · Buggfixar B-313

Detaljer
B-313 was written before I had actually read the sandbox's `sbt`, and got two things wrong. Correcting it matters because the entry as written would send the next person to create a file that already exists. ### 1. The repo already has the right configuration `.jvmopts` (`-Xmx8G -Xms2G -Xss8M -XX:+UseG1GC …`) and `.sbtopts` (the `-J-` equivalents) are both checked in and correct. Real sbt reads them. The actual fault: `sbt` in the agent sandbox is not real sbt. It is a 378-byte shim at `/usr/local/bin/sbt`: ```bash exec java -Xms256m -Xmx1280m -Xss48m … -jar "$SBT_LAUNCH_JAR" "$@" ``` It `exec`s java directly and reads neither `.jvmopts`, nor `.sbtopts`, nor `SBT_OPTS`. The repo's 8 GB becomes 1 280 MB, and a cold compile of 748 sources doesn't fit. **This never affected the Windows box**, which runs real sbt — it is agent-sandbox-only, which the original entry did not say. ### 2. The shim does not lack `-Xss` It sets `-Xss48m` — *more* than the repo's 8M. The `StackOverflowError` in `dotty.tools.dotc.typer.Typer.typedInfixOp` I reported as a second symptom came from my own `java -Xmx8g` workaround, which had no `-Xss` at all. Self-inflicted, not evidence. ### The lever for a real fix, and the trap in it `_JAVA_OPTIONS` (not `JAVA_TOOL_OPTIONS`) is read *after* the command line and therefore beats the shim's hardcoded `-Xmx1280m`. Measured: ``` _JAVA_OPTIONS=-Xmx8G java -Xmx1280m -XX:+PrintFlagsFinal -version → MaxHeapSize = 8589934592 ``` **But it is an environment variable, inherited by every child JVM** — including the forked test JVMs `build.sbt` deliberately caps at `-Xmx512m` (its comment explains why: forked tests exhausted the pagefile on Windows). Setting it blindly would raise each of those to 8 GB, which is precisely the OOM scenario CLAUDE.md warns about. A fix has to reach only the launcher process — e.g. `check-ci.mjs` detecting that the resolved `sbt` ignores `.jvmopts` and invoking `java -jar sbt-launch.jar` directly with the repo's flags. Left undone rather than guessed at, late at night, in the gate that every other PR depends on. ### Handoff `other/HANDOFF_LOKAL_VERIFIERING.md` lists what genuinely needs a real MySQL, a real browser, or real sbt — chiefly **confirming the 0270 backfill**, since there is no database here and that `UPDATE` has never executed against a live schema. It also flags that B-316 (a repurchased animal stays hidden forever) can masquerade as a backfill failure, so it doesn't get misread during that check. Docs only — no code paths touched. `bug:check` green, 305 unique headings. --- _Generated by [Claude Code](https://claude.ai/code/session_01EjegFXjeqmWohfPzPCiU69)_
  • 76605e9 docs: stamp the local-verification handoff with its outcome (all runnable items done) Jonas · 2026-08-19 · +21 −0
  • 97517cb Correct B-313's diagnosis and add a local-verification handoff Claude (AI) · 2026-08-19 · +164 −38

#331 docs(ci): B-313 handoff + Depot.dev in the runner comparison (the speed case for hosted CI is gone)

2026-08-19 · Claude (AI) · 2 commits · 1 filer · +315 −49 · Buggfixar B-313

Detaljer
## What Two documentation changes, both following from this round's measurements. No code. 1. `other/HANDOFF_B313_SBT_COLD_COMPILE.md` — a handoff for a **local session on the Windows dev box**, since two of its three goals can't be verified in an ephemeral Linux sandbox. 2. `other/BLACKSMITH_CI_ANALYSIS.md` — **Depot.dev added** alongside Blacksmith, with the cost picture recomputed against the times *after* the overflow parallelism landed. That recomputation changed the conclusion rather than refining it. ## 1. The finding that reframes B-313 **`.jvmopts` and `.sbtopts` already exist in the repo root** (since `044241c6`) and already ask for `-Xmx8G` — but only **`-Xss8M`**, against the **`-Xss32m`** that B-313 reports as the working invocation. And B-313's second symptom was a `StackOverflowError` in `dotty.tools.dotc.typer.Typer.typedInfixOp`. | | Hypothesis | Explains | Fix | |---|---|---|---| | **A** | The wrapper reads neither file | both symptoms (heap OOM *and* stack) | put the flags where the JVM is actually launched | | **B** | It reads them, but `-Xss8M` is too small | only the stack symptom | raise `-Xss` to 32M | So the handoff's first step is to **read the compile JVM's actual flags** (`jcmd VM.flags`) rather than guess which opts source bites — B-313 notes `SBT_OPTS`, `.jvmopts`, `-mem` and `-J-Xmx` were all tried without establishing which applies. It also records a contradiction worth knowing before debugging: PR #323 measured a cold `sbt ciTest` going **green in 948s** on the same kind of sandbox where B-313 measured it **failing twice** — most likely memory pressure from sibling sessions. So: reproduce first, and treat a non-reproduction as a result. Plus the Windows specifics (`setup-java25.ps1`, sbt on PATH, and the **paging file** that `build.sbt:38-43` already documents hitting — grow it rather than shrinking the heap back into B-313), and a checklist where every run starts from a deleted `target/`, because a warm cache hides the entire bug. ## 2. Depot vs Blacksmith — and why neither is the answer right now **Both are blocked by the same thing.** Depot: *"your repository must be owned by a GitHub organization (not a personal account)."* Blacksmith says the same. `whiteancient` is a `User` account. **The speed argument evaporated.** When this analysis was first written, a full local run was ~21 min without backend, so 2× hardware was an obvious win. It is now **6.2 min with backend included** — a hosted runner can at best match it. **And hosted runners are always cold**, while a local machine keeps `target/` and coursier between runs. With 96% of the backend cost in compilation: | `ci.yml` hosted | Estimated wall | |---|---| | As written (26 sequential steps, no cache) | **~37 min** | | With coursier/bun cache | ~16 min | | With cache **and** `ci.yml` invoking `check-ci.mjs` | ~6–8 min | Uncached, hosted CI is **slower than running locally**. Parity needs two changes that are free on GitHub's own runners anyway. | | Free/included | $/min (2 vCPU) | Sizes | Note | |---|---|---|---|---| | GitHub Actions | 2,000 min/mo | $0.008 | 2–64 vCPU | no org needed | | **Blacksmith** | **3,000 min/mo, genuinely free** | $0.004 | 2–8 vCPU | 25 GB cache/repo/week | | **Depot** | none — **$20/mo** (2,000 min included), 7-day trial | $0.004 | **2–64 vCPU**, ARM same price | zero-config cache, ~1000 MiB/s | At ~100 runs/month: Blacksmith ≈ **$0**, Depot ≈ **$26**. Depot is the stronger product; Blacksmith is free at this volume. **Updated recommendation:** do the two free changes, fix B-313, and get B-233's machine check from a `push: branches: [master]` trigger on GitHub's own runners — no vendor, no org move, inside the free tier for post-merge runs. Blacksmith only if the repo moves to an org for other reasons; Depot only if CI outgrows 8 vCPU. ## check:ci ⏭ Not run in full — docs-only, two markdown files under `other/`, touching no gated surface. The pre-push quick gate passed (12 static guards, gre
  • dae6f22 docs(ci): add Depot.dev to the runner comparison; the speed case is gone Claude (AI) · 2026-08-19 · +111 −49
  • 288c174 docs(ci): handoff for B-313 (cold sbt compile) + the Windows backend gate Claude (AI) · 2026-08-19 · +204 −0

#328 B-314: correct the CAP conditionality dossier's SMR numbering (it was shipping wrong law references to farmers)

2026-08-19 · Claude (AI) · 2 commits · 26 filer · +638 −173 · Buggfixar B-313 B-314

Detaljer
`GET /api/farms/:farmId/cap/conditionality-dossier` is live and `requireFarmer`-gated. It generates the annual **CAP conditionality dossier** a farm hands to its national paying agency (Jordbruksverket in SE) to release direct support, and `compliance/cap/+page.svelte` renders the fields verbatim. Two of the law references in it were wrong. ## What was wrong Verified against **Reg (EU) 2021/2115 Annex III**, fetched directly from CELLAR (CELEX `32021R2115`, `Accept: application/xhtml+xml`, `Accept-Language: eng`). The fetch was run **twice independently** — once producing `other/CAP_VILLKORLIGHET_RESEARCH.md`, once again inside this fix rather than trusting the earlier summary. | The file said | Actually | |---|---| | Reg (EC) 178/2002 = **"SMR 7"** | **SMR 5** — 178/2002 is not SMR 7 under the current Annex III at all | | Reg (EC) 852/2004 = **"SMR 8"** | **Not a standalone SMR.** It appears only inside SMR 5's own footnote (Annex III footnote 6) as an implementing detail, alongside Reg 470/2009 Art 14 and Reg 853/2004 | SMR 9 (Dir 2008/119, calf welfare), SMR 10 (Dir 2008/120, pig welfare), SMR 11 (Dir 98/58, general welfare) and GAEC 1 (permanent grassland) were checked and **confirmed already correct**. 852/2004 is now surfaced as a labelled feed-hygiene supplement rather than as a fabricated SMR number. ## Where the wrong numbers came from is *not* established Stated plainly rather than guessed at: the old "SMR 8 / 852/2004" pairing does not match the pre-2023 cross-compliance numbering either. That regime (Reg 1306/2013 Annex II) had SMR 6/7/8 as the **cattle / sheep-goat / pig identification** requirements — which the 2023 reform **dropped entirely**. There is no animal-ID SMR anywhere in the current Annex III. That is recorded in the code comment so a future edit does not helpfully reintroduce one. ## Root cause is structural, not a typo The numbering lived as **hardcoded Scala string literals**, not as data — while the rule registry (`rule`/`rule_text`, migration 0256) already documents exactly this shape in its `checkpoints` JSON: `{"scheme":"villkor","code":"SMR 11"}`. So two parts of the codebase had each guessed independently about the same subject and guessed *differently*: this route, and the AI-corpus file `src/main/resources/eurlex/cap-conditionality.md` (wrong in its own separate way — that one is being handled on `fix/cap-chunks-quarantine`). The disagreement between them is what surfaced both. The real fix is the rule-versioning schema change in **`other/REGELVERK_TIDSMODELL.md` §5.2**. This PR does the minimum to stop shipping wrong numbers today, and collects every code into one documented `CapConditionalityCodes` table with its source so that eventual move is mechanical rather than another re-derivation. ## Also changed Help docs (en + sv), the wiki CAP-conditionality page, `regulatory-compliance.md`, the feature catalog, and the affected i18n shards — everything that repeated the old numbering. `CapSmrEvidenceSpec` gains a guard so the numbering cannot silently drift again. ## Visual verification The dossier was generated and rendered in Chromium, and the screenshot was **read**: sections now read *1. SMR 5 – Livsmedels- och fodersäkerhet*, *2. Foderhygien (Reg 852/2004) — inte ett standalone SMR*, *3. SMR 9 – Kalvvälfärd*, *4. SMR 10 – Grisvälfärd*, *5. SMR 11 – Allmän djurvälfärd*, *6. GAEC 1 – Andel permanent betesmark*. Layout and the six stat cards intact, no clipping. Screenshots committed under `other/fas06-01-genomgang/verktyg/shots/`. ## check:ci ⏳ `FULL_CI=1` runs on the rebased merge product immediately before merge, per the B-233 rule — result will be posted here before this leaves draft. --- _Generated by [Claude Code](https://claude.ai/code/session_01EjegFXjeqmWohfPzPCiU69)_
  • 843855a Log B-313: check:ci's sbt step cannot compile the codebase cold Claude (AI) · 2026-08-18 · +43 −0
  • 4e9ca4b fix(cap): correct the conditionality dossier's SMR numbering (B-314) Claude (AI) · 2026-08-18 · +595 −173

#326 B-039: backfill animal.sold_at from the event log (migration 0270)

2026-08-19 · Claude (AI) · 2 commits · 4 filer · +348 −17 · Buggfixar B-316 B-039

Detaljer
Closes the data half of **B-039** ("a sold animal still shows as active"), and reframes the entry: this was never a code bug or a missing live-DB repro. ## What was actually wrong The code has been correct for a long time — `ProjectionService` stamps `animal.sold_at` when a movement event with `movementType=sale` is projected, and the animal list plus the sales/slaughter pickers all filter on it. But **migration 0202 only added the column**, `NULL` by default, with **no backfill** — and the boot replay (`replayAllFarmsUnapplied`) only revisits events whose `applied_at` is NULL. Every animal sold *before* that migration therefore kept `sold_at = NULL` permanently, despite its sale event sitting in the event log, and kept rendering as active regardless of how correct the current code is. ## The backfill New `db-patches/db-migration-0270-animal-sold-at-backfill.sql`, derived from the event log (the source of truth). The event shape was verified by reading the writer *and* the projector rather than assumed: `event_type = 'movement'`, `subject_type = 'animal'`, `subject_id` = the animal id, and `$.movementType = "sale"` in the payload JSON (confirmed against a second independent serialization site). - **Timestamp:** `occurred_at`, matching what `ProjectionService` itself stamps (and what 0202's own comment says). - **Multiple sales:** `MAX(occurred_at)` — because `rebuildFarm` replays in `occurredAt` order so later overwrites earlier; the backfill reproduces the converged live-system state exactly. - **Idempotent:** `WHERE a.sold_at IS NULL` guards every write, so a re-run is a no-op — proven as a fixed point in the spec. - **Scope-locked:** a test asserts the SQL has exactly one `SET` clause and mentions neither `death_date` nor `cause_of_death`. - Expressible as a single `UPDATE…JOIN`, which respects the migration runner's naive `;`-at-end-of-line splitting — locked in by a test. ## Tests New `AnimalSoldAtBackfillSpec` (12 tests; the existing `MigrationsSpec`'s 10 still green, 22 total). SQL-shape assertions read the checked-in file directly; a pure-Scala model of the rule covers single sale, multiple sales picking the latest, an already-set `sold_at` never overwritten, non-sale movement types not stamping, a batch-subject sale not stamping an animal, no-sale animals untouched, and second-run-is-a-no-op. **No MySQL in the sandbox**, so the SQL could not be executed against a real database — stated plainly, and covered at the two levels above instead. ## Same pattern found elsewhere (flagged, not fixed) Other columns added `NULL` with no backfill, fed only by future projections: - `animal.cause_of_death` (0203) - `animal.birth_holding_id`, `animal.current_holding_id`, `animal.death_cause_category` (0230) — **`current_holding_id` is the consequential one**: it backs the "exactly one current holding" invariant that CDB regulatory reporting relies on. - Ruled out as *not* the same pattern: `animal_weight.event_log_id` (0198), documented as intentionally NULL forever for pre-migration rows. ## check:ci **Backend — the actual deliverable — unambiguously green:** `sbt ciTest` **3 994 passed, 0 failed**, plus svelte-check 0 errors, eslint 0 errors, schema guard, bug guard and every static gate. Two frontend steps failed and were investigated: this diff touches **zero** frontend files (Scala + SQL + Markdown only), so neither can stem from it. vitest's 5 failures were all 5 s timeouts in one pre-existing unrelated file — **17/17 in isolation**. `overflow:check` crashed with Playwright `Target crashed`, and `dmesg` shows the kernel OOM-killer taking out the chrome process mid-run; two attempts had already cleared all 15 core routes (60 viewport checks) plus 40+ sampled routes with zero violations before the kill. The sandbox was running 3–6 concurrent agents' sbt compiles at up to 6.6 GB heap each. ## What you confirm afterwards One yes/no, once 0270 has run on boot: the originally-reported animal no longer appears under Akti
  • 333b135 Log B-316: sold_at is never cleared, so a repurchased animal stays hidden Claude (AI) · 2026-08-18 · +51 −0
  • 07258ce Backfill animal.sold_at from event log (B-039) Claude (AI) · 2026-08-18 · +297 −17

#324 Fix B-311: batch creation dropped the client's id, breaking every follow-up call

2026-08-19 · Claude (AI) · 1 commits · 10 filer · +450 −33 · Buggfixar B-311 B-300

Detaljer
Fixes **B-311** — the same defect class as B-300 (which made animal groups unusable), found by that fix's sweep and left out of scope at the time. ## Root cause (confirmed) `batchStore.ts#createBatch` created the batch in Dexie with its own `crypto.randomUUID()`, then POSTed **without** that id. `CreateBatchDTO` had no `id` field, and the handler always minted its own. Client and server ended up holding different ids for the same batch, so every follow-up call addressed an id the server never heard of — and the outbox treats 4xx as permanent, so the operation was silently dropped. The sibling defect from B-300 was here too: `close`/`delete`/the finisher PATCH raised "not found" but their error handling flattened everything except "Forbidden" to **400**, so a missing batch never returned 404. ## Fix (mirrors the merged B-300 shape) - `CreateBatchDTO` gains an optional `id`, honoured when present and **idempotent** — a repeat POST with the same id returns the existing row rather than duplicating (the outbox retries, so this matters) — minting only as fallback, and refusing a cross-farm id collision. - The id parse is wrapped in `ZIO.attempt`, so a malformed client id is a typed 400 instead of a fiber defect surfacing as a 500 that the outbox would retry forever — the exact follow-up B-300's code review caught. - `close`/`delete`/finisher now share one error handler with a proper "not found" → 404 case. - `batchStore` sends `id: row.id`. ## Sweep Only three frontend stores enqueue `kind: 'entity'` ops, which is where this defect class can occur: `batchStore` (fixed here), `groupStore` (B-300, merged), and **`holdingStore.ts` — same defect, not fixed here**. It is documented as a follow-up in the B-311 entry with file:line and what a fix must touch. Every other `crypto.randomUUID()` store enqueues `kind: 'event'`, where the pattern doesn't apply. ## Tests, proven red first `BatchRoutesAuthSpec` extended. Against the pre-fix code: malformed id returned **200** instead of 400; an idempotent re-POST created **2 rows** instead of 1; close/delete/finisher returned **400** instead of 404 for a missing batch. After the fix: **19/19 green**. ## Verification Drove the real batches UI in headless Chromium against a mock server modelling the fixed contract, asserting at the network level that the POST body carries the id matching the Dexie row and that the row is stored under exactly that id (passed on two consecutive runs). Screenshots taken and read. Honest caveat stated by the agent: the harness mocks all `/api/*` calls, so it cannot itself prove the server's id-matching — that is what the spec proves; no MySQL is available here for a true full-stack run. ## check:ci ✅ **`FULL_CI=1` green** — sbt test, svelte-check, eslint, vitest 3 510/3 510, layout-overflow guard (including a click-through of the batches page at 390 px), all static guards, `bug:check` confirming B-311 unique among 300 headings. One skip: bundle-size (no production build). --- _Generated by [Claude Code](https://claude.ai/code/session_01EjegFXjeqmWohfPzPCiU69)_
  • 772b8d2 Fix B-311: honour client-generated batch id (mirror B-300) Claude (AI) · 2026-08-18 · +450 −33

#327 B-215: retire the onboarding modal, make /welcome the single onboarding surface

2026-08-19 · Claude (AI) · 4 commits · 17 filer · +1 140 −1 507 · Buggfixar B-317 B-215

Detaljer
Resolves **B-215** ("why are there two onboarding guides?"). The answer turned out to be three surfaces, and the bug entry compared the wrong pair: `GettingStartedPanel` is the post-farm checklist and never competed with anything — the real duplication was `/welcome` (server-driven, 5 steps, gets the keeper to a first animal) versus `OnboardingWizard` (client-driven modal, 6 steps), both creating the first farm and setting preferences. The modal's trigger was also the broken one: it fired on `$activeFarms.length === 0` read straight from the Dexie cache, so an empty or cold cache read as "new customer" — which is exactly what hit Jonas, twice over (B-214's impersonation wipe, and `GET /api/farms` returning 500 in the 2026-07-31→08-03 window). Retiring it means the bug entry's "make the trigger robust" follow-up is **moot**: a trigger that no longer exists cannot misfire. ## The blocker that had to be cleared first Retiring the modal naively would have silently dropped the **GDPR Art 26/28 data-processing agreement**, since the modal was the only place first-time consent was captured. The agent stopped and reported rather than proceeding — correctly. Verified and fixed before any retirement work: `maybeShowDpaReprompt()` gated the dialog on `status === 'stale'` **only**, so `'never_acknowledged'` fell through and did nothing. New pure helper `shouldRepromptFor()` covers both, and the dialog now branches its copy on `isFirstTime` — a keeper who never approved is never told the agreement "changed". Bonus catch: `/app/legal/dpa`'s re-acknowledge button had the identical `stale`-only gap. ## What moved into `/welcome` 5 → 7 steps: farm → rules → **holding (new)** → animal → **DPA (new)** → preferences → done. The holding step reuses the existing country-aware label helper verbatim (so a Swedish keeper still sees "PPN / SE-nummer") and pre-fills the holding name from the farm name. The DPA step ports the wizard's exact copy — controller/processor framing, data categories, retention, sub-processors — and appends the same `dpa_acknowledgement` event through the standard outbox, so the audit trail is unchanged. **The 5-second consent countdown was kept**, after checking why it existed: the wizard's own comment ties it to the data-deletion idiom, and `DpaReacknowledgementDialog` independently reuses the same pattern. Two authors converging on friction-before-consent is deliberate design, not incidental polish. Server-resume mapping widens `/api/onboarding/status`'s `currentStep` (which predates the new steps) so a keeper is never shown a step the server already proves is behind them. Backend untouched. ## What was deleted `OnboardingWizard.svelte` (732 lines) and its mount; `onboardingStore.ts` trimmed 205 → 32 lines, keeping only `holdingNumberLabelKey` and `DPA_VERSION` (both still consumed). Every deletion was grepped for other consumers first. Tests for deleted behaviour were **removed with the code, not weakened**. 48 dead i18n keys removed through the shard system (89 → 40), including two now-empty `DYNAMIC_PREFIXES` entries. ## Evidence Red→green proven by reverting `shouldRepromptFor` to `stale`-only and watching `never_acknowledged → true` fail. Full `/welcome` walkthrough driven in Playwright with **screenshots taken and read** at 1440 and 390 px: the 6-tick rail, the new holding step with the correct Swedish label, the new DPA step with the countdown and enable/disable behaviour, and the done state. **The modal-absence check passed at every checkpoint including the exact former trigger scenario** (wiped Dexie → zero farms → cold cache on first paint). ## check:ci **16/17 real gates green** — i18n, all ratchets, WA guards, a11y 220/0, token-drift, schema, bug-number, svelte-check 0 errors, eslint 0 errors, vitest 3 484/3 484. `overflow:check` crashed 3/3 attempts with a Chromium OOM signature (`dmesg` confirms the memcg killed chrome; host load peaked at 82 on 4 cores from concurrent agents) — always deep in its
  • 62ef512 Log B-317: app/+layout.svelte's auth subscription is effectively dead code Claude (AI) · 2026-08-18 · +89 −83
  • 908c9e4 B-215 code review: extract + unit-test the /welcome resume mapping, fix doc drift Claude (AI) · 2026-08-18 · +83 −25
  • feb9e05 B-215: KNOWN_BUGS.md resolution write-up + /welcome Playwright verification Claude (AI) · 2026-08-18 · +348 −2
  • 90415fc B-215: retire OnboardingWizard modal, fold DPA + holding steps into /welcome Claude (AI) · 2026-08-18 · +620 −1 397

#325 B-312: sweep raw animalType strings onto animalTypeLabel — 71 sites across 52 files

2026-08-19 · Claude (AI) · 2 commits · 69 filer · +483 −101 · Buggfixar B-312

Detaljer
Completes the sweep B-264 (merged in #305) reported but could not take on: the app rendered the backend's raw English `animalType` ("Cow", "Other") straight into a Swedish UI instead of calling `animalTypeLabel()`. ## Method All 108 `.svelte` files touching `.animalType`/`.species` in markup were separated into **text rendering** vs **logic** (`#if`, `===`, `.includes()`, `bind:value`, CSS classes, already-`$t()`'d dynamic keys), then every candidate was verified against its real data source — the frontend type *and* the backing Scala DTO — before being touched. **71 sites fixed across 52 files**, plus 2 more found in code review (below). Everything B-264 named (`OrganicSummaryCard`, `compliance/organic`, `compliance/record-book`, `movements/**`, `breeding/**`, `health/**`, `devices/**-import`) plus more the systematic pass found: `AnimalPassportCard`, farm/animal/batch detail pages, `finance/animals-pnl`, `insurance`, and — the one that matters most — the **public farm-shop product page**, which was showing raw English species names to shoppers, not just to logged-in keepers. Two bonus catches outside the grep pattern (renamed locals): `compliance/amu/monthly`'s `{sp}` loop variable and `health/mortality`'s auto-generated task title. ## The farm-shop fix is client-side, and that is deliberate `TraceabilityChainService.scala` still puts the raw enum name into the public JSON response (`species = batch.animalType`, lines 195/322) — **this PR contains no Scala changes at all**. What changed is the *consumer*: `farmers/[slug]/products/[productSlug]/+page.svelte` now calls `animalTypeLabel(b.species, $t)` before rendering. That is the right split. The backend does not know the viewer's language, so translating there would bake one locale into a public API; and leaving the field's wire format untouched means no API contract change and no consumer can break. ## A different bug found on two pages `welfare/holding-risk` and `welfare/historical-anomalies` rendered **hardcoded lowercase category literals**, not the `AnimalType` enum — so `animalTypeLabel()` was the wrong tool. Fixed with dedicated key families (`holdingRisk.species.*`, `historicalAnomaly.species.*`), wording taken from the existing sibling prefixes rather than invented, and both registered in `check-i18n.mjs`'s `DYNAMIC_PREFIXES`. Code review traced both families back to every `out.push({species: …})` / `add(…, species)` call site and confirmed the coverage is exact — 7 and 5 keys respectively, nothing missing. ## Deliberately skipped (documented in the B-312 entry) Logic and comparisons, already-translated dynamic keys, `bind:value` filter inputs, props feeding only child-component logic, CSV export columns, marketing/demo mock copy — and notably the **TRACES NT pre-fill payload**: that literal is transcribed by the farmer into an official EU certificate field, so translating it would misrepresent the document. Code review examined that exemption independently and agreed with the reasoning. ## Code review found 2 more of exactly the kind the entry predicted The B-312 entry flags its own limitation under "Kvarstående risk": the sweep is grep-driven on property access, so a **renamed local** could still hide. Review found two, both template-string interpolations the `{x.animalType}` pattern could not see — and both are now fixed in `537661db`: - `groups/+page.svelte:89` — `animalName()` building `` `${a.name} (${a.animalType})` `` for the member list. - `compliance/record-book/+page.svelte:253` — the same shape in `animalLabel()`, used at **5 sites in the AHL Art 102 animal-register table**. That one is a regulatory view, so it is the more consequential of the two. No false positives were found among the original 71: every site traced back to a genuine `AnimalType` enum value, and `animalTypeLabel()` returns the **raw value** for an unmapped input rather than an empty string or the key, so it degrades to exactly the pre-fix behaviour and never worse. ## V
  • a23b010 fix(i18n): B-312 review — translate 2 missed animalType template-literal leaks Claude (AI) · 2026-08-18 · +2 −2
  • ae02d95 fix(i18n): B-312 — sweep raw animalType/species strings to animalTypeLabel() Claude (AI) · 2026-08-18 · +481 −99

#323 perf(ci): parallelize the overflow gate + bootstrap the backend toolchain (check:ci 2.1x faster, backend gated)

2026-08-18 · Claude (AI) · 4 commits · 5 filer · +763 −403 · Funktioner

Detaljer
## What Re-measures the CI gate after #306, then **builds the two fixes the measurements pointed at**. Full `check:ci` goes **786.8s → 374.6s (2.10×)** on this 4-core sandbox, and the backend is gated in an agent session for the first time. | | Before | After | | |---|---|---|---| | **Full `check:ci`** | 786.8s (13.1 min) | **374.6s (6.2 min)** | **2.10×** | | `overflow:check` (isolated) | 586.5s | **228s** | 2.57× | | `overflow:check` (inside full run) | 586.5s | 237.5s | 2.47× | | Σ steps | 835.2s | 490.3s | | | Pool's leverage | 1.1× | 1.3× | ceiling raised | | Backend in an agent session | skipped | **3982 tests, green** | | ## 1. The overflow gate was 70% of the run, in one serial step `overflow:check` was **586.5s of an 835.2s sequential sum** — a single pole that check-ci.mjs's step pool cannot break up, capping the whole run at an **Amdahl ceiling of 1.42×** (measured: 1.1×). It walked all 129 route+viewport combinations on one page. Route scans are independent — navigate, settle, measure against a stateless `/api` mock — so they now shard across N browser contexts in the same browser. **Each worker needs its own context, not merely its own page**: the mock route handler, the init script, and `loginBypass()`'s localStorage writes are all per-context state. Shards are assigned **round-robin** because `CORE_ROUTES` sort first and carry the dialog scan, so contiguous slices would load shard 0 and idle the rest. The default is **deliberately oversubscribed at 1.5× cores** (clamped 2..8). Most of a route's time is *waiting*, not computing — the 400ms settle, the 1500ms re-measure, content polling, and vite compiling each route on first visit. Measured over the identical scan: | Workers | Wall | | |---|---|---| | 1 (old serial code) | 586s | — | | 3 | 345s | 1.70× | | 6 | 228s | **2.57×** | `OVERFLOW_WORKERS=1` restores the old serial behaviour for debugging a flaky route. **Equivalence verified:** 129 combinations (126 main + 3 glove), zero skips, PASS both before and after. Failures and skips are now **sorted before reporting** so parallel completion order can't reshuffle the output between runs, and each shard closes its context in a `finally` so a mid-shard throw can't leak one while siblings hold the browser open. ## 2. The backend was gated nowhere CLAUDE.md claimed *"the agent sandbox has Java 25 + sbt and gates it there."* It has **neither** — java 21, no sbt. With the Windows box skipping (documented) and Actions being `workflow_dispatch`-only, **748 main + 458 test Scala files passed no automatic gate anywhere.** `scripts/bootstrap-backend-toolchain.sh` installs Temurin 25 + sbt 2.0.4 under `~/.enlantis-toolchain`: - **sha256-verified** against Adoptium's and sbt's published sums; a mismatch aborts rather than running an unverified toolchain - **idempotent** — an existing usable Java 25 / sbt makes it a no-op - `--export` emits eval-able lines (logs to stderr, so `eval "$(… --export)"` is clean) With it wired in, this PR's validation run **gated the backend green: 3982 tests passed, 0 failed.** CLAUDE.md's false claim is corrected, and check-ci.mjs's skip message now says the backend is gated *nowhere* rather than "gated elsewhere", and points at the one command that fixes it. **One trap worth recording:** the JDK version check originally read `java -version | head -1`. That is wrong wherever `JAVA_TOOL_OPTIONS` is set — this sandbox sets it for the proxy truststore, so the JVM prints a `Picked up JAVA_TOOL_OPTIONS…` line *first* and the check silently read noise and failed. It now matches `version "25` across the whole output. ## Also in this PR `other/BLACKSMITH_CI_ANALYSIS.md` is rewritten with the measurements behind all of the above, plus the Blacksmith verdict: **blocked before cost enters the conversation** — Blacksmith supports GitHub *organizations* only, and the repo owner is a `User` account. The case for hosted CI was never speed; it is that the backend needs a guaranteed home (now solve
  • d0a269d fix(ci): import freemem — the new memory cap crashed overflow:check on every run Claude (AI) · 2026-08-18 · +4 −4
  • cf443dc fix(ci): honor OVERFLOW_WORKERS=1, cap default workers by free memory, make checksum mismatch actually fatal Claude (AI) · 2026-08-18 · +75 −16
  • 85f14a9 perf(ci): parallelize the overflow gate; bootstrap the backend toolchain Claude (AI) · 2026-08-18 · +429 −133
  • 5c5526f docs(ci): re-measure the CI gate after #306; Blacksmith is org-gated Claude (AI) · 2026-08-18 · +255 −250

#322 Label the overdue-animal margin honestly; log B-309 and B-310

2026-08-18 · Claude (AI) · 1 commits · 6 filer · +101 −16 · Buggfixar B-309 B-310

Detaljer
Follow-up to #321 (B-308). Jonas's decision after we checked whether a real delay cost could be computed: **rename the number to what it is, and log the two underlying problems as their own bugs.** ## The label was making a claim the number doesn't support The subset figure sums each overdue animal's `lifetimeMargin`, so a *profitable* overdue animal pushes it **up**. Labelled "Förlorad marginal — försenad slakt", two profitable overdue animals rendered as "margin lost: 500 kr" **in green** — when those animals had in fact earned 500 kr. Now "Marginal, försenade djur" / "Margin — overdue animals", with a tooltip saying plainly that this is what the animals have earned so far, **not** what the delay costs. Comments in both the i18n shard and `AnalyticsRoutes` record why, so the next person doesn't "improve" it back into a loss label. ## Why not just compute the real delay cost Checked before deciding — the data isn't there: | Needed | State | |---|---| | Daily cost rate after the optimal date | ✅ derivable (`AnimalCostRow` has `costDate` + `costType`) | | Optimal slaughter date | ⚠️ exists, but the model is crude — see B-310 | | Carcass price penalty per weight band | ❌ **nowhere in the schema.** `kill_sheet_reconciliation` holds `total_payout_net` + `avg_carcass_kg` — a batch-level settlement, not a tariff | A krona figure derived from today's model would be a guess with more decimal places than the 1 %-per-day heuristic it replaced. ## B-309 — `financial_snapshot` is never written `ProfitabilityService.updateSnapshot` is the only writer and is called **nowhere** in production code (`calculateSnapshot` at `AnalyticsRoutes.scala:138` computes on the fly and persists nothing). `listAnimalsPassedOptimal()` reads that table, so in production: - `animalsPastOptimal` is always **0** - this margin is always **0** - **the copilot's slaughter priorities** (`AICoPilotService.scala:106–115`, `:257`) are always empty The "you should slaughter these animals" feature has in all likelihood never worked in production, and nothing alerts, because an empty list looks exactly like "no overdue animals". ## B-310 — the optimal-weight model `calculateOptimalSlaughterDate` hardcodes optimal weights per `animalType` (pig 110 kg, cow 600, sheep 45, chicken 2.5 — "cow 600" lumps cull dairy cows with beef cattle), derives growth from **first vs last weighing across the animal's whole life** (understating current growth after a slow start), and back-extrapolates the date from *today* rather than recording when the animal actually crossed. The entry also flags that the `1% loss per day` heuristic in `AICoPilotService` must be replaced by the same calculation when real data lands — not left as a second truth. ## Bug numbers `bun run bug:next` proposed B-309–B-310 but flagged them UNVERIFIED (`gh` is missing in the sandbox, so it couldn't consult open PRs). Verified manually instead against `origin/master` **and all 136 remote branches** — max is B-308, so both numbers are genuinely free. `bug:check` green (299 headings, all unique). ## check:ci ✅ **`FULL_CI=1` green** — `sbt ciTest` 3 982 passed / 0 failed, svelte-check 0 errors, eslint 0 errors, vitest, overflow guard, a11y, tokens, i18n and all ratchets. One skip: bundle-size (no production build). --- _Generated by [Claude Code](https://claude.ai/code/session_01EjegFXjeqmWohfPzPCiU69)_
  • 60f577c fix(analytics): label the overdue-animal margin honestly; log B-309/B-310 Claude (AI) · 2026-08-18 · +101 −16

#321 Fix B-308: profitability summary mixed whole-herd and past-optimal populations

2026-08-18 · Claude (AI) · 2 commits · 19 filer · +647 −74 · Buggfixar B-308

Detaljer
Fixes **B-308**, found while investigating whether "Total marginal" should be renamed "Totalt täckningsbidrag" — the label turned out to be the smaller problem. ## Root cause `GET /api/farms/:farmId/profitability-summary` summed `totalCosts`/`totalRevenue`/`totalMargin` over **only animals past optimal slaughter weight**, while `totalAnimals`/`highRiskAnimals` counted the **whole herd** — a farm with 200 animals of which 3 were late displayed those 3 animals' margin as the farm total, labelled "Total marginal". `averageMarginPerAnimal` divided that subset sum by the whole-herd count. The route's own comment admitted it: *"simplified - should use actual snapshots"*. ## Jonas's decision (option D): show both, correctly - **Headline: the whole herd's contribution margin** ("Totalt täckningsbidrag"), with `totalCosts`/`totalRevenue` covering the herd too, so `totalMargin = totalRevenue − totalCosts` **by construction**. - **Separate actionable number** for the past-optimal subset, kept visible rather than folded away. - `averageMarginPerAnimal` now divides by the population it measures. ## Performance — no N+1 New `AnimalCostRepository.listByFarm` (mirroring the existing revenue equivalent) means **2 aggregate queries regardless of herd size**, instead of one `calculateSnapshot` per animal. ## Tests, proven red first New `AnalyticsRoutesAuthSpec` — the route had **zero** prior coverage. Fixture of 5 animals (2 past-optimal) with herd margin (−140) deliberately different from the subset (−170); against the old logic `totalCosts=0 (expected 660)`, `totalMargin=−170 (expected −140)`, `averageMarginPerAnimal=−34 (expected −28)`. Green after the fix. ## Code review (`/code-review --fix`, medium) — 11 findings, 6 fixed - Every new comment/spec/script cited **B-269** (an unrelated bundle-size bug) instead of B-308. - `pastOptimalMargin` was read unguarded, but `sw.js` serves `/api` GETs from a 1-hour cache — a post-deploy offline client would render a literal **"NaN"**. - The minus sign had been rewritten to U+2011 NON-BREAKING HYPHEN, which **screen readers do not announce as minus** (a loss read as a gain); the anchored regex also no-opped in currency-first locales. Now U+2212 MINUS SIGN, un-anchored. - A zero subset margin painted **danger red**, though zero is the healthy default (no overdue animals). - The `*RoutesAuthSpec` had **no auth assertions** on an endpoint this PR widened — added 403 non-member and 400 missing-header cases. - A double traversal of the revenue rows collapsed into one sum. **Deferred to Jonas (product calls, not defects):** the subset label reads "Margin lost — overdue animals" but sums `lifetimeMargin`, so two *profitable* overdue animals show "margin lost: 500 kr" in green; and this endpoint is `requireFarmer` while sibling money endpoints in `AnimalPnlRoutes` are `requireOwner`, so farm-wide финances are visible to Farmhand/Vet/ViewOnly. Also deferred: `MarginSummaryWidget` shows the same partly-projected totals without the new caveat tooltip, and the subset still reads from ≤7-day-old snapshots while the totals beside it are live. ## Reported, not fixed (pre-existing) `ProfitabilityService.updateSnapshot` is **never called in production code**, so `financial_snapshot` — the source behind the past-optimal subset — is essentially unpopulated in prod. The subset may therefore render empty regardless of this fix. Needs its own decision. ## Visual verification Playwright with herd margin +18 500 vs subset −3 200 (opposite signs, so a collapse regression is visible): 14/14 checks at 390/850/1200 px in sv + en, screenshots taken **and read**, no horizontal overflow, contrast 220/220. Two regressions were caught during that verification and fixed (tooltip icon breaking label wrapping; the minus sign line-wrapping alone). ## check:ci — merge product (rebased onto master incl. #319/#320) ✅ **`FULL_CI=1` green, backend included** — `sbt ciTest` **3 982 passed, 0 failed**, svelte-check 0 errors, esli
  • 3a2370e fix(analytics): apply code-review findings on B-308 Claude (AI) · 2026-08-18 · +75 −28
  • 2410a98 Fix B-308: profitability-summary card mixed two populations under "Total marginal" Claude (AI) · 2026-08-18 · +572 −46

#320 docs: rule-versioning time model + CAP three-layer sourcing decision

2026-08-18 · Claude (AI) · 1 commits · 2 filer · +489 −0 · Funktioner

Detaljer
Records the decisions taken with Jonas 2026-08-18 that unblock the EUR-Lex corpus rebuild, plus the architecture for carrying rule changes over time. Docs only — no code. ## Why Jonas's framing made the temporal dimension a **product** requirement, not defensive engineering: *"Regelradarn måste kunna hantera att regler ändras och uppdatera bonden på vad som gäller och vad som gällde så han aldrig står oförberedd."* A system that only knows today cannot defend an inspection covering 2027, and cannot warn in advance. ## The verified finding this rests on The rule registry **looks** time-aware — `rule` has `version`, `effective_from`, `retired_at`, and its `checkpoints` JSON already documents `{"scheme":"villkor","code":"SMR 11"}`, i.e. CAP conditionality was anticipated in the design. But: 1. `UNIQUE KEY uq_rule_key (rule_key)` allows only **one row per logical rule**, so two versions with different validity windows cannot coexist — `version` can only increment in place, overwriting history. Creating a new row under a different `rule_key` would break the file's own "stable slug, never recycled" contract (used in URLs, check binding and findings). 2. **No read path filters on validity.** `effective_from`/`retired_at` are read only by `RulePublishGuard` at publish time; validation and answers ignore dates entirely. Net: structurally single-version. That is the same silent regime-change failure that left the CAP chunks describing the pre-2023 regime — at schema level. ## What the document specifies - **Decisions §5.1–§5.6** for the corpus rebuild (bilingual, hybrid verbatim/paraphrase, Regelradar-linked versioning + visible "as of" stamp, chunk-id renames). - **Three-layer CAP sourcing**: EU law (has CELEX, fits the existing heading gate) / Swedish binding law (SFS, `reg_document_relation` is already keyed on CELEX+SFS ids) / Jordbruksverket guidance (advisory, never cited as law). Chosen over both "drop it" and "build full Jordbruksverket verification" because the copilot should help, not decline. - **Time model**: identity becomes `(rule_key, version)` with supersede-not-mutate, all reads as-of a date, **fail closed at period boundaries** (a visible gap beats a confident wrong answer), and validation runs recording which rule versions they evaluated against. - **Advance warning as the product feature**: watch documents from publication rather than entry into force, match `applicability` against farms, notify before the change takes effect. - **2028 preparedness**, framed honestly: the 2023 transition already happened and passed unnoticed, so design for recurrence. Explicitly lists what this does *not* solve — political slippage, overlapping regimes (2021–22 was a transition period, so clean cutover is the exception), national lag, and the unverifiable guidance layer. ## Also included `other/CAP_VILLKORLIGHET_RESEARCH.md` — the sourced research memo behind the CAP decision, including that five of six current CAP chunks describe the pre-2023 regime while claiming 2023–2027, and that `CapDossierRoutes.scala` ships an **independently wrong** SMR numbering to farmers today (flagged in the doc as fix-first, ahead of the rebuild). ## check:ci Not run — this PR changes only two new files under `other/`, which no gate reads (`wiki:lint` covers `docs/wiki/`, not `other/`). --- _Generated by [Claude Code](https://claude.ai/code/session_01EjegFXjeqmWohfPzPCiU69)_
  • caf51fb docs: rule-versioning time model + CAP three-layer sourcing decision Claude (AI) · 2026-08-18 · +489 −0

#319 Fix round 9, group 1: style the last 18 unstyled pages — B-268 baseline 18 → 0

2026-08-18 · Claude (AI) · 5 commits · 47 filer · +2 390 −369 · Buggfixar B-268

Detaljer
Closes out **B-268**, fix-round 9's largest item: the pages left rendering `class` attributes with zero `<style>` block after the Tailwind removal never got its per-page redesign follow-up. 28 files were handled by earlier rounds (#279/#280/#282/#284); this branch does the remaining **18**, taking `unstyled-components-baseline.json` to **0**. Premise respected per the handoff: this was never a regression — it was unfinished work, so `other/TAILWIND_INTENT_INVENTORY.md` was used as the design facit rather than hunting for leftover Tailwind. ## Pages styled, customer-facing first - **Public (7):** farmer cart, gift-card thanks, order confirmation, links, embed instructions, newsletter confirm/unsubscribe — shared `Notice` component for callouts, `min-width:0` + `overflow-wrap:anywhere` on user-text cells, and an inline hardcoded hex on a `wa-icon` replaced with a token-backed class. - **Shop (2):** QR codes (equal-width card grid per DESIGN.md), wholesale (StatusPill + stacked-card mobile table). - **App (6):** stripe (23 hits — the largest single file), billing invoices, advisor, and the three kill-sheet host pages. - **Admin last (3):** AML, moderation (reusing the existing global badge classes), sim layout. ## A real bug caught by looking at the screenshots On `admin/aml` the case rows are `wa-button`s hosting multi-line card content, but WA's `::part(base)` is a fixed-height, `white-space:nowrap` flex box — the content overflowed and **overlapped the next row**. Found by reading the rendered screenshot (CLAUDE.md's hard rule doing its job), fixed with a `::part(base)` override, re-verified at 1440 and 390 px. ## Visual verification All 18 pages + `/sim` rendered at 1440 px and 390 px with adversarial fixtures (long farm/product names, long buyer emails); screenshots taken **and read** (`verktyg/shots/b268-round9/`); zero horizontal overflow measured on all 38 page/viewport combinations. Harness committed as `verktyg/b268-round9-verify.mjs`. ## Incidental unblock `frontend/scripts/i18n/keys/dashboard.mjs` carried a literal duplicate `dashboard.widget.moreCount` key that crashes `i18n:build` — removed (pre-existing, blocked regenerating dictionaries for a new key here). ## check:ci ✅ **`FULL_CI=1` green** — style ratchet (new 0 baseline), a11y 220/0, token-drift 917 files / 0 bare hex, svelte-check 0 errors, eslint 0 errors, vitest 3 510/3 510, **overflow guard PASS** against the now-adversarial fixtures, all i18n/WA/autocomplete guards. Skips: bundle (no prod build), backend (`SKIP_BACKEND=1`, zero Scala in this diff). `other/KNOWN_BUGS.md` B-268 updated honestly as DELVIS ÅTGÄRDAD — the gate only prevents regrowth; trust a fresh detector run over a stale count. --- _Generated by [Claude Code](https://claude.ai/code/session_01EjegFXjeqmWohfPzPCiU69)_
  • 3410876 chore(style-gate): lower unstyled-component baseline 18 -> 0, close out B-268 Claude (AI) · 2026-08-18 · +299 −23
  • b80a1ad style(admin): give AML and moderation dashboards real CSS (B-268) Claude (AI) · 2026-08-18 · +517 −81
  • 828b411 style(app): give billing, advisor, and kill-sheet host pages real CSS (B-268) Claude (AI) · 2026-08-18 · +801 −139
  • 2cb3216 style(shop): give the QR-codes and wholesale admin pages real CSS (B-268) Claude (AI) · 2026-08-18 · +269 −53
  • 3cb7400 style(shop): give the public customer-facing pages real CSS (B-268) Claude (AI) · 2026-08-18 · +504 −73

#318 Fix round 9, group 5: recent-events overflow + adversarial overflow-gate fixtures — B-278

2026-08-18 · Claude (AI) · 2 commits · 5 filer · +428 −59 · Buggfixar B-278

Detaljer
**B-278** — the widget overflow fixed with the shared `truncate` utility, and the gate's fixtures made adversarial (the main task per the handoff): a self-updating pathological event type read from the backend registry + 34–63-char unbroken names. **Proven red → green** on the exact reported row. ## Code review (`/code-review --fix`, medium) — 7 findings, 6 fixed - **The dashboard recent-animals card had been mis-diagnosed as "no CSS at all" and allowlisted** — it was the PR's own one-liner disease (`min-width:0` missing), and the allowlist entry blinded the gate to the whole `/app` component. Fixed properly (shared `truncate`), allowlist entry **deleted**, verified 0 violations with no suppression + visual check (screenshots read, computed styles measured). - The fixture would silently degrade to kind data the day all eventType keys exist (which #305 — now merged — did!) — synthetic never-translatable fallback added. - Registry scan scoped to `object EventType`; wrong "B-268 family" diagnoses corrected in comments and bug log (it's the min-width flex family); scope-hash rotation documented as a maintenance hazard. - Deferred: the areas/animals-page 98–264 px spills (legitimately beyond scope, remain allowlisted with corrected diagnoses) and a follow-up B-number for them (allocator needs gh; recommended in the bug log). Post-fix: overflow gate 129/129 PASS with no new suppression, vitest 3 463/3 463, svelte-check 0 errors. ## check:ci — merge product (rebased onto master incl. #306–#317; the anticipated `check-overflow.mjs` conflict vs #306 did not materialize — polling and fixtures touch disjoint regions) ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)"** (bundle: no prod build; backend: **zero Scala** — identical backend tree proven green at #316's merge, 3 979/0).
  • 64662fc fix(overflow): apply code-review findings on the gate Claude (AI) · 2026-08-18 · +105 −48
  • d593578 Fix B-278: min-width:0 overflow on Senaste handelser + make overflow gate adversarial Claude (AI) · 2026-08-17 · +323 −11

#317 Fix round 9, group 10: house selector scoped to /animals only — B-272

2026-08-18 · Claude (AI) · 2 commits · 17 filer · +459 −195 · Buggfixar B-272

Detaljer
**B-272** (decision A): the house selector lives only on `/animals` — TopBar and mobile-sheet pickers removed, a proper filter-group added among the page's other filters. `selectedHouseStore` untouched per the handoff; **B-122's house-card flow verified intact** end-to-end. ## Code review (`/code-review --fix`, medium) — 6 findings, 4 fixed - Switching house scope left a **stale invisible area filter** active (list emptied with a misleading display) — area filter now resets on scope change. - The filter rendered on zero-house farms (the removed pickers were gated) — gating restored. - B-272's entry carried two contradictory status lines — leftover ÖPPEN line removed. - Stale comment naming the deleted TopBar picker rewritten (its removal would have looked safe and silently broken B-122's flow). - Deferred: two orphaned i18n keys (deliberate wide-radius i18n pass); a possibly pre-existing overflow flag on the milk herd-table seen only on the un-rebased branch — **did not reproduce on the merge product's green overflow run below**. ## check:ci — merge product (rebased onto master incl. #306–#316; shell-component conflicts resolved keeping both #314's mode switcher and this PR's picker removal) ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)"** incl. the overflow gate (bundle: no prod build; backend: **zero Scala** — identical backend tree proven green at #316's merge, 3 979/0).
  • c10c838 fix(animals): apply code-review findings Claude (AI) · 2026-08-18 · +41 −24
  • 7aa97b9 fix(nav): narrow house/stall scope selector to /animals only (B-272) Claude (AI) · 2026-08-17 · +418 −171

#316 Fix round 9, group 8: locations/groups widgets, area list with welfare validation, clickable priorities — B-294, B-295, B-303

2026-08-18 · Claude (AI) · 2 commits · 33 filer · +1 680 −139 · Buggfixar B-294 B-295 B-303

Detaljer
**B-294**: Platser/Grupper widgets via registry → dispatch → aggregation (`aggregations/animals` extended per the one-endpoint-per-source rule; groups as its own endpoint). **B-295**: area table with Namn·Golvyta·Max djur·Kategori + weight-banded space validation via `WelfareWatchdog.legalMinSqm` (blocks too-small with the decided message format, warns on capacity, `None` never blocks) — route specs seen red pre-fix. **B-303**: priority rows are real `<a>`s (root-caused: the widget's DTO carries `targetAnimalId`, not the sibling model's deeplink); `/priorities` Copilot mini-cards had the same all-point-at-/copilot bug, fixed with a fallback chain. ## Code review (`/code-review --fix`, medium) — 3 findings, 2 fixed - Stale shared error state rendered unrelated earlier failures in a freshly opened area dialog — cleared on open. - The byHouse extension added 1+H+Σ(areas) strictly **sequential** DB round trips to the hot animals aggregation (~135 extra queries on a 15-house farm) — now `foreachPar` with parallelism 4, matching the in-repo precedent. - Skipped: hardcoded hex in the new table CSS — shares the file's pre-existing debt; belongs to the B-268 restyle with mandatory visual verification. ## check:ci — merge product (rebased onto master incl. #306–#315) ✅ **Backend `sbt ciTest`: 3 979 passed, 0 failed, 10 ignored** (machine-verifies the `foreachPar` edit the review sandbox couldn't compile). ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)."** ✅ **check:ci green (frontend + backend) on the actual merge product.**
  • 757f099 fix(dashboard,areas): apply code-review findings Claude (AI) · 2026-08-18 · +12 −3
  • 8f43b73 feat(dashboard,housing,priorities): B-294 Platser/Grupper widgets, B-295 area list + space validation, B-303 clickable priority rows Claude (AI) · 2026-08-17 · +1 668 −136

#315 Fix round 9, group 11: ProofRail metric mapping + currency glyph as text — B-260, B-265

2026-08-18 · Claude (AI) · 3 commits · 20 filer · +627 −107 · i18n

Detaljer
**B-260** (decision: fix the mapping): flat `MetricsDTO` → `Metric[]` in one place, fail-closed type guard (empty beats invented), a true 0 renders as 0, eyebrow row removed while the shared i18n key is kept. **B-265** (decision): currency symbol as TEXT via `formatToParts()` in a shared helper next to `formatCurrency()` (WA has no `kr` glyph); both surfaces fixed; tests seen red first. ## Code review (`/code-review --fix`, medium) — 5 findings, 4 fixed - **Glyph/amount divergence**: the glyph used the raw farm currency while amounts go through the supported-currency-else-EUR gate — a CZK farm would show "Kč" next to a €-formatted amount, the exact invariant the PR claims can never break. Both surfaces now derive the glyph from the same gate. - Type guard now checks `asOf` as its predicate declares; dead `.slice(0,5)` dropped; dangling status fragment in the bug log removed. - **Reported for Jonas (skipped — content decision):** DESIGN.md's Proof QA gate requires a per-metric source citation ("no unsourced numbers"); before this PR nothing rendered so nothing was unsourced — now five live numbers render with no citation, and the DTO's `asOf` is fetched but never shown. What each metric should cite needs a decision against DESIGN.md. Post-fix: vitest 50/50, eslint 0 errors, svelte-check 0 errors. ## check:ci — merge product (rebased onto master incl. #306–#314) ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)"** (bundle: no prod build; backend: **zero Scala** — identical backend tree proven green at #314's merge, 3 964/0).
  • 02b6bc7 chore(i18n): regenerate dictionaries after rebase Claude (AI) · 2026-08-18 · +32 −32
  • ef535d7 fix(marketing): apply code-review findings Claude (AI) · 2026-08-18 · +27 −10
  • 410bd5e Fix ProofRail's flat-DTO/Metric[] mismatch and hardcode dollar-sign icons Claude (AI) · 2026-08-17 · +568 −65

#314 Fix round 9, group 9: sim/real farm separation — mode switcher, restart reuse, proper sim deletion — B-266, B-275

2026-08-18 · Claude (AI) · 3 commits · 23 filer · +1 755 −249 · i18n

Detaljer
Sim/real farm separation per Jonas's decisions: shared mode switcher on all three surfaces (default real, session-remembered, reset on login per B-214's lesson), restart reuses the farm via `POST /api/sim/:farmId/restart`, manual mass-deletion with proper `sim_*` table coverage (NOT a `hardDeleteDemo` copy). Tests written first and seen red (`SimFarmWipeSpec`, `SimFarmLifecycleAuthSpec`); 19/19 visual checks green. ## Code review (`/code-review --fix`, medium) — 9 findings, 7 fixed - **The branch failed its own merge gate**: 5 drifted token fallbacks (tokens:check red) — corrected, pixel-neutral. - **The wipe leaked the multi-site scenario's `site` rows and `org` row** — now covered, org deleted once unreferenced, regression test added. - Real-mode empty farm list showed sim-specific copy; fixed. - `wipeFarmRows` now shared between demo and sim paths (the two DELETE loops had already started to drift); false "order is irrelevant" comment corrected on `DemoFarmTables` (it was load-bearing). - Dead i18n key removed; simulator wiki page extended per the data-path rule. - Deferred with reasons: restart atomicity (needs transactional wipe+seed design) and the TopBar zero-real-farms branch (visual restructure needing browser verification). Post-fix: SimFarmWipeSpec 8/8, SimFarmLifecycleAuthSpec 7/7, FarmDemoWipeSpec 4/4, overflow:check green (clears the earlier pending item). ## check:ci — merge product (rebased onto master incl. #306–#312) ✅ **Backend `sbt ciTest`: 3 964 passed, 0 failed, 10 ignored.** ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)."** ✅ **check:ci green (frontend + backend) on the actual merge product.**
  • f7901c2 chore(i18n): regenerate dictionaries after rebase Claude (AI) · 2026-08-18 · +30 −30
  • 4952ddd fix(sim): apply code-review findings Claude (AI) · 2026-08-18 · +161 −120
  • d4b53fa Add sim/real farm-mode switcher, restart-reuse, and manual sim-farm deletion Claude (AI) · 2026-08-17 · +1 564 −99

#311 Fix round 9, group 2: farm view reload, camera toolbar, tree placement, unplaced-animal link — B-289, B-288, B-293, B-274

2026-08-18 · Claude (AI) · 3 commits · 37 filer · +1 599 −188 · Buggfixar B-289 B-288 B-293 B-274

Detaljer
**B-289** farm-switch reload (+ same disease fixed on the sim page) · **B-288** shared four-control CameraToolbar with `zoomBy(delta)` on both surfaces · **B-293** tree placement: ring floor + unconditional `insideBuildings()`, tests proven red (13/300 & 86/300 pre-fix → 0/300, 12/12 green) · **B-274** URL-shareable `?location=none` filter. ## Code review (`/code-review --fix`, medium) — 6 findings, 5 fixed - **Two stale-response races**: rapid A→B→A sim navigation could permanently show the wrong farm under the URL (loadedFarmId claimed too late), and gardsvyn's `loadAll` had no sequence guard so a slow old-farm load could repaint farm A under `/gardsvyn/B` — the exact symptom B-289 set out to fix. Both fixed with eager claims + monotonic guards. - Zoom buttons enabled while the scene was null (contradicting their documented contract) — now disabled until orbit state exists. - The `?location=none` filter failed **open** before the area map loaded (whole herd flashed as "unplaced") — now fails closed. - Redundant double `setWorld` per mount/switch removed (whole static world was built twice). - Reported, not fixed: CameraToolbar hardcodes UI hex against CLAUDE.md's token rule — deliberately copied from the sim's game-art palette; retokenizing changes appearance and needs its own visual verification pass. Post-fix: svelte-check 0 errors, vitest 3 466/3 466 incl. the B-293 suites. ## check:ci — merge product (rebased onto master incl. #306–#310; version-aware KNOWN_BUGS conflict resolution, dictionaries regenerated, bug:check clean) ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)"** (bundle: no prod build; backend: **zero Scala** — identical backend tree proven green at #310's merge, 3 949/0).
  • 75dbadb chore(i18n): regenerate dictionaries after rebase Claude (AI) · 2026-08-18 · +30 −30
  • c0d0c12 fix(3d): apply code-review findings Claude (AI) · 2026-08-18 · +40 −4
  • 8d1e70f Fix round 9 cluster 1: farm view reload, camera toolbar, tree placement, unplaced-animal link (B-289, B-288, B-293, B-274) Claude (AI) · 2026-08-17 · +1 529 −154

#310 Fix round 9, group 6: root-admin auth, promote gating, treating-person name — B-276, B-290, B-292

2026-08-18 · Claude (AI) · 4 commits · 15 filer · +749 −132 · Buggfixar B-290 B-276 B-292

Detaljer
**B-276**: both routes compared UUID header against email list (never matches) — now delegate to the shared root-admin check; auth specs seen red pre-fix. **B-290** (decision): promote button disabled with visible explanation + server-side rejection of dead/sold animals; failure path surfaces the server's reason. **B-292**: treating person resolves to display name; UUID preserved untouched for traceability. ## Code review (`/code-review --fix`, medium) — 6 findings, 5 fixed - **Critical catch:** the free-text treating-person override was submitted into `EventPayload.Treatment.administeredBy: Option[UUID]` — a typed name failed the whole payload decode and the projection was **silently skipped**: no Art 108 journal row, no withdrawal alert, zero user-visible error. Fixed end-to-end via the pre-existing `administeredByName` (CDB J6) field. - `AnimalDTO` never serialized `soldAt`, so the client promote-gate was blind to sold animals on server-hydrated views and WS sync clobbered local state — field added and populated in all three serialization sites. - The duplicated root-admin wrapper hoisted to `AdminTokenAuth.requireRootAdminOrToken` (single copy — the exact drift family B-276 blames). - Machine-routing prefixes (`validation:` etc.) stripped before display. - Wiki log entry added per the auth-change rule. - Reported, not fixed (outside diff): `hiveStore.recordVarroaTreatment` has the same UUID-field free-text defect — noted in B-292's entry. Post-fix: 30/30 targeted backend tests, svelte-check 0 errors, vitest stores 483/483. ## check:ci — merge product (rebased onto master incl. #306/#308/#304/#312/#305/#309) ✅ **Backend `sbt ciTest`: 3 949 passed, 0 failed, 10 ignored.** ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)"** (after dropping a stale duplicate B-290 heading the rebase union produced — caught by the bug-number gate, exactly its job). ✅ **check:ci green (frontend + backend) on the actual merge product.**
  • d416bf5 docs(bugs): drop stale open duplicate of B-290 from rebase union Claude (AI) · 2026-08-17 · +0 −2
  • 70b13a0 chore(i18n): regenerate dictionaries after rebase Claude (AI) · 2026-08-17 · +30 −30
  • 2e806ca fix(auth,treatments): apply code-review findings Claude (AI) · 2026-08-17 · +93 −43
  • 250dcfe fix(auth,shop,health): root-admin identity check, disabled sale button for departed animals, resolved treatment-journal names (B-276, B-290, B-292) Claude (AI) · 2026-08-17 · +626 −57

#309 Fix round 9, group 4: WA neutral-token contrast root cause + gate, timeline, mobile cards — B-304, B-291, B-298, B-271

2026-08-18 · Claude (AI) · 2 commits · 305 filer · +2 140 −1 374 · Buggfixar B-304 B-291 B-298 B-271

Detaljer
**B-304/B-291** root cause: WA ships two coexisting neutral scales — the certified zero-padded bridge (`neutral-050`) vs the kit-native unpadded decade scale (`neutral-50` = dark end). ~290 files swept onto certified semantic tokens; measured 1.1–1.54:1 → 6.2:1. Gate extended (`check-contrast.mjs`): parser comment-stripping bug fixed (34 dropped declarations), WA bridge pairs pinned in CONTRACTS, new decade-50 misuse detector — proven red → green, 220 pairs / 0 failing. **B-298**: one custom property governs marker + line, drawn from `.timeline-item` (geometry measured). **B-271** (decision A): shared `{#snippet}` rows, card list under 560 px (verified 390/560/900 px). ## Code review (`/code-review --fix`, medium) — 6 findings, 4 fixed in `075c8e14` - Misuse-regex required a `;` and could cross `}` — both false-negative shapes (inline style, semicolon-less) and a cross-rule false positive confirmed by execution; regex bounded, 3 regression tests added. - Fix-it hint suggested the **nonexistent** `--wa-color-brand-050` (following it would ship a transparent background) — corrected + test. - The "primary-50 is not a real token" comment/wiki claim was false (the primary ramp aliases brand) — docs corrected to the true situation. - `fmtDateMobile` re-implemented ad-hoc date formatting — now delegates to the shared `formatDate`. - Deferred with reasons: dual-DOM table+cards (explicitly documented design decision), residual gate scope gaps (needs a scoping decision, no current offender). Body-claim correction: the "4 literal-hex near-misses" were in fact 1 (`#777`→`#555`). Post-fix: contrast vitest 45/45, a11y 220/0, svelte-check 0, wiki:lint OK. ## check:ci — merge product (rebased onto master incl. #306/#308/#304/#312/#305; wiki-log union-merged) ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)"** (bundle: no prod build; backend: **zero Scala** — identical backend tree proven green at #312's merge, 3 942/0). Visual verification: screenshots taken and reviewed (`verktyg/shots/b304-contrast/`), computed ratios measured live.
  • 67f0d36 fix(a11y): apply code-review findings on the contrast gate Claude (AI) · 2026-08-17 · +70 −17
  • e1e2345 Fix WA neutral-token contrast trap, timeline marker overlap, mobile health-check table (B-304/B-291/B-298/B-271) Claude (AI) · 2026-08-17 · +2 070 −1 357

#305 Fix round 9, group 3: i18n leaks — B-263, B-297, B-264

2026-08-18 · Claude (AI) · 3 commits · 23 filer · +942 −186 · i18n

Detaljer
Fix-round 9, PR group 3 ("i18n som läcker ut"): **B-263** ⭐, **B-297**, **B-264**. (Full detail in earlier revisions; summary below.) ## The fixes - **B-263**: `DYNAMIC_PREFIXES['eventType.']` had drifted 35 → 137 backend types. The enumeration is now **generated** from `DomainEvent.scala` (cannot drift again); 101 missing keys added (en+sv, incl. `generic_health_check`); vitest drift-guard, proven red first. - **B-297**: three hardcoded `en-US` date surfaces fixed via the shared `formatDate()`/`$locale`, + 7 more found in the demanded sweep. - **B-264**: raw species strings → `animalTypeLabel()`; 60+ further raw `animalType` sites reported as a follow-up candidate, not silently half-fixed. ## Code review (`/code-review --fix`, medium) Core verified: parser matches 137/137 vals, removed hand-list is a strict subset, all keys exist in en+sv, gates green. 2 findings fixed in the hardening commit: the parser regex would silently drop a future `val X: String = "…"` ascription or trailing comment (the exact silent-drift class this generator eliminates) and the block-end regex broke on declaration modifiers — both broadened, still exactly 137 types. Deferred with reasons: 5 surfaces kept ad-hoc `toLocale*($locale)` rather than the shared helper (output-format change beyond the diff's intent — follow-up with its own visual check); the in-app render of the touched surfaces never ran (pre-existing dev-bypass harness blocker; isolated real-Chromium render with the actual built dictionaries stands in — merge decision made knowing this). ## check:ci — merge product (rebased onto master incl. #306/#308/#304/#312; KNOWN_BUGS union-merged, dictionaries regenerated) ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)"** (bundle: no prod build; backend: **zero Scala in this diff** — the identical backend tree was proven green at #312's merge, 3 942 passed 0 failed).
  • 8c5591e chore(i18n): regenerate dictionaries after rebase union Claude (AI) · 2026-08-17 · +28 −28
  • e52404d fix(i18n): harden the EventType parser against future syntax drift Claude (AI) · 2026-08-17 · +12 −2
  • 76de274 Fix i18n leaks: eventType raw keys, en-US dates, raw species strings Claude (AI) · 2026-08-16 · +902 −156

#312 Fix round 9, group 0 (broken features): GDPR list crash, group id ownership, Pro-gated save, negative durations — B-302, B-300, B-299, B-296

2026-08-18 · Claude (AI) · 8 commits · 13 filer · +1 057 −78 · Buggfixar B-302 B-300 B-299 B-296

Detaljer
Fix-round 9's top-priority group: four surfaces that could not be used. (Full detail per bug in earlier revisions of this description; summary below.) ## The four fixes - **B-302** GDPR deletion page render-crash on the wrapped `ListDTO` → unwrap helper + visible load errors; the demanded 12-wrapper `ListDTO` sweep found this was the only live instance. - **B-300** client-generated group id now honored idempotently server-side; membership "not found" → proper 404. Same-pattern find in `batchStore#createBatch` reported for follow-up. - **B-299** 402s recorded app-wide in `apiFetch` → `billingGateStore`; dashboard Pro-gated up-front; save failure shows the server's reason. - **B-296** duration clamped at 0 + 9 more unguarded elapsed-time sites fixed in the demanded sweep. ## Code review (`/code-review --fix`, medium) — 7 findings, 5 fixed in `dfc7c11c` - Malformed client UUIDs in POST /groups became fiber defects (500, outbox retried a permanently-bad op) → typed 400s. - **The billing notice rendered BEHIND the customise dialog** — from inside the modal nothing visibly happened, reproducing the exact B-299 symptom; now rendered inside the dialog too. - Billing state now resets on farm switch (a Pro farm's tier could suppress the nudge on a free farm). - VoiceEntry's manual 402 recording removed (redundant with the new apiFetch wiring). - Local-first-sync wiki updated with the client-id contract (CLAUDE.md's data-path rule). - Deferred with reasons: billing-status block is a 4th copy-paste (cleanup note); get-then-create TOCTOU on concurrent duplicate creates (needs repo-level upsert; outbox serializes per client). ## check:ci — merge product (rebased onto master incl. #306/#308/#304) ✅ **Backend `sbt ciTest`: 3 942 passed, 0 failed, 10 ignored** (incl. `AnimalGroupRoutesAuthSpec` 16/16 with the review's new parse-failure paths). ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)."** ✅ **check:ci green (frontend + backend) on the actual merge product.**
  • 230ed1d fix(groups,dashboard): apply code-review findings Claude (AI) · 2026-08-17 · +52 −9
  • cbd630e fix(test): add missing occurredAt field to grazingStore test fixture Claude (AI) · 2026-08-17 · +1 −0
  • 588f2f9 chore(i18n): rebuild en/sv dictionaries for the three new keys Claude (AI) · 2026-08-17 · +34 −28
  • a04c45c docs(bugs): mark B-302/B-300/B-299/B-296 as fixed, reorder new i18n keys Claude (AI) · 2026-08-17 · +84 −7
  • f2b4ada fix(dates): clamp Math.floor day-diffs to 0 instead of showing negative durations (B-296) Claude (AI) · 2026-08-17 · +171 −15
  • d2e4902 fix(dashboard): gate "Spara layout" up front, surface 402 visibly, wire apiFetch to billingGateStore (B-299) Claude (AI) · 2026-08-17 · +460 −2
  • a96cddb fix(groups): let the client's id survive group creation, map missing-group to 404 (B-300) Claude (AI) · 2026-08-17 · +114 −3
  • c5140ab fix(gdpr): unwrap GdprRequestListDTO instead of assigning it as an array (B-302) Claude (AI) · 2026-08-17 · +141 −14

#304 Fix B-307: locale chip renders stale flag after hydration (Svelte 5 src-skip)

2026-08-17 · Claude (AI) · 2 commits · 1 filer · +69 −0 · Buggfixar B-307

Detaljer
## What Fixes **B-307**: the closed locale-picker chip in the header could render the UK flag next to the text `SV` while the open list simultaneously showed the correct Swedish flag. ## Root cause (reproduced live, not guessed) With `sv` persisted in `localStorage` while `/home` is SSR-baked as `en`, the chip rendered `data-flag="se"` (value correct) but `src="/flags/4x3/gb.svg"` (paint stale). Cause: a documented Svelte 5 hydration optimization — `set_attribute()` skips patching `src`/`srcset`/`href` while hydrating. Every attribute updates except `src`; components mounted post-hydration (the open list, mobile chip) never hit the gate, and later toggles are genuine updates — exactly why the bug "healed" when toggling. ## Fix Central in `Flag.svelte`: bind the `<img>` + corrective `$effect` re-syncing `src` when it drifts. No-op when SSR and client agree. All flag consumers covered automatically. ## Verification Before/after Playwright screenshots (read, pixel-matching the report); polling-based check PASS. ## Code review (`/code-review --fix`, medium) Core fix verified sound (incl. the subtle Svelte attribute-cache follow-on case). One real finding, fixed in `f2706ee8`: the B-307 number existed only in the unmerged reporting branch — invisible to `bun run bug:next` and open to reallocation. Entry now reserved in `KNOWN_BUGS.md` under Åtgärdade; the reporting branch's copy should be dropped at its rebase. ## check:ci — merge product (rebased onto master incl. #306 + #308) ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)"** (bundle: no prod build; backend: zero Scala in this diff and the identical backend tree was proven green minutes earlier on the same master — 3 938 passed, 0 failed).
  • f2706ee docs(bugs): reserve B-307 in KNOWN_BUGS.md as fixed Claude (AI) · 2026-08-17 · +32 −0
  • d32d70e Fix B-307: locale-picker chip shows wrong flag on first paint after SSR/hydration mismatch Claude (AI) · 2026-08-16 · +37 −0

#308 EUR-Lex corpus citation gate (B-306, handoff step 1): headings index + heading-match spec

2026-08-17 · Claude (AI) · 3 commits · 14 filer · +1 289 −77 · Buggfixar B-306

Detaljer
## What Implements **step 1 (the gate) of `other/EURLEX_KORPUS_HANDOFF.md`** — the citation audit (B-306) found 15 of 47 corpus chunks citing the wrong article. This PR makes that class of error mechanically impossible to reintroduce. **Step 2 (the corpus rebuild) is deliberately NOT here** — blocked on the §5 decisions reserved for Jonas. ## The gate - **`_headings.json`** — official article headings for all six regulations, generated from CELLAR (English, matching the corpus's current prose — not preempting §5.1). Checked in, CI offline. Covers every article, not just cited ones. - **`_Official:` line on every chunk**; **`EurLexCorpusHeadingGateSpec`** requires existence in the index AND exact heading match — existence alone would have stayed green through the whole incident. - **`update-headings.mjs`** — opt-in refetch with human-reviewable diff, NOT in CI, honest-risk note in header. - **`must_cite` sharpened** to article level for 18 established questions. ## Corrections (6, audit-established) & exemptions (28, documented per-chunk) See the table/details in the spec and `EURLEX_KORPUS_VERIFIERING.md` addendum. New finding: Dir 98/58/EC articles carry no official headings at all (pre-ELI) — nothing to compare against. ## Verification - ✅ `EurLexCorpusSpec` + `EurLexCorpusHeadingGateSpec`: 12/12 (after fixing 10 Scala string-precedence compile errors in `25ebf635`). - ✅ **Proven RED**: index entry for 32019R0006/108 corrupted to "Withdrawal periods" → gate fails on exactly the right chunk with a precise message, exit 1 → green after restore. ## Code review (`/code-review --fix`, medium) 6 findings, 4 fixed in `0791d5bb`: `EurLexQa.stub` carried the exact Art 105/108 error this gate eliminates (outside the gate's reach — corrected); tra-004's `must_cite` used heading text that can never match a citation (now uses the annex locator); `parseLocator`'s doc comment described the opposite of its precedence; the audit addendum's exemption arithmetic was wrong (6→8, 9→11). Deferred with reasons: stale chunk titles (§5.4 is Jonas's decision), boundary-less substring matching in `run-stress.mjs` (outside diff, changes harness semantics for all 97 questions — reported). ## check:ci — merge product (rebased onto master incl. #306) ✅ **Backend `sbt ciTest`: 3 938 passed, 0 failed, 10 ignored.** ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)"** (bundle: no prod build; sbt run separately above due to the sandbox wrapper's heap ceiling). ✅ **check:ci green (frontend + backend) on the actual merge product.**
  • 0791d5b fix(eurlex): apply code-review findings on the citation gate Claude (AI) · 2026-08-17 · +18 −12
  • 25ebf63 fix(eurlex): parenthesize multi-line strings in the exemptions map Claude (AI) · 2026-08-17 · +32 −32
  • 3ddab02 feat(eurlex): B-306 citation gate — verify chunk headings against CELLAR Claude (AI) · 2026-08-16 · +1 239 −33

#306 Local CI speedup: parallel runner, --quick pre-push, change detection, eslint cache

2026-08-17 · Claude (AI) · 6 commits · 3 filer · +954 −193 · Funktioner

Detaljer
## What Implements `other/HANDOFF_CI_SPEEDUP.md` (decided with Jonas 2026-08-16, checked in as part of this PR): zero GitHub Actions minutes still — options **1 + 3**: a ~2–3× faster full `check:ci`, and a light pre-push hook. No checks removed. ### The 8 changes 1. **ESLint `--cache`**: cold **98.2 s → warm 4.2 s** (6.4 s on the final merge-product run). 2. **`--quick` mode** (`--quick`/`CHECK_QUICK=1`, root script `check:quick`): the 12 pure-Node guards only; **~12–20 s wall**, banner-marked `QUICK MODE — full check:ci krävs före PR/merge`. Wired into `.githooks/pre-push` — pushes go from minutes to seconds. 3. **Async promise-pool runner**: weighted slots, longest-first, sbt outside the pool, `svelte-kit sync` serialized pre-pool, buffered atomic output, wall-clock + Σ in the summary. **2.0× under hostile contention.** All incident comments, the sbt verify predicate, skip semantics, Windows and proxied-sandbox handling carried over. 4. **Change detection vs merge-base** (`FULL_CI=1` overrides): exactly two steps gated (sbt, overflow) per the handoff; uncommitted files count; any ambiguity → run everything; skips never silent. 5. **Light pre-push hook** with `SKIP_CHECKS=1` escape hatch kept. 6. **Overflow `loginBypass` sleep→poll** (old values as ceiling; functional settle times untouched). 7. **Deliberately unchanged** per handoff §7 + the 6 drifted gates re-added to `ci.yml` (dispatch-only). 8. **Docs**: CLAUDE.md two-tier model, flags, B-233 sharpening; handoff checked in. Bonus fix: process-group kill orphaned grandchildren — replaced with a descendant-PID-tree kill, verified zero orphans. ### Code review (`/code-review --fix`, medium) 6 findings, 5 fixed in `747aa5cd`: PERF_BUNDLE_BUILD build chain serialized after pool+sbt (removes a real race against sbt's resource copy of ~980 static files); `setEncoding('utf8')` on captured streams (multibyte chunk-boundary corruption); quoted svelte-kit bin path on Windows; wall-clock now covers the install step; skip-icon double-prefix removed. Skipped by design: memory-aware pool capacity (the CPU-only weighting was the handoff's explicit decision; reported as a known limitation). ### Verification matrix (handoff §Verifiering) All 7 items verified — quick mode, FULL_CI full run, deliberate breakage red per gate group, change-detection scenarios incl. uncommitted-file and no-origin fallback, sbt verify predicate 4/4, hook stdin invocation, Ctrl-C orphan check. ## check:ci — merge product (rebased onto master @ `19e957de`-era HEAD) ✅ **Backend `sbt ciTest`: 3 935 tests passed, 0 failed, 10 ignored** (central run, working heap). ✅ **Frontend `FULL_CI=1`: "All checks passed (2 skipped)"** — svelte-check, eslint, vitest, i18n, all ratchets, WA guards, a11y, tokens, **overflow 929 s green**, schema, bug-number. Skips: bundle (no prod build), sbt (run separately above due to the sandbox sbt-wrapper's hardcoded 1.28 GB heap — an environment limitation, not a runner defect). ✅ **check:ci green (frontend + backend) on the actual merge product.** ## Repo hygiene incident (self-reported) During the no-origin fallback test the agent mutated the shared `.git/config` from inside a worktree, repointing `origin` at a local path — silently breaking pushes for the whole session until the orchestrator caught and restored it. Remote-affecting tests now run in disposable clones only; documented here so the pattern isn't repeated.
  • 747aa5c fix(ci): apply code-review findings on the parallel runner Claude (AI) · 2026-08-17 · +41 −28
  • 2921382 docs(ci): document the two-tier check:ci model, add the CI speedup handoff Claude (AI) · 2026-08-16 · +181 −10
  • 50e517c perf(overflow): poll for dev-bypass auth readiness instead of blind sleeps Claude (AI) · 2026-08-16 · +24 −2
  • 8aee7bb perf(ci): make the pre-push hook run quick mode, not the full suite Claude (AI) · 2026-08-16 · +26 −7
  • 7dd4b27 perf(ci): parallelize check-ci.mjs, add --quick mode and change detection Claude (AI) · 2026-08-16 · +652 −143
  • 14a7643 perf(ci): enable eslint --cache and fix its config-serialization crash Claude (AI) · 2026-08-16 · +30 −3

#313 docs(ci): Blacksmith runner analysis — can auto-CI come back?

2026-08-17 · Claude (AI) · 2 commits · 1 filer · +296 −34 · Funktioner

Detaljer
## What Adds `other/BLACKSMITH_CI_ANALYSIS.md` — an analysis of whether hosted CI is affordable again (via Blacksmith's drop-in Actions runners), and what to do about the `ci.yml` gate drift. **Analysis only — no workflow files are changed.** ## ⚠️ Reconciled with the 2026-08-16 decision (PR #306) The first draft of this doc was written without knowledge of `other/HANDOFF_CI_SPEEDUP.md` / **PR #306**, whose header records the opposite decision made with Jonas a day earlier: *zero GitHub Actions minutes, workflows stay `workflow_dispatch`-only, no trigger changes, self-hosted runners rejected.* Recommending push/PR triggers without flagging that was misleading. The doc now leads with the conflict and splits its recommendation into **what needs no decision** vs **what needs the decision re-opened**. Nothing here should be actioned over #306. ## Findings that stand independently of the runner question - **The `ci.yml` gate drift is structural, and it recurred while I was measuring it.** `ci.yml` duplicates `check:ci`'s step list rather than invoking it: `scripts/check-ci.mjs` runs **14** guard scripts, `ci.yml` runs **7**. #306 re-adds six of the missing ones — but `flags:check` landed via #307 *after* #306's base commit, so **it still needs adding after #306 rebases**. Durable fix: have `ci.yml` run `FULL_CI=1 node scripts/check-ci.mjs` as a single step. - **#306 strengthens the coverage argument rather than settling it.** Its change detection lets a plain `check:ci` legitimately skip `sbt ciTest` (frontend-only diff) or `overflow:check` (backend-only diff), so the PR-body merge signal now covers *less* unless the author remembered `FULL_CI=1`. That's the B-233 class of gap — convention where a machine check belongs. Two live data points: #306's own body reports it never achieved a clean in-session `sbt ciTest` (OOM-killed 3× under sandbox contention), and this session's gate skipped the backend entirely because `sbt` was absent. - **Dependency caching is missing from `ci.yml` and is the biggest single lever** (cold sbt), worth adding even for dispatch-only clean-room runs. - **Perf workflows must stay on `ubuntu-latest`** — their calibrated wall-clock budgets would be invalidated by different hardware. ## Budget math, re-anchored on measurement Replaced the original guess with measured times from this session's run: frontend gates sum to **~1,280 s ≈ 21 min sequentially**, dominated by `overflow:check` at **936 s**; plus a cold `sbt ciTest` that's another 15–25 min. So `ci.yml` as written is a **~35–50 min sequential clean-room run** — which is precisely why auto-CI was unaffordable on GitHub's 2,000 free min/month (≈50 runs). Blacksmith's 3,000 free min/month at ~2× hardware lands ≈200 runs. Stated error bar: ±30% (contended sandbox). A **zero-third-party fallback** is also costed out: caching + pooled runner + a master-only push trigger ≈400 min/month, inside GitHub's own free tier. ## check:ci ✅ **Green — full `check:ci` run to completion twice, exit 0 both times:** once on the pre-reconciliation tree (complete summary: *"All checks passed (2 skipped)"* — 17 frontend gates incl. svelte-check, eslint, vitest, i18n, both ratchets, autocomplete, flags, WA guards, a11y, tokens, layout-overflow, schema and bug-number) and once on the tree being merged here (`235e3f5`, exit 0). The only delta between the two runs is markdown prose in one file under `other/`. Two steps skipped for environment reasons, both pre-existing and unrelated to this diff: **`sbt ciTest`** (no `sbt` in this container — the very gap this doc documents) and the **bundle-size budget** (no production build present). ESLint reported warnings only (legacy `any`/`console`/non-null/`{#each}`-key debt, which the gate treats as non-blocking per CLAUDE.md). Honest note on process: the final push used the documented `SKIP_CHECKS=1` bypass rather than running the pre-push hook's gate a third time — the same tree had already passed twice, and two earlier hook r
  • 235e3f5 docs(ci): reconcile Blacksmith analysis with the 2026-08-16 decision + PR #306 Claude (AI) · 2026-08-17 · +150 −34
  • fd732f6 docs(ci): Blacksmith runner analysis — can auto-CI come back? Claude (AI) · 2026-08-17 · +146 −0

#307 WebAweseom 3.11 and flags

2026-08-16 · Jonas · 1 commits · 1 filer · +34 −21 · Funktioner

Detaljer
  • 3fa2ac5 WebAweseom 3.11 and flags Jonas · 2026-08-16 · +34 −21

2026-08-16 — 4 direkt-commits

Detaljer
  • af1505e Add handoff for fix round 9 (#301) Jonas · 2026-08-16 · +290 −0
  • a4f756c Record decisions on B-260, B-265 and B-290 (plus B-260's confirmed root cause) (#300) Jonas · 2026-08-16 · +97 −3
  • 8dd5ae2 Bug log B-295…B-304 + fix-round 8 handoff (#297) Jonas · 2026-08-16 · +1 052 −20
  • 8b3f2a7 Bug log B-288…B-294 + fix-round 7 handoff (#295) Jonas · 2026-08-16 · +623 −0

#302 docs(eurlex): corpus citation audit + code-review fixes (B-306)

2026-08-16 · Jonas · 3 commits · 5 filer · +414 −8 · Buggfixar B-306 B-287

Detaljer
Följer på PR #294 (B-287). Tre commits: granskningsfixar, revisionsrapport, handoff. ## 1. `/code-review --fix` på PR #294 Två parallella granskare (standards + spec). Sex fynd, alla åtgärdade: - **`toLowerCase()` utan locale** — tokeniseringen blev miljöberoende (Windows-dev vs Linux-container). Nu `Locale.ROOT`. Retrieval-korrekthet är hela poängen med B-287. - **Kategoritabellen saknade ERROR-kolumn** — en kategori där varje fråga gick i tom retrieval renderades som "0 PASS | 0 FAIL". Alltså exakt det tysta felläge B-287 finns till för att stoppa, återinfört av fixen själv. - **`judgeAnthropic`s vägransväg** returnerade 0-betyg utan `failed`-flagga → låg kvar i snitten. Flaggan bor nu i `EMPTY_VERDICT`. - Genererad sammanfattning påstod fortfarande att `ANTHROPIC_API_KEY` krävs; README saknade domarens `failed`-beteende; en testkommentar påstod fel shred-fragment. Granskarna verifierade också oberoende att ERROR-regexen matchar backendens exakta sträng, att exitkoden täcker FAIL **och** ERROR, och att tokenise-testerna är genuint röda mot gamla regexen. ## 2. Revisionen (B-306) — huvudsaken Förarbetet till korpusöversättningen ledde till en jämförelse mot **officiell svensk lagtext** (hämtad via CELLAR — EUR-Lex webb-UI bot-gatar med HTTP 202). Korpusen hade aldrig mött primärkällan. **15 av 47 chunkar hänvisar till fel artikel. 15 rätt. 17 okontrollerade.** Värst i de två mest citerade förordningarna — 5 av 7 fel i vardera. I 2016/429 har `art84` och `art87` i praktiken bytt plats. De två farligaste är samma sort: en handlingsstyrande siffra under fel hänvisning. | chunk | påstår | officiellt | |---|---|---| | `2019-6-art108` | Art 108 = karenstider, bär 28/7/7/500-dygnsvärdena | Art 108 = **journalföring** | | `1-2005-annex1-ch5` | Bilaga I kap V = utrymmeskrav i m²/djur | Kap V = **vattning, utfodring, viloperioder** (utrymme = kap VII) | Texten i utdragen är i huvudsak sakligt riktig — det är **hänvisningen** som är fel, vilket är hela poängen med ett compliance-citat. De 17 okontrollerade är *ogranskade*, inte "förmodligen rätt": de sitter på bilage- och punktnivå, och två av de 15 bekräftade felen har exakt den formen. CAP-villkorligheten är helt overifierad. Där rätt ersättningsartikel inte är fastställd — det allmänna karenstidskravet — säger rapporten det i stället för att gissa. Att fylla i fel nummer i en revision av felaktiga nummer hade undergrävt fyndet. **Ingen gate kunde ha fångat det:** `EurLexCorpusSpec` kollar att chunkar finns och att en CELEX-sträng innehåller rätt *förordning*, aldrig rätt artikel. Stresstestets `must_cite` är också på förordningsnivå. Ingen korpusändring här — enbart revision. ## 3. Handoff `other/EURLEX_KORPUS_HANDOFF.md` — självbärande brief för fortsatt arbete i en annan tråd: hur man hämtar officiell text från CELLAR (inkl. de två förordningar som bara finns konsoliderade), rekommenderad ordning (gate före ombyggnad), de sex beslut som kräver Jonas, och miljöfällorna. Gatens designpoäng: att bara kolla att den citerade artikeln **existerar** hade varit grönt genom hela den här historien — artikel 108 finns, den handlar bara om något annat. Gaten måste jämföra rubriker. ## Gate-status — läs detta Rebasad på `a4f756ca` omedelbart före merge (B-233). ✅ Gröna: svelte-check, eslint, vitest, i18n, hardcoded-English, unstyled-component, wa×3, a11y, tokens, schema, bug-number. ⏭️ **Överhoppade på Jonas begäran:** `overflow:check` (Playwright, 920 s) och **`sbt test`**. `sbt test` är den enda gate som täcker Scala-ändringen (`Locale.ROOT`). `EurLexCorpusSpec` kördes grön (9 tester) på exakt den ändringen — men före rebasen, så samspelet med masters nyare kod är otestat. `overflow:check` är ofarlig här: diffen rör inga frontend-filer. Buggnumret var först B-288; en annan session tog det numret för en Gårdsvyn-bugg medan arbetet pågick. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • e78374c docs(eurlex): handoff for the corpus rebuild + citation gate Jonas · 2026-08-16 · +223 −0
  • 984f283 docs(eurlex): audit the corpus against primary sources — 15 of 47 citations wrong (B-306) Jonas · 2026-08-16 · +170 −0
  • 75fe059 fix(copilot-stress): apply code-review findings from the B-287 change Jonas · 2026-08-16 · +21 −8

#296 design: kompakta pillerknappar — E som bas, F som size="small"

2026-08-16 · Claude (AI) · 5 commits · 4 filer · +1 151 −186 · Design/UI

Detaljer
Implementerar de kompakta pillerformade knapparna i hela appen, efter två mockuprundor (underlaget ligger kvar i `other/button-mockups/` och i PR-historiken nedan). Beslut av Jonas 2026-08-16: **"E som grund, F i täta rader"**. ## Vad som ändras **E — bas för alla knappar** (`frontend/static/wa/wa-overrides.css` + `frontend/src/global.css`): - Alla `<wa-button>`: 32 px höjd (ned från 43), 15 px sidopadding (ned från 16), pillradie (`--radius-pill`), serifetikett 14,5 px/500 (`--font-display`) istället för sans 16 px. Satt scopat på `wa-button`-elementet, **inte** `:root` — `<wa-input>`/`<wa-select>`/`<wa-textarea>` behåller sina mått; pillformade *fält* är ett separat, ej taget beslut. - `.btn` (marknadssidorna) följer med till exakt samma mått, så handskrivna knappar och WA-knappar inte driftar isär. Höjden är padding-driven eftersom SC 2.5.8-golvblocket senare i `global.css` omdeklarerar `min-height` på samma specificitet. - `SiteHeader.svelte`: komponentens egna part-override hade hårdkodad `border-radius: 2px` som annars vunnit över systemet — bytt till `var(--radius-pill)`. Kodbasen är svept: detta var enda komponenten med kantig part-radie (TopBar:s ikonslot kör redan 999 px — det är profilknappen vars form riktningen lånar). **F — tät kapselnivå** (`size="small"`): - 28 px höjd, 12 px padding, sans 12,5 px/600. Sekundären (`appearance="outlined"`, ej primärvarianten) blir en tonad kantfri lozenge i `--color-surface-offset` — profilknappens grepp. - Adoption per yta är medvetet uppskjuten (Jonas: "Låt det vara så länge"); i dag används `size="small"` bara i `FarmAnalyticsSummary`. **DESIGN.md**: radietabellen (`--radius-sm` är nu enbart tag; `--radius-pill` omfattar knappar) och CTA-hierarkins Primary-rad uppdaterade — annars hade nästa designgranskning underkänt pillformen som drift. **Testinfra**: `frontend/scripts/check-overflow.mjs` fick en formad fixtur för `/api/org/:orgId/validation/rollup` (M3-12d, mergad till master efter branchpunkten) — utan den kraschade org-dashboarden i harnessen (B-234-klassen) och rödmarkerade gaten för alla brancher. Felet kom från master men fixen togs här så gaten kan intyga att knappändringen inte skapar nya layoutträffar. ## Tillgänglighet - SC 2.5.8 (AA) golv 24 px: E = 1,3×, F = 1,17×. Mått i `rem` så SC 1.4.4-zoom skalar. - Täthetslägena sätter golv **ovanpå** grundvärdet och är verifierade opåverkade: ladugård 48 px, stora fingrar 56 px, utomhus 56 px — med pillradien bevarad. - F:s kantfria sekundär: SC 1.4.11 kräver ingen komponentkant — identifiering bärs av etikett + fyllnad (10,8:1), fokusringen (8,28:1) bär tangentbordet. - Inga nya färger; fylld knapp 8,28:1 som förut. ## Visuell verifiering (obligatorisk enligt CLAUDE.md) Chromium mot `bun run dev` + mockApi-harnessen (`other/fas06-01-genomgang/verktyg/`), skärmbilder tagna och granskade: - Startsidan: hero-CTA:er och header ("Start free" gick från kantig till pill efter SiteHeader-fixen). - `/app`-dashboarden inkl. onboarding-**formulärdialogen**. - Uppmätta beräknade stilar: 32,0 px höjd / 999 px radie / 14,5 px Iowan Old Style / 15 px padding; F: 28 px, 12,5 px General Sans, tonad `#e3d6b3` utan kant; `.btn` 32,0 px. - Alla tre täthetslägena mätta (48/56/56 px). ## check:ci ✅ **check:ci grönt (frontend; backend skippad — sbt saknas i sandlådan, gatas i agentsandlådan).** Full körning på det träd som mergades (branchen rebasead på `origin/master` 3d5d0c3 + alla fyra commits): Alla steg gröna: svelte-check, eslint, vitest, i18n, hardcoded-English-, unstyled-component-, autocomplete- och flaggratchetarna, WA-gaterna, a11y-kontrast, token-drift, **layout-overflow (129 rutt/viewport-kombinationer renderade med de nya knappmåtten — 0 träffar)**, schema och bug-nummer. Skippade: bundle-size (ingen produktionsbuild) och `sbt test`. Körningen blev klar strax efter mergen — resultatet gäller merge-produkten eftersom branchen var rebasead på senaste master och ingen annan PR mergats emellan. ## Historik 1. **Runda
  • f0d8d4c test: lär overflow-mocken org-rollup-endpointen (M3-12d) Claude (AI) · 2026-08-16 · +22 −0
  • 05b52f3 design: kompakta pillerknappar — riktning E som bas, F som size="small" Claude (AI) · 2026-08-16 · +87 −11
  • 5bf9600 design: knappmockups — rätta nulägets radie till 2 px Claude (AI) · 2026-08-16 · +4 −6
  • da93277 design: knappmockups runda 2 — pillerformade, 28-32 px Claude (AI) · 2026-08-16 · +218 −169
  • 4acfd7b design: knappmockups — fyra kompaktare knappriktningar Claude (AI) · 2026-08-16 · +820 −0

#299 Move Områdesadmin to Inställningar → Gård & konto, and let Managers actually use it (B-305)

2026-08-16 · Jonas · 2 commits · 12 filer · +497 −44 · Buggfixar B-305

Detaljer
## What Two related changes to the housing-admin surface (`/app/farms/:id/admin/areas` — the only place a keeper can create a barn or a pen). **1. Nav move.** Jonas: *"det borde vara Inställningar -> Gård -> Områden"*. The `farm.admin.areas` node moves out of the Gårdsadmin group (which lives in the Enheter bucket, next to integrations and GDPR) into `global.settings.g.farmAccount`. It mirrors `farm.dashboards` in the appearance group — a `section: 'farm'` / `f()`-href node folded into a global settings group. Deliberately unchanged: **id** (saved ⭐ Favoriter and bottom-bar slots referencing this node keep resolving), **labelKey** (no i18n key moves), **href**, and **roles**. Keeping `section: 'farm'` is what preserves the role gate — `visible()` only enforces `roles` when `section !== 'global'`, so making it a global node would have shown housing admin to every Farmhand/Vet/ViewOnly. The route does not move and the "Områden" card on `/app/farms/:id/admin` stays as a second door, so every existing link and CTA keeps working. **2. B-305 — Managers could see the page but not use it.** Found while mapping the page for the nav move. `FarmAdminGuard` defaults to `allowedRoles = ['owner','Manager']` and the nav node said the same, but all six mutation routes in `FarmHouseRoutes` were `requireOwner` while the three read routes were `requireFarmer`. A Manager could reach the page, list every house and area, see Lägg till stall / Redigera / Ta bort — and 403 on every one of them. The expensive half was the error copy: all four catch blocks set `errors.server.500` regardless of status, so a permission rejection rendered as *"Något gick fel hos oss. Kontakta support@enlantis.com"* and routed the keeper to support for a non-bug. Resolved in favour of the frontend (Jonas: Managers get to edit): - The six mutation routes are now `requireRole(..., "Manager")`; owner and root admin still pass implicitly. Reads stay `requireFarmer`. The route env gains `EmployeeRoleRepository`, already provided in `Main.scala` for `FarmerInvitationRoutes`, so the union env is unchanged. - `apiService.request` stamps the HTTP status on the thrown error (additive — existing `catch` blocks reading only `.message` are unaffected), and 401/403 renders the new `sweep14c.access.noPermissionAction` string, kept separate from the guard's page-level copy whose "komma åt det här området" reads as the wrong sense of *område* on this page. - New `FarmHouseRoutesAuthSpec` — these routes had **no auth coverage at all**. Its Manager-allowed case is the one that would have caught the mismatch. ## Worth knowing - `requireRole` has no "farmer with no role rows defaults to writer" fallback (unlike `requireWriter`, which keeps that for pre-session-36 data). A de-facto manager with no `employee_role` row is still rejected — deliberate, but it will look like the same bug if someone reports it. - `employee_role.role` is free text; `"Manager"` is not a case of the `EmployeeRole` enum. Consistent with `FarmerInvitationRoutes`, which uses the same literal, but a typo there would fail closed and silently. - Same "everything is a 500" pattern likely exists on other admin pages built from this template. The status is now on the error object so a sweep is cheap — noted in B-305, not done here. - Filed as B-302, renumbered to **B-305** on rebase: master had meanwhile taken 302 for the GDPR-deletion bug. `bun run bug:check` green (294 headings, all unique). ## Verification **Backend: `sbt test` — 3935 passed, 0 failed, 10 ignored** (full suite, run against the rebased tree). `FarmHouseRoutesAuthSpec` on its own: 21 passed, 0 failed. **Frontend `check:ci`: 15 green, 2 red — and both reds are master's, not this branch's.** | Guard | Result | |---|---| | svelte-check, eslint, vitest, i18n, hardcoded-English, unstyled-component, autocomplete, WA ×3, contrast, token-drift, bundle-size, schema, bug-number | ✅ | | flag manifest freshness | ❌ **pre-existing** | | layout-overflow (Playwr
  • d5ae9e6 fix(auth): let Managers edit houses and areas, and stop rendering 403 as a server error (B-305) Jonas · 2026-08-16 · +482 −42
  • 3f45276 feat(nav): move Områdesadmin to Inställningar → Gård & konto Jonas · 2026-08-16 · +15 −2

#298 WebAweseom 3.11 and flags

2026-08-16 · Jonas · 1 commits · 587 filer · +1 583 −231 · Funktioner

Detaljer
  • 6306229 WebAweseom 3.11 and flags Jonas · 2026-08-16 · +1 583 −231

#287 fix(forms): give every input field an explicit autocomplete

2026-08-16 · Jonas · 1 commits · 264 filer · +1 879 −1 379 · Funktioner

Detaljer
## Why Browsers autofill/suggest into any text-entry field they recognise. In a livestock app that is almost always wrong — noise on domain data (ear tags, weights, dates, notes), and actively harmful where the field holds *someone else's* details: Chrome offering the farmer's own address while they type a supplier's. Before this, only 21 of ~1 400 fields said anything about autocomplete at all. ## What changed | | count | |---|---| | `autocomplete="off"` | 1 328 | | Real semantic token | 51 | | Already correct, untouched | 21 | | Not gated (radio/checkbox/file/color/range/… — browsers never suggest into these) | 81 | **Autofill kept ON**, only where a person types their *own* details: - Login / register / magic-link / passphrase (already correct) - Saved shipping + billing addresses, both storefront variants — `name`, `address-line1/2`, `postal-code`, `address-level2`, `tel` - Farm-shop checkout buyer block + tax country/postcode, CSA join, gift-card **sender**, product review - Public lead forms — support, newsletter, contact, cooperatives, addons, data-licensing, partners, influencers, podcast **Deliberately OFF** — including the cases that most likely caused the bad suggestions: inviting a farmer, admin-creating a user, CRM contacts, gift-card *recipient*, supplier/buyer names and addresses on movements, animal country-of-origin, API-key fields, and every number/date/note field. ## Upstream bug found along the way Web Awesome 3.6.0 accepts `autocomplete` on `<wa-textarea>` but never renders it onto the inner `<textarea>` — `WaInput`'s template binds it, `WaTextarea`'s doesn't. Without a fix, ~220 of the app's fields would have kept suggesting regardless of markup. `frontend/src/lib/util/waTextareaAutocompleteFix.ts` patches the missing binding (wired next to `initWaPreUpgradeAttrSweeper()` in the root layout) and self-disables if a WA upgrade fixes it upstream. ## Ratchet gate `find-missing-autocomplete.mjs` + `check-missing-autocomplete.mjs` vs `autocomplete-baseline.json`, same shape as the hardcoded-English and unstyled-component gates — but starting from a **clean, empty baseline**, so it is effectively strict. It also rejects unrecognised tokens, since browsers treat a typo like `autocomplete="emial"` as `on`. Wired into `check:ci` and `ci.yml`; documented in CLAUDE.md. Both the codemod and the detector parse attribute lists with quote+brace tracking rather than one regex — `<input[^>]*>` ends the tag at the `>` inside `oninput={(e) => …}`, which is everywhere in this codebase and made the first pass misread six fields and emit duplicate attributes. ## Verification ✅ **`check:ci` green (frontend + backend)** — all 16 steps, including the new *autocomplete explicitness guard*; 3 912 backend tests. Also green via the pre-push hook. **Browser-verified** against the dev server on `/support`: inner `<input>`/`<textarea>` elements carry the expected `name` / `email` / `organization` / `off`; late-created instances (dialogs, `{#each}` rows) and host property changes both sync through the prototype patch; typing still round-trips to the Svelte binding; input chrome still renders with the themed border and background rather than unstyled. No screenshot — the Browser pane wasn't displayed so the page wouldn't composite frames, so the visual evidence here is computed styles and DOM state, not an image. Worth a glance on a form-heavy app page before merge. **Gate proven to fail**, not just to pass: injecting a bare `<input>`, a bare `<textarea>` and `autocomplete="emial"` into `newsletter/+page.svelte` was caught as 3 hits with exit 1, then reverted. ## Note I added the new step to `ci.yml` too, but that workflow was already missing the hardcoded-English and unstyled-component gates — so CLAUDE.md's "mirrors `ci.yml` step-for-step" claim is already out of date independent of this PR. Left that pre-existing divergence alone rather than widen the diff. 🤖 Generated with [Claude Code](https://claude.com/claude-cod
  • 9af1313 fix(forms): give every input field an explicit autocomplete Jonas · 2026-08-15 · +1 879 −1 379

#294 fix(eurlex): tokenise on Unicode letters so Swedish queries survive retrieval (B-287)

2026-08-16 · Jonas · 2 commits · 8 filer · +223 −38 · Buggfixar B-287

Detaljer
## Vad och varför Första skarpa körningen av `scripts/copilot-stress` (97 bondefrågor mot `/api/farms/:id/eurlex/ask`) gav **`PASS 11 · FAIL 4 · ERROR 82`**. 82 frågor — samtliga svenska — retrieverade noll chunkar och nådde **aldrig** någon LLM. Två defekter bakom det. Den här PR:en fixar den billiga. ### Fixad: `tokenise` shreddade svenska ord `EurLexCorpus.tokenise` delade på `[^a-z0-9]+`, så varje icke-ASCII-bokstav var en avgränsare: | ord | före | efter längdgolv ≥3 | |---|---|---| | `måste` | `m`, `ste` | `ste` | | `får` | `f`, `r` | *(inget)* | Svenska djurhållningens viktigaste substantiv var alltså **helt osynligt** för retrievalen. Nu `[^\p{L}\p{N}]+`. ### Inte fixad: korpusen är enbart engelsk De sju filerna under `src/main/resources/eurlex/` är på engelska medan UI-språket är svenska. Den här PR:en gör alltså **inte** svenska frågor sökbara — den är en förutsättning för en översatt korpus. Kvarstår öppen i B-287. ## Det fynd som väger tyngst Inte de 82 tysta felen, utan de 9 "lyckade". Sju av de nio svenska frågor som *nådde* LLM:en tog sig dit på skräptoken — `per`, `under`, `mask`, `register`: - `edge-003` förväntade 2016/429, retrieverade 2019/6 + 2018/848 via `under`, och svarade självsäkert ur fel lagrum. - `mov-004` nådde LLM:en enbart för att `köper` shreddades till `per`, som matchade engelskans "per". Efter fixen returnerar den ärligt noll chunkar. Tom retrieval failar högljutt. Slumpmässig retrieval failar tyst. Kvarstående i samma klass, dokumenterat men ej fixat: `search` faller tillbaka på `chunks.take(k)` vid tom tokenmängd. Retrievalen ligger **före** transportvalet, så allt detta gällde lika mycket i prod (Vertex) som lokalt. ## Tester Två regressioner i `EurLexCorpusSpec`, **bevisat röda mot ofixad källa**: ``` - tokenise keeps short non-ASCII words that the length filter would drop once shredded corpus.tokeniseForTest("får").contains("får") .tokeniseForTest("får") = EmptySet$() ``` Att verifiera genom `search()` går inte — en shreddad fråga och en fråga utan träff ger båda noll chunkar — så tokeniseraren exponeras som `private[service] tokeniseForTest`. Mina två första testförsök passerade före fixen och kasserades av just det skälet. ## Följdändringar - **`run-stress.mjs`** räknade backendens `"No relevant regulation chunks found"` som ett giltigt svar → 46 av 97 rader blev fantom-PASS och första körningen rapporterade vilseledande `PASS 57 · FAIL 40`. Tom retrieval är nu ett eget `ERROR`-verdikt som räknas in i exitkoden. - **`judge.mjs`** får Hermes/Ollama-transport (`JUDGE_PROVIDER`, auto när `HERMES_LLM_URL` är satt) så domaren kan köras utan Anthropic-nyckel. Ollamas `format`-schema överlever inte `*:cloud`-proxyn — gemma svarar i prosa — så schemat specas i prompten och parsas löst. Anthropic-vägen är omstrukturerad (dynamisk import) men **inte körd**. - Andra committen: felade domaranrop scorade 0 rakt igenom och föll ur "svaga svar"-filtret, så en körning där allt felade rapporterade `korrekthet 0.00` med tom flagglista — omöjligt att skilja från "bedömd, allt dåligt". Nu taggade, uteslutna ur snitten och räknade i sammanfattningen. - B-287 i `other/KNOWN_BUGS.md` (nummer allokerat via `bun run bug:next`), README, `docs/wiki/architecture/llm-dual-transport.md` + `log.md`. ## check:ci ✅ **grön (frontend + backend)** — kört på den rebasade grenen (bas `ffdcff15`) per B-233. 15 av 16 checkar gröna i en körning; `sbt test` föll där på 8,9 s med `AccessDeniedException` på meta-buildens jar — den kända sbt2-flakan (en kvarlämnad java-process från en avbruten körning), inte ett testfel. Efter att processen dödats: **3914 tester passerade, 0 föll, 10 ignorerade.** Pushad med `SKIP_CHECKS=1` eftersom gaten just körts för hand på exakt detta commit — att köra om hade kostat ~20 min (layout-overflow-guarden ensam är 920 s) med risk för samma flaka. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 833cc5d fix(copilot-stress): make failed judge calls visible instead of scoring them 0 Jonas · 2026-08-16 · +15 −6
  • 8fccbfc fix(eurlex): tokenise on Unicode letters so Swedish queries survive retrieval (B-287) Jonas · 2026-08-16 · +208 −32

#293 docs(market): CDB appendix — add full postal address and timing details

2026-08-16 · Claude (AI) · 1 commits · 6 filer · +95 −68 · Funktioner

Detaljer
## What Two small detail additions to the CDB-agreement appendix in both editions, so the report is self-contained once this working conversation is gone: - Full postal address for Djurregisterenheten (Jordbruksverket, Djurregisterenheten, 826 84 Söderhamn) in action step 1. - Timing note: CDB Internet is under active redesign and SJV has openly asked for e-service feedback — the machine-interface question lands at the right moment. Plus the clarification that Mina sidor digital fullmakter apply to private individuals only; a limited company always uses the paper form. Docs-only diff, page visually verified, both editions regenerated. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY --- _Generated by [Claude Code](https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY)_
  • e8ff002 docs(market): CDB appendix — add full postal address and timing details Claude (AI) · 2026-08-15 · +95 −68

#292 docs(market): add CDB-agreement road appendix + hens section

2026-08-16 · Claude (AI) · 1 commits · 6 filer · +342 −122 · Funktioner

Detaljer
## What Two additions to both report editions (report now 12 pages): ### New appendix: "Vägen till CDB-avtalet — vad 'relationsuppgiften' faktiskt är" Grounds GTM phase 3 / de-risk item 3 in a sixth source sweep: the SJV "agreement" is not a product you apply for but three possible routes in — the fullmakt/ombud mechanism (verified, but the ready-made JSB3.39 form covers only slaughterhouses), bulk reporting files (explicitly anticipated for "those who handle reporting for others", but no public spec/API), and the Agronod data platform (documented APIs, CDB connected, read-vs-write unknown, 2025 SJV funding call as possible financing). Names the crux question (can a software company be automated ombud for many farms?), the MinGård/TopCow precedents proving third-party CDB write exists, and a four-step action plan with contacts (Djurregisterenheten email, Agronod meeting, TopCow call, discovery farms as negotiating card). ### Species appendix: hens section Flock-level only — near-zero recurring burden, confirming §1.2's recruitment-not-revenue classification — with the two exceptions worth marketing on: universal premises registration (even five backyard hens; the horse-style compliance gap) and the egg-withdrawal medicine journal. Appendix title simplified since the species list kept growing. Docs-only diff (`marketing/` content scripts + regenerated PDF/MD). New pages visually verified. No gated surfaces touched. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY --- _Generated by [Claude Code](https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY)_
  • 23ffc50 docs(market): add CDB-agreement road appendix + hens section to species appendix Claude (AI) · 2026-08-15 · +342 −122

#291 docs(market): add cattle section to the species-by-species paperwork appendix

2026-08-16 · Claude (AI) · 1 commits · 6 filer · +155 −60 · Funktioner

Detaljer
## What The paperwork appendix (both editions) now covers cattle too — "får, nöt, häst och bin" — answering "does AgroVision own the cattle customers?" with the three-games split: - **Dairy (~2,700 farms):** a closed door, but Växa Sverige's (MinGård, milk recording, advisors, robot-vendor systems), not AgroVision's — the same moat structure as PigVision, so don't touch. - **Suckler-cow farms (~9,800 holdings):** the empty segment — nobody serves them, their tools are CDB Internet + the binder, and they're the single largest group in the slaughter tail (75% of the ~14,800 delivering holdings send cattle). - **Heaviest per-animal burden in the tail:** individual 7-day CDB reporting of every birth/movement/death — the deadline-driven, support-risking profile that creates willingness to pay. - **Sequencing:** sheep now (no agreement needed), suckler farms as the main segment the moment the Jordbruksverket agreement lands — plus the practical synergies (same farms as sheep; every suckler conversation strengthens the SJV negotiation). Docs-only diff (`marketing/` content scripts + regenerated PDF/MD, report now 11 pages). New page visually verified. No gated surfaces touched. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY --- _Generated by [Claude Code](https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY)_
  • 9a7be1a docs(market): add cattle section to the species-by-species paperwork appendix Claude (AI) · 2026-08-15 · +155 −60

#290 docs(market): broaden paperwork appendix to species-by-species — sheep, horses, bees

2026-08-15 · Claude (AI) · 1 commits · 6 filer · +204 −61 · Funktioner

Detaljer
## What The paperwork appendix in both report editions is retitled "Pappersbördan art för art — får, häst och bin" / "The paperwork burden species by species", keeping the sheep year as its first section and adding: - **Horses — less routine, but two sharp traps:** event-driven burden (passport, ownership changes) plus the ~36% premises-registration compliance gap across ~105,000 horse premises, and the medicine trap — food-producing by default, with phenylbutazone causing permanent irreversible food-chain exclusion (the rule Enlantis already enforces as a hard gate). Pain = ignorance + high-stakes decisions → copilot territory, phase 2 tone of voice. - **Bees — almost nothing, except the rule nobody knows:** apiary registration, notifiable diseases, and the surprise that varroa treatments (oxalic/formic acid, thymol) are VMPs requiring a 5-year medicine journal. 5–15 entries/yr, no deadline anxiety → no willingness to pay → Free-tier volume and evangelists, as §1.2 ranks them. - A closing paragraph tying the three burdens back to §1.2's segment ranking, plus a stale sweep-count fix in the sources appendix. Docs-only diff (`marketing/` content scripts + regenerated PDF/MD). New pages visually verified. `check:ci` green on this master base earlier; no gated surfaces touched. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY --- _Generated by [Claude Code](https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY)_
  • 3abc178 docs(market): broaden paperwork appendix to species-by-species — sheep, horses, bees Claude (AI) · 2026-08-15 · +204 −61

#289 docs(market): add "the sheep farm's paperwork year" appendix to both editions

2026-08-15 · Claude (AI) · 1 commits · 6 filer · +173 −48 · Funktioner

Detaljer
## What New appendix in both report editions ("Fårgårdens pappersår — pärmen i siffror" / "The sheep farm's paperwork year — the binder in numbers") concretizing what §1.2's record-keeping obligations mean for a ~50-ewe working flock: - The yearly cycle in numbers: ~100 journal rows in six weeks at lambing, 10–30 medicine entries/yr, 5–15 double-booked movements, 3–6 FCI transcriptions, annual census/SAM/KRAV audit. - The real pains named: transcription (same fact written 2–4×), deadline anxiety (nothing reminds), and inspection risk (cross-compliance gaps dock support payments). - What Enlantis derives from one offline entry — with the honest caveat that e-service submission awaits the SJV agreement. - The pitch line ("write once, never miss a deadline, stand confident at the inspection") and the discovery question that validates it. Docs-only diff (`marketing/` content scripts + regenerated PDF/MD, report now 10 pages). New page visually verified in both editions. `check:ci` was green on this master base; no gated surfaces touched. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY --- _Generated by [Claude Code](https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY)_
  • bafbd21 docs(market): add 'the sheep farm's paperwork year' appendix to both editions Claude (AI) · 2026-08-15 · +173 −48

#288 docs(market): sharpen sheep-app appendix — Elitlamm captures only the breeding flocks

2026-08-15 · Claude (AI) · 1 commits · 6 filer · +103 −69 · Funktioner

Detaljer
## What Wording sharpening of the sheep-app appendix in both report editions, answering the objection "why target sheep farmers when they're forced to use Elitlamm?" — because they aren't: - **Two thirds of Sweden's ~8,300 sheep farms sit entirely outside Elitlamm** (its ~3,000 flocks include Denmark); the stable journal carries no Elitlamm monopoly, and even Fritid-tier customers are not captive (the breeding layer sits in the pricier tier). - Phase-0 conclusion restated as an explicit sharpening of §1.2: we target the untagged majority (~5,000+ binder-run farms) + the mixed farms — the breeding elite (~2–3,000 flocks) is left to the association, met with export/import rather than competition. Docs-only diff (`marketing/` content scripts + regenerated PDF/MD). Appendix page visually verified in both editions. `check:ci` was green on this same master base earlier today; no gated surfaces touched. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY --- _Generated by [Claude Code](https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY)_
  • 098744d docs(market): sharpen sheep-app appendix — Elitlamm captures only the breeding flocks Claude (AI) · 2026-08-15 · +103 −69

#268 Regelboken M1+M2+M3: rules registry, validator, catalog, share links, funnel, continuous revalidation, importers, org rollup

2026-08-15 · Jonas · 44 commits · 52 filer · +48 165 −4 338 · Buggfixar B-284 B-286 B-171

Detaljer
Implements Milestones **M1 + M2** of `other/RULES_PLAN.md` (Regelboken — rules registry & farm validator), decisions locked with Jonas 2026-08-09: naming Regelboken/Regelrapport/"Validera gården", all 62 rules published with honest automation levels, TODO_REVIEW markers seeded as-is. ## M1 — Validator inside Enlantis (one commit per plan item) - **M1-1** — migrations 0256–0260 (`rule`, `rule_text`, `validation_run`/`validation_finding` append-only, `validation_share`, `rule_attestation_state`), six Quill repositories + FakeRepos. - **M1-2** — validation engine (`service/rules/`): ApplicabilityEvaluator, CheckRegistry (boot-time resolution check), FarmSnapshot, 8 adapter checks over existing seams, FarmValidationService (pure core + Live, injected now, sim-clock aware); public catalog API `/api/public/rules`; per-farm run/report routes (requireFarmer, 1 run/min/farm); auth + engine specs. - **M1-3** — `rule_attestation` event type + projection into `rule_attestation_state` (expires server-side; replay-safe), local-first frontend write path via Dexie + outbox. - **M1-4** — report page `/app/farms/[id]/compliance/validation` (area rollup tiles, verdict rows w/ evidence deep links, attestation dialog, run history + diff, offline Dexie cache, SIMULERING banner), `validation_status` dashboard widget, PDF export via shared PdfService (SHA-256 stamp inherited), golden-farm regression specs. - **M1-5** — AI-drafted 62-rule Swedish catalog (`other/RULES_SEED_DRAFT.yaml`) → deterministic generator `scripts/rules/build-seed-from-draft.mjs` → per-area seed migrations 0261–0267 (8 enforced / 7 monitored / 13 attestation / 34 planned); SeedIntegritySpec; wiki page. ## M2 — The acquisition weapon (five worktree branches, merged + integration-gated) - **M2-6 Public catalog** — `/rules` (search, area/species/scheme/automation facets, live coverage stat band) + `/rules/[ruleKey]` (legal deep links, "Så här hanterar Enlantis detta", inspector checkpoints, collapsed technical section, funnel CTA); SEO meta + schema.org Legislation; both sitemaps emit per-rule URLs; SiteHeader/SiteFooter + `rules` nav bucket; in-app twin `/app/farms/[id]/rules` with "gäller min gård" toggle + per-rule "din status" chip. - **M2-7 Share links** — `ValidationShareRoutes`: create/list/revoke (requireOwner), scope full|summary, expiry default 30d; 256-bit token shown once, only SHA-256 stored; public `GET /api/public/validation-report/:token` (IP rate-limited, sanitized DTO, bodiless 404 on unknown/expired/revoked); `/report/[token]` page (noindex + robots.txt disallow, "Självskattning — ej myndighetsbeslut"); owner share dialog on the report page. 24 new auth-spec tests. - **M2-8 Funnel** — prerendered landing `/home/regelrapport`; guided wizard (`…/compliance/validation/wizard`) over the AgroVision import with a typed importer seam; auto-run: wizard posts `{importBatchId}`, the **server** derives `source=import_funnel` + `data_as_of` (client cannot forge freshness); staleness banner >14 days; `cannot_verify` upsell links; funnel analytics reuse `upgrade_nudge_event` (import_completed / report_viewed / first_own_event at the outbox choke point). - **M2-9 Consolidation** — `/regulations{,/[id]}` and `/docs/reg/*` redirect into `/rules` (URL-query filters incl. live `?basis=<CELEX>`; mapping table in `lib/rules/consolidation.ts`); `/cdb-compliance` **kept** with a "Superseded by the rulebook" banner (52 CDB codes vs 10 rules — parity honestly not there; retirement condition documented in-page); `/help/regulations-index` rewritten as a registry-driven printable A4 digest; sitemaps + internal link sweep (fixed 4 pre-existing 404 citation links on /docs). - **M2-10 Admin editorial** — "Regler" tab in root-admin Regelradar area; `/api/admin/rules` CRUD + per-locale texts + status transitions behind a server-side publish guard (sv+en, resolving check_key, ≥1 legal ref); version policy: obligation-affecting field changes auto-bump published rules, text edits never do (expl
  • 2afd154 fix(content): Erik keeps beef cattle, so his shed is a calving shed, not a lambing shed Jonas · 2026-08-15 · +11 −11
  • 86465d6 fix(bugs): renumber our B-284 to B-286 — master had already taken 284 Jonas · 2026-08-14 · +4 −4
  • dfe9944 fix(ci): lower the hardcoded-English baseline — it still grandfathered the now-keyed story pages Jonas · 2026-08-14 · +4 −18
  • c1548f9 fix(content): stories index cards described farms that were not the ones they link to Jonas · 2026-08-14 · +10 −10
  • 3f68d39 rule engine Jonas · 2026-08-14 · +0 −1
  • 7657733 rule engine Jonas · 2026-08-14 · +83 −30
  • a9394f7 Regelboken M3-12: adversarial-review fixes — demo-farm delete, banner trust UX, detect equivalence, org-rollup cap honesty Jonas · 2026-08-12 · +742 −100
  • 78fa817 Regelboken M3-12: session log entry (M3-11 entry compressed per house rule) Jonas · 2026-08-12 · +6 −4
  • 90bae65 Regelboken M3-12c: Växa/Kokontrollen fixture-first importer + plan/wiki closeout Jonas · 2026-08-12 · +1 028 −48
  • 8c2d853 Regelboken M3-12b: SJV/CDB importer (cattle), WinPig dialect, wizard format UX Jonas · 2026-08-12 · +2 081 −81
  • 3550991 Regelboken M3-12a: FarmImporter seam, farm_import_batch generalisation (0269), imported-events IR guard Jonas · 2026-08-11 · +1 958 −439
  • 6df0de3 Regelboken M3-12d: org-level validation rollup — OrgAuth extraction, /api/org/:orgId/validation/rollup, compliance strip Jonas · 2026-08-11 · +1 744 −76
  • 57b5ddb Regelboken M3-11: actually drop the three dead validation.continuous.* keys Jonas · 2026-08-11 · +28 −34
  • cacfa6e Regelboken M3-11: adversarial-review fixes — zombie-run diff baseline, clearing-verb watch set, per-tick cap + jitter, toggle desync Jonas · 2026-08-11 · +118 −47
  • 3215d04 Regelboken M3-11c: run auto-diff notifications — NotificationDispatcher wake-up over web push Jonas · 2026-08-11 · +835 −81
  • 7dcfbe0 Regelboken M3-11b: continuous revalidation worker, validation_watch opt-out, migration 0268 Jonas · 2026-08-11 · +1 974 −45
  • 655eead Regelboken M3-11a: server-side run diff + continuous provenance + aggregation surface Jonas · 2026-08-11 · +910 −50
  • 762e170 Regelboken M2: wiki + plan + session log updates Jonas · 2026-08-10 · +187 −40
  • e9a1907 Regelboken M2: fix WA size token in the funnel tour (size=small -> size=s, B-171 gate) Jonas · 2026-08-10 · +1 −1
  • ab0dea9 Regelboken M2: regenerate i18n dictionaries after M2-8 + M2-9 merge Jonas · 2026-08-10 · +31 −31
  • a3c6ef3 Regelboken M2-9: consolidate the four legacy catalog surfaces into /rules Jonas · 2026-08-10 · +1 859 −1 850
  • d9acd34 Regelboken M2-8: the acquisition funnel — landing, upload wizard, auto-run, staleness, analytics Jonas · 2026-08-10 · +3 091 −57
  • 906c90d Regelboken M2: regenerate i18n dictionaries after M2-10 merge Jonas · 2026-08-10 · +171 −31
  • 4be3265 Regelboken M2: regenerate i18n dictionaries after M2-6 + M2-7 merge Jonas · 2026-08-10 · +32 −32
  • 48f0bfc Regelboken M2-10: editorial rules admin + Regelradar change-flagging Jonas · 2026-08-10 · +2 736 −37
  • 99b400b Regelboken M2-7: share links for a private Regelrapport + /report/[token] Jonas · 2026-08-10 · +2 667 −36
  • 6b96f15 Regelboken M2-6e: in-app catalog twin with farm-aware extras Jonas · 2026-08-10 · +738 −0
  • 400d54e Regelboken M2-6d: /rules and /rules/[ruleKey] + site-chrome wiring Jonas · 2026-08-10 · +1 564 −0
  • 9050758 Regelboken M2-6c: i18n chrome for the public catalog (en + sv) Jonas · 2026-08-10 · +656 −38
  • b8768d5 Regelboken M2-6b: pure catalog model — filtering, coverage maths, applicability Jonas · 2026-08-10 · +995 −0
  • b5f68d3 Regelboken M2-6a: public catalog API surface + sitemap coverage Jonas · 2026-08-10 · +50 −1
  • 44ca5ce Regelboken M1: the 11 attestation statement keys the 62-rule seed references (en+sv) Jonas · 2026-08-09 · +1 180 −279
  • eb5e34d Regelboken PDF polish: footer wrap, human dates, param phrasing, column widths Jonas · 2026-08-09 · +246 −31
  • 2172d66 feat(i18n): key the four customer-story dispatches + the stories index (en + sv) Jonas · 2026-08-09 · +1 434 −683
  • 8615486 Regelboken M1-5b: 62-rule seed (0261-0267), integrity spec, enforced verdict, wiki Jonas · 2026-08-09 · +5 605 −8
  • 5307288 Regelboken M1-4c: visual-verification fixes on the report page Jonas · 2026-08-09 · +16 −16
  • 4e880b6 fix(i18n): hardcoded-English detector was line-ending dependent — gate flapped by checkout Jonas · 2026-08-09 · +19 −4
  • 411311e Regelboken M1-4a: report page, dashboard widget, i18n, offline cache Jonas · 2026-08-09 · +3 077 −34
  • 44cd9f6 Regelboken M1-4b: PDF export, dashboard aggregation, golden-farm specs Jonas · 2026-08-09 · +1 358 −12
  • 38a5e2b Regelboken M1-3: rule_attestation event type + projection + local-first write path Jonas · 2026-08-09 · +724 −31
  • 26ea5e8 Regelboken M1-2: validation engine, routes, auth + engine specs Jonas · 2026-08-09 · +2 945 −1
  • ec8882e Regelboken M1-5a: AI-drafted 62-rule Swedish catalog (DRAFT, needs human review) Jonas · 2026-08-09 · +3 519 −0
  • f2b7c1e Regelboken M1-1: migrations 0256-0260, repositories, seed skeleton (11 rules) Jonas · 2026-08-09 · +1 233 −0
  • f517fc4 initial plan Jonas · 2026-08-09 · +495 −6

#284 fix(tailwind-sweep): finish cluster 4 — SETTINGS/ADVISOR/ADMIN (B-268/B-277)

2026-08-14 · Jonas · 11 commits · 19 filer · +2 630 −577 · Buggfixar B-277 B-268

Detaljer
## Summary Finishes Tailwind-sweep cluster 4 (B-268/B-277 — SETTINGS/ADVISOR/ADMIN). A previous agent's work on this branch was interrupted by a watchdog kill after doing roughly half the cluster (2 commits pushed: whitelabel/ authorities/certifications, billing/cancel). Picked up from there in the same worktree, where it turned out the full cluster had actually already been finished locally but never pushed or reported. This PR: - Independently re-verifies all 8 cluster-4 pages against real (dialog-free) screenshots at 1440px and 390px. - Fixes a broken onboarding-dismissal trick in the verification harness itself that produced two false-clean screenshots this session (one was the onboarding wizard covering the page, one was blank). - Fixes a real hardcoded-English regression the `i18n:hardcoded:check` ratchet gate caught (whitelabel/authorities email placeholders). - Fixes an a11y gap in a component this PR itself created (`AdminInlineAddRow` quick-add input: `aria-label`-only → visible `<label>`). - Measures (not assumes) focus-visible on both a form-family and a list-family page. - Closes B-277 with a confirmed shared-component finding (whitelabel and authorities share three real components, not duplicated CSS) and updates the sweep triage doc. ## Shared-component migration — done (PR #282 merged) `claude/tailwind-sweep-shop` (PR #282) created shared `frontend/src/lib/components/ui/{Notice,StatusPill}.svelte`, hitting the same "third consumer owns the extraction" trigger documented in `other/TAILWIND_SWEEP_TRIAGE.md` §5.7. This branch independently hit the same trigger (9 pasted `.notice` copies + 2 `.status-pill` copies) while §5.7 still pointed nowhere. Did **not** extract a competing component or import from an unmerged sibling branch — that would repeat the exact coordination hazard PR #279/B-279 already flagged for this trigger — and instead recorded the dependency in §5.7 and waited. **#282 has since merged.** Rebased onto the merged result and migrated all 9 `.notice` consumers (`AdminAddPanel.svelte` + 8 pages) and both `.status-pill` consumers (`admin/whitelabel`, `farms/[id]/advisors`) to import the shared components, deleting the local CSS in each file. Two non-obvious things found doing the swap (both documented in §5.7 for the next agent): - The shared `Notice` colours only `.notice__title` by tone; a bare children-only `Notice` (every call site here) renders body text in `--color-text-secondary` regardless of tone — matches the one pre-existing real consumer (`shop/orders`), not a regression, but a visible change from the pasted copies' behavior (which mostly coloured the whole message by tone). - Neither shared component sets external margin; 3 of 9 call sites needed a thin wrapper `<div>` to keep the spacing their old pasted `.notice` rule supplied, since their parent isn't a gap-having flex container. Re-verified with a forced-error-state pass (mocked 500s on whitelabel/authorities/advisors, mocked risk-profile failure on insurance): the shared `notice notice--danger` class renders identically across all 4 pages with the correct tone border/tint, screenshots opened. ## Pages fixed (8) | Page | Before | Verified | |---|---|---| | `app/admin/whitelabel` | BROKEN (B-277) | All 4 B-277 symptoms fixed, re-confirmed | | `app/admin/authorities` | BROKEN | Same shared fix as whitelabel | | `farms/[id]/settings/certifications` | BROKEN | Confirmed | | `farms/[id]/settings/billing/cancel` | BROKEN | Confirmed (3 phases) | | `app/advisor/billing` | BROKEN | Confirmed (loading/error are the only branches by design — see notes) | | `farms/[id]/links` | THIN | Confirmed | | `farms/[id]/advisors` | THIN | Confirmed | | `farms/[id]/insurance` | THIN | Confirmed | ## B-277 (whitelabel) — all four symptoms confirmed fixed 1. Heading hierarchy: h1 40px/24.8px (1440/390) vs. panel heading 16px small-caps. 2. Full-bleed borderless inputs: bounded panel, `wa-input` base border resolves to
  • f40da33 refactor(tailwind-sweep): migrate cluster 4's 9 .notice + 2 .status-pill copies to the shared components (PR #282) Jonas · 2026-08-14 · +153 −206
  • 9893f6b chore(i18n): regenerate coverage.json after rebase Jonas · 2026-08-14 · +34 −34
  • 838b2f8 fix(tailwind-sweep): visible label on AdminInlineAddRow; record the Notice/StatusPill extraction ownership; measure focus-visible Jonas · 2026-08-14 · +146 −3
  • db96fee docs(tailwind-sweep): record independent re-verification of cluster 4 Jonas · 2026-08-14 · +21 −3
  • 38dbae1 fix(tailwind-sweep): i18n the whitelabel/authorities email placeholders; revert a wrong text-slate-500 cleanup Jonas · 2026-08-14 · +3 −3
  • ef0144f fix(tailwind-sweep): harden cluster-4 Playwright harness against a false-clean onboarding race Jonas · 2026-08-14 · +91 −17
  • 0604c20 docs: close B-277, update triage rows + lower unstyled-components baseline Jonas · 2026-08-13 · +40 −15
  • 49b6ab9 fix(farms): style links/advisors/insurance THIN pages (B-268) Jonas · 2026-08-13 · +598 −117
  • 29bf65a fix(advisor): style billing-redirect page's loading/error branches (B-268) Jonas · 2026-08-13 · +178 −5
  • dc3a40c fix(settings): style billing-cancel exit-survey page (B-268) Jonas · 2026-08-13 · +260 −30
  • b05d21d fix(admin): style whitelabel/authorities partner forms + certifications page (B-277/B-268) Jonas · 2026-08-13 · +1 106 −144

#282 style(sweep): B-268 cluster 2 — the shop admin pages

2026-08-14 · Jonas · 11 commits · 6 filer · +3 085 −844 · Buggfixar B-284 B-268

Detaljer
## Summary Tailwind-debt sweep, cluster 2 of 4 (B-268): the nine in-app shop admin pages under `frontend/src/routes/app/farms/[id]/shop/`. All nine had a `class` attribute and zero `<style>` block — real browser defaults leaking everywhere (unstyled `fieldset`/`legend` frames, browser-blue links used as navigation, full-bleed inputs, unlabelled fields, collapsing table headers). Follows `other/TAILWIND_SWEEP_TRIAGE.md` §5 (the translation guide) and matches the idiom of the two reference implementations merged in PR #276. **Files fixed:** - `orders/+page.svelte` — status/risk pills, table -> card-list at <=40rem (6 columns + button-heavy actions genuinely didn't fit), refund modal, expanded order-detail row. - `discounts/+page.svelte` — panel/field form, StatusPill for active/inactive (added the missing `discounts.inactive` i18n key). - `shipping-rules/+page.svelte` — added **visible labels** to every zone/rule-form field that previously had only a placeholder (added `shippingRules.rule.zone` / `.kind` / `.surchargeKind` i18n keys). - `products/+page.svelte` (list) — StatusPill, CSV export as a bordered pill link, filter chips. - `shipping/+page.svelte` — carrier/quote/label tables, route-stop fields (previously fully unlabelled). - `gift-cards/+page.svelte` — same full-bleed/card-less-form fix as discounts; StatusPill maps card status to tone. - `reviews/+page.svelte` — the one raw `<fieldset>`/`<legend>` leak on an otherwise-styled page; review list as real cards. - `withdrawal-requests/+page.svelte` — fixed the `justify-between`-with-no-gap collision ("BeställningBegärd" at 390px). - `analytics/+page.svelte` — the triage could only reach this page's "Laddar…" branch; this pass mocks a full populated `GET /api/farms/:id/analytics/shop` response to reach and style the real KPI/funnel/table state. Swapped a raw-hex `FUNNEL_COLORS` JS array for the `--chart-1..5` tokens. **Extracted** `frontend/src/lib/components/ui/StatusPill.svelte` and `Notice.svelte` — the third+ consumers of that pattern after the two reference pages, per the triage doc's "third consumer owns the extraction" rule. Pure-CSS atoms with no behaviour (`.panel`, `.field`, `.field-grid`, `.editor-head`, `.back-link`) stay duplicated per file in scoped `<style>` blocks, matching the two already-merged reference pages — `global.css` already defines an unrelated marketing `.panel`, and Svelte `<style>` has no shared-partial mechanism for a low-risk, low-value CSS-only extraction. **Responsive tables:** added a `.data-table` → card-list fallback at `<=40rem` (each `<td>` carries `data-label`, read via `::before`) rather than `overflow-x: auto`, per the explicit instruction that a too-wide table should become cards at the breakpoint, not just frame the spill (repo's own B-179/B-271 history). ## Verification Rendered every page at 1440px and 390px against a mocked API (`other/fas06-01-genomgang/verktyg/b268-shop-cluster2-verify.mjs`), screenshots opened and read, computed styles measured (0px horizontal overflow at both widths on all 9 pages, 0 browser-default-blue anchors). `orders` additionally verified with the expanded detail row and the refund modal dialog open, at both widths. **Follow-ups filed, not fixed by the original sweep (out of scope for a styling pass):** - `wa-input[required]` renders an unattached "*" glyph via its own shadow DOM when given an external `<label>` instead of its `label` attribute — pre-existing (reproduces on unmodified master), affects `discounts`/`gift-cards`. - `shipping/+page.svelte`'s carrier table rendering zero data rows (B-284, `state_unsafe_mutation` from a `??=` inside `{@const}`) was flagged here as pre-existing and out of scope — see **Coordinator fixes** below, it landed via #283 and is now merged into this branch too. **Status-pill semantics audit** (per a mid-task reminder about a sibling cluster's cancelled/line-through-reused-as-active bug): `StatusPill.svelte` only ever varies `color` per tone — it has zero `t
  • 6411d91 fix(shop): preserve the B-284 carrier-render fix through the restyle, add Notice success tone Jonas · 2026-08-14 · +11 −14
  • f300984 docs(tailwind-sweep): fill in the actual PR number (#282) Jonas · 2026-08-14 · +9 −9
  • 50d0c4f docs(tailwind-sweep): mark shop-app cluster 2/4 rows done in the triage doc Jonas · 2026-08-14 · +9 −9
  • c59372b style(shop): give the analytics page real CSS (B-268) Jonas · 2026-08-13 · +349 −94
  • e69f51a style(shop): give the withdrawal-requests page real CSS (B-268) Jonas · 2026-08-13 · +102 −17
  • 92f19ad style(shop): give the reviews page real CSS (B-268) Jonas · 2026-08-13 · +131 −18
  • a97c0f2 style(shop): give the gift-cards page real CSS (B-268) Jonas · 2026-08-13 · +328 −85
  • 6677681 style(shop): give the shipping page real CSS (B-268) Jonas · 2026-08-13 · +360 −118
  • d1de6e7 style(shop): give the shipping-rules page real CSS (B-268) Jonas · 2026-08-13 · +377 −123
  • 84a21af style(shop): give the discounts page real CSS (B-268) Jonas · 2026-08-13 · +327 −123
  • 165674c style(shop): give the orders + products list pages real CSS (B-268) Jonas · 2026-08-13 · +1 082 −234

#286 fix(a11y): B-285 — drive outdoor-mode/big-finger darkening through tokens, not legacy class hooks

2026-08-14 · Jonas · 5 commits · 5 filer · +737 −50 · Buggfixar B-285

Detaljer
## B-285 — outdoor mode and big-finger mode darkened muted text only through legacy class hooks ### The defect `frontend/src/app.css` darkened muted text for both barn-usability modes **exclusively** through Tailwind-looking class hooks: ```css .outdoor-mode .text-slate-500, .outdoor-mode .text-slate-400 { color: #1f2937 !important; } ``` **Neither mode redefined a single `--color-*` token.** `.outdoor-mode body { color: #000 }` only reaches text that *inherits* its colour; any element that sets its own (`color: var(--color-text-secondary)` — most hint/subtitle/empty-state text in the app) kept its normal low-contrast value in direct sunlight and with gloves on. This is **older than the Tailwind sweep**. The sweep (PR #282) only made it visible by removing the last hooks on four shop pages. **Counted, not estimated** (`git show origin/master:… | grep -o 'text-slate-[45]00' | wc -l` against the same file on the branch): `origin/master` has **33** occurrences across the five shop files — analytics 15, orders 7, shipping-rules 5, shipping 4, reviews 2. PR #282 keeps **6**, all in `orders`, so **27 were removed**: analytics 15, shipping-rules 5, shipping 4, reviews 2, orders 1. `discounts` / `products` / `gift-cards` / `withdrawal-requests` had none to start with. (Both the brief and the addendum gave per-file splits; neither matched the tree.) ### Measured, before (Playwright vs `bun run dev`, real path via `localStorage['enlantis.outdoorMode']`) `/app/farms/:id/shop/analytics`, computed `color`: | element | normal | outdoor | big-finger | |---|---|---|---| | token-coloured muted text (`--color-text-secondary`) | `rgb(93, 82, 64)` | **`rgb(93, 82, 64)` — unchanged** | **`rgb(93, 82, 64)` — unchanged** | | token-coloured muted text (`--color-text-muted`) | `rgb(110, 95, 72)` | **`rgb(110, 95, 72)` — unchanged** | **`rgb(110, 95, 72)` — unchanged** | | `.text-slate-500` (legacy hook) | `rgb(42, 34, 24)` | `rgb(31, 41, 55)` ✔ | `rgb(31, 41, 55)` ✔ | Census of text-bearing leaf elements still rendering at an undarkened muted colour, with the mode ON: **20** on that page. Only the 2 that happened to carry `.text-slate-500` darkened. ### The fix — tokens are the mechanism `.outdoor-mode` and `body.big-finger` now redefine the ink family. Values are **deliberately identical to the legacy hook's `#1f2937`**, so the two paths cannot disagree and a half-migrated page cannot show two different "darkened" colours side by side. | token | base | `.outdoor-mode` | `body.big-finger` | |---|---|---|---| | `--color-text`, `--color-text-primary` | `#2a2218` | `#000` | `#0a0a0a` | | `--color-text-secondary`, `-light`, `-muted`, `-faint` | `#5d5240` / `#6e5f48` | `#1f2937` | `#1f2937` | | `--wa-color-text-default/-primary/-secondary/-subtle/-quiet` | — | derived | derived | | `--color-on-warning` | `var(--color-text)` | derived | derived | **The `:root` substitution trap (second commit).** A custom property whose value contains `var()` is substituted **where it is declared**, not where it is used. `--wa-color-text-subtle: var(--color-text-muted)` (webawesome.css) and `--color-on-warning: var(--color-text)` (global.css) are declared at `:root`, so they kept resolving against the `:root` ink even after `body.outdoor-mode` overrode the input. Measured: 14 shell elements (topbar search label + `kbd`, rail leaves, bottom-bar labels, mobile sheet rows) were still `rgb(110, 95, 72)` after the first commit. They are now re-declared inside each mode block. `--wa-color-text-quiet` (WA's own, 222 call sites, never bridged to a house token) is darkened with them. `--color-text-inverse` is deliberately **not** touched — `--color-on-primary/-danger/-info/-alert` alias it, and darkening it would put dark text on the burgundy fills. ### Measured, after | element | normal | outdoor | big-finger | |---|---|---|---| | `.hint` (`--color-text-secondary`) | `rgb(93, 82, 64)` | **`rgb(31, 41, 55)`** | **`rgb(31, 41, 55)`** | | shell muted (`--wa-color-text
  • fd121ba docs(a11y): correct the counted removals, add wiki 2.1, record two measured residuals Jonas · 2026-08-14 · +77 −10
  • 30e3167 review(a11y): make the mode guard fail closed; cover grouped selectors and both modes at once Jonas · 2026-08-14 · +176 −22
  • 46851c4 docs(a11y): B-285 — rewrite sweep rule 7; log the outdoor/big-finger trap Jonas · 2026-08-14 · +299 −4
  • 3abf14c fix(a11y): re-declare :root-derived ink tokens inside the mode blocks Jonas · 2026-08-14 · +35 −5
  • 0958fca fix(a11y): drive outdoor-mode/big-finger darkening through tokens Jonas · 2026-08-14 · +150 −9

#285 fix(wa): app-wide suppression of the orphan required marker on label-less form controls (B-283)

2026-08-14 · Jonas · 5 commits · 3 filer · +277 −19 · Buggfixar B-283

Detaljer
Fixes **B-283**. Pre-existing on `origin/master` — found during the Tailwind sweep, not caused by it. ## The defect Every `required` form control that gets its label from the hand-rolled `<label><span>Kod</span><wa-input required></wa-input></label>` wrapper rendered a standalone `*` on its own line between the label text and the input. ## Root cause (in the vendored WA build, `chunk.DLTFNMAZ.js` = `form-control.styles.ts`) Two rules in the shared form-control base disagree: ```css /* the label is only STYLED when it has content … */ :is([part~='form-control-label'], [part~='label']):has(*:not(:empty)), :is([part~='form-control-label'], [part~='label']).has-label { … } /* … but the marker is drawn gated ONLY on [required] — not on label content */ :host([required]) :is([part~='form-control-label'], [part~='label'])::after { content: var(--wa-form-control-required-content); } ``` **The marker rule is unconditional on label content.** The `'*'` value comes from the theme (`static/wa/styles/themes/mellow.css:338`). The component renders its `<label part="form-control-label label">` **unconditionally**, so with no label the box is empty — 0 px on its own — but `::after` still fires and inflates it to **25.59 px**. ## Option chosen: (b) app-level `::part` override Not a vendored patch. `static/wa/dist-cdn/` is re-fetchable third-party build output (`vendor-wa-dist.mjs`); a patch there vanishes on the next refresh (cf. `other/FAS06_SELFHOST_THEME_BUG.md`). No durable patch mechanism exists in this repo. Within (b), I measured two variants rather than reasoning about them: | variant | required+unlabelled | nested labelled child in a group | |---|---|---| | `--wa-form-control-required-content: ''` (the vendor token, as B-176 uses inline) | **25.59 px — gap survives** | **loses its `*`** (custom properties inherit through the shadow boundary) | | `::part(form-control-label)::after { content: none }` | **0 px / `none`** — identical to the not-required baseline | keeps its `*` | So the token is only a half-fix. Shipped the pseudo-element suppression. Targeting the `::after` rather than `display:none` on the part matters for an app-wide rule: the guard is a light-DOM *approximation* of the component's internal `hasLabel`, so a false positive must degrade to "missing asterisk", never "invisible label". Guards: `:not([label]):not([with-label]):not(:has(> [slot='label']))` — the three light-DOM paths that set `hasLabel`. ## Components covered `wa-input`, `wa-select`, `wa-textarea`, `wa-number-input`, `wa-time-input`, `wa-color-picker`, `wa-checkbox-group`, `wa-radio-group`, `wa-otp-input`. Deliberately **excluded**: `wa-checkbox` / `wa-switch` (their `[part~=label]` is the default slot — real visible text), `wa-slider` (already visually hidden unlabelled), `wa-known-date` (renders no label element at all without one). ## Relationship to PR #279 — credit where due **Cluster 1 (PR #279, now merged) found and root-caused this first**, and fixed it `.field`-scoped with `display:none` on the part. Its own §7 recorded the snippet at **2 copies with a 3rd expected**. This PR generalises the same idea app-wide, which it has to be: **744 of the 1081 `<wa-input>` tags pass no `label=`** (69 %), and most are nowhere near a `.field` wrapper. (Counted with a brace/quote-aware tag extractor — a line-based grep undercounts because Prettier wraps long tags; see the counting note in sweep-guide 5.3b.) Merge order: #279 already landed; this branch is **rebased on top of it**. Both rules coexist harmlessly. `other/TAILWIND_SWEEP_TRIAGE.md` §5.3 now carries a "superseded — do not copy" pointer and new §5.3b explains the app-wide rule, so the guide describes **one** mechanism and no cluster adds a fourth copy. ## Visual verification (Playwright/Chromium vs `bun run dev`, PNGs opened and looked at) Before-pass asserts in-script that the defect actually reproduces on each surface, so an after-shot can't be vacuously green. | surface | requi
  • 89c82f2 docs(B-283): correct the wa-input scope figures and record the counting method Jonas · 2026-08-14 · +41 −7
  • f40961a refactor(wa): collapse the B-283 selector list with :is(); drop novel bug tag Jonas · 2026-08-14 · +17 −10
  • 63a7dbc docs(sweep-guide): mark 5.3's scoped required-marker CSS superseded by 5.3b Jonas · 2026-08-14 · +9 −2
  • e52e624 docs: B-283 required-marker fix — DESIGN.md rule, sweep-guide 5.3b, KNOWN_BUGS Jonas · 2026-08-14 · +148 −0
  • fa81d6e fix(wa): suppress orphan required marker on label-less form controls (B-283) Jonas · 2026-08-14 · +62 −0

#283 fix(shop): shipping carrier table renders zero rows (B-284)

2026-08-14 · Jonas · 3 commits · 9 filer · +252 −23 · Buggfixar B-283 B-284

Detaljer
## Summary - `frontend/src/routes/app/farms/[id]/shop/shipping/+page.svelte` rendered **zero `<tr>` rows** in its carrier table on `origin/master`. Header renders, then the page jumps straight to "Frakttaxor" — no visible error in the UI, only in the browser console (`Svelte error: state_unsafe_mutation`). - Root cause: `{@const row = editing[c.carrierCode] ??= {...}}` inside the `{#each carriers as c}` block mutated `editing` (`$state`) as a side effect of a `{@const}` template expression. Svelte 5 rejects this and aborts the entire each-block's rendering. - Fix: moved the `editing[code]` seeding out of the template into `loadCarriers()` (seed-if-absent, so a reload never clobbers text the user already typed). `{@const row = editing[c.carrierCode]}` is now a pure read. - Diff on the page itself is minimal (11 insertions / 1 deletion) — script section + the one `{@const}` line, per the conflict-avoidance note below. **Not a styling regression.** Reproduced with Playwright on `origin/master`, and confirmed at the source level (`git show origin/claude/tailwind-sweep-shop:.../+page.svelte`) that the sibling branch doing a pure CSS pass on this same file still has the same unpatched `??=` (just at a different line number) — this bug predates and is independent of the Tailwind-sweep styling work. ## Verification Playwright against `bun run dev -- --port 5205 --strictPort`, mocked `/api/farms/:id/shipping/carriers` + `?dev_user=` bypass (`other/fas06-01-genomgang/verktyg/b284-shipping-carriers-verify.mjs`): - **Repro'd the bug**: temporarily stashed the fix → `pageerror: Svelte error: state_unsafe_mutation`, 0 rendered rows (`shots/b284-00-BUGGY-before-fix.png`, `b284-00b-BUGGY-carrier-table-crop.png`). - **With the fix**: all 8 carrier rows render (`postnord`, `bring`, `dhl_parcel`, `ups`, `gls`, `colissimo`, `dpd`, `self_delivery`) — zero Svelte errors, zero pageerrors (`shots/b284-01-fixed-full-page.png`, `b284-01b-fixed-carrier-table-crop.png`). The only remaining console entries are `ws://localhost:5205/ws/sync` connection failures, which are inherent to this backend-less mock harness (it only intercepts `**/api/**`) and appear regardless of this change. - **Form still works**: typed a value into a row's countryCodes field, it survived (`shots/b284-02-shipping-typed.png`); clicked Save and confirmed the `PUT` payload shape is unchanged (`{enabled, apiKey?, countryCodes?, coldChainCapable}`) and carriers reload (`shots/b284-03-shipping-after-save.png`). ## Codebase sweep for the same shape Grepped `frontend/src` for the same `{@const ... }` + mutation shape (`??=`, `+=`, `-=`, `++`, `--`, mutating array/Map/Set methods) — **zero other hits**. Also wrote a scope-aware scanner checking for mutation of known `$state` variables inside `$derived(...)`/`$derived.by(...)` bodies — found 3 candidates, all confirmed false positives on manual review (locally-scoped variable/const shadowing a same-named `$state`, or a `.push()` on a fresh local object's same-named property). No other instances of this bug class exist today. **Gate recommendation**: a cheap CI check for compound-assignment/increment operators inside `{@const}` tags would be low-noise (zero false positives in the sweep) and would have caught this exact bug. A similar check for `$derived` body mutation is **not** recommended without real AST/scope analysis — the regex heuristic false-positived on all 3 candidates. Flagged as a follow-up task rather than built in this PR to keep the diff minimal. ## Bug log Logged as **B-284** in `other/KNOWN_BUGS.md` (allocated via `bun run bug:next`), marked ÅTGÄRDAD (fixed in code, PR open/draft, not merged). Note: this was originally filed as B-283, but an independent `/code-review` pass caught that `claude/wa-required-marker` had claimed B-283 a minute earlier for an unrelated fix (not yet visible to `bug:next` at allocation time) — re-ran the allocator after fetching and renumbered to B-284 (entry, verify script, and screenshot
  • 41b77f7 fix: renumber B-283 to B-284 (allocator collision) + add hard assertions to verify script Jonas · 2026-08-14 · +55 −18
  • f34dc3a docs(known-bugs): tighten B-283's cross-branch repro claim Jonas · 2026-08-14 · +7 −4
  • 1928f5d fix(shop): shipping carrier table renders zero rows (B-283) Jonas · 2026-08-14 · +190 −1

#279 style(sweep): B-268 cluster 1 — the four public / customer-facing pages

2026-08-14 · Jonas · 8 commits · 4 filer · +911 −145 · Funktioner

Detaljer
Phase 2 of the Tailwind-debt sweep (**B-268**), cluster 1: the four **public, customer-facing** surfaces. Follows the translation guide in `other/TAILWIND_SWEEP_TRIAGE.md` §5 and the two reference implementations merged in #276. These pages are not a regression — Tailwind was never active in this repo, so the markup's layout classes were inert strings and the pages have simply never had styling. ## Files | File | Was | Now | |---|---|---| | `farmers/[slug]/products/[productSlug]/ReviewForm.svelte` | BROKEN | card frame, field rhythm, floating-`*` fixed | | `farmers/[slug]/gift-cards/+page.svelte` | BROKEN | 42rem column, panels, equal-width chip tracks | | `farmers/[slug]/gift-cards/redeem/+page.svelte` | THIN | 36rem column, balance card, house link tier | | `routes/status/+page.svelte` | BROKEN | 48rem column, service cards, segmented uptime strip, legend swatches | Container widths come from `other/TAILWIND_INTENT_INVENTORY.md` (§8 step 2's "paid-for spec"), not from taste. ## Corrections landed against the guide - **§5.3's floating-`*` diagnosis was wrong, and is now fixed in the doc.** The marker is not a light-DOM sibling and `.field` alone does not fix it: Web Awesome renders its own `form-control-label` part even with no `label` attribute, so every `required` control gets an empty ~26px row carrying just the asterisk. Suppressing that part where we supply the label does fix it. Measured on `ReviewForm`: label heights `68/93/169/93/68` → a uniform `73/73/148/73/73`. - **§5.7's `.field` extraction trigger was based on a miscount.** A grep for a scoped `.field { … }` rule returns **72 files** — it is a pre-existing house idiom, not a two-copy sweep artifact, and the §6.1 reference page has no `.field` at all. A global `.field` is also ruled out, since those 72 definitions differ from one another. Doc corrected; the genuine sweep artifact to watch is the WA `form-control-label` suppression, now at 2 copies, and the shop-app cluster is its natural owner. - **§5.3a is untouched** — every control here is a `wa-*` component, so this branch is not its first proof. The honesty note stays, with an addendum saying so. - **`.notice` extraction trigger deliberately not consumed.** None of these four surfaces needs a bordered callout card; inline validation text and empty-state cards are a different pattern, and with four clusters running concurrently, four agents independently creating `ui/Notice.svelte` is a merge hazard. Recorded in the doc so the trigger stays armed. - The redeem page's invalid-code state carries the archived intent's amber semantics as the certified `--color-warning-text` (#7a5600) on `--color-surface` (#f1ead7). That token is documented at 5.35:1 against the page ground, and `--color-surface` is lighter than the page ground, so the realised ratio is ≥ 5.35:1 — stated explicitly because `a11y:check`'s 50 pairs do not include this exact combination. - Dropped a hardcoded `#3b82f6` stroke on the status latency line for `--color-secondary` (measured through to `rgb(110,122,74)`). ## Verification (CLAUDE.md hard rule) Playwright against `bun run dev` on a private port, mocked API, screenshots **opened and read** before and after at 1440px and 390px. Measured across 24 combinations (4 surfaces × 2 widths × normal / `body.outdoor-mode` / `body.big-finger`), re-run after the width correction: **0px horizontal overflow**, **0 anchors computing to `rgb(0,0,238)`**, **0 native `groove` fieldsets**, no page errors. Also exercised and looked at: the gift-cards enabled CTA (`rgb(122,31,26)` on white, 44px), the purchase-receipt / stub-payment branch, all three redeem branches, the ReviewForm 500-error branch, the invalid-amount and gift-cards-disabled branches, status banner up/degraded/down, empty incidents, and the incident `wa-details` Timeline **expanded** (`white-space: pre-wrap`, still 0px overflow at 390). No page in this cluster has a `wa-dialog`; the expanded `wa-details` is the honest stand-in for CLAUDE.
  • 2d2501c style(sweep): honour the archived container widths (code-review fix) Jonas · 2026-08-14 · +24 −3
  • 1f0fb1c docs(tailwind-sweep): correct the .field extraction trigger (code-review fix) Jonas · 2026-08-14 · +30 −5
  • bb2038b docs(tailwind-sweep): record cluster 1 and correct the floating-asterisk rule Jonas · 2026-08-13 · +72 −13
  • f9a687a chore(style-gate): lower the unstyled-component baseline 44 -> 40 Jonas · 2026-08-13 · +1 −5
  • 9b367b4 style(status): give the public status page its containers and rhythm Jonas · 2026-08-13 · +342 −49
  • 2d41621 style(shop): style the public gift-card redeem page Jonas · 2026-08-13 · +117 −12
  • b4d46d8 style(shop): style the public gift-card purchase page Jonas · 2026-08-13 · +241 −44
  • a341b4f style(shop): give the public ReviewForm a card frame and field rhythm Jonas · 2026-08-13 · +84 −14

#280 style(production): Tailwind sweep cluster 3 — pigs production pages + KillSheetPage (B-268)

2026-08-14 · Jonas · 7 commits · 3 filer · +1 651 −298 · Buggfixar B-268

Detaljer
## Summary Tailwind-debt sweep, cluster 3 of 4 (production/pigs pages + the shared `KillSheetPage.svelte`), per `other/TAILWIND_SWEEP_TRIAGE.md`. These pages shipped with zero `<style>` block — Tailwind was wired into Vite but never activated, then stripped 2026-07-03, leaving raw browser-default rendering. This is a pure restyle: dead utility class names renamed to semantic classes built from `frontend/src/global.css` tokens, all work in new scoped `<style>` blocks. No markup restructuring, no data/logic changes. - `production/pigs/withdrawal-calendar/+page.svelte` — the month grid had no `grid-template-columns` at all (weekday labels stacked in one column, day numbers in another); now a real 7-column `.calendar-grid`, token-based legend swatches (was a raw `#1e3a5f` hex), today outlined in `--color-primary`. - `production/pigs/slaughter-bookings/+page.svelte` — "Ny bokning"/"Avboka" were raw native `<button>` elements; now `.btn`/`.btn-primary`/`.btn-text`. Status is an outlined `.status-pill` per state. Create form + pre-flight detail panel in bounded `.panel` cards. - `production/pigs/kill-sheets/reconciliations/[recId]/+page.svelte` — back link was a raw browser-blue underlined anchor; now `.back-link`. Stats in a bounded `.panel` + `.stat-grid`, condemnations as `.chip-row`. - `production/pigs/genetics/+page.svelte` — the three tables (boars/AI-doses/progeny) had no cell borders and overflowed at 390px; now each in `.table-scroll` + `.data-table`. Import form's dead `.block` replaced with `.field`/`.select-input`/`.file-input`. - `frontend/src/lib/components/KillSheetPage.svelte` — **the important one**: triage rated the three host pages (`production/{pigs,broilers,cattle}/kill-sheets`) FINE as chrome-only, but the actual body is this shared component, which had zero `<style>` block. KPI rollup + dispatch list now in bounded `.panel` cards; status text (previously dead `bg-*`/`text-*-600` tokens rendering nothing) is now a proper `.status-pill`; the reconciliation modal is a real `.modal-backdrop` + `.modal-card` with `.field`/`.field-grid`/`.grade-grid`, and its ~30 native inputs are covered by one descendant rule (`.modal-card :global(input...)`) rather than touching every tag, per the sweep guide's §5.3a note. ## Regulatory-data safety These pages carry withdrawal periods (Reg (EU) 2019/6 Art 108), slaughter bookings, and kill-sheet reconciliation figures. **No rendered value, unit, date format, or rounding was changed** — every edit was a `class` attribute rename plus a new `<style>` block; no `{...}` expression, formatter call, or computation was touched. Verified by diff review of every changed file and by comparing mocked values end-to-end from fixture → rendered screenshot (see Test plan). ## Test plan - [x] Rendered all 4 pages + `KillSheetPage.svelte` on **two host routes** (`production/pigs/kill-sheets` and `production/cattle/kill-sheets`) at **1440px and 390px** against mocked API data (`other/fas06-01-genomgang/verktyg/tw-sweep-production-verify.mjs`), screenshots opened and read. - [x] Captured **before** screenshots (stashed the changes, re-ran the same script) confirming the triage's stated defects (columnless calendar grid, raw native buttons, run-together legend text) and confirming the **after** screenshots fix them. - [x] `document.documentElement.scrollWidth === clientWidth` at 390px on every page — 0px overflow. - [x] 0 anchors computing to `rgb(0, 0, 238)` (browser-default blue) on every page. - [x] `npm run style:baseline` — unstyled-component count dropped 44 → 39. - [x] `SKIP_BACKEND=1 npm run check:ci` — ✅ green, see below. ``` ✅ svelte-check (TypeScript + Svelte) 134.5s ✅ eslint (lint gate) 132.0s ✅ vitest unit tests 38.2s (3218 tests passed, 193 files) ✅ i18n coverage guard 1.7s ✅ hardcoded-English ratchet guard 1.7s ✅ unstyled-component ratchet guard 0.9s ✅ WA boolean-attr bin
  • 78e6a26 fix(sweep): give the slaughter-booking blocked pill its own tone (B-268) Jonas · 2026-08-14 · +196 −3
  • edff85b chore(tailwind-sweep): lower unstyled-component baseline, record cluster-3 verdicts Jonas · 2026-08-13 · +223 −12
  • 15267eb style(components): style KillSheetPage — shared by pigs/cattle/broilers kill-sheets (B-268) Jonas · 2026-08-13 · +481 −111
  • a578914 style(production): style the pigs genetics tables + import form (B-268) Jonas · 2026-08-13 · +212 −86
  • 1406c1c style(production): style the pigs kill-sheet reconciliation detail page (B-268) Jonas · 2026-08-13 · +114 −11
  • 6d9444a style(production): style pigs slaughter-bookings — house buttons + status pills (B-268) Jonas · 2026-08-13 · +211 −48
  • 1bcd95e style(production): give the pigs withdrawal calendar a real 7-col grid (B-268) Jonas · 2026-08-13 · +214 −27

#281 feat(tools): copilot stress-test suite + sheep-farm rules one-pager

2026-08-14 · Claude (AI) · 2 commits · 9 filer · +882 −61 · Tester

Detaljer
## What Two follow-ups to the tail market-research work (#274, #278): ### 1. Sheep-farm rules one-pager (`marketing/`) `render_lathund_far.py` + `lathund_fargardens_regler.pdf` — a one-page Swedish handout, "Fårgårdens regler — på en sida": the ten documentation duties governing a Swedish sheep farm's daily life, distilled from the app's own regulatory content (`other/eu_regulations_short.md`, the regulations catalog, `welfare_check_rule` seeds, `IRDeadlines`). Built for phase-0 marketing (REKO pickups, association talks). Carries a mandatory disclaimer: simplified guidance, not legal advice, external expert review pending (de-risk item 7). Visually verified. ### 2. Copilot stress test (`scripts/copilot-stress/`) A farmer-simulation stress test for the regulatory Q&A surface, answering "does the app survive a farmer's every conceivable question?": - `questions.json` — 97 questions in 16 categories (medicines/withdrawal, tagging, stable journal, movements, welfare, slaughter/FCI, organic, transport, horses, bees, poultry, pigs, CAP, panic questions, off-topic/prompt-injection, tricky edge cases), each with an expected behaviour: `answer` (must answer with the right citation), `refuse` (must decline — e.g. vet emergencies, sanction amounts, human medicine), or `either`. - `run-stress.mjs` — zero-dep runner. Farm mode bootstraps a user via `POST /api/users/auth` (dev header auth) + a mixed-scenario sim farm (`POST /api/sim/farms` — includes sheep) and hits `POST /api/farms/:id/eurlex/ask`; public mode hits the rate-limited `POST /api/public/copilot/ask`. Automatic PASS/FAIL scoring, JSONL raw output + markdown summary, non-zero exit on failures. - `judge.mjs` — LLM judge (`@anthropic-ai/sdk`, new root devDependency; model `claude-opus-5`, structured outputs) grading correctness, citation faithfulness, tone, and flagging hallucinations — the failure mode the automatic scorer can't see. **Honest caveat: the suite has not been executed yet.** This sandbox cannot run the backend (Java 21, no sbt/MySQL), so the first run happens on the dev machine per the README. Expect a few questions to need expectation recalibration after the first real run. ## check:ci ✅ check:ci green (frontend: all 15 gates passed incl. layout-overflow; backend skipped — `sbt` not on PATH in this sandbox) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY --- _Generated by [Claude Code](https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY)_
  • a838f82 docs(market): add sheep-app competitive appendix (Elitlamm/Lammio) to both report editions Claude (AI) · 2026-08-14 · +208 −60
  • 15e2131 feat(tools): copilot stress-test suite + sheep-farm rules one-pager Claude (AI) · 2026-08-14 · +674 −1

#277 fix(scene3d): seven render-frame geometry bugs the gates cannot see

2026-08-13 · Claude (AI) · 3 commits · 10 filer · +663 −86 · Buggfixar

Detaljer
A deliberate sweep for **geometry and placement errors that every automated gate passes and a rendered frame exposes** — the class the formspråk pass kept producing (backwards ground slabs, a doorway backed by a solid wall, a sorting gate anchored to nothing, bales stored where no loader can reach). Method: rendered `/dev/scene3d-harness` at 10 / 100 / 400 actors and Gårdsvyn under Playwright (Chromium at `/opt/pw-browsers/chromium`, own dev server on **5193**, never 5173), drove the orbit camera to close crops via `__setCamera`, **opened every PNG and looked at it**, then measured each suspect through the pure builders (they take a `StaticBatcher` as their only sink, so every emitted piece is measurable without WebGL). > Branched off `origin/claude/scene3d-formsprak-rendering-w2rdn6` per the brief, then **merged `origin/master`** in so the work sits on the real merge product (master had B-279's `WALL_CLEARANCE_M`, which the base branch predates). No rebase, no merge of this PR. --- ## Findings, ranked by how badly they mislead a farmer reading the scene | # | Finding | File:line | Why no test catches it | Evidence | Status | |---|---|---|---|---|---| | 1 | **Pen status disc exists on one face only.** A `CircleGeometry` parked 0.03 m in front of an **opaque** plate. `DoubleSide` stopped it backface-culling, but the plate did the occluding instead — from anywhere on the −z side of a sign the plaque reads **blank**. The scene orbits a full turn and signs are placed in world axes, so on any farm roughly half the pens show *no status* from an ordinary camera angle. That is a claim about the herd ("nothing flagged here") the data does not make. | `scene.ts:1626` (`buildPenSigns`) / `scene.ts:1698` (`writeSignMatrices`) | `penSign.test.ts` asserts sizes and wall clearance; nothing asserts *visibility from a direction*. Picking still worked, so the interaction tests were green. | Rendered the same sign from θ=+π/2 and θ=−π/2 with `?statuses=1`: amber disc on one side, blank plate on the other. | **Fixed** | | 2 | **The feed silo floats.** Legs stopped at the hopper cone's **apex** height (`legH = r*1.7`) while standing `0.86 r` off the axis — and a cone is a *point* at its apex. At the shipped 3 m footprint: leg tops y = 1.94 m, cone surface at the leg radius y = 3.15 m. Four poles in the grass with a silo hovering **1.2 m** above them. | `barn.ts:735` (`buildSilo`) | Nothing renders the silo in a test, and the numbers are individually plausible. | Close crop from ground level — visible gap between the leg tops and the cone. | **Fixed** (legs run to `bodyBase`) | | 3 | **The sorting gate projects through the door gable.** `interiorZones()` sizes the leaf from the building's **WIDTH** (`halfW * 0.8`) while laying it out along the building's **LENGTH**, inside a handling zone only `halfL − penMax` long. On the standard Gårdsvyn barn (16.2 × 26.4, five 40 m² pens ⇒ **2.2 m** of handling area) the swung end lands at local x **13.30** against a gable at **13.20** — the end post and all three rail ends stand ~11 cm proud of the falu-red siding, seen from outside. | `barn.ts:678` | `barn.test.ts`'s only handling-area fixture is a wide-and-short 20×12 barn where the clamp never binds — the exact shape the real adapter never produces. | Close crop from outside the gable: three steel studs through the siding at y 0.38 / 0.71 / 1.03 m. | **Fixed** (`gLen ≤ handLen − 0.8`) | | 4 | **Silo "corrugation rings" are square boxes on a round body** (`w = d = r*2.08` across radius `r`) — four corners per band **0.47 r** proud, as shelves. And **three** of them at exactly 1/4, 2/4, 3/4 of the body height is a graduated scale up a storage vessel, i.e. a **fill gauge** → straight breach of the locked "decoration must never look like data". | `barn.ts:754` | The rule is prose in a header comment, not an assertion. | Overview crop: the silo reads as a stack of dark plates, not a vessel. | **Fixed** (round seams, ~0.5 m pitch) | | 5 | **Grass grows throu
  • 1039df2 fix(scene3d): seven render-frame geometry bugs the gates cannot see Claude (AI) · 2026-08-13 · +508 −48
  • d167abf fix(scene3d): bales stood on end beside the door, real walkways between pens Claude (AI) · 2026-08-11 · +42 −21
  • 4016a61 fix(scene3d): open the door gable from inside, hang the sorting gate, water in the pen Claude (AI) · 2026-08-11 · +113 −17

#278 docs(market): English edition of the tail market-research report

2026-08-13 · Claude (AI) · 1 commits · 7 filer · +1 091 −152 · Funktioner

Detaljer
## What Adds an English edition of the "Att nå svansen" market-research report (merged in #274), per request. Same content, same honest-assessment style, 9 pages. - `marketing/render_tail_report_en.py` — English content (translation of the Swedish edition, which stays canonical) - `marketing/tail_market_research_en.{pdf,md}` — generated outputs ("Reaching the Tail") - `marketing/report_render.py` — the PDF/markdown renderer extracted into a shared module so the two language editions don't duplicate ~150 lines of reportlab code; `render_svansen_rapport.py` slimmed to content + a `render()` call (Swedish outputs regenerated, content unchanged) - `.gitignore` — ignore `__pycache__/` Visual verification: English PDF pages rendered and reviewed (title page, tables, §6–7); one bad column wrap fixed during review. Swedish edition regenerated through the shared renderer and spot-checked. ## check:ci Docs-only diff (markdown/PDF/report-generator scripts under `marketing/` + a `.gitignore` line — no gated surfaces touched). A full `check:ci` run passed green earlier this session on this same master base (frontend: all gates; backend skipped, `sbt` not on PATH in this sandbox). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY --- _Generated by [Claude Code](https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY)_
  • d529f31 docs(market): add English edition of the tail market-research report; extract shared PDF/MD renderer Claude (AI) · 2026-08-13 · +1 091 −152

#274 docs(market): Svansen-marknadsundersökning — nå långsvanskunderna i Skåne/Sverige

2026-08-13 · Claude (AI) · 2 commits · 3 filer · +1 010 −46 · Funktioner

Detaljer
## What Follow-up market research report to the "Enlantis vs. AgroVision — honest assessment" (2026-08-11), answering: who the long-tail customers are, where in Skåne/Sweden they live, their digital and physical meeting places, and an ROI-ranked marketing plan. Swedish-language, same honest-assessment style, 8 pages. Files live in the new `marketing/` directory: - `marketing/svansen_marknadsundersokning.pdf` — the report (deliverable) - `marketing/svansen_marknadsundersokning.md` — markdown mirror for in-repo reading - `marketing/render_svansen_rapport.py` — single source of truth; regenerates both with `python3 marketing/render_svansen_rapport.py` (requires `reportlab`) ## Contents 1. Who the tail customer is — size/species/demographics (55,400 holdings, 63% ≤20 ha; ~8,300 sheep farms; 15–17k beekeepers; avg. age 59) + four marketing personas 2. Where they live — Sweden and a Skåne map (≈6,700 holdings, the Österlen belt and the ridges, "60 min radius from Sjöbo" pilot-recruitment thesis) 3. Digital meeting places — REKO rings, species Facebook groups, Alternativ.nu (verified 51,270 members), farm press, niche podcasts 4. Physical meeting places — events (Elmia Oct 2026, Skördetid på Österlen; MILA flagged as likely dormant), associations (LRF Skåne 13,200; Skåne is Biodlarna's largest district), Granngården, sheep shearers as a channel 5. Long-tail competitors (Elitlamm, Lammio, MinGård, the state's own portals, paper/Excel) 6. ROI-ranked channel plan phased against `other/GTM_REALITY_PLAN.md` — no paid marketing before pilot signal; CAC/LTV worked example with all assumptions visible 7. Honest caveats (unverified Facebook counts, missing keyword volumes, zero validated users still the top issue) 8. **New:** the age question and generational handover — the digital cliff is at 75+, not 59 (Internetstiftelsen SOI 2024/2025); farmers already file SAM digitally at near-100% rates since ~2012; handover is structural tailwind with an honest caveat (much of it is farm exit, not transfer) Sources: Jordbruksverket/SCB statistics, association/media data, four web research sweeps (2026-08-13). Visual verification: all PDF pages rendered and reviewed. ## check:ci ✅ check:ci green on the first commit (frontend: all 14 gates passed; backend skipped — `sbt` not on PATH in this sandbox). After rebasing onto master (post-#275), the re-run was skipped at the repo owner's explicit request — docs-only change (three files under `marketing/`, no code paths touched). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016htp5ABGuVSXCXYnNXdyPY
  • cb3d776 docs(market): move svansen report to marketing/; add §8 on age/tech-adoption and generational handover Claude (AI) · 2026-08-13 · +143 −46
  • 12cf1dd docs(market): svansen-marknadsundersokning — who the long-tail customers are, where in Skåne/Sweden they live, their digital/physical meeting places, and an ROI-ranked marketing plan Claude (AI) · 2026-08-13 · +867 −0

#276 B-268 phase 1: triage all 46 unstyled pages, translation guide, and the two worst customer-facing pages

2026-08-13 · Jonas · 6 commits · 3 filer · +2 211 −423 · Buggfixar B-268 B-267

Detaljer
Phase 1 of the **B-268** sweep. Foundation only: the triage of all 46 unstyled pages, the translation guide the rest of the sweep will follow, and the two worst customer-facing pages implemented as the reference application of that guide. Later agents take the remaining 21 BROKEN + 5 THIN. Related: **B-268** (umbrella), **B-267** (the shop product page Jonas reported), **B-277** (white-label — confirmed here). --- ## The premise, restated Tailwind was wired into Vite but **never activated** — no stylesheet ever imported it, so every utility class was an inert string. The 2026-07-03 removal stripped the classes and archived the intent in `other/TAILWIND_INTENT_INVENTORY.md`, expecting per-page redesign passes that were never scheduled. **These pages are not a regression — they have never had styling.** Grepping for Tailwind class names is the wrong search; the signal is the absence of CSS. ## What is in this PR ### 1. Triage of all 46 — `other/TAILWIND_SWEEP_TRIAGE.md` Every one of the 46 was **rendered in headless Chromium at 1440 px and 390 px, and the screenshots were opened and read.** Per file: verdict, honest **data state**, and one line of concretely observed detail. | | BROKEN | THIN | FINE | total | |---|---|---|---|---| | shop-public (`routes/farmers/**`) | 3 | 1 | 5 | 9 | | public | 1 | 0 | 3 | 4 | | shared component | 0 | 1 | 0 | 1 | | shop-app (`.../shop/**`) | 8 | 0 | 4 | 12 | | app | 8 | 3 | 5 | 16 | | admin | 3 | 0 | 1 | 4 | | **total** | **23** | **5** | **18** | **46** | **2 of the 23 BROKEN are fixed here**; 21 BROKEN + 5 THIN remain, with a suggested order in §4.7 of the doc. **6 files could only be reached in a loading / error / not-found branch** and a 7th only partially; many more rendered an empty list. All of it is flagged per row in a **Data state** column — a guessed verdict is worse than an admitted gap. Also called out explicitly: `KillSheetPage.svelte` is rated THIN while its three host pages are rated FINE, because the hosts were judged as chrome only — fixing the component fixes all three, and the doc says so in one place so nobody reads "cattle kill-sheets: FINE" as "nothing to do". **B-277 confirmed independently:** all four reported symptoms on `/app/admin/whitelabel` reproduce, and `/app/admin/authorities` shows the identical set — it is a shared form pattern, not two bugs. ### 2. The translation guide — same doc, §5 The point of the phase: decide **once** how the recurring stripped-Tailwind patterns map to this project's tokens and scoped CSS, with before/after snippets taken from the two pages actually implemented here. Page container · `<fieldset>`→`.panel` · `class="block"`→`.field`/`.hint` · `grid-cols-*`→`.field-grid` · `flex justify-between`→`.editor-head` · `bg-green-100 text-green-800`→`.status-pill--*` · `border-red-300 bg-red-50`→ `.notice` · browser-blue `<a>`→`.back-link`/`.inline-link` · wide tables→ `.table-scroll` · `border-blue-600 bg-blue-50`→`.is-selected` · `fixed inset-0`→`.modal-backdrop`/`.modal-card` · raw-size images · `class="hidden"`→ the real global `.sr-only`. Load-bearing findings recorded so nobody re-derives them: - **`.block`, `.grid`, `.relative`, `.fixed`, `.group`, `.hidden`, `.shadow`, `.inline` have no rule anywhere** in `global.css` / `app.css` / `webawesome.css`. They are on the intent inventory's KEEP list because the strip script was conservative, **not** because they work. - **`.flex`, `.flex-col`, `.items-center`, `.justify-between`, `.text-center`, `.sr-only` genuinely do work** — hand-defined house CSS. - **Keep `text-slate-500`/`400` in the class list** and add a semantic class alongside: they render nothing normally, but they are the hook `body.outdoor-mode` / `body.big-finger` use in `app.css` to darken muted text. - **`flex-wrap` and `gap` are the two properties whose absence produced most of the 390 px collisions** in this triage. - Status/accent colours used as **text** take the a11y contract tokens (`--color-
  • 185c1df docs(tailwind-sweep): flag the one guide rule that is not demonstrated Jonas · 2026-08-13 · +9 −0
  • 9310f9f fix(tailwind-sweep): apply code-review findings Jonas · 2026-08-13 · +67 −5
  • 11e25a7 chore(tailwind-sweep): adopt master's detector, drop the duplicate, lower baseline to 44 Jonas · 2026-08-13 · +47 −63
  • 5c59f51 docs(tailwind-sweep): triage all 46 unstyled pages + translation guide (B-268) Jonas · 2026-08-13 · +829 −0
  • 50ab3c2 style(shop): give the admin product editor real CSS (B-267) Jonas · 2026-08-13 · +650 −242
  • 50e1274 style(shop): give the public product page real CSS (B-267/B-268) Jonas · 2026-08-13 · +609 −113

#275 Add unstyled-component ratchet gate (B-268)

2026-08-13 · Jonas · 2 commits · 8 filer · +958 −9 · Buggfixar B-268

Detaljer
## Summary Builds the regression gate B-268 asks for: **absence of CSS is the signal**, not Tailwind class names (only ~4 dead Tailwind-class hits remain app-wide, per `other/TAILWIND_INTENT_INVENTORY.md` / B-268's own correction of its premise). - `frontend/scripts/find-unstyled-components.mjs` — detector. Deliberately simple: a `.svelte` file that renders markup, has at least one `class` attribute (static `class="…"`, a `class:name` directive, or dynamic `class={…}`), and has **no `<style>` block anywhere in the file**. `src/lib/illustrations/**` is exempt on purpose — those are leaf SVG-art components that take a `class` *prop* and forward it to the caller (`class={klass}`), a separate, already-closed system (see `MEMORY.md` "Ikonprojekt Enlantis-60"), not unstyled product UI. - `frontend/scripts/check-unstyled-components.mjs` — per-file ratchet against the checked-in `frontend/unstyled-components-baseline.json`, modelled directly on `check-hardcoded-english.mjs`: does **not** require fixing the existing debt, only fails when a file's offending-line count goes up or a brand-new offending file (absent from baseline == 0 allowed) appears. - Wired as `npm run style:check` / `style:baseline` / `style:unstyled:report` (mirrors the `i18n:hardcoded:*` naming), added as a step in `scripts/check-ci.mjs`. - Documented in `CLAUDE.md` next to the hardcoded-English gate. - `other/KNOWN_BUGS.md` B-268 updated to note the gate now exists — **B-268 itself stays open**; the 46 pages are a separate, ongoing job. ## Detector vs. the other agent's branch `git fetch origin claude/tailwind-sweep-foundation` found **no such branch** on the remote — checked before writing any code, and re-checked again before this update. No competing `find-unstyled-components.mjs` existed to build on at any point, so I wrote the detector myself, at exactly that path/name so a later branch converges onto it rather than colliding. ## Two bugs found in independent review, fixed (2nd commit) An independent bug-scan of the first commit found two real defects in `find-unstyled-components.mjs`, both reproduced with isolated fixture files before fixing (fixture output below), then fixed and re-verified: **Bug 1 — `hasStyleBlock()` false negative.** It ran against the RAW file source. Any literal `<style` occurring in an HTML comment, a `<script>`-side string, or documentation/example markup written as a JS/template-literal string made the file count as "has scoped styling" and get silently skipped — defeating the gate on exactly the pages most likely to contain such text (docs/help/example pages). Fixed by stripping `<script>` bodies, HTML comments, `{expr}` bodies, and quoted string literals before checking, and requiring an actual open+close `<style>...</style>` pair rather than a bare substring. **Bug 2 — `CLASS_ATTR_RE` over-match.** `/\bclass\s*[:=]/` — `\b` fires on either side of a hyphen, so a kebab-case prop/attribute ending in `-class=` (e.g. `wrapper-class={...}`) was wrongly read as a real `class` attribute. Fixed with a negative lookbehind excluding a preceding word character or hyphen, and by scanning class attributes on the same `{expr}`/string-blanked text used for the `<style>` check (closing the same documentation-text gap for `class="..."` that bug 1 closed for `<style>`). **Fixture evidence (before fix, isolated per-bug):** ``` fixture_a (no real <style>, only "<style" inside a comment/script string): NOT FLAGGED <-- BUG #1 CONFIRMED (false negative) fixture_b (no real class attr, only a "wrapper-class" prop/data-attr): FLAGGED: {"file":".../fixture_b/+page.svelte","count":1,...} <-- BUG #2 CONFIRMED (false positive) ``` **After fix:** ``` fixture_a: FLAGGED: {"file":".../fixture_a/+page.svelte","count":2,"lines":[15,16]} <-- FIX #1 CONFIRMED fixture_b: not flagged <-- FIX #2 CONFIRMED ``` **Baseline impact: none.** Re-running the fixed detector against the real (non-fixture) codebase produced the exact sa
  • 0efc7d5 Fix two confirmed detector bugs in the unstyled-component gate Jonas · 2026-08-13 · +61 −8
  • b92d473 Add unstyled-component ratchet gate (B-268) Jonas · 2026-08-13 · +897 −1

#273 docs(bugs): sweep shipped entries out of Öppna, correct B-273 premise

2026-08-11 · Jonas · 1 commits · 1 filer · +689 −654 · Buggfixar B-273

Detaljer
Housekeeping sweep of `other/KNOWN_BUGS.md` — the Öppna section listed 28 entries when only 21 were actually open. **Moved to Åtgärdade (7):** B-279, B-269, B-261, B-258, B-257, B-256, B-218. Two had status lines written before their PRs merged, now filled in: B-279 → #271 (root cause + the invariant tests, including the tautological-test trap that was caught and fixed), B-269 → #256 (budget recalibration + the loud skip). **Deliberately NOT moved:** B-096. Its own status says the code side landed in #107 but the Kinde-console verification is still outstanding — it belongs under "waiting on a human", not "done". **B-273 corrected rather than closed.** The entry asked to hide Gårdsvyn on mobile; Jonas decided the opposite on 2026-08-09, and the entry point + fullscreen shipped in #265. The entry now leads with the decision (quoted), records what shipped, and states what remains — mobile adaptation of the 3D view itself, plus a 30 fps target still unmeasured on a real phone. The old code analysis is kept but explicitly demoted to background: it is still accurate as a reading of the code, while its conclusion is void. **Verification:** the set of `### B-NNN` headings is identical to master's — 271 entries, none lost, none duplicated (checked with a sorted diff, not by eye) — and `bun run bug:check` passes. Docs-only; no source touched. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 36708e9 docs(bugs): sweep resolved entries out of Öppna; correct B-273's premise Jonas · 2026-08-11 · +689 −654

#271 B-279: fix pen/wall containment (zero-clearance touch)

2026-08-10 · Jonas · 4 commits · 4 filer · +442 −133 · Buggfixar B-279

Detaljer
## Summary Jonas reported (2026-08-10) that on `/dev/scene3d-harness?actors=400`, pen rail fencing crosses the barn wall and pigs render outside the building. Logged as B-279 in `other/KNOWN_BUGS.md`. A previous agent session root-caused and fixed this, then was killed by a network error before it verified its own tests/screenshots. This session picked up from its two commits and independently re-verified everything below from scratch — including finding and fixing a flaw in the original red-proof methodology (see the Tests section). ## Root cause (single cause, confirmed) `frontend/src/lib/gardsvyn/farmWorldAdapter.ts`'s `layoutHouse()` packed the two pen rows with `buildingDepth = ROW_DEPTH * 2 + AISLE_WIDTH` — exactly filling the building's short axis with **zero margin**. A pen's outer edge therefore landed on the exact same world coordinate as `scene3d/barn.ts`'s `longWalls()` wall line. `buildPen()` draws its rail tubes AT that same coordinate (no clamping of its own), so the rail visually interpenetrated the wall siding, and an animal jittered toward its cell's outer 30% (`placeAnimalsInPen()`) rendered pressed against — and, from the scene's oblique camera, over/through — that same line. This is **not** the originally-suspected "pen rect escapes the building" bug — a numeric probe against the exact harness scenario (`n=400`, 4 rotated houses) found every pen rect mathematically *contained* within its building's rotated AABB (`buildingAabb()`), and the pre-existing test "every pen stays inside its own buildings footprint" already passed. The bug is a **zero-clearance touch**, not an escape: containment alone doesn't catch it, only a minimum-clearance check does. `sim3d/simWorldAdapter.ts` (`/sim`) does **not** share this root cause — its server-owned seed data (`SimSeedService.scala`) already bakes in ~1m clearance. Confirmed visually this session with a mocked world in that shape; no backend change made. ## Fix `WALL_CLEARANCE_M = 0.6` constant. `buildingDepth` gets `+ WALL_CLEARANCE_M * 2`, both pen rows are inset by `WALL_CLEARANCE_M`, and the length-axis margin (previously purely proportional, `END_MARGIN_FRACTION`) is floored at `WALL_CLEARANCE_M * 2` so a small building can't shrink it below a safe minimum either. Shared code between Gårdsvyn (real farms) and the harness — the fix isn't harness-only. This session also added `FarmScene.setOrbit()` / `window.__setCamera` (`scene.ts`, `scene3d-harness/+page.svelte`) — a small automation hook, alongside the existing `__harnessPicks`/`__sceneStats`/`__benchReady`, that lets a Playwright script pose the camera at an exact angle instead of simulating mouse drags. Used to get the before/after screenshots below. ## Tests — red → green, independently re-proven this session `farmWorldAdapter.test.ts` (28 tests) has: - a general wall-clearance invariant over the `house1`/`house2` fixture, - a small-building edge case where the proportional margin alone would be tiny, - an exact reproduction of the harness's `n=400` / 4-rotated-house scenario, pinning `bench-a160`'s actual position, - a general "every actor renders inside its own assigned pen" invariant. **Methodology note:** a naive first attempt to prove red — setting the exported `WALL_CLEARANCE_M = 0` — still passed all 28 tests. The tests import `WALL_CLEARANCE_M` from the same module as their own assertion threshold, so zeroing the constant also zeroes the threshold: tautological, proves nothing. Fixed by shadowing `WALL_CLEARANCE_M` to `0` **only inside `layoutHouse()`** (a function-local `const`), keeping the test file's imported threshold (0.6) independent of the production value. With that, 3 of 28 tests genuinely fail, with measured clearance `-8.881784197001252e-16` / `0` (float noise, i.e. mathematically exact zero) — matching the original diagnosis. Restored → all 28 green again. ## Visual verification (this session, Playwright/headless Chromium against `bun run dev -- --port 5189 --strictPort`) - `/de
  • b932ca3 docs(known-bugs): B-279 — independent verification of the pen/wall fix Jonas · 2026-08-10 · +61 −21
  • 449b8a9 feat(scene3d): expose setOrbit camera-pose hook for B-279 visual verification Jonas · 2026-08-10 · +27 −0
  • f3f9430 fix(gardsvyn): give pen rows WALL_CLEARANCE_M from the building wall (B-279) Jonas · 2026-08-10 · +237 −112
  • 7a4dfda Log B-279: pen/wall containment bug — preliminary findings (WIP) Jonas · 2026-08-10 · +117 −0

2026-08-10 — 1 direkt-commits

Detaljer
  • f50da9a Bug log B-271…B-278 + fix-round 6 handoff (#257) Jonas · 2026-08-10 · +764 −0

#270 fix(scene3d): fence the idle wander inside the animal's own pen

2026-08-10 · Claude (AI) · 1 commits · 3 filer · +149 −2 · Funktioner

Detaljer
## What and why Found while checking a report that animals were standing outside their pens in the new formspråk render. **That specific report turned out to be about the concept-scene reference image** (`other/sim-formsprak-referens/barn-v2-view1.png` has three loose pigs on the alley by the door) — not the app. But reading the wander code to check it surfaced a real defect underneath. `stepActors()` picked a wander target anywhere within `wanderRadius` (1.4 m) of an actor's anchor, with **no reference to the pen the animal stands in**. In a 3.8 m pen most animals are anchored closer than 1.4 m to a rail, so the wander walked them through it. The scene then showed an animal outside its box — a claim about the herd the data does not make, and exactly what the honesty rule (`other/SIMULATOR_VISUALIZATION_VISION.md`) forbids the renderer from inventing. This was invisible while pens were knee-high timber. The formspråk pass's crisp rail line at 0.36/0.70/1.04 m and its inset straw bed made the pen boundary legible, which is what turned a latent geometry sloppiness into a visible false statement. ## Measured, not assumed Instrumented `FarmScene` with a temporary probe (removed before commit) reporting each actor's live position and world bounding box against its pen rect, on `/dev/scene3d-harness` after a 50 s settle: - **0 of 10 actors outside their pen rect** — so nothing was grossly misplaced. - Bodies reached up to **+0.10 m past a rail** (worst case `a5`, pen-3's `x0` side). Small in this fixture, but the bound was arbitrary: with an anchor 1.2 m from a rail the old code could target 0.2 m outside it, and the adapter's `legalMinSqm`-derived pens can be considerably tighter than the harness's 3.8 × 8. ## The fix New pure `penRoamRect(pens, x, z)` in `barn.ts`: resolves the pen containing the point and insets it by `PEN_BODY_CLEARANCE_M` (0.5 m) so the animal's *body*, not just its origin, stays inside the rails. The inset is capped at 35% of the pen's own extent, so a very small pen still leaves room to move and the rectangle can never invert. `scene.ts` resolves it once per actor spawn and once per real pen move (reusing the existing `anchorMoved` branch that already re-resolves `buildingId`) — never per frame — and the wander clamps its target into it. An animal in no pen (loose on the yard) gets `null` and keeps the historical unfenced wander. Pure and exported so the invariant is unit-testable without a WebGL context — same rationale as `cameraGlide.ts` / `penSign.ts` / `focus.ts`. ## Tests Six new cases in `barn.test.ts` (16 total in the file, 3209 → 3215 in the suite): - returns the containing pen, inset on every side; - null outside every pen, and on an empty pen list; - never inverts on a pen narrower than twice the clearance; - clamps a target that would otherwise cross the rail; - **a full 1.4 m wander sweep (64 angles) at every one of the 10 fixture anchors stays inside its own pen.** ## No B-number `bun run bug:next` cannot reach open PRs from this container and reports its own answer as **UNVERIFIED**. Allocating off that risks exactly the collisions the tool exists to prevent, so the code comments describe the defect without a number. Worth allocating one from a machine with `gh` if this should be logged in `other/KNOWN_BUGS.md`. ## check:ci ✅ **green on the merge product** — run by the pre-push hook against this branch on master `3fb7d41c`. ✅ svelte-check · ✅ eslint · ✅ vitest · ✅ i18n coverage · ✅ hardcoded-English ratchet · ✅ WA boolean-attr · ✅ wa-dialog hide-handler · ✅ WA variant/size · ✅ a11y contrast · ✅ token-drift · ✅ layout-overflow (Playwright, 128 combos) · ✅ UUID/column-type schema · ✅ bug-number duplicates ⏭ bundle-size (no production build) · ⏭ sbt test (`sbt` not on PATH in this container — this branch touches zero backend files) ## Visual verification Playwright + Chromium at `/opt/pw-browsers/chromium` against a dev server on port 5190 (never 5173). `/dev/scene3d-harness`
  • b466a2c fix(scene3d): fence the idle wander inside the animal's own pen Claude (AI) · 2026-08-10 · +149 −2

#269 feat(scene3d): the locked Swedish-barn formspråk, as merged static geometry

2026-08-10 · Claude (AI) · 4 commits · 11 filer · +3 032 −321 · Dokumentation

Detaljer
Implements `other/HANDOFF_MILJOGRAFIK.md` — the miljögrafik/formspråk pass. `scene3d` drew a floor slab plus knee-high timber walls; it now draws the form language locked 2026-08-02, calibrated against the three approved renders in `other/sim-formsprak-referens/` (whose concept scene's `siding()` / `beam()` / `canvasTex()` builders were **ported, not rewritten**). Since the simulator and Gårdsvyn share `lib/scene3d/`, this lands on both surfaces at once. ## What was built, against the locked list | Locked item | Where | Status | |---|---|---| | Full falu-red gables to ridge height | `barn.ts` `gable()` | ✅ ridge − eave ≈ 0.54 × half-width, as in the reference | | Locklist siding, per-board colour variation | `siding()` | ✅ boards + cover battens, deterministic per-board tone jitter | | White corner boards (knutar) | `trimAndCorners()` | ✅ two boards per corner, meeting at the arris | | Barge boards (vindskivor) with end details | `trimAndCorners()` | ✅ one straight rake board each + eave foot + ridge cap | | Ventilation grille | `ventGrille()` | ✅ louvred, white-bordered, above the door | | Large Z-braced sliding door, half open | `slidingDoor()` | ✅ leaf slid ~80% clear, hangers on a galvanised track | | Tall long walls (~2.4 m over the slab) | `longWalls()` | ✅ 2.45 m | | Light spruce lining, flat horizontal rails | `longWalls()` / `gable()` | ✅ lining + girts + studs, no truss | | Galvanised tubular pen gates (~1.1 m) | `buildPen()` | ✅ 1.14 m, `metalness 0.9` — unmistakably a different system | | Light concrete alley, painted edges, drain | `floorAndAlley()` | ✅ | | Straw bedding in pens | `buildPen()` | ✅ | | Gravel yard outside the door | `environment.ts` | ✅ apron per door gable + instanced loose stones | | Spruce forest edge | `buildScatter()` | ✅ instanced, 1 draw | | Gärdsgård with gate openings where roads pass | `buildFence()` | ✅ **enforced by construction** — see below | | Interior hierarchy: hay at one gable, handling area at the door | `interiorZones()` | ✅ derived from where the pens actually are | | NO roofs, NO trusses | — | ✅ none exist | | Decoration must never look like data | palette | ✅ see below | **"No geometry may cross a road"** is not eyeballed: roads are declared first as corridors, and every fence run is literally built from the segments left after `clipAgainstRoads()` subtracts them; trees and grass tufts are rejected inside a corridor too. Gate openings and their heavier gateposts + open galvanised field gate are placed at the resulting gaps. **"Decoration must never look like data"**: green/amber/red stay reserved. The treeline is a dark blue-green `#2c4d35`, falu red is a dark brown-red, bedding is a pale ochre. `GROUND_COLORS` (contrast.ts) was refreshed to the *effective* surface colours (tint × texture base), so the WCAG selection-ring assertions in `contrast.test.ts` still describe pixels that are really drawn — re-measured table is in the file. **Deliberately left out:** a mobile-specific renderer (the handoff says "legible fullscreen on a phone" is enough for this pass); anything from the not-in-scope list (timeline, drag-and-drop, layout editor, walk mode, consequence model). No external assets were added — every texture is procedural (`textures.ts`), which costs zero requests, keeps the offline-first promise, and avoids new texture bindings in a render-bound pass. ## Rotation contract change `SceneBuilding` gained `rotationY` (radians about the footprint centre; **local +x is the door gable**). `posX/posZ/width/depth` still describe the building's *own* rectangle — **not** the box it occupies once rotated. That distinction is the whole trap: a rotated rectangle's AABB is larger than the rectangle, so any code reserving space, testing overlap, picking the focus house or framing the camera from `posX + width` is wrong. Two exported helpers make it impossible to re-derive it wrongly: `buildingAabb(b)` and `rotatedExtent(w, d, θ)`. `scene.ts` uses `buildingAabb` for focus footprint
  • 1bcebb8 fix(scene3d): recapture the harness parity reference at the branch tip Claude (AI) · 2026-08-10 · +21 −4
  • a3080f7 fix(scene3d): code-review fixes for the formspråk pass Claude (AI) · 2026-08-10 · +308 −65
  • c2ddc9c perf(scene3d): re-baseline the budget on merged static geometry, + wiki Claude (AI) · 2026-08-10 · +180 −25
  • a6e39bf feat(scene3d): the locked Swedish-barn formspråk, as merged static geometry Claude (AI) · 2026-08-09 · +2 523 −227

#267 feat(scene3d): tier C status-dot rendering for the off-focus herd (nivå C)

2026-08-10 · Claude (AI) · 3 commits · 9 filer · +427 −84 · Funktioner

Detaljer
## What and why Tier C ("statusprickar", `other/SIMULATOR_VISUALIZATION_VISION.md` §Prestandabudgeten) shipped in `lib/scene3d`, **pulled forward ahead of the miljögrafik/formspråk pass**. The 2026-08-09 perf investigation (`other/SIM_PERF_400_ACTORS.md`) measured 20.7 fps at 400 actors on real hardware against a 60 fps target, with `render()` accounting for >95% of frame time. Of the levers isolated there, *not drawing the off-focus herd* was the single biggest one — bigger than shadows. The formspråk pass will add gables, panel walls, fences and gates **into that same shadow-casting `render()`**, so the headroom has to be bought first. That is what this PR does. **This branch has since been rebased onto master @ `1c8b73f6`, which includes PR #264 (single-material GLB bake). Every draw-call figure below is re-measured on that merge product** — the numbers in the original PR body were taken against a pre-bake master and are stale. See "Re-measured on the merge product". ### Implementation - **Threshold**: `TIER_C_ACTOR_THRESHOLD = 400`, inclusive (`isTierCActive(focusModeActive, actorCount)` in `focus.ts`, unit-tested). Inclusive on purpose — `tierCTargets.atActors` is 400, so an exclusive boundary would leave the locked measurement point permanently in tier B where the caps were provably unreachable. - **Rides the existing focus gate**: tier C can never activate without focus mode (≥2 buildings *and* >120 actors), and it uses the same 3 m / 1 s focus hysteresis. Only the **off-focus** herd becomes dots; the focus house keeps full tier A/B fidelity. - **Instancing**: one pooled `THREE.InstancedMesh` (`SphereGeometry(0.14, 8, 6)` + `MeshBasicMaterial`, `DynamicDrawUsage`), capacity grown geometrically and never shrunk, `castShadow=false`, `receiveShadow=false`, `frustumCulled=false`. Matrices + per-instance colours are rewritten each frame and finalized after the actor loop. The whole off-focus herd is **1 draw call**. - **Honesty rule holds**: a dot is the real animal at its real position, tinted with its real B-113 welfare status colour; neutral grey while status is unknown. Nothing invented, nothing hidden. - **CPU**: an off-focus actor `continue`s past wander/mixer/tint entirely — it costs one instance slot per frame and nothing else. - **Picking**: the dot mesh is added to `getPickables()`; `raycastPickable()` maps `hit.instanceId → dotIndexToAnimalId[i]`. Hover degrades to cursor + click (no lift/lighten visual) since the material is shared. - **`?hideOffFocus=1` keeps its meaning** ("draw NOTHING off-focus") and at tier C additionally suppresses the dots, so `hideOffFocus` vs plain tier C isolates the dots' own cost. - **New harness param `?statuses=1`** feeds a deterministic green/yellow/red map (`i % 3`) through the real `setAnimalStatus`/`setPenStatus` path. Opt-in — plain harness runs are byte-for-byte unchanged. - `getStats()` gains additive `tierC: boolean` and `dots: number`. ## Code-review fixes (commit `42355b88`) Five real defects found in review of the original implementation, all fixed and re-verified on the merge product: 1. **`raycastPickable()` now skips hits on actors that are currently `dotHidden`.** The original PR body claimed the hidden full model "resolves to the same `animalId`, so whichever is nearer wins with the same answer". **That was wrong.** `THREE.Raycaster` does not skip `visible === false`, so a hidden off-focus body sitting *in front of* another animal's dot swallowed the click and answered with its own id — clicking a dot could select a different animal. It also reported a pointer cursor + `onPickAnimal` over ground where nothing is drawn at all. A/B probe went from 34/34 phantom pickables over empty off-focus houses to 1/34. 2. **The dot `InstancedMesh` gets an unbounded `boundingSphere` at creation.** `THREE.InstancedMesh.raycast()` computes the sphere lazily on first raycast and then caches it forever, never re-deriving it as instance matrices change. Since a focus switch swap
  • 4b13038 perf(scene3d): re-measure the budget on tier C + the single-material bake Claude (AI) · 2026-08-09 · +62 −38
  • 42355b8 fix(scene3d): code-review fixes for tier C status dots Claude (AI) · 2026-08-09 · +53 −12
  • 398e9aa feat(scene3d): tier C status-dot rendering for the off-focus herd (nivå C) Claude (AI) · 2026-08-09 · +312 −34

#264 perf(scene3d): bake animal GLBs to one material — draw-call hypothesis test

2026-08-09 · Claude (AI) · 2 commits · 11 filer · +246 −16 · Tester

Detaljer
# Single-material animal GLBs — the draw-call hypothesis test **Hypothesis framing** (Jonas-approved 2026-08-09, `other/HANDOFF_MILJOGRAFIK.md` §"Budgetbeslutet — omformulerat"): every animal GLB had 2–3 material primitives ⇒ ≥2 draw calls per animal ⇒ ≥800 draws for the herd alone at 400 actors. After the 2026-08-09 measurement (`other/SIM_PERF_400_ACTORS.md`) we did **not** know whether the scene is draw-call-bound or geometry/vertex/fill-bound — `hideOffFocus` lowered both at once (draws −69 % *and* triangles −97 %). This PR halves+ animal draw calls **without changing triangle count**, which cleanly separates the two. Until this is answered, the tierC draw-call caps (600/350 @400 in `frontend/scene3d-budget.json`) measure the wrong quantity. ## What was done The 2–3 materials per model are untextured flat colours differing **only** in `baseColorFactor`, and every primitive already carried an all-white `COLOR_0` — so the shader was already computing `baseColorFactor × vertexColor`. The bake (`frontend/scripts/sim-assets/bake-single-material.mjs`, new, idempotent) moves each primitive's factor INTO `COLOR_0` (normalized uint16, exact to ~1/65535 in linear), points everything at ONE white material, and joins the primitives. Mathematically identical render at rest; **no texture atlas ⇒ no seams, anywhere**. Skinning, joints, all animation clips, meshopt + KHR quantization preserved (verified by re-inspection: identical index counts, clips, skins). | Model | materials | primitives | draws/animal | triangles | |---|---|---|---|---| | pig | 2 → 1 | 2 → 1 | 2 → 1 | 562 (unchanged) | | cow | 3 → 1 | 3 → 1 | 3 → 1 | 796 (unchanged) | | sheep (+goat, tinted reuse) | 2 → 1 | 2 → 1 | 2 → 1 | 610 (unchanged) | | horse | 2 → 1 | 2 → 1 | 2 → 1 | 690 (unchanged) | **Hover path — uniform emissive lift for every species.** With a white base material, the legacy colour-lerp toward white is a no-op on the vertex-painted parts, so hover gained a uniform emissive lift. Code review then caught that goat — the one species whose shared material is *tinted* (non-white) — would get **both** the lerp and the lift and read double-strength. Per Jonas's call (2026-08-09) the lerp is removed from the hover path entirely: hover is now the emissive lift **only**, identical strength on every species. Sick tint is unchanged. **Bake-script hardening** (from code review, no output change — a re-run reproduces the same GLBs): the material-divergence guard now includes `KHR_materials_*` extension names and aborts when identical-but-present extensions would be silently dropped by the merged material; primitives carrying the glTF default (null) material now abort with a clear message instead of a `TypeError`; `alphaCutoff` is copied onto the merged material. `scene3d-budget.json` baselines deliberately **not** touched (upper-bound gate — the improvement passes as-is; baseline ownership left to whichever branch merges first). `perf-budget.json` untouched. Manifest: `optimizedBytes` refreshed + bake provenance note (licenses unchanged, same CC0 sources). ## Measurements (agent sandbox, software-rendered — RELATIVE numbers only) `/dev/scene3d-harness?actors=400` + `window.__sceneStats()`: | Metric @400 actors | before (master) | after (baked) | Δ | |---|---:|---:|---:| | drawCalls | 1313 | 824 | **−37 %** | | triangles | 280 779 | 280 779 | **±0** (the point of the test) | | frameMs.render (8-run alternating A/B, trimmed mean) | 64.9 ms | 57.7 ms | **~−11 %** (high variance: old 55–75, new 48–83) | | fps (bench counter) | 16–16.5 | 16.5–17 | ~0 | `bun run bench:scene3d` drawCalls: 100 actors 507→**307**, 400 actors 1311.5→**822**, 1000 actors 1993→**1242**. **Sandbox verdict (caveat: software rasterizer, no real GPU — relative only):** cutting draw calls −37 % at constant triangles moved render time only ~−11 % ⇒ **in this sandbox the scene leans geometry-/per-object-bound, not draw-call-bound**, consistent with `SIM_PERF_400_ACTORS.md`'s finding that cost
  • df2f56f fix(scene3d): uniform hover lift for all species + bake-script hardening Claude (AI) · 2026-08-09 · +22 −7
  • 3a2afd6 perf(scene3d): bake animal GLBs to one material — draw-call hypothesis test Claude (AI) · 2026-08-09 · +224 −9

#265 fix(gardsvyn): Farm page links to Gardsvyn instead of embedding it, opens fullscreen (B-273)

2026-08-09 · Jonas · 2 commits · 19 filer · +589 −60 · Buggfixar B-273

Detaljer
## Summary B-273 (Jonas, 2026-08-09) superseded its own stale premise. The `other/KNOWN_BUGS.md` B-273 entry — still in draft on PR #257 (`claude/testning-runda5-rapport2`), not yet on `master` — says "hide Gårdsvyn on mobile entirely." Jonas's actual decision, verbatim: > "Jag vill ha någon form av gårdsvy på mobilen, men den får mobilanpassas, det går komma längre fram. Ha en länk till 3D-gårdsvyn på Gårdssidan, men inte rutan som var där innan. När man klickar på länken öppnas gårdsvyn i helskärm." i.e.: keep some form of farm view on mobile (mobile-specific 3D polish deferred, not built here); the Farm page gets a **link** to Gårdsvyn, not an embedded 3D box; clicking opens Gårdsvyn in **fullscreen**. Per the task's own instruction: since B-273 is not yet on `master`, this PR does **not** touch `other/KNOWN_BUGS.md` — that stays with PR #257's thread. This PR only implements the decision in code. ## What changed - **No embedded 3D box existed anywhere on the Farm page or `/areas` page** — verified by grepping `$lib/scene3d` imports and `FarmScene` usage across `frontend/src`. Only `/app/farms/[id]/gardsvyn/+page.svelte` (plus `/sim` and the dev harness) ever renders the canvas. The `/areas` page already had a plain link **button** to Gårdsvyn (added in "Gårdsvyn etapp 3", commit `ae972a8f`) — not a box. So there was nothing to *remove*; the gap was the **fullscreen** behavior. - **`frontend/src/routes/app/farms/[id]/+page.svelte`** (the Farm page / "Gårdssidan"): added a new `.gardsvyn-entry-card` — an icon + title + description + arrow, styled with existing design tokens (`--color-surface`, `--radius-lg`, `--shadow-sm/md`, `--spacing-*`), positioned before the "Stallar" section. Click handler `openGardsvynFullscreen()`. - **`frontend/src/routes/app/farms/[id]/areas/+page.svelte`**: the existing "Öppna Gårdsvyn" button now goes through the same fullscreen-entry handler instead of a plain `href` (dropped `href` — unclear whether `preventDefault()` reliably stops a `wa-button`'s internal shadow-DOM anchor, so navigation goes entirely through the handler, matching this file's own existing pattern for its other JS-driven buttons). - **`frontend/src/routes/app/farms/[id]/gardsvyn/+page.svelte`**: reused the existing Fullscreen API toggle (shipped in Gårdsvyn v1 etapp 3) instead of reimplementing it. Added `?fullscreen=1` handling: on mount, re-targets fullscreen from `<html>` onto its own `wrapperEl`. Added `forcedImmersive` state that engages a CSS-only immersive layout (`position:fixed; inset:0; height:100dvh`, `.header` hidden, explicit exit button) whenever the native Fullscreen API is unavailable or refused — an honest degrade, never a silent no-op. `.content.is-fullscreen` gets `z-index:500` so the CSS degrade actually covers ordinary app chrome (topbar/sidebar, `z-index:30-40`) — deliberately kept below `z-index:1000`, which belongs to the B-125 impersonation banner (see Code review section below). ### Fullscreen mechanism (verified in a real browser, not assumed) `requestFullscreen()` needs a user gesture, and SvelteKit's `goto()` swaps a new page's DOM into the *same* document (no reload) — so: 1. The entry click calls `document.documentElement.requestFullscreen()` synchronously inside the click handler (still within the gesture), then navigates with `goto(...&fullscreen=1)`. 2. Gårdsvyn's `onMount` re-targets fullscreen from `<html>` onto its own `wrapperEl` — browsers allow switching the fullscreen *element* without a fresh gesture once already fullscreen. 3. If either step fails (no element-level Fullscreen API — iOS Safari — or a refused/expired gesture), `forcedImmersive` still engages, giving a full-viewport CSS layout instead of nothing. Verified with Playwright against a real Chromium (not assumed from reading the code): - Click-through from the Farm page → `document.fullscreenElement` really is the Gårdsvyn `.content` wrapper (`class: "content ... is-fullscreen"`), `.header` is gone, exit button pre
  • 6b8eb60 fix(gardsvyn): code-review fixes for B-273 (z-index vs impersonation banner, exit-fullscreen stack, stale doc pointer) Jonas · 2026-08-09 · +95 −8
  • 9cf5444 feat(gardsvyn): Farm page links to Gardsvyn instead of embedding it, opens fullscreen (B-273) Jonas · 2026-08-09 · +494 −52

#266 fix(ci): reviewed ratchet baseline bump 31→32 for annelies story page

2026-08-09 · Claude (AI) · 1 commits · 1 filer · +3 −3 · Funktioner

Detaljer
## What and why Pristine master is currently **red** on the hardcoded-English ratchet, with no branch to blame: ``` [zero-$t()] src/routes/home/[[lang=locale]]/stories/annelies-gelderland/+page.svelte: 32 hit(s), baseline allows 31 ``` The page has been untouched since 2026-07-29. What changed is the **detector**: the tightening commit `9fd67db` counts one more string in a file that already belongs to the deliberately all-English story class the baseline carries (31 hits recorded, not 0). The +1 is the sharper detector seeing an existing string — not new debt. This is the exact B-233 merge-product failure mode the repo already documents: the detector-tightening PR and the story page were each green on their own branch, and nothing re-checks master after a merge because Actions is `workflow_dispatch`-only. ## The fix `node scripts/check-hardcoded-english.mjs --write-baseline`, which moves that one file's `zeroTFiles` entry from 31 → 32 (plus the `generatedAt` timestamp). No other counts move — `totalHits` (250) and `totalFiles` (87) are unchanged, because the file was already in the baseline. This is a **reviewed exception**, explicitly approved by Jonas on 2026-08-09, per the gate's own documented escape hatch ("if this is a deliberate, reviewed exception, lower the baseline in the SAME PR — do not run `--write-baseline` to silence an unreviewed regression"). Nothing is being silenced: the story pages remain fully counted as i18n debt at their real size. ## check:ci ✅ **check:ci green** — full run via the pre-push hook against this branch, which is `origin/master` + this one commit, so the branch *is* the merge product (B-233 satisfied by construction). All frontend guards green, including the previously-red `hardcoded-English ratchet`, plus `layout-overflow guard (Playwright)` 128/128. Two documented skips: `bundle-size perf budget` (no production build) and `sbt test` (sbt not on PATH in this container — this branch touches zero backend files). ## Scope One file, three lines: `frontend/hardcoded-english-baseline.json`. No source, no UI, no i18n keys. --- _Generated by [Claude Code](https://claude.ai/code/session_01BuW5fyse32LRgsM1zzPneK)_
  • 6ebd62b fix(ci): reviewed ratchet baseline bump 31->32 for annelies story page (approved by Jonas) Claude (AI) · 2026-08-09 · +3 −3

#258 perf(sim3d): diagnose 400-actor frame-time cost, add A/B toggles + stress seed

2026-08-09 · Jonas · 1 commits · 2 filer · +180 −3 · Funktioner

Detaljer
## Summary Investigation into why the Simulator/Gardsvyn can't yet hit the 400-animal design point (`other/SIMULATOR_VISUALIZATION_VISION.md` Prestandabudgeten). Full findings, measurement table, and real-hardware toggle commands are in **`other/SIM_PERF_400_ACTORS.md`**. - Real-hardware ground truth (Jonas): `/dev/scene3d-harness?actors=400` costs **~50ms/frame** (~3x over the 60fps budget). Draw calls (1295) match the checked-in post-optimization baseline (1311) — no drift, but 1295 draws shouldn't cost 50ms on modern hardware on their own. - Added `SceneDebugFlags` (`forceBackend`/`disableAnimation`/ `disableShadows`/`hideOffFocus`) wired to URL params on both the harness and `/sim/:farmId`, plus a `frameMs` breakdown in `getStats()` (anim/hover/render/other), so subsystem cost can be isolated with a single-variable A/B — on real hardware, not just this sandbox. - Found and fixed a real bug: the on-screen fps counter EMA'd the instantaneous rate (`1/dt`) instead of the period (`dt`), which over-reports sustained fps under variable frame times. - Sandbox isolation (software-rendered, relative-only — see the doc's "measurement honesty" section) shows `render()` itself dominates (>95% of loop time), not `AnimationMixer.update()` (tier B's existing throttle target). Shadows cost ~30% of render time; simply not drawing the off-focus herd (a tier-C stand-in) is the biggest single lever (~40-65%). Written up as evidence for pulling tier C's dot rendering forward. - Added a root-admin-only `stress` seed scenario (`SimSeedService.seedStressFarm` + `SimRoutes`) so a real 400+-actor `/sim/:farmId` farm can be created, not just the harness synthetic world. **Requires a backend restart to take effect** — not live on any currently-running process. - Measured the `buildWorldDto` N+1 query pattern (`SimRoutes.scala:329-355`, sequential per-actor DB round-trips) against the real running dev backend: 38.3ms @ 21 actors → ~730ms @ 400 extrapolated (likely worse in production over Cloud SQL). Written up as a recommendation, not fixed (needs new batch repository methods that don't exist yet). ## Test plan - [x] `npm run check` (svelte-check) — 0 errors, 1358 pre-existing warnings (unrelated files), from a clean re-run after fixing one TS narrowing issue this session introduced. - [x] `npm test` (vitest) — 3182/3182 passed, 192/192 files. - [ ] `npm run lint` (eslint) — started but did not finish within this session's time budget (large codebase, cold run); not confirmed green. No new lint-relevant patterns introduced (no `any`/`console`/ non-null assertions added) but this is unconfirmed, not asserted. - [ ] `sbt compile` — started (isolated worktree, own `target/`, confirmed disjoint from the running dev backend's checkout) but did not finish within this session's time budget (~45+ min, still using CPU when stopped — a cold from-scratch compile in a fresh `target/`, not hung). Manually reviewed instead: `SimSeedService.seedStressFarm` and its two private helpers mirror the existing `seedMultiSiteOrg`/ `seedPigs`/`seedHerd`/`makePenBuilding` patterns field-for-field (same row constructors, same append-path event sequencing); the `SimRoutes.scala` route change is a 3-line dispatch + a root-admin gate using the existing `RouteAuth.requireRootAdmin` helper. **Needs a real `sbt compile`/`sbt test` pass before merge.** - Full `check:ci` not run — the above is what completed in-session. **Jonas: before merging, please run `sbt compile` (and ideally `sbt test`) once your own backend is free, since this session's compile never finished confidently in the time available.** ## Not done - The `buildWorldDto`/welfare-status N+1 fix (needs new batch repository methods — out of scope for a diagnosis session). - Tier C dot rendering itself (recommended, not built — product decision). - `?backend=webgl2`'s draw-call
  • f699a2b feat(sim): root-admin stress seed scenario for 400-actor perf testing Jonas · 2026-08-09 · +180 −3

#263 docs: environment-graphics handoff for scene3d, rewritten against current master

2026-08-09 · Jonas · 1 commits · 1 filer · +265 −0 · Funktioner

Detaljer
Rewrites the environment-graphics/formspråk handoff so the next thread starts from facts that are still true. Corrections to the previous draft: - Start point was `87fe6713`; master is now `e9103a35` (15 commits, fix-round 5). - It told the next agent to leave `overflow:check` red on the schedule route because of B-256. **B-256 is fixed** (#249) and the gate is green 128/128 — following that instruction would have masked a real failure. - The perf section was rebuilt around the 2026-08-09 real-hardware measurement (20.7 fps @ 400 actors): `render()` is >95% of the frame, shadows are 30%, the animation mixer is 2.7 ms of 88 ms, and hiding the off-focus herd is worth −45%. The old "recompile the animal GLBs to one material and the cap becomes reachable in v1" framing is kept but demoted to what it actually is — a hypothesis test for draw-call-boundness, since no test so far separates draw calls from geometry. - Flags the direct collision this pass has with the budget: it adds shadow-casting geometry to a pass that is already 30% shadows. - Documents tooling that did not exist when the draft was written: the `?noAnim` / `?noShadow` / `?hideOffFocus` / `?backend=webgl2` A/B toggles, `__sceneStats().frameMs`, the corrected fps counter, and `other/SIM_PERF_400_ACTORS.md`. - Records Jonas's B-273 decision (2026-08-09): keep a farm view on mobile, mobile polish deferred, and replace the embedded 3D box on the farm page with a link that opens Gårdsvyn fullscreen. The draft had this as an open question that would have changed the mobile requirements. - New traps: `strictPort` is on (never use 5173), `overflow:check` can give false negatives, packages hoist to the repo root, and bug numbers must come from `bun run bug:next`. Docs-only change; no source touched. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 5067b4a docs: environment-graphics handoff, rewritten against current master Jonas · 2026-08-09 · +265 −0

#262 fix(blog): split article body out of the synchronous bundle graph (B-270)

2026-08-09 · Jonas · 3 commits · 8 filer · +1 029 −759 · Buggfixar B-270

Detaljer
## Summary - `/blog` and `/blog/[slug]` statically imported every article's full `body` HTML in every translated locale (`articles.ts` + `articles.sv.ts`), so a visitor's first-load JS carried all 30 articles' text in both languages regardless of which one they read — the reason both routes blew their bundle-size budgets once B-218 (PR #247) finished translating the corpus (66.0/66.6 KB gz vs 55/60 KB budgets), and it scales linearly with both article count (30 -> 600 planned) and locale count (2 -> 24 targeted). - `body` moved out of `articles.ts`/`articles.sv.ts` into new sibling `content.en.ts`/`content.sv.ts` (slug -> HTML), loaded on demand via a new `getArticleBody(locale, slug)` that dynamically `import()`s only the locale a visitor actually reads. `[slug]/+page.svelte` fetches it reactively (`$effect`) whenever the resolved article's locale/slug changes. - `resolveArticle`/`resolveArticlesForLocale` contract, translation-notice marking, and slug/SEO/prerender behavior are unchanged. - Verified `/blog` and `/blog/[slug]` are **not prerendered** (root `+layout.ts` sets `ssr = false` / `prerender = false` outside `/home/**`; adapter-static serves them as SPA fallback) — confirmed directly by checking the built `src/main/resources/static` tree contains no `blog/**` HTML either before or after this change. So there is no static article text to lose — dynamic-loading the body cannot regress SEO here. - `articles.test.ts` extended: metadata carries no leftover `body` field, `content.en.ts`/`content.sv.ts` match their metadata siblings' slug sets 1:1, and `getArticleBody` is exercised for every `(locale, slug)` pair **derived from `articles` itself** (not hardcoded) so a future locale added without registering its content loader fails the suite instead of shipping silently. ## Measured (fresh `bun install` + `bun run build`, budgets untouched — `frontend/perf-budget.json` not edited) - `/blog`: 66.0 KB -> **38.37 KB** gz (budget 55 KB) - `/blog/[slug]`: 66.6 KB -> **39.08 KB** gz (budget 60 KB) - `node scripts/check-bundle-size.mjs --json` -> `failures: []` across all routes; entry/main measured 37.0 KB gz, matching B-269's own sandbox baseline for the same yardstick. ## Test plan - [x] `bunx vitest run src/lib/blog/articles.test.ts` — 13/13 green - [x] `bunx vitest run` (full suite) — 3188/3188 green - [x] `bunx svelte-check` — 0 errors on touched files - [x] Real production build + `check-bundle-size.mjs` — both blog routes under budget (see numbers above) - [x] Playwright against `bun run dev` (real browser, not inference): `/blog` and `/blog/reg-2019-6-amu-register-monthly-sweden` in Swedish — full translated body renders, translation notice visible, å/ä/ö intact, zero console errors - [x] Client-side navigation (no full reload) from one article to another — body correctly swaps, confirming the reactive load isn't stuck on stale content - [x] `SKIP_BACKEND=1` not needed — full `npm run check:ci` run green end-to-end including `sbt test` (3490 tests passed, 0 failed) ✅ check:ci green (frontend + backend) — full local run, all 14 steps passed, "✓ All checks passed — ok to merge." 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • ccf78b2 docs(bugs): cite PR #262 in the B-270 Atgardad heading, not the branch name Jonas · 2026-08-09 · +1 −1
  • 1cb555b test(blog): derive getArticleBody coverage from metadata, not hardcoded locales Jonas · 2026-08-09 · +10 −6
  • 0855233 fix(blog): split article body out of the synchronous bundle graph (B-270) Jonas · 2026-08-09 · +1 018 −752

#261 docs: fix-round 5 session log + regenerate /changelog

2026-08-09 · Jonas · 2 commits · 3 filer · +1 120 −52 · Funktioner

Detaljer
## Summary End-of-round bookkeeping for fix-round 5. - `other/ENLANTIS_MASTER_TODO.md` — one session entry covering the round: the 11 merged PRs (#247–#253, #255, #256, #259, #260), every named root cause, and the follow-ups. Written in the established What / Why / Next shape; no prior entries grown or rewritten. - `frontend/static/changelog/index.html` + `src/main/resources/static/changelog/index.html` — regenerated via `npm run history:build` (1744 commits, 186 PRs). History moved a lot today, and CLAUDE.md asks for a regeneration at the end of a session when it does. Note: `npm run history:fetch` got a GitHub API 404 and fell back to the checked-in PR cache, so the newest PRs' titles/bodies are not enriched in the generated page (their commits are present). Worth a look at the token/auth the fetch step uses — flagged, not fixed here. ## Verification - `node scripts/check-bug-numbers.mjs` — ✓ 262 headings, all unique and well-formed (the guard merged in #260, exercised here on the real file). - Merge-product gate had already been run on master earlier in the round: `SKIP_BACKEND=1 npm run check:ci` green, including `layout-overflow` 128/128 with the schedule route (proving #249's B-256 fix on the actual merge product), and backend verified transitively — master's `src/main/scala`, `src/test/scala`, `build.sbt` and `db-patches` were byte-identical to #251's branch, which ran `sbt test` 3490/0. - This PR is docs + generated output only; no source changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 5ce4d9f docs: fix-round 5 session log + regenerate /changelog Jonas · 2026-08-09 · +1 115 −52
  • 5333bb2 docs(todo): log fix-round 5 (PR #247-#253) + the 400-animal perf finding Jonas · 2026-08-09 · +5 −0

#260 Bug-number allocator + CI dup guard (KNOWN_BUGS.md collisions)

2026-08-09 · Jonas · 2 commits · 6 filer · +541 −32 · Buggfixar

Detaljer
## Summary Three B-NNN collisions in one day (2026-08-09) because bug numbers in `other/KNOWN_BUGS.md` were allocated by eyeballing the highest heading in whatever local snapshot of the file a branch happened to have. PR #257 currently double-books B-269/B-270 (already merged to master via #256) plus mints B-271..B-274 that nobody else has claimed yet. - **`scripts/next-bug-number.mjs`** (`bun run bug:next` / `bun run bug:next N` for a block of N): scans `origin/master`, the working tree, **every open PR's branch** (`gh pr list` + `git show origin/<branch>:...`, with a `gh pr diff` fallback for fork PRs), and all remote branches as a backstop. Prints the next free number and *which source* produced the current max, so the jump is never a mystery. Exits non-zero if it couldn't reach `origin/master` or `gh pr list` (offline / unauthenticated) — an agent scripting against it should treat that as "don't allocate off this." - **`scripts/check-bug-numbers.mjs`** (`bun run bug:check`, now a step in `check-ci.mjs`): fully offline, deterministic guard that fails CI on duplicate `### B-NNN` headings or malformed ones (`### B-` with no digits). - Building the allocator surfaced that master was **not** actually clean: it already carries one duplicate, `B-024`, used twice — an intentional pre-allocator "kontrastdelen" continuation entry for one part of an old bug, not an accidental collision. Per "report it, don't silently fix it," the heading text is untouched; it's grandfathered in `check-bug-numbers.mjs` by *exact* occurrence count, so a genuine third collision at B-024 would still fail the gate. - `CLAUDE.md`: short pointer to `bug:next`/`bug:check` near the local-CI section. - `other/KNOWN_BUGS.md`: header note pointing contributors at `bun run bug:next` instead of eyeballing the file. ## Acceptance check (the allocator seeing open PRs) PR #257's branch (`claude/testning-runda5-rapport2`) carries `B-271..B-275` on top of master's `B-270`. Current `bun run bug:next` output: ``` Per-source highest B-NNN: working tree copy: highest B-NNN = 270 origin/master: highest B-NNN = 270 PR #258 (claude/sim-perf-bigfarm): highest B-NNN = 270 PR #257 (claude/testning-runda5-rapport2): highest B-NNN = 275 PR #16 (claude/internal-chat-tool-07bmfi): no other/KNOWN_BUGS.md on that branch (skipped). Highest allocated number seen: B-275 (source: PR #257 (claude/testning-runda5-rapport2)) Next free number: B-276 ✓ origin/master and open PRs both consulted — this number is safe to allocate. ``` B-276 ≥ the required B-275 floor — confirms the allocator actually looks at open PRs rather than just origin/master. ## Guard proof — red then green Temporarily duplicated the `### B-270` heading in `other/KNOWN_BUGS.md`: ``` ✗ 1 duplicate B-NNN number(s) in other/KNOWN_BUGS.md — two different bugs are sharing one number: B-270 appears 2 times: line 11: ### B-270 — [TEST-INJECTED DUPLICATE FOR GUARD PROOF] line 13: ### B-270 — [PERF] PR #247 ... EXIT: 1 ``` Reverted, re-ran — green: ``` ✓ Bug-number guard OK — 262 B-NNN heading(s) in other/KNOWN_BUGS.md, all unique and well-formed. EXIT: 0 ``` Also verified malformed-heading detection (`### B-` with no digits) fails separately with a line-numbered report. ## Test plan - [x] `SKIP_BACKEND=1 npm run check:ci` — green except: - one pre-existing `a11y.test.ts` vitest timeout flake (`Test timed out in 5000ms`), confirmed as a flake by an isolated re-run: `bun run test -- src/lib/a11y.test.ts` → 38/38 passed in 2.27s. Unrelated to this change — no test files touched. - bundle-size gate's expected "no production build" skip (same behavior on any branch without a prior `bun run build`). - `layout-overflow guard (Playwright)` ran the full 958s and passed (no dynamic-import hiccup this run). - [x] `bug:check` passes on the real `other/KNOWN_BUGS.md` (262 headings, 1 grandfathered legacy duplicate reported, 0 new duplicates
  • 4e314be review: fix silent stale-answer bug + dedupe heading regex Jonas · 2026-08-09 · +67 −31
  • e0512dd Add bug-number allocator + CI dup guard for other/KNOWN_BUGS.md Jonas · 2026-08-09 · +474 −1

#259 perf(sim3d): A/B perf toggles + fps-counter fix (frontend split of #258)

2026-08-09 · Jonas · 2 commits · 6 filer · +598 −35 · Funktioner

Detaljer
## Summary Frontend-only split of #258 (`claude/sim-perf-bigfarm`) so Jonas can run GPU measurements on real hardware without waiting on the backend half of that PR, which has never been compiled/verified. This PR touches **zero** `src/main/scala` files. - URL-param perf toggles wired into `frontend/src/lib/scene3d/scene.ts` + `types.ts`, exposed on both `/dev/scene3d-harness` and the real `/sim/<farmId>` page: - `?noAnim=1` — skip `AnimationMixer.update()` for every actor - `?noShadow=1` — `renderer.shadowMap.enabled = false` - `?hideOffFocus=1` — hard-hide the off-focus herd (tier-C stand-in) - `?backend=webgl2` — force WebGL2 instead of the default WebGPU probe - fps-counter bug fix: the EMA was averaging the instantaneous rate (`1/dt`) instead of the period (`dt`), which systematically over-reports fps under variable frame times (root-caused in `other/SIM_PERF_400_ACTORS.md` — Jonas saw "24 fps" on real hardware while an independent rAF stopwatch measured 20.7 fps over the same window). - `window.__sceneStats()` now also returns `frameMs: {anim, hover, render, other}` — a per-subsystem timing breakdown for isolating where frame cost goes. - `other/SIM_PERF_400_ACTORS.md` (400-actor findings doc) + a short follow-up note in `other/GARDSVYN_V1_PLAN.md`. Backend half of #258 (root-admin `stress` seed scenario in `SimSeedService`/`SimRoutes`) deliberately excluded — it has never been compiled. Tracked separately; #258 stays open for it, or it gets its own PR once `sbt compile` is verified green. ## Verification - `bun run check` (svelte-check): **0 errors** (1358 pre-existing warnings, unrelated to this diff). - `bunx vitest run`: **3182/3182 passing** (1 flaky timeout on an unrelated filesystem-walk a11y test — `src/lib/a11y.test.ts`'s `aria-describedby appears wherever FieldError is used`, passes standalone in <1s; not touched by this diff). - Smoke-tested all four toggles against `/dev/scene3d-harness?actors=400` in a real Chrome tab (`window.__sceneStats()`, dev server on port 5183, software-rendered sandbox — absolute fps is not meaningful here, so state/drawCalls changes are reported instead): - **`noAnim=1`** — PROVEN. `frameMs.anim` dropped from a ~1.8–2.5ms baseline to 0.62ms. - **`noShadow=1`** — PROVEN. `textures` count dropped from 8 to 6 (shadow-map render targets removed from the WebGPU texture pool); `drawCalls`/`triangles` unchanged as expected (same geometry, just unshaded). - **`hideOffFocus=1`** — PROVEN, strongly. Visually confirmed (all off-focus pig groups vanish from every pen in a screenshot) and quantitatively: `drawCalls` 1046→325, `triangles` 208671→5829, `frameMs.render` 105.21ms→39.06ms. - **`backend=webgl2`** — PROVEN. `getStats().backend` reports `"webgl2"` (vs `"webgpu"` for every other run), and the browser console shows classic-`WebGLRenderer`-only log lines (`THREE.WebGLProgram` shader compile, `THREE.WebGLShadowMap: PCFSoftShadowMap has been deprecated. Using PCFShadowMap instead.`) that never appear on the WebGPU path. ⚠️ Not fixed here, noting for awareness: while smoke-testing, `buildWorldDto` was observed doing N+1 queries — 38.3ms for 21 actors, extrapolating to roughly ~730ms at 400 actors. Separate issue, out of scope for this split. ## Test plan - [x] `bun run check` — 0 errors - [x] `bunx vitest run` — 3182/3182 (1 flaky, unrelated, confirmed passing standalone) - [x] Manual smoke test of all 4 toggles against the dev harness, in-browser, with before/after evidence - [ ] Jonas: run GPU measurements on real hardware with `?noAnim=1` / `?noShadow=1` / `?hideOffFocus=1` / `?backend=webgl2` on `/sim/<farmId>` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 9f14581 review: dedupe debug-flag parsing, fix stale comment, caveat stress doc Jonas · 2026-08-09 · +42 −22
  • f6e20fd perf(sim3d): A/B perf toggles + fps-counter fix (split from #258) Jonas · 2026-08-09 · +556 −13

#256 fix(perf): recalibrate bundle-size budgets after vite 6->8 (B-269)

2026-08-09 · Jonas · 1 commits · 5 filer · +252 −98 · Buggfixar B-269

Detaljer
## Summary PR #244 (vite 6->8, Oxc/Rolldown) shifted three bundle-size soft budgets over their caps: - `[entry-soft] entry/main`: 37.0 KB gz > 36 KB budget - `[route-soft] /app/farms/[id]/reports/export`: 45.5 KB > 45 KB - `[route-soft] /app/release-notes`: 44.6 KB > 43 KB This was invisible for a full day because `check-bundle-size.mjs` self-skips (exit 2, "no build found") in every sandbox/worktree, which never has a production build sitting around by default. ## Drift vs leak — verdict: DRIFT Bisected `bfff60ad` (last master commit before #244 merged, own `bun install` per worktree, **no** symlinked `node_modules` — the exact trap B-256 already warns about) against current HEAD: - The entry chunk's **module composition is identical** on both sides — same Svelte 5 / Kit runtime chunks (`client`/`runtime`/`index-client`/`utils`/ `effects`/`render`/`events`/`template`/`entry`). No new npm dependency entered the synchronous entry graph. - The only addition on the #244 side is a ~0.7 KB `rolldown-runtime` interop chunk — an artifact of the esbuild/Rollup -> Oxc/Rolldown bundler swap itself, not an application-level leak. - The remaining growth (entry 31.2 -> 37.0 KB gz on **identical source**) is codegen/minification drift: Rolldown/Oxc merges what used to be 11 fine-grained runtime chunks into 7, but produces more gzipped bytes per module than esbuild's output did. - The two route regressions inherit the same shared runtime graph in their "first load" calculation. A fully controlled A/B for those two specific routes wasn't possible (`bfff60ad` is ~40 commits behind and carries unrelated feature drift in those routes too), but no new heavy module shows up in either route's graph. ## Budget diff Recalibrated via `node scripts/check-bundle-size.mjs --update` (the documented `ceil(actual * 1.15 / 5) * 5` ratchet formula in `PERF_BUDGET.md`): | Budget | Before | After | |---|---:|---:| | `entryKb` | 36 | 45 | | `/app/farms/[id]/reports/export` | 45 | 55 | | `/app/release-notes` | 43 | 55 | All three stay far under the 200/250 KB hard caps. The jump looks large relative to a rough back-of-envelope guess (~38/46/45), but it's exactly how this formula already behaves on small budgets that are genuinely over cap — see `PERF_BUDGET.md`'s own 2026-08-08 re-baseline, where the same formula gave 45 KB for a 35 KB entry. Not an anomaly. Two previously-unbudgeted routes (`/dev/scene3d-harness`, `/app/farms/[id]/gardsvyn`) also picked up budgets in the same `--update` pass — unrelated app drift since 2026-08-08, not part of the #244 regression. ## Also in this PR - `frontend/scripts/check-bundle-size.mjs` and `scripts/check-ci.mjs`: the self-skip (exit 2 / "no production build") is now loud on both paths and names this incident (B-269) plus how to actually run the gate (`bun run build` or `PERF_BUNDLE_BUILD=1`) — the whole reason this regression went unnoticed for a day. - `other/KNOWN_BUGS.md`: B-269 (this fix, full writeup) and B-270 (a separate, unrelated `/blog` + `/blog/[slug]` budget regression from PR #247's Swedish blog translations, discovered incidentally when rebasing this branch onto master — filed but deliberately **not** fixed here, out of scope for a PR about the vite bundler swap). - `frontend/PERF_BUDGET.md`: history entry for this re-baseline, matching the existing convention for prior baseline events. ## Number collision note (happened twice) Originally logged as B-263; a parallel session's PR #254 (then still open) had already minted B-263 through B-266 by the time this branch was ready, so it was renumbered to B-267/B-268 and pushed. PR #254 merged shortly after and turned out to have minted B-267 and B-268 too (same session, second sweep), so when this branch was rebased onto the new master it was renumbered again to **B-269** (fix) and **B-270** (blog finding) before this push. All references in `PERF_BUDGET.md`, the script comments, and the KNOWN_BUGS numbering fo
  • f75c260 fix(perf): recalibrate bundle-size budgets after vite 6->8 (B-269) Jonas · 2026-08-09 · +252 −98

2026-08-09 — 3 direkt-commits

Detaljer
  • 0df9afe Bug log B-263…B-268 + handoff: unstyled pages, farm-overview i18n, sim farms (#254) Jonas · 2026-08-09 · +309 −2
  • aad160c Bug log: B-260…B-262, "Öppna" cleanup, fix-round 5 handoff (#241) Jonas · 2026-08-09 · +405 −58
  • d56860c Log B-256: schedule route crashes in dev after the dependency wave Claude (AI) · 2026-08-09 · +14 −0

#255 fix(vite): strictPort 5173 + reconcile start-dev.ps1's now-dead fallback path

2026-08-09 · Jonas · 3 commits · 2 filer · +141 −70 · Funktioner

Detaljer
## Summary Lands Jonas's `strictPort: true` change (`frontend/vite.config.ts`, cherry-picked from the local `claude/vite-strict-port` branch, commit dad2d27b) and reconciles the behavioral conflict it creates with `start-dev.ps1` (the B-259 fix, PR #245). **The conflict:** `strictPort: true` is correct — Kinde only registers `http://localhost:5173` as a callback URL, and Playwright e2e hardcodes 5173 too, so a silent Vite bump to 5174 breaks login with "Invalid callback URL". But `start-dev.ps1`'s B-259 fix was built around the *opposite* assumption: when 5173 is held by a foreign process it printed "Starting ours anyway; Vite falls back to the next free port" and then scanned/waited on ports 5173-5177. Under strictPort, Vite can no longer fall back — it exits immediately on a bind failure — so the old behavior would spawn a frontend window that dies on the spot and leaves a confusing timeout after the full wait period. ## Changes - `frontend/vite.config.ts`: `server.strictPort = true` (Jonas's commit, unchanged). - `start-dev.ps1`: - Kept `Test-OwnedByThisRepo` and the "who owns 5173" ownership check unchanged — that's the actual B-259 fix (asking *who* holds the port, not just whether it's taken). - New behavior when 5173 is held by a **foreign** process: don't spawn a doomed frontend window. Name the process + PID, explain why 5173 is mandatory (Kinde callback URL + Playwright e2e), suggest `Stop-Process -Id <pid>`, and exit non-zero. Backend can still start independently. - When 5173 is free: unchanged (start as usual). - When 5173 is held by **our own** dev server: unchanged (`[SKIP] Frontend already running`). - Replaced `$FrontendPorts`/`Find-OurFrontendPort`'s 5173-5177 scan with `Test-OurFrontendRunning` (5173 only — strictPort guarantees no other port is reachable). Threaded a `$frontendBlocked` flag through the early-exit, `-NoWait`, polling, and final-report code paths so none of them can misreport a blocked frontend as "already up" (this was the main risk in the reconciliation — several of those paths previously inferred "frontend usable" purely from `-not $startFrontend`, which is no longer safe once `$startFrontend = $false` can also mean "blocked"). - Rewrote the file-header comment block to match (it previously documented the fallback as the expected behavior). - `other/KNOWN_BUGS.md`: added a note to B-259 (not reopened) — parts (a)/(b) of that fix stand as verified, part (c) (the 5173-5177 port-fallback scan) is superseded by strictPort, and the verified transcript's `Frontend listening on 5174` line is now historical (that code path no longer exists). ## Other fallback assumptions checked Grepped the repo for `5174`/`5175`/`5176`/`5177` and `strictPort`: - `frontend/scripts/check-overflow.mjs` and `frontend/scripts/bench-scene3d.mjs` each spawn their own `vite dev --port <N> --strictPort` on a self-chosen free port (5175 etc.) — CLI `--port`/`--strictPort` override the config, so `vite.config.ts`'s `strictPort` doesn't affect them. No change needed. - `frontend/e2e/playwright.config.ts`, `e2e/playwright.config.ts`, `benchmark/playwright.config.ts`, `frontend/scripts/perf-cold-start.mjs`, `frontend/audit/src/config.ts`, `frontend/audit/repro.mjs` all hardcode `http://localhost:5173` and hit an already-running dev server rather than spawning one — strictPort makes this assumption *more* correct, not less. - No other script spawns `vite dev`/`bun run dev` without an explicit `--port`. ## Verification - `powershell -NoProfile` PSParser tokenize on `start-dev.ps1` — parse-ok. - **Live-exercised the foreign-process branch** (the one that matters) against real running processes — not a synthetic test. Both 8090 and 5173 were already held by a backend + Vite instance from the main repo checkout (different `$PSScriptRoot`, so `Test-OwnedByThisRepo` correctly can't prove them as this worktree's own): ``` [SKIP] Backend already running - PID 10820 is listening on 8090. [FAIL] Port 5173 is tak
  • 79f1a08 review: dedupe frontend-blocked message, rename Get-OurFrontendPid Jonas · 2026-08-09 · +11 −5
  • 82db3bb fix(start-dev): stop scanning for Vite's port fallback now that it's gone Jonas · 2026-08-09 · +124 −65
  • fcaca6b fix(vite): strictPort 5173 so a port collision fails loudly Jonas · 2026-08-09 · +6 −0

#248 fix(i18n): close en-$t() detector loophole, key AnimalPassportCard, verify B-210

2026-08-09 · Jonas · 4 commits · 25 filer · +636 −129 · Buggfixar B-210

Detaljer
## Summary fixrunda 5 — i18n-remnants cluster (B-229 + B-210 remaining points). - **B-229, "en-`$t()` loophole" (closed):** `find-hardcoded-english.mjs`'s zero-`$t()` multi-line-aware scan (`collectZeroTHits`) skipped any file that called `$t()` at least once, falling back to the per-line scan — which has the exact same multi-line blind spot the zero-`$t()` pass exists to fix. Added a `mixedTFiles` pass that runs the same whole-file scan on files with *some* `$t()` calls, with its own noise filter (`isRealWordToken`/`looksLikeMixedPassNoise`) since this population mixes real prose with `{expr}`-stripping residue (filename templates, emails). Verified against the review's own repro (one `$t()` call + 2 hardcoded multi-line sentences → 0 hits before the fix, 2 hits after). `check-hardcoded-english.mjs` ratchets the new bucket the same way as `zeroTFiles`. Raw hit count 224 files / 954 hits → 146 files / 813 hits after the noise filter, all grandfathered into the lowered baseline (mostly marketing/legal/dev-docs, same class the rest of B-229 already leaves open). - **B-229, AnimalPassportCard timeline (partial, as scoped):** `record.recordType`/`record.complianceStatus` now keyed via `DYNAMIC_PREFIXES` (mirrors `ComplianceService.getTraceabilityHistory`'s closed enum, read but not modified — backend untouched). `record.eventDescription` stays English on purpose — backend-generated interpolated prose, same class as B-210 point 1, can't key a DB string. - **B-229, five more in-app product files fixed:** six SvelteKit redirect-stub pages → `common.redirectingToApp`; `PigKpiTile` ("prev:"/"Top X%"), `CopilotTrigger` aria-label, `ChartBar`/`ChartLine` aria-labels. `EidScannerPanel`'s "Batteri %" hit is a detector false positive (already Swedish). `PhotoTriageCard.svelte` intentionally left as-is — its own header marks it a Storybook-only design study, same exemption class as B-229's dev-sandbox carve-out. - **B-210 points 1-2 verified, not fixed:** confirmed both (`AICoPilotService`, `MultiSiteCopilotRules.metricLabel`) are backend Scala, out of this round's frontend scope (backend agent owns those files this round). No code change; documented in KNOWN_BUGS.md. - **Marketing/legal bulk translation intentionally NOT done:** B-229's own fix-direction (c) makes that its own round, and `SiteFooter.svelte`/`ProofRail.svelte`/`home/+page.svelte` were reserved for another agent this session. `other/KNOWN_BUGS.md` B-229 and B-210 entries updated with exact remaining state. ## Gate result `SKIP_BACKEND=1 npm run check:ci` — **fully green**, no known-B-256 failure observed (the schedule route's overflow check passed cleanly in this run, `960.0s`, `128 route+viewport combination(s) scanned`): ``` ✅ install frontend deps (bun install) 17.8s ✅ svelte-check (TypeScript + Svelte) 127.0s ✅ eslint (lint gate) 146.9s ✅ vitest unit tests 32.8s ✅ i18n coverage guard 1.1s ✅ hardcoded-English ratchet guard 1.4s ✅ WA boolean-attr binding guard 14.2s ✅ wa-dialog hide-handler target guard 8.2s ✅ WA variant/size hygiene guard 1.3s ✅ a11y token-contract guard (contrast) 0.3s ✅ token-drift guard 0.8s ✅ layout-overflow guard (Playwright) 960.0s ⏭ bundle-size perf budget (C-03) no production build (expected, no prior `npm run build`) ✅ UUID/column-type schema guard 3.0s ⏭ sbt test (backend) SKIP_BACKEND=1 (backend agent's scope this round) ✓ All checks passed (2 skipped) — ok to merge. ``` Pushed with `SKIP_CHECKS=1` only to bypass the pre-push hook re-running the same gate a second time (and risking a concurrent `sbt` invocation while another agent's worktree may be running one) — the gate above is this branch's own, freshly-run result, not a skip of verification itself. ## Test plan - [x] `bun run i18n:build` + `bun run
  • a7575bd chore(i18n): regenerate coverage.json after rebase onto master Jonas · 2026-08-09 · +32 −32
  • 9fd67db fix(i18n): mixedTFiles noise filter still waved through @-adjacent English sentences Jonas · 2026-08-09 · +22 −7
  • 926b578 fix(i18n): en-$t() loophole was narrowed not closed — trailing-punctuation bug in noise filter Jonas · 2026-08-09 · +44 −23
  • bb59414 fix(i18n): close en-$t() detector loophole, key AnimalPassportCard timeline, verify B-210 Jonas · 2026-08-09 · +538 −67

#253 fix(blog): correct produktionsplats -> anlaggning terminology in sv CDB article

2026-08-09 · Jonas · 1 commits · 1 filer · +1 −1 · Funktioner

Detaljer
## Summary - Fixes a terminology bug in the Swedish translation of the `sweden-ir-cdb-onboarding` blog article (`frontend/src/lib/blog/articles.sv.ts`): the HMMER-ID parenthetical used **"produktionsplatsnumret"**, which is the pre-Reg-2016/429 term. Reg 2016/429 replaced "produktionsplats" with "anläggning" (establishment) as the correct Swedish regulatory term — this is the convention documented in PR #247's fuller version of this file's header comment (`anläggning (never "produktionsplats")`); on `master`, the 3-article header this branch is based on doesn't carry that glossary prose yet, but the regulatory reasoning is the same either way. - Changed `(produktionsplatsnumret)` -> `(anläggningsnumret)` — one line, one word, surgical fix, no re-translation. - Bug was introduced in PR #216 and flagged as out-of-scope during PR #247's review: https://github.com/whiteancient/enlantis/pull/247#issuecomment-5231087407 ## Scope note (merge ordering with #247) Branched from `master` (not from #247's `claude/runda5-b218-blog-sv`), touching only the `sweden-ir-cdb-onboarding` article region, so this stays conflict-free when #247 (which adds 27 more articles to the same file) merges. **#247's branch also contains "produktionsplats" at the exact same spot** (same article, carried over unchanged from PR #216) — confirmed via: ``` git grep -ni "produktionsplats" origin/claude/runda5-b218-blog-sv -- frontend/src/lib/blog/articles.sv.ts ``` Two hits: the header-comment glossary line itself (`anläggning (never "produktionsplats")` — correct, describing the convention) and the same buggy `produktionsplatsnumret` occurrence this PR fixes. No occurrences found in the 27 *new* articles #247 adds — the term isn't reused elsewhere. Whichever of these two PRs merges second will need this same one-line fix reapplied/rebased on top of the other (trivial — single word, single line). ## Test plan - [x] `bun install` (this worktree had no `node_modules`) - [x] `git grep -i produktionsplats` on this branch's `articles.sv.ts` — zero remaining hits - [x] `bunx vitest run src/lib/blog/` — no test files exist for this path on `master` (the `articles.test.ts` suite is added by #247, not present here) - [x] `bunx tsc --noEmit -p .` — pre-existing unrelated errors in vendored `static/wa/**` and `svelte.config.js` (present on master, not touched by this change); grepped output for `articles.sv.ts`/`articles.ts` — zero errors on those files - [x] Diff verified clean: `git diff --stat` shows exactly 1 file, 1 insertion/1 deletion; no BOM, no CRLF/LF churn, UTF-8 preserved (`ä` round-trips correctly) `SKIP_CHECKS=1` was used for the push (bypassing the local pre-push `check:ci` gate) — justified given the tiny, surgical content-only diff, and that the full gate is a known ~15 min run that includes an unrelated known-failing overflow check (B-256). Not run against this branch pre-push; happy to run full `check:ci` on request before merge. ## Self-review (code-review --fix, two-axis) Ran `/code-review --fix` (standards + spec sub-agents in parallel) against `master...HEAD` before treating this as done: - **Standards:** no hard violations against repo conventions (CLAUDE.md) or the Fowler smell baseline — diff is a single-token content edit, no code structure to assess. One judgement call: the `SKIP_CHECKS=1` bypass is labelled "Emergency bypass" in CLAUDE.md; used here for a low-risk, pre-approved, draft-PR content fix rather than a genuine emergency — noted and accepted rather than reverted, since the task instructions explicitly pre-authorized this exact bypass. - **Spec:** all steps from the task brief verified as completed correctly (branch point, scope, term chosen, #247 grep, test/typecheck fallback ladder, UTF-8/BOM check, draft PR not merged). One wording nit caught and fixed in this PR body: the original phrasing implied the glossary text lives in `master`'s copy of the file header; it actually only exists in PR #247's fuller header (see Summary ab
  • b21b314 fix(blog): use anlaggning terminology in sweden-ir-cdb-onboarding sv article Jonas · 2026-08-09 · +1 −1

#247 B-218: translate remaining 27 blog articles to Swedish

2026-08-09 · Jonas · 2 commits · 4 filer · +955 −59 · Buggfixar B-218

Detaljer
## Summary - Closes the B-218 content backlog from PR #216 (fixrunda 3): all **30/30** blog articles now have a Swedish (`locale: 'sv'`) entry in `frontend/src/lib/blog/articles.sv.ts`, following the exact contract (`sourceLocale`, `translated`, `translatedBy: 'enlantis-ai'`, `translatedAt`) the first 3 established. - Translated the 27 remaining articles (7 regulation, 10 species, 10 country) via 6 parallel translation passes against a shared terminology glossary, then a consistency pass: normalized pre-Lisbon EU citations to `(EG)` in Swedish prose (EUR-Lex convention), normalized 3 titles that still had English `Dir`/`Reg` abbreviations, verified cattle/pig hoof terminology uses `klövar` (never `hovar`, reserved for horses), and verified the Reg 589/2008 egg production-method codes use the fixed official Swedish names. - Added `frontend/src/lib/blog/articles.test.ts` (7 tests) as mechanical proof that `resolveArticlesForLocale('sv')` now returns 30 `'translated'` entries and zero `'fallback'`, that all 30 slugs match 1:1 between the English and Swedish corpora with no duplicates, and that the combined `articles` array has 60 entries. - Updated header comments in `articles.ts`/`articles.sv.ts` and the B-218 entry in `other/KNOWN_BUGS.md` to reflect 30/30. ## Not done in this PR (flagged explicitly) - **Visual/browser verification of the rendered `/blog` pages is outstanding.** Claude in Chrome was reserved for another concurrent agent this session, so `/blog` and an article page were not rendered and eyeballed. The KNOWN_BUGS.md entry is marked "innehåll 30/30 klart" but explicitly **not** fully ÅTGÄRDAD pending that visual pass (per CLAUDE.md's mandatory visual-verification rule and the FAS06 self-host theme-bug precedent). - `hreflang` per language variant and native-speaker regulatory review of the translated legal citations remain out of scope (documented in KNOWN_BUGS.md, unchanged from PR #216). ## Test plan - [x] `bun install` in `frontend/` - [x] `npm run check` (svelte-check/TS) — 0 errors - [x] `bun run lint` — 0 errors (2238 pre-existing warnings, ratchet gate is errors-only) - [x] `bun run test` (vitest) — **1 file-level failure, all 5 individual test timeouts confirmed flaky under concurrent-agent system load**: re-ran each of the 5 failing tests (`a11y.test.ts`, `auth.test.ts`, `api/fetch.test.ts`, `copilot/nudges.cache.test.ts`, `stores/selectedHouseStore.test.ts`) in isolation and all passed. None touch the files this PR changes. - [x] `bun run i18n:check` — ✅ all `$t()` keys covered (no new i18n keys needed — `BlogTranslationNotice.svelte`'s notice keys already shipped in #216) - [x] `bun run i18n:hardcoded:check` — ✅ no new hardcoded-English regressions - [x] `bun run wa:check` / `wa:dialog-hide:check` / `wa:variant:check` — ✅ all pass - [x] `bun run a11y:check` (contrast) — ✅ 50/50 pairs pass - [x] `bun run tokens:check` — ✅ no token drift - [x] `bun run overflow:check` (Playwright, 128 route+viewport combos incl. `/app/farms/[id]/schedule`) — ✅ **PASS**, no B-256 hit this run - [x] `SKIP_BACKEND=1 npm run check:ci` (repo root, full local CI) — **green except the same transient vitest timeouts above**; ran the full official gate once (background) and re-verified every failing test in isolation **Overall: gate green except transient system-load test timeouts (verified non-issue via isolated re-run, unrelated files).** Backend (`sbt test`) skipped per instructions — no backend code touched. Pushed with `SKIP_CHECKS=1` to bypass the pre-push hook's own `check:ci` re-run, to avoid concurrent `sbt` contention with another agent's session on this shared machine (per `other/KNOWN_BUGS.md`-adjacent memory on sbt2 ActionCache crashes) — the full gate had already been run and verified manually as above. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 779947a docs(bugs): B-218 visually verified — mark ATGARDAD Jonas · 2026-08-09 · +26 −17
  • aeef694 feat(blog): translate remaining 27 blog articles to Swedish (B-218) Jonas · 2026-08-09 · +929 −42

#250 fix(marketing): stop outdoor-mode padding from leaking onto footer nav links (B-262)

2026-08-09 · Jonas · 2 commits · 2 filer · +121 −47 · Buggfixar B-262

Detaljer
## Summary - B-262 — footer's two-line links had their first line indented ~20px relative to the continuation line, which sat flush with the column heading. B-221's earlier fix (PR #214, `text-align: left`) never addressed this — its own status admitted the cause was never found, and `text-align` structurally cannot produce a first-line-only shift. - Root cause, measured in the browser (not guessed): `frontend/src/app.css` lines ~52–62 (`.outdoor-mode a { padding-inline: 1rem; ... }`, part of the C-07 "bigger hit-targets for barn/pasture use" outdoor-mode feature) was being applied to `.footer-grid a` because the `outdoor-mode` body class was toggled by an unguarded `$effect` in `frontend/src/routes/+layout.svelte` (~line 385) — it applied to every route, including the public marketing site, whenever the user's `enlantis.outdoorMode` preference was on. `.footer-grid a` is `display: inline`; per the CSS box model, `padding-left` on an inline element only shifts the **first** line box, matching the exact symptom. - Confirmed with `Range.getClientRects()` on the actual text node (plain `element.getClientRects()` reported misleading identical x-coordinates for both lines despite the visible offset — a real trap, cost a full investigation loop) and by toggling `outdoor-mode` off in DevTools, which removed both the padding and the indent together. ## Fix Gate the outdoor-mode body-class effect on the existing `isAppRoute` derivation (same one already used for `SiteHeader`/`SiteFooter` visibility) — outdoor mode is for barn/pasture use of the product app, not the marketing site. This is a real behavioural fix, not a CSS patch on `SiteFooter.svelte`: the padding rule stops applying to the whole marketing site, not just the footer. Known trade-off, called out in the KNOWN_BUGS.md entry: `/me/settings` (where the toggle lives) isn't under `/app/**`, so flipping it there gives no immediate visual feedback on that page — only on the next visit to `/app`. Judged correct; Settings isn't a barn/pasture surface. Not touched (same unguarded-body-class pattern, different bug): `body.big-finger` / `body.barn-mode` — flagged in KNOWN_BUGS.md and the wiki as a related, unfixed latent issue. ## Code review (`/code-review --fix`) Ran the standard two-axis review (Standards + Spec) against `master`: - **Standards:** flagged `docs/wiki/architecture/frontend.md`'s "Outdoor mode" section as now stale (described the body class as unconditional). Fixed — added a scoping note + the `/me/settings` trade-off + the flagged `big-finger`/`barn-mode` gap, plus a `docs/wiki/log.md` entry. `npm run wiki:lint` passes (82 pages, no broken links). - **Spec:** confirmed the rule was named (file + selector) in both the code comment and KNOWN_BUGS.md, `SiteFooter.svelte` was correctly left untouched (rule genuinely lives outside it), and B-221's hover-italic fix is intact. Flagged that gating the whole outdoor-mode effect reaches beyond just the footer (all marketing anchors, plus the `/me/settings` behavior change) — judged as the correct root-cause fix rather than scope creep: narrowing it to a `SiteFooter.svelte`-only CSS patch would have reintroduced exactly the "defensive fix, real cause not addressed" pattern this bug-round explicitly exists to stop (B-221 → B-262, B-190 → B-253). ## Gate — `SKIP_BACKEND=1 npm run check:ci` ✅ Green except the known blocker **B-256** (`overflow:check` fails `/app/farms/farm-1/schedule @ 390px` with `SyntaxError: Unexpected token 'export'` — documented pre-existing regression from PR #244's vite 6→8 upgrade, unrelated to this change; confirmed as the *only* failure, and confirmed reproducible/non-reproducible depending on background-process contention on this Windows box — not something this branch caused). All other 12 steps passed, including vitest (3175 tests) and svelte-check/eslint. `wiki:lint` passes separately (not part of `check:ci`). ## Visual verification Rendered `/home` at a narrow desktop viewport (~750–1156p
  • 4cbf19a docs(wiki): note outdoor-mode's new /app-only scope (code-review follow-up, B-262) Jonas · 2026-08-09 · +5 −1
  • 278be66 fix(marketing): stop outdoor-mode padding from leaking onto footer nav links (B-262) Jonas · 2026-08-09 · +116 −46

#252 fix(marketing): remove Legend eyebrow on /home + name B-260's root cause (B-261, B-260)

2026-08-09 · Jonas · 3 commits · 8 filer · +95 −51 · Buggfixar B-261 B-260

Detaljer
## Summary **B-261 (fix)** — removes the "— Teckenförklaring —" ("— Legend —") eyebrow above "Därför väljer lantbrukare Enlantis." ("Why farmers choose Enlantis") on `/home`. Internal "map legend" design vocabulary that leaked onto the customer-facing copy (same family as B-220, B-237), per Jonas's direct request. **B-260 (investigation only, no fix, per the fixrunda-5 handoff)** — names the root cause of why `ProofRail` renders empty on `/home` (the "double lines" Jonas flagged are `.rail`'s `border-top` + `border-bottom` sitting next to each other with nothing in between). ## B-261 — what changed - Removed `<p class="overline">— {$t('marketingHero.home.legend.eyebrow')} —</p>` from `frontend/src/routes/home/[[lang=locale]]/+page.svelte:317`. Heading + subheading are unchanged. - `marketingHero.home.legend.eyebrow` had exactly one call site (confirmed by repo-wide grep before removing) — different from B-260's `marketing.proof.eyebrow`, which has three and was *not* touched. Removed from the shard source of truth: `frontend/scripts/i18n/keys/marketingHero.mjs` + the matching row in `frontend/scripts/i18n/keys/_order.mjs`, then `npm run i18n:build`. - **Follow-up commit, found by `/code-review --fix`:** the shard-only removal wasn't actually enough. `build-i18n.mjs` has a documented "builds can only ADD keys, never remove them" additive-only safety net — its direct-edit preservation pass re-injects any key still sitting in `en.json`, so the key survived in `en/sv/de/fr/nl/pl.json` (the six locales with a real, non-placeholder translation for it) after the first commit. Fixed by removing it directly from those six JSON files with a small Node script matching `build-i18n.mjs`'s own writer settings (tab indent, LF, no BOM, trailing newline) — never PowerShell `Out-File`/`ConvertTo-Json`. Verified: `npm run i18n:build` is now a no-op beyond `coverage.json`'s timestamp (`totalKeys` correctly dropped 24430 → 24429, re-running touches nothing else), and `npm run i18n:check` is green (22759 keys scanned, ✅ all `$t()` keys covered). - **Note for the next key deletion** (left in the follow-up commit message too): the shard-only removal procedure doesn't fully delete a key that already has real translations checked into the locale JSONs — a key with only en/sv translations doesn't hit this trap, but one with de/fr/nl/pl overrides (like this one) does. - `other/KNOWN_BUGS.md`: B-261's heading now reads `[ÅTGÄRDAD 2026-08-09 — PR #252]` and its Status line is updated, matching the B-259 format (stays under "Öppna" with the bracket/status marking it fixed, per that same precedent — not moved to "Åtgärdade"). - Side observation, not acted on (per the handoff — Jonas said a bigger marketing overhaul comes later): `/home` has 14 eyebrows of this same `<p class="overline">— … —</p>` shape. Worth asking Jonas whether to sweep them in one pass instead of one at a time. ## B-260 — finding (no fix, per the handoff's explicit instruction) `GET /api/public/metrics` returns 200 with a valid but **wrong-shaped** payload — `PublicMetricsRoutes.scala`'s flat `MetricsDTO`: ```json {"speciesSupported":12,"countriesWithIRSync":1,"irOnTimePercent30d":65,"medianSyncMs":420,"auditsPassed":0,"asOf":"..."} ``` `ProofRail.svelte` (lines 63–64) expects `{metrics: Metric[]}` with entirely different field names (`key/label/value/suffix/prefix/source`). Neither side ever implemented the other's contract: - `json.metrics` is always `undefined` → `list = []` → `metrics = [].slice(0,5) = []`. - `{:else if metrics}` (line 98) still takes the branch because an **empty array is truthy in JS** — `{#each metrics as m, i}` iterates zero times. - `.rail` (border-top + border-bottom, lines 133–134) renders with zero `.metric` children between them — exactly the "double lines" Jonas flagged. - `failedClosed` (lines 85–86) never fires because the response is a genuine `res.ok` 200, just the wrong shape — this isn't a missing/down endpoint, it's a wire-format contract that w
  • e67f3a5 fix(i18n): actually remove the orphaned B-261 eyebrow key (code-review follow-up) Jonas · 2026-08-09 · +41 −40
  • 5bfaf37 docs(bugs): name B-260's root cause — ProofRail/PublicMetricsRoutes wire mismatch Jonas · 2026-08-09 · +47 −2
  • 265581c fix(marketing): remove Legend eyebrow above home #why section (B-261) Jonas · 2026-08-09 · +7 −9

#251 fix: B-257 quiet expected-auth-rejection logging, B-258 dedupe routes

2026-08-09 · Jonas · 2 commits · 65 filer · +694 −618 · Buggfixar B-257 B-258

Detaljer
## Summary Fixrunda 5, backend cleanup cluster — B-257 and B-258 from `other/KNOWN_BUGS.md`, plus the B-259/B-214 "Öppna" → "Åtgärdade" housekeeping move noted in `other/HANDOFF_RUNDA5.md`. ### B-257 — expected auth rejections no longer log as ERROR with a 25-frame stacktrace - `RouteHelpers.isExpectedAuthRejection` (package-private, unit-tested in `RouteHelpersLogSpec`) recognizes the same cases `handleRouteError` already maps to 401/403: `ImpersonationInvalidException`, the exact message `"User ID required"`, and messages starting with `"Forbidden"`/`"Unauthorized"`. Deliberately narrower than the generic "any Exception with a message → 400" bucket, which also covers ordinary validation errors ("Animal not found" etc.) — a different concern. - `RouteHelpers.logRouteError(context)` logs those as `scribe.warn("$context: ${msg}")` (no stacktrace); everything else keeps the original `scribe.error(context, error)` with full stacktrace. - **Response status codes are unchanged.** The bug text originally suggested `Status.Unauthorized`, but `handleRouteError` already maps `"User ID required"` to `Status.BadRequest` with its own comment calling 400 the deliberate convention, and 30+ `*RoutesAuthSpec` files assert 400 for this exact case. Flipping to 401 would have meant rewriting those specs against a documented convention for what is a logging-severity bug, not a status-code bug. Checked `frontend/src/lib/api/fetch.ts`'s 401/403 handling — it's scoped to `getActiveImpersonation()` and a specific `impersonation_invalid` body code, unaffected either way. - Applied via a scripted regex sweep (reviewed on a sample, then `sbt compile`-verified) across **61 files / 193 call sites** under `src/main/scala/com/enlantis/api/routes/` — not just the 3 named-verified files (`FarmRoutes`, `UserRoutes`, `FarmHouseRoutes`). Confirmed zero matching `scribe.error(ctx, throwable)` two-arg calls remain anywhere outside `api/routes/`. Four call sites were deliberately left alone (`CspReportRoutes` ×3, `UpgradeNudgeRoutes` ×1) — they're not auth rejections, already correctly labeled as internal errors. ### B-258 — two duplicate-route pairs, one handler in each silently dead - **`POST /shop/promote/honey-harvest/:id`**: `ShopAdminRoutes`' handler was dead *and* wrong — it required `requireOwner` (vs. the live `PromoteToShopRoutes`' `requireFarmer`) and returned a DTO with field `id`, not `productId`, which the frontend redirect (`data.productId`) needs. Had it ever been reachable, users would have landed on `/shop/products/undefined`. Deleted. - **`GET /farms/:id/reports`**: both handlers are real. `SavedReportRoutes` (the report builder, live UI at `/app/farms/[id]/reports`) wins and stays on the bare path. `FeedComplianceRoutes`' compliance-report list was re-pathed to `GET /farms/:id/compliance-reports` — real backend capability (`ComplianceService`), just with no UI consumer yet (confirmed `api.ts`'s `listReports()` wrapper has zero callers). Not `/reports/compliance`, since `SavedReportRoutes` already owns `GET /reports/:reportId` and that string would land inside it. - `RouteDuplicationSpec` (new) builds `Main.app` (visibility widened `private` → `private[enlantis]`) and asserts no two registered routes share a `(method, routePattern.render)`, plus two named regression guards for these exact pairs — turns zio-http's boot-time "Duplicate routes detected" WARN into something `sbt test` gates on every PR. - Did not add a build-time fail-fast on the zio-http warning itself — the spec above does the same job without touching zio-http internals; noted as a possible future hardening in `KNOWN_BUGS.md`. ### Housekeeping B-259 and B-214 were marked ÅTGÄRDAD but still sat under "Öppna" — moved verbatim to "Åtgärdade" per `HANDOFF_RUNDA5.md`. ## Test plan - [x] `sbt test` (PowerShell, worktree, standalone run before the full gate below) — **3490 tests passed, 0 failed, 10 ignored**. Includes the new `RouteDuplicationSpec` (all 3 cases green) and `Rout
  • 5c3931a docs(wiki): update logging section for RouteHelpers.logRouteError (B-257) Jonas · 2026-08-09 · +1 −1
  • 72a86f1 fix(routes): B-257 quiet expected-auth-rejection logging, B-258 dedupe routes Jonas · 2026-08-09 · +693 −617

#249 fix(vite): transpile Lit legacy decorators under vite 8 Oxc (B-256)

2026-08-09 · Jonas · 2 commits · 2 filer · +40 −3 · Buggfixar B-256

Detaljer
## B-256 — schedule page crash after the dependency wave (PR #244) ### Root cause (named) Vite 8's Oxc/rolldown transform — unlike esbuild in vite 6 — does **not** honor tsconfig's `experimentalDecorators: true` or `useDefineForClassFields: false`. The Lit schedule components (22 files under `frontend/src/lib/schedule/`) were emitted with **raw TC39 decorator syntax**: the served transform of `lit-schedule.ts` literally contained ```js export @customElement("lit-schedule") class LitSchedule extends BaseElement { ``` (line 103 0-based — the exact position CDP `Runtime.exceptionThrown` reported). Chromium has no native decorator support, so the module fails to parse; V8's error for `export @...` is `SyntaxError: Unexpected token 'export'`. The "loaded as classic script" theory in the bug entry was a red herring — the module WAS in module context, it was just syntactically invalid; the `Debugger.scriptParsed` sweep showed no unexpected classic scripts. **Production was broken too** (the entry warned prod was unverified): pre-fix `bun run build` succeeded silently and the shipped schedule chunk contained raw decorator syntax (`var dn=@f(...)class extends v{...}`). ### Fix (`frontend/vite.config.ts`, `oxc` block) - `decorator: { legacy: true }` — Oxc's equivalent of `experimentalDecorators: true`. - `assumptions: { setPublicClassFields: true }` + `typescript: { removeClassFieldsWithoutInitializer: true }` — Oxc's documented equivalent of `useDefineForClassFields: false`. Without these, `@property` class fields stay native [[Define]] fields that shadow the prototype accessors Lit's legacy decorators install, silently killing reactivity (verified: `EventCard.ts` emitted `lang = "";` without them, `this.lang = ""` constructor assignments with them). Restores exact esbuild/vite-6 semantics. ### Verification (Windows, main working copy, fresh `bun install`, vite 8.2.1 confirmed in node_modules) - **Repro before fix:** dedicated Playwright+CDP harness against fresh `vite dev` → `SyntaxError: Unexpected token 'export'`, `customElements.get('lit-schedule')` undefined — reproduced with **both cold and warm** `.vite` cache. (Note: `overflow:check` itself is timing-sensitive on this error — its pageerror window closes once the shell renders, so a single green gate run does not disprove the bug.) - **After fix, dev:** same harness → 0 pageerrors, `lit-schedule` defined and mounted; `OVERFLOW_ROUTES=/app/farms/farm-1/schedule bun run overflow:check` PASS with cold cache. - **After fix, prod:** `bun run build` OK; built chunk contains the legacy `__decorate` helper form (`Dn=S([f("lit-schedule")],Dn)`), no raw decorator syntax; runtime smoke against `vite preview` — dynamic import of the chunk parses, executes, and defines the element (0 pageerrors). A full logged-in prod navigation is not possible headless (the `?dev_user=` bypass is DCE'd out of prod builds); the chunk import is exactly the code path that failed. ### Gate `SKIP_BACKEND=1 npm run check:ci` run manually in-session (backend skipped: another agent owned sbt concurrently; this PR touches only `frontend/vite.config.ts` + `other/KNOWN_BUGS.md`). Push used the documented `SKIP_CHECKS=1` bypass for the same reason. - ✅ svelte-check (0 errors) · eslint · vitest · i18n guard · hardcoded-English ratchet · WA boolean-attr guard · wa-dialog hide guard · WA variant/size guard · a11y contrast · token-drift · **layout-overflow (128 route+viewport combos PASS, incl. the schedule route)** · UUID schema guard - ❌ bundle-size perf budget — **pre-existing on master, NOT caused by this PR**: rebuilding with master's unfixed vite config gives byte-identical numbers (entry 37.0>36 KB, /reports/export 45.5>45, /release-notes 44.6>43 gz). It came with #244's bundler swap and was invisible because the perf:bundle step self-skips when no production build output exists (sandbox/fresh worktrees). Needs its own KNOWN_BUGS entry — flagged separately, out of scope here. - ⏭ sbt test (backend) — SKIP_BA
  • 0a860ec docs(bugs): B-256 record gate result and pre-existing bundle-budget finding Jonas · 2026-08-09 · +1 −1
  • b8e4a4e fix(vite): transpile Lit legacy decorators under vite 8 Oxc (B-256) Jonas · 2026-08-09 · +39 −2

#246 docs(bugs): B-214's residual prod check is not Jonas's to run

2026-08-09 · Claude (AI) · 1 commits · 1 filer · +36 −9 · Buggfixar B-214

Detaljer
One-paragraph follow-up to #242, which closed B-214 as ÅTGÄRDAD with a single residual item: run two read-only queries against production to confirm migration 0254 landed there too. That item was written as though it were Jonas's to tick off. It is not — he has neither Cloud SQL access nor deploy rights and works against localhost only. Left as written it would have sat open indefinitely, pointed at someone who cannot close it. ## What changes - The residual check is **reassigned** to whoever operates production, and says so in the entry rather than implying Jonas is the blocker. 0254's author has run queries against prod before and is the natural recipient. - **Both queries are written out verbatim**, in priority order, with how to read each result — so nobody has to reconstruct them from the surrounding prose. Instance `west4`, database `enlantis_v10`, per `cloudbuild.yaml` and `application-prod.conf`. - **A zero-access alternative is added:** open the production site and read the *status code* of `GET /api/farms`. 200 means the whole chain including `existsForFarmIds` succeeded, so the column is `binary(16)` and the question is settled without touching a database. 500 is urgent. The emphasis on the status code is deliberate. An empty account and a broken one look **identical in the UI** — both show zero farms — and that exact confusion is what made B-214 take three investigations to pin down. An empty account answers 200 with `[]`; the bug answers 500. Reading the code instead of the screen removes the ambiguity that started this. - Records that **no admin endpoint exposes migration state** (checked: nothing outside `db/Migrations.scala` reads `schema_migration`), so there is no in-app route to the answer and nobody needs to go looking for one. ## Checks `npm run wiki:lint` not applicable — the change is in `other/KNOWN_BUGS.md`, which is not part of the wiki graph. No code touched, so no gate applies. Draft: this is a hand-off note about who owns a task, and the person it hands the task to is not me. Worth a glance before it lands. --- _Generated by [Claude Code](https://claude.ai/code/session_01HcHto2hVuQeeuqHSthoycc)_
  • d164b6f docs(bugs): B-214's residual prod check is not Jonas's to run Claude (AI) · 2026-08-09 · +36 −9

#245 fix(start-dev): identify who owns 5173 instead of just counting the port

2026-08-09 · Claude (AI) · 3 commits · 1 filer · +183 −22 · Buggfixar B-259 B-256

Detaljer
## The bug `start-dev.ps1` asked *"is anything listening on 5173?"* and treated yes as *"our Vite is up"*: ```powershell $frontendPid = Get-ListenerPid 5173 if ($frontendPid) { $startFrontend = $false } ``` Reported by Jonas: another project's dev server was holding 5173, so the script skipped the frontend entirely and printed ``` [SKIP] Frontend already running - PID 21432 is listening on 5173. ``` No Enlantis frontend started, and the message said one had. The backend check in the same file has always matched on the process command line rather than the port; the frontend check never got the same treatment. ## Three changes **1. Ask who owns the port.** `Test-OwnedByThisRepo` walks the listener's parent chain looking for this repo's path. `bun.exe` carries nothing useful on its own command line, but the window this script spawns is a `powershell` whose `-Command` contains the repo root followed by `\frontend`, so the marker sits on an ancestor. Depth-capped at 6 and cycle-guarded, so a mangled parent chain cannot hang the script. **2. Start ours anyway when the squatter is not ours.** Instead of skipping, name the process holding 5173 and launch the frontend regardless — Vite takes the next free port. **3. Report the port Vite actually bound.** `Frontend: http://localhost:5173` was a hardcoded constant, and the verification loop polled only 5173. A perfectly healthy frontend on 5174 was therefore reported as `[FAIL] Frontend did not reach port 5173` — while the script's own error text on the next line said *"vite falls back to 5174 if 5173 is taken"*. All of 5173–5177 are now scanned, waited on, and reported. Also guards against an empty `$PSScriptRoot` (dot-sourced or pasted rather than run as a script). Without it the ownership check compares against the empty string, which every command line contains, so every process would be identified as ours. ## Verification — run on Windows by Jonas, passed Not executed in the agent sandbox (no PowerShell there). Jonas ran it, and one run happened to cover the ownership check in **both** directions, which is the part that could not be reasoned about safely: ``` [INFO] Port 5173 is taken by node (PID 21432) - not this repo's dev server. Starting ours anyway; Vite falls back to the next free port. ... [OK] Frontend listening on 5174 [OK] Backend listening on 8090 [OK] Backend and frontend are both up Backend: http://localhost:8090 Frontend: http://localhost:5174 (not 5173 - that port was already taken by something else) ``` Line by line against the three changes: - `[INFO] …` — the check said **no** to a foreign process (change 1, negative direction). - The frontend started regardless (change 2). - `[OK] Frontend listening on 5174` — the check said **yes** to our own, found via the port scan. This is the load-bearing one: it proves the spawned window's command line really does carry the repo path where the parent-chain walk looks for it. - `Frontend: http://localhost:5174` plus the `(not 5173 …)` note (change 3). The frontend was found after one poll and the backend after fifteen, so the two are awaited independently as intended. The `[SKIP]` path (our own frontend already running) was not exercised separately. That branch is a `Write-Host` and one assignment; the function it rests on is proven above. ## Known limit A frontend started by hand (`cd frontend; bun run dev`) has no repo path anywhere in its process ancestry, so it is not recognised as ours and a second one starts beside it. Noisy but harmless: Vite takes the next port and the URL printed at the end is the real one. Documented in the file header rather than papered over — proving ownership of an arbitrary process without a marker is not something this script can do reliably. ## Also in this branch `other/KNOWN_BUGS.md`: - **B-259** logs this bug and its fix. - **B-256** (the other thread's schedule-page regression) gains an addendum: Jonas's *main* working copy was still running `VITE v6.4.
  • 143fcea docs(bugs): B-259 verified on Windows — ownership check proven both ways Claude (AI) · 2026-08-09 · +24 −5
  • 2495832 docs(bugs): note the stale node_modules trap on B-256, log B-259 Claude (AI) · 2026-08-09 · +48 −0
  • 2794ce7 fix(start-dev): identify who owns 5173 instead of just counting the port Claude (AI) · 2026-08-09 · +111 −17

#242 docs(uuid): drop migration 0256, document the real triage path instead

2026-08-09 · Claude (AI) · 5 commits · 9 filer · +1 135 −949 · Buggfixar B-214 B-257 B-258

Detaljer
**Scope reversed from what this PR originally proposed.** It opened as migration `0256`, a repair for the errno 1300 recurrence. The boot log settled the diagnosis and `0256` is now deleted rather than merged; what ships is the documentation. ## What actually happened The `Cannot convert string '\x08\xD5c…' from binary to utf8mb4` (MySQL errno 1300) came back on 2026-08-07, four days after `0254` was verified live — same column, `shop_storefront.farm_id`, via `SyncWebSocket.handleFarmSync` → `ShopStorefrontRepository.existsForFarmIds` → `WHERE farm_id IN (?, ?)`. That is the codebase's one `liftQuery … contains` site, which is why it raises where an equality predicate would silently return zero rows. The cause was mundane. `0254` had never run on the affected machine: migrations apply at backend startup, and that backend had not been restarted since the commit landed. Its next boot said so directly: ``` Migrations: discovered 191 file(s) in db-patches, 189 already recorded, firstRun=false Migrations[db-migration-0254-uuid-text-column-binary-convergence.sql]: applied Migrations[db-migration-0255-welfare-rule-sim-verbs.sql]: applied ``` `firstRun=false` is strict mode, where a failing statement aborts boot. The server went on to serve traffic, so every `ALTER` in `0254` succeeded — in roughly 8 seconds. The database is converged; nothing needs repairing. ## Why 0256 is deleted, not merged It repaired a state no database is known to be in. And it is not cheap to carry: a MySQL type change is a table rebuild (`ALGORITHM=COPY`), and the file did `MODIFY VARBINARY(36)` → `UPDATE` → `MODIFY BINARY(16)` per column — two full rebuilds each, over 172 columns in 73 tables, on **every** database that boots, including production with real rows in `animal`/`treatment`/`event_log`. Number `0256` is free again; `CLAUDE.md` is reverted to say so. ## What ships instead `docs/wiki/architecture/uuid-storage-convention.md` — the section rewritten around the lesson the incident actually taught: - **`npm run schema:check` is a static gate.** It replays the `db-patches` DDL against `Models.scala` and never opens a connection, so a green result cannot tell you a *database* is behind the files. Neither can the test suite (`FakeRepos`, in-memory). Added the `information_schema.COLUMNS` triage query to run *first* when a binary/text symptom appears, before anyone reaches for a migration. - **The lenient-baseline weakness stays documented as an open weakness**, explicitly labelled as *not* what happened here: `Migrations.applyPending` runs `lenient = firstRun`, swallows per-statement errors and records the file as applied anyway; the `lenient` column lands in `schema_migration` and nothing ever reads it. - **The two constraints any future repair script must respect** are kept, since they were expensive to work out: never `CONVERT(… USING utf8mb4)` (that function *is* what raises 1300 on raw UUID bytes, and MySQL does not guarantee `WHERE` evaluation order, so a `LENGTH(col) = 36` guard cannot be relied on to short-circuit it), and handle the 10 FK-spanning columns by hand (errno 3780 blocks the type change, `foreign_key_checks = 0` does not lift it — it skips row validation, not column-type matching — and `DROP FOREIGN KEY` has no `IF EXISTS`, so a blind re-drop aborts boot with 1091). Such a script belongs in `other/` as an operator tool, never in `db-patches/`. ## Also in this branch Two build-noise fixes from the same session, unrelated to the migration: - The 12 Scala 3.8 `Implicit parameters should be provided with a 'using' clause` warnings, all in the Regelradar repositories — the last positional `(Ord.desc)` call sites in the codebase; every other `sortBy` already wrote `(using …)`. Mechanical, no behaviour change. - Quill's compile-time query logging (`Quill Query (compiled in 24ms): SELECT …`), several hundred `[info]` lines on a full rebuild that bury the warnings above. The switch is the `quill.
  • ff5c105 docs(bugs): close B-214 — the char(36) hypothesis is confirmed live Claude (AI) · 2026-08-09 · +47 −5
  • 9df0ab9 docs(bugs): log B-257 and B-258, found while verifying migration 0254 Claude (AI) · 2026-08-09 · +67 −0
  • 69a564f docs(uuid): drop migration 0256, document the real triage path instead Claude (AI) · 2026-08-09 · +60 −931
  • 044241c chore(backend): silence Quill compile-time query logs, fix 12 Ord using-clause warnings Claude (AI) · 2026-08-09 · +22 −12
  • bf930e2 fix(db): migration 0256 — idempotent repair for UUID columns 0254 left as text Claude (AI) · 2026-08-07 · +939 −1

#244 chore(deps): dependency upgrade wave + wire the bundle-size gate

2026-08-09 · Jonas · 2 commits · 535 filer · +27 656 −5 831 · Tester

Detaljer
Brings the backend and frontend dependency trees up to current releases, plus the source changes each upgrade forced. One coherent wave rather than a drip of individual bumps. ## Backend - zio 2.1.14 → 2.1.19, zio-http 3.0.1 → 3.3.3, zio-json 0.7.3 → 0.7.43 - quill-jdbc-zio 4.8.6, mysql-socket-factory 1.28.4, google-cloud-storage 2.69.0, google-auth-library 1.48.0, pdfbox 3.0.7, bouncycastle 1.80, twelvemonkeys imageio 3.12.0, slf4j 2.0.17 - **zio-schema pinned to `VersionScheme.Always`.** zio-schema 1.7.0 is the first release to declare `early-semver`, which flips the long-standing (and benign) zio-jdbc 0.1.2 eviction *warning* into a hard error. zio-jdbc has been unmaintained since 2023 with no newer release, and we never touch its schema-derived decoders — all DB access goes through Quill. The intent is now stated explicitly instead of relying on the warning being ignored. - **zio-http 3.1 widened `Handler#apply` / `Routes#runZIO` to `ZIO[Scope & R, …]`** so the server can keep a streamed response body's resources alive for the request. The three call sites that invoke a handler *by hand* now supply that scope themselves via `ZIO.scoped`: the `Main` static-asset fallback, `MobileRoutes.serveResource`, and `HttpTestKit.run`. Safe in each case — `Handler.fromResource` returns a body that owns its own resources (the `jar:` branch opens its own `ZipFile` inside the stream), so nothing the response still needs is released with that scope. This is what churns the ~40 route specs. ## Frontend - Vite 6 → 8, which moves transforms from esbuild to Oxc/Rolldown: `esbuild.target` → `oxc.target`, and `__dirname` → `import.meta.dirname` (Vite 8 warns that `__dirname` is unsupported by the `configLoader: 'native'` mode slated to become the default). - vitest 1 → 4, ESLint 9 → 10, vite-plugin-svelte 7, SvelteKit 2.70, Svelte 5.56.8, Playwright 1.62, Web Awesome 3.6.0 → 3.11.0 (the self-hosted `dist-cdn` assets are regenerated — that's the bulk of the file count), Stripe 9, three 0.185, temporal-polyfill 1.0, sharp 0.35 (root `overrides` kept in lockstep), mjml 5.4, globals 17. - **Svelte compiler `warningFilter`** for Web Awesome custom elements. The a11y pass has a hard-coded table of known-interactive HTML elements, so every custom element is analysed as if it were a `<div>` — `<wa-button onclick={...}>` produced both `a11y_no_static_element_interactions` and `a11y_click_events_have_key_events`. With ~1800 `<wa-button>` click handlers that was ~3600 warnings, enough to bury the genuine ones. The warnings are wrong here: these tags attach their shadow root with `delegatesFocus: true` and render a real `<button>`/`<a>`/`<input>` inside, so the host is focusable and Enter/Space already work — "fixing" them would nest a button role inside the shadow button's implicit one and double-fire activation. Deliberately an **allowlist** of genuinely interactive `wa-*` tags, not a blanket `wa-*` filter: static containers (`wa-card`, `wa-callout`, `wa-details`) and plain `div`/`span` click handlers still warn. - sim3d: `DEFAULT_CONTROL_MODE` moved to its own module so `+page.svelte` can read it without statically importing `scene.ts` (and three with it). - help-kb: markdown rendering extracted to `lib/util/markdown.ts`. - vitest 4 types a bare `vi.fn()` as `Mock<Procedure | Constructable>`, no longer callable without narrowing — affected test mocks now state their signatures. ## CI `check-ci.mjs` now runs the **C-03 bundle-size budget**, which had never actually executed: `perf-budget.yml` is `workflow_dispatch`-only, *and* `check-bundle-size.mjs` looked for `_app` while the config sets `appDir: 'app_assets'`. So the budget went unenforced from the day it was committed. It reads the *last* production build rather than making one — `bun run build` costs ~1 min and rewrites ~980 tracked files under `src/main/resources/static/`, and a pre-push gate must not dirty the working tree. It self-skips (exit 2) when no build output ex
  • 7baabd9 fix(ci): make the backend gate actually run tests Jonas · 2026-08-09 · +88 −10
  • 1d23928 chore(deps): dependency upgrade wave + wire the bundle-size gate Jonas · 2026-08-09 · +27 568 −5 821

#243 Capacitor parity

2026-08-08 · Jonas · 3 commits · 15 filer · +803 −99 · Funktioner

Detaljer
  • 15baa2a /getApp Jonas · 2026-08-04 · +1 −1
  • c866db2 /getApp Jonas · 2026-08-04 · +790 −95
  • 4ffe199 idea files Jonas · 2026-08-03 · +12 −3

2026-08-07 — 1 direkt-commits

Detaljer
  • c6bbfe2 fix(marketing): one box model in the header row — "Profil" + nav alignment (B-236, 2nd pass) (#239) Jonas · 2026-08-07 · +402 −38

2026-08-06 — 17 direkt-commits

Detaljer
  • ff3055f B-255: fix the broken +/- stepper styling on mobile (#240) Jonas · 2026-08-06 · +360 −17
  • de2dbcd Code review: apply 13 verified fixes across Gårdsvyn v1 Claude (AI) · 2026-08-06 · +423 −121
  • 49c9c2c Harden welfare status maps against malformed payloads Claude (AI) · 2026-08-06 · +43 −6
  • 8697ab9 Gårdsvyn etapp 5: wiki page, session log, plan outcome Claude (AI) · 2026-08-06 · +95 −1
  • b8134d4 Gårdsvyn etapp 4: scene3d bench + tier A/B perf pass Claude (AI) · 2026-08-06 · +1 218 −95
  • ae972a8 Gårdsvyn etapp 3: embedded route, data panel, areas entry Claude (AI) · 2026-08-06 · +1 557 −31
  • e5d7a27 Lock performance budget for Gårdsvyn / shared 3D scene (#236) Jonas · 2026-08-06 · +58 −2
  • c44ce98 Gårdsvyn etapp 1: retype scene core to SceneWorld + adapters + welfare split Claude (AI) · 2026-08-06 · +575 −165
  • 7c7542a docs(bugs): sweep resolved entries out of "Öppna" into "Åtgärdade" (#235) Jonas · 2026-08-06 · +6 111 −6 188
  • 361e43d Gårdsvyn etapp 2: deterministic auto-layout adapter + test battery Claude (AI) · 2026-08-06 · +895 −0
  • dba00d0 docs: close out fix-round 4 in KNOWN_BUGS.md, re-evaluate B-214, regenerate /changelog (#233) Jonas · 2026-08-06 · +7 815 −4 325
  • 2722083 Lock route + code-sharing decision for Gårdsvyn vs /sim (#234) Jonas · 2026-08-06 · +58 −2
  • df48426 Gårdsvyn etapp 0: scene3d import boundary + SceneWorld type contract Claude (AI) · 2026-08-06 · +154 −0
  • 45e0bf2 Add Gårdsvyn v1 implementation plan Claude (AI) · 2026-08-06 · +188 −0
  • bb84ff4 Lock Gårdsvyn placement decision + approved embedded-chrome mockup (#232) Jonas · 2026-08-06 · +234 −2
  • 51d0fbc Lock auto-layout decision for the farm visualization (#231) Jonas · 2026-08-06 · +29 −1
  • 746f978 Lock simulator form-language decision + approved reference renders (#230) Jonas · 2026-08-06 · +1 076 −30

#228 B-244: camera pans to the selected animal

2026-08-05 · Claude (AI) · 2 commits · 4 filer · +555 −0 · Buggfixar B-244

Detaljer
Stacked on #225 — merge that first, then retarget/merge this. ## What Fixes B-244 (`other/KNOWN_BUGS.md`): selecting an animal in the sim's "Att göra idag" (daily-chores) list marked it with the B-193 ring but never moved the camera, so the marked animal could sit outside the visible frame — the exact scenario the ring was meant to fix. `SimScene.setSelected()` (`frontend/src/lib/sim3d/scene.ts`) now kicks off a calm camera-pan glide whenever a non-null animal is selected and already loaded in the world. ## Design - **Glide**: orbit target eases from its current position to the selected animal's current `(x, z)` over **0.65s** (inside Jonas's 0.5–0.8s "calm" window) using **ease-out-cubic**. Only the orbit target (look-at point) moves — zoom (`radius`) and orbit angles (`theta`/`phi`) are untouched, per the write-up ("behåll orbit-vinkel och zoom; flytta bara målpunkten"). - **Near-center skip**: if the animal already projects within **~15% of the viewport center** (normalized-device-coordinate radius ≤ 0.15 from center) the glide is skipped entirely — avoids jitter when re-selecting an adjacent animal in a crowded pen. - **Reduced motion**: `prefers-reduced-motion: reduce` jumps straight to the target instead of easing (same guard pattern as `lib/motion/actions.ts`). - **Drag cancels**: `attachControls()`'s `pointerdown` handler nulls the in-flight glide immediately, before any pan/orbit input is applied — a manual drag or orbit always takes control back from the camera, mid-glide or not. - **Not implemented**: continuous tracking of a wandering animal — per the KNOWN_BUGS write-up, panning to the animal's position *at click time* is the simpler, sufficient choice ("Det senare är enklare och räcker"). - Pure easing/threshold math extracted to `frontend/src/lib/sim3d/cameraGlide.ts` (`easeOutCubic`, `glideProgress`, `lerp`, `isNearViewportCenter`) so it's vitest-covered without a WebGL context — 16 new tests, all passing. `scene.ts` wires it into `stepCameraGlide()` (called every render-loop frame with real `dt`) and `startGlideTo()`/`projectToNdc()`. - Kept the glide code in clearly separated, well-commented sections (own private fields, own methods, own module) so the upcoming #226 (3D-affordance) merge — which refactors hover/selection in the same file — stays a trivial conflict. ## Gate `SKIP_BACKEND=1 bun run check:ci` from `/home/user/enlantis-w1` — **all green** (frontend-only diff; backend skipped per flag): ``` ✅ svelte-check (TypeScript + Svelte) ✅ eslint (lint gate) ✅ vitest unit tests ✅ i18n coverage guard ✅ hardcoded-English ratchet guard ✅ WA boolean-attr binding guard ✅ wa-dialog hide-handler target guard ✅ WA variant/size hygiene guard ✅ a11y token-contract guard (contrast) ✅ token-drift guard ✅ layout-overflow guard (Playwright) — 127 route+viewport combos, PASS ✅ UUID/column-type schema guard ⏭ sbt test (backend) — SKIP_BACKEND=1 ✓ All checks passed (1 skipped) — ok to merge. ``` No flake this run — overflow:check completed cleanly on the first `check:ci` invocation. ## Visual verification Dev server on port 5174; harness `other/fas06-01-genomgang/verktyg/b244-camera-pan-verify.mjs` (committed, self-contained, follows the b245-b251-b252 pattern) drives the real DOM path (expand the "Daglig tillsyn" chore row → click a `.chore-animal` button → `onPick()` → `scene.setSelected()`) against a mocked 3-pig world, screenshotting the WebGL canvas at each step. Screenshots reviewed under `/tmp/.../scratchpad/pr4-shots/` (not committed — ephemeral verification artifacts): - **Off-center select → glide to center** (`00-overview.png` → `01-far-select-*.png` → `01-far-select-99-settled.png`): the selected pig starts near the top of frame (screen pos ≈(660,300), well outside the 15% center threshold — confirmed against a Node-side replica of the exact camera math) and ends dead-centered in the settled frame. Looked at cropped before/settled pairs side by side — clear convergence. - **Adjacent near
  • b3a6d85 verify(B-244): coach the per-scenario tutorial flag in the camera-pan script Claude (AI) · 2026-08-05 · +5 −0
  • a654221 B-244: camera pans to the selected animal instead of leaving it off-screen Claude (AI) · 2026-08-05 · +550 −0

#225 Sim HUD row: camera tools out, Gårdshälsa in, hamburger separation, pan default (B-239/B-247/B-253/B-250)

2026-08-05 · Claude (AI) · 3 commits · 8 filer · +416 −89 · Buggfixar B-250 B-239 B-247 B-253

Detaljer
## What moved where Fixrunda 4, kluster 2 ("HUD-raden byggs om från tre håll") — B-239 + B-247 + B-253 + B-250, done in one pass since they all touch `frontend/src/routes/sim/[farmId]/+page.svelte`'s `.hud.top` row. - **B-239** — the rotate (⟳) / pan (✥) camera toggle moved out of the top HUD's `.controls` group into its own `.hud.camera-tools` box, bottom-center of the 3D view (`bottom: 1rem; left: 50%; transform: translateX(-50%)`). **Sound stays in the top HUD** — per Jonas's decision it isn't a camera tool and already exists as a menu item, so it wasn't moved. - **B-247** — the Gårdshälsa herd-health gauge (`.health`) moved from the sidebar `.hud.inbox` panel into the top HUD, right of Poäng. To keep it on one line at common desktop widths instead of wrapping onto its own row, the top row's horizontal gap and a few `min-width`s (`.rmid`, `.kassa`, `.poang`) and `.health`'s own width were tightened — verified empirically against real `getBoundingClientRect()` measurements at 1280px, not just eyeballed. - **B-253** — real separation between the hamburger and the clock panel. `.top`'s `left` went from `4rem` to `6rem` (an actual ~38px gap vs. the previous nominal 6.4px that B-190 measured as "no overlap" but still read as stuck-together under an 18px blur shadow), and the hamburger's own `box-shadow` blur was tightened from 18px to 8px as a second line of defense. - **B-250** — pan is now the default camera tool in both places that declare it: `+page.svelte`'s `controlMode` `$state` and `scene.ts`'s `SimScene.controlMode` field, both changed from `'rotate'` to `'pan'`. There was no `localStorage`/persistence mechanism to remove — grepped the whole frontend for `controlMode`/`sim-control` and found only the two hardcoded defaults, so "no persistence" was already true; only the defaults needed flipping. **Incidental fix caught by visual verification, not by any automated check:** moving the camera tools to bottom-center put them directly under the achievement-toast stack, which already anchored to the same `bottom: 1.25rem; left: 50%` spot. A completed-objective toast landed right on top of the rotate/pan buttons on first load. Lifted `.toasts`' `bottom` offset to `4.75rem` so it stacks clear of the new box. This is exactly the kind of thing the handoff doc's sharpened verification rule is about — no rect check would have caught two absolutely-positioned bottom-center boxes sharing a spot at different times. ## B-253 separation — visual proof Cropped screenshot of the hamburger/clock corner (not just a rect diff): - Before (B-190's fix): hamburger and clock card read as one stuck-together block under the 18px shadow blur, per Jonas's screenshot. - After: hamburger is a clearly separate white rounded square with visible page background between it and the clock card — confirmed by looking at the rendered PNG, not just measuring `gapPx = 38.4px > 25px` in the automated check. ## Verification Playwright against `bun run dev -- --port 5174` with a mocked `/api/sim/:id/{world,coverage,economy,control,act}` + `/api/farms/:id/welfare-status`, script at `other/fas06-01-genomgang/verktyg/hudrow-fixrunda4-verify.mjs` (committed). Desktop 1280x800 + narrow 480x800. Screenshots taken **and looked at** (not just measured): 1. `hud-01-desktop-full.png` — camera-tools box rendered bottom-center below the pens, sound button confirmed absent from it and still in the top HUD; Gårdshälsa visible on the same row, right of Poäng, no wrap. 2. `hud-02-desktop-rotate-selected.png` — clicking Rotera in the new box moves the active-state highlight there correctly. 3. `hud-03-desktop-reload-pan-again.png` — after a full page reload, pan is active again (no persistence survives a reload, consistent with there being no persistence mechanism to begin with). 4. `hud-04-hamburger-corner-crop.png` — cropped close-up of the hamburger vs. clock-panel corner; visible background gap between the two cards, no shadow bleed. 5. `hud-05-narrow-full.png` — 480p
  • d7136d8 review(B-250): single source for the default camera tool Claude (AI) · 2026-08-05 · +12 −5
  • 6931dc1 review(B-239): dock camera-tools bottom-right below 660px so the inspect card cannot cover it Claude (AI) · 2026-08-04 · +25 −14
  • 8c45551 Sim HUD row: camera tools out, Gårdshälsa in, hamburger separation, pan default (B-239/B-247/B-253/B-250) Claude (AI) · 2026-08-04 · +379 −70

#226 Sim 3D: hover affordance, two-tone selection ring, larger pen sign (B-252/B-245/B-251)

2026-08-05 · Claude (AI) · 2 commits · 6 filer · +637 −74 · Buggfixar B-245 B-252 B-251

Detaljer
Fix-round 4, cluster 3 (`other/HANDOFF_RUNDA4.md`) — B-252 + B-245 + B-251 in one PR because they share the 3D view and the colour choices must be coordinated. ## What changed (`frontend/src/lib/sim3d/scene.ts`) ### B-252 — hover affordance (previously: none at all) `pointermove` used to bail out immediately unless dragging — no hover raycast existed for animals or pen signs. Now built generically: anything with `animalId` or `penAreaId` in `userData` gets the pointer cursor plus a visible highlight. The canvas-level `pointermove` listener only caches the latest client-space pointer position; the actual raycast (`stepHover()`) runs **once per rendered frame** from the render loop, never per event. Suppressed while dragging so it never fights the pan-mode grab/grabbing cursor (`.canvas.pan` CSS, owned by B-116/B-250) — cursor is cleared to `''` (not a fixed value) so that CSS class resumes control. ### B-245 — selection ring contrast (previously: 1.60:1, fails WCAG) The old ring (`#ffd23f`, single tone, `opacity: 0.85`) measured 1.60:1 against the pen floor — WCAG SC 1.4.11 needs 3:1. No single colour clears 3:1 against all four ground surfaces in the bug entry's own measurements, so the ring is now **two concentric, fully opaque bands**: dark `#14110d` inner + white `#ffffff` outer (opaque, not alpha-blended — blending was part of why the old ring read worse than its nominal number). Numeric contrast (WCAG 2.1, `frontend/src/lib/sim3d/contrast.ts` + `contrast.test.ts`, computed independently and asserted in vitest): | Ground | dark `#14110d` | light `#ffffff` | passes 3:1? | |---|---|---|---| | Pen floor `#bfa878` | 8.14:1 | 2.31:1 | ✅ (dark) | | Barn floor `#8a7355` | 4.18:1 | 4.50:1 | ✅ (both) | | Outside ground `#7d8a63` | 5.10:1 | 3.69:1 | ✅ (both) | | Wall `#8f4634` | 2.77:1 | 6.79:1 | ✅ (light) | At least one tone clears 3:1 against every ground the ring can be drawn over. ### B-251 — pen sign clipped by the wall (previously: ~1/3 hidden) The plaque spanned y 0.89–1.21 against a 1.0-high wall, so about a third of it — including the status disc — sat hidden. Applied the bug entry's own worked-out proposal (~1.7×, proportions kept), pulled into `frontend/src/lib/sim3d/penSign.ts` (unit-tested in `penSign.test.ts`): | Part | Before | After | |---|---|---| | Post | `Box(0.09, 1.1, 0.09)` @ y=0.55 | `Box(0.09, 1.75, 0.09)` @ y=0.875 | | Plate | `Box(0.46, 0.32, 0.045)` @ y=1.05 (spans 0.89–1.21) | `Box(0.80, 0.55, 0.05)` @ y=1.48 (spans **1.20–1.75**) | | Disc | `Circle(0.1)` @ z=0.026 | `Circle(0.17)` @ z=0.03 | Plate now clears the 1.0 m wall by **0.20 m** — matches the bug entry's calculation exactly. ## Colour coordination (binding requirement) Three distinct visual languages, verified none use yellow (reserved for the status dot): - **Hover** = lift off the ground (+8cm animals, +6cm signs) + lighten-toward-white on the model/sign itself — no new hue, so it can never collide with any status colour. - **Selected** = the two-tone ground ring (dark/white), unrelated hue family. - **Status** = unchanged red/yellow/green dot (B-113) / disc (B-114). ## Visual verification (mandatory per the runda-4 handoff's sharpened rule) Dev server (`bun run dev -- --port 5174`) + Playwright chromium (`/opt/pw-browsers/chromium`) driving a mocked `/sim/farm-1` with 4 pigs and mixed green/yellow/red welfare-status. Harness: `other/fas06-01-genomgang/verktyg/b245-b251-b252-affordance-verify.mjs`. Screenshots taken and read (not just saved): 1. **Overview** — pen sign now stands clearly above the red wall, yellow status disc fully readable (previously ~⅓ hidden). 2. **Hover on an animal** — the hovered pig is visibly lightened/lifted vs. its neighbours; no ground ring, no status-colour collision. 3. **Hover on the pen sign** — post/plate/disc all lighten and the sign lifts; same affordance language as the animal hover. 4. **Selected animal, cropped/zoomed** — the black-inner/white-outer ring is crisp and clearly visible against the
  • cf36752 review(B-245/B-252): single source for ground colours, allocation-free hover raycast, skip at-rest signs Claude (AI) · 2026-08-05 · +60 −12
  • 128c55a Sim 3D: hover affordance, two-tone selection ring, larger pen sign (B-252/B-245/B-251) Claude (AI) · 2026-08-04 · +577 −62

#229 chore: remove the NPS feature permanently (B-254) — code only, database untouched

2026-08-05 · Claude (AI) · 2 commits · 83 filer · +323 −3 561 · Buggfixar B-254

Detaljer
## Summary B-254 — permanently remove the NPS feature (Jonas's explicit decision: removal, not a toggle-off). Code only; **the database is untouched**. ## Removal inventory (as executed) **Frontend — deleted:** - `lib/components/NpsPromptDialog.svelte`, `lib/components/NpsPromptHost.svelte` - `lib/util/npsPromptScheduler.ts` + `.test.ts` - `lib/stores/npsStore.ts` + `.test.ts` - `routes/app/admin/nps/+page.svelte` - both mount points in `routes/+layout.svelte` (the lazy-load import + the `{#if}` block) - `scripts/i18n/keys/nps.mjs` shard (its 2 keys were admin-only, unreferenced once the page was gone) + its registration in `_index.mjs` **Frontend — cleaned (not deleted):** - `lib/nav/registry.ts` — removed the `admin.nps` nav entry; `registry.map.test.ts` updated (28 → 27 admin destinations) - `lib/util/adminMetricsCatalog.ts` — comment only; `lib/data/admin-metrics-catalog.json` — removed the `npsScore` catalog entry (17 entries now), which auto-drops the NPS card from both `/app/admin/metrics-overview` and the public `/help/admin-metrics` (both are pure renders off this JSON) - `routes/app/admin/+page.svelte` — removed the NPS card + `openNpsResponses` quick-stat badge; renamed the metrics-summary card off "NPS + Time Saved Summary" - `routes/app/admin/metrics-overview/+page.svelte` — footer link relabelled off "NPS summary"; caught by visual review, the "retention" category intro text still promised "...and how strongly they would recommend us" — fixed (same for the public `/help/admin-metrics` category summary, "...and Net Promoter Score") - `routes/app/admin/metrics-summary/+page.svelte` — removed the rolling-90-day NPS + per-quarter NPS trend sections entirely; kept the time-saved section (separate survey, not in scope) - `routes/app/admin/complaints/+page.svelte` — removed `'nps'` from the *new*-complaint source picker (`SOURCES`) and its placeholder text; **kept** the `nps` → "NPS" label in `sourceLabel()` so historical `source='nps'` rows (auto-fed by the old NPS-detractor pipeline) still render correctly instead of showing a raw key — verified live with a mocked historical row (see Visual verification) - `lib/stores/timeSavedStore.ts`, `routes/help/performance-benchmarks/+page.svelte` (dropped the "NPS quarterly survey" ongoing-measurement bullet — no longer true), `routes/home/[[lang=locale]]/+layout@.svelte` — comment-only mentions - i18n: removed every dead `nps*`/`*Nps*` key from the shards *and* from the generated `en.json`/`sv.json` output directly — the build's "preserve keys added directly to JSON" safety net would otherwise keep resurrecting them on every `i18n:build` even after the shard source was clean. Renamed `adminMetricsHub.footer.linkNpsSummary` → `linkTimeSavedSummary`, added `adminHome.card.timeSavedSummary.{title,desc}`. **Backend — deleted:** - `api/routes/NpsRoutes.scala`, `api/routes/NpsPromptRoutes.scala`, `service/NpsService.scala` - `db/repository/NpsSurveyRepository.scala` (NpsSurveyInviteRepository/NpsResponseRepository/NpsUserScanRepository), `db/repository/NpsPromptDismissedRepository.scala` — not explicitly named in the B-254 inventory but orphaned once the service/routes above were gone, so deleted rather than left as dead plumbing per "don't leave components behind" - their ZLayer wiring, route mounts, and daemon workers (`npsEligibilityWorker`, `npsComplaintFeedWire`) in `Main.scala` - `NpsSurveyInviteRow`/`NpsResponseRow`/`NpsPromptDismissedRow`/`UserCreatedAtRow` row types in `Models.scala` (orphaned alongside the repos) - Test specs `NpsRoutesAuthSpec.scala`, `NpsServiceSpec.scala` **Backend — cleaned (not deleted):** - `AdminQuickStatsRoutes.scala` + `AdminQuickStatsSpec.scala` — dropped the `openNpsResponses` stat/field (quick-stats DTO now 4 fields, not 5) - `UserComplaintService.scala` — removed `'nps'` from `ValidSources` (new complaints can no longer be tagged that way) and rewrote the doc comment to past tense; kept `classifyTheme` (a general heuristic, not NPS-speci
  • ce9dff0 review(B-254): parallelize the admin time-saved farm fetches Claude (AI) · 2026-08-05 · +5 −6
  • 3b05210 chore: remove the NPS feature permanently (B-254) — code only, database untouched Claude (AI) · 2026-08-05 · +318 −3 555

#227 fix(sim): loading spinner + request dedupe for sim→animal-card, floating return button (B-248, B-249)

2026-08-05 · Claude (AI) · 2 commits · 10 filer · +800 −83 · Buggfixar B-248 B-249

Detaljer
## Summary Fixrunda 4, Kluster 4 — "In och ut ur simulatorn": B-248 + B-249. ### B-248 — "Öppna djurkort" from the sim: no loading indication + long wait **Measured before fixing** (Playwright, real SPA click via `page.mouse.click` at the button's coordinates — not a stand-in navigation — against a mocked backend with an artificial 900ms delay per animal-card endpoint so the window is observable; same script, same mocks, before vs. after): | | Requests fired | Elapsed (click → name rendered) | Loading indicator | |---|---|---|---| | **Before** | 5 — `GET /animals/:id` ×1 + `.../health-risk` ×2 + `.../profitability` ×2 | 12448ms | none | | **After** | 3 — `GET /animals/:id` ×1 + `.../health-risk` ×1 + `.../profitability` ×1 | 11154ms | spinner + "Öppnar…" | **40% fewer requests** (5→3). Elapsed time is dominated by the artificial per-request mock delay in this harness (not representative of real backend latency), so the ~10% wall-clock delta is a secondary signal — the reliable, reproducible win is the request count. Per B-169's lesson (perf fix without measurement didn't suffice), the two requests eliminated were the ones actually proven redundant, and the third cause (route-transition weight — WebGL teardown + a 1145-line page mount) was deliberately **left untouched**, per the bug's explicit "don't build all three" guidance. Fixes: 1. **Loading indicator.** The sim's "Öppna djurkort" link was a bare `<a>` — zero feedback while SvelteKit tore down the WebGL scene and navigated. `openingCardId` (keyed on animal id, not a bare boolean — see code comment for why: the inspect panel gets a fresh object reference every ~1s world-refresh tick, so a boolean effect keyed on that reference would flicker back off) now drives a spinner + "Öppnar…" on the button itself. 2. **Dedupe the double analytics fetch.** The animal-card page called `healthRiskStore.fetch()` + `profitabilityStore.fetch()` from BOTH `onMount` and a `$effect` with an identical body — four requests on first mount instead of two. `onMount` is redundant with the `$effect` (which already covers "on mount" and "on id change") and is removed; an `analyticsFetchedId` guard stops a re-run of the effect from re-firing for the same id. 3. **Fix the sim-animal store miss.** A simulated animal is *never* in the local-first Dexie `animalStore`, so the animal-card's `GET /api/animals/:id` fallback fetch is a guaranteed cache miss every time — and it only starts once the destination route has mounted, i.e. after the sim→card transition has already eaten into the wait. New `$lib/stores/animalPreloadCache.ts` lets the sim page fire that exact same request at **click time** instead, and the destination page's fallback effect consumes the in-flight result rather than firing a second, redundant request. Deliberately *not* written into `animalStore`/Dexie — that store's `sync()` replaces the whole local table, the wrong tool for stashing one ad-hoc read (see comment in the new file). ### B-249 — floating "return to simulator" button on the animal card - New fixed-position `.return-float` button, top-right, `data-testid="sim-return-float"`, positioned **below** the app shell's TopBar (56px) so it never sits over the bell/user icons already there — verified via screenshot + a bounding-box overlap check against the header controls at 480px (no overlap). - **No dismiss/close affordance**, per Jonas's explicit revision: a closable return path can be closed away and strands the user outside the simulator. It stays visible for as long as `?from=` names a known origin. - Origin is a generic `?from=<source>` → `RETURN_ORIGINS` table (currently one row: `sim`), replacing the old `if (fromSim) …` boolean — the next origin costs one table row. Reuses the existing `animalDetail.backToSim` i18n key rather than adding a duplicate. "Visualisering" is called out **only as a code comment** (future surface, no route
  • 40badf7 review(B-248/B-249): navigation-aware spinner reset, shared wa-spinner, preload TTL, token fallbacks Claude (AI) · 2026-08-05 · +43 −25
  • 6ba9038 fix(sim): add loading spinner + dedupe + preload for the sim->animal-card transition; add floating return button (B-248, B-249) Claude (AI) · 2026-08-05 · +757 −58

#224 fix(sim): close the welfare supervision loop (B-240, B-241, B-242, B-243, B-246)

2026-08-05 · Claude (AI) · 3 commits · 21 filer · +1 142 −132 · Buggfixar B-243 B-240 B-241 B-242 B-246

Detaljer
## Root-cause chain (other/HANDOFF_RUNDA4.md, Kluster 1 — "Tillsynsslingan") Player sees a status dot → cannot understand it (B-240, B-246) → acts, but buttons can be pressed unlimited times (B-243) → dot never turns green (B-241), even though the player did everything right. B-241's chain, traced in code before this PR: 1. `WelfareCheckStatusService.animalDot` is worst-of over **all** applicable `welfare_check_rule` rows for the animal's species — no `sim_visible` filter. 2. `chores.ts`'s `aggregateChores` **does** filter by `sim_visible`, so non-visible rules are hidden from the sim's chore list. 3. The sim's action buttons wrote only 4 event types. 4. `hoof_care_cattle` / `hoof_care_small_ruminant` / `hoof_care_equine` (migration 0248) were seeded `sim_visible=0`, and their shared clearing event type (`hoof_care`) had **no registered `EventType`/`EventPayload` anywhere in the codebase at all** — not just missing a sim verb, the event type itself didn't exist in the domain model. ⇒ those rules could never clear, in the sim *or* in production, but still counted toward the dot — permanently yellow/red no matter what the player did. ## Decisions applied (Jonas, binding — see HANDOFF_RUNDA4.md) - **B-241** — gave the simulator VERBS for the missing rules (not `sim_visible`-filtering of the dot). Added `EventType.HoofCare` + `EventPayload.HoofCare` (new — did not exist before this session) and a `hoof_care` sim action (`SimRoutes.playerEvent`). Migration `0255` flips `hoof_care_cattle`/`hoof_care_small_ruminant`/`hoof_care_equine` to `sim_visible=1` now that they're clearable. - `flock_supervision`/`hive_inspection` stay `sim_visible=0` — batch-level, excluded from the per-animal dot regardless (poultry/bees are actor-less batch domains in the sim seed data). - `calf_supervision`/`organic_outdoor` stay `sim_visible=0` — their lifecycle predicates never hold for sim-seeded animals (`SimSeedService.seedHerd` always seeds 3-year-old adults; sim births never set `isOrganic=true`). - Bonus fix found along the way: the `bcs` sim verb always wrote `PigBcsAssessment` regardless of species (harmless for clearing the rule, wrong in the regulatory log) — now picks the species-correct BCS event (Cow/SmallRuminant/Equine/Pig). - **B-246** — the "Att göra idag" chore card splits into **"Daglig tillsyn"** (statutory) and **"Övriga kontroller"** (practice) using the rule's `statutory` flag, never a hand-written rule-key list. `birth_window_supervision` lands in Övriga kontroller (confirmed: supervision in the same spirit, but not itself a statutory rule). ## The other three posts - **B-242** (prerequisite for B-240): `.abtns button` vertical padding trimmed 0.4rem → 0.22rem, freeing room for the due-checks list. - **B-240**: the animal-card inspect panel now renders the selected animal's non-fresh `sim_visible` rules (icon, label, legal basis, status) — reading the same `welfare-status` poll that already fed the dot/pen-sign/chore list. No new backend endpoint. - **B-243**: "done" (disabled + ✓) is derived from server rule freshness (`status === 'fresh'`), **never a local latch** — the B-195 trap. `acting` still only gates the write itself. ## Tests - Backend: `WelfareCheckStatusServiceSpec` gained a dedicated `hoof_care_small_ruminant` loop-closure suite (overdue → clears on `hoof_care` → dot goes GREEN once every rule is fresh) — this is the actual regression proof for B-241. `SimRoutesSpec` extended for the new `hoof_care` verb and species-correct `bcs` mapping. - Frontend: `chores.test.ts` extended for the `statutory` field propagation and the `hoof_care_*` rule-label mapping. ## check:ci ✅ green (frontend + backend) — all 13 steps passed, including `sbt test` (3486 backend tests) and `vitest` (3121 frontend tests). Full run captured on push via the pre-push hook. ## Visual verification Rendered the actual player flow against a mocked mixed-species world (pig/cow/sheep/horse) via Playwright (`other/fas06-01-genomgang/verkt
  • c12332c review(B-243): one derived done-map instead of 12 isActionDone() calls per render Claude (AI) · 2026-08-05 · +18 −12
  • 684762b review(B-240): share the due-checks sort with chores.ts instead of duplicating it Claude (AI) · 2026-08-04 · +26 −19
  • 6a6091f fix(sim): close the welfare supervision loop — verbs for every counted rule, card explains due checks, fresh-derived button state, statutory split (B-240, B-241, B-242, B-243, B-246) Claude (AI) · 2026-08-04 · +1 098 −101

#223 feat(sim): rebuild the onboarding into a six-step introduction (B-238)

2026-08-05 · Claude (AI) · 3 commits · 14 filer · +548 −84 · Buggfixar B-238

Detaljer
## What **B-238** — the sim onboarding was three bare do-this prompts ("Starta gården / Klicka på en gris / Väg den") in hardcoded Swedish, with no welcome, no explanation of the 3D view, and nothing about levels or money. Rebuilt into six steps in Jonas's mandated order: 1. **Welcome card** (centred modal): what the simulator *is* (an isolated 3D practice farm), what you can do in it, and *why* it exists (learn Enlantis's real workflows risk-free). Primary CTA "Starta introduktionen" + skip. 2. **The scene** (arrow at the 3D view): the 3D view is your farm — **"De röda byggnaderna är stallarna"** verbatim per the bug entry — animals walk their pens, the sign shows each pen's state. Manual "Nästa". 3. **The animals** (interactive): click any animal; the card shows its properties — weight, health, and the daily-supervision status dot. Auto-advances on a real pick. 4. **First chore** (interactive, spotlight on ⚖ Väg): the card's buttons are the daily-supervision chores; chores keep the status dot green. Auto-advances on a real weigh. 5. **Time controls** (spotlight on ▶ Kör): ▶ starts the day, ⏸ pauses, 1×/Snabb set speed. Auto-advances when time starts. 6. **Progression** (spotlight ringing the rank → cash → score HUD cluster): rank climbs with the right chores at the right time + cleared objectives; cash grows with profit (healthy animals, sell at the right weight); the score rolls it all up. "Klart — nu kör vi!" ends the tour. Mechanics: a new `info` step mode (centred coach card), manual-advance steps with their own Next button sharing the exact ✓-beat path with auto-advanced steps, and `updateSpot` now unions multiple target rects (used by step 6). Replay from the HUD menu (B-105) unchanged. **Model requirement honored:** per the binding note in the B-238 entry, this post was implemented directly by the session's main-thread model (Fable 5), not delegated to a subagent. ## Prerequisites re-verified (per the handoff) - **B-191** (auto-start): still holds — a fresh profile auto-starts the new onboarding; `sim-coached` + per-scenario flags persist on finish (asserted programmatically in the verify script). - **B-194** (bubble off-screen): still holds — every step's bubble/card is asserted fully inside the viewport at 1280×800 **and** 480×800, and the chore step's bubble visibly flips above its low target. ## i18n All copy via new `sim.tutorial.*` keys in `frontend/scripts/i18n/keys/sim.mjs` (+ `_order.mjs`), en + sv slots, regenerated `en.json`/`sv.json` — the old hardcoded-Swedish step texts (a B-229-class debt) are gone, including "Steg x / y" and "Hoppa över guiden". ## Visual verification (mandatory rule — screenshots taken AND read) New committed script `other/fas06-01-genomgang/verktyg/b238-onboarding-verify.mjs` plays the entire tour as a player against `bun run dev` + the mockApi harness (WebGPU renders headless): welcome auto-start on fresh profile → Next → real 3D animal click (probe grid) → real ⚖ Väg click → real ▶ Kör click → finish; every step asserts bubble-in-viewport; final run ALL CHECKS PASSED. I personally read the screenshots for steps 1–6 plus the 480px welcome/scene shots: welcome card with three paragraphs and both buttons; scene step with the herd and red pen visible; spotlight ring on ⚖ Väg with the bubble flipped above (B-194 flip); ring on ▶ Kör; step-6 ring spanning the whole score/rank/cash cluster with the closing CTA; mobile card fully contained. ## Gate ✅ check:ci green (frontend; backend skipped — no Scala in diff, `SKIP_BACKEND=1`): svelte-check 0 errors, eslint, vitest, i18n coverage, hardcoded-English ratchet, WA guards, a11y token contract, token drift, 127-combination layout-overflow guard, schema guard. Note: the pre-push hook's duplicate re-run was skipped (`SKIP_CHECKS=1`) because the identical tree had just completed the full gate green and two sibling fix-round gates were running concurrently on the same box — the gate summary above is from that immediately-preceding run.
  • 68f3e9e review(B-238): pause the world on tutorial replay so the time step can teach Claude (AI) · 2026-08-05 · +6 −0
  • cc2fcca review(B-238): match EN tutorial copy to actual button labels, close inspect on replay, tut-card width fix Claude (AI) · 2026-08-04 · +24 −10
  • f697415 feat(sim): rebuild the onboarding into a six-step introduction (B-238) Claude (AI) · 2026-08-04 · +518 −74

#222 fix(marketing): align Profil with header row + rename Sandlådan to Simulatorn (B-236, B-237)

2026-08-05 · Claude (AI) · 2 commits · 7 filer · +249 −11 · Buggfixar B-236 B-237

Detaljer
## What **B-236** — "Profil" (a plain text link) sat on a visibly different optical line than "Logga ut" (a `wa-button`) in the authenticated marketing header's `.actions` row (`frontend/src/lib/components/SiteHeader.svelte`). **Measured root cause** (headless Chromium, `other/fas06-01-genomgang/verktyg/b236-header-verify.mjs`, authenticated `/home` via the `?dev_user=` bypass): `.ghost` (Profil) sized its box via `padding: 0.5rem 0.85rem` + the browser's natural line-height for its font (font-dependent, measured ~40.3px tall), while `<wa-button>` sizes itself via the fixed `--wa-form-control-height` design token (height set directly, `line-height` forced to fill it — font-independent, measured 38px tall). Two different box-model formulas, centered against each other via `align-items: center` on `.actions`, only line up by coincidence for a given font stack. `getBoundingClientRect`-level glyph measurement showed Profil's text sitting measurably higher than Logga ut's even in this sandbox's fallback-serif environment, in the direction Jonas reported. Of the bug entry's two hypotheses, font-family divergence (hypothesis #1) was already mitigated by an existing `wa-button::part(base) { font-family: var(--font-display) }` override (confirmed via computed styles — both elements resolve the same font stack); the box-model mismatch (hypothesis #2) is what the measurement actually confirmed. **Fix**: give `.ghost` the same `--wa-form-control-height` / `--wa-font-size-s` tokens `wa-button size="s"` resolves to, and center its text via flex (matching `wa-button`'s own internal centering mechanism) instead of padding-driven line-height — so the two boxes are pixel-identical regardless of which font actually renders. Kept as a plain text link, not converted to a `wa-button`, per `DESIGN.md`'s CTA hierarchy (text link vs. plain-appearance button stay distinct components on purpose). **B-237** — renamed "Sandlådan" → "Simulatorn" in `sim.entry.dialog.safety` (`frontend/scripts/i18n/keys/sim.mjs`), both the `sv` slot and the `en` slot (`"The sandbox…"` → `"The simulator…"`) since this is a product-name rename, not a copy tweak — per Jonas's explicit request. `apiSandbox.*`/`loadTest.*` (a different, dev-facing feature) were left untouched. Regenerated `en.json`/`sv.json` via `npm run i18n:build`. ## Visual verification (mandatory per CLAUDE.md) Ran against `bun run dev --port 5174` with Playwright (chromium at `/opt/pw-browsers/chromium`), using the `?dev_user=` bypass + a local API mock to reach the **authenticated** marketing header state. - **Before**: screenshot + `getBoundingClientRect`/computed-style measurement of `.ghost` vs `wa-button` vs nav links vs locale picker, at 3x device-scale-factor for pixel precision. Confirmed the box-height mismatch (40.3px vs 38px) and a small but measurable, directionally-consistent text-center offset. - **After**: re-measured — `.ghost` and `wa-button` box heights are now pixel-identical (38px, same top/bottom), and glyph-center offset matches the pre-verified candidate-fix simulation. Screenshot re-taken and read, confirming "Profil" and "Logga ut" sit on the same optical line. - **/sim entry page** screenshotted (authenticated, full page) and read — safety row now reads "🔒 Simulatorn är helt isolerad: inga riktiga myndighetsanrop, ingen e-post, ingen fakturering." Surfaces looked at: authenticated `SiteHeader` `.actions` row on `/home` (before + after, both a full-header shot and a zoomed `.actions`-only shot), and the `/sim` entry page safety copy. Screenshots + measurement scripts kept as `other/fas06-01-genomgang/verktyg/b236-header-verify.mjs` and `other/fas06-01-genomgang/verktyg/b237-sim-verify.mjs`. ## Gate ✅ check:ci green (frontend; backend skipped — no Scala in diff, `SKIP_BACKEND=1`) Run twice: once directly (`SKIP_BACKEND=1 bun run check:ci`) and once via the repo's pre-push hook re-running the same gate on push — both green, including `svelte-check`, `eslint`, `vitest`, i1
  • 15290c4 review: fix verify-script shots path + null-guard debug lines Claude (AI) · 2026-08-04 · +10 −5
  • 40f51f6 fix(marketing): align Profil with header row + rename Sandlådan to Simulatorn (B-236, B-237) Claude (AI) · 2026-08-04 · +239 −6

#218 Docs: Simulator + Visualization vision (brainstorm 2026-08-02)

2026-08-04 · Claude (AI) · 1 commits · 1 filer · +167 −0 · Funktioner

Detaljer
## What Adds `other/SIMULATOR_VISUALIZATION_VISION.md` — the outcome of a brainstorm session with Jonas (2026-08-02) about the simulator's next chapter. **Documentation only, no code changes.** Implementation is explicitly not started; the next step is a follow-up brainstorm about execution. ## The vision in one paragraph Two sibling products sharing one 3D world: the **simulator** (fictional farm, engine-driven time — for marketing/demo and for developers/new hires to learn farm work) and a new **visualization** (the real farm, driven by the app's data — so farmers and developers can see what the app's data corresponds to in 3D). They must look identical; recognition is the pedagogy. ## Decisions captured - All four idea tracks are in: environment graphics, the visualization, walk mode, wiring in the dormant `SimConsequenceModel`. - **No roofs on buildings** — animals must always be directly visible. Instead the building form language gets strengthened (gable silhouettes/open trusses, Falu-red walls with white trim, tall building walls vs low pen gates, doors, floor language) so barns read as barns without explanation. - Visualization layout: auto-generated from `farm_house`/`farm_house_area` data first, farmer-placed editor later. - Surroundings: 2.5D (near environment in real 3D, distant fields as backdrop/HDRI). - End goals: full input surface (drag an animal between pens = movement event) and full timeline (event-log replay backwards, forecast forwards — eventually the sim engine running what-if on a copy of real farm data). - Scenery principle: mixed — data-driven elements (silo shows feed level) plus decoration, but decoration must never look like data. ## check:ci Not run — markdown-only addition under `other/`, no code, tests, i18n or build files touched (per approved plan; pre-push hook bypassed for the same reason). --- _Generated by [Claude Code](https://claude.ai/code/session_019bK17Cig6Xbk8peNmPUfwh)_
  • e968d46 Add simulator + visualization vision doc from brainstorm session Claude (AI) · 2026-08-02 · +167 −0

2026-08-04 — 1 direkt-commits

Detaljer
  • c12c6ad Fix-round 4 handoff + B-254 (remove the NPS feature) (#220) Jonas · 2026-08-04 · +310 −0

#221 feat(mobile): Capacitor 8 native shells to bttlgrnd parity — Android + iOS, OTA, native auth

2026-08-03 · Jonas · 2 commits · 223 filer · +10 985 −4 652 · Infra/verktyg

Detaljer
## What Ports the sibling project's mature Capacitor implementation onto Enlantis's never-exercised v6 scaffold (A-14/F-24/F-41), taking it from "scaffolded, never synced" to a working, reviewed native-app foundation. Design contract + provenance: `other/CAPACITOR_PARITY_PORT.md`. ### Scaffold (Capacitor 6 → 8.5) - Shells regenerated for `com.enlantis.app`: OAuth custom-scheme + App Links intent filters (Android), SPM project + URL types + `applinks:` entitlement (iOS), generated icons/splash from the existing SVG masters - `build:capacitor` OOM fix (missing `NODE_OPTIONS`), `cap:*` scripts wired with `VITE_API_BASE` - Web bundle verified to ship **zero Capacitor bytes** (stub aliasing preserved) ### Backend - **CORS middleware rewrite**: shell origins allowed, `Access-Control-Allow-Headers` floor + preflight echo (was entirely absent — every cross-origin `Authorization` request would have been refused), **latent PATCH bug fixed** (~20 PATCH routes were preflight-404), `Vary: Origin` on all responses, `X-Clock-Skew-Seconds` exposed - `/.well-known/` AASA + assetlinks served config-gated (explicit 404 when unset — never the SPA fallback) - `/api/mobile/ota-check` + `/api/mobile/version-gate` (anonymous, fail-safe) + streaming `/download/ota/*` (traversal-proof filename regex) and APK routes - 30 new ZIO tests across 3 specs ### Native runtime (frontend) - Global `fetch` patch (relative→`VITE_API_BASE`, covers all ~110 raw fetch sites with zero call-site changes), `window.open`→Browser patch, resume listener, universal-link router - Hand-rolled Kinde PKCE (browser SDK can't work in a webview) + `@capacitor/preferences` token store; web SDK path untouched - WS URL fix, `publicOrigin()` for printed animal-QR/Stripe-return/embed URLs, install-prompt + service-worker gating on native - OTA client: manual download/set, prompt-then-reload, `notifyAppReady` rollback watchdog, sha256-verified, same-origin-only download; non-dismissable force-update gate. All strings `$t()` (en + sv) ### Pipeline - `build-staging-apk.ps1` (signing hard-fail preflight, JDK 17–24 autodetect), OTA bundle baked in the Docker build (`git rev-list --count HEAD` = versionCode = bundle version, one scale), `CAPACITOR.md` rewrite, wiki page `mobile-capacitor.md` ## Review Adversarially reviewed by a dedicated agent; all 3 major findings fixed in this PR (native boot race that could beat the fetch patch; unauthenticated 500 via oversized Origin port; OTA integrity — empty sha256 + client trusting response-supplied origins) plus 9 minors. Landmine checklist from the reference implementation walked ✔. ## Notes for review - `frontend/bun.lock` **deleted**: it was a May-era orphan whose stale nested workspace tree broke `vite build` the moment `bun install` ran in `frontend/` (proven pre-existing by clean-worktree bisect). Root `bun.lock` is the lockfile. - `hardcoded-english-baseline.json` +1 for `annelies-gelderland`: pre-existing drift from master wip commit `4d043856` (master's ratchet was already red); this branch doesn't touch that page. - `src/main/resources/static/**` churn is the rebuilt committed deploy artifact, per repo convention. - OTA is **dark** until `MOBILE_OTA_ENABLED=true`; ship an OTA-capable APK before ever publishing bundles. - OWED manual steps (documented in `CAPACITOR.md`): Kinde native-app dashboard registration, Android keystore generation + Secret Manager backup, Apple Team ID (AASA + first macOS build — `Package.swift` has Windows path separators to regenerate), iOS git-derived `CURRENT_PROJECT_VERSION`, `/getapp` page. ## Verification ✅ check:ci green (frontend 11/11 gates + sbt, single clean run in the pre-push hook; sbt full suite 2 918 tests green post-backend-changes) ✅ `gradlew assembleDebug`: BUILD SUCCESSFUL — 27.6 MB APK with all 10 plugins ✅ `cap sync android` + `cap sync ios` clean; synced config shows `webContentsDebuggingEnabled:false` ✅ OTA/force-update dialogs visually verified in a real browser (Claude in Chrome
  • 94be5a1 fix(db): converge 74 tables' char(36) UUID columns to BINARY(16) (migration 0254) Jonas · 2026-08-03 · +1 330 −31
  • 53ee0fa feat(mobile): Capacitor 8 native shells to bttlgrnd parity — Android + iOS, OTA, native auth Jonas · 2026-08-02 · +9 655 −4 621

2026-08-02 — 1 direkt-commits

Detaljer
  • f1b3216 Bug log round 4: B-236…B-253 (#219) Jonas · 2026-08-02 · +670 −0

2026-08-01 — 11 direkt-commits

Detaljer
  • 205011a docs: fix-round 3 closeout — session log, forensic + perf reports (#217) Jonas · 2026-08-01 · +176 −12
  • de8b7ff fix(i18n): farm-shop comparison table + blog translation contract (B-228, B-218) (#216) Jonas · 2026-08-01 · +791 −134
  • ae129ae fix(copilot): nudge cache to Dexie — survives mobile sessions (B-169 follow-up) (#215) Jonas · 2026-08-01 · +490 −11
  • fc86628 fix(marketing): barn-mode table, map dots, copy, footer order, sim navbar (B-219, B-220, B-223, B-224, B-225, B-230) (#213) Jonas · 2026-08-01 · +236 −148
  • 3429a3c fix(marketing): design sweep — links, select, footer, captions (B-221, B-226, B-227, B-231, B-232) (#214) Jonas · 2026-08-01 · +141 −26
  • 2d5a836 fix(i18n): zero-$t() detector rule + product-critical files (B-229, B-235, B-210) (#212) Jonas · 2026-08-01 · +962 −146
  • 9c25796 fix(auth): hydration contract with four-state authStatus (B-222, B-216, B-217) (#211) Jonas · 2026-08-01 · +861 −76
  • 87dd6d0 fix(overflow-gate): green master, no silent client-error skips (B-233, B-234) (#210) Jonas · 2026-08-01 · +137 −24
  • 513eeb6 Bug log round 3 (B-213…B-235) + fix-round 3 handoff (#192) Jonas · 2026-08-01 · +872 −0
  • eead8d9 docs: correct CLAUDE.md CI note (all workflows manual-only) (#135) Jonas · 2026-08-01 · +1 −1
  • dae583b B-213: record decision - no data repair (no paying farms; exposure theoretical) Jonas · 2026-08-01 · +5 −0

#209 start-dev.ps1: reuse a running backend instead of spawning a doomed second sbt

2026-08-01 · Jonas · 1 commits · 1 filer · +238 −28 · Infra/verktyg

Detaljer
## What `start-dev.ps1` now looks at what is already running before it launches anything. | Situation | Before | After | |---|---|---| | Backend serving 8090 | spawned a second `sbt run` that died on the lock | `[SKIP]` + starts **only** the frontend against it | | Vite on 5173 | spawned a second one (silently landed on 5174) | `[SKIP]`, reuses it | | sbt busy, nothing on 8090 | spawned a doomed `sbt run` | no backend started; frontend started; reports backend unavailable | | `check:ci` running | no signal | warns — it ends in `sbt test` and contends for the same lock | Plus two fixes: - **Bad `JAVA_HOME` was exported unconditionally.** The child window got `JAVA_HOME`/`JAVA25_HOME`/`PATH` even when `Test-Path` had just failed and the script printed "Java not found". `build.sbt` does `ThisBuild / javaHome := sys.env.get("JAVA25_HOME").map(file)`, so a path that isn't there points sbt at a non-existent JDK instead of letting it fall back. Now only exported when the JDK exists. - **The script claimed success it never observed.** It printed `[OK] Both servers starting` unconditionally. It now polls 8090/5173 and reports what it saw. `-NoWait` skips verification; `-TimeoutSeconds` (default 360) covers cold Scala compiles. 8090 is only polled when a backend was actually launched, so a known-unavailable backend fails in seconds instead of stalling the full timeout. ## Why Only one sbt invocation can run on the machine at a time — the launcher holds `\.\pipe\sbt-load-<n>_lock`, so a second `sbt run` dies with `ServerAlreadyBootingException ... error 5`. That error landed in the spawned window while the launcher console had already printed `[OK] Both servers starting`, so a *blocked* start read as a *broken build*. This cost a morning debugging a compile that was never broken. With several agent worktrees and `check:ci` runs on the box, concurrent sbt is the normal case, not the exception. Vite proxies `/api` and `/ws` to `127.0.0.1:8090` (`vite.config.ts:107`, `:111`), so it genuinely does not matter which thread or worktree started the backend — reusing it is correct, not a workaround. ## Reviewer notes - Detection keys on java processes whose command line matches `xsbt.boot.Boot` or `com.enlantis.Main`, so an unrelated JVM on the box does not trip the guard (verified). The forked test JVM from `sbt test` does not match itself, but its launcher does and stays alive for the duration. - `-Force` restores the old always-start behaviour. **It is the one branch not exercised** — it always spawns windows, which would have polluted the other tests. ## Verification Every other branch was run for real: | Scenario | Result | |---|---| | Backend up, frontend down | skips backend, starts only frontend, exit 0 | | Backend up, frontend up | `Nothing to start`, exit 0, **0 windows spawned** (7 → 7) | | sbt busy, frontend up | `[FAIL]` + exit 1 | | sbt busy, frontend down | starts frontend, `[FAIL]` backend, exit 1 in **6 s** | | Java missing | exports no `JAVA25_HOME` at all | | Real end-to-end start | backend up on 8090 in **88 s**, exit 0; `/` → 200 | Two bugs were found and fixed during that testing: 1. sbt busy + frontend already up reported `[OK] Nothing to start - both are already up` and exit 0 despite no backend existing → now `[FAIL]` + exit 1. 2. The script waited out the full 360 s timeout for a backend it had never launched → now 6 s. ## check:ci ❌ **not fully green — see below.** Run via the pre-push hook with `SKIP_BACKEND=1`: ``` ✅ svelte-check 114.9s ✅ eslint 77.6s ✅ vitest 21.8s ✅ i18n coverage guard 1.0s ✅ hardcoded-English ratchet 0.8s ✅ WA boolean-attr binding guard 6.7s ✅ wa-dialog hide-handler guard 3.3s ✅ WA variant/size hygiene guard 1.0s ✅ a11y token-contract (contrast) 0.1s ✅ token-drift guard 0.6s ❌ layout-overflow guard (Playwright) 433.6s
  • 061d330 start-dev.ps1: reuse a running backend instead of spawning a doomed second sbt Jonas · 2026-08-01 · +238 −28

#208 Fix B-211: variant="text" sweep + guard rule

2026-08-01 · Jonas · 3 commits · 23 filer · +112 −66 · Buggfixar B-211

Detaljer
## What `variant="text"` is invalid in WA 3.6.0 and silently falls back to neutral + filled accent — a solid grey box (same mechanism as B-171's `variant="default"`). Swept all 32 sites across 21 files to `variant="neutral" appearance="plain"`, with 2 hand-classified exceptions (both genuine delete buttons → `variant="danger" appearance="plain"`). ## Guard `frontend/scripts/check-wa-variant.mjs` extended with a `variant="text"` rule (tag-aware, arrow-safe, static + dynamic ternary forms). Proof-of-red on pre-sweep code: 32 hits, exit 1 — green after: 0 hits, exit 0. ## Verification - Visual (headless Chromium vs `bun run dev` + mocked `/api`): "Tillbaka till gårdar" at 390/1200px measures transparent `::part(base)` background (was rgb(122,118,106)); glove-mode 56px floor intact; health-check delete button renders danger/plain; SiteHeader "Logga in" clean. One surface (compliance/archive delete) could not render in the mock rig (Dexie-fed list) — classification verified byte-identical to the rendered equivalent; documented in KNOWN_BUGS. - ✅ `SKIP_BACKEND=1 npm run check:ci` green (all 11 frontend steps incl. the new guard rule and the full overflow gate). - Independent two-axis review: 0 spec findings; zero `variant="text"` remain in frontend/src. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 6e3cead Fill B-211 heading PR number (#208) — review finding Jonas · 2026-08-01 · +1 −1
  • ec96e4c Amend B-211 verification notes (mock-rig surface coverage) Jonas · 2026-08-01 · +16 −5
  • 7d85a15 Fix B-211: variant="text" sweep + guard rule Jonas · 2026-08-01 · +95 −60

#207 Fix B-213: PUT /api/farms/:id overwrote FarmRow columns with defaults on every rename

2026-08-01 · Jonas · 1 commits · 3 filer · +207 −2 · Buggfixar B-213

Detaljer
## What The PUT handler rebuilt the persisted `FarmRow` from only its six leading positional constructor args, and `FarmRepository.update` performs a full-column UPDATE — so a plain name/country/currency edit **reset twelve other columns in the database** to case-class defaults, including `subscriptionTier` (billing gate), `orgId` (multi-site membership), `weiAmberDays`/`weiRedDays`, `isDemo`/`isSimulated`. ## Fix - `existingFarm.copy(name = ..., country = ..., currencyCode = ...)` — every untouched field carries over (Scala language guarantee). - Response now reuses the shared `farmToDto` helper (same as the GET paths), preserving PR #206's `hasShop` handling unchanged. ## Frontend exposure (traced) NOT exposed to Dexie/farmStore — both call sites discard the PUT response body and patch the store with only the user-edited fields (partial merge). The damage was server-side only. ## Data exposure (no self-healing) Any farm that renamed/changed currency via this endpoint since it was written has had those columns reset in the database. GET paths were always correct, so current reads show the (reset) stored values. No automated repair attempted — flagged for a human decision. ## Verification - New `FarmRoutesPutFieldsSpec`: name-only PUT round-trips `weiAmberDays`/`weiRedDays`/`isDemo`/`isSimulated`/`hasShop` with non-default stored values. **Run red against the unfixed code** (7/14/false/false observed), green after. - ✅ check:ci green (frontend + backend) via the pre-push hook. - Independent two-axis review: 0 hard violations; grep of all `FarmRow(`/`.update(` sites confirms no other positional-reconstruction hazard exists (all other update paths already use `.copy`). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 48d39ca Fix B-213: PUT /api/farms/:id overwrote 12 FarmRow columns with defaults on every rename Jonas · 2026-08-01 · +207 −2

2026-07-31 — 1 direkt-commits

Detaljer
  • d9f6190 Session log: fix round 2 complete (PR #181-#206); compress older MASTER_TODO entries; regenerate /changelog Jonas · 2026-07-31 · +1 440 −92

#206 Fix B-203/B-164: real hasShop feature flag, consolidate FCI pages

2026-07-31 · Jonas · 2 commits · 16 filer · +625 −94 · Buggfixar B-203 B-164

Detaljer
## Summary **B-203** — `FarmDTO.hasShop` is now a real field, derived server-side from `shop_storefront` row **existence** (no migration needed — `farm_id` is already the table's PK, migration 0065). Backend: - `FarmDTO.hasShop: Boolean = false` (`api/dto/DTOs.scala`). - New `ShopStorefrontRepository.existsForFarmIds(farmIds)` — batched `farm_id IN (...)` lookup so `GET /api/farms` (list) doesn't do an N+1 per farm; default trait impl (loop over `get`) keeps existing test fakes compiling. - Threaded through all `FarmDTO` construction sites: `GET /api/farms`, `GET /api/farms/:id`, `POST /api/farms` (defaults `false` for a brand-new farm), `PUT /api/farms/:id` (re-looks-up so an unrelated edit doesn't reset the flag) — plus `SyncWebSocket.scala`'s `farm sync_all` handler, which had the exact same DTO-construction gap and would otherwise have always sent `hasShop:false` regardless of what REST returned. - `existsForFarmIds` counts a storefront row regardless of its `enabled` flag — same "existence is the signal, not `enabled`" rule B-198 already established for `ShopTodayCard`. Frontend: - `farmFeaturesStore.ts` now reads `hasShop`/`has_shop` directly and resolves `'shop'` from it *before* the permissive fallback — and excludes that field from the fallback's "does this payload carry any flags at all" probe. Without that exclusion, the moment `hasShop` always-ships, the probe would flip to "flags present" for every farm and every *other* feature (species flags, `organic`, `csa` — none of which the backend sends yet) would silently go from permissive to hidden. Verified with a new `farmFeaturesStore.test.ts`. - `frontend/src/routes/app/farms/[id]/+page.svelte`: removed the point-probe against `/api/farms/:id/shop/storefront` (B-198's interim fix, written specifically because this field didn't exist yet) and replaced it with `$farmFeaturesStore.has('shop')` — the single mechanism, as B-203 originally envisioned. The manual `$effect` reset-on-farm-switch is no longer needed either: `farmFeaturesStore` is `derived` off `selectedFarm`, which the root `+layout.svelte` already keeps synced to the route's farm id on every navigation. - Net effect: B-160's `features: ['shop']` gates (`farm.links`, `farm.crm`, the whole `farm.shop.*` subtree — 15+ nodes) are now actually enforced, not a no-op. **B-164** — turned out to already be fixed in code (redirect stub at `movements/slaughter/fci`, hidden nav node, slaughter-list button linking straight to `/compliance/fci`) since 2026-07-28 (commits `a82200f9` / `dc9d7740`, PR #168 family). Only `other/KNOWN_BUGS.md`'s status marker was stale — the docs-only commit that recorded the fix (`dbbebae8`) was on a branch that never made it to `master`. Added a `registry.test.ts` regression case pinning `farm.movements.slaughter.fci` as `hidden` so a future refactor can't silently un-hide it, and corrected the bug-log entry. ## check:ci ✅ check:ci green (frontend + backend) — ran via the pre-push hook (`.githooks/pre-push`), all 11 steps: svelte-check, eslint, vitest, i18n coverage, hardcoded-English ratchet, WA boolean-attr guard, wa-dialog hide-handler guard, a11y contrast, token-drift, layout-overflow (Playwright), and `sbt test` (in-memory FakeRepos). ## Test plan - [x] Backend: `FarmRoutesShopFlagSpec` (new) — `hasShop` true/false, true-even-when-disabled (B-198 semantics), batched list-endpoint correctness across multiple farms, PUT preserves the flag across an unrelated update. - [x] Frontend: `farmFeaturesStore.test.ts` (new) — `shop` excluded from permissive fallback without affecting other features; snake_case/camelCase; absent field behaves like `false`. - [x] `registry.test.ts` — new case pinning `farm.movements.slaughter.fci` as hidden. - [x] `npm run wiki:lint` — clean. - [x] Visual (Claude in Chrome, `bun run dev` + `sbt run` against local MySQL): created a fresh farm via the real `POST /api/farms` endpoint, confirmed the live JSON response carries `"hasShop":false`; loaded `/app/farms
  • d234547 Fix B-203 legacy-cache regression + review touch-ups Jonas · 2026-07-31 · +113 −21
  • 316b037 Fix B-203/B-164: real hasShop feature flag, consolidate FCI pages Jonas · 2026-07-31 · +512 −73

#205 Fix B-212: KbArticleBody effect loop broke help-article loading

2026-07-31 · Jonas · 2 commits · 2 filer · +67 −15 · Buggfixar B-212

Detaljer
## Summary - Fixes B-212: `KbArticleBody.svelte`'s `$effect` was self-triggering forever, so `/help/kb/<slug>` and `/app/help/<slug>` never got past "Laddar…"/"Loading…". - Root cause: the effect wrote `meta` (`$state<HelpArticleMeta | null>`) and then read it back synchronously in the same run (`meta = resolveMeta(s, lo); if (!meta) {...}`). Svelte 5 deep-proxies objects assigned to `$state`, so the freshly-written value is never referentially equal to the previous read — the equality bail-out that normally stops an effect from re-triggering on its own write never fired. The effect became self-dependent and re-ran itself until Svelte's `effect_update_depth_exceeded` ceiling (1000 runs) stopped it mid-run, leaving `loading` stuck `true` forever. Confirmed in-browser with a temporary run counter: **1001 effect executions per page load before the fix, 1 after** (2 after a genuine slug/locale change). - Fix: `meta` is now a pure `$derived(resolveMeta(slug, activeLocale))` — `resolveMeta` already is a pure function of those two inputs, so there's no reason to duplicate it as effect-written state. The effect now only *reads* `slug`/`activeLocale`/`meta` and only *writes* `loading`/`html`/`fetchFailed`/`usedLocale` — no overlap, no self-dependency. - Added a `cancelled` cleanup guard in the effect so a slower, stale `loadArticleBody` resolution can't clobber a newer one on fast slug/locale navigation (previously masked entirely by the infinite loop never settling). - The `basePath` href-rewrite (B-186/PR #199, `.replaceAll('href="/help/kb', ...)`) is untouched and verified intact (see verification below). ## Root cause detail (exact looping dependency) ``` $effect(() => { meta = resolveMeta(s, lo); // WRITE to $state<object> if (!meta) { ... } // READ of the same $state, same execution ... }); ``` `$state` on an object wraps the assigned value in a new reactive proxy each write. The `if (!meta)` read registers `meta` as a tracked dependency of the effect. Because the write is never referentially `Object.is`-equal to what was last read (proxy wrap), the dependency is always considered "changed," so the effect re-schedules itself — forever, until Svelte's depth-exceeded guard kills it after 1000 runs. ## Housekeeping - `other/KNOWN_BUGS.md`: added the B-212 entry (number was pre-assigned by a concurrent B-171 sweep-agent whose own KNOWN_BUGS entry hadn't landed on `master` at the time this branch started — `master` tip only had up to B-210. Whoever merges resolves any numbering collision against the sweep agent's own entry.) - No vitest regression test added: the loop lived in Svelte 5 reactivity wiring (an effect's self-dependency via `$state` object identity), not in extractable pure logic — `resolveMeta` itself is already referentially stable and isn't what was broken. The repo has no `@testing-library/svelte` set up for mounting Svelte components in vitest, so a component-level regression test isn't feasible without new test infra. Flagged for the reviewer rather than added speculatively. ## Verification (Claude in Chrome, headless-equivalent, dev server + live backend) - `/help/kb/pregnancy-scan?locale=en` (public route): loads and renders full article body (was previously stuck on "Laddar…"/"Loading…" forever). Screenshot taken and read. - `/app/help/pregnancy-scan?locale=en` (in-app route, via `?dev_user=` bypass against a live `sbt run` backend): loads and renders full article body inside the app shell. Screenshot taken and read. - Temporary `window.__diagEffectRuns` counter (removed before commit — `grep -n __diag` on the final file returns nothing): **1 effect run** on fresh load of either route. - SPA-level locale switch (injected `<a href="?locale=sv">`, clicked — no full page reload): counter went **1 → 2**, article body re-rendered correctly in Swedish. Confirms "reloads exactly once" on locale change, not a full-navigation artifact. - In-article link click from `/app/help/pregnancy-scan` (a rea
  • 719e667 KNOWN_BUGS: B-212 heading tag to house format (review finding) Jonas · 2026-07-31 · +1 −1
  • 8154d39 Fix B-212: KbArticleBody effect loop broke help-article loading Jonas · 2026-07-31 · +66 −14

#204 Fix B-171: CTA-hierarchy sweep, long-form WA sizes, wa-variant lint guard, help-kb token conversion

2026-07-31 · Jonas · 2 commits · 189 filer · +881 −614 · Buggfixar B-171

Detaljer
## Summary - **CTA hierarchy sweep (B-171):** `variant="default"` is not a real Web Awesome variant — WA silently falls back to its own neutral+filled default, rendering a solid grey box that reads as disabled and has no place in `frontend/DESIGN.md`'s CTA hierarchy. Swept **472 sites** (349 static `variant="default"` + 123 dynamic filter-toggle ternaries like `cond ? 'primary' : 'default'`) across **184 files** to the DESIGN.md secondary treatment (`variant="neutral" appearance="outlined"`), matching the incidents-admin reference implementation (post-#186). 7 sites were hand-classified against the mechanical rule per context: 4 dense-table delete icons → `variant="danger" appearance="plain"`, 1 labeled discard action → `variant="danger" appearance="outlined"`, 1 tertiary row action → `appearance="plain"` only, and the goats page's three-way status ternary got a mirrored `appearance` ternary so only the "ok" branch is outlined. - **Long-form WA sizes:** `size="small"/"medium"/"large"` → `"s"/"m"/"l"` on the 12 genuine WA sites (4 files). Note: **`size="s"` is canonical here** (PR #181) — the original B-171 report's claim about `size="s"` was inverted; corrected in `KNOWN_BUGS.md`. - **New CI guard:** `frontend/scripts/check-wa-variant.mjs` (`npm run wa:variant:check`), wired into `check:ci` and `.github/workflows/ci.yml`. Tag-aware and `=>`-safe (arrow functions inside attribute expressions don't truncate tag matching). Flags new `variant="default"` (static + dynamic) on `<wa-button>` and long-form `size=` on any `<wa-*>` element. **Proof-of-red** captured against pre-sweep code (484 violations, exit 1); zero-entry allowlist after the sweep (green). - **Help-kb token conversion (PR #199 follow-up):** ~93 hardcoded hex values + non-token `border-radius` across `Kb{Landing,Article,Category,Search}Body.svelte` (moved verbatim from the old public pages) converted to `--color-*`/`--radius-*` tokens, matching the already-clean `KbLocaleSwitch.svelte`. 2 dead CSS rules removed (`.kb-search input/button`, `.kb-feedback button/textarea` — markup uses `wa-input`/`wa-button`/`wa-textarea`, not native elements). One deliberate exception: the code-block dark "terminal" surface (`#0f172a`/`#e2e8f0`) has no DESIGN.md token and is left as literal hex with a comment rather than inventing an unreviewed global token. ## Out of scope (logged, not fixed here) Two adjacent bugs surfaced during mandatory visual QA and were deliberately left out to keep this sweep scoped, each filed in `other/KNOWN_BUGS.md` and flagged as a follow-up task: - **B-211** — `variant="text"` has the *identical* grey-fallback bug as B-171 (32 sites, 21 files), confirmed via computed `::part(base)` styles in a live browser. - **B-212** — `/app/help/<slug>` articles get stuck on "Loading…" with a Svelte `effect_update_depth_exceeded` infinite loop in `KbArticleBody.svelte`. Confirmed **pre-existing and unrelated to this PR** by `git stash`-ing this PR's help-kb changes and reproducing the identical crash on unmodified master. ## Verification - `npm run check` (svelte-check): 0 errors - `npm run lint` (eslint): 0 errors - `npm test` (vitest): 2996/2996 passed - `npm run i18n:check`, `wa:check`, `wa:dialog-hide:check`, `i18n:hardcoded:check`, `tokens:check`: all green - `npm run a11y:check`: 50/50 contrast pairs pass (token conversion did not break any contract pair) - `npm run overflow:check` (Playwright, 127 route×viewport combos incl. glove/big-finger mode at 390px, real dialogs opened): PASS - `wa:variant:check`: proof-of-red on pre-sweep code (484 violations) → green post-sweep (0) - **Full gate:** `SKIP_BACKEND=1 npm run check:ci` — ✅ green (frontend); backend skipped per flag - **Visual verification (live browser, real dev backend, real WA custom elements):** form dialog footer (vaccination-schedule dialog: outlined "Avbryt" + filled "Spara", correct `Individ`/`Besättning` toggle), a nav-card-heavy farm dashboard (equal-height widget grid), the incidents admin pag
  • 34b0827 code-review fix: document the deliberate green/blue -> --color-primary token collapse in help-kb Jonas · 2026-07-31 · +23 −0
  • 5faef55 Fix B-171: CTA-hierarchy sweep, long-form WA sizes, wa-variant lint guard, help-kb token conversion Jonas · 2026-07-31 · +858 −614

#203 Sim: B-111 zone legibility, B-113 status dots, B-114 pen status signs

2026-07-31 · Jonas · 2 commits · 11 filer · +663 −76 · Buggfixar B-111 B-113 B-114

Detaljer
## Summary - **B-111** (zone legibility): the pen floor never fills the outer building rectangle *by design* (`SimSeedService` seeds a building envelope and a smaller fenced pen as separate rectangles), but the gap between them showed bare grass and read as a scaling bug. Adds a dedicated dirt-tone floor under each building's full footprint (`scene.ts`) so the two zones read as intentional layers, plus a new `sim.legend.*` HUD modal (☰ menu → "❔ Förklara hagen") spelling out both zones and the status-colour vocabulary. - **B-113** (per-animal status dot): a billboard sprite dot (green/yellow/red, one shared canvas texture tinted per animal) now floats above every animal, sourced from the same `GET /welfare-status` poll that already feeds the sim-chores HUD card. Height is derived per-species from `models.ts`'s `adultHeightFor`. Deliberately distinct in shape/height from the B-193 ground selection ring per that PR's own coexistence design note — both render together on a selected, dotted animal. - **B-114** (per-pen status sign): a clickable signpost per pen (raycast against a new `signGroup`, separate from the animal raycast) opens a summary modal with green/yellow/red/withdrawal counts + capacity, independent of the animal inspect panel so the two never conflict. `chores.ts`'s `FarmWelfareStatus.pens` gains a real `PenStatusWire` type (previously `unknown[]`, unused) to carry the per-pen aggregate to both the scene and the new modal. New `sim.legend.*` / `sim.pen.*` / `sim.hud.legend` i18n keys (en+sv filled, other locales machine-placeholder per repo convention). ## Visual verification (live Grisbox sim world) - **1200px**: two-tone floor (dark barn floor filling the building envelope, lighter pen floor + fence inside it) — no more bare-grass gap. Every pig has a red status dot (fresh farm, no controls done yet); pen sign at the pen's front-left corner also reads red, matching the aggregate. - Selected an animal with a status dot: the gold B-193 ground ring and the red B-113 dot are both visible at once, distinct in shape and height (zoomed screenshot confirmed). - Clicked the pen sign → "Hagens status / Box A / 0 av 10 djur fullt kontrollerade" modal opened, **while the animal inspect panel stayed open** — confirmed independent, non-conflicting. - Performed two hands-on actions in a row (health check, then weigh) on the same animal — both completed ("Hälsokontroll registrerad" / "Vägning registrerad"), the chore count and the animal's dot updated live (red → yellow after the first clearing rule), B-195 regression confirmed clean. - **390px**: verified via an injected same-origin iframe (the real desktop Chrome window here is shared across concurrent agent sessions and wouldn't resize below its current size) — the two-tone zones and status dots remain visible on the canvas; the new legend and pen-summary modals (both `width: min(380px, 92vw)`) render fully legible with no horizontal overflow. The pre-existing desktop-oriented HUD chrome (objectives/chores panels) is unrelated pre-existing behaviour, not a regression from this change. ## Gate `SKIP_BACKEND=1 npm run check:ci` — ✅ green (frontend-only change; backend untouched, all 10 non-skipped steps passed, `sbt test` skipped). ## Test plan - [x] Zones legible at 1200px and 390px - [x] Status dots visible + distinct from B-193 selection ring, both visible together on selected animal - [x] Pen sign click opens summary modal, independent of animal inspect panel - [x] Animal click still works (picking unaffected by new sign raycast) - [x] Two hands-on actions in a row still work (B-195 regression) - [x] `SKIP_BACKEND=1 npm run check:ci` green 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 2b76b5b Review fixes: unify WelfareColor type, token-aligned status colors, spawn-gap fix, explicit modal z-index Jonas · 2026-07-31 · +80 −34
  • ad14930 Sim: B-111 zone legibility, B-113 status dots, B-114 pen status signs Jonas · 2026-07-31 · +583 −42

#202 Fix B-172/B-174/B-180: i18n keys+params contract for copilot, component i18n, hardcoded-text ratchet gate

2026-07-31 · Jonas · 1 commits · 55 filer · +1 941 −236 · Buggfixar B-172 B-174 B-180

Detaljer
## Summary Three i18n bugs from the fix-round-2 handoff ("Kluster 3"), fixed together since B-174 and B-180 share one backend contract: - **B-172** — `FarmAnalyticsSummary.svelte` (0 `$t()` calls) and `DailyPrioritiesWidget.svelte` fully i18n'd. New shard `frontend/scripts/i18n/keys/farmAnalytics.mjs`. `analyticsStore.getRiskLevelInfo` gained a `labelKey` (used by `RiskScoreBadge.svelte`). - **B-174 + B-180 (shared contract)** — backend rule engines used to send finished English `title`/`primaryAction.verb` text (only `body`/`rationale` ever got the LLM-rephrase locale treatment). `CopilotService`, `PigCopilotRules`, and `MultiSiteCopilotRules` now set `titleKey`/`titleParams` on every `Nudge` and `verbKey` on every `PrimaryAction`, alongside the legacy English text (kept as fallback + LLM-rephrase input). Same contract extended to `AICoPilotService`'s four rule-engine `ActionPriorityItem` generators (`titleKey`/`titleParams`/`descriptionKey`/`descriptionParams`/`reasoningKey`/`reasoningParams`, mirrored in `AICoPilotRoutes.DailyPriorityDTO`). Frontend resolves via `resolveNudgeTitle`/`resolveNudgeActionVerb` (`frontend/src/lib/copilot/nudges.ts`) across all 5 nudge-render sites (NudgesWidget, PigMorningBriefWidget, /today, /priorities, /copilot) plus CopilotPanel's priorities tab — with a safe fallback to the English text whenever the key is absent or the translator can't resolve it (never leaks a raw dotted key). - `CopilotPanel.svelte` fully i18n'd (was 1 `$t()` call for the whole file). - Off-brand purple gradient (`#667eea`/`#764ba2`, both occurrences — header AND chat-assistant avatar, the second one wasn't in the original bug report) replaced with `var(--gradient-primary)`. - "999 days ago" health-check sentinel replaced with an explicit never-checked key. Trigger condition is unchanged (`None` still fires the priority, matching the old `999 > 60` behaviour) — only the *display* branches on `Some`/`None` now. - `regelradarRule`'s title (curated DB content, not an English template) intentionally has no `titleKey` — documented in code. - **New CI gate** — `frontend/scripts/check-hardcoded-english.mjs` wraps the previously-unwired `find-hardcoded-english.mjs` detector in a per-file ratchet against `frontend/hardcoded-english-baseline.json`. Fails only on **new** offenders (a file's count going up, or a brand-new offending file); does not require fixing the existing debt. Baseline: **88 files / 252 hits** (recorded after this PR's own fixes, so it doesn't grandfather them back in). Wired into `scripts/check-ci.mjs`. Documented in `CLAUDE.md`. - Side fixes found along the way: sim page's `Art: Pig` (raw backend enum, B-192's side-note) now goes through the existing `animalTypeLabel()` helper. Remaining known gaps (AICoPilotService's `getRecommendations`/`answerQuery`, `MultiSiteCopilotRules`'s metric-label English remainder, sim page's 2 pre-existing hardcoded-Swedish hits) filed as **B-210** — deliberately out of scope here per "only rule-engine templates become keys." ## Contract shape ```scala final case class Nudge(..., title: String, ..., primaryAction: PrimaryAction, titleKey: Option[String] = None, titleParams: Option[Map[String, String]] = None) final case class PrimaryAction(verb: String, deeplink: String, payload: Option[String] = None, verbKey: Option[String] = None) ``` ```ts export function resolveNudgeTitle(nudge, t): string // prefers $t(titleKey, titleParams); falls back to title ``` ## Test plan - [x] `npm run check:ci` green (frontend + backend) — confirmed twice, including the pre-push hook's own re-run. - [x] Backend: `CopilotNudgeServiceSpec` new "i18n-keys-not-text contract" test (composite fixture, asserts every emitted nudge has `titleKey` + `primaryAction.verbKey`); `AICoPilotServiceHealthCheckSpec` (999-sentinel trigger-preserved + display-fixed). - [x] Frontend: `nudges.test.ts` (resolver fallback contract — key hit, key miss, key absent, empty-string translator). - [x] Visual veri
  • 95c3905 Fix B-172/B-174/B-180: i18n keys+params contract for copilot, component i18n, hardcoded-text ratchet gate Jonas · 2026-07-31 · +1 941 −236

#201 Health-check: touched-state tracking for numeric scales (B-177 follow-up)

2026-07-31 · Jonas · 3 commits · 25 filer · +1 031 −158 · Buggfixar B-177

Detaljer
## Summary Follow-up to B-177 per Jonas's 2026-07-30 decision: untouched numeric welfare scales must NOT be persisted — they stay "not assessed" in the welfare record instead of silently saving as the visible `0` prefill. A shared `touched` Set (generalized from the barn-mode-only `barnTouched`) now drives both office and barn mode: - Every numeric-scale `NumberStepper`'s `onvaluechange` marks the field touched; barn-mode segment chips guard their active state on `touched.has(key) && f_x === v` so the `0` chip is never pre-selected. - `doSave()` only persists a numeric value when its key is in `touched` (`touchedInt`/`touchedNum` wrappers) — untouched → `undefined`, i.e. absent from the saved event. - `openEdit()` pre-seeds `touched` from whichever of the 13 numeric fields already have a saved (non-null) value, so re-saving an existing record without touching a field doesn't wipe its prior value. - `unassessedScaleCount()` (renamed from `emptyScaleCount()`) counts numeric scales NOT in `touched`; the "N fields not assessed" callout now self-hides once real assessment (or the bulk action) closes the gap. - New "No remarks on the rest" bulk action (office: near Save; barn: near the progress indicator) touches every non-BCS numeric scale, forcing genuinely-unassessed fields to `0`. BCS/Hull is deliberately excluded — its 1–5 scale has no valid "0", so the bulk action can never fabricate a body-condition score; the warning correctly keeps flagging it alone until the keeper enters a real value. The touched-set logic is extracted into a pure, Svelte-free helper module (`frontend/src/lib/util/healthCheckTouchedFields.ts`) with 13 vitest unit tests, including cases that specifically distinguish CREATE-mode (fields already `'0'`, just need touching) from EDIT-mode (a never-assessed field is `''`, so the bulk action must force it to `'0'` rather than being a no-op). ## Verification **check:ci — frontend all green (9/9), backend skipped (environment):** ``` ✅ svelte-check (TypeScript + Svelte) 97.1s ✅ eslint (lint gate) 87.0s ✅ vitest unit tests 16.7s ✅ i18n coverage guard 1.0s ✅ WA boolean-attr binding guard 9.1s ✅ wa-dialog hide-handler target guard 6.0s ✅ a11y token-contract guard (contrast) 0.4s ✅ token-drift guard 0.8s ✅ layout-overflow guard (Playwright) 429.5s ⏭ sbt test (backend) SKIP_BACKEND=1 ``` Backend gate was skipped via `SKIP_BACKEND=1` — `sbt test` hits a Windows named-pipe lock conflict (`Could not create lock for \.\pipe\sbt-load-..._lock, error 5`) on this shared sandbox when another sbt session holds the pipe; this is an environment/infrastructure issue unrelated to this change (no Scala files touched by this PR). check:ci itself caught a real bug during development: a first draft of the bulk-action helper had `.toBeUndefined()` assertions on a property (`valueOverrides.bcs`) that TypeScript's `Exclude<NumericScaleKey,'bcs'>` type structurally forbids accessing — svelte-check failed with 2 real TS errors, fixed to `'bcs' in valueOverrides` checks, re-verified at `0 ERRORS`. **Visual verification (mandatory per CLAUDE.md) — Playwright against the mocked-API harness (`bun run dev`, self-hosted WA assets), screenshots taken AND read, not just saved. 15 screenshots in `other/fas06-01-genomgang/verktyg/shots/b177-followup/`:** - **Office mode, 1200px:** steppers still show visible `0` on open; warning callout reads "11 bedömningsfält är ännu inte bedömda"; touching 3 fields drops the count to 8; bulk action clears the warning and count to 0 in one tap; save succeeds; view dialog for a draft record (2 fields touched, bulk NOT used) shows only the 2 assessed fields ("10 av 24 fält ifyllda") with every untouched scale absent — direct confirmation untouched fields are not persisted; view dialog for a bulk-use
  • 9ed5c08 Code review fix: fix touch/unassessed-count gate disagreement, extract pure helper Jonas · 2026-07-31 · +136 −27
  • a137202 KNOWN_BUGS: fix B-177 PR reference (#201, not #200) Jonas · 2026-07-31 · +2 −2
  • bcda57f Health-check: touched-state tracking for numeric scales (B-177 follow-up) Jonas · 2026-07-30 · +893 −129

2026-07-30 — 21 direkt-commits

Detaljer
  • c697bd5 CLAUDE.md: migration ledger to 0253 latest / 0254 next free (PR #200) Jonas · 2026-07-30 · +1 −1
  • 03ebe97 CLAUDE.md: update migration ledger to 0252 latest / 0253 next free Jonas · 2026-07-30 · +1 −1
  • 850cc9f Review fixes: stale-response guard in loadHasShop, correct i18n-removal claim in B-198 notes Jonas · 2026-07-30 · +15 −6
  • 51127f3 Docs: correct spread-algorithm description (SplitMix64, not the rejected XOR/rotate draft) + note seedEverythingFarm stagger gap Jonas · 2026-07-30 · +8 −2
  • bd473f1 overflow gate: dynamic free port instead of fixed 5311 Jonas · 2026-07-30 · +22 −2
  • 5e2e77f code-review fix: re-run loadHasShop on client-side farm switch Jonas · 2026-07-30 · +18 −0
  • 28ab71c Strengthen B-184-protection test: assert label inequality, not just count Jonas · 2026-07-30 · +6 −0
  • 6c4ccf3 Fix B-192: stagger sim seed birth dates + deterministic per-animal weight spread Jonas · 2026-07-30 · +363 −23
  • 4f29daa Code-review fix: B-196 KNOWN_BUGS.md self-contradiction Jonas · 2026-07-30 · +2 −2
  • 30bdcbb Review fixes: scope topbar part-revert to outdoor-mode, revert wrap guards on compact buttons, KNOWN_BUGS ÅTGÄRDAD convention Jonas · 2026-07-30 · +16 −6
  • 81620e9 Fix B-198/B-199: shop-gate farm-page cards, remove What's-new card Jonas · 2026-07-30 · +159 −114
  • 77ccef7 Address self-review: explicit min-width guard, trim KNOWN_BUGS.md notes Jonas · 2026-07-30 · +31 −31
  • 6a21a3d Fix B-196: suppress redundant nav subgroup heading Jonas · 2026-07-30 · +185 −13
  • 82664de claude-design: add shareable overview page (artifact source) Jonas · 2026-07-30 · +876 −0
  • 9d14ed9 Fix B-170/B-188: glove-mode wrap guards + outdoor-mode wa-button sizing Jonas · 2026-07-30 · +99 −15
  • caccf05 Code-review fixes: skip-button pointer-events, stale spot on resize, PR# Jonas · 2026-07-30 · +38 −26
  • de72f43 claude-design: add Brand/Logotyp card (leaf mark, dark-surface fill, sizes) Jonas · 2026-07-30 · +84 −0
  • c253f97 claude-design/README: canonical target is the new "Enlantis" project Jonas · 2026-07-30 · +3 −1
  • f2b5cc8 Fix sim UI cluster: B-190/B-191/B-193/B-194/B-195 Jonas · 2026-07-30 · +699 −81
  • 5a785a6 claude-design/README: dist/ is gitignored and regenerable, not committed Jonas · 2026-07-30 · +2 −2
  • 06d80f5 Design: first Claude Design sync — fix DESIGN.md token lies, add sync bundle Jonas · 2026-07-30 · +3 296 −3

#200 B-197: admin user management — edit + anonymize-or-delete

2026-07-30 · Jonas · 2 commits · 43 filer · +1 387 −94 · Buggfixar B-197

Detaljer
## Summary - `PATCH /api/admin/users/:id` — edit a user's name + email from `/app/admin/users` (root-admin only). `status`/farm role are out of scope: `status` is a derived field (no column to PATCH), and per-farm role edits already live in `FarmerRoutes`/`EmployeeRoleRoutes`. - `DELETE /api/admin/users/:id` implements Jonas's binding decision ("anonymisera, inte radera" for real users, but junk/duplicate test accounts must be genuinely removable): a new pure `domain/UserRetentionPolicy.decide` classifies the target from four signals — `event_log` authorship, farm ownership, farmer/staff history (active or not), impersonation-audit-trail membership (as admin or target). Any signal → **anonymize** (email rewritten to a unique `deleted-<uuid>@removed.enlantis.invalid` placeholder, `kindeId` cleared, person name blanked, row kept). Zero signals → **hard delete** (user + person rows removed). - **B-069 safety net**: the hard-delete branch attempts both deletes as one unit and falls back to anonymizing on *any* failure — the direct fix for the B-069 precedent (a blind hard-delete cascade that 500'd on an FK it didn't know about, e.g. `health_check.performer_id → person` `ON DELETE RESTRICT`, which none of the four explicit signals check). - **Root-admin protection**: both endpoints reject any target that is currently a root admin (`RouteAuth.isRootAdminUuid`), including the caller. Root-admin status is config-driven (`ENLANTIS_ROOT_ADMIN_USERS`/`enlantis.admin.rootUsers`), not a DB property this API can safely mutate — anonymizing a root admin's email would silently de-admin them at next boot. Since the caller of a root-admin-only endpoint is necessarily a root admin, this single rule also satisfies "never delete/anonymize yourself" and "never delete/anonymize the last root admin" without counting remaining admins. - New append-only `admin_user_audit` table (migration `0253`, deliberately **no FK** on `target_user_id` so the audit row survives a hard-delete) records who/when/what for every PATCH/DELETE. - Frontend: edit dialog (name/first/last/email) and remove dialog (explainer up front, actual outcome shown after — "had no history and was permanently deleted" vs "has history and was anonymized instead of deleted (<reason>)"). - (d) bulk cleanup of obvious test accounts — **skipped**: the same "Remove" action on a junk account already resolves to hard-delete with zero extra mechanism, so a bulk tool only saves clicking, not risk. Left for a future session if Jonas wants a multi-select. ## Classification rule (`UserRetentionPolicy.decide`) ``` hasEventLogAuthorship → Anonymize("event_log_authorship") else ownsAnyFarm → Anonymize("farm_ownership") else hasFarmerHistory → Anonymize("farmer_history") else hasImpersonation → Anonymize("impersonation_history") else → HardDelete ``` ## Verification - `sbt test`: **3406 passed, 0 failed, 10 ignored** (full suite, clean run with no other sbt/backend process holding the lock). Adding `EventLogRepository.existsByAuthorId` broke 22 pre-existing test doubles (local `EventLogRepository` implementations without the new method) — each got a trivial `ZIO.succeed(false)` stub matching its own file's style; no behavioral changes to those specs. - Targeted: `UserRetentionPolicySpec` (pure classification, 10 tests) + `AdminUserRoutesAuthSpec` (32 tests covering PATCH/DELETE gating, all four anonymize reasons, hard-delete, root-admin protection on both endpoints, email-collision 409, unknown-id 400) — 42/42 passed. - `npm run check:ci`: ✅ green (frontend + backend) — see gate output in this PR's session. - Visual verification (mandatory rule): live against the local dev DB (`?dev_user=` bypass, real MySQL data, not mocks) via a real Chrome browser: - **Edit**: renamed a real test user ("anna@example.com" → "Anna Testsson"), saved, row updated in place. - **Hard delete**: removed user "1" (zero farm/event history) → dialog showed "1 hade ingen historik och raderades p
  • 70286ca B-197 review fixes: atomicity, self-guard, PII scrub Jonas · 2026-07-30 · +244 −55
  • 41e2139 B-197: admin user management — edit + anonymize-or-delete Jonas · 2026-07-30 · +1 143 −39

#199 Fix B-186/B-185: mount help inside the app shell, fix locale-switcher styling

2026-07-30 · Jonas · 2 commits · 23 filer · +1 288 −865 · Buggfixar B-186 B-185

Detaljer
## Summary - **B-186** — `/help/kb` sat outside the `/app` route tree, so `TopBar`/`BottomBar` (mounted only in `routes/app/+layout.svelte`) disappeared when a logged-in keeper opened Hjälp from the mobile nav sheet, dropping them out of the app shell entirely. Adds `/app/help/**` (landing, `category/[category]`, `search`, `[slug]`) mounted inside `/app`, per Jonas's decision on B-186 (alternative a). - **B-185** — the KB language switcher rendered as big round pills (`border-radius: 999px` + hardcoded slate/green hex). Fixed to `--radius-sm` (2px) with tight padding and design tokens. ## How the KB content is shared between public and in-app routes Extracted four presentational components into `frontend/src/lib/components/help-kb/` — `KbLandingBody`, `KbCategoryBody`, `KbArticleBody`, `KbSearchBody` — each taking a `basePath` prop so internal links (`category/…`, `search`, article slugs, breadcrumbs) stay inside whichever tree they're mounted in. Both `routes/help/kb/**` (public) and the new `routes/app/help/**` (in-app) are now thin wrappers: ```svelte <KbLandingBody basePath="/help/kb" /> <!-- public --> <KbLandingBody basePath="/app/help" /> <!-- in-app --> ``` The locale switcher (B-185) is its own shared component, `KbLocaleSwitch.svelte`, used inside all four body components — so the styling fix lands once instead of twice. Underlying data/markdown loading (`$lib/util/helpKb.ts`) was already environment-agnostic and needed no changes. Public `/help/kb` behavior for logged-out visitors is unchanged. ## Links re-pointed - `frontend/src/lib/components/shell/MobileNavSheet.svelte` — the mobile sheet's "Hjälp" link now points at `/app/help` instead of `/help/kb`. This is the only in-app help link found (grepped `/help/kb` across `frontend/src`). - `SiteHeader.svelte`, `SiteFooter.svelte`, `routes/university-partners/+page.svelte`, `routes/sitemap.xml/+server.ts` intentionally still point at the public `/help/kb` — they're marketing/logged-out surfaces outside `/app`. ## Other changes - `frontend/src/lib/nav/registry.ts` — registered the four new `/app/help/**` patterns in `INTENTIONALLY_UNLISTED` (reached via the mobile sheet, not a nav-registry destination); this is required by `registry.test.ts`'s route-reachability guard. - `KbLocaleSwitch`'s active-pill text uses `--color-on-primary` (the documented contract token for text on `--color-primary`, same pattern as `.btn-primary` in `global.css`) rather than a fourth hardcoded hex — not one of the three tokens literally named in the B-185 bug entry, but the correct token for the job. - `other/KNOWN_BUGS.md` — B-185 and B-186 marked ÅTGÄRDAD with the fix summary. ## Visual verification (mandatory per CLAUDE.md) Claude in Chrome could not capture screenshots in this sandbox (extension host-permission error on every tab, confirmed not origin-specific). Fell back to the documented CCR-sandbox path: a Playwright mockAPI harness (no backend needed — `other/fas06-01-genomgang/verktyg/harness.mjs`), new script `other/fas06-01-genomgang/verktyg/b185-b186-help-inapp-verify.mjs`. Screenshots taken **and read**, plus computed-style assertions: 1. **`/app/help` at 390px and 1200px** — TopBar + SideRail/BottomBar + KB hero all present (`b185-b186-app-help-390-viewport.png`, `b185-b186-app-help-1200-viewport.png`). 2. **Mobile sheet → "Hjälp" real click** — lands on `/app/help` with TopBar/BottomBar still mounted (`b185-b186-mobile-sheet-help-landed-viewport.png`), confirmed via `hasTopBar/hasBottomBar/hasKbHero` all `true` in the actual post-click DOM. 3. **Locale switcher compact + rectangular** on both public and in-app pages (`b185-b186-locale-switch-public-390.png`, `b185-b186-locale-switch-app-390.png`), verified via computed styles: `border-radius: 2px`, `border-color` = `--color-border` mix, link text `rgb(122, 31, 26)` (`--color-primary`), active background same color with `rgb(239, 230, 207)` text (`--color-on-primary`) — ~34×24px, not circles. Re-measured with `lo
  • 18209b6 Rewrite in-article /help/kb links against basePath (spec-review catch) Jonas · 2026-07-30 · +6 −1
  • ab3076d Fix B-186/B-185: mount help inside the app shell, fix locale-switcher styling Jonas · 2026-07-30 · +1 282 −864

#197 Fix B-167/B-168: compact calendar controls, remove redundant color legend

2026-07-30 · Jonas · 3 commits · 3 filer · +70 −49 · Buggfixar B-167 B-168

Detaljer
## Summary - **B-167** (mobile calendar controls too tall): tightened `.chip` (filter pills) and `.view-modes button` (Month/Week/Agenda toggle) padding/font-size/line-height, plus an extra `@media (max-width: 480px)` compacting pass. Nav (Prev/Today/Next) uses `<wa-button size="s">`, already the compact WA variant, so it was left as-is. - **B-168** (redundant color-legend card): removed the `"Färgförklaring"` card entirely — it repeated the same `KIND_COLOR` dots + labels already on the filter chips one row above. The chips already carry their own `.dot` swatch (unchanged), so they stand alone as the legend. Also removed the now-orphaned `calendar.legendHeading` key from its i18n shard (`frontend/scripts/i18n/keys/misc-1.mjs`) + `_order.mjs`. `min-height` was deliberately left untouched on `.chip`/`.view-modes button` — the 24px `--tap-target-min` floor (normal mode) and the 48-56px glove/big-finger/outdoor-mode overrides (`app.css`) both target the bare `button` element selector globally, so they keep applying without any extra glove-mode wiring in this file. ## Verification (mandatory per CLAUDE.md) No backend was started for this — `sbt run` blocks `sbt test`/gate runs machine-wide on this box (documented quirk), and other concurrent agents needed sbt. Instead: Playwright headless + a network-level `/api` mock + the `?dev_user=` bypass, modeled on `frontend/scripts/check-overflow.mjs`'s harness, against `npm run dev`. Screenshots were taken **and read** at every stage (not just saved): - **390px controls block** (toolbar top -> filter-chips bottom): **247px -> 162px, 86px saved (~35%)**. - `.chip` height: 30.5px -> 24px (now at the `--tap-target-min` floor). - `.view-modes button` height: 38.4px -> 24px. - **Legend card**: DOM query `legendPresent` true -> false, at both 390px and 1200px. - **Interactions still work**: filter-chip toggle (visible `.cal-chip` count 4->3 when disabling one kind), Month->Week view switch renders `.week-grid`, Prev/Next nav advances the period label (`juli 2026` -> `augusti 2026`). - **Glove mode** (`enlantis.bigFinger=on`) at 390px: chip / view-toggle / nav-button heights all measured **56px** (the global glove floor), zero horizontal overflow on `document.scrollingElement` or `#app-shell-main`. - **Desktop 1200px**: no legend card, no overflow, chips render in a single row - unaffected/improved. ## Gate `SKIP_BACKEND=1 npm run check:ci`: all 9 steps green (svelte-check, eslint, vitest, i18n:check, WA boolean-attr guard, wa-dialog guard, a11y contrast guard, token-drift guard, layout-overflow guard - which itself scans `/app/farms/farm-1/calendar` at 390px and confirms no overflow); `sbt test` skipped per `SKIP_BACKEND=1`. ## KNOWN_BUGS.md B-167 and B-168 will be marked ÅTGÄRDAD with this PR's number in a follow-up commit once the PR number is known. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 3a49afe Review fixes: chip font floor to --text-xs, drop orphaned calendar.legendHeading from generated JSONs Jonas · 2026-07-30 · +3 −3
  • a2dfc3d B-167 follow-up: compact the Prev/Today/Next nav buttons too Jonas · 2026-07-30 · +47 −20
  • 027bec1 Fix B-167/B-168: compact calendar controls, remove redundant color legend Jonas · 2026-07-30 · +20 −26

#198 Fix B-142/B-181: global print foundation + health-check print margins

2026-07-30 · Jonas · 2 commits · 6 filer · +246 −10 · Buggfixar B-142 B-181

Detaljer
## Summary B-142's global print reset (PR #165) correctly hides the app shell on print, but missed one piece of chrome: the AI Co-Pilot side panel. `CopilotPanel.svelte` is deliberately never `display:none` on screen — it's parked off-canvas with `position: fixed; right: 0; transform: translateX(100%)` so its controls stay in the tab order for keyboard/AT users even while closed. Paginated media resolves that `translateX(100%)` against the (much narrower) printable page box instead of the screen viewport, so the 400px-wide/100vh-tall panel re-enters the printed page — this is what produced B-181's right-edge bleed and a spurious blank trailing page, not an app-shell width problem (B-142's own reset for `.app-shell`/`.app-viewport`/ `.table-scroll` was already correct). Root cause confirmed with a DOM measurement under `page.emulateMedia('print')`: `.copilot-panel` measured `left:1440 right:1840` against a 1440px viewport — i.e. sitting right at the edge, not hidden — while every other element stayed in bounds. ### Changes - **`frontend/src/global.css`** (B-142 print block): add `.copilot-panel` + `.panel-overlay` to the print-hide list, next to the already-hidden `.copilot-trigger` FAB. Two more latent leaks found in the same sweep and closed alongside it: the root layout's own `.skip-link` (a different element than `AppShell.svelte`'s already-hidden `.app-shell__skip`) and `.install-banner-mount` (PWA install-banner wrapper; the card itself already had the `banner` class caught by the existing rule). - **`frontend/src/routes/app/farms/[id]/welfare/health-check/+page.svelte`**: resolve the competing `@page` rules called out in B-181 — dropped the page's own `margin: 1.2cm; size: A4 portrait;` (which competed with `global.css`'s `margin: 1.5cm`). `global.css` is now the single owner of `@page` margin/size; the page's `@page` block keeps only its `@bottom-center` page-number footer, which is genuinely page-specific. - **`frontend/src/routes/+layout.svelte`**: `print-hide` the dev-only auth-bypass banner (already dead-code-eliminated from prod builds via `import.meta.env.DEV`, so no prod behavior change) — it was polluting dev-mode print verification renders. - **`other/fas06-01-genomgang/verktyg/pr8-print-verify.mjs`**: the Playwright verification script used below (mocked-API harness + `?dev_user=` bypass, reusing `verktyg/harness.mjs`), kept for future print-regression checks. - `other/KNOWN_BUGS.md` (B-142 follow-up note + B-181 closed) and `other/ENLANTIS_MASTER_TODO.md` session entry. ## Verification Real print rendering via headless Chromium (`page.pdf()`, A4 portrait), not just screen-mode CSS review. Rendered and **read** the following (PNG screenshots + PDF pages opened and inspected): | Page | Before | After | |---|---|---| | `/welfare/health-check` ("Skriv ut översikt") | 2 pages: page 1 had the DEV banner + a full-height vertical bleed down the right edge from the leaking Co-Pilot panel; page 2 was a near-blank trailing page (just the panel's frame + footer) | **1 page.** No app chrome, symmetric left/right margins, footer with page number, no trailing blank page | | `/animals/:id/print` (lifetime journal) | Same right-edge bleed pattern | **2 legitimate pages** (table content flows to page 2), no chrome, no bleed, QR code fully in bounds | | `/weekly-plan` (already part of B-142's original repro, used as a regression check) | — | Still clean: 1 page, "1/1" footer, no chrome, no bleed (own landscape `@page`, untouched) | Screen rendering spot-checked at 390px and 1200px on all three pages — unchanged (chrome present as expected on screen; only print media was touched). Reproduce: `bun run dev` (or `npm run dev`) from `frontend/`, then `node other/fas06-01-genomgang/verktyg/pr8-print-verify.mjs` from the repo root (`TEST_BASE=http://localhost:<port>` if Vite picked a non-default port; `SHOTS_DIR=<dir>` needed on Windows — the shared harness's default `shots/` path doesn
  • 9461d80 Restore size: A4 portrait in health-check @page (spec-review catch) Jonas · 2026-07-30 · +7 −2
  • bc31043 Fix B-142/B-181: global print foundation + health-check print margins Jonas · 2026-07-30 · +239 −8

#191 Fix B-176/B-177/B-179: health-check form labels, steppers, table overflow

2026-07-30 · Jonas · 4 commits · 8 filer · +460 −134 · Buggfixar B-177 B-179 B-176

Detaljer
## What Three health-check-form bugs from live-testing, fix round 2 / PR 10: - **B-176** — "Objekt" → "Djur" in the generic health-check dialog (`Typ av objekt` → `Typ av djur`, `Objekt` → `Djur`). Batch support in the subject-type field is unchanged (Djur/Parti). The unexplained `*` is hidden via `--wa-form-control-required-content: ''` on the dialog's `<wa-dialog>` (pierces the shadow DOM of every field inside); `required` stays on the subject + date fields so client-side validation is unchanged (subject field is guarded in `submit()`; the date field relies on the native `wa-input` HTML5 validation, no separate JS guard). - **B-177** — new shared `NumberStepper.svelte` (`wa-input type="number"` with always-visible +/− `wa-button`s slotted into WA's `start`/`end` slots) wired into all 15 numeric fields (13 office + 2 barn mode). Every scale defaults to `'0'` **except** Hull/BCS, which stays empty per Jonas's explicit decision (its scale is 1–5, no 0). First +/− tap from empty lands on `min`, not `min+step`. - **B-179** — both `.mini-table` instances (the "Senaste hälsokontroller" table and the record-view dialog) wrapped in a new `.table-scroll { overflow-x: auto; }` container so the table scrolls in its own box instead of pushing the page wider. ## Decisions (Jonas, recorded in `other/KNOWN_BUGS.md`) - B-176: rename to "Djur", keep batch support, remove the `*` entirely (no legend). - B-177: steppers + default `0` for every numeric field except Hull/BCS, which stays empty. - B-179: scroll container for the recent-checks table (both instances). ## Audit + code review (this session — original agent's session was lost to an API outage; work was already committed as `ef989045`) The original commit correctly implemented all three decisions (i18n via the shard system, sv-only slots with EN untouched per B-156, required-field asterisk hiding, NumberStepper wiring, table-scroll wrapper). Rebased onto master (past PR #188, which rewrote the overflow gate to render real routes and open dialogs) and ran an independent code review before merge, which found and fixed: 1. **Barn-mode/big-finger button sizing never actually applied** — NumberStepper's own `min-height: 1.75rem` on its +/- buttons outranked app.css's global handskläge sizing rule on CSS specificity (4 class-equivalents vs 1), silently defeating it regardless of mode. Removed the local `min-height`. 2. **Indistinguishable `aria-label`s** — all 15 stepper instances announced identical bare "Decrease"/"Increase". Added an `srLabel` prop so barn-mode fields (which omit the visible `label`) still get a distinct accessible name. 3. **`size="small"`** — the deprecated long-form WA size token — normalized to the canonical `size="s"`. 4. **`emptyScaleCount` semantics** flagged (not fixed) as an out-of-scope follow-up: defaulting every scale to `'0'` means an "unanswered" field is now indistinguishable from an intentional `0` answer for any downstream completeness/scoring logic. Spun off as a separate task since it's a product decision, not a bug in this PR's scope. A **second, deeper round** then found and fixed two real layout regressions that the first round's own verification had missed (root-caused after discovering an earlier check:ci run had silently tested a stale dev server on a port shared with another agent's worktree on this sandbox — a false PASS): 5. **`.grid-2 > :global(wa-input.number-stepper) { min-width: 0; }` never reached the compiled CSS.** It shared a comma-separated selector list with `.grid-2 > wa-input, .grid-2 > wa-select`, both now Svelte "unused CSS selector" warnings (every grid-2 field in this component is a NumberStepper, not a raw wa-input/wa-select). Svelte's unused-selector pruning drops an entire comma-group once every non-`:global()` selector in it is unused — taking the `:global()` one down with it. Confirmed via a `document.styleSheets` dump in-page. Fixed by giving NumberStepper its own fully-global selector (`:global(.grid-2 > wa-input.
  • 2d8e401 Disclose B-177 default-0 semantics: create-mode gate guards only BCS, edit mode keeps '' deliberately Jonas · 2026-07-30 · +14 −0
  • 5de7915 Fix remaining B-177/B-179 glove-mode overflow regressions Jonas · 2026-07-30 · +88 −17
  • 73fa6ff Code review fixes for B-176/B-177/B-179 after rebase onto master Jonas · 2026-07-30 · +124 −59
  • f57169d Fix B-176/B-177/B-179: health-check form labels, steppers, table overflow Jonas · 2026-07-29 · +234 −58

#187 Fix B-182/B-183/B-184/B-187: collapsed nav surfaces, settings groups, menu-foot cleanup

2026-07-30 · Jonas · 3 commits · 12 filer · +415 −137 · Buggfixar B-182 B-183 B-184 B-187

Detaljer
## Summary Fix round 2, PR 11 (nav/IA), per Jonas's 2026-07-29 orchestration-thread decisions recorded in `other/KNOWN_BUGS.md` / `other/HANDOFF_RUNDA2.md`. - **B-182** — mobile "Mer" sheet AND desktop side rail now open **fully collapsed**, even while standing on a route inside a bucket (e.g. a health route). `SideRail.svelte`'s `$effect` that auto-opened the bucket owning the active route on every mount/navigation is gone (`syncToRoute` removed from `bucketAccordion.ts`). `openBucketId` now changes **only** via an explicit press (`toggleBucket`) on either surface — no auto-derivation on mount, and the old pre-B-132 persisted multi-open model was **not** reintroduced. The bucket owning the active route is still visually marked (`rail__bucketHead--current` / `sheet__bucketHead--current`, primary-coloured label) without expanding it. - **B-183** — renamed **"Nedre fält" → "Anpassa mobilmeny"**. Two i18n keys pointed at this destination: `nav.settings.bottomBar` (the Inställningar list entry) and `nav.bottomBar.settings.title` (TopBar's user-menu shortcut + the settings page's own H1/`<title>`). Both sv slots renamed so every surface matches; **EN slots left untouched** on both (B-156 pattern: sv-only copy changes don't touch the EN slot). - **B-184** — Inställningar's flat 19-item list split into Jonas-approved subgroups: **Utseende & språk** (Enheter & språk, Språk, Tillgänglighet, Anpassa startsida, Anpassa mobilmeny, Favoriter) / **Aviseringar** (Aviseringar, Larm, Frekvensgränser) / **Data & export** (Dataexport, ICAR-export, Dataradering, Lagring, Lokal DB-kryptering) / **Gård & konto** (Gårdar, Ny gård, Jämförelser) / **AI & övrigt** (Copilot, Tidsbesparingsenkät). Implemented in `registry.ts` as 5 new group nodes using the same `children` pattern the compliance bucket's 9 subgroups already use; 5 new i18n keys added (`nav.settings.group.*`, en+sv filled, other slots auto-fill to `TRANSLATION_NEEDED`). - **B-187** — removed the "Nedre fält" shortcut `<a>` in `MobileNavSheet.svelte`'s `.sheet__links` foot row. "Hjälp" stays. The renamed (B-183) entry remains reachable one tap away under Inställningar. ## Visual verification (Claude in Chrome, `/welfare/health-check`) Native browser-window resize was unavailable in this sandbox (window stuck maximized at 2134×1160 — `resize_window` had no effect). Desktop rail was verified at native width directly. The mobile sheet was verified by injecting a CSS override to force the real `.app-shell__bottom`/`.app-shell__rail` breakpoint classes (same components, same code path a phone would render) and clicking the actual "Mer" trigger — not a synthetic re-render. - **Desktop rail**: only "Hälsa" carries `rail__bucketHead--current` (`color: rgb(122,31,26)`); **every** bucket head has `aria-expanded="false"` on load, including Hälsa. Clicking "Hälsa" expands it and collapses whatever was open (max one at a time). Clicking "Inställningar" shows all 5 subgroups, alphabetically sorted, "Anpassa mobilmeny" present. - **Mobile sheet**: same result — all 12 bucket heads `aria-expanded="false"` on open, only "Hälsa" marked `sheet__bucketHead--current`. `.sheet__links` contains exactly one link: "Hjälp" (`/help/kb`) — no "Nedre fält". Tapping "Inställningar" renders the 5 subgroup headings (`AI & övrigt`, `Aviseringar`, `Data & export`, `Gård & konto`, `Utseende & språk`) with "Anpassa mobilmeny" inside "Utseende & språk". ## Tests - `frontend/src/lib/nav/bucketAccordion.test.ts` — added two tests: opening a surface never mutates `openBucketId` (pure `activeBucketId` reads have zero effect on the store), and a regression guard asserting `syncToRoute` no longer exists as an export. - `frontend/src/lib/nav/registry.map.test.ts` — added an "Inställningar — 5 subgroups" describe block (mirrors the existing compliance-subgroups test) plus updated the now-stale assertions that expected the 19 settings leaves to carry their own `.bucket` field (they now inherit it from their group parent, same as
  • e82d023 Translate Swedish code comment to English (standards review, PR #187) Jonas · 2026-07-30 · +3 −3
  • 811d3d8 Code-review polish: non-colour "current bucket" cue, one-shot script sync Jonas · 2026-07-30 · +51 −8
  • 0e00802 Fix B-182/B-183/B-184/B-187: collapsed nav surfaces, settings groups, menu-foot cleanup Jonas · 2026-07-29 · +361 −126

2026-07-29 — 10 direkt-commits

Detaljer
  • 9b4e7e2 Fix B-175: overflow gate generates routes, opens dialogs, adds glove-mode variant Jonas · 2026-07-29 · +880 −137
  • a4b6cdd Fix B-169: cold-start /today + /copilot — index + bounded parallelism Jonas · 2026-07-29 · +168 −15
  • e56fa61 incidents admin: restore fallback badge tone for unknown severity values Jonas · 2026-07-29 · +2 −1
  • 1e24511 Record final Jonas decision: B-156 EN-slot pattern - Swedish-only copy changes leave EN untouched Jonas · 2026-07-29 · +6 −5
  • 8b3c141 Record Jonas decisions 2026-07-29: B-176 'Djur', B-177 BCS empty default, B-182 collapsed also on desktop, B-184 grouping approved, B-186 help inside app shell Jonas · 2026-07-29 · +33 −22
  • 7291008 Ikonspåret stängt (Jonas 2026-07-29): radera pensionerade egenritade ikonset Jonas · 2026-07-29 · +13 −8 567
  • e479f8f Dev-historiksajten omgenererad efter byggomgången (PR #162-#179) Jonas · 2026-07-29 · +8 371 −153
  • 9f370be Sessionslogg: stora byggomgången 2026-07-28/29 (PR #162-#178) inforda i master-TODO:ns narrativ Jonas · 2026-07-29 · +1 −1
  • d4744f0 B-156 bokförd som åtgärdad (omtolkad): lövet fick Hälsokontroll-etiketten (PR #167), stubben dold (PR #164) Jonas · 2026-07-29 · +11 −3
  • 0bd41a0 Nav E2-plan: mobilfältet, favoritsystemet, Mer-kartan, handskläget — 5 PR-etapper Jonas · 2026-07-29 · +1 805 −0

#184 Log B-190→B-199: simulator round + nav, user management, shop gating, release-notes surface

2026-07-29 · Claude (AI) · 1 commits · 1 filer · +317 −2 · Buggfixar B-190 B-199 B-111

Detaljer
Docs-only: appends B-190 through B-199 to `other/KNOWN_BUGS.md` and re-confirms B-111. Live testing (Jonas, 2026-07-29). No code changes. *(Renumbered from B-188→B-197: master took B-188/B-189 for the outdoor-mode and motion-detection fixes while this branch was open.)* **Simulator** - **B-192** — all pigs weigh exactly **1.5 kg**. Two code-verified causes: `SimSeedService.scala:138` seeds the pig-pen scenario with `birthDate = now` (age 0 → weight = birth weight), and `SimGrowthModel.weightForAge` is a pure function of age, so there's **no individual variation**. Other scenarios already stagger birth dates. **Decision (Jonas): spread both birth dates and weights.** The entry warns explicitly that the model constants (1.5 kg birth / 0.8 kg-day / 130 kg mature) are realistic and must **not** be "fixed" — the seeding is the bug — and that variation must be id-derived, not random, to keep replay reproducible. - **B-195** — after one chore, **all** chore buttons stay disabled. Gated only on `acting || timeLimitReached`; `timeLimitReached` is provably false, so `acting` appears stuck. Repro candidates included. - **B-194** — tutorial bubble renders off-screen (positioned unconditionally below the target, no viewport clamping or flip-above). - **B-191** — onboarding guide **is** implemented (B-102) but permanently gated by the `sim-coached` localStorage flag; the real issue is discoverability + no reset for testing/demos. - **B-193** — clicking an animal gives no visual selection in the 3D view. - **B-190** — hamburger overlaps the sim HUD panel (family of B-101/B-107). - **B-111 (re-confirmed)** — pig-pen floor doesn't fill the red fence; sharpened hypothesis that fence and floor geometry derive from different dimensions. **App** - **B-199** — ⚠️ "What's new" card still renders on the farm page: **B-159's fix (PR #165) missed this surface**. Flags a recurring pattern — third instance of a "done" fix covering only one of several surfaces (B-074→B-178, B-161→B-169, B-159→B-199) — and recommends grepping all occurrences before closing an entry. - **B-198** — `ShopTodayCard` renders unconditionally on farms without a shop; the `'shop'` feature flag already exists in `farmFeaturesStore`. Same decision class as B-160. - **B-197** — user management needs edit + delete. **Decision (Jonas): anonymise, not delete** — but test/duplicate accounts should be genuinely removable. Proposes one action that picks the right path based on whether the user has retained history (avoiding the B-069 hard-delete cascade). - **B-196** — "Foder" group renders a redundant "FODER" subheading (bucket label == sole subgroup label). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv
  • a8b46de Log B-190..B-199: sim round (weights, selection, tutorial, chores), nav, user management, shop gating, release-notes surface; re-confirm B-111 Claude (AI) · 2026-07-29 · +317 −2

#186 Smafix: B-165 incidents restyle+i18n, B-166 nav-sheet counters, B-173 palette kbd hints

2026-07-29 · Jonas · 1 commits · 13 filer · +318 −88 · Buggfixar B-165 B-166 B-173

Detaljer
## Summary Three small, independent fixes bundled per the fix-round assignment: - **B-165** — `/app/admin/incidents` was unstyled (leaned on inert Tailwind utility classes; Tailwind was removed 2026-07-03) and had a hardcoded English subtitle line. Rewrote the page with component-scoped `<style>` + `global.css` tokens (same pattern as `admin/support/+page.svelte`), and moved every hardcoded English string on the page (subtitle, empty-state copy, "Resolved"/"Ongoing" badges, summary placeholder) to i18n — new `status.admin.subtitle` / `.noIncidentsTitle` / `.noIncidentsHint` / `.summaryPlaceholder` keys, plus reused the existing `status.incident.ongoing` / `.resolved` keys instead of hardcoded English. - **B-166** — removed the leftover mockup counter badges (`<span class="sheet__count">`) next to each group in the mobile nav sheet, plus the now-dead CSS. `sheet__count` had exactly one usage. - **B-173** — the command palette's `esc` chip and "↵ to open · esc to close" footer hint are keyboard-only affordances that showed on touch/mobile where there's no physical keyboard. Hidden both inside the existing `@media (max-width: 640px)` breakpoint, plus a belt-and-braces `@media (hover: none) and (pointer: coarse)` rule so genuinely touch/no-hover devices above 640px (tablets) also hide them. **Bonus finding while wiring the B-165 i18n keys (not part of this PR's fix, flagged separately):** every *pre-existing* key in `frontend/scripts/i18n/keys/status.mjs` uses a stale 6-slot convention (`[en, sv, de, es, fr, nl]`, per the file's own header comment) that no longer matches `build-i18n.mjs`'s actual `LOCALE_CODES` slot order — e.g. `bg.json` currently contains German text, `de.json` contains Dutch text, `ro.json` contains Spanish text, `da.json` contains French text, all under `status.*` keys. `sv`/`en` are unaffected (those two slots are hardcoded regardless of the bug), so nothing gated by CI or user-facing for the primary/fallback locale is broken. The 4 new keys added in this PR use the *correct* slot order. A background task has been spawned to fix the pre-existing mismatch as its own follow-up. **Other admin pages found with the same inert-Tailwind inheritance (not fixed here — out of this PR's scope, reported per the B-165 note):** - `frontend/src/routes/app/admin/authorities/+page.svelte` — bare `class="grid"` (inert) - `frontend/src/routes/app/admin/whitelabel/+page.svelte` — bare `class="grid"` (inert) + `bg-green-100 text-green-700` / `bg-red-100 text-red-700` (inert) ## Visual verification `resize_window` did not affect the real Chrome window in this environment (it stayed pinned to the OS-maximized size regardless of the requested dimensions), so viewport testing was done with an in-page iframe harness (same-origin, `?dev_user=` dev-auth bypass) sized to the exact target widths — this gives a genuine CSS-viewport test (the iframe's own `window.innerWidth` drives the media queries) without needing to resize the outer window. - **B-165, `/app/admin/incidents` @ 390px and @ 1200px:** screenshotted both — burgundy primary button, proper card/border/token styling, Swedish copy ("Incidenthantering", "Hantera publika incidentrapporter som visas på **/status**.", "Inga incidenter ännu." / "Posta en incident för att kommunicera driftstörningar till användare."). Opened the "Posta incident" form dialog (checked at desktop width): date-range grid, uppercase field labels, severity select, service pill-chips, summary field showing the new i18n placeholder "Kort enradsbeskrivning av incidenten", markdown textarea, Spara/Avbryt buttons — all token-styled, no raw/unstyled text. - **B-166, mobile nav sheet @ 390px:** opened via the bottom-bar "Mer" trigger; screenshotted the group list (Produktion & mark, Efterlevnad & journaler, Föreskrifter och regler, Ekonomi & drift, Enheter, AI-copilot, Inställningar, Rådgivarportal) — icon + label + chevron only, no counter numbers. - **B-173, command palette:** at 390px, computed `getComput
  • 933a154 Smafix: B-165 incidents restyle+i18n, B-166 nav-sheet counters, B-173 palette kbd hints Jonas · 2026-07-29 · +318 −88

#185 Fix B-178: guard wa-dialog hide handlers app-wide + CI guard (B-074 regression)

2026-07-29 · Jonas · 2 commits · 71 filer · +765 −119 · Buggfixar B-178 B-074

Detaljer
## Summary - **B-178**: `welfare/health-check/+page.svelte`'s two `<wa-dialog>` elements (lines 651, 1166) had unguarded `onwa-hide` handlers. B-074 (2026-07-20) was supposed to be a systemic fix for exactly this class of bug (a `wa-select`/`wa-dropdown` inside a `wa-dialog` bubbles its own `wa-hide`/`wa-after-hide` up to the dialog when its popup closes; an unguarded handler then closes the whole dialog), but was never enforced by CI — so it silently regressed. - **App-wide sweep**: wrote an AST-based scanner (`frontend/scripts/wa-dialog-hide-guard.mjs` + `wa-dialog-hide-guard-lib.mjs`, same shape as the existing `wa-bool-attrs.mjs` guard) that parses every `.svelte` file's Svelte AST and flags any `onwa-hide`/`onwa-after-hide` on a `<wa-dialog>` that isn't guarded with `if (e.target === e.currentTarget)`. Pre-fix, it found **90 unguarded sites across 60 files** — the B-074 sweep (51 dialogs / 39 files) never actually covered the whole app, or new dialogs were added afterward without the guard. - All 90 fixed via the script's `--write` codemod (two auto-fixable shapes only, verified by hand before enabling: zero-arg inline arrow handlers, and bare identifier references to zero-arg local functions / `() => void` props — both wrapped as `(e: Event) => { if (e.target === e.currentTarget) <action>; }`). - **New CI guard**: `npm run wa:dialog-hide:check` wired into `scripts/check-ci.mjs` as its own step, right after the existing WA boolean-attr guard. The pattern can't silently regress again. - Unit tests: `frontend/scripts/waDialogHideGuard.test.mjs` — 7 synthetic-snippet cases (the original B-178 shape, bare-identifier shape, already-guarded inline/early-return forms, a non-dialog element that must NOT be flagged, a param'd-but-unguarded manual-fix case, and a malformed-source parse-error case) plus a repo-wide scan asserting zero violations. - `other/KNOWN_BUGS.md`: B-178 marked ÅTGÄRDAD with the fix summary; B-074 got a cross-note about the regression and its CI-guard fix. ## Proof the guard was red pre-fix ``` [wa-dialog-hide] ❌ 90 unguarded onwa-hide/onwa-after-hide handler(s) on <wa-dialog>: lib/components/AnimalQrCard.svelte:104 [onwa-after-hide] referenced function 'closeDialog' takes no event param — cannot guard onwa-after-hide={closeDialog} lib/components/CalvingDialog.svelte:103 [onwa-hide] handler 'onClose' not resolvable in this file — cannot verify guard onwa-hide={onClose} ... routes/app/farms/[id]/welfare/health-check/+page.svelte:651 [onwa-hide] inline handler takes no event param — cannot guard onwa-hide={() => showDialog = false} routes/app/farms/[id]/welfare/health-check/+page.svelte:1166 [onwa-hide] inline handler takes no event param — cannot guard onwa-hide={() => viewRow = null} ``` (90 total; full list captured before any fix was applied.) Post-fix: `[wa-dialog-hide] ✅ every onwa-hide/onwa-after-hide handler on <wa-dialog> is target-guarded.` ## Visual/behavioral verification Playwright headless Chromium against `bun run dev`, mocked `/api`, `?dev_user=` bypass, 390px viewport (Jonas's original repro width). Screenshots read and compared before/after — both surfaces confirmed: the wa-select's value changes AND the dialog stays fully open. 1. **`/welfare/health-check`** (the reported bug, dialog opened via the `?animal=` deeplink): "Typ av objekt" select changed from "Djur" to "Batch" — dialog ("Registrera hälsokontroll") stayed open. Escape still closes it normally afterward. 2. **`/production/pigs/health`** ("Uppdatera status" dialog — independent route, same inline-arrow fix shape, sanity-checks the sweep wasn't file-specific): "Status" select changed from "Okänd" to "Negativ" — dialog stayed open. Screenshots: `other/fas06-01-genomgang/verktyg/shots/b178-{01,02,03,04}-*.png`. Verify script: `other/fas06-01-genomgang/verktyg/b178-dialog-hide-guard-verify.mjs` (5/5 checks passed). ## check:ci result ✅ check:ci green (frontend only — `SKIP_BACKEND=1`, Windows dev
  • 20c3816 Dedupe svelteFiles() walk across the new B-178 guard files Jonas · 2026-07-29 · +22 −27
  • 7a47611 Fix B-178: guard wa-dialog hide handlers app-wide + CI guard (B-074 regression) Jonas · 2026-07-29 · +743 −92

#181 Ta bort redundant role="button" på <wa-button> (185 platser)

2026-07-29 · Jonas · 2 commits · 30 filer · +191 −193 · Buggfixar B-153 B-165

Detaljer
Uppföljning på B-153. Mekaniskt svep över `frontend/src`. > **Uppdaterad efter kodgranskning (två axlar, parallella subagenter).** Granskningen bekräftade svepet men skärpte två saker: beviset för att `size`-svepet skulle avvisas är starkare än jag först skrev, och min ursprungliga formulering om "död padding" var **fel**. Båda rättade nedan. ## Vad Tar bort `role="button"` från 185 `<wa-button>` i 30 filer. Web Awesome sätter redan attributet internt på shadow-delen: ```js // WaButton, frontend/static/wa/dist-cdn/chunks/chunk.2OS3OBFO.js role=${o(isLink ? void 0 : "button")} // på ::part(base) ``` Attributet på light-DOM-värden är därför inte bara överflödigt utan aktivt skadligt i handskläge (C-07). I `frontend/src/app.css` träffar `body.big-finger [role="button"]` värdelementet samtidigt som `body.big-finger wa-button::part(base)` träffar shadow-delen — 56px min-height + 14px/20px padding staplas till ~84px i stället för avsedda 56px. Det var rotorsaken bakom B-153:s "enorma knappar", inte den `size="s"` som först misstänktes. ## Uppmätt i webbläsare (inte bara tester) `/app/admin/health` och `/app/farms` mot `bun run dev`, skärmdumpar granskade. Kontrollerat A/B på samma knapp i samma DOM, `body.big-finger` påtvingat: | | värdhöjd | värd min-height | värd padding | ::part(base) | |---|---|---|---|---| | med `role="button"` (återinlagt i DOM) | **84px** | 56px | 14px/14px | 56px | | utan (detta PR) | **56px** | 0px | 0px | 56px | Alla 53 synliga knappar på `/app/admin/health` och alla 54 på `/app/farms` mäter exakt 56px, `wa-button[role]` = 0. Dialogsurface kontrollerad (`Lägg till ny parameter` → footer-knapparna Cancel/Create Parameter: värd 56px, base 56px, ingen värdpadding). Normalläge oförändrat (base 38/43px mot `--tap-target-min: 24px`). Temaklasserna `wa-theme-mellow wa-palette-natural` ligger på plats och formkontrollerna är styled — ingen FAS06-regression. ## Rättelse: paddingen var ÄKTA träffyta, inte död yta En tidigare version av denna PR-text påstod att den borttagna paddingen var "död". **Det stämmer inte.** Kontrollerat experiment (isolerad `wa-button`, `position:fixed`, inget överlappande): ``` paddingband 14px ovanför den synliga knappen: document.elementFromPoint(x, hostTop+4) → wa-button (hitIsHost: true) klickhanterare utlöst → true ``` Svelte binder `onclick` på värdelementet och padding ingår i elementets träffbox. Attributet skapade alltså en **osynlig träffyta utanför den synliga kontrollen** — 84px klickyta runt en 56px-knapp. Att ta bort det låter träffytan sammanfalla med det användaren ser, vilket är rätt i handskläge där den synliga knappen redan är 56px. Konsekvensen för outdoor-läget är däremot verklig och spåras nu — se nedan. ## Outdoor-läget → spårat som B-165 `.outdoor-mode` i `app.css` dimensionerar bara `[role="button"]` och saknar — till skillnad från `big-finger` och `barn-mode` — en `wa-button::part(base)`-motsvarighet. Uppmätt på `/app/farms` med `enlantis.outdoorMode=true`: | | värdhöjd (träffyta) | ::part(base) (synlig knapp) | |---|---|---| | om `role` fanns kvar | 67px | 43px | | utan (detta PR) | 43px | 43px | Outdoor-läget levererar alltså 43px i stället för lägets egen avsikt 56px — fortfarande långt över `--tap-target-min: 24px`, så ingen WCAG-överträdelse, och samma 43px som de tusentals WA-knappar som aldrig bar attributet redan fick. Det är en **redan existerande lucka som svepet blottar**, inte något svepet införde. Fixen (`.outdoor-mode wa-button::part(base) { … }`) hålls medvetet utanför detta PR: det är en app-övergripande stiländring som dessutom går förbi `TopBar.svelte`:s reset (rad 406–418, når bara light-DOM) och kan sträcka topbar-chromet. Kräver egen bred-viewport-verifiering. Spårat som **B-165** i `other/KNOWN_BUGS.md` med mätvärden och fix-förslag. ## Avgränsning - Rör **bara** `<wa-button>`. Riktiga `role="button"` på `div`/`tr`/`rect`/`g` (10 platser) är orörda, liksom två bortkommenterade rader i `routes/ap
  • b54759d Rätta B-153:s size="s"-slutsats + spåra outdoor-luckan som B-165 Jonas · 2026-07-29 · +45 −8
  • cd89058 Ta bort redundant role="button" på <wa-button> (185 platser) Jonas · 2026-07-29 · +146 −185

#182 Fix B-165: rörelsedetektering tolkar "ingen sensor" som kraftig rörelse

2026-07-29 · Jonas · 1 commits · 7 filer · +272 −9 · Buggfixar B-165

Detaljer
## Vad Båda C-07-rörelsedetektorerna (`uiMode.ts` storhandsläge, `outdoorMode.ts` utomhusläge) läste `acc.x ?? 0` och tolkade därmed **"ingen sensoravläsning"** som **"avläsningen 0"**. En webbläsare utan rörelsesensor levererar ändå en `devicemotion`-händelse, men med alla axlar `null` — W3C Device Orientation säger uttryckligen (SHOULD) att implementationer som aldrig kan ge data *bör* fyra av händelsen med null-attribut. `?? 0` gör då magnituden 0, och den här netto-av-gravitation-formeln förvandlar det till sitt nära **högsta** värde: | detektor | netto vid nollvektor | tröskel | |---|---|---| | `uiMode` | `\|0 − 9,81\| / 9,81` = **1,0 g** | 0,6 g | | `outdoorMode` | `\|0 − 9,81\|` = **9,81** | 3,0 | En maskin som inte *kan* röra sig lästes alltså som den kraftigaste rörelse som finns → `body.big-finger` (56 px tryckytor, 1,1× typsnitt) och/eller `body.outdoor-mode` slogs på app-brett utan användarinteraktion. Det befintliga skyddet `if (!acc) return` fångar bara fallet där hela *attributet* är `null` — inte ett objekt vars axlar är `null`, vilket är den form som faktiskt når hanteraren (verifierat live i Chrome: `wouldPassOldGuard: true`). ## Fix - Båda detektorerna kastar nu sampel där `x`, `y` och `z` alla är `null`/`undefined`. Testat mot `== null` och inte falsiness — `0` är ett giltigt mätvärde (fritt fall). - Guarden ligger **före** throttle-bokföringen, så ett tomt sampel inte äter platsen för ett riktigt som kommer 200 ms senare. - `outdoorMode` var den värre av de två: den **sparar** till `localStorage`, så ett falsklarm följde med mellan sessioner. - **Utöver den bokstavliga buggen:** `uiMode`:s glidande medelvärde saknade sampelgolv — medelvärdet av *ett* sampel *är* det samplet, och inget krymper fönstret tillbaka, så en enda stöt (även äkta, på riktig telefon) låste läget PÅ resten av sessionen. Det motsäger "ihållande rörelse" som filens egen header lovar. Nytt `MIN_SAMPLES = 10`. ⚠️ `MIN_SAMPLES` är en medveten beteendeändring utöver buggen: storhandsläget kräver nu ~10 s ihållande rörelse i stället för att slå till på första samplet. Fortfarande snabbare än de 30 s headern alltid beskrivit — men säg till om du hellre vill ha ett annat golv. ## Verifiering `check:ci` ✅ grön (frontend; backend hoppad — ingen Scala rörd). **Visuell verifiering i riktig Chrome** (Jonas Windows-maskin, `/app` + `/app/settings/accessibility`, renderade sidor granskade) — A/B på **samma hydrerade sida**, direkt efter varandra: | | body-klasser | radioknapp | brödtext | |---|---|---|---| | 60 nollvektor-händelser | *inga* | 24 px | 16 px | | 60 äkta rörelsesampel, direkt efter | `outdoor-mode big-finger` | 56 px | 17,6 px | Kontrollen är poängen: att de äkta samplen slog på båda lägena bevisar att lyssnarna var aktiva hela tiden, så "inget hände" på nollvektorerna är inte ett falskt negativt. Före fixen mättes trasiga läget till `wa-button` 56 px med `min-height: 56px` på shadow-parten. **Tester:** 7 nya fall som är röda på gammal kod och gröna på ny (4 i `uiMode.test.ts`, 3 i nya `outdoorMode.test.ts`). Hela sviten 2 936 tester / 168 filer grön. ## Om verkligt utfall på riktig hårdvara Ärlig sammanfattning: på just den här maskinen fyrar Chrome oftast **inte alls** (0 händelser på 45 s i ny flik). Men `big-finger` observerades ändå slå på **spontant en gång** vid en kall sidladdning, utan interaktion — engångshändelse som inte gick att framkalla på beställning (troligen en sensor-probe vars resultat cachas per renderer-process). Payloaden hann inte fångas. Slutledningen håller ändå: `enlantis.bigFinger` var osatt → läget var `auto`, och bara `applyBigFingerClass` rör klassen, driven enbart av `motionActive` — alltså levererades en riktig händelse med ett `acc`-*objekt* med null-axlar. Not: `enlantis.outdoorMode` var redan `'true'` i din browser när jag började. Jag återställde det till `'true'` för att inte tyst ändra din inställning — men det kan mycket väl vara rester av just den här buggen snarare än ett val d
  • c1f9bde Fix B-165: motion auto-detect reads "no sensor" as violent movement Jonas · 2026-07-29 · +272 −9

#183 Add HANDOFF_RUNDA2: fix-round handoff for the 28 open findings

2026-07-29 · Claude (AI) · 1 commits · 1 filer · +187 −0 · Buggfixar B-111 B-187

Detaljer
Docs-only: adds `other/HANDOFF_RUNDA2.md` — the handoff for the next fix thread, covering the 28 open findings (B-111 → B-187) from round-2 live testing. Contents: - **Dev rules** (English-only code, check:ci, mandatory visual verification, test at 390px **and with glove mode on**). - **⛔ Decisions blocked on Jonas** (B-176 batch-vs-animal naming, B-177 BCS default, B-182 desktop scope, B-184 grouping, B-186 help placement). - **5 root-cause clusters**, ordered so fixing the cause collapses the symptoms: 1. ⭐ Overflow gate is blind (B-175) — includes Jonas's directive: update → *prove it fails* on known cases → run app-wide → fix everything. 2. "Systemic" fixes that were never systemic (B-178 = B-074 regression; B-171 button drift; B-170 glove mode). 3. i18n leaks at three different levels (component / backend text / missing CI gate). 4. Print has no global base (B-142 → B-181). 5. Cold-start first impression (B-169 — the B-161 fix falls short). - **14-PR breakdown** with ordering and blockers marked. - **Full list of all 28 open entries**, grouped by area. - **Recurring structural patterns** worth fixing beyond the individual bugs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv --- _Generated by [Claude Code](https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv)_
  • 281c6a4 Add HANDOFF_RUNDA2: fix-round handoff for the 28 open findings (B-111..B-187) Claude (AI) · 2026-07-29 · +187 −0

#180 Log B-165→B-187: round-2 live testing (23 findings)

2026-07-29 · Claude (AI) · 17 commits · 1 filer · +617 −5 · Buggfixar B-185 B-186 B-187 B-184 B-182 B-183 B-181 B-179 B-180 B-175 B-177 B-178 B-074 B-176 B-174 B-172 B-173 B-171 B-170 B-169 B-161 B-168 B-167 B-166 B-165

Detaljer
Docs-only: appends B-165 through B-187 to `other/KNOWN_BUGS.md` (round-2 live testing, Jonas). No code changes. 612 lines added. **Systemic / highest impact** - **B-175** — ⚠️ the overflow CI gate has two blind spots: it scans only **12 hand-picked routes** and **never opens dialogs** (zero `.click()` calls). Includes Jonas's explicit directive: update the gate, *prove* it fails on known cases (B-175/B-179/B-155/B-140/B-170) before fixing them, then run it app-wide and fix everything. - **B-169** — `/today` + `/copilot` still slow on **cold start** despite B-161 being marked fixed (#174); post-fix build confirmed, so the fix falls short. First-impression priority. - **B-171** — grey `variant="default"` buttons contradict `DESIGN.md` (secondary = outline; no grey-fill tier exists). Measured: **350** occurrences + **1036** invalid `size="s"`. - **B-170** — glove mode forces 56px/fixed padding + 1.1× text with **zero** wrap guards → text overflows app-wide. - **B-178** — health-check dialog closes on animal select: **regression of the "systemic" B-074 fix**, which was never applied to this dialog (unguarded `onwa-hide`). Blocking. - **B-142/B-181** — no global print reset; page-level print CSS can't compensate (right margin bleeds). - **B-172/B-174/B-180** — hardcoded English in components *and* backend (nudge titles, copilot suggestions); `find-hardcoded-english.mjs` exists but isn't a CI gate. **Surface-level**: B-165 (incidents unstyled), B-166 (mobile count badges), B-167 (calendar buttons), B-168 (redundant legend), B-173 (keyboard hints on mobile), B-176/B-177 (health-check form), B-179 (table overflow), B-182 (menu opens expanded), B-183/B-187 (bottom-bar naming/shortcut), B-184 (flat settings list), B-185 (help locale pills), B-186 (help outside app shell). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv
  • 5ed4ef5 Log B-185/B-186/B-187: help locale pills, help outside app shell, remove bottom-bar shortcut Claude (AI) · 2026-07-29 · +67 −0
  • 1fb3a8a Log B-184: Settings is a flat 19-item list; propose sub-grouping Claude (AI) · 2026-07-29 · +29 −0
  • 47ffa64 Log B-182 + B-183: mobile menu should open fully collapsed; rename 'Nedre falt' to 'Anpassa mobilmeny' Claude (AI) · 2026-07-29 · +46 −0
  • 8f0da71 Log B-181: health-check print output lacks right margin (consequence of missing global print reset) Claude (AI) · 2026-07-29 · +28 −0
  • 7277001 Log B-179 + B-180: recent-checks table overflow, Farm Co-Pilot i18n/brand/sentinel; add overflow-gate directive to B-175 Claude (AI) · 2026-07-29 · +65 −0
  • 3efdd6e Log B-177 + B-178: number steppers with default 0; health-check dialog closes on select (B-074 regression) Claude (AI) · 2026-07-29 · +64 −5
  • 1c9cf1e Log B-176: health-check form 'Objekt' rename (batch-support caveat) + unexplained required asterisk Claude (AI) · 2026-07-29 · +28 −0
  • 5c8ebd9 Log B-175: health-check dialog overflow + overflow gate blind spots (curated routes, never opens dialogs); fullscreen modal decision Claude (AI) · 2026-07-29 · +43 −0
  • 3768167 Log B-174: nudge titles + action labels hardcoded English in backend (LLM rephrase only covers body) Claude (AI) · 2026-07-29 · +27 −0
  • b45f665 Log B-172 + B-173: FarmAnalyticsSummary hardcoded English; command palette shows keyboard hints on mobile Claude (AI) · 2026-07-29 · +48 −0
  • a2cc85b Log B-171: grey variant=default buttons violate DESIGN.md CTA hierarchy + invalid size=s app-wide Claude (AI) · 2026-07-29 · +37 −0
  • 241f8af Log B-170: glove mode (big-finger) breaks button layout - text overflows, no wrap guards Claude (AI) · 2026-07-29 · +27 −0
  • 46b0ea1 Log B-169: Today/Copilot still slow on cold start despite B-161 fix (post-fix build confirmed) Claude (AI) · 2026-07-29 · +47 −0
  • 382ce0b Log B-168: calendar color-legend card is redundant with the filter chips Claude (AI) · 2026-07-29 · +13 −0
  • 47a44e1 Log B-167: calendar control buttons too tall/padded on mobile (filter chips + nav + view toggle) Claude (AI) · 2026-07-29 · +18 −0
  • b2dbe0a Log B-166: remove leftover count badges in mobile nav sheet (mockup remnants) Claude (AI) · 2026-07-29 · +12 −0
  • 8ea7910 Log B-165: incidents admin page unstyled (inert Tailwind classes) + hardcoded English string Claude (AI) · 2026-07-29 · +18 −0

#179 Nav icon-conversion audit: coverage guard + stale verify-tool fix (Enlantis-60 revival out of scope)

2026-07-29 · Jonas · 1 commits · 2 filer · +96 −1 · Funktioner

Detaljer
## Premise conflict — read this first The assigned task was "convert the nav to the Enlantis-60 custom icon set" (297 hand-drawn glyphs + 17 aliases, PR #69, merged 2026-07-15). That set **no longer exists in the app**: it was deliberately retired back to Font Awesome Free on **2026-07-18** (commit `055372f2`, decision record `docs/wiki/architecture/species-icons.md`): > "Every other UI icon is Font Awesome Free... The earlier hand-drawn 'enlantis' UI set (297 glyphs + 17 aliases, PR #69) was retired the same day... do not wire them into any surface without an explicit decision." `frontend/static/wa/icons/enlantis/` (the folder + manifest) is gone from disk; only review artifacts survive under `other/icon-style-proposals/enlantis-60/`. Reviving it would reverse a documented Jonas decision, so this PR does **not** do that. Consulted an independent reviewer before proceeding, who confirmed this reading and suggested the pivot below. ## What this PR actually does Audited the **current** nav (rail, bottom bar, "Mer" map, command palette, favourites panel, sitemap) for icon-coverage gaps and species-icon regressions, since the nav itself was rebuilt in PR #164–#178 (Nav-IA round 2) *after* the 2026-07-18 icon-strategy decision, and no test guarded that the two stayed in sync. **Result: zero gaps.** Every `icon:` value in `registry.ts`/`bottomBar.ts`/`search.ts`/`favourites.ts` and every hardcoded `name="..."` in the shell components (SideRail, BottomBar, MobileNavSheet, CommandPalette, TopBar, FavouritesSheet) already resolves under `static/wa/icons/` (`solid/` ∪ `brands/` ∪ `animals/`). Species-specific nodes already use `species-*` glyphs (cow, pig, chicken, duck, goose, turkey, llama, goat, horse, rabbit, sheep) — no regression from the MegaMenu retirement. ### Mapping table (bucket → current icon → source) | Bucket | Icon | Source | |---|---|---| | Idag / Planering | `calendar` | Font Awesome | | Djur | `paw` | Font Awesome | | Hälsa | `heart-pulse` | Font Awesome | | Foder | `wheat-awn` | Font Awesome | | Produktion & mark | `industry` | Font Awesome | | Efterlevnad & journaler | `scale-balanced` | Font Awesome | | Föreskrifter och regler | `book-open` | Font Awesome | | Ekonomi & drift | `sack-dollar` | Font Awesome | | Enheter | `microchip` | Font Awesome | | AI-copilot | `robot` | Font Awesome | | Inställningar | `gear` | Font Awesome | | Djur → per-species leaves | `species-cow`/`-pig`/`-chicken`/`-duck`/`-goose`/`-turkey`/`-llama`/`-goat`/`-horse`/`-rabbit`/`-sheep` | Fluent Emoji High Contrast + MDI (`animals/`) | | Bee production | `beehive` | MDI (`animals/`) — bee-as-colony house rule from the decision record | ### Unmatched candidates (from the retired 297-glyph set) Even the full retired Enlantis-60 set never covered `industry`, `book-open`, `ellipsis`, or any species name beyond cow/horse/pig — so a complete conversion was never possible without drawing new art. This is itself evidence the 2026-07-18 rollback was the right call, not just a stopgap. ### What shipped - **`frontend/src/lib/nav/iconCoverage.test.ts`** (new): permanent regression guard. The existing `frontend/scripts/waIconCoverage.test.mjs` only checks literal `<wa-icon name="...">` tags against `solid/`+`brands/` — it can't see dynamic `name={node.icon}` bindings sourced from nav data, and doesn't know about `animals/`. This test imports `registry`/`FARM_BUCKETS`/`NON_FARM_SECTIONS`/`SLOT_META` directly and checks every icon name against `solid/` ∪ `brands/` ∪ `animals/`. Verified it actually fails (injected a bogus icon name, confirmed red, reverted). - **`other/fas06-01-genomgang/verktyg/nav-e2-map.mjs`** (fix): still clicked the retired `#mega-menu-trigger-bb` id from the pre-round-2 MegaMenu. Found while reusing this harness for visual verification — the "Mer" trigger is now `#nav-more-trigger-bb` (`BottomBar.svelte`). Fixed so this tool is usable again for future nav work. ### Found but NOT fixed here (flagged as a separate follow-up
  • 49c7fce Nav icon-conversion audit: add coverage guard, fix stale verify selector Jonas · 2026-07-29 · +96 −1

#178 Nav E3: pensioneringen — retire the MegaMenu, the seven work-modes, and bucketState.ts

2026-07-29 · Jonas · 5 commits · 32 filer · +352 −2 950 · Tester

Detaljer
## Summary ETAPP E3 of the Nav-IA runda 2 rebuild — now that the rail (E1) and the mobile map/favourites/bottom-bar (E2) are the only nav surfaces, this PR deletes the MegaMenu, the seven work-modes, and their supporting infrastructure per `docs/superpowers/specs/2026-07-26-nav-ia-runda-2-design.md` (§ Pensioneras/flyttas, § Steg E3). ## What was deleted, by commit 1. **`MegaMenu.svelte`** (1 021 lines + `.mm-*` styles) — the hamburger is now the rail's unconditional collapse/expand toggle (no MegaMenu fallback); `BottomBar`'s "More" always opens the mobile nav sheet; `sideRailEnabled`/`mobileSheetEnabled` opt-out flags + their settings-page toggle removed (`AppShell`'s rail always renders when a `rail` snippet is given). 2. **The seven work-modes** — `appModeStore.ts` + `modeMetrics.ts` deleted outright; `registry.ts`'s `MODE_ALLOWLIST`/`MODE_UNIVERSAL`/ `modeMenuEntries`/`modeHomeHref` deleted + `registry.mode.test.ts` removed. **Deviation from the spec's assumption** ("exactly three components read the mode: MegaMenu, MobileNavSheet, TopBar"): grep found three more real consumers — the dashboard's per-mode starter layout (`widget-registry.ts`/`dashboard-api.ts`/`DashboardGrid.svelte`), the first-week "Kom igång" checklist (`gettingStartedItems.ts`/ `GettingStartedPanel.svelte`), and `/welcome`'s step-5 "pick your working lens" finale. All three are genuine product surfaces, not menu chrome, so they were **adapted to a fixed non-mode default** (the old `'barn'` content, since `'barn'` was `DEFAULT_APP_MODE`) rather than deleted. `/welcome` step 5's mode-grid is replaced by a single "Go to my farm" CTA (reuses the existing `welcome.done.enter.button` copy) — deliberately *not* wired through the existing `finish()` helper, which re-POSTs `/api/onboarding/complete` with `preferences:null` and would wipe the prefs step 4 already stamped. 3. **`bucketState.ts`** (`openFarmBuckets`/multi-open persisted map) — its only consumer was the MegaMenu; the rail/mobile map already use `bucketAccordion.ts`'s derived single-open state (no persistence). 4. **i18n cleanup** — 51 dead keys removed (the whole `mode.mjs` shard, seven `nav.megaMenu.*` MegaMenu-only strings, the retired opt-out toggle copy, `welcome.mode.*`, and six retired work-mode `getstarted.item.*` pairs). Every removal was grep-verified against actual `$t()` usage first, not guessed from the name prefix — `nav.megaMenu.selectFarmHint`/ `.noPagesForRole`/`.pin`/`.unpin` looked MegaMenu-only but are still read by `MobileNavSheet`/`FavouritesMenu` and were kept. ## Check:ci ✅ `SKIP_BACKEND=1 npm run check:ci` green (no `.scala` file touched): svelte-check, eslint, 2 924 vitest tests (166 files), i18n coverage guard, WA boolean-attr guard, a11y/contrast guard, token-drift guard, layout-overflow guard (Playwright, 48 route×viewport combos). ## Visual verification Playwright + network-level API mock + `?dev_user=` bypass (headless Chromium, mirroring `check-overflow.mjs`'s harness) — screenshots taken and read, not just captured: - **Desktop 1280px**, `/app/farms/farm-1`: rail always renders (no MegaMenu anywhere in the DOM), hamburger click visibly collapses/expands the rail, ⭐ Favourites chip present in the TopBar, Ctrl-K/search opens the command palette in browse mode (Mest använda / Senaste). - **Desktop 1280px**, `/app/sitemap`: webbkarta renders the full catalog. - **Mobile 390×844**, `/app/farms/farm-1`: bottom bar is Idag/Favoriter/Skanna/Sök/Mer; "Mer" opens the mobile nav sheet (GÅRD/ STALL/🧤 rows + bucket list, not a MegaMenu); the 🧤 Handskläge row toggles `body.barn-mode` and the sheet's rows visibly grow; ⭐ opens the favourites tile panel (📌 Fästa 2×2 + Mest använda row). - **`/welcome` step 5**: no `.mode-grid`/`.mode-card` picker; a single "Gå till min gård" CTA that navigates to the farm start page. ## Not done (flagging, not silently skipping) The spe
  • 6d554a1 Nav E3: tick off the spec's pensioneringen checklist Jonas · 2026-07-29 · +45 −11
  • 5a7db39 Nav E3: i18n cleanup — drop 51 MegaMenu/work-mode keys Jonas · 2026-07-29 · +54 −425
  • dd8243f Nav E3: retire bucketState.ts (MegaMenu's multi-open bucket map) Jonas · 2026-07-29 · +4 −118
  • 140c1e2 Nav E3: retire the seven work-modes Jonas · 2026-07-29 · +158 −1 184
  • f38e314 Nav E3: retire the MegaMenu (1021 lines + .mm-* styles) Jonas · 2026-07-29 · +91 −1 212

#177 Nav-IA E2-P4: ⭐ Favoriter tar lägeschippets plats; paletten speglar favoriterna

2026-07-29 · Jonas · 6 commits · 9 filer · +603 −352 · Buggfixar

Detaljer
## Sammanfattning Implementerar ETAPP E2-P4 ur `docs/superpowers/plans/2026-07-29-nav-e2-mobil-favoriter.md`: den delade favoritpanelkroppen, TopBars ⭐-chip som ersätter det pensionerade lägeschippet, och kommandopalettens tomläge som speglar favoriterna. ## Per task **Task 4.1 — `FavouritesMenu.svelte`, den delade panelkroppen** - Ny `frontend/src/lib/components/shell/FavouritesMenu.svelte`: äger 📌 Fästa (2×2, fyra stabila platser), Mest använda (hscroll med peek), tomlägena, pin/lossa-knapparna och "Anpassa favoriter…"-foten. Tar en `onNavigate`-callback; ingen egen `<wa-drawer>`/`<wa-dropdown>`. - `FavouritesSheet.svelte` (P2) skrivs om till att bara äga drawer-kromet och konsumera den nya komponenten. Klassnamnen (`.fav__grid`, `.fav__tile`, `.fav__hscroll` m.fl.) är oförändrade eftersom P2:s Playwright-harness redan asserterar på dem. **Task 4.2 — TopBar: lägeschippet ersätts av `⭐ Favoriter`** - `switchMode`, `appModeStore`/`APP_MODES`/`MODE_ICON`/`modeHomeHref`-importerna och `farmRoleStore` (icke-actual) borttagna. `actualFarmRoleStore` kvar (används av `canViewAs`). - Nytt `⭐ Favoriter ▾`-chip i TopBars högergrupp, dropdown med `FavouritesMenu`. **Task 4.3 — Palettens tomläge speglar favoriterna** - `CommandPalette.svelte`: vid tom sökning visas nu Fästa → Mest använda → Senaste, var sektion med egen rubrik (`.cp__sectionhead`, samma klass som redan fanns för "Senaste"). Tangentnavigering (↑↓/↵) går över den sammanslagna, renderade listan (`visibleRows`), inte bara `recentEntries`. **Task 4.4 — check:ci + visuell verifiering** - `check:ci` (`SKIP_BACKEND=1`) grönt (se nedan). - `other/fas06-01-genomgang/verktyg/nav-e2-field.mjs` utökad med desktop-checkar (@1280/@900) för chippet, dropdown-ordning och palettens tomläge, plus en @390-regressionscheck. 25/25 checkar gröna. ## Avvikelser mot planen 1. **`sheet__sectionLabel` finns inte längre** (P3 skrev om `MobileNavSheet` till Mer-kartan och tog bort klassen) — sektionsrubrikerna i paletten återanvänder `CommandPalette`s egen `.cp__sectionhead` istället. 2. **`slot="prefix"`-bugg, hittad i den obligatoriska visuella verifieringen.** Planens egen kodsnutt (kopierad från den gamla lägesväljaren) satte `<wa-icon slot="prefix">` på chippets ikon. Den självhostade Web Awesome-byggen definierar bara `start`/`end` som namngivna slots på `wa-button` (inte `prefix`/`suffix` — Shoelace-namngivning som inte finns här), så ikonen slottades aldrig och renderade 0×0 (osynlig, ingen konsolvarning). Fixat till `slot="start"`. Samma bugg fanns redan i den gamla lägesväljaren; andra TopBar-dropdowns med samma mönster (gård-/stallväljaren, användarmenyn) rörs INTE här — flaggat som uppföljning. 3. **"Försvinner ALDRIG responsivt" omtolkat efter en riktig regression.** Första varvet höll chippet synligt (ikon-only) ner till 0px. `check:ci`s `overflow:check` (multi-gård/-stall-fixture) slog larm: chippet tryckte gård/stall-brödsmulan 7px utanför sin container @390px — `nav-e2-field.mjs`s egen en-gård-fixture missade detta helt. Designspecen skopar faktiskt kravet till "TopBar (desktop)" — mobilen har redan sin egen ⭐-plats i bottenfältet (P2). Chippet döljs nu <768px precis som resten av TopBar-kromet vid den brytpunkten; "aldrig dold" gäller ner till 768px, inte till 0. ## check:ci ✅ check:ci grönt (frontend, `SKIP_BACKEND=1` — ingen `.scala` rörd i denna etapp). Två varv: första rött på `overflow:check` (avvikelse 3 ovan), fixat, andra grönt (svelte-check, eslint, vitest, i18n:check, wa:check, a11y:check, tokens:check, overflow:check — 48 rutt/breddkombinationer, 0 fel). ## Visuell verifiering Playwright mot `bun run dev` (mockApi + `?dev_user=`), skärmdumpar TAGNA OCH LÄSTA: - TopBar @1280: ⭐ Favoriter-chip synligt med etikett, ingen lägesväljare kvar. - TopBar @900: chippet kollapsat till ikon (etikett dold, knappen aldrig `display:none`). - TopBar @390: chippet dolt (desktop-only; bottenfältets egen ⭐-plats täcker mobilen). - Dropdownen öppen: Fästa (2×2, tomma streckade
  • de749f5 Nav-IA E2-P4: tick off the plan tasks; check:ci green Jonas · 2026-07-29 · +54 −14
  • 46ae824 Nav-IA E2-P4: overflow:check catches a real regression — the favchip is desktop-only, not "never hidden at any width" Jonas · 2026-07-29 · +35 −10
  • c5e21ad Nav-IA E2-P4: visual verification catches a real bug — favchip icon slot="prefix" is invisible Jonas · 2026-07-29 · +84 −4
  • d5068dd Nav-IA E2-P4: the palette's empty state mirrors the favourites (pinned first), then Recent Jonas · 2026-07-29 · +84 −23
  • 07cbd99 Nav-IA E2-P4: ⭐ Favourites chip replaces the work-mode switcher (never responsively hidden) Jonas · 2026-07-29 · +46 −42
  • 6a723ad Nav-IA E2-P4: extract the shared favourites panel body (sheet + dropdown) Jonas · 2026-07-29 · +300 −259

#176 Nav-IA E2-P5: Handskläget (Blad H) — --touch-scale on barn-mode

2026-07-29 · Jonas · 4 commits · 10 filer · +361 −102 · Funktioner

Detaljer
## Summary Etapp E2-P5 from `docs/superpowers/plans/2026-07-29-nav-e2-mobil-favoriter.md` — the glove-mode ("Handskläge") scale-up. Base is latest master (E2-P1/P2/P3 + E1-P4 already merged). **Mechanism (Task 5.1):** a single `--touch-scale` CSS variable — `1` at `:root`, `1.28` under the *existing* `body.barn-mode` class (no new mode mechanism). Every nav surface's touch targets scale from it via `calc(base * var(--touch-scale))`: - `BottomBar.svelte`: bar height, item font-size, icon font-size, scan-disc width/height/position/font-size; labels also go bold in barn-mode. - `MobileNavSheet.svelte`: `.map__row` (GÅRD/STALL/glove/home rows), `.sheet__bucketHead`, `.sheet__leaf`, `.sheet__close` — extended beyond the plan's two named selectors to keep the map's own comment ("same card treatment ... so the map reads as one system") true once glove mode is on. - `FavouritesSheet.svelte`: `.fav__tile` (pinned + auto row) and `.fav__tileIcon` — this is the file that plan called `FavouritesMenu.svelte` (see Deviations below). **Switches (Task 5.2):** glove mode now has exactly three explicit toggles — the map's 🧤 row (P3), the existing one-time prompt in `app/+layout.svelte`, and a new toggle in Inställningar/Tillgänglighet — and the Phase-4 auto-trigger that used to flip `barnMode` off `appModeStore.setMode('barn')` is deleted (only that one line, per the plan's Global Constraints). Copy pass: relabelled `nav.barnMode.*` from "Barn mode"/"Ladugårdsläge" to "Glove mode"/"Handskläge" to match the map row's existing wording. TopBar's `.iconbtn--barn` is hidden `<768px` (Q8 default) — it was already hidden there by long-standing CSS; documented that as the intentional Q8 decision rather than leaving it as an unexplained accident. **Verification (Task 5.3):** `nav-e2-map.mjs` extended with 13 new checks (39/39 total green) plus side-by-side off/on screenshots. ## Deviations from the plan (code wins, per instructions) 1. **`FavouritesMenu.svelte` doesn't exist yet.** E2-P4 (the shared TopBar-chip/palette panel) hasn't landed — the real favourites panel today is `FavouritesSheet.svelte` (from E2-P2), so tile scaling landed there instead. 2. **TopBar's `.iconbtn--barn`** was already `display:none` below 768px via pre-existing CSS (predates this plan, unrelated original reason). Split it into its own rule with a comment recording it as the Q8 default rather than an accident — this was my one TopBar change, as instructed. 3. Extended scaling to `.map__row` and `.sheet__close` beyond the plan's two named MobileNavSheet selectors, for visual consistency with the rows the plan did name (same reasoning the existing code comment already states). ## Measured pixel values (Task 5.3 Step 3) | Surface | Spec table | Base × 1.28 (this codebase's actual base) | Measured | |---|---|---|---| | `.sheet__leaf` height | 56→72 | 56×1.28=71.7 | **71.67px** | | `.bb__item` height | 56→72 | base is 64 not 56 → 64×1.28=81.9 | **76.9px** (>=70 floor met) | | `.bb__item--scan .bb__icon` width | 46→60 | 46×1.28=58.9 | **58.9px** | | `.fav__tile` height | not in table (~96 in prototype) | 84×1.28=107.5 | **107.5px** | Per the plan's Q6 default: one variable, not four hand-tuned constants — a future pixel-exact pass is a one-line change to `--touch-scale`. ## check:ci `SKIP_BACKEND=1 npm run check:ci` — ✅ green (svelte-check, eslint, vitest 2979 tests, i18n coverage, WA boolean-attr guard, a11y contrast, token-drift, overflow guard). Backend skipped (Windows dev box has no sbt in this worktree; per CLAUDE.md the agent sandbox gates it — not applicable here). Re-ran via the pre-push hook on `git push`, which also passed. ## Visual verification (mandatory, done in the CCR-style sandbox via Playwright + mockApi + `?dev_user=`, `bun run dev`) All four required surfaces rendered at 390×844, screenshots taken and read, off/on side by side: - **Bottom bar** — off vs on: labels visibly larger and bold, icons bigger, bar taller. - **⭐ favourites tiles** — off vs
  • 485b1d4 Nav-IA E2-P5: check off the plan's Handskläget tasks (5.1-5.3) Jonas · 2026-07-29 · +14 −14
  • 52d9654 Nav-IA E2-P5: extend the map harness for --touch-scale (Blad H) Jonas · 2026-07-29 · +161 −1
  • 5263b8a Nav-IA E2-P5: glove mode has explicit switches only — the work-mode auto-trigger is gone Jonas · 2026-07-29 · +128 −67
  • ef99d3a Nav-IA E2-P5: --touch-scale (1.28) on barn-mode; nav surfaces scale from one variable Jonas · 2026-07-29 · +58 −20

#175 Nav-IA E2-P2: bottenfältet blir Idag · ⭐ · Skanna · Sök · Mer + favoritpanelen

2026-07-29 · Jonas · 8 commits · 9 filer · +792 −129 · Funktioner

Detaljer
## Summary Implements ETAPP E2-P2 of `docs/superpowers/plans/2026-07-29-nav-e2-mobil-favoriter.md` (follows E2-P1 favourites module / PR #171, E2-P3 Mer-map / PR #172, and P4/TopBar cluster / PR #173, all already on master). - **Task 2.1** — `favouritesSheetOpen` overlay state in `uiStore.ts` (`openFavouritesSheet` closes the Mer sheet first, same class of overlay as `mobileNavSheetOpen`); wired into `app/+layout.svelte`'s `afterNavigate` blanket dismissal. - **Task 2.2** — `bottomBar.ts`: new `DEFAULT_SLOTS = ['today','favourites','scan','search','more']`, new `favourites` slot (`SLOT_META.favourites` resolves `{ kind: 'favourites' }`), new `SlotResolution` kind. `MODE_DEFAULT_SLOTS` / `defaultSlotsForMode` are deleted and `bottomBarSlotsStore` drops its `appModeStore.subscribe` — the field is no longer a lens on the active work mode. `home` / `animals` / `health` / `dashboard` etc. stay in `SLOT_META` / `ALL_SLOT_IDS`, just out of the default. `settings/bottom-bar/+page.svelte` updated to match (`getAppMode` import removed). `bottomBar.test.ts`: read all 21 existing tests first per the plan's instruction — 6 asserted `MODE_DEFAULT_SLOTS`/`defaultSlotsForMode`/mode-scoped `getSlots` directly and were removed/updated in this commit (2 adminOnly tests that depended on `dashboard` being in `DEFAULT_SLOTS` were rewritten against a literal custom config instead, since `dashboard` left the default). Added the plan's new "Nav-IA runda 2" describe block. 22 tests green. - **Task 2.3** — `FavouritesSheet.svelte` (new): `wa-drawer` bottom sheet, `--size: auto; max-height: 70dvh`, 📌 Fästa as a stable 2×2 grid (empty slots are dashed placeholders, never collapse), "Mest använda" as a horizontally-scrolling card row with a deliberate peek (`flex: 0 0 30%` + `scroll-padding-inline-end`), "Anpassa favoriter…" → `/app/settings/favourites`, full-width footer close button. Each tile is an `<a>`; pin/unpin is a separate `<button aria-pressed>` (44×44px) in the corner. Rendered in `app/+layout.svelte` next to `<MobileNavSheet/>`. - **Task 2.4** — `BottomBar.svelte`: ⭐ slot opens the favourites panel; `anyOverlayOpen` now includes `$favouritesSheetOpen`; generalised `RenderedSlot.ariaExpanded/ariaControls` so the ⭐ button's aria-expanded/-controls track `#favourites-sheet` independently from the "Mer" button's `#mobile-nav-sheet`/`#mega-menu-panel` (previously a single `menuSurfaceOpen` boolean served both, which would have reported the wrong state on one of the two once a second non-navigate slot existed). `TopBar.svelte`: mobile magnifier button and comment updated. **Deviation from the plan text** (noted per the task's "koden vinner" instruction): the plan assumed `.iconbtn--msearch` was purely mobile-only and said to delete it outright. It also carries the B-121 overflow-gate rule (`max-width: 1023px`), which is the *only* search entry point in the 768–1023px tablet band — at that width the centre search pill is hidden (`max-width: 1023px`) **and** the BottomBar itself is `display:none` (`AppShell.svelte`, `min-width: 768px`). Deleting the icon outright would have removed search entirely for tablets. Kept the markup + the 1023px rule, and only hid it at `max-width: 767px` (where the field's own `Sök` slot now covers it) — see the comment left at the button and in the CSS. - **Task 2.5** — new Playwright harness `other/fas06-01-genomgang/verktyg/nav-e2-field.mjs` (390×844, mockApi + `?dev_user=`, modelled on `nav-e2-map.mjs`): field order, ⭐ opens `#favourites-sheet` not `#mobile-nav-sheet`, the pinned grid always renders 4 slots, the auto row overflows horizontally (peek), the bar is `pointer-events: none` while the ⭐ panel is open, Sök opens the palette, no mobile magnifier <768px, no horizontal overflow @390 (panel open and closed), desktop (≥768px) unaffected. **16/16 checks pass.** - **Task 2.6** — `check:ci` green + full visual verifica
  • 4709fcd Nav-IA E2-P2: tick off the plan tasks; check:ci green Jonas · 2026-07-29 · +22 −22
  • f1f6429 Nav-IA E2-P2: visual verification — /app/settings/bottom-bar still lets Hem back in Jonas · 2026-07-29 · +0 −0
  • 6a8b44e Nav-IA E2-P2: visual verification screenshots for the field + favourites panel Jonas · 2026-07-29 · +0 −0
  • 0560046 Nav-IA E2-P2: Playwright harness for the new field + favourites panel Jonas · 2026-07-29 · +245 −0
  • e349dd6 Nav-IA E2-P2: ⭐ slot opens the favourites panel; the mobile magnifier leaves the TopBar Jonas · 2026-07-29 · +60 −27
  • 8c9fd26 Nav-IA E2-P2: the ⭐ tile panel — 2x2 pinned + horizontally scrolling most-used Jonas · 2026-07-29 · +371 −0
  • f449ea1 Nav-IA E2-P2: new default field (Idag · ⭐ · Skanna · Sök · Mer); bottom bar drops the work-mode lens Jonas · 2026-07-29 · +73 −79
  • 3cfb9a2 Nav-IA E2-P2: favouritesSheetOpen overlay state + navigation dismissal Jonas · 2026-07-29 · +21 −1

#174 Fix K5 AI bug cluster: B-133, B-134, B-143, B-161 (locale + perf + copilot wiring)

2026-07-29 · Jonas · 6 commits · 6 filer · +601 −242 · Buggfixar B-133 B-134 B-143 B-161

Detaljer
## Summary Four AI-related bugs (bugglogg-kluster K5): - **B-133** — "Dagens sammanfattning" (copilot daily summary) alltid på engelska trots vald svenska. Fix: `today/+page.svelte` skickar nu alltid `?locale=` via en ny delad hämtare (`frontend/src/lib/copilot/nudges.ts`). - **B-143** — Prioriteringar: AI-Copilot-nudges på engelska trots vald svenska (samma rotorsak som B-133, annat anropsställe). Fix: routade `priorities/+page.svelte` genom samma delade hämtare; grepp efter alla `copilot/nudges`-anropsställen fångade även `/copilot`-tavlan och dashboard-widgeten `NudgesWidget`, som fick samma fix. Upptäckte och fixade även att prioriteringspanelen visade `n.body` (alltid engelsk) i stället för `n.rephrased` (den faktiskt locale-medvetna texten). - **B-161** — "Idag"-listan tog ~1 minut att ladda (LLM-berikning synkront, ingen klient-cache). Fix: backend accepterar `?enrich=false` för att hoppa över LLM-rephrase + daily-summary-anropet; frontend hämtar snabb/oberikad lista först (renderas <1s i test) och den fullt berikade listan i bakgrunden, plus stale-while-revalidate-cache i sessionStorage. - **B-134** — Dashboard-copiloten (`/dashboard/copilot`) var en helt död stubbe (inga onclick-handlers alls). Fix: kopplade in riktig chat-state mot samma befintliga `/api/farms/:id/chat`-endpoint som den fungerande gårds-copilot-FAB:en redan använder. Mikrofonknappen togs bort (medvetet scope-beslut — röstinput är en separat funktion). Fixade även den rapporterade horisontella overflow:en. ## check:ci ✅ check:ci green (frontend + backend) — both the in-session run and the pre-push hook's automatic re-run passed all 9 gates, including `sbt test` (backend touched: `CopilotService.scala`, `CopilotRoutes.scala`, two test fakes). ## Visuell verifiering Playwright mot `bun run dev` (mockat API, `?dev_user=`-bypass), enligt `other/fas06-01-genomgang/verktyg`-mönstret. Körd med en konstgjord 2.5s fördröjning på den berikade `/copilot/nudges`-varianten för att bevisa den icke-blockerande laddningen. - **`/app/farms/farm-1/today`** — bas-nudge-listan renderas på <1s (mätt: ~750–870ms), långt innan den 2.5s LLM-fördröjningen är klar. Efter berikning: "DAGENS SAMMANFATTNING"-panelen visar svensk text ("God morgon. Ett djur behöver uppmärksamhet idag — CDB-anmälan har fastnat."), nudge-brödtexten byts till den svenska LLM-omskrivningen. Skärmdumpar tagna och granskade i båda lägena (snabb målning + berikad). - **`/app/farms/farm-1/priorities`** — AI-Copilot-kortet visar svensk text efter berikning ("CDB-anmälan för födsel har fastnat i tre dagar — kontrollera anslutningen."). Skärmdump granskad. - **`/app/farms/farm-1/dashboard/copilot`** (root-admin-gatad sedan B-054, mockad öppen i testet) — tomt läge visar riktigt UI (inte längre stubben); klick på förslagsknapp + fritextmeddelande + Enter skickar `POST /chat` och renderar chattbubblor med tidsstämpel. Ingen horisontell overflow (`scrollWidth` ≤ `clientWidth` på både chattcontainer och body). Computed-style-kontroll bekräftar `wa-theme-mellow wa-palette-natural` på `<html>` och en riktig `1px solid`-kant på inputets shadow-DOM-bas (inte `0px none`). 14/14 automatiserade repro-checks gröna. Skärmdumpar togs och lästes (temp-skript + skärmdumpar borttagna efter granskning per konventionen). ## Ogjort / avgränsat - B-134: mikrofonknappen är medvetet borttagen, inte wire:ad — röstinput kräver en egen inspelnings-/transkriberingsflöde (separat funktion). - B-143/B-133: de råa v1-regeltitlarna/-texterna (`n.title`/`n.body`, t.ex. "I&R submission stuck (birth)") förblir hårdkodad engelska oavsett locale — det är by design i `CopilotService.scala` (bara LLM-`rephrased`-lagret är locale-medvetet), samma konvention som redan fanns på `/today` och `/copilot`-tavlan. Att i18n:a alla regelsträngarna i backend är utanför den här buggens minimala scope. - B-161: sido-obs `[svelte] derived_inert`-konsolvarningen på `/today` är en separat reaktivitetsbugg, ej rörd. 🤖 Generated with [Claude Code](htt
  • 0c6946a Fill in real PR number (#174) for B-133/B-134/B-143/B-161 Jonas · 2026-07-29 · +8 −8
  • aa71781 Log B-133, B-134, B-143, B-161 as fixed in KNOWN_BUGS.md Jonas · 2026-07-29 · +87 −22
  • 4690ddb Fix B-134: wire the dashboard-copilot stub to the real chat backend Jonas · 2026-07-28 · +257 −74
  • 842bf71 Fix B-143: send ?locale= on every remaining /copilot/nudges call site Jonas · 2026-07-28 · +46 −61
  • 5781995 Fix B-133 + B-161: locale-aware, progressively-loaded /today nudges Jonas · 2026-07-28 · +182 −69
  • 0af5367 Fix B-161: skippable LLM enrichment on /copilot/nudges Jonas · 2026-07-28 · +21 −8

#173 Nav-IA P4 + TopBar bug cluster: rail hamburger, B-129/B-157/B-158

2026-07-29 · Jonas · 6 commits · 10 filer · +780 −163 · Buggfixar B-129 B-157 B-158

Detaljer
## Summary Picks up a half-finished session (crashed agent, 4 commits already on the branch) that covered the plan's P4 section plus the TopBar bug cluster **B-129, B-157, B-158**, merges `origin/master` (which had moved on with PR #172's farm/house context switcher and a MobileNavSheet rewrite), fixes a real regression the merge exposed, and closes out the docs. - **P4** (`docs/superpowers/plans/2026-07-26-nav-e1-rals-overallt.md`) — the topbar hamburger now toggles the rail's collapsed state when `sideRailEnabled`, and falls back to opening the MegaMenu when a keeper has opted out. Verification harness extended (`nav-e1-verify.mjs`, 34/34). - **B-157** — the `SE SV` locale chip is gone from the TopBar; language picking moved to a new `/app/settings/language` page (`global.settings.language` nav node), reusing `LocalePickerCompact` as-is. - **B-158** — the bell is now a real `NotificationsDropdown` (light-dismiss panel, Escape + click-outside) built from the existing `dashboard/aggregations/{tasks,welfare}` endpoints (no dedicated notifications table exists), with a gear linking to `/me/notifications` (also de-hardcoded from English to `$t()`). - **B-129** — moot after B-157: the locale chip was the only square interactive control in the right icon group; every remaining control is already the 999px pill shape. No new CSS needed — confirmed by measurement, not assumption (see below). ## Merge conflict resolution `git fetch origin && git merge origin/master` conflicted in three files, all from parallel, non-overlapping i18n-key additions (this branch's `notificationsDropdown.*`/`notificationsPrefs.*`/`nav.settings.language` keys vs. master's PR #172 `nav.house.*`/`nav.map.*` keys): - `frontend/scripts/i18n/keys/nav.mjs` — kept both key blocks. - `frontend/scripts/i18n/keys/_order.mjs` — kept both ordering blocks. - `frontend/src/lib/i18n/coverage.json` — took ours, then regenerated with `npm run i18n:build` (also regenerated `en.json`/`sv.json`, which came out byte-identical to the merge result — no drift). `TopBar.svelte` and `MobileNavSheet.svelte` auto-merged cleanly; verified by hand that both sides' features coexist post-merge (PR #172's farm/house breadcrumb + this branch's hamburger/notifications/no-locale-chip). ## A real regression found and fixed (not pre-existing) `SKIP_BACKEND=1 npm run check:ci` failed after the merge on `layout-overflow guard`: the farm/house context breadcrumb overflowed its container by 8.5px at 390px on every `/app/farms*` route. Verified against a clean checkout of `origin/master` in a scratch worktree first — **0 violations there** — so this was introduced by the merge, not pre-existing. Root cause: B-158 changed the bell from `<a class="iconbtn">` to a real `<button>` (needed for `onclick`/`aria-expanded`). `body.big-finger button` (the 56px min-width floor for grov-motorik/big-finger mode, `app.css`) matches `<button>` but never matched the old `<a>`, so the bell silently grew from 36px to 56px whenever big-finger mode is active, squeezing the shrinkable context breadcrumb. Fix: `data-compact` on the bell button — the already-documented opt-out attribute for icon-dense controls (`global.css`, "data-compact remains the documented opt-out") that no topbar button was actually using yet. `overflow:check` now passes 48/48 route+viewport combinations. ## check:ci ✅ check:ci green (frontend; backend `SKIP_BACKEND=1` — Windows dev box). Ran twice: once locally before push, once again automatically via the pre-push hook. ## Visual verification Playwright against `bun run dev -- --port 5193` with a network-level `/api` mock (mockApi + `?dev_user=` bypass), screenshots taken and read, not just saved: - Hamburger toggles `#app-rail`'s `rail--collapsed` class and flips `aria-expanded`/`aria-label`/`title` correctly. - Right-group icon controls (`iconbtn--barn`, sync badge, bell, avatar) all measured `border-radius: 999px`; none spill past the topbar's bot
  • a18bf22 Nav-IA P4 + B-129/B-157/B-158: tick off plan, mark KNOWN_BUGS fixed, regen i18n coverage Jonas · 2026-07-29 · +108 −66
  • 1b4b687 B-158 merge fix: restore data-compact on the notif bell (390px overflow regression) Jonas · 2026-07-29 · +1 −0
  • 3f9468b B-158: replace the topbar bell link with an actual notifications dropdown Jonas · 2026-07-29 · +480 −47
  • 268ae80 B-157: move the locale picker out of the topbar into Instaellningar > Sprak Jonas · 2026-07-29 · +118 −44
  • fd71912 Nav-IA P4: verification harness — hamburger toggle checks (both branches) Jonas · 2026-07-29 · +56 −0
  • fa14aa2 Nav-IA P4: hamburger toggles the rail when enabled, falls back to the MegaMenu when opted out Jonas · 2026-07-29 · +17 −6

#172 Nav-IA E2-P3: Mer-kartan — GÅRD/STALL/🧤/🏠 + bucketdragspel, "Hela gården" som default

2026-07-29 · Jonas · 7 commits · 8 filer · +1 190 −549 · Funktioner

Detaljer
## Summary Implements ETAPP E2-P3 (Mer-kartan) from `docs/superpowers/plans/2026-07-29-nav-e2-mobil-favoriter.md`. Independent of E2-P1 (favourites) per the plan — favourites do not appear in the map. ### Task 3.1 — "Hela gården" becomes a real, default scope - `selectedHouseStore.ts`: removed the two auto-select-first-house subscriptions and the `farmHouses[0]` fallback in `selectedHouse`. `null` (Hela gården) is now the real default and stays sticky until an explicit pick. Added `__resetForTests()`. - `animals/+page.svelte` splits `scopeHouse` (drives the list filter, badge, saved-report snapshot — `null` shows every house) from `createHouse` (drives the create-animal dialog, falling back to the farm's first house so the dialog can still propose areas instead of showing "no house" when houses actually exist). - New `frontend/src/lib/stores/selectedHouseStore.test.ts` — 8 tests. - **Regression found and fixed during Task 3.6 visual verification** (not in the original plan text): the farm-change subscription treated the *first* notification after every fresh module load (i.e. every full page reload) as a farm change, since `previousFarmId` always started at `null` while `selectedFarmId` typically already held the route's farm id from localStorage. That silently wiped a keeper's persisted house pick on every navigation — invisible before because the old auto-select fallback refilled it with *some* house. Fixed with an explicit "not yet seen a farmId" sentinel; added a dedicated regression test. ### Task 3.2 — the breadcrumb carries the house switcher - `TopBar.svelte`'s house dropdown now renders whenever the farm has ≥1 house (not gated on a selection existing), with "Hela gården" as the first, checked entry when scope is null. - `pickHouse(null)` is now valid. **B-081 reassessed**: the forced navigation to the animals list is removed — the scope is now visible in the breadcrumb itself (and, on mobile, in the map's STALL row), so the picker is no longer inert. - B-124's areas-page URL drill-down keeps its special case unchanged; picking Hela gården there drops the `?house=` param, landing on the existing all-houses overview (verified below). ### Task 3.3 — `mobileMap.ts` - `MOBILE_MAP_EXCLUDE` + `withoutFieldDestinations()`: filters `bucket.nodes` against the bottom bar's own destinations (`farm.today`, `global.movements.scan`) without mutating the shared `navBuckets` store. Applied only in the mobile map, never in `navContext.ts`. - 5 tests in `mobileMap.test.ts`. ### Task 3.4 — `MobileNavSheet.svelte` becomes the map Full rewrite: GÅRD (admin-only "byt") → STALL (always "byt", Hela gården ✓ default) → 🧤 Handskläge → 🏠 Gårdens startsida → bucket accordion (same combined order as the desktop rail: farm buckets, divider, admin/advisor/vet groups) → footer trimmed to "Nedre fält" + "Hjälp". Removed: the pinned/recent shortcuts block, the mode-chip row (+ its `appModeStore`/`APP_MODES`/`MODE_ICON`/`modeHomeHref` imports — E2's allowed mode cleanup), and the per-node `sheet__twist` toggle (subgroups now render as static headings + indented leaves, same shape as the rail). Reset-on-leave: `close()` and `onAfterHide` both clear `openBucketId` plus the local farm/house list disclosure flags. ### Task 3.5 — Playwright harness `other/fas06-01-genomgang/verktyg/nav-e2-map.mjs` — 25 checks at 390×844 (non-admin + admin sessions). All pass. Documented in the harness: a COLD dev-bypass session with a freshly wiped Dexie can lose `farmStore.hydrate()`'s auto-trigger to a `hasCheckedAuth`/`authState` ordering race, leaving `$farmStore` empty until something else populates it. Out of E2-P3 scope (a real browser always has cached farm rows from a prior session); the harness works around it by visiting `/app/farms` once per session. ### Task 3.6 — check:ci + visual verification **`SKIP_BACKEND=1 npm run check:ci` — ✅ green** (svelte-check, eslint, vitest, i18n:check, wa:check, a11y:check, tokens:check, overflow:check; backend skipp
  • bbee8d9 Nav-IA E2-P3: check off the plan's Mer-kartan tasks (3.1-3.6) Jonas · 2026-07-29 · +24 −24
  • 517bd1f Nav-IA E2-P3: fix a real regression found in visual verification — persisted house pick survived only within a session, not a reload Jonas · 2026-07-29 · +43 −2
  • 613a01f Nav-IA E2-P3: Playwright harness for the map (farm/house rows, accordion, reset-on-leave, exclusions) Jonas · 2026-07-29 · +340 −0
  • 72c49c0 Nav-IA E2-P3: MobileNavSheet becomes the map — GÅRD/STALL/🧤/🏠, accordion, no favourites, no mode row Jonas · 2026-07-29 · +402 −383
  • c122eb3 Nav-IA E2-P3: the map's field-destination exclusion as tested data Jonas · 2026-07-29 · +112 −0
  • 78cc9fe Nav-IA E2-P3: the breadcrumb carries the house switcher (Hela gården ✓ default) Jonas · 2026-07-29 · +64 −50
  • 17273cd Nav-IA E2-P3: Hela gården is a real default scope (no auto-select), animals page splits scope from create-context Jonas · 2026-07-29 · +205 −90

#171 Nav-IA E2-P1: favoritmodulen — 30-dagars frekvens ovanpå recordVisit, max 4 fästa

2026-07-29 · Jonas · 6 commits · 5 filer · +779 −54 · Infra/verktyg

Detaljer
## Summary Implements **ETAPP E2-P1 — Favoritmodulen** from `docs/superpowers/plans/2026-07-29-nav-e2-mobil-favoriter.md`: a pure, testable favourites data source (`$lib/nav/favourites.ts`) built on top of NAV-P7's existing `nav_recent` Dexie table, plus the `/app/settings/favourites` administration page. No visible nav-surface changes yet — the mobile bottom-bar panel and desktop chip land in E2-P2/P4. ### Task 1.1 — `visitDays` on `NavRecentRow` + pure window functions - `frontend/src/lib/nav/recent.ts`: `visitDays?: Record<string, number>` (unindexed — no Dexie version bump), `dayKey`, `bumpVisitDays` (prunes >30 days on write), `frequencyIn30d` (30-day sum, deliberately ignores all-time `count`). `recordVisit` now bumps the histogram on both the existing-row and new-row branches. - Also added `getAllRecentRows()` / `allRecentStore` (uncapped, unfiltered — needed by the auto-favourites list; `getRecent(10)` filters out pinned rows and caps to 10 most-recent, wrong shape for "most used"). - Tests: `recent.test.ts`, new `describe('30-day visit window …')` block, 6 new tests. ### Task 1.2 — `favourites.ts`, the one favourites source - `buildFavourites()`: pinned first (capped `MAX_PINNED=4`, `pinnedOrder`), auto = 30-day frequency desc / `lastVisitedAt` tie-break, excluding already-pinned rows, `farm.today` + `global.movements.scan` (field's own bottom-bar destinations, per spec), zero-frequency legacy rows, and unreachable paths (role/species changed). - `favouritesStore` (derived), `pinFavourite`/`unpinFavourite`/`reorderPinned` (write side, enforces the cap). - Tests: `favourites.test.ts`, 9 tests (`buildFavourites` rules + `pinFavourite` cap rejection). ### Task 1.3 — i18n keys - `nav.favourites.*` (title/pinned/mostUsed/customise/empty/autoEmpty/capReached/settings.title/settings.intro) + `nav.settings.favourites`, British `favourite(s)` spelling throughout per the plan's Global Constraints, Swedish UI text "Favoriter". ### Task 1.4 — `/app/settings/favourites` - New registry node `global.settings.favourites` (alphabetical between `dataExport`/`icarExport`, bucketed `settings`). - New `+page.svelte`: pinned list (move up/down/unpin), auto list (read-only, per-row pin button disabled at the cap), empty-state help text for both, uses `var(--wa-color-…, #fallback)` tokens (no hardcoded hex). ### Task 1.5 — gate + visual verification - `SKIP_BACKEND=1 npm run check:ci` — ✅ green (frontend-only; no `.scala` touched in this etapp). - Visual verification via a temp Playwright script (mockApi + `?dev_user=` bypass, deleted after use) against `bun run dev` on port 5194, both @1280×900 and @390×844: - Page renders; empty-state help text shown correctly for pinned AND auto lists. - Pinning 5 rows: exactly 4 succeed, the 5th pin button is disabled (UI-level cap enforcement) — `pinFavourite` returning `false` / `nav.favourites.capReached` is the defence-in-depth path, covered at the unit level. - Navigating 5 routes then reloading: entries persist correctly in "Mest använda" via Dexie; `farm.today` correctly excluded from auto (rule 4), a manually-pinned `farm.today` is NOT excluded (rule 4 scopes to auto only). - "Favoriter" appears in the Inställningar bucket in alphabetical order. - `wa-button` pin controls are styled (non-zero border), no repeat of the WA-unstyled-control regression. - Screenshots taken and read at both viewports; no horizontal overflow. ## Deviations from the plan/spec (documented in code comments too) 1. **2 inline pin buttons, not 5**: the design spec says "the shared pin component is now extracted, replacing the 5 inline copies" — current code only has 2 (`MegaMenu.svelte`, `MobileNavSheet.svelte`), and per spec `MobileNavSheet` gets NO favourites (P3 rewrites it into a catalogue-only "Mer" map). A shared pin component would serve one dying and one deleted call site, so it's deferred to P2/P4 where the real consumers land. 2. **Dexie, not localStorage**: the spec's "localStorage" note is st
  • 54392d2 Nav-IA E2-P1: check off the favourites-module plan tasks Jonas · 2026-07-29 · +21 −21
  • 8179ed5 Nav-IA E2-P1: fix token-drift on the favourites settings page Jonas · 2026-07-29 · +1 −1
  • 3639e16 Nav-IA E2-P1: /app/settings/favourites — the Anpassa favoriter destination Jonas · 2026-07-29 · +185 −0
  • 8711141 Nav-IA E2-P1: i18n keys for the favourites surfaces Jonas · 2026-07-29 · +72 −32
  • 86f9c13 Nav-IA E2-P1: favourites.ts — one source, pinned (max 4) + 30-day auto list Jonas · 2026-07-29 · +393 −0
  • 22e77be Nav-IA E2-P1: 30-day visit histogram on nav_recent (unindexed, no migration) Jonas · 2026-07-29 · +107 −0

#170 Fix K2 payment-wall & voice-input bugs: B-140, B-141, B-151, B-152, B-154

2026-07-29 · Jonas · 4 commits · 10 filer · +410 −104 · Buggfixar B-140 B-141 B-151 B-152 B-154

Detaljer
## Summary Fixes buggkluster K2 (betalvägg & röstinmatning) från `other/KNOWN_BUGS.md`: - **B-140** — Röstinmatningsdialogen spillde ut horisontellt: `<wa-dialog>` bodyn tvingade `min-width` istället för att sätta `--width` på dialogen själv (samma mönster som `DpaReacknowledgementDialog`/`FlockCensusDialog`). Bytt till `width:100%; box-sizing:border-box; overflow-wrap:anywhere` + `--width` på dialogen. - **B-141** — "Tolka om" visade rått `HTTP 402`. Ny delad hjälpare (`lib/util/billingGateMessage.ts`) + presentationskomponent (`lib/components/BillingGateNotice.svelte`) tolkar `BillingGate`s `subscription_state`-402 och visar ett begripligt meddelande + uppgraderingsknapp, samt matar den app-övergripande `billingGateStore` (så `UpgradeBanner` också triggas). - **B-151** — Rubrik/kort-kollision + ostylad "← Tillbaka"-länk + hårdkodade hex-färger på `/upgrade`. Rubrikblock fick egen marginal, länken följer appens `.back-link`-mönster, alla hex bytta mot tokens (döda `button`-regler som aldrig matchade något — sidan renderar bara `<wa-button>` — togs bort istället för att konverteras). - **B-152** — (1) Ingen vertikal luft mellan "Uppgradera"/"Prova Pro gratis"-knapparna: den döda `button { margin-top }`-regeln ersatt med en riktig `.tier-actions`-flexkolumn. (2) 400 Bad Request vid subscribe: `StripeBillingRoutes.mapErr`s catch-all gav ett tomt 400-svar för ALLA oväntade fel (inte bara ägar-gaten, som redan mappade korrekt till 403). Alla grenar får nu ett riktigt meddelande i svarskroppen, och frontend visar en översatt "endast ägaren kan uppgradera"-text specifikt på 403. - **B-154** — Massåtgärd (status) visade rå 402-JSON + no-op-övergångar ("active → active") såg ut som en riktig ändring. Återanvänder B-141:s delade gate-notis; ny no-op-heuristik (bara när förhandsgranskningens sample bevisligen täcker alla matchade djur) inaktiverar "Applicera på alla" och visar "redan detta värde — ingen ändring behövs" istället. ## check:ci ✅ check:ci green (frontend + backend) — svelte-check, eslint, vitest (2915 tester), i18n:check, wa:check, a11y:check, tokens:check, layout-overflow-guard, `sbt test` (3410 passed, 0 failed, 10 ignorerade). ## Visuell verifiering (Playwright, headless Chromium mot `bun run dev`, mockad `/api` + `?dev_user=`-bypass) - **Röstinmatningsdialogen** (`/priorities`, 420×850 viewport): skärmdump + mätning visar `bodyScrollWidth === bodyClientWidth` (306px) och `docScrollWidth === docClientWidth` (420px) — ingen horisontell overflow. Simulerad `SpeechRecognition` → 402-mock → "Kräver uppgradering / Röst-AI kräver en Pro-prenumeration. Uppgradera för att fortsätta." rendrerar med lås-ikon + uppgraderingsknapp i stället för "HTTP 402". - **`/upgrade`-sidan** (1280×1400 viewport): skärmdump visar rubrik med tydligt luftmellanrum (24px) till "Nuvarande prenumeration"-kortet, tokenstylad "← Tillbaka"-länk (neutral grå, ingen understrykning), 8px gap mellan de staplade CTA-knapparna. Mockad 403 → "Endast gårdens ägare kan ändra prenumerationen." i en danger-callout istället för rå text. - **Massåtgärd-dialogen** (`/animals`, mockade djur): no-op-preview (status=Aktiv, båda djuren redan aktiva) → neutral callout "Alla 2 valda djur har redan detta värde (Aktiv) — ingen ändring behövs." + "Applicera på alla" disabled. Riktig ändring (status=Såld) → preview + mockad 402 → samma "Kräver uppgradering / Massredigering kräver en Pro-prenumeration…"-notis som B-141, plus att den app-övergripande `UpgradeBanner` triggas ovanför dialogen. Alla skärmdumpar lästes (inte bara sparade) och jämfördes mot layouten före fix. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 9aa2d59 K2 cluster: mark B-140/B-141/B-151/B-152/B-154 as fixed (PR #170) Jonas · 2026-07-29 · +62 −14
  • 3661ae2 B-154: friendly 402 message + no-op detection for bulk animal actions Jonas · 2026-07-28 · +86 −19
  • df8f12d B-151/B-152: upgrade page spacing/tokens + friendly owner-only message Jonas · 2026-07-28 · +83 −37
  • 073f5d3 B-140/B-141: fix voice-entry dialog overflow + friendly Pro-gate message Jonas · 2026-07-28 · +179 −34

#169 Nav-IA P3: rälsen på alla /app-rutter — dragspel, hemknapp, gårdsväljare, webbkarta-fot

2026-07-29 · Jonas · 9 commits · 9 filer · +909 −221 · Buggfixar B-132

Detaljer
## Sammanfattning Etapp P3 ur `docs/superpowers/plans/2026-07-26-nav-e1-rals-overallt.md`. Bygger på P1 (#164) + P2 (#167), redan mergade i master. - **Task 3.1** — ny `frontend/src/lib/nav/bucketAccordion.ts`: `openBucketId`-store, max EN öppen bucket, härlett ur aktiv rutt + senaste tryck, **ingen** localStorage-persistens. Gamla `bucketState.ts` orört (MegaMenyn använder den fortfarande till E3). - **Task 3.2** — gaten i `routes/app/+layout.svelte` bytt från `sideRailEnabled && farmId && farm-scoped route` till bara `sideRailEnabled`. Rälsen renderar nu på ALLA `/app`-rutter. - **Task 3.3** — ny i18n-nyckel `nav.rail.pickFarm` ("Välj gård"). - **Task 3.4** — `SideRail.svelte` omskriven: 🏠-toppknapp, dragspelet, tunn linje före Inställningar (ingen "System"-rubrik), admin/advisor/vet-grupperna under linjen (endast admin), kompakt gårdsväljare + "Ny gård" när ingen gård är vald, webbkarta-fot, egen ‹-knapp borttagen. - **Task 3.5** — Playwright-harness `other/fas06-01-genomgang/verktyg/nav-e1-verify.mjs`, 27 checkar. - **Task 3.6** — `check:ci` grönt, visuell verifiering (se nedan), B-132 bockad av. ## Avvikelser plan → verklig kod (viktigast överst) 1. **`activeBucketId`s matchningslogik skiljer sig från planens utkast**, upptäckt genom att faktiskt klicka igenom rälsen i Playwright (inte bara läsa testresultat): - Planen föreslog `buckets.find(b => b.nodes.some(hits))` — FÖRSTA bucketen med någon träff. Det är fel: en grund nod som `global.farms` (`/app/farms`, i Inställningar) prefix-matchar VARJE gårdsrutt, så den kunde stjäla ägarskapet från t.ex. Efterlevnad på `/compliance/amu`. Fixat: **längsta matchande href vinner**, inte första träffen. - Även med längsta-match-fixen: `global.farms`s href (`/app/farms`) är en textuell PREFIX av den aktuella gårdens EGEN rot (`/app/farms/<id>`), så på gårdens EGEN hemsida — där ingen annan bucketnod matchar alls (`farm.home` är obucketed) — skulle den vinna som enda träff och auto-öppna Inställningar på varje gårds startsida. Fixat med `ownsRoute`: en href som själv är en strikt prefix av gårdens rot kräver EXAKT match, samma undantag `isNavHrefActive` redan ger `farm.home`, generaliserat ett steg upp. - **Viktigt för E2:** mobilytans "Mer"-karta konsumerar samma `bucketAccordion.ts`-modul. Implementeraren där ska INTE återskapa planens ursprungliga `.find()`-variant — den har båda buggarna ovan. 2. **`rawGroups` i SideRail byggs från det RÅA registryt** (+ `stripHidden`, se nedan) i stället för planens reaktiva `[...$navBuckets, ...$adminSectionGroups]` inuti `$effect`. Skälen: (a) `.bucket` stämplas bara på toppnivånoder, så gruppering på toppnivå från `registry` matchar den strukturen exakt; (b) `$effect` spårar då BARA `currentPath`/`selectedFarmId`, inte de filtrerade/sorterade bucket-storesen som räknas om vid varje locale- eller feature-hydreringstick — annars kunde ett medvetet knapptryck tystas av en orelaterad store-omräkning, eller den aktiva bucketen resolva till `null` under förhydreringsfönstret och aldrig auto-öppnas. 3. **`stripHidden`** (ny hjälpfunktion i SideRail): `global.app` (dold, href `/app`) prefix-matchar bokstavligen VARJE `/app/*`-rutt. Lämnad i rawGroups skulle den vinna `activeBucketId`s förstaträff före den riktiga ägande-bucketen på varenda sida. Dolda noder renderas ingenstans, så de får inte heller "äga" en rutt för dragspelet. 4. **Ingen-gård/hem-grenen inverterad** att gata på `!$selectedFarmId` FÖRST (B-062), inte på `homeRow`-sanning. `nodePasses` har ingen farmId-gate, så `farm.home` överlever `filterRegistry` även utan vald gård och skulle annars resolva till den bokstavliga platshållaren `/app/farms/[id]`. 5. **localStorage-testet i `bucketAccordion.test.ts`** stubbar en minimal in-memory `localStorage` — `vitest.config.ts` kör `environment: 'node'` och `jsdom` är inte installerat, så global `localStorage` finns inte i testmiljön. 6. **Radnummerdrift** från P1/P2 — gaten låg på rad 373 i `+layout.svelte`, inte 430-436 som planen
  • eccdc32 Nav-IA P3: harness — verify the admin branch, home-on-non-farm, and cold deep-link timing Jonas · 2026-07-29 · +70 −0
  • bcf1e7d Nav-IA P3: tick off P3 tasks in the plan, mark B-132 fixed in KNOWN_BUGS.md Jonas · 2026-07-28 · +36 −26
  • 8648591 Nav-IA P3: Playwright harness (rail everywhere, accordion, home button, no-farm picker, footer, geometry) Jonas · 2026-07-28 · +304 −0
  • 9ad8c9d Nav-IA P3: fix activeBucketId's route-ownership resolution (2 real bugs, found by manual verification) Jonas · 2026-07-28 · +80 −4
  • 8af51f1 Nav-IA P3: strip hidden branches before route-matching in the rail accordion Jonas · 2026-07-28 · +26 −3
  • d9b08eb Nav-IA P3: rail renders the curated map — home button, accordion, divider, sitemap footer; own collapse button gone Jonas · 2026-07-28 · +240 −152
  • 8177c33 Nav-IA P3: add nav.rail.pickFarm Jonas · 2026-07-28 · +33 −29
  • f1f4562 Nav-IA P3: the rail renders on every /app route Jonas · 2026-07-28 · +8 −7
  • 248a80a Nav-IA P3: accordion store — max one open bucket, derived from route, never persisted (B-132) Jonas · 2026-07-28 · +112 −0

2026-07-28 — 14 direkt-commits

Detaljer
  • dc9d774 B-164 uppföljning: dölj nav-noden farm.movements.slaughter.fci (sidan är redirect sedan PR #168) Jonas · 2026-07-28 · +3 −1
  • 9153145 Nav E1-planen reviderad mot stängda rundans beslut — P1 gating, P2 bucketkarta, P3 räls, P4 TopBar Jonas · 2026-07-28 · +2 033 −1 044
  • 91e510f Nav-IA runda 2: spec uppdaterad med hela beslutsloggen — rundan stängd, bygget startar Jonas · 2026-07-28 · +491 −65
  • a248682 Nav-IA runda 2: Fodertilldelning + Foderstater är också stubbar — döljs; Foder = Foderlager + Foderplan Jonas · 2026-07-28 · +16 −23
  • 5895233 Nav-IA runda 2: Välfärdshälsokontroll → Hälsokontroll + döda stubbar dolda i prototypen Jonas · 2026-07-28 · +23 −38
  • c56ffef Nav-IA runda 2 STÄNGD: byggbeslut + scope för den stora omgången (nav + alla buggar + rapportmerge) Jonas · 2026-07-28 · +33 −0
  • 1c0dded Nav-IA runda 2: Anpassa översikt → Anpassa startsida Jonas · 2026-07-28 · +11 −9
  • 3e64bd6 Nav-IA runda 2: hem-knapp, admin-gårdsbyte, Systemadmin-undergrupper + nav-audit (B-163/B-164) Jonas · 2026-07-28 · +303 −135
  • 2109908 Rapportbyggar-utredning: rekommendera sammanslagning med Sparade rapporter som bas Jonas · 2026-07-28 · +65 −2
  • ead9d8d Nav-IA runda 2: Årspaket → Årsrapport (etikettpass) + prototypsnapshot Jonas · 2026-07-28 · +13 −10
  • fec944a Nav-IA runda 2: Efterlevnad-uppdelningen + Regelradar-placeringen beslutade; rapportbyggar-dubbletten loggad Jonas · 2026-07-28 · +14 −5
  • 388f802 Nav-IA runda 2: dela Efterlevnad i 9 undergrupper, Hälsa egen bucket, B-162 landsgating-läcka Jonas · 2026-07-28 · +348 −235
  • 652597d Nav-IA round 2: decision log from the interactive mockup round + prototype snapshot Jonas · 2026-07-28 · +5 677 −0
  • f9a3b2d Log B-160: farm.links (link-in-bio) is shop functionality — gate it behind the shop feature Jonas · 2026-07-28 · +12 −0

#168 Rapportbyggar-sammanslagning: slå ihop Anpassade rapporter i Sparade rapporter + B-164 FCI-dubblett

2026-07-28 · Jonas · 6 commits · 9 filer · +700 −2 271 · Buggfixar B-164

Detaljer
## Sammanfattning Genomför RAPPORTBYGGAR-SAMMANSLAGNINGEN enligt `other/RAPPORTBYGGARE-SAMMANSLAGNING-UTREDNING.md`: "Sparade rapporter" (system A) blir den enda rapportbyggaren. groupBy/aggregate (count/sum/avg/min/max) och filteroperatorerna `neq`/`gt`/`lt` porteras in från "Anpassade rapporter" (system B), som sedan pensioneras. Passar även på B-164 (FCI-sidedubblett) i samma PR eftersom den är liten och orelaterad men var redo. ### Backend-port (`21d915eb`) - `SavedReportRoutes.scala`: `Source`-registret oförändrat med 9 datakällor (från A), `FilterClause`/`Op` fick `neq`/`gt`/`lt` bredvid befintliga `eq`/`in`/`between`/`gte`/`lte`/`like`. Ny `groupBy`/`aggregateColumn`/`aggregateFn` på `SavedReportRow` (`Models.scala`) och request/response-DTO:erna. Grupperings-/aggregeringslogiken är portad från `CustomReportRunner.applyGroupBy`/`computeAgg` rad för rad, anpassad till A:s projektionsfunktioner. - `CustomReportRoutes.scala` och `CustomReportRunner.scala` borttagna; `/api/farms/:id/custom-reports*` finns inte längre. `CustomReportRepository`-klassen och `custom_report`-tabellen är kvarlämnade (repo avregistrerat från `Main.scala`:s ZLayer, men filen och tabellen finns kvar som backup, enligt uppdrag). - **Billing-gapet stängs avsiktligt:** hela den sammanslagna ytan ärver A:s `BillingGate.requireFeatureAccess(PremiumFeature.SavedReports)` (kräver tier `small`/Pro+) på skriv-vägar. System B saknade tidigare denna spärr helt — anpassade rapporter var gratis för alla tiers. Efter denna PR kräver all rapportbyggning Pro-tier. ### Migration 0251 (`a82a3aae`) `db-patches/db-migration-0251-report-builder-merge.sql`: 1. `ALTER TABLE saved_report ADD COLUMN group_by, aggregate_column, aggregate_fn`. 2. En engångskonvertering av befintliga `custom_report`-rader till `saved_report`-format, ren SQL (`JSON_TABLE` + `JSON_OBJECTAGG` för att bygga om filterklausulerna till A:s `{field: {op, value}}`-form, `CAST(...AS JSON)` för att undvika dubbel-JSON-encoding av arrayer i `in`/`between`-värden). 3. `dataSource: 'inventory'` byts till `'inventory_lots'` (namnbytet mellan systemen). 4. Rader med `dataSource: 'orders'` exkluderas (källan finns inte i A och har ingen motsvarighet). 5. Idempotent (`NOT EXISTS`-vakt på `id`), radernas ID återanvänds. 6. `custom_report`-tabellen droppas inte — kvar som backup, enligt uppdrag. Migrationen är verifierad genom att köra filen manuellt via `mysql`-klienten mot en engångs scratch-databas (`enlantis_migration_scratch`, skapad och droppad igen) med 6 syntetiska rader — inte genom `Migrations.runOnBoot` i en riktig boot. SQL-splitten (`Migrations`-körarens naiva semikolon-delning) är ofarlig här: filen har exakt två statements, båda med semikolon i radslut. ### Frontend (`e1574d29`, `a82200f9`) - `/reports/new`: nytt "Gruppera efter"-fält + ett "Aggregat"-kryssblock som expanderar till fn (count/sum/avg/min/max) + kolumn, plus `neq`/`gt`/`lt` i operatorlistan där relevant per fälttyp. - `/reports` (lista) och `/reports/[reportId]` (visning): visar/duplicerar groupBy+aggregat-fält, "Anpassade rapporter"-kortet borttaget från modulnavigeringen. - Thin redirects (`+page.ts` -> `redirect(307, ...)`, befintligt husmönster som `dashboard/today`): `/reports/custom` -> `/reports`, `/reports/custom/new` -> `/reports/new`, `/reports/custom/[rid]` -> `/reports` (ingen ID-mappning görs client-side; migrationen återanvänder ID men den gamla ruttens dynamiska segment leder ändå bara till listan). - i18n: nya nycklar `reports.op.neq/gt/lt` (`misc-2.mjs`) och `savedReports.builder.groupBy/aggregate` (`savedReports.mjs`) genom skärv-systemet + `_order.mjs`, `npm run i18n:build` kört (endast `en.json`/`sv.json`/`coverage.json` fick faktiska innehållsdiffar). ### B-164 (`a82200f9`) `movements/slaughter/fci` var en kodmässig dubblett av `compliance/fci` mot samma `/api/farms/:id/fci`-endpoint. Sidan är nu en thin redirect-stub till `/compliance/fci` (samma mönster som ovan), och slaktlistans "Generera FCI"-knapp län
  • 04df02f Docs: wiki updated for report-builder merge (migration 0251) Jonas · 2026-07-28 · +18 −15
  • a82200f Frontend: /reports/custom* thin redirects + B-164 FCI dedupe Jonas · 2026-07-28 · +78 −1 095
  • e1574d2 Frontend: groupBy/aggregate step + neq/gt/lt operators in the saved-report builder Jonas · 2026-07-28 · +148 −37
  • 1626b94 Tests: retire CustomReport specs, add groupBy/neq/gt/lt HTTP-level coverage Jonas · 2026-07-28 · +151 −443
  • a82a3aa DB: migration 0251 — convert custom_report rows into saved_report format Jonas · 2026-07-28 · +110 −0
  • 21d915e Backend: port groupBy/aggregate + neq/gt/lt into SavedReportRoutes, retire CustomReportRoutes Jonas · 2026-07-28 · +195 −681

#167 Nav-IA P2: den kuraterade bucketkartan — Globalt upplöst, undergrupper, etiketter, bokstavsordning

2026-07-28 · Jonas · 10 commits · 8 filer · +877 −352 · Tester

Detaljer
## Summary Implements **Etapp P2 — Bucketkartan** from `docs/superpowers/plans/2026-07-26-nav-e1-rals-overallt.md`: the curated bucket order, Globalt dissolved into the buckets, the Hälsa/Efterlevnad/Systemadmin subgroups, the label pass, and alphabetical ordering — the shared data layer both the existing SideRail and the future P3/E2 surfaces consume. Builds on P1 (gatingpass, PR #164, already merged). ## Per-task summary - **Task 2.1** — New `FarmBucketId`/`FARM_BUCKETS` in curated order (`today · animals · health · feed · production · compliance · rules · business · devices · ai · other · settings`); 4 new i18n bucket keys (`health`, `feed`, `rules`, `settings`), 2 renamed (`today`→"Planering", `devices`→"Enheter"). - **Task 2.2** — "Globalt" dissolved: `global.devices`/`global.settings` group parents deleted, their 4+14 children promoted to top-level and bucketed; `global.app`/`global.sitemap` hidden (Home = logo/rail-footer only); `farm.compliance.radar` extracted to a top-level node in the new `rules` bucket; `MODE_UNIVERSAL`/`MODE_ALLOWLIST.office` updated to keep `registry.mode.test.ts` green. New `registry.map.test.ts`. - **Task 2.3** — Efterlevnad's 46-child wall split into 8 new group nodes (Djurskydd & tillsyn, Dokument & arkiv, Intyg & deklarationer, Journaler, Läkemedel & antibiotika, Myndighetsrapportering, Smittskydd, Stöd & certifiering) + the pre-existing `farm.reports` group = 9 subgroups. The hub (`farm.compliance`) survives as a childless leaf. - **Task 2.4** — Systemadmin's 29 destinations split into 6 subgroups (Drift & säkerhet, Kunder & konton, Support & feedback, Mätvärden & analys, Innehåll & moderering, Referensdata & regelverk); `admin.home` stays loose. `NON_FARM_SECTIONS` drops the `global` entry. - **Task 2.5** — `farm.home` unbucketed (🏠 button, not a row); `farm.dashboards` ("Anpassa startsida") moves from Planering to Inställningar. - **Task 2.6** — Label pass: Årsrapport, Hälsokontroll, Grisproduktion (+ the `<Art>produktion` pattern on all 7 species groups), Stödvillkor (CAP), Gränsförflyttningar (TRACES), Gårdens länksida. - **Task 2.7** — New `sortNodesByLabel` (pure, locale-injected `Intl.Collator`) applied recursively on every leaf/subgroup level; `navContext.ts` gets the surface-agnostic `navBuckets`/`homeNode`/`adminSectionGroups` exports (replacing `farmNavBuckets`). - **Task 2.8** — Persona-coverage test: hard invariants (no leaked stub/foreign journal, every named bucket non-empty, admin > pig farmer) plus a documented measured-counts snapshot. - **Task 2.9** — `check:ci` green, visual verification (below). ## Deviations from the plan 1. **`farm.compliance.passport` label** (huvudtrådens beslut, per denna sessions uppdrag): "Djurpass" -> **"Djuridentitet"** — the plan's own open question 4 recommended this default; the parent task explicitly instructed taking it as decided rather than leaving it open. Nav label only, page `<h1>` untouched. 2. **Advisor portal admin-gated.** No `FarmRole` covers "advisor" (unlike the vet portal, reachable via the existing `owner|Vet` role gate that owners always pass). Without an explicit gate a plain farm owner would have leaked into the admin/advisor/vet persona rule the design spec states ("Bara admin ser allt" / "Advisor-/vet-portalerna visas inte för bönder"). Added `adminOnly: true` to both `advisor.*` nodes — not explicitly spelled out as a task step, but required to make the plan's own `registry.map.test.ts` assertion ("a non-admin sees no admin/advisor/vet group at all") true, and visually confirmed live (see below). 3. **SideRail.svelte import fix.** The plan's Task 2.7 lists only `MegaMenu.svelte`/`MobileNavSheet.svelte` as `farmNavBuckets` consumers. `SideRail.svelte` (the rail already on master, predating this branch) also imports it — not touching it would have broken `svelte-check`. Applied the identical 2-line import rename there. 4. **Measured persona counts differ from the spec's prototype numbers** (documented in the Task 2.
  • 4462183 Nav-IA P2 correction: move Foderplan into the Foder bucket (matches approved facit) Jonas · 2026-07-28 · +28 −1
  • b49e609 Nav-IA P2: check off completed tasks in the E1 plan Jonas · 2026-07-28 · +43 −43
  • 5519927 Nav-IA P2: persona coverage test (invariants hard, measured counts documented) (Task 2.8) Jonas · 2026-07-28 · +48 −0
  • 83ef68c Nav-IA P2: alphabetical order + the surface-agnostic navBuckets/homeNode/adminSectionGroups export (Task 2.7) Jonas · 2026-07-28 · +92 −11
  • 865d623 Nav-IA P2: label pass (Årsrapport, Hälsokontroll, Grisproduktion, Stödvillkor (CAP), Djuridentitet, …) (Task 2.6) Jonas · 2026-07-28 · +44 −40
  • bd240ca Nav-IA P2: farm.home is the home button; Anpassa startsida moves to Settings (Task 2.5) Jonas · 2026-07-28 · +13 −4
  • 32e86d8 Nav-IA P2: split Systemadmin into 6 subgroups; NON_FARM_SECTIONS is admin-only now (Task 2.4) Jonas · 2026-07-28 · +159 −65
  • 3155bb4 Nav-IA P2: split Efterlevnad into the 9 decided subgroups (Task 2.3) Jonas · 2026-07-28 · +221 −81
  • 1b8c14b Nav-IA P2: curated bucket list + Globalt dissolved into it (Task 2.2) Jonas · 2026-07-28 · +158 −65
  • 08c858c Nav-IA P2: curated bucket list + i18n keys (Task 2.1) Jonas · 2026-07-28 · +71 −42

#166 Fix K4 cluster: B-136, B-137, B-138, B-139 (Anpassad översikt + adminöversikt)

2026-07-28 · Jonas · 5 commits · 7 filer · +476 −120 · Buggfixar B-136 B-137 B-138 B-139

Detaljer
## Summary Fyra dokumenterade buggar på översikts-/dashboardytan (`other/KNOWN_BUGS.md`), en commit per bugg: - **B-136** — Anpassad översikt: tomtillståndets copy hänvisade till en "Lägg till widget"-knapp som inte fanns på visningssidan (den låg bara under "Redigera widgetar"). Lade till en riktig "Lägg till widget"-knapp både i tomtillståndets callout och i sidans header (bredvid "Redigera widgetar"), båda länkar till `/edit`. - **B-137** — Widgetarnas konfiguration var ett fritt JSON-textfält (`{}` som default, ingen vägledning). Ersatt med ett per-widget-typ-formulär (källa/etikett/checkbox för kpi-counter, händelsetyp för chart-line, antal rader för recent-list, URL för iframe, info-text för chart-bar/nudges, och ett **platsfält** för weather + tydlig demo-notis eftersom backend-resolvern fortfarande är en hårdkodad stub). Lade även till en "konfigurera"-knapp på redan tillagda widgetar (fanns inte tidigare — bara ta-bort). - **B-138** — Adminöversikten (`/dashboard`) visade röda stat-kort + röd hero-banner. Root cause: `.stat-card`/`.admin-header` i `global.css` fyllde med `--gradient-primary` (burgundy CTA-token, fel användning) i stället för neutrala panel-tokens. Bytt till samma `--gradient-panel`-behandling som `.panel` på ai-ops-sidan. - **B-139** — "Spara översikt" gav ingen feedback. `saveLayout` kollade aldrig `res.ok`. Lade till samma success/error-callout-mönster som admin-settings-sidan använder. ## Kvalitetsgrind ✅ `SKIP_BACKEND=1 npm run check:ci` grönt (frontend: svelte-check, eslint, vitest 2915 tester, i18n:check, wa:check, a11y:check, tokens:check, overflow:check — alla ✅; backend hoppades enligt uppdragets instruktion, inga backendändringar gjorda). ## Visuell verifiering Körd i riktig Chrome mot dev-servern (`bun run dev`) och **mot en riktig, redan seedad backend** (`Sim Slaktsvinsstall`-gården, `?dev_user=`-inloggningsbypass) — inte bara mockad harness: - `/app/farms/:id/dashboards/:did` (tomt) — header + callout visar båda "Lägg till widget" (B-136). - `/app/farms/:id/dashboards/:did/edit` — Lägg till/konfigurera-dialogen visar rätt fält per widget-typ (kpi-counter, chart-line, chart-bar, recent-list, weather, iframe); rå JSON-ruta borta helt. Konfigurera-dialogen prefyller korrekt från sparad `configJson` (källa=Djur, "endast levande djur" ikryssad, etikett ifylld). "Spara översikt" visar en grön "Översikten sparad."-callout (B-139). - `/app/farms/:id/dashboards/:did` (med widgetar) — KPI-räknaren visar riktigt djurantal ("2"), väder-widgeten visar "sunny 18°C" + en "DEMO"-badge. - `/app/farms/:id/dashboard` (adminöversikt, root-admin via `jonas.olson@gmail.com`) — hero + alla fyra stat-kort renderar med neutral cream-bakgrund och mörk text; mätt via `getComputedStyle`: `background-image` = `--gradient-panel` (`#f6f0e0`→`#e3d6b3`), ingen burgundy kvar. Under verifieringen hittades och fixades en riktig bugg i första implementationsvarvet: `wa-checkbox` dispatchar en vanlig `"change"`-DOM-event, inte `"wa-change"` — checkboxen för "endast levande djur" band fel eventnamn och sparade aldrig `filter`. Fixat till `onchange`, verifierat om end-to-end. Testdata (en tillfällig testöversikt) skapades via UI och togs bort igen via API efter verifiering. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 0c8bc42 Mark B-136, B-137, B-138, B-139 as fixed in KNOWN_BUGS.md Jonas · 2026-07-28 · +47 −18
  • 29a9f92 B-139: success/error feedback for "Spara översikt" Jonas · 2026-07-28 · +62 −30
  • f07d8b0 B-138: neutral background for admin overview hero + stat cards Jonas · 2026-07-28 · +20 −6
  • 85cdd2f B-137: per-widget-type config form for custom-dashboard widgets Jonas · 2026-07-28 · +330 −65
  • cc08371 B-136: real "Lägg till widget" button on the custom-dashboard view page Jonas · 2026-07-28 · +17 −1

#165 K6: global print stylesheet (B-142) + retire user-facing release log (B-159)

2026-07-28 · Jonas · 3 commits · 3 filer · +191 −219 · Buggfixar B-142 B-159

Detaljer
## Summary Two independent bugcluster K6 fixes from `other/KNOWN_BUGS.md`. **B-142 — global utskriftsstil** - Added one app-wide `@media print` block in `frontend/src/global.css`. Before this there was no global print rule — only 126 pages with their own local `@media print`, none of which touched the nav shell. - Hides shell chrome on print: `.app-shell__top` (topbar), `.app-shell__rail` (side rail), `.app-shell__bottom` (bottom bar), skip link, overlays (`.mm-overlay`/`.cp-backdrop`/`.sheet`), in-app notice banners (`.banner`/`.upgrade-banner`/`.impersonation-banner`), and the global Co-Pilot trigger. - Frees the content column from its screen-only fixed-height/`overflow:hidden` boxing (`.app-viewport`/`.app-shell`/`.app-shell__main`) and the `.table-scroll` wide-table wrapper, so tables flow at full width and break across pages instead of clipping to the viewport. - `svart-på-vitt`: repoints the surface/text design tokens to white/black — scoped to `body`, not `:root`, so `scripts/check-tokens.mjs`'s drift guard keeps reading the real screen token values (an unconditional `:root` override would poison the guard's baseline and false-flag every real `var(--tok, #screen-hex)` fallback in the codebase). - Tables get a repeating header row (`thead { display: table-header-group }`) and `page-break-inside: avoid` on rows/cells; `@page { margin: 1.5cm }`. - Added `.print-hide`/`.print-only` escape-hatch utility classes for page-specific overrides. Page-local print rules (e.g. `/weekly-plan`) are unaffected and still layer on top. **B-159 — release-loggen är utvecklarinfo, flytta till admin** - `/app/admin/release-notes` already existed (root-admin-gated via the shared `/app/admin/+layout.svelte` guard, B-056) with full CRUD against the existing backend admin API — no backend change needed. - Removed the "Nytt den här veckan" banner from `frontend/src/routes/app/+layout.svelte`, plus the polling/state/dismiss logic that drove it and the now-fully-dead `frontend/src/lib/stores/releaseNotesStore.ts` (its `unseen` store had no other consumer). - `frontend/src/routes/app/release-notes/+page.svelte` is now a thin redirect: root admins land on `/app/admin/release-notes`, everyone else bounces to `/app`. - `/changelog` (dev-history site) untouched. - **Not touched, by design:** `frontend/src/lib/nav/registry.ts` and the nav shell components — the `global.releaseNotes` nav node (still pointing at `/app/release-notes`) is retired separately by the parallel nav-IA track. Its link still works (redirects), so nothing dead-ends in the meantime. ## check:ci ✅ check:ci green (frontend only — `SKIP_BACKEND=1`, no backend touched): svelte-check, eslint, vitest, i18n:check, wa:check, a11y:check, tokens:check, layout-overflow guard all passed. Backend skipped per the task's own frontend-only guidance; also re-ran clean on push via the pre-push hook. ## Visual verification (Playwright, mockApi + `?dev_user=`, dev server on port 5188) **B-159** - Non-admin dashboard (`/app/farms/farm-1`): no "Nytt den här veckan" banner. - Non-admin visiting `/app/release-notes` directly: redirected to `/app` (home), no old page content flashes. - Root-admin visiting `/app/release-notes` directly: redirected to `/app/admin/release-notes`, admin CRUD page renders (`.admin-wrap` present, translated title "Administratör för versionsnoteringar"). **B-142** — print-emulated (`page.emulateMedia({media:'print'})`) + screenshots + PDF export, on: - `/app/farms/farm-1/animals` (card-heavy content page): topbar/rail/bottom-bar all `display:none`, `.app-shell__main` overflow visible, body background white, cards full-width white-on-black. - `/app/farms/farm-1/weekly-plan` — the bug report's own named repro case ("bottenraden skrivs fortfarande ut"): topbar, side rail, **and** bottom bar (the specific defect called out in the bug) are now all hidden; table renders full-width, black-on-white, clean page. All screenshots were opened and read, not just saved; computed styles
  • c877e5a Log B-142 + B-159 as fixed (PR #165) Jonas · 2026-07-28 · +25 −3
  • 4747538 Fix B-159: retire the user-facing release log, keep it admin-only Jonas · 2026-07-28 · +30 −216
  • f8e418b Fix B-142: add app-wide @media print stylesheet Jonas · 2026-07-28 · +136 −0

#164 Nav-IA P1: gatingpasset — landsdimension, art-/featureläckor, döda stubbar

2026-07-28 · Jonas · 8 commits · 3 filer · +344 −152 · Buggfixar B-135 B-160 B-162 B-163 B-159

Detaljer
## Sammanfattning Etapp P1 (Gatingpasset) ur `docs/superpowers/plans/2026-07-26-nav-e1-rals-overallt.md`. Ren data-/predikatändring i `frontend/src/lib/nav/registry.ts` + `navContext.ts` + en punktfix i `sitemap/+page.svelte`. Ingen strukturell ombyggnad av bucketkartan (det är P2), inga nya i18n-nycklar, inga komponentändringar. Ingen `.scala`-fil rörd. ### Per ändring - **`NavNode.hidden`** — ny flagga, verkställd i EXAKT ETT ställe (`filterRegistry`). `registry`/`flattenRegistry` behåller dolda noder så route-täckningskontrakten och lägeslinsen är opåverkade. - **Landsdimensionen (B-162)** — `NavNode.countries?: string[]` + `FilterContext.country?: string` + OR-gate i `nodePasses` (case-insensitive, endast verkställd när gårdens land är känt). `navContext.navFilterContext` matar in `$selectedFarm.country`. - **18 nationella efterlevnadsjournaler taggade** — dkMovement, eeMovement, esMovement, esHerdTest, sitranExport, fiMovement, frTagging, hitAmu, ieMovement, itMovement, plMovement, vetstatAmu, vivaAmu, embPigs, nlSheepGoatMovement, nlCattleMovement, nlPoultryMovement + `cdbPig` (SE, behålls). - **Art-/featuregating (B-163) + shop-gate (B-160)** — `farm.production.feeding` (bees — sidan är bikupematning), `farm.production.scanning` (sheep), `farm.devices.vikmateImport`/`activityCsvImport`/`activitySensorCsvImport` (dairy), `farm.crm` (shop), `farm.fields` (plantProduction), `farm.movements.killReportImport` (pigs), `farm.compliance.penCapacity`/`penRecount`/`dailySupervision` (pigs), `farm.links` (shop, B-160). - **Döda stubbar + retirerade poster dolda** — `farm.health.checks`, `farm.breeding.pregnancyChecks`, `farm.feed.feedingLog`/`allocations`/`rations` (B-163 "coming soon"-platshållare), `farm.dashboard.today` (B-135, 307-redirect-dubblett), `global.releaseNotes` (B-159, flyttad till admin), `farm.reports.new`/`farm.reports.custom.new` (rapportbyggarens "Ny …"-poster — åtgärder, inte platser). - **`MODE_ALLOWLIST`-konsistens** — `health`/`breeding`-listorna tappar referenserna till de nu dolda stubbarna, annars faller `registry.mode.test.ts`s "union av alla lägesmenyer är en STRIKT delmängd"-kontrakt. - **Sitemap-fix (utöver planen)** — `sitemap/+page.svelte` byggde sin egen `FilterContext` (från innan `navContext.ts` fanns) och saknade `country`. Upptäckt under den visuella verifieringen: en svensk gård såg fortfarande NL-journalerna på `/app/sitemap` trots att rälsen och paletten (som går via `navFilterContext`) redan hade tappat dem. Lade till samma `$selectedFarm?.country`-läsning. ### Avvikelse från planens ordalydelse `nlSheepGoatMovement` fick **inte** `features: ['sheep','goats']` som B-162:s text antydde. `nodePasses` (registry.ts) använder `.every()` för `features` (AND, inte OR) — en tvåarts-array hade dolt noden för en gård med bara får ELLER bara getter. Landsgaten (`countries: ['NL']`, redan landad) löser det Jonas faktiskt rapporterade (NL-noder på en svensk grisgård). Artgating för just den noden skjuts till konsolideringsrundan tillsammans med frågan om `features` ska stödja OR. ## Test `✅ check:ci grönt (frontend, SKIP_BACKEND=1)` — samtliga sju frontend-guards gröna (check, lint, test [2924 tester], i18n:check, wa:check, a11y:check, tokens:check + overflow:check), backend explicit överhoppad. Körd tre gånger (efter huvudimplementationen, efter sitemap-fixen, och efter bugglogg-committen). ## Visuell verifiering Playwright mot `bun run dev` (port 5186), mockApi + `?dev_user=`-mönstret (`other/fas06-01-genomgang/verktyg/`), en svensk grisgård (`country: 'SE'`, `has_pigs: true` ⇒ `farmFeaturesStore` = `{pigs}`). Skript skrivet som `x-verify-p1.tmp.mjs` i worktree-roten, raderat efter körning. 29/29 automatiska kontroller gröna; skärmdumparna ÖPPNADES och LÄSTES: - **Gårdssidan (räls, alla buckets expanderade):** Efterlevnad & journaler-bucketen visar bara toppnoden "Regelefterlevnad" (rälsen expanderar inte grandchildren) — verifierat i stället via palett/sitemap nedan. Inga döda stubbar (Häl
  • 086bbfd Nav-IA P1: mark B-135/B-160/B-162/B-163 fixed in the bug log Jonas · 2026-07-28 · +55 −74
  • c266f41 Nav-IA P1: check off completed tasks in the E1 plan Jonas · 2026-07-28 · +36 −36
  • f02f946 Nav-IA P1: feed the country gate into the sitemap page too (B-162) Jonas · 2026-07-28 · +4 −0
  • e76fb61 Nav-IA P1: drop the two hidden stubs from MODE_ALLOWLIST (mode lens stays consistent) Jonas · 2026-07-28 · +3 −2
  • b9b9743 Nav-IA P1: hide the dead stubs and the decided retirements (B-163/B-135/B-159) Jonas · 2026-07-28 · +44 −10
  • a2f0ba1 Nav-IA P1: close the species/feature leaks (B-163) and shop-gate farm.links (B-160) Jonas · 2026-07-28 · +57 −12
  • ec9e430 Nav-IA P1: country-gate the 18 national compliance journals (B-162) Jonas · 2026-07-28 · +45 −18
  • 8e533e7 Nav-IA P1: add NavNode.hidden + the country dimension to FilterContext (B-162) Jonas · 2026-07-28 · +100 −0

#163 Buggkluster K1: Schema & planering (B-144..B-150)

2026-07-28 · Jonas · 6 commits · 9 filer · +178 −42 · Buggfixar B-144 B-150 B-146 B-149 B-145

Detaljer
## Sammanfattning Fixar sju visuella buggar i K1-klustret (schema/planering), grupperade per rotorsak. - **B-144** — Veckoplan (`/weekly-plan`) byggde grid:en som ett rullande 7-dagarsfönster från "idag" i stället för kalenderveckan, så en söndag visade söndag→lördag. Ankrar nu till måndagen i innevarande ISO-vecka; "Idag"-markören, radfiltret (`inWeek`) och CSV-exporten matchar samma veckostart. - **B-145** — Kalenderns månadsvy (`/calendar`) klippte söndagskolumnen vid mellanbredder eftersom `.cal-grid` använde `repeat(7, 1fr)` (implicit `minmax(auto, 1fr)`) — den icke-radbrytande händelsechippen tvingade upp spårens minimibredd. Byter till `repeat(7, minmax(0, 1fr))` och höjer tvåkolumns-brytpunkten 900px → 1100px så detaljpanelen läggs under kalendern tidigare. - **B-146/B-147/B-148/B-149** — Delad rotorsak i den vendrade `lit-schedule`-kalendern (`frontend/src/lib/schedule/**`): flera `:host`-block sätter `color-scheme: light dark`, vilket kopplar bort appens `color-scheme: light` och gör att kalenderns `light-dark()`-CSS-funktioner styrs av besökarens OS/webbläsarpreferens i stället för appens tema. - B-146 (årsvy, vita dagssiffror): bryggar `--lc-grid-base-color` → `--color-text`. - B-148 (dagsvy, svart heldags-band): bryggar `--lg-background-color` → `--color-bg-card`. - B-147 (rutnäts-/list-knapparna "gör inget"): de fungerade redan (växlar `presentation` och renderar om) — men vald/ovald-indikeringen delade samma nästan-vita färgfamilj i mörkt schema, så ingen skillnad syntes. Bryggar `--lc-switch-active-color` → `--color-primary`. - B-149 (röd datumchip, "gör inget" vid klick): chippen dispatchar `day-selection` korrekt, men i en endags-vy (`daysPerWeek === 1`) pekar den alltid på dagen som redan visas — kan aldrig navigera. Hoppar nu över klick-/tangentbordshanterarna och tar bort pekar-cursorn (`.is-static`) i det fallet. - **B-150** — Filterpanelen på Uppgifter → Alla uppgifter använde `--wa-color-neutral-50` (Web Awesomes otematiserade kalla gråskala) som bakgrund, vilket syntes som en grå-brun ruta mot den varma cream-paletten. Byter till `--color-bg-card` + `--color-border-light`, i linje med övriga kort på sidan. ## check:ci ✅ check:ci grönt (frontend, `SKIP_BACKEND=1`) — kört både lokalt i sessionen och i pre-push-gaten (svelte-check, eslint, vitest 2915 tester, i18n, wa:check, a11y:check, tokens:check, overflow:check). Backend skippat (Windows-arbetsstationen saknar sbt); ej rört av det här klustret. ## Visuell verifiering (obligatorisk, se CLAUDE.md) Playwright mot `bun run dev` (port 5183), mockApi + `?dev_user=`-bypass, både normalt (ljust) och tvingat mörkt `colorScheme` (för att reproducera B-146–B-149:s OS-scheme-läcka, som annars kräver en mörk-preferens-webbläsare). - `/app/farms/farm-1/weekly-plan` — grid:en visar nu måndag 27 juli → söndag 2 augusti med "Idag"-badge på rätt dag (tisdag 28 juli). Skärmdump: `k1-b144-weekly-plan.png`. - `/app/farms/farm-1/calendar`, månadsvy @1000px och @1280px — alla sju kolumner (MÅN–SÖN) lika breda (89px @1000px), söndagskolumnen inte längre klippt, chip "Skift" fullt synlig. Skärmdumpar: `k1-b145-calendar-1000.png`, `k1-b145-calendar-1280.png`. - `/app/farms/farm-1/schedule`, årsvy under tvingat mörkt `colorScheme` — dagssiffror mörka och läsbara (uppmätt `color: color(srgb 0.16 0.13 0.09 / 0.42)` = `--color-text` både i ljust och mörkt schema, mot tidigare vitt i mörkt schema). Skärmdump: `k1-b146-schedule-year.png`. - `/app/farms/farm-1/schedule`, dagsvy under tvingat mörkt `colorScheme` — heldags-bandet visar cream-yta + burgundy "juli 28"-chip i stället för en svart ruta. Skärmdump: `k1-b148-b149-schedule-day.png`. - Grid/list-växlaren under tvingat mörkt `colorScheme` — klick på List-ikonen ger synligt burgundy aktiv-tillstånd (mätt via computed style + zoomad skärmdump före/efter) och renderar om till listvyn med händelsen. Skärmdumpar: `k1-b147-zoom-before.png`, `k1-b147-zoom-after.png`, `k1-b147-full-after.png`. - `/app/farms/farm-1/tasks/
  • 8d6dd5d K1: markera B-144..B-150 som åtgärdade i KNOWN_BUGS.md (PR #163) Jonas · 2026-07-28 · +79 −30
  • dcc094b K1: visuell verifieringsdokumentation (skärmdumpar) för B-144..B-150 Jonas · 2026-07-28 · +0 −0
  • 4d6d71d B-150: filterpanelen på Alla uppgifter använder korrekt kort-token Jonas · 2026-07-28 · +5 −1
  • b798486 B-146..B-149: tematisera vendrade lit-schedule-kalendern för ljus palett Jonas · 2026-07-28 · +66 −4
  • 98e1ba0 B-145: kalenderns månadsvy klipper inte längre söndagskolumnen Jonas · 2026-07-28 · +8 −2
  • 52543cf B-144: veckoplan börjar nu på måndag, inte rullande 7-dagarsfönster från idag Jonas · 2026-07-28 · +20 −5

#162 Fix K3 cluster: B-128, B-130, B-153, B-155 (Djur & produktion)

2026-07-28 · Jonas · 5 commits · 1 filer · +107 −41 · Buggfixar B-128 B-130 B-153 B-155

Detaljer
## Summary - **B-128** — Slaughter booking page (`/movements/slaughter`) never called `animalStore.sync(...)`; a cold direct navigation/bookmark left the animal picker empty. `onMount` now fetches and syncs the store itself, same pattern as `/animals` and the farm overview page. - **B-130** — Cattle herd-test dialog (`/production/milk`): `submitRecord()` saved local-first then immediately re-fetched from the server, whose response could still be stale (outbox flush lag), so the row stayed "overdue" until a second save. Now optimistically merges the just-saved event into the local list, and `loadEvents()` merges rather than overwrites. - **B-153** — Bulk animal action bar buttons rendered oversized. Two causes: invalid `size="s"` (now `size="small"`) and a redundant `role="button"` on `<wa-button>` that double-applied the C-07 big-finger 56px accessibility floor (once on the host, once on the shadow part) — up to 84px instead of 56px. - **B-155** — Production module nav-cards: long single-word labels (e.g. "Slaktkroppsåterkoppling") overflowed the card. Added `overflow-wrap: anywhere` + `hyphens: auto` + `min-width: 0`, widened the grid minimum from 160px to 180px. ## Test plan - ✅ `SKIP_BACKEND=1 npm run check:ci` green (frontend only — svelte-check, eslint, vitest, i18n, WA bool-attr guard, a11y contrast, token-drift, layout-overflow). All four fixes are frontend-only; backend `sbt test` not required (matches CLAUDE.md guidance for frontend-only changes). - ✅ Visual verification via a throwaway Playwright harness (mockApi + `?dev_user=` bypass, pattern from `other/fas06-01-genomgang/verktyg/`) against `bun run dev`, screenshots read + computed styles measured, script deleted afterward: - `/app/farms/farm-1/production` @ 1000px — nav-card labels wrap inside their cards, `scrollWidth <= clientWidth` on every `.nav-card` and `.nav-card span`. - `/app/farms/farm-1/animals`, select-all → bulk bar — measured button height both with big-finger mode forced off (38px, matches other small controls) and forced on (56px, the correct single application of the accessibility floor vs. the previous 84px double-up); confirmed no `role="button"` left on the host. - `/app/farms/farm-1/movements/slaughter`, cold direct navigation (no prior visit to a page that fills `$animalStore`) — confirmed `GET /animals` fires from this page itself and the animal picker has options. - `/app/farms/farm-1/production/milk?recordHerdTest=bvd` — recorded a BVD herd test with a mock that deliberately keeps returning stale data on the post-save GET (worst-case outbox lag); the row cleared from "overdue" after exactly one save (verified the assertion is discriminating by reverting the fix and confirming it fails). ## Out of scope, flagged separately - `size="s"` is invalid on ~294 files' `wa-button` usages app-wide, not just the bulk bar — out of scope here (touching nav-shell/other clusters' files is forbidden by this task's brief); worth a dedicated sweep. - The same redundant `role="button"` pattern exists on `wa-button` in ~27 other files. - Confirmed a separate, unrelated bug while investigating B-153: `initBigFingerMotionDetect()` (`frontend/src/lib/stores/uiMode.ts`) can spuriously flip "big-finger" mode ON from a single `devicemotion` event carrying an all-null/zero acceleration vector (net magnitude computes to 1.0g, over the 0.6g threshold) — seen consistently in headless Chromium and plausible on some real desktop/laptop browsers too. Not touched here (unrelated to K3). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 0972a3b KNOWN_BUGS: mark B-128, B-130, B-153, B-155 ÅTGÄRDAD (PR #162) Jonas · 2026-07-28 · +50 −27
  • 7dc6614 Fix B-130: herd-test row stuck "overdue" until a second save Jonas · 2026-07-28 · +34 −3
  • aa5c496 Fix B-128: slaughter booking page never fetches the animal list Jonas · 2026-07-28 · +12 −0
  • c3e0c72 Fix B-153: bulk animal action bar buttons render oversized Jonas · 2026-07-28 · +8 −8
  • 12220da Fix B-155: production module nav-card labels overflow their cards Jonas · 2026-07-28 · +3 −3

#161 Log B-161: Today list ~1min load (synchronous LLM enrichment + no client cache)

2026-07-28 · Claude (AI) · 1 commits · 1 filer · +30 −0 · Buggfixar B-161

Detaljer
Docs-only: appends B-161 to `other/KNOWN_BUGS.md`. (Renumbered B-159→B-160→B-161 as master took the intervening numbers.) The "Idag" (Today) list takes ~1 minute to load. Code-verified: the frontend re-fetches `/copilot/nudges` on every visit (sessionStorage is only an error fallback), and the backend runs LLM enrichment (daily summary + per-nudge rephrase) synchronously in the HTTP handler, so the first load per (farm, day, locale) blocks on serial LLM calls. Fix direction (matching Jonas's ask): client-side cache with a low stale-time + stale-while-revalidate, and make the LLM enrichment non-blocking. Also notes a `derived_inert` Svelte warning on the same page. No code changes — check:ci not applicable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv
  • 3b6dbd1 Log B-161: Today list ~1min load (synchronous LLM enrichment + no client cache) Claude (AI) · 2026-07-28 · +30 −0

2026-07-27 — 4 direkt-commits

Detaljer
  • 212b43c Log B-159: release log is developer info — move to admin, retire the user-facing surfaces Jonas · 2026-07-27 · +11 −0
  • eb49044 Nav E1 implementation plan: the rail on every /app route Jonas · 2026-07-27 · +1 392 −0
  • db09571 Nav-IA round 2 spec: retire the seven work modes entirely Jonas · 2026-07-27 · +24 −15
  • a6cd87c Nav-IA round 2 spec: retire the MegaMenu — rail everywhere, favorites, palette browse, sitemap catalog Jonas · 2026-07-27 · +134 −0

#160 Log B-143→B-156: i18n, weekly-plan, calendar, work-schedule theming, tasks/upgrade/bulk/production surfaces

2026-07-26 · Claude (AI) · 10 commits · 1 filer · +298 −0 · Buggfixar B-158 B-157 B-156 B-151 B-155 B-150 B-146 B-149 B-145 B-142 B-144 B-143

Detaljer
Docs-only: appends bug entries B-143 through B-156 to `other/KNOWN_BUGS.md` (round-2 live testing, Jonas). No code changes. - **B-143** priorities copilot nudges English (2nd `/copilot/nudges` call site missing `?locale=`). - **B-144** weekly plan starts on today (Sunday), not Monday-anchored ISO week. - **B-145** calendar month view clips the Sunday column at mid widths. - **B-146–B-149** work-schedule `<lit-schedule>` not themed for light palette: year-view white text, dead corner buttons, black all-day band, dead date-chip affordance. - **B-150** all-tasks filter bar uses `neutral-50` → muddy grey-brown on cream. - **B-151** upgrade page: h1 collides with card, unstyled back link, hardcoded hex colors. - **B-152** upgrade "Uppgradera" → 400 (likely `requireOwner` mapped to BadRequest) + no vertical button spacing. - **B-153** animals bulk-action bar buttons oversized (`wa-button size="s"` — invalid WA size). - **B-154** bulk status change → 402 `subscription_state` shown as raw JSON; no-op preview should say "already active". - **B-155** production module tile labels overflow their cards (no `overflow-wrap`). - **B-156** copy/IA: rename nav label "Välfärd" → "Hälsokontroll" (matches destination page); logged for the fix thread. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv
  • 9c0be45 Log B-158: bell icon opens notification settings instead of a notifications dropdown (with gear + click-outside close) Claude (AI) · 2026-07-26 · +24 −0
  • 66bc4cc Log B-157: move language picker from topbar into Settings as a separate Language page Claude (AI) · 2026-07-26 · +23 −0
  • 81f7b5e Log B-156: rename nav label 'Valfard' to 'Halsokontroll' to match destination page Claude (AI) · 2026-07-26 · +18 −0
  • 95f286b Log B-151..B-155: upgrade page styling/400/spacing, bulk buttons oversize, bulk 402 gate UX, production tile overflow Claude (AI) · 2026-07-26 · +104 −0
  • cc35f2d Log B-150: all-tasks filter bar uses neutral-50, renders muddy grey-brown on cream Claude (AI) · 2026-07-26 · +15 −0
  • 5a8075d Log B-146..B-149: lit-schedule not themed for light palette (year contrast, dead view buttons, black all-day band, dead date-chip affordance) Claude (AI) · 2026-07-26 · +56 −0
  • 51c782c Log B-145: calendar month view clips Sunday column at mid widths (two-col layout + overflow:hidden) Claude (AI) · 2026-07-26 · +20 −0
  • 85be3f3 B-142: add weekly-plan print example (bottom bar still printed) + log B-144 already committed Claude (AI) · 2026-07-26 · +5 −0
  • 0ee4607 Log B-144: weekly plan starts on today (Sunday) instead of Monday-anchored ISO week Claude (AI) · 2026-07-26 · +17 −0
  • 3930a83 Log B-143: priorities copilot nudges English; second nudges call site omits ?locale= Claude (AI) · 2026-07-26 · +16 −0

#159 Log B-134→B-141 + 2 wire-up todos: copilot stub, nav redundancy, custom-dashboard, admin-dash styling, voice dialog

2026-07-26 · Claude (AI) · 6 commits · 2 filer · +212 −1 · Buggfixar B-142 B-137 B-138 B-139 B-140 B-141 B-136 B-135 B-134

Detaljer
Docs-only: appends bug entries B-134 through B-141 to `other/KNOWN_BUGS.md`, plus two `ENLANTIS_MASTER_TODO.md` "unfinished feature" entries. Round-2 live testing (Jonas). - **B-134** — `/dashboard/copilot` is a static stub (no handlers/chat state). Reframed as an unfinished feature: wire the AI to the copilot backend. Added to master TODO. - **B-135** — Nav node "Idag i översikten" 307-redirects to `/today` — redundant with "Idag". - **B-136** — Custom-dashboard empty state points to an "Add widget" button that only exists inside the `/edit` view; IA note (add shouldn't be buried under "edit"). - **B-137** — Custom-dashboard widgets aren't user-configurable: raw JSON `{}` textarea, no config UI, weather is a hardcoded stub (no location). Reframed as unfinished feature; added to master TODO. - **B-138** — Admin overview (`/dashboard`) shows red "broken" stat cards + hero, independent of the custom-dashboard column setting (confirmed: persists on 2-col). Likely hardcoded red vs neutral tokens per DESIGN.md. - **B-139** — "Spara översikt" gives no success/failure feedback (`saveLayout` shows no toast, doesn't check `res.ok`). - **B-140** — Röstinmatning dialog content overflows its container (horizontal scroll). - **B-141** — Voice "Tolka om" surfaces a raw "HTTP 402" instead of a friendly Pro-gate message; the 402 is the by-design `VoiceLlm` billing gate on the LLM fallback path. No code changes — check:ci not applicable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv
  • 1491f02 Log B-142: no global print stylesheet; nav shell prints and tables overflow the page Claude (AI) · 2026-07-26 · +20 −0
  • 20eceb8 Log B-137/B-138/B-139/B-140/B-141: custom-dashboard widgets unfinished, admin-dash red styling, save feedback, voice dialog overflow + 402 gate UX Claude (AI) · 2026-07-26 · +115 −0
  • a8c8001 Log B-136: custom-dashboard empty state references missing Add-widget button + IA note Claude (AI) · 2026-07-26 · +29 −0
  • 87f22c0 Log B-135: redundant nav node 'Idag i översikten' 307-redirects to /today Claude (AI) · 2026-07-26 · +19 −0
  • 9e49822 B-134: reframe as unfinished feature (wire up dashboard-copilot AI); add to master TODO Claude (AI) · 2026-07-26 · +5 −1
  • 4bcdf1a Log B-134: dashboard-copilot page is a dead stub (no click handlers) + content overflow Claude (AI) · 2026-07-26 · +24 −0

#158 Log B-131 + B-132: side-rail auto-expand & copilot daily summary always English

2026-07-26 · Claude (AI) · 1 commits · 1 filer · +46 −0 · Buggfixar B-132 B-133

Detaljer
Docs-only: appends B-131 and B-132 to `other/KNOWN_BUGS.md`. - **B-131** — Entering barn mode ("I stallet") shows the AI-copilot, Djur, and Produktion groups already expanded; only the Today/planning bucket should be open. Code note: `FARM_BUCKETS` marks only `today` as `defaultOpen`, and `SideRail` has no auto-expand-active logic — so the extra-open groups come from persisted localStorage or a force-open path (flagged for the fix thread to confirm). - **B-132** — The "Dagens sammanfattning" copilot summary on the Today page renders in English even when Swedish is selected. Code-verified: the backend `GET /copilot/nudges` route defaults `?locale=` to `"en"` (`CopilotRoutes.scala:103`), and the frontend `loadNudges` (`today/+page.svelte:95`) calls it without `?locale=` — so the LLM generates the summary (and rephrased nudges) in English. The LLM prompt itself honors the locale. Fix direction: pass the current UI locale as a query param. No code changes — check:ci not applicable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv
  • 8a28356 Log B-132 + B-133: side-rail auto-expand & copilot daily summary always English Claude (AI) · 2026-07-26 · +46 −0

2026-07-26 — 8 direkt-commits

Detaljer
  • df7746c B-131: stop the simulator at a sim time ceiling instead of spinning on MySQL's 2038 wall (#157) Jonas · 2026-07-26 · +769 −113
  • 65ca639 Session log: 2026-07-26 orchestration session (PRs #152-#156) Jonas · 2026-07-26 · +1 −1
  • 3183048 B-120 PR 4: registry nav IA is the default — flags default on, farm-page longlist deleted (#156) Jonas · 2026-07-26 · +139 −1 362
  • 6ccf795 Sim-chores Fas A: visible supervision loop in the teaching-sim HUD + the bcs verb (#155) Jonas · 2026-07-26 · +812 −8
  • e513a9f wiki: document the billing-gate tier source (PR #154 follow-up) Jonas · 2026-07-26 · +3 −0
  • b2b184b fix(billing): read the premium-feature tier from farm.subscription_tier (#154) Jonas · 2026-07-26 · +108 −22
  • d3dedb5 Sim-chores spec (Fas A) + sim-thread handover + schema_migration doc fix (#153) Jonas · 2026-07-26 · +445 −2
  • 5b6cdcc B-120 PR 3: slim the farm page, shortcuts + compact modes in the sheet (#152) Jonas · 2026-07-26 · +625 −55

2026-07-25 — 1 direkt-commits

Detaljer
  • a563a35 B-120 PR 2: mobile thumb navigation — bottom sheet + raised scan (#151) Jonas · 2026-07-25 · +1 292 −50

2026-07-24 — 4 direkt-commits

Detaljer
  • 5927619 Teaching-simulator foundation: pig-husbandry knowledge base + husbandry_rule seed + consequence model (#148) Jonas · 2026-07-24 · +627 −0
  • ff070f0 Sim onboarding: tutorial, HUD menu, entry points, gamification, pan/rotate (#142) Jonas · 2026-07-24 · +1 537 −158
  • 2ab035d B-120 PR 1: persistent desktop side rail from the nav registry (#149) Jonas · 2026-07-24 · +865 −191
  • 3b89445 Fix AI-Ops chat thread that loops re-asking the same question (#147) Jonas · 2026-07-24 · +181 −54

#150 Log B-130: cattle herd-test row persists until second save (local-first vs server-GET race)

2026-07-24 · Claude (AI) · 1 commits · 1 filer · +21 −0 · Buggfixar B-130

Detaljer
Docs-only: appends B-130 to `other/KNOWN_BUGS.md`. In the cattle herd-test (BVD) dialog on the milk-production page, saving a test doesn't clear the overdue row on the first attempt — it takes two saves. Code-supported root cause: `submitRecord` writes local-first (Dexie + outbox) then calls `loadEvents()`, which re-reads from the **server** (`apiFetch(.../events?type=cattle_herd_test_result)`) before the outbox has flushed — so the first refresh returns stale data. Includes a fix direction (derive overdue rows from the local Dexie source, or insert the just-saved event optimistically). No code changes — check:ci not applicable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv --- _Generated by [Claude Code](https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv)_
  • a94a519 Log B-130: cattle herd-test row persists until second save (local-first vs server-GET race) Claude (AI) · 2026-07-24 · +21 −0

#146 feat(ai-ops): activate KB tool loop + real cron so AI Ops works fully on Hermes

2026-07-24 · Jonas, Claude (AI) · 3 commits · 17 filer · +780 −143 · Tester

Detaljer
## What Makes the **AI Ops dashboard fully functional locally** on the Hermes (Ollama) transport — agents with working memory and true scheduled runs. Follows PR #127 (Hermes as third LLM transport). Three gaps + one blocking bug closed. ## 1. Dispatcher tool-use loop (agents get memory) `AnthropicAiAgentDispatcher` previously did a single `LlmClient.complete` per turn — no tools, no memory. It now runs a bounded `completeWithTools` loop over the knowledge-base tools (`service/kb/KbToolDefs.scala` — `search_kb` / `read_wiki_page` / `write_memory`), mirroring `CopilotAgentService`'s house pattern (three-way `toolUses` branch, `maxSteps`/`maxToolCalls` caps, wall-clock timeout). Role agents can now **read curated wiki + accumulated memory and durably write learnings during a chat or scheduled run**. - Provenance (`author` = role slug, `source` = thread/run id) is **executor-supplied** to `write_memory`, never model-supplied. New `source: Option[String]` on the dispatcher trait's `chat`/`runScheduled` (defaulted → back-compatible; `FakeRepos`/Regelradar unchanged). - New `AiAgentConfig` (`enlantis.aiOps.*`) for the loop budgets — 180s wall-clock since Hermes is slower. ## 2. Hermes role-model fix (blocking bug) AI Ops roles default to `claude-opus-4-7`, which isn't a key in `hermesModelMap` and would be sent verbatim to Ollama → **404**. New `CopilotLlmConfig.hermesSingleModel` returns the one gateway model (override-aware — honours `HERMES_LLM_MODEL`); the dispatcher substitutes it when `provider == Hermes`. `hermesModelOf`'s "404 on unknown model" contract for Vertex is left untouched. ## 3. Real cron `cron_spec` was a label; scheduling was a `now + 1h` placeholder. New `service/CronSpec.scala` — dependency-free 5-field parser + `nextRunAfter` evaluated in the schedule's **timezone (default `Europe/Stockholm`, DST-aware via `java.time`)**. Migration **0249** adds `ai_schedule.timezone`. `AiOpsRoutes` + `AiOpsWorker` compute the next firing from the real spec at all three sites (create / update / after-fire) and validate the spec + zone (400 on bad input). ## Testing - `CronSpecSpec` — field syntax, ranges/steps/lists/names, dom/dow OR-semantics, unsatisfiable specs, and the Europe/Stockholm summer/winter UTC offsets + spring-forward-gap safety. - `AiAgentDispatcherSpec` rewritten for the tool loop — scripted `completeWithTools` turns + a fake `KnowledgeBase`: tool dispatch, executor provenance stamping, Hermes model substitution, `maxSteps` cap, live/stub gating. - `hermesSingleModel` (default + override) in `LlmTransportSpec`. ✅ check:ci green (frontend guards incl. eslint + full `sbt test`). ## How to run locally Prereqs: local MySQL `enlantis_v10` (boot once → migrations seed the 10 roles + tables), sign in once so your root-admin email resolves, and `ENLANTIS_LLM_PROVIDER=hermes` + `HERMES_LLM_URL` set. Then `/app/admin/ai-ops`: chat with a role agent (ask it to remember something, then recall it in a new thread), and create a schedule (`0 8 * * MON` → fires 08:00 Swedish time). Durable cross-restart memory needs `ENLANTIS_KB_BUCKET`; without it memory is in-RAM but fully testable within a run. ## Notes / risks - Gemma multi-step tool-calling is unproven at loop depth (single calls verified on the gateway); `maxSteps`/`maxToolCalls` bound it, and `HERMES_LLM_MODEL=qwen3.5:cloud` (stronger at tools) is picked up automatically by `hermesSingleModel`. - Prod (Vertex) is untouched. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01RRgzV27Rjq3irekxBoaoEf --- _Generated by [Claude Code](https://claude.ai/code/session_01RRgzV27Rjq3irekxBoaoEf)_
  • 82f95d4 fix(ui): clear 15 layout-overflow guard violations (topbar icons + today card) Jonas · 2026-07-24 · +14 −3
  • d47b270 test(ai-ops): cover maxToolCalls budget cap + fix Schedule cron tz label Jonas · 2026-07-24 · +30 −1
  • dba6f41 feat(ai-ops): activate KB tool loop + real cron for local AI Ops Claude (AI) · 2026-07-23 · +736 −139

#145 Log B-129: topbar icon hover bleeds over bottom border + round/square inconsistency

2026-07-24 · Claude (AI) · 1 commits · 1 filer · +21 −0 · Buggfixar B-129

Detaljer
Docs-only: appends B-129 to `other/KNOWN_BUGS.md`. Topbar right-hand icon group: the hover background bleeds over the topbar's black bottom border, and the controls are inconsistently shaped — icon buttons are round (`border-radius: 999px`) while the `SE SV` locale chip and context badges are square (`4px`). Includes an open design question for Jonas (round vs square — pick one) before the fix lands. (Renumbered from B-128 → B-129: master had taken B-128 for the slaughter-page animal-list bug.) No code changes — check:ci not applicable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv --- _Generated by [Claude Code](https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv)_
  • 156659f Log B-129: topbar icon hover bleeds over bottom border + inconsistent round/square shape Claude (AI) · 2026-07-23 · +21 −0

2026-07-23 — 6 direkt-commits

Detaljer
  • 2598d9b Fix check:ci frontend-deps install failing behind the agent proxy (#144) Jonas · 2026-07-23 · +26 −2
  • 6fcafa7 Fix content-overflow bugs (B-126/B-127 + 2 more) and extend the overflow gate to marketing (#141) Jonas · 2026-07-23 · +129 −14
  • 3d7a032 Log B-128: slaughter booking page never syncs animalStore (#143) Jonas · 2026-07-23 · +24 −0
  • 8597b6e B-124: farm-scoped "Stallar & Områden" overview + URL-carried house drill-down (#140) Jonas · 2026-07-23 · +621 −152
  • 27453dd B-113/B-114 Phase 1: rule-driven welfare-check status engine (backend) (#138) Jonas · 2026-07-23 · +1 727 −9
  • 02b25a9 Fix sim HUD/UX bug cluster (B-100, B-101, B-104, B-107) + B-117 seed split (#137) Jonas · 2026-07-23 · +537 −87

#139 Log B-127: chip overflows demo card on marketing /home

2026-07-23 · Claude (AI) · 1 commits · 1 filer · +12 −0 · Buggfixar B-127

Detaljer
Docs-only: appends B-127 to `other/KNOWN_BUGS.md`. The black "UTESLUTEN UR LIVSMEDELSKEDJAN" chip overflows its card in the treatment demo section on the marketing `/home` page. Third surface in the B-121/B-126 overflow class — to be fixed together with the app-wide overflow CI gate. No code changes — check:ci not applicable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv --- _Generated by [Claude Code](https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv)_
  • 9b1f26e Log B-127: food-chain-exclusion chip overflows demo card on marketing /home Claude (AI) · 2026-07-23 · +12 −0

2026-07-22 — 4 direkt-commits

Detaljer
  • d3875be B-125: Full user management — admin user directory, create-with-invite, TRUE impersonation (#129) Jonas · 2026-07-22 · +2 653 −76
  • 27f2f68 Fix sim animal data bug cluster (B-097, B-098, B-103, B-110, B-112) (#130) Jonas · 2026-07-22 · +300 −172
  • 1a7b42b Fix B-118 + B-119 + B-121: icon vendoring, undefined farmId guards, card overflow + new app-wide layout-overflow CI gate (#128) Jonas · 2026-07-22 · +971 −94
  • 007b9ad ci: make perf-budget workflow manual-only to stop auto-run Actions minutes (#133) Jonas · 2026-07-22 · +7 −12

#136 Fix B-099: replace dark procedural sim drone with pre-rendered music loops

2026-07-22 · Claude (AI) · 2 commits · 7 filer · +446 −58 · Buggfixar B-099

Detaljer
# B-099 — Sim background music: rural & positive pre-rendered loops ## What / why Jonas found the sim's procedural drone (dark A2+E3 oscillator pad) "mörk och dyster" (B-099). This PR retires the drone and ships three **product-owner-approved pre-rendered music loops**: composed in MIDI, rendered through the GeneralUser GS soundfont with reverb, tail-wrapped into seamless **80.000000 s** loops (loop tail baked around the start; sample-exact loop points at file start/end), AAC-encoded for shipping. - `frontend/static/sim/music/loop-{a,b,c}.m4a` — new shipped assets - `frontend/src/lib/sim3d/audio.ts` — player rework (same public interface: `start` / `toggleMute` / `isMuted` / `blip` / `dispose`; `routes/sim/[farmId]/+page.svelte` untouched — parallel PR #130 owns that file) - `frontend/src/lib/sim3d/audio.test.ts` — unit tests for the AAC-delay trim (`loopPoints()`) - `other/KNOWN_BUGS.md` — B-099 moved to **Åtgärdade** ## Encoding ffmpeg native AAC-LC, **120 kbps stereo 44.1 kHz**, `-movflags +faststart` (encoder obtained via npm since GitHub-release downloads are egress-blocked in the sandbox: `@ffmpeg-installer/ffmpeg`; `ffmpeg-static`'s postinstall 403s). | File | Size | |---|---| | loop-a.m4a | 1 219 370 B (1.22 MB) | | loop-b.m4a | 1 220 176 B (1.22 MB) | | loop-c.m4a | 1 219 268 B (1.22 MB) | ## AAC encoder delay + loop-seam proof ffmpeg's AAC encoder adds **1024 priming samples** at the head (declared in the MP4 edit list) and **704 padding samples** at the tail. Measured by decoding each shipped .m4a back to PCM and cross-correlating against the source WAV: - Edit-list-**aware** decode: 3 528 704 frames → head offset **0**, tail pad 704 → `loopStart=0`, `loopEnd=80.0` - Edit-list-**ignoring** decode: 3 529 728 frames → head offset **1024**, tail pad 704 → `loopStart=1024/44100≈0.02322 s`, `loopEnd=loopStart+80` The player detects which case it got from the decoded duration at runtime (threshold midway between the two) and sets `loopStart`/`loopEnd` accordingly. **Seam discontinuity at the loop join** (|sample delta| across loopEnd-1 → loopStart, decoded PCM, vs neighboring sample deltas around the seam): | Loop | seam |Δ| L / R | neighbor Δ mean / max | |---|---|---| | a | 0.002590 / 0.004062 | 0.001270 / 0.005040 | | b | 0.000401 / 0.000486 | 0.000501 / 0.001449 | | c | 0.000787 / 0.000867 | 0.000491 / 0.001301 | Seam deltas are at (or below) the level of ordinary neighboring sample deltas → **no click**. Wrap-match mean error over the first 64 frames after loopEnd vs after loopStart: 0.005–0.011 (lossy-codec noise floor), confirming the tail really is the wrapped start. ## Player behaviour - One of three variants chosen at random per session; after **2–3 full passes**, **3 s equal-power crossfade** (sin/cos curves via `setValueCurveAtTime`) to the next variant through two gain nodes; the faded-out decoded buffer (~27 MB) is dropped. - Mute toggle unchanged (HUD button, off by default); volume LOW — master gain **0.25**. - Autoplay policy unchanged: `AudioContext` only created on a user gesture (play / un-mute). - Interaction blips unchanged. - Music load failure is non-fatal (sim never breaks if audio is unavailable). - Dev-only `window.__simAudio` hook for browser verification (absent in prod builds). ## Gates (frontend) - ✅ `check` (0 errors) - ✅ `lint` (0 errors) - ✅ `test` (2 883 passed, incl. 4 new `loopPoints` tests) - ✅ `i18n:check` - ✅ `wa:check` - ✅ `a11y:check` (50 pairs, 0 failing) - ✅ `tokens:check` - ✅ `overflow:check` (28 route+viewport combos) - ⚠️ backend `sbt test` not run (frontend-only change, no Scala touched) ## Verification **Offline (objective):** Node script decodes the shipped .m4a via ffmpeg to PCM, cross-correlates the head against the source WAV, and measures the seam (numbers above). **In-browser (Playwright, Chromium, dev server on :5333, mockApi + `?dev_user=` harness):** `other/fas06-01-genomgang/verktyg/b099-sim-music-verify.mjs` — **11/11 checks PASS**: - no `Aud
  • 6e584ab Add PR reference #136 to the B-099 KNOWN_BUGS entry Claude (AI) · 2026-07-22 · +1 −1
  • e130881 Fix B-099: replace dark procedural sim drone with pre-rendered music loops Claude (AI) · 2026-07-22 · +445 −57

#131 fix(sim3d): cursor-anchored zoom (B-109) + world-sized ground plane (B-115 part 1)

2026-07-22 · Claude (AI) · 2 commits · 2 filer · +380 −31 · Buggfixar B-109 B-115

Detaljer
## What / why ### B-109 — Scroll-zoom toward the cursor, not screen center The wheel handler in `frontend/src/lib/sim3d/scene.ts` only mutated `orbit.radius`; the camera position derives per-frame from the fixed `orbit.target`, so scroll-zoom always converged on the world target (≈ screen center). Now each zoom-in unprojects the pointer onto the y=0 ground plane and eases `orbit.target` toward that point in proportion to the radius change (`k = 1 − r1/r0`), so the view converges on whatever the mouse hovers. Zoom-out keeps the target (plain dolly out). The target is clamped to the world footprint. ### B-115 (world-boundary half) — Pen no longer sits outside the world The ground plane was a fixed `PlaneGeometry(120,120)` centered at `(6,0,4)` (X ≈ −54…+66), while the multi-site scenario places the finisher barn/pen at X 60…72 — it overhung the edge onto the sky backdrop. `setWorld` now computes the world footprint over **buildings AND pens**, sizes/re-centers the ground plane from it (+40 m grass apron, min 120 m so small scenarios look unchanged), and reuses the same bounds for camera framing and the zoom-target clamp. The empty-pen half of B-115 is handled in a separate PR. Changes are scoped to `lib/sim3d/scene.ts` plus a new verification script; `other/KNOWN_BUGS.md` updated (B-109 → Åtgärdade, B-115 part 1 noted fixed). ## Gates (run step-by-step in the agent sandbox, frontend only — no Scala touched) - ✅ `npm run check` (svelte-check: 0 errors) - ✅ `npm test` (2879 tests, 160 files, all passed) - ✅ `npm run i18n:check` - ✅ `npm run wa:check` - ✅ `npm run a11y:check` (50 pairs, 0 failing) - ✅ `npm run tokens:check` - ⚠️ `sbt test` not run (no backend changes; frontend-only PR) ## Visual verification (mandatory 3D rule — rendered and READ in a real browser) Playwright (Chromium) against `bun run dev --port 5283` with the mocked sim-world API (`other/fas06-01-genomgang/verktyg/b109-b115-camera-world-verify.mjs`, harness pattern + `?dev_user=` bypass). Full-page screenshots come back white in the sandbox whenever a WebGL/WebGPU canvas is present, so the script captures the 3D canvas pixels via `toDataURL` inside a `requestAnimationFrame` (right after the scene's render). Scenarios rendered and inspected: 1. **Multi-site ("Flerplatsdrift") overview** — three 12×8 barns at posX 0/30/60 with 6×6 pens (sow pen stocked with 4 sows + 10 weaners, finisher pen with 3 finishers). **Seen:** all three barns and pens sit fully on the green ground plane with a wide grass apron on every side; the finisher barn/pen (X 60–72) that previously overhung onto the blue backdrop is entirely on green. 2. **B-109 zoom sequence** — pointer parked at (0.80 w, 0.73 h), over the off-center finisher pen; 26 wheel-in events dispatched, screenshots before / mid / after, then 26 wheel-out events. **Seen:** the view progressively converges on the finisher pen — after full zoom-in the finisher pen and its three pig models fill the lower-right/center of the frame (before: whole farm framed, finisher small in the corner); zoom-out returns to the full-farm framing with all pens on the ground plane. Also confirmed numerically in-page: `orbit.radius` 60 → 45 after 10 wheel events with `orbit.target` moving from (36, 4) toward the pointer's ground point. 3. **Grisbox (pig-pen-mvp) regression** — single 12×8 barn, 10 pigs. **Seen:** unchanged look; barn centered on a full-size (min 120 m) ground plane. Note for future sim verification: the first-run coach's `coach-backdrop` overlays the canvas and swallows real (hit-tested) wheel events — set `localStorage['sim-coached']='1'` before load (the verify script now does this via an init script). Screenshots under `other/fas06-01-genomgang/verktyg/shots/` (not committed): `b115-01-multisite-overview.png`, `b109-01-before-zoom.png`, `b109-02-mid-zoom-in.png`, `b109-03-after-zoom-in.png`, `b109-04-after-zoom-out.png`, `b115-02-grisbox-regression.png`. ## Not touched (per task scope) - B-111 pen inner-zone color
  • d2a46ff docs(known-bugs): B-109 fixed, B-115 world-boundary half fixed Claude (AI) · 2026-07-21 · +34 −15
  • 34ba30f fix(sim3d): cursor-anchored zoom (B-109) + world-sized ground plane (B-115) Claude (AI) · 2026-07-21 · +346 −16

#126 Fix B-122: house dashboard cards all linked to Stall1

2026-07-22 · Claude (AI) · 2 commits · 3 filer · +237 −21 · Buggfixar B-122

Detaljer
## What / why Every house card in the farm dashboard's houses-grid (`/app/farms/:id`, `+page.svelte` ~line 1445) rendered a hardcoded link `<a href="/app/farms/{farmId}/areas" class="house-card-link">` with no `house.id` interpolated anywhere. The areas overview scopes itself by the global `selectedHouseId` store, which normally still held whatever house was already selected (usually the first one) — so **every** card navigated to the areas overview scoped to "Stall1", regardless of which card was actually clicked. Fix mirrors the B-081 fix in `TopBar.svelte`'s `pickHouse`: each card's `onclick` now calls a new `pickHouseCard(houseId)` helper, which sets `selectedHouseId` to the clicked card's house **before** navigating to `/app/farms/:id/areas`. The anchor keeps its `href` (still an accessible, focusable `<a>`, not a dead anchor) — `onclick` calls `preventDefault()` and does the state update + `goto()` itself so the store update always happens before the areas view mounts. Minimal, scoped fix — does not touch the separate planned IA rework of this page (B-120/B-124, farm-scoped areas overview + breadcrumb), per the ticket. ## Gate results (frontend/, from repo root `scripts/check-ci.mjs` step list) - ✅ `npm run check` — 0 errors (3253 files, pre-existing warnings elsewhere only) - ✅ `npm test` — 2879 passed (160 files) - ✅ `npm run i18n:check` — all `$t()` keys covered - ✅ `npm run wa:check` — all `<wa-*>` boolean-attr bindings guarded - ✅ `npm run a11y:check` — 50 contrast pairs checked, 0 failing - ✅ `npm run tokens:check` — 894 files, 0 token drift No Scala touched, so `sbt test` was not run (frontend-only change). ## Visual verification (Playwright, sandbox — no Claude in Chrome available here) Ran `bun run dev -- --port 5263` + a new mocked-API Playwright script, `other/fas06-01-genomgang/verktyg/b122-house-card-links-verify.mjs`, seeded with 3 houses (Stall 1/2/3, each with one distinct area Box 1/2/3): - 3 house cards render on the farm dashboard, each with correct name/species/area (screenshot read: `b122-01-farm-dashboard-houses.png`). - Clicking card 2 ("Stall 2") sets `selectedHouseId=house-2` and navigates to `/app/farms/farm-1/areas`, which shows only "Box 2" — Box 1 and Box 3 (the other houses' areas) are correctly absent (screenshot read: `b122-02-areas-scoped-to-house2.png`). - Returning to the dashboard and clicking card 1 sets `selectedHouseId=house-1` (not stuck on house-2 from before) — confirms per-card scoping, not just "always goes to house 2" (screenshot read: `b122-03-areas-scoped-to-house1.png`). - All 8/8 script assertions pass. Console showed only pre-existing/unrelated noise (mocked `release-notes/latest` 404, sandbox-network-blocked Font Awesome icon-fallback CDN requests) — nothing related to the fix. - Screenshots were opened and visually reviewed (not just saved) as required by CLAUDE.md's mandatory visual-verification rule. Not committed (local artifacts under `other/fas06-01-genomgang/verktyg/shots/`, per repo convention for run-specific screenshots). ## KNOWN_BUGS.md B-122 moved from "Öppna" to "Åtgärdade" with root cause, fix, and verification summary (Swedish, per repo convention). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CA4NYUon2SkWNevu8NtqrJ --- _Generated by [Claude Code](https://claude.ai/code/session_01CA4NYUon2SkWNevu8NtqrJ)_
  • 18ec65a docs: fill in PR #126 reference for B-122 fix in KNOWN_BUGS.md Claude (AI) · 2026-07-21 · +2 −2
  • 04a20a1 Fix B-122: house dashboard cards all linked to Stall1 Claude (AI) · 2026-07-21 · +235 −19

#134 Add ESLint gate to check:ci (@typescript-eslint + eslint-plugin-svelte)

2026-07-22 · Claude (AI) · 1 commits · 23 filer · +381 −42 · Funktioner

Detaljer
## What & why The app had **no ESLint** — `svelte-check` gives typechecking but no linting (unsafe `any`, dead code, Svelte 5 runes/markup traps, markup-a11y, stray `console`). This adds an ESLint flat-config lint gate wired into both the local CI runner (`scripts/check-ci.mjs`) and `.github/workflows/ci.yml`, right after `svelte-check`. > Context for the type-coverage question that prompted this: `tsconfig` already has `strict: true` and **100% of `.svelte` components use `lang="ts"`**. Exact `type-coverage` over the `.ts` program is **91.95%** strict / **92.77%** default — so a 100% gate was deliberately *not* added (it would be a wall of `any`-suppressions, and the tool can't even see `.svelte` scripts). ESLint was the real gap; this PR fills it. ## The gate - **Config:** `frontend/eslint.config.js` — `@eslint/js` + `typescript-eslint` + `eslint-plugin-svelte` recommended sets, Svelte 5-aware (TS parser for `<script lang="ts">`, real `svelte.config.js` handed to the plugin). - **Scope:** shipped app only — `src/**` + `e2e/**`. Build output (`.svelte-kit`, `build/`, `static/`), native shells (`ios/`, `android/`), and node build-tooling (`scripts/**`, root `*.config.*`) are excluded. - **Adoption strategy:** green on the current codebase, **gates on errors only** (`eslint .` exits non-zero on errors, not warnings). Legacy debt is `warn` — a visible ratchet backlog (2 179 warnings today), not a blocker. Real-bug rules stay `error`. - `warn`: `no-explicit-any`, `no-non-null-assertion`, `no-unused-vars`, `no-console`, `svelte/require-each-key`, `svelte/no-at-html-tags`, `svelte/prefer-svelte-reactivity`, … - `off`: `svelte/no-navigation-without-resolve` (`<base>`-path future-proofing this app doesn't use, ~1 900 hits) and core `no-undef` (TS already checks it; false-positives on TS/Svelte — per typescript-eslint guidance). ## Fixes needed to reach green - **JSON-LD `</script>` parse errors (8 files):** `{@html}` blocks embedded a raw `</script>` literal inside a template string, which the svelte-eslint-parser misreads as the component's `<script>` closing (the Svelte compiler / `svelte-check` handle it fine). Split to the `</` + `script>` idiom **already used elsewhere in the repo** — rendered output is byte-identical (invisible SEO metadata, no visual change). - **Small real fixes** surfaced by error-level rules: remove dead code (an unused `#hours` field; an empty copilot-mode `if/else` branch + its now-unused import), `let`→`const`, `!Boolean(x)`→`!x`, drop two stale `eslint-disable` comments. Documented the deliberate Lit self-assign idiom (`SwipeContainer.firstUpdated`) and a TS `export type` re-export false positive (`Schedule.svelte`) with targeted inline disables. ## Testing `bun run check:ci` — **frontend green**: ``` ✅ svelte-check ✅ eslint (lint gate) ✅ vitest ✅ i18n:check ✅ wa:check ✅ a11y:check ✅ tokens:check ⏭ sbt test (backend) — sbt/Java 25 unavailable in this environment ``` Backend `sbt test` could not run here (no sbt/Java 25); **no Scala files are touched** by this change, so the backend is unaffected. Run the backend gate in a Java 25 + sbt environment before merge for the full clean-room signal. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01RKATP6rSDnHqWFHFUdjen2)_
  • ac2d557 Add ESLint gate to check:ci (@typescript-eslint + eslint-plugin-svelte) Claude (AI) · 2026-07-22 · +381 −42

#127 feat(llm): Hermes (Ollama gateway) as third LLM transport for all AI features

2026-07-22 · Claude (AI) · 2 commits · 13 filer · +1 168 −353 · Funktioner

Detaljer
## What Connects Jonas's self-hosted **Hermes gateway** (the Ollama-compatible proxy on the Hetzner server that newsAgg already uses, reached over Tailscale) as a third LLM transport in Enlantis — selectable for **every** AI surface as an alternative to Claude on the Anthropic API. ## How it works - **Explicit provider switch:** `ENLANTIS_LLM_PROVIDER=anthropic|vertex|hermes` (conf `enlantis.copilot.provider`). Unset/empty ⇒ legacy `useVertex` behavior, so **prod (Cloud Run → Vertex/Gemini) is unchanged** without any config edit. Anthropic-direct stays available as fallback. - **Dev usage:** `ENLANTIS_LLM_PROVIDER=hermes HERMES_LLM_URL=http://<hetzner-tailscale-host>:11434 sbt run`. No auth header — the URL is Tailscale-private. Both claude-* logical model names map to `gemma4:cloud` (override all roles via `HERMES_LLM_MODEL`). - **New `OllamaJson` wire object** in `LlmClient.scala` (sibling of `GeminiJson`): `/api/chat` non-streaming, model in body, `options.num_predict`, OpenAI-style `tools` with the JSON Schema passed verbatim, positional tool-result correlation (`role:"tool"`, synthesised `hermes-tc-N` ids), `done_reason` mapping, and `<think>…</think>` stripping for gemma output. Timeouts: complete 30s / tools 60s / vision 60s. - **Coverage:** everything on the `LlmClient` trait follows the switch automatically (Copilot rephrase+summary, Copilot v3 tool agent, AI Ops dispatcher + Regelradar auto-draft, voice parse, product translator, vet chat). **EurLexQa** is refactored onto the trait (its duplicated inline Anthropic/Gemini transport deleted, ~150 lines; keeps a 45s budget via the new `LlmRequest.timeoutSeconds`). **Photo-triage vision** follows the same env vars via `PhotoTriageLlmConfig` + an Ollama path with raw-base64 `images` (any provider other than `hermes` keeps Anthropic — vision has no Vertex path). ## Risks / notes - **gemma tool-calling** is markedly weaker than Claude/Gemini — the Copilot agent loop's max-steps bound contains the blast radius, but treat `completeWithTools` via Hermes as experimental. - No tool_call ids on the Ollama wire — correlation is positional, same posture as the existing Gemini mapping (`gemini-fc-N`). - `num_predict` counts `<think>` tokens, so tight budgets may truncate (visible as `max_tokens` in telemetry). Token counters may be absent → 0-fallback; `cacheReadTokens` always 0. - Vision on `gemma4:cloud` is unverified — if the model rejects images, PhotoTriage surfaces a clean `failed` row (existing posture). - Docs updated: wiki `llm-dual-transport` (now tri-transport), `CLAUDE.md` LLM section (also fixes the stale `gemini-2.5-pro` reference — code maps to `gemini-3.5-flash`), `log.md`, MASTER_TODO. ## Verification ✅ check:ci green (frontend + backend) — svelte-check 0 errors, vitest, i18n/WA/a11y/token guards, `sbt test` 3 241 passed / 0 failed (incl. the extended `LlmTransportSpec`: provider selection, Hermes model map, OllamaJson render/parse round-trips, stripThink, vision images, NotConfigured guards). ⚠️ **Live smoke is Jonas-side only** (the sandbox can't reach the Tailscale gateway): 1. `curl http://<host>:11434/api/tags` — gateway up, `gemma4:cloud` listed 2. `curl http://<host>:11434/api/chat -d '{"model":"gemma4:cloud","messages":[{"role":"user","content":"ping"}],"stream":false}'` 3. `ENLANTIS_LLM_PROVIDER=hermes HERMES_LLM_URL=http://<host>:11434 sbt run` → try a copilot rephrase, the copilot agent (tool loop), EUR-Lex QA, a photo-triage submit; then flip back to `anthropic` and confirm fallback. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01RRgzV27Rjq3irekxBoaoEf --- _Generated by [Claude Code](https://claude.ai/code/session_01RRgzV27Rjq3irekxBoaoEf)_
  • 7e51962 fix(llm): address code-review findings on the Hermes transport Claude (AI) · 2026-07-22 · +249 −138
  • 12a117b feat(llm): add Hermes (Ollama gateway) as third LLM transport Claude (AI) · 2026-07-21 · +919 −215

#132 Log B-126: content overflow on marketing card (/me/settings) + widen overflow CI-gate requirement

2026-07-22 · Claude (AI) · 1 commits · 1 filer · +17 −0 · Buggfixar B-126

Detaljer
Docs-only: appends bug entry B-126 to `other/KNOWN_BUGS.md` (round 2 live testing). - Marketing product-card chips overflow their container on `http://localhost:5173/me/settings` (same class as B-121, different surface). - Notes an open question: why does `/me/settings` render the marketing landing page at all (route-guard fallback?). - Sharpens the B-121 requirement: the app-wide overflow CI check (not yet built) must run on ALL routes/widths, app + marketing, not only new content. No code changes — check:ci not applicable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv --- _Generated by [Claude Code](https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv)_
  • ebf9f43 Log B-126: marketing-card content overflows container on /me/settings; overflow CI gate must cover all routes Claude (AI) · 2026-07-22 · +17 −0

#125 docs: Round 1 simulator/IA findings (B-097–B-124) + next-round handoff

2026-07-21 · Claude (AI) · 3 commits · 3 filer · +673 −11 · Buggfixar B-125 B-097 B-124

Detaljer
## What Documentation only — no code changes. Adds the simulator and information- architecture findings from Jonas's round 1 (2026-07-20) that were not yet in master, plus a handoff for the next fix thread. - `other/KNOWN_BUGS.md`: new open entries **B-097 … B-124** (B-123 withdrawn). - `other/HANDOFF_RUNDA1_SIM.md`: categorized handoff with two root-cause clusters and a suggested PR breakdown. Rebased onto current master — **B-075 … B-096 are already fixed** (PR #107–124), so this branch only carries the not-yet-tracked round-1 remainder. ## Two root-cause clusters 1. **Sim animals not projected as real animals** — B-097 (weight 0), B-098 (animal card "not found", species-agnostic), B-112 (weight not reactive), B-110 (empty pens), B-115 (only one pen populated). 2. **Stall-id handling broken** — B-122 (all stall buttons open Stall1). ## Also inside - Sim 3D/camera (B-109 cursor zoom, B-115 pen outside world, B-111 pen zones, B-116 pan/rotate tools). - Sim UX/HUD (B-101/B-107 overlaps, B-100 karens help, B-108 goals, B-099 music, B-103 back-to-sim, B-117 sow vs finishing-pig model). - Sim features (B-102 tutorial, B-105/B-106 sim entry, B-113/B-114 status indicators). - App IA (B-120 menu system, B-124 stalls-and-areas hierarchy). - App bugs (B-118 FA icons 403, B-119 undefined farmId, B-121 overflow + app-wide overflow test). - Re-verify live: B-036, B-037 (already fixed in master; Jonas saw the old build). ## Scope Reporting/handoff record. Fixes happen in a separate thread per the handoff's PR breakdown. Kept as draft. ## check:ci Docs-only change; no code touched. --- _Generated by [Claude Code](https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv)_
  • 7550001 docs: B-125 requires TRUE backend impersonation (not preview) + audit trail Claude (AI) · 2026-07-21 · +20 −10
  • 6f25982 docs: log B-125 (full user management: list/create/impersonate) + restore categorized summary Claude (AI) · 2026-07-21 · +113 −1
  • 9e74319 docs: log round-1 sim/IA findings B-097-B-124 + handoff for next fix round Claude (AI) · 2026-07-21 · +540 −0

#124 docs: align six KNOWN_BUGS headings with their in-body fix status

2026-07-21 · Claude (AI) · 1 commits · 1 filer · +6 −6 · Funktioner

Detaljer
## What Docs-only. B-004, B-020, B-029, B-042, B-047 and B-051 were all fixed between 2026-07-11 and 2026-07-15 — each entry documents its root cause and fix — but the headings still read `[otriagerad]`/untagged, which made the bug ledger look like it had open items. The headings now carry the same ÅTGÄRDAD date as the body. After this, every `### B-0xx` heading in `other/KNOWN_BUGS.md` carries a resolution tag (ÅTGÄRDAD / INAKTUELL / AVSIKTLIGT / TRIAGERAD+ÅTGÄRDAD) — the ledger has zero open entries. ## Verification Markdown-only change; no code touched. `grep` over the headings confirms no untagged entries remain. --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • 4abfb95 docs: align six KNOWN_BUGS headings with their in-body fix status Claude (AI) · 2026-07-21 · +6 −6

#123 B-007: triage + fix — dead dashboard-widget links

2026-07-21 · Claude (AI) · 1 commits · 6 filer · +148 −7 · Buggfixar B-007

Detaljer
## What B-007 ("broken links/images spread across the app") was an untriaged field observation with no concrete URLs. This PR does the triage the entry asked for, with a new systematic sweep harness (`other/fas06-01-genomgang/verktyg/b007-link-sweep.mjs`): - **Phase 1:** extract all 309 hrefs from the nav registry and status-check each against the dev server → **0 dead routes**. - **Phase 2:** open 15 high-traffic pages in the browser, harvest every in-page `/app` anchor and every `<img>` → **0 broken images**, but **3 dead anchors**, all in the §F-34 dashboard widgets (plus 2 more dead targets in widget kinds that weren't rendered during the sweep). None of the five link targets ever existed as routes, so every click on those widgets 404ed: | Widget | Old target (404) | New target | |---|---|---| | WelfareAggWidget (withdrawal alerts) | `/withdrawals` | `/health/treatments` | | WelfareAggWidget (vaccinations due) | `/vaccinations` | `/health/vaccinations` | | WelfareAggWidget (IR pending) | `/ir-submissions` | `/movements/submissions` | | AnimalsAggWidget (births) | `/events` | `/animals` | | AnimalsAggWidget (treatments) | `/treatments` | `/health/treatments` | | RecentEventsWidget | `/events` | link removed — the widget is the feed; no farm-wide event-log page exists (`WidgetShell.href` is optional) | `other/KNOWN_BUGS.md` B-007 tagged TRIAGERAD + ÅTGÄRDAD with the findings; if Jonas spots more broken links/images live, the entry asks for concrete URLs so they get their own entries. ## Verification - Sweep re-run after the fix: **0 dead links** (309 registry routes + 78 in-page anchors), 0 broken images. Raw report checked in as `verktyg/b007-report.json`. - ✅ check:ci frontend green: svelte-check 0 errors, vitest 2879/2879, i18n:check, wa:check, tokens:check, a11y:check all pass. Backend untouched (frontend + docs only). --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • b838153 B-007: triage + fix — dead dashboard-widget links (/withdrawals, /vaccinations, /ir-submissions, /events) Claude (AI) · 2026-07-21 · +148 −7

#122 B-006/B-014/B-016/B-049: backlog batch 4 — farm-start race, stable shortcut, animal quick-nav, upgrade-page rewrite

2026-07-21 · Claude (AI) · 1 commits · 26 filer · +657 −191 · Buggfixar B-006 B-014 B-016 B-049

Detaljer
## What Backlog batch 4 — the last four tractable open bugs in `other/KNOWN_BUGS.md`: - **B-006 (farm start "Loading…" race)** — root cause confirmed: `let farmId` was a plain non-reactive `let` in a runes-mode component, so `farm = $derived($farmStore.find(...))` never re-evaluated when `onMount` set `farmId` with an already-hydrated store. Now `$state('')`. Additionally `DashboardGrid` loaded its layout in `onMount`, which could fire before the parent resolved the farm id — the live backend error `GET dashboard layout undefined: Invalid UUID string` from Jonas's log. It now loads via a farmId-guarded `$effect` (once per farm id). - **B-014 (first-stable path buried)** — the fix direction in the bug entry asked for a getting-started shortcut: the checklist now has "Skapa ditt första stall" right after the add-animals lead, linking to the area overview. - **B-016 (animal detail: everything buried, no top navigation)** — added a quick-nav chip row at the top of the animal detail page: Edit animal (smooth-scrolls to the edit card) + Weights, Health, Locations, Feed, Documents, Timeline, Print. The management card below is unchanged. - **B-049 (upgrade page broken)** — rewritten: 1. The 400 on the status call: the page authenticated with a bogus localStorage key (`enlantis-user-id`); now `apiFetch`/`authHeaders` like the rest of the app. 2. NaN prices: price display now requires a real number and otherwise falls back to the headline EUR from `/api/public/pricing/tiers` (the fallback the backend DTO explicitly documents) — stub mode shows €12/mån / €29/mån. 3. Broken layout: the old page had **no stylesheet at all** (inert legacy Tailwind classes); now a token-based card grid. 4. English throughout: feature bullets are i18n-keyed (`billing.upgrade.feat.*`) with Swedish translations; tier names aligned to the catalog (Free/Pro/Pro+ per `PricingTiers.scala` — the old page showed a `pro_plus` column that no catalog entry ever matched, plus a "Starter" label that disagreed with every other surface). 5. Off-screen dead confirmation: the confirm "modal" was an unpositioned div at the end of the document flow; now a real `wa-dialog` overlay (light-dismiss, guarded `wa-after-hide`), buttons enabled, real price in the text. The old post-swap redirect to the nonexistent `/stripe` route is gone (stay + toast + reload). 6. The portal error now says *why* when Stripe isn't configured (`billingPortal.notConfiguredHint`). Also: B-044's heading tag aligned with its already-documented 2026-07-18 fix, MASTER_TODO summary paragraph refreshed, KNOWN_BUGS entries tagged ÅTGÄRDAD. After this, the only open KNOWN_BUGS item is B-007 (untriaged field observation "broken links/images" — needs concrete URLs from the next test round). ## Verification Browser-verified against `bun run dev` (Playwright/Chromium, harness `other/fas06-01-genomgang/verktyg/b006-b014-b016-b049-backlog4-verify.mjs`) — **15/15 passed**, screenshots read and checked (`b006-b014-farm-start`, `b016-animal-quicknav`, `b049-upgrade-grid`, `b049-upgrade-confirm`): - farm start renders with no API call carrying a literal "undefined" id - "Skapa ditt första stall" visible in the checklist - quick-nav above the fold with all 8 entries - 3 styled plan cards, no NaN, Swedish bullets, €12/mån stub fallback, centered confirm dialog with enabled buttons ✅ check:ci frontend green: svelte-check 0 errors, vitest 2879/2879, i18n:check, wa:check, tokens:check, a11y:check all pass. Backend untouched (frontend + docs only); sbt suite was green at 3272/0 on the current master base. --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • 686145f B-006/B-014/B-016/B-049: backlog batch 4 — farm-start race, stable shortcut, animal quick-nav, upgrade-page rewrite Claude (AI) · 2026-07-21 · +657 −191

#121 B-017/B-025/B-040: backlog batch 3 — weights back button, breeding-board + regulatory-radar findability

2026-07-21 · Claude (AI) · 1 commits · 11 filer · +128 −8 · Buggfixar B-017 B-025 B-040

Detaljer
## What Backlog batch 3 — three open UX/findability bugs from Jonas's live testing: - **B-017** — the animal weights page had no way back to the animal and the breadcrumb row rendered fused with the H1. Added a "Back — {animal name}" button under the breadcrumb plus spacing between crumb row, button and title. - **B-025** — the breeding kanban was unfindable: Swedish nav label was the half-English "Avel kanban" and the nav node had no search keywords. Renamed the sv label to **"Avelstavla"** and added keywords (`avelstavla`, `tavla`, `kanban`, `kalvning`, `lamning`, `grisning`). Palette search "avelstavla" now hits and navigates to `/breeding/kanban`. (The wider design wish — species-separated breeding boards and unifying the three different page/card names — is out of scope and stays noted in the bug entry.) - **B-040** — Regelradar was unreachable without "Show all features". Added keywords (`regelradar`, `regelverk`, `radar`, `regler`) to the radar node and added the whole `farm.compliance` group to the **office-mode allowlist**, so the radar is clickable from Kontor mode. (A dedicated top-level menu category for regulation remains a design question.) Also fixes a broken test on master: `farmRoleStore.test.ts`'s `$lib/auth` mock was missing the `isSuperAdmin` export added by the view-as-role work (#115+#116 merge interaction), which failed the whole vitest file at collection. Tagged all three bugs ÅTGÄRDAD in `other/KNOWN_BUGS.md` and added the batch-3 Playwright harness + screenshots. ## Verification Browser-verified against `bun run dev` (Playwright/Chromium, harness `other/fas06-01-genomgang/verktyg/b017-b025-b040-backlog3-verify.mjs`) — **6/6 passed**: - B-025: palette shows the "Avelstavla" entry; activating it lands on `/app/farms/farm-1/breeding/kanban` - B-040: palette shows "Regelradar"; activating it lands on `/app/farms/farm-1/compliance/radar` - B-017: back button present with correct href; crumb→title gap 108px Screenshots read and checked: `b025-avelstavla-search.png`, `b040-regelradar-search.png`, `b017-weights-header.png`. ✅ check:ci frontend green: svelte-check 0 errors, vitest 160 files / 2879 tests green (after the mock fix), i18n:check, wa:check, tokens:check, a11y:check all pass. Backend untouched by this diff (frontend + docs only); sbt suite was green at 3272/0 on the current master base. --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • d5ca923 B-017/B-025/B-040: backlog batch 3 — weights back button, breeding-board findability, regulatory-radar findability Claude (AI) · 2026-07-21 · +128 −8

#120 B-021/B-022/B-023/B-028/B-048: backlog batch 2

2026-07-21 · Claude (AI) · 1 commits · 20 filer · +153 −41 · Buggfixar B-021 B-022 B-023 B-028 B-048

Detaljer
## What / why Second batch of the pre-runda-1 open bugs — all navigation/UX gaps. | Bug | Fix | |---|---| | **B-021** | DPA re-acknowledgement dialog no longer mounts on **error/404 pages** (`!page.error` gate in the root layout) | | **B-022** | Animal detail management card links to the farm-level **vaccinations** page — there was no path from the animal | | **B-023** | The staff/**vet invite** page (`admin/personnel/invite`) is now a real nav node (owner/Manager) with `veterinär`/`bjud in`/`invite` keywords — palette-searchable + in the MegaMenu, instead of URL-only | | **B-028** | Management card gets **"Registrera försäljning"** → Movements → Sales, distinct from the shop's easily-confused "list for sale" button | | **B-048** | "Office & settings" mode home lands on the **farm-settings overview** (`admin/settings`) instead of the AgroVision import wizard | ## Verification - **Playwright (`b021-048-backlog2-verify.mjs`, 4/4):** office mode lands on `admin/settings`; both management-card links render with correct hrefs; palette search **"veterinär" finds the invite page**. (Also root-caused a harness gotcha: the farms mock's deliberate 2.5s delay means `selectedFarm` must hydrate before the modeswitch/palette carry farm context.) - **check:ci frontend guards:** ✅ `check` (0 errors), ✅ `i18n:check`, ✅ `wa:check`, ✅ `tokens:check`, ✅ `a11y:check`; lib tests **2787/2787**. - Frontend-only. KNOWN_BUGS entries tagged ÅTGÄRDAD in the same commit. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • 2b09995 B-021/B-022/B-023/B-028/B-048: backlog batch 2 Claude (AI) · 2026-07-21 · +153 −41

#119 B-019/B-034/B-045 fixes + docs-close B-002/B-012/B-033/B-041

2026-07-21 · Claude (AI) · 1 commits · 22 filer · +187 −48 · Buggfixar B-019 B-034 B-045 B-002 B-012 B-033 B-041

Detaljer
## What / why Backlog batch 1 — the most actionable of the pre-runda-1 open bugs. | Bug | Fix | |---|---| | **B-019** | Next-actions card's slaughter "Safe now" deep link now lands on **movements/slaughter** (booking page) instead of the animal's health journal | | **B-034** | Annual compliance pack: (1) explanatory **hint under the disabled generate button** (farm data not loaded) instead of a silent grey button; (2) **country filtering** — HIT (DE) / VIVA (NL) / VetStat (DK) submission files are listed + fetched only on farms in their own country (new `countries` field on `CompliancePackEntry`, applied to the list, the fetches and the manifest) | | **B-045** | `/app/finance`, `/app/shop`, `/app/csa`, `/app/employees` now **redirect to the selected farm's page** (`FarmShortcutRedirect`; no farm → `/app/farms`) instead of 404ing | **Docs-closed** (no code needed): **B-002** (obsolete — thumbs row removed by B-078/#113), **B-012** (already fixed via `?raw` snippet files; verified by this session's many cold `bun run dev` starts), **B-033/B-041** (already fixed on master; regression tests + browser verification landed in #110). ## Verification - **Playwright (`b034-b045-backlog-verify.mjs`, 6/6):** Swedish farm's pack lists **13 entries with no HIT/VIVA/VetStat rows**; all four short URLs redirect to the farm-scoped page (no-farm fallback to `/app/farms` also observed). - **check:ci frontend guards:** ✅ `check` (0 errors), ✅ `i18n:check`, ✅ `wa:check`, ✅ `tokens:check`, ✅ `a11y:check`; lib tests **2787/2787** (incl. registry orphan guard + manifest catalogue tests). - Frontend-only. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • 4848629 B-019/B-034/B-045 fixes + docs-close B-002/B-012/B-033/B-041 Claude (AI) · 2026-07-21 · +187 −48

#118 docs: mark round-2 fixes done (B-075/076/077/079/080/084/089/090)

2026-07-21 · Claude (AI) · 1 commits · 1 filer · +16 −16 · Buggfixar B-075

Detaljer
Tags the just-merged round-2 work in `other/KNOWN_BUGS.md`: mobile-shell cluster (#115), view-as-role (#116), photo-evidence v1 (#117) — headings + status lines with one-line fix summaries. Docs-only. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • f717d31 docs: mark round-2 fixes done (B-075/076/077/079/080/084/089/090) Claude (AI) · 2026-07-21 · +16 −16

#117 B-080: photo evidence for birth & euthanasia (v1)

2026-07-21 · Claude (AI) · 1 commits · 25 filer · +638 −40 · Buggfixar B-080

Detaljer
## What / why **B-080 [feature, v1]** — staff must be able to document a stillborn or malformed (euthanised) piglet **with mandatory photo evidence**. Built entirely on the existing document infrastructure (no backend changes). ## Changes - **`PhotoEvidenceSection.svelte`** (new, reusable) — camera-capture input (`capture="environment"`), thumbnails, parent-driven deferred upload via `documentStore.upload` → photos land in the animal's **Documents** tab with a descriptive `kind`. - **Add-animal dialog** — new *"Utfall vid födsel"* segment (normal / dödfödd / missbildad-avlivad; **buttons**, not `wa-select`, so no nested-select `wa-after-hide` bubbling can close the dialog). Stillborn/malformed **requires ≥1 photo** before submit; photos upload post-create as `birth_evidence_<outcome>`, **non-fatally** (an offline upload failure never blocks the create — keeper re-attaches from Documents; limitation noted in KNOWN_BUGS per the B-080 entry's offline-first ask). - **Animal detail page** — **`DeathPhotoEvidenceCard`** (new) renders when a `deathDate` exists: red callout while no evidence is on file, in-place attach + upload (`euthanasia_evidence`), flips to a green "Fotobevis sparade: n" state. - **i18n** — new `photoEvidence.*` shard (en/sv + da/de/es/fr/nl). - **docs** — B-085 marked *already shipped* (NAV-P8 customisable bottom bar; Playwright-verified 7/7) and B-096 annotated: the remaining step is the **Kinde-tenant self-registration setting** (dashboard-only; Kinde is network-blocked from the sandbox). ## Deliberate v1 scope Photos are stored as **documents** (retention follows document storage), not event-log blobs — matching the B-080 entry's own guidance. Outbox-queued offline photo upload and an outcome field on the `Birth` event payload are follow-ups needing backend/projection work. ## Verification - **Playwright (`b080-photo-evidence-verify.mjs`, 11/11):** outcome segment renders; photo section appears for stillborn; **submit without photo blocked** ("Minst ett foto krävs.") with the dialog kept open; attached photo **uploads as `birth_evidence_stillborn`**; death card **warns → uploads → flips to "on file"**. Screenshots `b080-01..04` inspected (incl. a preview-sync fix caught visually: thumbnails now clear after successful upload). - **check:ci frontend guards:** ✅ `check` (0 errors), ✅ `i18n:check`, ✅ `wa:check`, ✅ `tokens:check`, ✅ `a11y:check`. - Frontend-only. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • 91bbd9f B-080: photo evidence for birth & euthanasia (v1) Claude (AI) · 2026-07-21 · +638 −40

#116 B-075: "view as role" preview for owners/admins

2026-07-21 · Claude (AI) · 1 commits · 17 filer · +340 −38 · Buggfixar B-075

Detaljer
## What / why **B-075 [testability]** — there was no way to see the app as another user type without inviting a second test user. Owners (and root/super admins) can now preview the app as **Manager / Farmhand / Vet / ViewOnly**. ## Design - **`roleOverrideStore`** (new) — sessionStorage-scoped preview role; gone when the tab closes, so a preview never sticks. - **`farmRoleStore`** honours the override **only when the actual resolved role is `owner`** (or root/super admin) — owners preview *down*, nobody previews *up*; a hand-set sessionStorage value is ignored for everyone else. The real role stays available as **`actualFarmRoleStore`**. Pure frontend simulation: every API call still carries the real identity, so backend `RouteAuth` is untouched — this can never grant real permissions. - **TopBar** — picker in the avatar menu (owners/admins only) + a highly visible **amber chip** ("👁 Endast läsning ✕") while active; tapping the chip exits. The preview propagates through every `farmRoleStore` consumer: nav filtering, gated buttons, ViewOnly states. - **i18n** — `nav.viewAs.*` keys (en/sv/da/de/es/fr/nl) + a `DYNAMIC_PREFIXES` entry for the role-name template. ## Verification - **Playwright (`b075-view-as-role-verify.mjs`, 9/9):** owner previews ViewOnly → amber chip appears naming the role, the slaughter page's role-gated "Boka slakt" disappears (visible-only assertion; screenshot `b075-01` inspected), preview **survives reload** (sessionStorage), chip tap **restores** owner controls; a **non-owner gets no picker** and a hand-set sessionStorage override is ignored (no chip, no effect). - **check:ci frontend guards:** ✅ `check` (0 errors), ✅ `i18n:check`, ✅ `wa:check`, ✅ `tokens:check`, ✅ `a11y:check`. - Frontend-only. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • 0b054be B-075: "view as role" preview for owners/admins Claude (AI) · 2026-07-21 · +340 −38

#115 B-076/077/079/084/089/090: mobile-shell fixes at 390px

2026-07-21 · Claude (AI) · 1 commits · 9 filer · +197 −8 · Buggfixar B-076

Detaljer
## What / why The six mobile-shell findings from runda-1 (390×844), fixed **conservatively** — no topbar/bottom-bar redesign, so this stays reviewable and reversible if Jonas wants a different mobile IA later. | Bug | Fix | |---|---| | **B-084** | Topbar hamburger hidden <768px — the bottom bar's "Mer" opens the same MegaMenu; it was a duplicate entry crowding the bar | | **B-076/B-079** | With the centre group hidden the right group lands in grid track 2; first track is now `minmax(0,1fr)` + `overflow:hidden`, labels capped at `21vw` — long farm/house names **ellipsize** instead of sliding under the right-corner icons. Right corner = search · bell · avatar with breathing room | | **B-089** | New mobile-only **search icon** in the topbar (opens the command palette). The centre pill is hidden on phones and barn mode's bottom-bar preset has no search slot — there was no visible search entry at all | | **B-077** | Copilot FAB lifted above the 64px bottom bar (`bottom: calc(64px + safe-area + 0.75rem)`) so it never covers "Mer" | | **B-090** | MegaMenu mode row **wraps** on phones instead of sideways-scrolling with a hidden scrollbar (which read as clipped) — all 7 chips visible | ## Verification - **Playwright at 390×844** (`other/fas06-01-genomgang/verktyg/b076-090-mobile-shell-verify.mjs`, **12/12**): hamburger hidden; search icon visible + opens the palette; long farm+house labels stay inside the viewport and never underlap the right icons (paint-clip guard — the first cut of this fix failed exactly that and was caught + fixed); right icons don't overlap; FAB clears the bar; mode row wraps with all 7 chips inside the viewport. Screenshots `mobil-01..04` inspected. - **check:ci frontend guards:** ✅ `check` (0 errors), ✅ `wa:check`, ✅ `tokens:check`, ✅ `a11y:check`. No new i18n keys (`nav.search` reused). - CSS + one topbar button; frontend-only. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • 4f82023 B-076/077/079/084/089/090: mobile-shell fixes at 390px Claude (AI) · 2026-07-21 · +197 −8

#114 docs: mark runda-1 bugs fixed in KNOWN_BUGS + MASTER_TODO

2026-07-21 · Claude (AI) · 1 commits · 2 filer · +32 −31 · Funktioner

Detaljer
Records the 2026-07-21 runda-1 fix round (PRs #107–#113) in the tracking docs — the code merged but the ledgers still listed the bugs as open. - **`other/KNOWN_BUGS.md`** — tag the 13 fixed entries (`B-096` Kinde signup, `B-095`, `B-094`, `B-093`, `B-092`, `B-091`, `B-088`, `B-087`, `B-086`, `B-083`, `B-082`, `B-081`, `B-078`) with `[ÅTGÄRDAD 2026-07-21 — #PR]` and rewrite each `Status:` line with a one-line fix summary. Mark `B-037`'s "bekräftelsedialog" new-requirement implemented (#109). - **`other/ENLANTIS_MASTER_TODO.md`** — extend the "latest known code work" pointer with the runda-1 round + the still-open items (mobile-shell cluster B-076/077/079/084/089/090 and features B-075/B-080/B-085). Docs-only; no code or gate impact. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • ab4f82b docs: mark runda-1 bugs fixed in KNOWN_BUGS + MASTER_TODO Claude (AI) · 2026-07-21 · +32 −31

#109 B-095/B-037: fix FCI farm-notes auth + add slaughter-booking confirmation

2026-07-21 · Claude (AI) · 1 commits · 15 filer · +211 −37 · Buggfixar B-095 B-037

Detaljer
## What / why - **B-095** — the FCI declaration page loaded/saved the four free-text `farm_note` fields (`disease_history` / `test_results` / `feeding_regime` / `environmental`) with a **bare `fetch()`** that omitted `X-User-Id`. `farm-notes` is auth-gated (`requireFarmer` / `requireWriter`), so header-mode `resolveUser` failed with *"User ID required"* → **400 on every GET and PUT** as the farmer clicked into a field. (The `/fci` generate route worked only because it isn't gated — the asymmetry was the tell.) - **B-037** — booking a slaughter sets the animal's death date + FCI window, but **"Boka slakt" committed the movement event immediately** with no confirmation. New requirement: a confirmation step. Sold animals must not be selectable (they already weren't — `!a.soldAt`). ## Changes - **`fci/+page.svelte`** — `loadNote`/`saveNote` now send `authHeaders()`; added it to `generate()` too for robustness if that route is ever gated. - **`slaughter/+page.svelte`** — the primary "Boka slakt" button now opens a **confirmation dialog** naming the selected animal ("Boka slakt för {name}? …"); only its confirm button enqueues the movement event. The confirm dialog's `onwa-after-hide` is guarded with `e.target === e.currentTarget` (B-074 class) so a nested control can't bubble it closed. - **i18n** — three new keys `movement.slaughter.confirmTitle` / `confirmBody` / `confirmCta` (en/sv + de/es/fr/nl/da). ## Verification - **Playwright (`other/fas06-01-genomgang/verktyg/b037-b095-slaughter-fci-verify.mjs`, 7/7 pass)** — sold animal ("Såld-Svin") is **not** in the picker; clicking "Boka slakt" opens the confirmation dialog naming "Greta"; confirming closes both dialogs; the mocked **`farm-notes` GET carries `X-User-Id`**. Screenshots `b037-01`, `b037-02` inspected — dialog renders cleanly stacked over the booking form. - **check:ci frontend guards (run individually):** ✅ `check` (0 errors), ✅ `i18n:check`, ✅ `wa:check`, ✅ `a11y:check`, ✅ `tokens:check`. - Frontend-only; no backend changes (`sbt test` n/a). Pre-existing unrelated master vitest failure (`registry.test.ts` orphan `compliance/emb-pigs`) will be closed in the search-palette PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • f9805e2 B-095/B-037: fix FCI farm-notes auth + add slaughter-booking confirmation Claude (AI) · 2026-07-20 · +211 −37

#113 B-078/B-083/B-082/B-092: remove thumbs, admin-only news log, stock unit/step

2026-07-21 · Claude (AI) · 2 commits · 8 filer · +166 −116 · Buggfixar B-078 B-083 B-082 B-092

Detaljer
## What / why UX-cleanup group from runda-1. - **B-078** — remove the **"Was this useful?" thumbs** feedback row from the Today and Copilot nudge cards (markup, state, handler, CSS in both). The `copilot.useful.*` i18n keys stay (referenced by other surfaces). - **B-083** — the release-notes banner is editorial/back-office and should show **only for root admins**, not end users. Gate the fetch **and** the banner on `$isRootAdmin`; non-admins never load or see it. - **B-082** — `releaseNote` / `releaseNoteHidden` were plain `let` in a runes component, so the **✕ mutated a non-reactive variable** and the banner never re-rendered. Make them `$state` so dismissal works (now on the admin surface). - **B-092** — the medication stock-correction dialog showed a **unit-less delta with a `0.001` step** (rendering confusing values like "0,003"). Add the dosage-unit suffix and use a **`0.1` step** (covers ml, still allows whole doses); same on the add/edit dialog's stock + threshold inputs. ## Verification - **Playwright (`other/fas06-01-genomgang/verktyg/b078-b082-b083-b092-uxcleanup-verify.mjs`, 9/9):** no thumbs icons on Today/Copilot; release banner **hidden for a non-admin** and **shown for an admin**, whose **✕ dismisses it**; the stock dialog's delta input shows **"ml"** and **step=0.1**. Screenshots `b078-01`, `b083-02`, `b092-03` inspected. - **check:ci frontend guards:** ✅ `check` (0 errors, warnings ↓ as thumbs CSS removed), ✅ `i18n:check`, ✅ `wa:check`, ✅ `tokens:check`, ✅ `a11y:check`. - Frontend-only. One pre-existing unrelated master vitest failure (`registry.test.ts` orphan `compliance/emb-pigs`) — fixed in the search-palette PR (#110); all 2876 other tests pass. > Note: B-082 was consolidated here (rather than the dialogs PR) because it shares `app/+layout.svelte`'s banner logic with B-083. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • 4788330 review: stock inputs use step="any"; drop stale thumbs doc-comment Claude (AI) · 2026-07-21 · +12 −10
  • 090ee81 B-078/B-083/B-082/B-092: remove thumbs, admin-only news log, stock unit/step Claude (AI) · 2026-07-20 · +154 −106

#112 B-088/B-081: light-dismiss dialogs app-wide + house picker navigates

2026-07-21 · Claude (AI) · 2 commits · 6 filer · +169 −18 · Buggfixar B-088 B-081

Detaljer
## What / why - **B-088** — clicking outside a dialog never closed it, app-wide. Web Awesome's `<wa-dialog>` only closes on a backdrop click when the **`light-dismiss`** attribute is set, and no dialog opted in (128 dialog-bearing files). Escape already closes the top dialog regardless of `light-dismiss` (`isTopDismissible` ignores it), so backdrop-close is a **consistent second path, not a new data-loss vector**. - **B-081** — the TopBar house/stable picker only wrote `selectedHouseId`, which re-filters the animals list but has no visible effect on most routes, so it read as a **dead control** (the B-020/B-047/B-062 "inert picker" family). ## Changes - **`lib/ui/lightDismissDialogs.ts`** (new) + wired from the root layout `onMount` — one global default that sets `light-dismiss` on every `<wa-dialog>` (existing + observer for dialogs mounted later), with a per-dialog **opt-out via `data-no-light-dismiss`**. Chosen over annotating 128 templates. - **`shell/TopBar.svelte`** — new `pickHouse(id)` mirrors `pickFarm`/`switchMode`: set the house **and** navigate to the house-scoped animals list (re-filters in place if already there). ## Verification - **Playwright (`other/fas06-01-genomgang/verktyg/b088-b081-dialogs-nav-verify.mjs`, 5/5):** the booking dialog now carries `light-dismiss` and a **backdrop click closes it**; picking "Stall 2" **navigates to the animals list** with that house selected (breadcrumb + filter chip "Stall 2 (Cattle)"). Confirms light-dismiss is compatible with the B-074 `onwa-after-hide` guards (the guarded booking dialog closes cleanly). Screenshots `b088-01`, `b081-02` inspected. - **check:ci frontend guards:** ✅ `check` (0 errors), ✅ `wa:check`, ✅ `tokens:check`, ✅ `a11y:check`. - Frontend-only. > B-082 (news-banner ✕) was moved into the UX-cleanup PR since it shares `app/+layout.svelte`'s banner logic with B-083 (news-log admin-only). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • 4cb664e review: light-dismiss via delegated wa-show listener, not a global observer Claude (AI) · 2026-07-21 · +22 −17
  • 40ab4bc B-088/B-081: light-dismiss dialogs app-wide + house picker navigates Claude (AI) · 2026-07-20 · +147 −1

#111 B-094: align FCI animal-picker checkboxes with their labels

2026-07-21 · Claude (AI) · 1 commits · 3 filer · +77 −1 · Buggfixar B-094

Detaljer
## What / why **B-094** — on the FCI slaughter-declaration page, the animal-picker checkboxes were misaligned with their labels. The `.cb-row` styled the `wa-checkbox` host but left Web Awesome's internal `base` part centred/indented, so the checkbox control drifted out of line with the name + type spans (same class as the already-fixed B-073 health-check list). ## Change `fci/+page.svelte` — apply the B-073 pattern to `.cb-row`: a left-aligned flex row plus `.cb-row::part(base) { margin-inline: 0; justify-content: flex-start; }`. CSS-only. ## Verification - **Playwright (`other/fas06-01-genomgang/verktyg/b094-fci-checkbox-verify.mjs`, 4/4):** rendered the picker with three animals; measured `flex-direction: row` + `text-align: left`, base part `justify-content: flex-start`, and the checkbox control's vertical centre within **0.5px** of its label's. Screenshot `b094-01` inspected — control, name, and type sit in line across all rows. - **check:ci frontend guards:** ✅ `check` (0 errors), ✅ `tokens:check`, ✅ `a11y:check`. CSS-only; no i18n/behaviour change. > Note: B-035/B-036 (slaughter-page link + button alignment) were already fixed/verified 2026-07-19 on master, so this PR is just B-094. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • fa1e9e0 B-094: align FCI animal-picker checkboxes with their labels Claude (AI) · 2026-07-20 · +77 −1

#108 B-087/B-093/B-086: fix add-animal location race, duplicates & vanishing list

2026-07-21 · Claude (AI) · 1 commits · 5 filer · +146 −2 · Buggfixar B-087 B-093 B-086

Detaljer
## What / why Three linked animal-data bugs from Jonas's runda-1 testing, all rooted in the add-animal flow: - **B-087** — "Lägg till djur" creates the animal (local-first `birth` event via the outbox) then fires a **direct** `POST /api/animal-locations`. That route looks the animal up server-side and 404s it (→ **400**) when the birth event hasn't flushed + projected yet — a race between two independent requests. - **B-093** — the 400 threw out of the create handler, leaving the modal open and inviting a re-click, which minted a **second animal** with a fresh UUID ("Rosa" twice). - **B-086** — the animals list hid every animal lacking a current location whenever a house was selected (and `selectedHouse` falls back to the first house), so a whole farm's animals showed **"Inget att visa"** while still appearing on farmId-scoped pages like FCI. ## Changes (all in `animals/+page.svelte`) - **Order the location POST after the birth event lands**: drain the outbox and confirm the birth op has left the queue (short bounded retry — an in-flight flush makes a single `flush()` a no-op) before assigning the area. If the birth event is still pending (offline) or the assignment fails, it's **non-fatal** — the animal is already created locally and syncs; the area can be set from the detail page. Added a re-entrant-submit guard (`if (loading) return`). - **Filter fix (B-086)**: only exclude an animal from the selected house when it's assigned to a *different* house; keep location-less ("unassigned") animals visible. ## Verification - **Playwright (`other/fas06-01-genomgang/verktyg/b086-b087-b093-animals-verify.mjs`, 7/7 pass)** — with `/api/animal-locations` mocked to **400** (the race), the dialog **closes with no error banner** and the created animal appears **exactly once**; two location-less animals render under the selected house ("3 aktiva djur", not the empty state). Screenshots in `verktyg/shots/` (`b086-01`, `b087-01`, `b093-02`) — inspected. - **check:ci frontend guards (run individually):** ✅ `check` (0 errors), ✅ `wa:check`, ✅ `a11y:check`, ✅ `tokens:check`. No i18n keys added. - Frontend-only; no backend changes (`sbt test` not applicable). One pre-existing vitest failure on master (`registry.test.ts` orphan `compliance/emb-pigs`, from the B-005 fix) is unrelated and will be closed in the search-palette PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • 725df3c B-087/B-093/B-086: fix add-animal location race, duplicates, and vanishing list Claude (AI) · 2026-07-20 · +146 −2

#107 B-074: wire sign-up CTAs to Kinde register() instead of login()

2026-07-21 · Claude (AI) · 1 commits · 35 filer · +114 −43 · Buggfixar B-074

Detaljer
## What / why **B-074 [BLOCKER]** — a brand-new visitor could not create an account. The header/footer **"Start free"** CTA (and the home pricing CTA) called `authService.login()`, sending the user to Kinde's **sign-in** page, which rejects an unknown email with *"No account found."* A working `register()` (Kinde `prompt=create`) already existed — the prominent CTAs just didn't call it. ## Changes - **`SiteHeader.svelte`** — "Start free" (desktop + mobile hamburger) now calls `handleRegister()` → `authService.register()`. The ambiguous **"Login / Sign up"** text button is simplified to **"Login"** (`nav.user.login` label updated across locales); the accent CTA now owns sign-up. - **`SiteFooter.svelte`** "Start with Enlantis" and **home `[[lang=locale]]/+page.svelte`** pricing "start" CTA → `register()`. - **Backend (`UserAuthDTO` + `UserRoutes`)** — thread the Kinde `sub` through `POST /api/users/auth` so a first-login user row carries its `kindeId`, and backfill it on existing rows that predate this. Without it, prod `jwt`-mode auth (`RouteAuth.findByKindeId`) can 401 even after a successful upsert. Frontend passes `userData.id` via `apiService.authenticateUser(...)`. ## Verification - **Visual + behavioral (Playwright, `other/fas06-01-genomgang/verktyg/b074-signup-cta-verify.mjs`)**: rendered `/home`, confirmed the header renders cleanly with "Logga in" + "Börja gratis". Clicking **"Börja gratis"** navigates to the Kinde authorize URL with **`prompt=create`** (`state` decodes to `event: register`); **"Logga in"** goes to sign-in (`event: login`, no `prompt=create`). Screenshot in `verktyg/shots/b074-signup-01-header.png`. 4/4 checks pass. - **check:ci (frontend guards, run individually — full `check:ci` OOMs in the agent sandbox):** ✅ `check` (0 errors), ✅ `i18n:check`, ✅ `wa:check`, ✅ `a11y:check`, ✅ `tokens:check`. `test` (vitest): all green **except one pre-existing failure on master** — `registry.test.ts` flags `/app/farms/[id]/compliance/emb-pigs` as an orphan route (added by the B-005 fix, not this change); it will be closed in the search-palette PR that touches `registry.ts`. - **Backend `sbt test`:** ⚠️ not run — the sandbox has Java 21 only (build requires Java 25) and no `sbt`. Scala changes reviewed by inspection; `UserRow.kindeId` already existed (migration 0226), and `UserAuthDTO.kindeId` is optional with a default so existing callers/specs stay valid. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • 9f44142 B-074: wire sign-up CTAs to Kinde register() instead of login() Claude (AI) · 2026-07-20 · +114 −43

#110 B-091/B-033/B-041: HPCIA search + register orphan eMB-Pigs page

2026-07-21 · Claude (AI) · 1 commits · 5 filer · +82 −2 · Buggfixar B-091 B-033 B-041

Detaljer
## What / why Search-palette group from runda-1. - **B-091** — searching **"HPCIA"** returned nothing. The acronym lived only in antimicrobial-usage logic/components (`hpciaClassifier.ts`, treatments/inventory), never as a nav keyword. Added `hpcia` / `antimicrobial` / `antibiotika` keywords to the **AMU register** and **AMU-monthly** compliance nodes, and **registered the eMB-Pigs AMU export page** (`/compliance/emb-pigs`) — previously an orphan route reachable only from a card, which also tripped `registry.test.ts`'s reachability guard. It's pig antimicrobial-usage reporting, so it carries the same keywords and is now searchable. - **B-033** (compliance / inlämning) and **B-041** (result-click closes the palette) were **already resolved on master** — keyword synonyms on the compliance/submissions nodes, and `CommandPalette.activate()` closes the palette synchronously before `goto()` (plus the `afterNavigate` close in `app/+layout.svelte`). Added regression tests + a browser check rather than new code. ## Changes - `nav/registry.ts` — `hpcia`/AMU keywords on `farm.compliance.amu` + `.amu.monthly`; new `farm.compliance.embPigs` node (features `['pigs']`, keywords incl. `hpcia`, `emb`, label `embPigs.title`). - `nav/search.test.ts` — new B-091 block asserting `"hpcia"`/`"HPCIA"` resolve to the AMU + eMB-Pigs entries. ## Verification - **Unit:** `search.test.ts` 12/12 (incl. new HPCIA cases + existing B-033 cases), `registry.test.ts` **18/18 — the orphan `emb-pigs` guard now passes** (this closes the pre-existing master failure the other runda-1 PRs flagged). **Full vitest 2879/2879.** - **Playwright (`b091-b041-search-verify.mjs`, 3/3):** Ctrl+K → "hpcia" returns **AMU-register / AMU månadsvis / eMB-Pigs AMU-export (GB)** under Regelefterlevnad; clicking a result **closes the palette**. Screenshot `b091-01` inspected. - **check:ci frontend guards:** ✅ `check` (0 errors), ✅ `i18n:check`, ✅ `wa:check`, ✅ `tokens:check`, ✅ `test`. - Frontend-only; no backend changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01SSYqS6k77QfAygmwGPUb76)_
  • b6e9ec6 B-091/B-033/B-041: HPCIA search + register orphan eMB-Pigs page Claude (AI) · 2026-07-20 · +82 −2

#106 docs: Round 1 manual-test bug log (B-074–B-095) + fix-thread handoff

2026-07-20 · Claude (AI) · 24 commits · 2 filer · +486 −4 · Buggfixar B-074 B-096 B-095 B-094 B-037 B-093 B-088 B-036 B-092 B-090 B-091 B-089 B-087 B-086 B-085 B-084 B-083 B-082 B-081 B-080 B-079 B-078 B-002 B-076 B-077 B-075

Detaljer
## What Documentation only — no code changes. Logs the findings from Jonas's round 1 manual testing (2026-07-20, desktop + mobile 390px) and adds a handoff for a separate fix thread. - `other/KNOWN_BUGS.md`: new entries **B-074 … B-095**; live re-confirmations on B-036, B-037, B-088; note that B-078/B-083 supersede B-002/B-082 for the user surface. - `other/HANDOFF_RUNDA1_MOBIL.md`: grouped, prioritized handoff (P0 blockers / data integrity → P3 UX cleanup + features) with a suggested PR breakdown. ## Highlights - **B-074** (P0 blocker): new users cannot register — Kinde returns "No account found", no sign-up path. - **B-087 / B-093 / B-086**: add-animal `POST /api/animal-locations` 400 → duplicate animals + empty Animals tab (likely one root cause). - **B-095**: FCI `farm-notes` GET/PUT 400 per category on field edit. - Mobile shell cluster (B-076/077/079/084/089/090) — needs a design decision before rebuild. ## Scope This branch is the reporting/handoff record. Fixes will be done in a separate thread per the handoff's PR breakdown. Kept as draft. ## check:ci Docs-only change; no code touched. --- _Generated by [Claude Code](https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv)_
  • 326e95b docs: renumber Kinde-registration bug B-074 -> B-096 (collision with master); note parallel slaughter fixes Claude (AI) · 2026-07-20 · +20 −4
  • d4cfc27 docs: round 1 handoff (B-074-B-095) for fix thread Claude (AI) · 2026-07-20 · +96 −0
  • 123f402 docs: log B-094/B-095 (FCI checkbox alignment; farm-notes GET/PUT 400) Claude (AI) · 2026-07-20 · +40 −0
  • 1a30244 docs: B-037 re-confirmed + confirmation-dialog requirement; log B-093 (duplicate animals in slaughter picker) Claude (AI) · 2026-07-20 · +25 −0
  • fc25bac docs: B-088 confirmed app-wide (Boka slakt dialog on desktop too) Claude (AI) · 2026-07-20 · +4 −0
  • b2e4b33 docs: B-036 re-confirmed live on slaughter bookings page Claude (AI) · 2026-07-20 · +7 −0
  • 55ad604 docs: log B-092 (inventory adjust: unclear unit, too many decimals) Claude (AI) · 2026-07-20 · +14 −0
  • 908393b docs: log B-090/B-091 (mobile mode-chip row overflows; HPCIA not searchable) Claude (AI) · 2026-07-20 · +27 −0
  • 9cb262d docs: log B-089 (no visible search on mobile) Claude (AI) · 2026-07-20 · +15 −0
  • fac6b4f docs: log B-088 (cannot click outside dialog to close) Claude (AI) · 2026-07-20 · +13 −0
  • cadac8f docs: log B-087 (add animal 400 on POST /api/animal-locations) Claude (AI) · 2026-07-20 · +22 −0
  • 751e31f docs: log B-086 (Animals tab empty despite two animals on farm) Claude (AI) · 2026-07-20 · +17 −0
  • d4b0572 docs: log B-085 (feature: user-customizable bottom nav) Claude (AI) · 2026-07-20 · +15 −0
  • b53da24 docs: log B-084 (mobile hamburger duplicates More menu) Claude (AI) · 2026-07-20 · +15 −0
  • f82c98c docs: log B-083 (news log should be admin-only, not shown to user) Claude (AI) · 2026-07-20 · +15 −0
  • 5e6b742 docs: log B-082 (news banner X does not dismiss) Claude (AI) · 2026-07-20 · +12 −0
  • 13b0f40 docs: log B-081 (stall switcher does not navigate on click) Claude (AI) · 2026-07-20 · +14 −0
  • 8f693d2 docs: log B-080 (feature: birth/culling photo-evidence section) Claude (AI) · 2026-07-20 · +19 −0
  • 224a645 docs: log B-079 (mobile top-right controls cramped) Claude (AI) · 2026-07-20 · +15 −0
  • 276b031 docs: log B-078 (remove thumbs + 'was this helpful' row, supersedes B-002) Claude (AI) · 2026-07-20 · +13 −0
  • d076f12 docs: log B-076/B-077 (mobile: stall breadcrumb clipped, More tab hidden by copilot FAB) Claude (AI) · 2026-07-20 · +25 −0
  • 2a5f85d docs: log B-075 (no way to view app as another user role) Claude (AI) · 2026-07-20 · +18 −0
  • eaae4b0 docs: note B-074 blocks manual test round 1 Claude (AI) · 2026-07-20 · +4 −0
  • e598982 docs: log B-074 (Kinde registration blocked for new email) Claude (AI) · 2026-07-20 · +21 −0

2026-07-20 — 1 direkt-commits

Detaljer
  • 432fe5e B-074: guard wa-select-in-dialog close handlers against bubbling (51 dialogs) (#105) Jonas · 2026-07-20 · +215 −71

#104 Fas 06 bug round: slaughter booking, sold animals, FarmCard i18n, eMB-Pigs page

2026-07-20 · Claude (AI) · 4 commits · 11 filer · +721 −45 · Buggfixar B-009 B-010 B-031 B-005 B-037 B-039

Detaljer
## Summary A fas 06 bug-fix round from `other/KNOWN_BUGS.md`, picked because these were the open findings that could be fully verified autonomously in the sandbox (code + Playwright screenshots, no live DB / Jonas machine). Two were functionality-breaking; the rest are quick, screenshot-verifiable cleanups. | Bug | What was broken | Fix | |-----|-----------------|-----| | **B-037** | Slaughter booking dialog closed silently the instant an animal was picked → booking impossible | The dialog's `onwa-after-hide` also fired on the `<wa-select>`'s own after-hide (event bubbles). Guard with `e.target === e.currentTarget` — same trap already guarded on `admin/health` / `admin/personnel`. | | **B-039** | Sold animals still selectable (and shown active) | Backend already stamps `soldAt` and the main list already filters it (migration 0202) — but the slaughter + sales pickers filtered only `!deathDate`. Added `!soldAt` to both. Main-list "shows sold as active" part is code-verified as already filtered; **needs Jonas live-DB confirmation**. | | **B-031** | FarmCard buttons hardcoded English | Wire through `$t` (reuse `common.edit`/`common.delete`, new `farmCard` shard). | | **B-005** | GB farm-home card linked to `compliance/emb-pigs` → 404 (route never built) | Built the page mirroring the German `hit-amu` page on a quarterly cadence, against the existing `UkEmbPigsExportRoutes` backend export. New `embPigs` i18n shard. | | **B-009** | Health-journal action buttons at ragged heights | `align-items: center` on `.action-row`. | | **B-010** | Animal-detail conversion button overflowed card / label overlap | `flex-wrap: wrap` on `.info-item`. Live-visual deferred (harness can't seed animal-detail Dexie state). | Frontend-only — **no `.scala` changes**. `UkEmbPigsExportRoutes` was already in the codebase and is unmodified. ## Verification Playwright against `bun run dev` with self-hosted Web Awesome (`other/fas06-01-genomgang/verktyg/`), screenshots read + computed styles measured per the CLAUDE.md hard rule: - **B-037**: selecting "Nasselisa" keeps the dialog open (`value=a-1`) and enables the Book button — `shots/fixround-04-slaughter-after-select.png`. Was: dialog vanished on select. - **B-039**: a sold pig is absent from the picker options (`["Nasselisa","Rölleka"]`). - **B-031**: farm list renders "Visa djur / Redigera / Ta bort" on Swedish — `shots/fixround-01-farmlist.png`. - **B-005**: `/compliance/emb-pigs` renders (no 404) with the quarter picker — `shots/fixround-05-emb-pigs.png`. - **B-009**: three action buttons share top edge (delta 0px, `align-items: center`). - **B-010**: CSS-only, mechanism verified by inspection; live render deferred to Jonas. Verification scripts: `verktyg/b037-b039-b031-b005-verify.mjs` (9/9 pass) and `verktyg/b009-b010-verify.mjs` (2/2 pass). **check:ci status:** frontend green — svelte-check **0 errors** (4895 pre-existing warnings), `i18n:check` ✅, `wa:check` ✅, `a11y:check` ✅ (0 failing), `tokens:check` ✅ (0 drift). Backend `sbt test` **not run in this sandbox** (sbt absent, Java 21 not 25) — no backend code changed, so nothing to gate. ## Follow-up Recorded a new **B-074**: the B-037 close-on-select bug is systemic — a static scan found ~30 files with a `wa-select`/`wa-dropdown` inside a dialog plus a naive `onwa-after-hide` closer. Kept out of this PR (can't screenshot-verify 30 files individually); it wants a dedicated guarded sweep with per-file verification. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01X7CuYT9jMUmAdC7dBtiZvG --- _Generated by [Claude Code](https://claude.ai/code/session_01X7CuYT9jMUmAdC7dBtiZvG)_
  • 5d571b7 Fas 06: KNOWN_BUGS updates + Playwright verification for this bug round Claude (AI) · 2026-07-20 · +290 −7
  • 0afa2c4 Align health-check action row; wrap animal-detail field rows (B-009, B-010) Claude (AI) · 2026-07-20 · +2 −1
  • a1d0d3f Localise FarmCard buttons; build GB eMB-Pigs export page (B-031, B-005) Claude (AI) · 2026-07-20 · +426 −34
  • c946e87 Slaughter dialog: stop wa-select's after-hide from closing it; hide sold animals (B-037, B-039) Claude (AI) · 2026-07-20 · +3 −3

#103 Dead-button audit: automated crawler + fixes for confirmed dead buttons

2026-07-20 · Claude (AI) · 3 commits · 24 filer · +686 −65 · Funktioner

Detaljer
## What & why Jonas hand-tested the app and found buttons that do nothing (close crosses, menu items, ordinary buttons). This adds an automated way to find them across **all ~586 reachable pages** instead of clicking every button by hand, plus fixes for the confirmed-dead class. **Playwright vs browser control** (the original question): Playwright, decisively — the crawler runs autonomously and only a compact JSON summary is read back, so token cost is ~constant regardless of button count; browser control would need a model round-trip per click. Playwright is also the most *complete* — it mechanically enumerates every visible clickable element per page. ## New - **`other/fas06-01-genomgang/verktyg/dead-button-crawl.mjs`** — reusable crawler on the existing backend-free harness (`harness.mjs` mockApi + `?dev_user=` bypass). For each element it clicks and measures whether *anything* observable happens within ~0.5 s (URL/route change, dialog open/close, network request, file download, `window.print()`, meaningful scroll, or ≥12 DOM mutations). Zero-effect → "dead" candidate, re-verified with a fresh handle to kill stale-node races. Audits buttons inside opened dialogs too. - **`other/DEAD_BUTTON_AUDIT.md`** — the report: confirmed dead (source-verified) + false-positive analysis + coverage. ## Findings (headline) The crawler flagged 157 zero-effect candidates. After source-verifying every class, the **real** dead buttons are a **systemic class of unwired primary CTAs** — mostly `sweep14e`/`sweep13` scaffold pages that shipped a live-looking "Add / Record / Create / Import / Pair" button with **no handler at all**. Most of the other 157 are detector blind spots (window.print, clipboard copy, CSS-class-only tab/menu toggles, already-active filters, sortable headers), verified working. ## Fixes in this PR - **Scaffold-page CTAs** (feed/rations, feed/feeding-log, iot + iot/devices + iot/scales + iot/import ×4, production/wool, production/carcass, breeding/services, breeding/pregnancy-checks, health/health-checks, animals/[animalId]/{breeding,movements}, advisor/{prescriptions,reports}): `disabled` + a "Kommer snart" tooltip, reusing the existing `common.comingSoon` convention (already used as a banner on finance/transactions + finance/subsidies). They now read as "not available yet" instead of dead buttons. - **`movements/+page.svelte:151`** — wired the "Registrera förflyttning" CTA to `.../movements/on-farm` (the page already has nav-cards to every movement type). - **`KillSheetPage.svelte:587`** — dropped the empty-`onclick` `<button>`; it's now a non-interactive tooltip hint (its info was only in `title`/`aria-label`). Documented-only (need real features or product calls, not auto-fixed): a few built-page CTAs (fci "Ladda ner PDF", shop/orders "Fyll i risknivåer"), the `welfare/health-check` info-button cluster (same pattern as KillSheet), and the intentional platform-admin **mock** dashboard. ## Verification - ✅ `check:ci` green (frontend guards: svelte-check 0 errors, vitest, i18n, WA-attr, a11y contrast, token-drift). Backend `sbt test` skipped — sbt not on PATH in this session and there are **no Scala changes**. - **Behavioral regression** via the crawler on the fixed routes: dead candidates → 0; the movements CTA now reports `ok` (navigates). - **Visual verification** (screenshots read): feed/rations CTA renders greyed-out/disabled above its empty state; movements CTA renders enabled and navigates. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JJvCz6dNmSfnf6NX91kYLx --- _Generated by [Claude Code](https://claude.ai/code/session_01JJvCz6dNmSfnf6NX91kYLx)_
  • 58b0d09 Code-review fixes: dedup crawler verdict logic + printed re-verify Claude (AI) · 2026-07-20 · +16 −14
  • 48883fe Add visual-verification screenshots for dead-button fixes Claude (AI) · 2026-07-20 · +0 −0
  • 4f82f94 Add dead-button audit crawler + fix confirmed dead buttons Claude (AI) · 2026-07-19 · +670 −51

#102 Verify B-038 + B-047 closed via dry-run; annotate KNOWN_BUGS

2026-07-19 · Claude (AI) · 1 commits · 3 filer · +70 −0 · Buggfixar B-038 B-047

Detaljer
## Summary Dry-ran the two "already-fixed" findings from the handoff (B-038, B-047) to confirm they're genuinely closed, and recorded the verification. Both were already fixed on master and already live under **## Åtgärdade** in `KNOWN_BUGS.md` (moved there by earlier work) — so they were already struck from the open list; this just closes the loop with a re-verification note. ### B-038 — National ID example contradicted the validation - Validation is `/^SE\d{10}$/` (`animals/+page.svelte` `validateNationalId`); the hint now reads *"SE + 10 siffror (12 tecken), t.ex. SE1234567890"*. - Regex dry-run: new example `SE1234567890` **passes**, the old 12-digit `SE123456789012` **fails**. No 12-digit examples remain in user-facing surfaces. ### B-047 — Mode switcher was a no-op when picking the active mode - `switchMode` in `TopBar.svelte` now calls `goto(modeHomeHref(...))` unconditionally when a farm is selected (the `if (m !== …)` only guards the store write). - Live Playwright dry-run: from `/reports/kpis` with Economy already active, selecting **Ekonomi** again navigates to `/finance` (before ≠ after). No silent no-op. ## Changes - `other/KNOWN_BUGS.md` — appended "Torrkört + verifierat 2026-07-19" confirmations to both Åtgärdade entries. - `other/fas06-01-genomgang/verktyg/b047-modeswitch-verify.mjs` — runnable regression harness for B-047 (+ its confirmation screenshot). ## Verification Docs + verification-script only — no product code, i18n, or build inputs touched, so the frontend/backend gates are unaffected. The two dry-runs themselves are the verification (both pass). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01VZ4oz6dfbUWVs4pX8nrTFa)_
  • 6ff3093 Verify B-038 + B-047 closed via dry-run; annotate KNOWN_BUGS Claude (AI) · 2026-07-19 · +70 −0

#101 Slaughter page: clear FCI button label + idiomatic wa-button href (B-035, B-036)

2026-07-19 · Claude (AI) · 2 commits · 13 filer · +164 −46 · Buggfixar B-035 B-036

Detaljer
## Summary PR B of the two thematic PRs for the open Fas 06 test findings — the **slaughter-bookings page** (B-035, B-036). (PR A = search palette, #99.) ### What I found Both bugs were filed 2026-07-10, but the FCI link + button row were added the **next day** (commit `b5ed9dc`, 2026-07-11) — so, like B-038/B-047 in the handoff, **the structural parts no longer reproduce on master**. Verified with a Playwright harness against `bun run dev`: - **B-035 (FCI unreachable):** the FCI button is present and clicking it navigates to `…/movements/slaughter/fci`. ✅ reachable. - **B-036 (button misalignment):** "Boka slakt" and the FCI button share an identical top edge and height (**0 px delta** at both 1200 px and 430 px), confirmed by measurement and screenshot. This page uses `.actions-bar`, not the global `.header-actions wa-button { flex: 1 }` rule that caused the B-009 family, so the stretch bug never applied here. ### What this PR still changes The one genuine residual is B-035's core complaint — *"ingen synlig väg till FCI-sidan"*: the button read only **"Generera"/"Generate"** with no mention of FCI, so the path wasn't recognisable. - Relabel → **"Generera FCI" / "Generate FCI"** (new i18n key `movement.slaughter.generateFci`, with en/sv/da/de/es/fr/nl). - Replace the `<a><wa-button>` wrapper with the idiomatic **`<wa-button href>`** (matches `EmptyState`/organic pages), keeping the button a direct flex sibling of "Boka slakt" so alignment stays robust. ## Changes - `frontend/src/routes/app/farms/[id]/movements/slaughter/+page.svelte` — clearer label + `<wa-button href>`. - `frontend/scripts/i18n/keys/movement.mjs`, `_order.mjs` + regenerated `src/lib/i18n/*.json` — new key. - `other/KNOWN_BUGS.md` — B-035/B-036 recorded as verified-closed. - `other/fas06-01-genomgang/verktyg/b035-b036-slaughter-verify.mjs` — runnable regression harness. ## Verification - ✅ Visual (mandatory): Playwright screenshots of the action bar before/after — button reads "Generera FCI", aligned with "Boka slakt", navigates to the FCI route. Shared in chat. - ✅ `svelte-check` — 0 errors - ✅ `vitest` — 2874 tests pass - ✅ `i18n:check`, `wa:check`, `a11y:check`, `tokens:check` — all green - ⚠️ Backend (`sbt test`) not run: this sandbox has Java 21 only (no Java 25/sbt). **Frontend-only change — no Scala touched.** > Note: if you'd rather keep the plain "Generera" label, the only functional change here is cosmetic and can be dropped — the underlying bugs were already fixed on master. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01VZ4oz6dfbUWVs4pX8nrTFa)_
  • 1a40fc3 Add B-035/B-036 verification screenshots (before/after action bar) Claude (AI) · 2026-07-19 · +0 −0
  • b8ed3e2 Slaughter page: label FCI button clearly + idiomatic wa-button href (B-035, B-036) Claude (AI) · 2026-07-19 · +164 −46

#100 Auto-generated development-history site at /changelog

2026-07-19 · Claude (AI) · 2 commits · 363 filer · +18 906 −0 · Funktioner

Detaljer
## What Automatic, visual documentation of everything built — generated straight from git history + PRs, the way larger orgs do it (changelog generated in the build step, no manual process). - **`scripts/gen-history.mjs`** (Node ESM, builtins only, matches `check-ci.mjs` conventions): renders a fully self-contained Swedish HTML site from the complete git log — **1 029 commits / 78 PRs since 2026-01-09** (detects a shallow clone and `git fetch --unshallow`s first). - **Content:** stat tiles (commits, PRs, contributors, +/− lines), weekly-activity inline-SVG bar chart, category breakdown (Funktioner/Buggfixar/Design/Infra/Tester/i18n/Dokumentation), and a filterable timeline (free-text + category + author) of expandable PR cards (title/body from the PR cache, commit list, diffstat, `B-0xx` ticket chips) plus per-day direct commits. Light/dark via `prefers-color-scheme`, zero external requests, works opened as a local file. - **`scripts/history/pr-cache.json`** (checked in): 96 PRs enriched with title/body/author from the GitHub API. `npm run history:fetch` tops it up (uses `GITHUB_TOKEN`, silently skips offline); site builds fine from git alone. - **Output committed twice by design:** `frontend/static/changelog/index.html` is canonical (survives the frontend build, which empties `src/main/resources/static`) and mirrored to `src/main/resources/static/changelog/index.html` so the fat JAR serves it without a frontend build. - **`Main.scala`:** explicit `GET /changelog` route — the SPA fallback treats dot-less paths as app routes, so the reserved sitemap URL never resolved before. - **Wiring:** root `package.json` scripts `history:build` / `history:fetch`; docs in `CLAUDE.md`, session entry in `other/ENLANTIS_MASTER_TODO.md`, line in `docs/wiki/log.md`. Not a CI gate — regenerate at end of session. ## Verification ✅ check:ci green (frontend + backend) — frontend guards run via `SKIP_BACKEND=1 npm run check:ci` (svelte-check, vitest 2 874+, i18n, WA, a11y, tokens all ✅); backend gated separately in-session with `sbt compile` + `sbt test` (**3 272 passed, 0 failed**) since `sbt` isn't on this sandbox's PATH. - Visual verification (mandatory rule): Playwright/Chromium rendered the site as `file://` in **light + dark**, screenshots read — tiles, chart bars, category bars, PR cards and expanded details all render correctly; computed styles checked (page bg `rgb(249,249,247)` light / dark ink `#fff`, link color = token blue, no horizontal overflow); client-side filters exercised (search `B-044` → 1 card, category Buggfixar → 31 cards). - `cd frontend && npm run build` → confirmed `src/main/resources/static/changelog/index.html` survives (canonical copy re-copied, md5-identical). - `wiki:lint` OK. - Not verified live: `curl localhost:8090/changelog` (backend boot needs MySQL, unavailable in this sandbox) — the route is a one-line clone of the existing `static/index.html` handler. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01LSWV6kmGwBXbnJcMnKFTou --- _Generated by [Claude Code](https://claude.ai/code/session_01LSWV6kmGwBXbnJcMnKFTou)_
  • d6d2717 Commit built wa icon mirrors into backend static Claude (AI) · 2026-07-19 · +437 −0
  • 7c99091 Auto-generated development-history site at /changelog Claude (AI) · 2026-07-19 · +18 469 −0

#99 Search palette: synonym keywords + close overlays on navigate (B-033, B-041)

2026-07-19 · Claude (AI) · 1 commits · 3 filer · +42 −2 · Buggfixar B-033 B-041

Detaljer
## Summary PR A of the two thematic PRs for the open Fas 06 test findings — this one covers the **search palette** (B-033, B-041). PR B (slaughter-booking B-035/B-036) follows separately. ### B-033 — palette didn't find "compliance" / "inlämning" The command palette matched nav nodes by their displayed (Swedish) label and id segments only, so English/synonym queries missed. Added synonym `keywords` to the two nodes called out in testing: - `farm.movements.submissions` (label **"I&R-rapporter"**) → now found via `inlämning`, `inlämningar`, `i&r`, `submission`, `rapporter`, … - `farm.compliance` (label **"Regelefterlevnad"**) → now found via `compliance`, `efterlevnad`, `regelverk`, `records`, … Root cause of the original "compliance → nothing" was the label being Swedish while the query was English; `inlämning` still returned **0 hits** at handoff because the node label is "I&R-rapporter", not "Inlämningar". ### B-041 — clicking a search result left the palette/mega-menu up The command palette can be opened on top of the mega menu (higher z-index). Activating a palette result navigated in the background while both overlays stayed visible, so it looked like nothing happened. Now a single `afterNavigate` hook in the app layout closes **both** overlays, covering every navigation path (palette, in-menu links, back/forward). ## Changes - `frontend/src/lib/nav/registry.ts` — synonym `keywords` on the submissions + compliance nodes. - `frontend/src/routes/app/+layout.svelte` — `afterNavigate` closes mega menu + command palette. - `frontend/src/lib/nav/search.test.ts` — registry-backed regression tests (uses real Swedish labels) proving `inlämning`/`inlämningar`/`compliance`/`efterlevnad`/`regelverk` resolve to the right node. ## Verification - ✅ `svelte-check` — 0 errors - ✅ `vitest` — 2877 tests pass (incl. new B-033 tests) - ✅ `i18n:check`, `wa:check`, `a11y:check`, `tokens:check` — all green - ⚠️ Backend (`sbt test`) not run: this sandbox has Java 21 only (no Java 25/sbt). **Frontend-only change — no Scala touched**, so the backend gate is unaffected. These are behavioral (not appearance) changes, so the mandatory visual-verification rule doesn't apply. B-033 is covered by the new unit tests; B-041 is a navigation-lifecycle fix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01VZ4oz6dfbUWVs4pX8nrTFa)_
  • 7176210 Search palette: synonym keywords + close overlays on navigate (B-033, B-041) Claude (AI) · 2026-07-19 · +42 −2

#98 Add handoff notes for remaining fas-06 test findings (B-033/B-035/B-036/B-041)

2026-07-19 · Claude (AI) · 1 commits · 1 filer · +61 −0 · Buggfixar B-033 B-035 B-036 B-041

Detaljer
## What Adds `other/HANDOFF_B033-B047.md` — a handoff for a follow-up session covering the remaining open manual-test findings from the fas-06 rounds. ## Details - Records that **B-038** and **B-047** are already fixed (commit `4c03f96` / PR #62) — only four findings remain: **B-033, B-035, B-036, B-041**. - Per-bug fix direction, surface pointers, and development rules (English-only in code, `check:ci` gate, mandatory visual verification with screenshots). - Agreed structure: two thematic PRs — search palette (B-033 + B-041) and slaughter-booking page (B-035 + B-036). Docs-only change; no code touched. ## check:ci Not run — documentation-only change under `other/`, no source or i18n edits. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv)_
  • 266929a Add handoff notes for remaining test findings B-033/B-035/B-036/B-041 Claude (AI) · 2026-07-19 · +61 −0

#97 Phase 06: manual-test support — sandbox gap coverage, B-071/B-072 fixes, health-check draft/view/edit (B-073)

2026-07-19 · Claude (AI) · 3 commits · 18 filer · +883 −53 · Buggfixar B-073 B-072 B-071

Detaljer
## What Support work for the manual phase 06 test rounds, grown across the session as findings came in: - **Playwright coverage of the gaps the local agent skipped** (`other/fas06-01-genomgang/verktyg/fas06-manuell-gaps*.mjs` + screenshots in `shots/gap-*.png`): bottom nav on mobile 390×844 (5 targets ≥ 55 px, all tabs navigate, no horizontal overflow), onboarding wizard + Today in narrow view, the Add animal dialog on mobile (incl. the "Inget stall valt" context callout), `/sim` logged out (banner + greyed cards). Results recorded in `other/TESTRUNDOR_HANDOFF.md`. - **B-071 (fixed):** Swedish typo "Guidens tar under 5 minuter" → "Guiden tar…" in `dashboard.noFarm.body`. - **B-072 (root-caused + fixed):** clicking Log in bounced back / got stuck as an old test user — a stale dev-bypass `dev_user_email` in localStorage swallowed the Kinde `?code=` callback (`_tryDevBypass` runs before the SDK code exchange). `login()`/`register()` now clear the bypass keys before redirecting to Kinde. Dev-only, dead-code-eliminated in prod. - **B-073 (fixed):** health-check form (`…/welfare/health-check`): - all 11 welfare scale inputs show explanation hints (reusing `welfareBarn.field.*.tip`), so 1-vs-5 direction is clear; - checkboxes left-aligned in a straight column (an inherited column flex direction made `align-items: center` center them horizontally; fixed with explicit row direction + `::part(base)` reset); - saved checks appear in the recent list immediately via an optimistic row (outbox write races the server read), deduped by id; - incomplete saves warn and offer **Save as draft**; drafts get a badge and are resumable; - list rows open a view dialog; completed checks are editable — the edit is appended as a **new event with `supersedesId`** so the original stays in the append-only log (Dir 98/58/EC record-keeping) and the list shows the latest version. - Standalone manual-test checklist page at `other/fas06-03-testrundor/checklista.html` (localStorage-backed tick-off). ## Verification Visually verified in a real Chromium against `bun run dev` (screenshots taken and read, committed under `verktyg/shots/`): mobile views, sim logged-out, and the full health-check flow — hints rendered, checkboxes flush left (measured `::part(base)` left = container left), incomplete warning → draft save → badge in list → row click → view dialog → resume draft. ⚠️ check:ci run step-by-step (full script OOM-killed in the sandbox): ✅ `check` (0 errors), ✅ `test` (2874/2874), ✅ `i18n:check`, ✅ `wa:check`, ✅ `a11y:check`, ✅ `tokens:check`. Backend `sbt test` cannot run here (Java 21 only); the only backend-adjacent change is a new optional event payload field, read defensively. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DZ13KdD62fcnjE6g2mrwGv
  • f7588ef Health-check form: scale hints, aligned checkboxes, instant list update, draft/view/edit flow (B-073) Claude (AI) · 2026-07-18 · +591 −49
  • eebe573 Fix B-072: explicit login must clear a stale dev-bypass user Claude (AI) · 2026-07-18 · +27 −1
  • 84cf979 Phase 06: sandbox coverage of manual-test gaps + B-071 fix + B-072 log Claude (AI) · 2026-07-18 · +265 −3

#96 Fix B-070: scope the sync WebSocket per user — stop leaking every farm to every client

2026-07-18 · Jonas · 1 commits · 4 filer · +254 −61 · Buggfixar B-070

Detaljer
> **Stacked on #94 → #95.** Until those merge, the commit list also shows their commits; the **only new change here** is the top commit (`SyncWebSocket.scala`, new `SyncWebSocketSpec.scala`, `sync.ts`, `KNOWN_BUGS.md`). Review just the top commit, or merge #94/#95 first. ## The leak `SyncWebSocket.handleFarmSync` (`sync_all`) called `FarmRepository.listAll()` — the **whole `farm` table, unfiltered** — and broadcast it to every connected client, so every user's farm picker filled with strangers' holdings. REST `GET /api/farms` filters correctly via `listByUserId`; only the WS path leaked. Dev-data mixing today, a **privacy/authorization leak** in prod. The animal handler also accepted any `farmId` with no access check. Bonus damage: the WS farm DTO dropped `country`/`isDemo`/`isSimulated`, silently stripping the DEMO/SIM badges whenever a WS sync overwrote the store (B-020/B-067 family). ## The fix — user-scoped WS protocol Browsers can't set custom headers on a WebSocket handshake, so the caller asserts identity **per message** via a `userId` field in the request `data` — the same client-asserted trust level as the REST `X-User-Id` header in header-auth mode (RouteAuth): - **farm `sync_all`**: requires `userId`; returns ONLY `listByUserId(userId)` (parity with `GET /api/farms`). Missing/invalid `userId` → error frame, never data. - **animal `sync_all`**: requires `userId` + `farmId`; farm must belong to the user or → `Forbidden`. - **DTO**: now carries `country`/`currency`/`wei*`/`isDemo`/`isSimulated` so badges survive WS sync. - **`sync.ts`**: sends `userId`; skips sync until auth has populated `user_id`. ## Verification - New **`SyncWebSocketSpec`**: 7/7 green — incl. *"farm sync_all returns ONLY the requesting user's farms"* (user A never sees user B's farm) and *"animal sync_all for a farm the user does NOT own → Forbidden"*. - **Live** (real backend + browser): after a Dexie wipe, `r2.testare`'s farm list holds exactly its own two farms — **Grisbox `SIM`**, **Lindhult Demo Farm `DEMO` + "Delete demo"** — none of the other five users' farms that leaked before, and the badges survive the WS sync. - `bunx svelte-check`: **0 errors**. check:ci gates run locally (Actions manual-only / out of minutes); pushed with the documented `SKIP_CHECKS=1` bypass. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • daeef4a Fix B-070: scope the sync WebSocket per user — stop leaking every farm Jonas · 2026-07-18 · +254 −61

#95 Fix B-044 (/app stuck on "Laddar gårdar…"); close B-068 by-design; log B-070

2026-07-18 · Jonas · 1 commits · 2 filer · +58 −10 · Buggfixar B-044

Detaljer
> **Stacked on #94.** Until #94 merges, the commit list also shows #94's three commits; the **only new change here** is the B-044 commit (`frontend/src/routes/app/+page.svelte` + `KNOWN_BUGS.md`). Merge #94 first, or review just the top commit. ## B-044 — `/app` stuck on "Laddar gårdar…" (fixed) `app/+page.svelte` is a **runes** component (`$derived`/`$effect`), so a plain `let isLoading = false` is **not reactive** — reassigning it never re-renders. `loadFarms()` sets it true; when a store-driven re-render painted the `{#if isLoading}` spinner while it was transiently true, the `finally { isLoading = false }` could not re-render to clear it → stuck spinner for no-farm accounts. **Root cause proven, not guessed:** sibling `app/farms/+page.svelte` uses the identical `let isLoading` but is in **legacy mode** (no runes) where `let` *is* reactive and never sticks; Svelte 5's own `track_reactivity_loss` dev helper also surfaced for this component. **Fix:** `isLoading` / `showCreateModal` / `newFarmName` → `$state(...)` — all three are non-reactive flags of the same class in this one runes file. ## B-068 — `/advisor` 404 (closed, not a bug) `advisor/+layout.ts` **deliberately** throws `error(404)` for the whole `/advisor` tree — a documented gate keeping the unfinished portal out of search engines/stray links until it ships (comment sketches the future role check). Adding a route would be wrong. Closed as by-design in `KNOWN_BUGS.md`. ## B-070 — WebSocket farm sync leaks every farm to every user (logged, not fixed) Found while reproducing B-044: `SyncWebSocket.handleFarmSync` uses `listAll()` (whole `farm` table, unfiltered by owner), so every account's store gets every user's farms. REST `GET /api/farms` filters via `listByUserId`; only the WS path leaks. Dev-data mixing now, a **privacy/authorization leak** in prod. Logged for its own focused PR — **not** fixed here (out of B-044/B-068 scope). ## Verification `bunx svelte-check`: **0 errors**. Live: `/app` renders without the stuck spinner after the fix. The exact empty-store stuck state is now masked by B-070 (the WS floods the store with farms), which both explains the original flaky repro and is logged above. check:ci run locally (Actions is manual-only / out of minutes); pushed with the documented `SKIP_CHECKS=1` bypass — svelte-check green by hand. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 06a146f Fix B-044: /app stuck on "Laddar gårdar…" (non-reactive isLoading in runes) Jonas · 2026-07-18 · +58 −10

#94 Fix three fas-06 testrunda blockers (AI Ops menu, demo badge, delete demo 500)

2026-07-18 · Jonas · 2 commits · 7 filer · +174 −25 · Funktioner

Detaljer
## What End-to-end verification of the five fas-06 test rounds (`other/TESTRUNDOR_FAS06_RESOR.md`) against a **real backend + real browser**. Three blockers stopped rounds from completing; all three are fixed and re-verified live. ### B-066 — `/api/me/root-admin-status` always returned false → "AI Ops" hidden in the user menu (blocks R5 uppgift 2) The endpoint parsed the root-admin **email** list and tested `emails.contains(userId.toString)` — a UUID never equals an email, so it returned `false` for every real root-admin. The user menu gates the "AI Ops" item behind `{#if $isRootAdmin}`, so the only documented entry to AI Ops was invisible for everyone (incl. Jonas). Fix: new `RouteAuth.isRootAdminUuid` over the resolved-UUID set `requireRootAdmin` already gates on; the status endpoint now uses it (no split-brain). Regression spec added. ### B-067 — demo farm has no DEMO badge / "Delete demo" button (blocks R2 uppgift 5) The onboarding did `farmStore.add(farm)` **before** `seedDemoFarm` flips `is_demo=TRUE` server-side. `farmStore.sync()` is local-wins, so the server value never landed and the card rendered plain "Delete" with no badge. Both demo paths (`welcome`, `farms/newfarm`) now reflect the flip locally. ### B-069 — "Delete demo" returns 500 (blocks R2 uppgift 7) The hard-delete cascade ran `DELETE ... WHERE farm_id` against tables that lack that column (`withdrawal_alert` → `animal_id`, `farm_house_area` → `farm_house_id`) and against `animal_field_update`, which is an event **type**, not a table. First offender threw `Unknown column 'farm_id'`, rolled the transaction back, and the farm was never deleted. Cascade corrected; delete now returns `{"deleted":1}`. ## Verification - **Live**: AI Ops menu item now appears → board loads with data, no 403 (root-admin `jonas@oumworld.com`). Demo card shows green DEMO badge + "Delete demo". `DELETE /api/farms/:id/demo` → `200 {"deleted":1}`, farm list empties. - ✅ `bun run check` (svelte-check): **0 errors** - ✅ `sbt test`: **2889 passed / 0 failed / 10 ignored** (incl. new `RootAdminStatusSpec` guard) check:ci run locally (GitHub Actions is manual-only / out of minutes for July). Pushed with the documented `SKIP_CHECKS=1` bypass because the local pre-push hook's second `sbt` collides with the running dev backend + the known sbt2 jar-lock race with the VS Code Java LS; the gates above were run by hand and are green. ## Not fixed here (logged in `other/KNOWN_BUGS.md`, out of blocker scope) - **B-044** reproduced live: `/app` sticks on "Laddar gårdar…" for a no-farm account (likely non-reactive `let isLoading` in a runes component — should be `$state`). Its own fix. - **B-068**: `/advisor` 404 (unlinked-surface observation). - Native `confirm()` on "Delete demo" (works for a human, freezes CDP automation) + `benchmark_snapshot.farm_id` CHAR-vs-binary no-op — noted for later. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 7827eb7 Record per-round green-light verdicts in testrunda handoff Jonas · 2026-07-18 · +15 −5
  • 3291eca Fix three fas-06 testrunda blockers found in end-to-end verification Jonas · 2026-07-18 · +159 −20

#93 Complete graphics overview + hide hand-drawn farm icons from the app

2026-07-18 · Claude (AI) · 2 commits · 8 filer · +208 −315 · Funktioner

Detaljer
## What Two follow-ups to the icon strategy, both requested by Jonas: ### 1. Make `other/graphics-overview.html` a complete inventory The overview previously covered only **code-generated** graphics. Two new sections close the gap: - **Bildfiler & appikoner (10)** — the five PWA/favicon logo variants (`static/icons/`), the Open Graph share image (SVG source + the PNG used by `seo/meta.ts`), both video poster stills for the /home demo video, and `src/resource/logo.svg` explicitly flagged as an unused duplicate. Rasters are embedded as data URIs so the file stays self-contained. - **Font Awesome Free — UI-ikonbiblioteket (345)** — every vendored glyph (343 `solid/` + 2 `brands/`) rendered in a compact mini-grid, wired into the page's live filter. Intro rewritten ("All grafik i appen"), stats now 446 entries / 429 rendered / 11 categories, nav chips added. ### 2. Hide the hand-drawn farm icons from the app entirely `/brand/icons` was the last surface rendering the hand-drawn farm icon set. Per Jonas the set stays in the repo but must not be visible in the app: - Deleted the `/brand/icons` route and its two links from `/brand`. - Added `src/lib/icons/farm/README.md` marking the 12 components as archived — nothing imports them, so they are excluded from the bundle. - Updated `docs/species-icons.html`, `docs/custom-icons.html`, the wiki decision record (`architecture/species-icons.md` + `log.md`) and the graphics overview (archive cards now say "renderas inte i appen"). ## Verification (all local — no Actions minutes used) - ✅ `check:ci` green after the route removal (svelte-check, vitest, i18n, WA, a11y, tokens). Backend skipped — no backend code touched. - Playwright/Chromium against `bun run dev`: `/brand` renders with zero links to the icon catalog (screenshot read), `GET /brand/icons` → 404. - Overview verified in light + dark theme: 10 + 345 new cards render, 0 broken images, live filter matches the mini-grid. - `npm run wiki:lint` OK. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01RoybuyvJ4DCPLcvvvetV6U
  • c321a10 Hide hand-drawn farm icons from the app entirely Claude (AI) · 2026-07-18 · +51 −308
  • 19921de Make graphics overview complete: image-file assets + FA library Claude (AI) · 2026-07-18 · +157 −7

#84 Icon strategy: species icons from MDI+Fluent, generic UI back to Font Awesome

2026-07-18 · Claude (AI) · 7 commits · 391 filer · +1 274 −999 · Funktioner

Detaljer
## Decision (Jonas, 2026-07-18) After comparing our hand-drawn icons against the open icon libraries: **species/animal icons switch to a vendored MDI + Fluent mix; every generic UI icon reverts to the self-hosted Font Awesome Free set.** The earlier commit on this branch (farm line-art heads wired app-wide) is superseded by this strategy and rolled back in the second commit. ## Species icon set (`frontend/static/wa/icons/animals/`) | Species | Icon | Source | |---|---|---| | Cow, Pig, Chicken, Sheep, Goat, Duck, Rabbit, Turkey, Llama/Alpaca (shared), Bee, Goose, Other | `species-*` | Fluent Emoji High Contrast (Microsoft, **MIT**) | | Horse | `species-horse` | **MDI `horse-variant`** (horse head — explicit choice) | | Bee **barns** | `beehive` | **MDI `beehive-outline`** — a bee house is a colony: house cards show the hive, animal surfaces the insect (new `Stuff.getHouseIcon`) | | Donkey (reserve) | `species-donkey` | **MDI `donkey`** | Each SVG carries a license header; `animals/README.md` documents provenance (`@iconify-json/fluent-emoji-high-contrast@1.2.5`, `@mdi/svg@7.4.47`). Both licenses are free for commercial use with no UI attribution. ## What changed - `wa-icon-resolver.js`: checks `animals/manifest.json` first, then the vendored FA `solid/` set. The `enlantis/` override block is gone. - **Rollback**: `frontend/static/wa/icons/enlantis/` deleted (297 glyphs + 17 aliases + 10 farm heads); `gen-icons.mjs` no longer installs into the app (review artifacts remain under `other/icon-style-proposals/`). All 17 alias names were already vendored in `solid/`, so nothing broke. - `Stuff.getAnimalIcon` → `species-*`; new `Stuff.getHouseIcon` (Bee → `beehive`); house call sites (admin areas + farm dashboard) switched to it. - Nav registry, KPI tiles, demo page repointed; ducks/geese/turkeys get real species icons instead of feather/drumstick placeholders. - **Icon-coverage audit → 100% local**: 30 FA Free icons that the app referenced but had never been vendored (pre-existing broken CDN fallbacks: `comment`, `flag`, `inbox`, `receipt`, `route`, `mountain`, …) vendored from `@fortawesome/fontawesome-free@7.3.1` with clean Free-license headers; 9 Pro-only names remapped to vendored free equivalents (`wheat→wheat-awn`, `grid-2→table-cells`, `scale→weight-scale`, `cleaver→truck-ramp-box`/`drumstick-bite`, `wifi-off→link-slash`, `circle-bolt→bolt`, `comment-exclamation→comment-dots`, `shield-exclamation→shield-halved`, `grid→table-cells`). - **Docs**: `docs/species-icons.html` — visual reference of the full set (incl. the remaining hand-drawn `tractor`/`milking-stand`/`ear-tag` components); new wiki page `docs/wiki/architecture/species-icons.md` as the AI-readable decision record (registered in index, log appended). ## Verification (local — GitHub Actions is out of minutes) - `svelte-check`: **0 errors** (3220 files) · `vitest`: **2874 passed** · `wiki-lint`: OK (78 pages) - **Playwright against `bun run dev` through the real resolver**: all 14 species icons render (Fluent animals + MDI horse head), Bee house card shows the **beehive**, and 21 sampled UI icons (aliases + newly vendored + remapped) all render as FA — **zero CDN-fallback warnings**, i.e. 100% locally served. Screenshots read and confirmed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01RoybuyvJ4DCPLcvvvetV6U
  • fa3339c Switch species-horse to the Fluent horse glyph Claude (AI) · 2026-07-18 · +14 −12
  • 4ec5adb Move tractor and ear-tag cards to the assets section in graphics overview Claude (AI) · 2026-07-18 · +25 −17
  • 89d2885 Update graphics overview with the approved icon strategy Claude (AI) · 2026-07-18 · +143 −22
  • e74e221 Utility icons: ear-tag = MDI tag, tractor stays FA, milking-stand deleted Claude (AI) · 2026-07-18 · +30 −147
  • ad6803b Docs: complete inventory of hand-drawn icons & illustrations Claude (AI) · 2026-07-18 · +486 −0
  • 055372f Species icons: MDI+Fluent mix; retire custom UI glyphs back to FA Claude (AI) · 2026-07-18 · +498 −769
  • 1c834f1 Wire Enlantis' own species icons in everywhere (drop Font Awesome animals) Claude (AI) · 2026-07-17 · +78 −32

#91 Add manual test rounds for the five FAS06 user journeys

2026-07-18 · Claude (AI) · 3 commits · 2 filer · +342 −136 · Tester

Detaljer
Adds two docs under `other/`: **`TESTRUNDOR_FAS06_RESOR.md`** — five manual click-through test rounds for Jonas, one per user journey from `other/FAS06_GENOMGANG.md`: 1. **Resa 1** — brand-new user: landing page → real Kinde signup → onboarding → first logged event 2. **Resa 2** — curious tester: the demo maze (`/demos`, `/home/demo`, `/welcome` demo track), Lindhult demo farm against real backend, demo teardown 3. **Resa 3** — advanced daily user: the three "homes", card wall, mode menu, Ctrl+K palette, quick entry (incl. the new HPCIA acknowledge gate from PR #72), slaughter/FCI flow 4. **Resa 4** — simulator tester: discoverability, `/sim` gating, first real-backend world creation, sim-farm bleed into the farm picker 5. **Resa 5** — new developer/admin: appkarta walkthrough, AI Ops, admin gating consistency, Regelradar + new ingest queue, unlinked surfaces, and the "explain it to someone else" final test Each task states an expected result and cross-references open `KNOWN_BUGS.md` entries (B-nnn) so known bugs get a "kvarstår / verkar fixad" note instead of duplicate reports. Ends with a bug-report template. **`TESTRUNDOR_HANDOFF.md`** — mission brief for a local Claude in Chrome session to verify all five rounds end-to-end against the real backend before Jonas runs them manually (the cloud sandbox cannot run backend/Kinde). Lists likely blockers to test first (B-037, B-044, B-006, B-065, B-030, B-002) and a per-round status table. (First revision of this branch mapped the spec marketing personas instead; replaced after Jonas clarified the stories are the FAS06 journeys.) Docs-only change. `check:ci` result: ✅ all six frontend guards green (svelte-check, vitest, i18n:check, wa:check, a11y:check, tokens:check). ⏭ backend skipped (`sbt` not on PATH in this session). The runner's overall exit was non-zero only because the `bun install` bootstrap step failed on `sharp`'s postinstall binary download (403 from the sandbox network proxy) — an environment limitation unrelated to this diff, which touches no code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_019dTL1FgFFDtGHUFNJ3yUjq
  • 2dda43e Add handoff for local end-to-end verification of the test rounds Claude (AI) · 2026-07-18 · +86 −0
  • 6877378 Replace persona test rounds with FAS06 five-journey test rounds Claude (AI) · 2026-07-18 · +120 −136
  • b706218 Add manual persona test rounds for Jonas's click-through testing Claude (AI) · 2026-07-18 · +136 −0

#90 Add generated-graphics overview page

2026-07-18 · Claude (AI) · 1 commits · 1 filer · +2 511 −0 · Funktioner

Detaljer
## Vad En fristående, självständig HTML-sida — `other/graphics-overview.html` — som samlar **all grafik som appen ritar i kod** (inte färdiga bildfiler), med en överblick över *var* varje grafik används. Öppna filen direkt i en webbläsare (inget byggsteg, inga externa beroenden). ## Vad som ingår (75 poster, 8 kategorier) - **Illustrationer (Stil C)** — tomt-lägen/välkomstflöde i appen + hjältebilder för djurslag, länder och funktioner på marknadsföringssidorna (29) - **Djurslags-ikoner** — 24×24 linjeikoner (13) - **Cartography-komponenter** — CompassMark, ContourBand, PlotPin (3) - **Domänspecifik grafik** — FAMACHA©-ögonreferens (poäng 1–5) + nationsflaggor (11) - **Diagram & trendkort** — individ-trendkort, besättnings-rollups, dashboard-widgetar, kill-sheet-stapel (5) - **Kart- & flödesvisualisering** — Sankey + Europakarta (2) - **Förklarande diagram** — EU-dataresidens, offline-dataflöde, revisionslogg, barn-mode-telefon (4) - **Övriga tillgångar & ikoner** — logotyp, QR-generator, 3D-simulator, schemaläggnings-ikoner (Lit) (8) ## Hur Varje kort renderar den **faktiska grafiken**: statiska SVG:er är extraherade ordagrant ur källkoden; datadrivna diagram och 3D-scenen visas som trogna representativa renderingar (markerade "Om renderingen"). Varje kort visar namn, **var den används** (route/yta) och källfil. Sidan är filtrerbar (namn/sida/fil), tema-medveten (ljust/mörkt) och använder appens egna Cartography-tokens. Grafik-brickorna hålls alltid ljusa eftersom illustrationerna är ritade för ljus bakgrund. ## Verifiering Visuellt verifierad genom rendering i Chromium (headless via CDP) i både ljust och mörkt tema — alla åtta kategorier granskade som skärmbilder: illustrationer, ikoner, diagram, FAMACHA-ögon, kartografi, flaggor, förklarande diagram, logotyp och platshållare renderas korrekt i båda teman. Ändringen är en enda fristående dokumentfil i `other/` och påverkar varken frontend- eller backend-bygget (rör ingen kod som `check:ci` validerar). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01GHMmVz5t5FvfSCzNg19Lte --- _Generated by [Claude Code](https://claude.ai/code/session_01GHMmVz5t5FvfSCzNg19Lte)_
  • a31cb96 Add generated-graphics overview page Claude (AI) · 2026-07-18 · +2 511 −0

#92 Fixa horisontell overflow på marknadsytorna i smala viewports

2026-07-18 · Claude (AI) · 1 commits · 10 filer · +36 −5 · Funktioner

Detaljer
Uppföljningen från stil C etapp 3 ("ContourBand-overflowen"). Vid mätning element för element visade sig **tre separata orsaker**: ## Vad 1. **ContourBand** (`cartography/ContourBand.svelte`): en roterad boxs bounding box är ~`höjd·sin|vinkel|` bredare än layoutboxen → fantomscroll där bandet ligger full-bleed (4px @1000 på /home). Bandet krymps nu med exakt det överhänget och centreras; med kantmasken (fade 0–12 %) är skillnaden osynlig. Fixen gäller alla ~20 konsumenter. 2. **OutboxMock:s clock-skew-pill** ärvde `.pill`:s `white-space: nowrap` — den långa monotexten (`… X-Clock-Skew-Seconds: 7200`) var den verkliga 49px-syndaren @375 på /home. Bryts nu. 3. **SpeciesPage:s coverage-tabell** (min-content bredare än smala telefoner, 120px över @375) scrollar nu i egen `overflow-x: auto`-container, och **HiveGridMock:s apiary-summary** släpper sin nowrap (bisidan var 192px över @375). ## Verifiering - ✅ Grindar gröna: `check` (0 fel), `test` (2874), `i18n:check`, `wa:check`, `a11y:check`, `tokens:check`. Backend orörd. - ✅ **Alla 26 marknadsytor** (home, 8 art, 8 land, 8 feature, barn-mode) mäter **0 px horisontell overflow @375**; /home även ren @1000/@1280. - ✅ Visuellt verifierat oförändrat på desktop (PNG:er lästa, incheckade som `verktyg/shots/overflowfix-*`): bandet på home, apiary-headern (en rad @1280, bryter @375), coverage-tabellen (fullbredd på desktop, scrollcontainer @375). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m --- _Generated by [Claude Code](https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m)_
  • 11f38e3 Fix horizontal overflow on marketing surfaces at narrow viewports Claude (AI) · 2026-07-18 · +36 −5

#88 Stil C etapp 3 PR 5 — home story-beat-vinjetter (skurken + bättre morgondag)

2026-07-18 · Claude (AI) · 3 commits · 13 filer · +308 −15 · Design/UI

Detaljer
Femte PR:en i stil C etapp 3. **Ligger kvar som draft tills Jonas granskat skärmdumparna** — det här är etappens högsta designrisk (nytt läge: berättande interiörvinjetter i stället för hero-planscher). ## Vad Två INTERIÖRSCENER (står inte på den delade betekullen) som rimmar på varandra: - **`HomeStoryVillain`** (beat 2, "You know this feeling"): köksbordet 23:40 — lutande pärmar, spridda ark (ett på golvet), kallt kaffe, sen klocka, regn i fönstret. Negativiteten ligger i komposition/rekvisita, aldrig i palett eller djur: ockra-laveringen sitter PÅ pappersproblemet (pärmar + ark), salvian är nästan borta (bara det kalla kaffet). - **`HomeStoryTomorrow`** (beat 5, "Less paper. More farm."): samma bord/fönster/kopp på morgonen — en tunn mapp, telefon med bock, ångande kaffe, betekullen + sol genom fönstret. Salvian tillbaka (kullen, mappen); ockran har flyttat från pappret till solen. Layout: sidlokal `.beat-grid` (`minmax(0,1fr)/minmax(0,0.5fr)`, kollaps <900px) — vinjetten max 360px bredvid listan, INTE hero-tvåkolumn. Block-head och slutraden (`villain-close`/`tomorrow-close`) ligger kvar i fullbredd utanför griden. 820px-listan i beat 5 fick plats bredvid vinjetten även @1000 — den förberedda fallbacken (plansch efter close) behövdes inte. ## Verifiering - ✅ Frontend-grindar gröna: `check` (0 fel), `test` (2874), `i18n:check`, `wa:check`, `a11y:check`, `tokens:check`. 0 nya i18n-nycklar. Backend orörd. - ✅ Visuellt verifierad (Playwright, PNG:er lästa — skript `stilc-e3-pr5-verify.mjs`, shots `verktyg/shots/e3pr5-*`): båda beats @1280/@1000 (2 spår, vinjett ≤360px, 4:3) och @375 (1 spår), close-raden utanför griden, preview-cellerna på plats. - ⚠️ Observation (EJ denna PR): home har en pre-existerande horisontell overflow från ContourBand (49px @375, 4px @1000) — uppmätt identiskt på master utan dessa ändringar. Uppföljningskandidat. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m --- _Generated by [Claude Code](https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m)_
  • e1a7495 Retake PR 5 reference shots — previous pair had the Vite error overlay Claude (AI) · 2026-07-18 · +0 −0
  • aecb16c Refresh PR 5 reference shots from the post-merge verify run Claude (AI) · 2026-07-17 · +0 −0
  • 068f773 Stil C etapp 3 PR 5 — home story-beat vignettes (villain + better tomorrow) Claude (AI) · 2026-07-17 · +308 −15

#89 Stil C etapp 3 PR 6 — barn-mode-hero + FinalCTA-beslutet

2026-07-17 · Claude (AI) · 1 commits · 8 filer · +179 −16 · Design/UI

Detaljer
Sjätte och sista PR:en i stil C etapp 3. ## Vad - **`BarnModeHero`:** ladan med korsförstärkt port, och telefonen på **noll signalstaplar** (överstrukna) med posten redan bockad — "sparat i ladan, ingen signal behövs". Ochra på ladan, salvia på telefonskärmen. - **`/barn-mode`** (utanför `[[lang=locale]]`): heron konverterad sidlokalt till tvåkolumnslayout via den delade `.hero--illustrated` i global.css (samma grid som art-/lands-/feature-sidorna — hissen från #86 betalar sig). Demo-widgetens **teal-telefon-SVG står medvetet kvar** (off-token; separat uppföljningsärende). - **FinalCTA får INGEN plansch — dokumenterat beslut:** komponenten renderas på varje marknadssida, så en plansch där (1) buntas i alla bundles (samma misstag som det förkastade slug-registryt i etapp 2), (2) saknar texttomrum att sitta i, och (3) repetition av samma motiv på varje sida degraderar registret. Omprövas endast om FinalCTA görs om till sidunik yta. Även i `other/icon-style-proposals/README.md`. ## Verifiering - ✅ Frontend-grindar gröna: `check` (0 fel), `test` (2874), `i18n:check`, `wa:check`, `a11y:check`, `tokens:check`. 0 nya i18n-nycklar. Backend orörd. - ✅ Visuellt verifierad (Playwright, PNG:er lästa — skript `stilc-e3-pr6-verify.mjs`, shots `verktyg/shots/e3pr6-*`): barn-mode @1280 (grid 2 spår, text vänster, svg 4:3, cta i linje, ingen overflow) + @375 (enkolumn, centrerad) + teal-telefonen intakt + preview-cellen. Obs: `index.ts`/preview-sidan/README ändras på samma ställen som draft-#88 (story-beats) — den som mergas sist får en trivial konflikt; jag löser den i #88-grenen. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m --- _Generated by [Claude Code](https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m)_
  • de67d30 Stil C etapp 3 PR 6 — barn-mode hero plate + FinalCTA decision Claude (AI) · 2026-07-17 · +179 −16

#87 Stil C etapp 3 PR 4 — de fyra återstående feature-planscherna

2026-07-17 · Claude (AI) · 1 commits · 17 filer · +387 −0 · Design/UI

Detaljer
Fjärde PR:en i stil C etapp 3 — ren utfanning på mönstret från #86 (inga mall- eller CSS-ändringar; bara 4 planscher + wiring). ## Vad | Sida | Plansch | Motiv | |---|---|---| | movements | `FeatureHeroMovements` | Utvikt trepanelskarta, streckad rutt mellan två plot-pins | | breeding | `FeatureHeroBreeding` | Litet stamträd: två föräldrabrickor ner till en avkommebricka, hjärta vid unionen | | integrations | `FeatureHeroIntegrations` | Gårdsgavel ⇄ myndighetshus (pediment + kolonner), streckade utbytespilar | | offline-first | `FeatureHeroOfflineFirst` | Utkorg med köade ark, moln på streckad uppskjuten länk, tålamodsklocka | Named exports i `index.ts`, preview-celler på `/design/illustrations`, 0 nya i18n-nycklar. Därmed har **alla 8 feature-sidor** illustrerad hero. ## Verifiering - ✅ Frontend-grindar gröna: `check` (0 fel), `test` (2874), `i18n:check`, `wa:check`, `a11y:check`, `tokens:check`. Backend orörd. - ✅ **Visuellt verifierad** (Playwright, PNG:er lästa + computed styles — skript `stilc-e3-pr4-verify.mjs`, shots `verktyg/shots/e3pr4-*`): de 4 nya sidorna @1280 (grid 2 spår, text vänster, svg 4:3, ingen overflow) + spot-check av alla 4 PR 3-sidor + preview-sidans 8 FeatureHero-celler + breeding @375 (enkolumn, centrerad). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m --- _Generated by [Claude Code](https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m)_
  • 7d56d95 Stil C etapp 3 PR 4 — the four remaining feature hero plates Claude (AI) · 2026-07-17 · +387 −0

#86 Stil C etapp 3 PR 3 — FeaturePage hero plates + shared illustrated-hero CSS

2026-07-17 · Claude (AI) · 1 commits · 26 filer · +538 −63 · Design/UI

Detaljer
Tredje PR:en i stil C etapp 3 (efter #82 pathfinder + #83 landsplanscher + #85 flagg-fix). ## Vad **CSS-hissen (F2-fyndet från pathfinder-reviewn, Jonas-godkänd):** den duplicerade `.hero--illustrated`-tvåkolumnsgriden + `.hero-art svg`-4:3-fallbacken är flyttad från `SpeciesPage.svelte` och `CountryPage.svelte` till `global.css`, bredvid `.hero--start`. Specificitetsdetalj: Sveltes scopade `.hero { text-align: … }` (klass + hash-klass) slår globala enklassregler, så varje malls centrerade fallback är nu scope:ad som `.hero:not(.hero--illustrated)` — den globala gridden vinner när en plansch är satt. Sidspecifik spacing (`.lede`/`.lead`-marginaler, mobil-centrering av h1/cta-row) stannar lokalt. Enda avsiktliga visuella skillnaden: artsidornas mobil-gap mellan text och plansch följer nu landssidornas 1.25rem (var 2.25rem). **FeaturePage hero-prop:** samma `hero?: Component`-mönster som SpeciesPage/CountryPage. Planschen sitter i heron (texttomrummet); surface-mocken behåller sin panel under — tre register: löfte (herotext), hantverk (plansch), bevis (mock). Utan prop behålls centrerad hero (de 4 återstående feature-sidorna orörda tills PR 4). **4 nya planscher** (stilleben ur bondens vardag, inte scener — står inte på `HERO_GROUND_D`): | Sida | Plansch | Motiv | |---|---|---| | ai-copilot | `FeatureHeroAiCopilot` | Kompass över öppen dagbok, streckad ledlinje ner till bockad rad, gnistor | | animal-register | `FeatureHeroAnimalRegister` | Tre öronbrickor på skena, var och en faller (streckat) i sin registerrad | | health-management | `FeatureHeroHealth` | Medicinflaska + timglas — behandlingen och dess karensklocka | | compliance | `FeatureHeroCompliance` | Dokument med vikt hörn, bock, bandförsett sigill med stjärnring | Named exports i `index.ts` (inte registryobjektet), preview-celler på `/design/illustrations`, 0 nya i18n-nycklar (dekorativa `aria-hidden`). ## Verifiering - ✅ Frontend-grindar gröna: `check` (0 fel), `test` (2874), `i18n:check`, `wa:check`, `a11y:check`, `tokens:check`. Backend orörd (0 Scala-filer i diffen). - ✅ **Visuellt verifierad** (Playwright mot `bun run dev`, PNG:er lästa + computed styles mätta — skript `other/fas06-01-genomgang/verktyg/stilc-e3-pr3-verify.mjs`, shots incheckade under `verktyg/shots/e3pr3-*`): - 4 illustrerade feature-sidor @1280: grid = 2 spår, text vänster, svg `aspect-ratio 4/3`, cta-raden i linje med textkolumnen, surface-mocken kvar, ingen horisontell overflow. - 4 ej-illustrerade feature-sidor: centrerad fallback intakt. - Hiss-regression: `species/cattle` + `eu/sweden` renderar oförändrat ur global.css (grid/vänster/4:3, flagga i h1); `/design/species-fallback` fortsatt centrerad. - `/design/illustrations`: alla 4 nya celler; health-management @375: enkolumn, centrerad. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m --- _Generated by [Claude Code](https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m)_
  • 0c5e201 Stil C etapp 3 PR 3 — FeaturePage hero plates + shared illustrated-hero CSS Claude (AI) · 2026-07-17 · +538 −63

#85 Landssidans hero: flaggan i rubriken i stället för eyebrow-raden

2026-07-17 · Claude (AI) · 1 commits · 9 filer · +10 −11 · Funktioner

Detaljer
Designjustering på den illustrerade landssidans hero (Jonas begäran). ## Problem Flaggan satt inline i början av eyebrow-raden, vilket (a) såg tafatt ut framför den lilla versaltexten och (b) knuffade eyebrow-texten så "FARM —" hamnade ensam (orphan) på rad två. ## Fix Flaggan flyttad in framför titeln i `<h1>` (0.9em, baslinjeställd, ~0.5rem luft) som nationell markör. Eyebrow-raden är nu fri och får plats på en rad. Fallback-varianten (planschlösa landssidor) behåller den stora 3.5rem-flaggan centrerad ovanför eyebrow. Rör bara `CountryPage.svelte`. ## Verifiering - Playwright mot `bun run dev`, **alla 8 landssidor** (skärmdumpar `verktyg/shots/e3flag-*.png`, lästa + computed styles): flagga i h1, eyebrow = 1 rad, ingen horisontell overflow. - Grindar (CI nere — lokalt): `svelte-check` 0 fel · `a11y:check` ✅ · `tokens:check` ✅ (0 nya i18n-nycklar — flaggan är dekorativ `aria-hidden`, återanvänder `marketingCountries.<key>.flag`). Wiki: ingen uppdatering (UI/styling). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m --- _Generated by [Claude Code](https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m)_
  • aa1a504 Country hero: move the flag into the h1 instead of the eyebrow line Claude (AI) · 2026-07-17 · +10 −11

#83 Stil C etapp 3 · PR 2 — de 7 övriga landsplanscherna

2026-07-17 · Claude (AI) · 1 commits · 28 filer · +561 −0 · Design/UI

Detaljer
Utfanning efter Sverige-pathfindern (#82, mergad). Sju nya landsplanscher på samma anatomi + mönster som Sverige — inga mall- eller CSS-ändringar (hero-prop finns redan i `CountryPage` från #82). ## Vad - **7 nya `CountryHero*.svelte`** (Danmark, Tyskland, Frankrike, Nederländerna, Polen, Irland, Finland) — samma recept som Sverige: viewBox 0 0 128 96, förskjuten `translate(2.6,2.6)`-wash-grupp, 2px `var(--ink)`, sekundärdetalj 1.5px @ 0.72, tokens only. - **`scene.ts`**: nya `HERO_GROUND_FLAT_D`/`_WASH_D` (flack baslinje för DK/NL lågland); DE/FR/PL/IE/FI står på den delade betekullen. - Named exports i `index.ts` (ej registry), hero-prop på de 7 route-wrappers, 7 preview-celler på `/design/illustrations`. ## Motiv DK bindingsværk + bokgren + vindsnurra · DE Fachwerk + ek + plogfåror · FR kalkstensgård + luckor + lavendel · NL väderkvarn + polderdike + tulpaner · PL timmerstuga + lövträd + råg · IE stenstuga + stenmur + sol · FI sjöstuga + bastu + björkar + sjöreflex. **Jonas-fixar efter första utkastet:** Irland-solen fick strålar runt om (hade bara på halva); Polens stork-på-pinne blev ett generellt lövträd (fågeln blev en linjeknut). ## Verifiering (alla 8 landssidor) - Playwright mot `bun run dev` (`stilc-e3-pr2-verify.mjs`, skärmdumpar `verktyg/shots/e3pr2-*.png` — **alla lästa**): illustrerad tvåkolumnshero, svg `aspect-ratio: 4/3` + höjd = bredd×0.75, inline-flagga, på alla 8. Mobil (375) spot-checkad på Finland: enkolumn centrerad, ingen horisontell overflow. - Grindar (CI nere — lokalt): `svelte-check` 0 fel · vitest grönt · `i18n:check` ✅ (0 nya nycklar, dekorativa `aria-hidden`) · `wa:check` ✅ · `a11y:check` ✅ · `tokens:check` ✅ Wiki: ingen uppdatering (UI/styling). Kvar i etapp 3: PR 3–4 (feature-sidor), PR 5 (story-beats), PR 6 (barn-mode + FinalCTA-beslut). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m --- _Generated by [Claude Code](https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m)_
  • 892977a Stil C etapp 3 PR 2 — seven remaining country-page hero plates Claude (AI) · 2026-07-17 · +561 −0

#82 Stil C etapp 3 · PR 1 — Sverige-pathfinder (landssidans hero-plansch)

2026-07-17 · Claude (AI) · 2 commits · 18 filer · +259 −8 · Design/UI

Detaljer
Första PR:en av sex i stil C etapp 3 (fullt omfång beslutat av Jonas 2026-07-17). **Detta är review-grinden: mönstret + receptet ska godkännas här innan de 7 övriga landsplanscherna (PR 2) och resten fanas ut.** ## Vad - **`CountryHeroSweden.svelte`** — falurött torp (gavelfront, skorsten, dörr + spröjsade fönster), vita knutar som bläck-ticks, gran och hässja på den delade betekullen (`HERO_GROUND_D`). Receptet: 2px `var(--ink)` + förskjuten salvia/ockra-lavering (0.34/0.42), sekundär detalj 1.5px @ 0.72. **Falurött läses ur bläcket** — laveringen håller sig inom etapp 2:s tokenpar. - **`CountryPage.svelte`** — samma hero-som-prop-mönster som SpeciesPage: valfri `hero: Component`, `.hero--illustrated`-tvåkolumnsgrid + delade `.hero--start`, explicit `aspect-ratio: 4/3`-fallback på svg:n, kollaps till centrerad enkolumn <760px. Named export i `index.ts` — **inte** i registryobjektet (etapp 2-granskningens regel: en sida buntar bara sin egen plansch). - **Flaggbeslut (godkänn/underkänn i denna review):** i illustrerad variant krymps flagg-emojin från 3.5rem-mittpunkt till ~1.4rem inline på eyebrow-raden — planschen tar över som nationell identifierare, flaggan behålls som skanningsstöd. Centrerad 3.5rem kvarstår som fallback på de 7 planschlösa sidorna. - Preview-cell på `/design/illustrations` + beslutslogg i `other/icon-style-proposals/README.md`. ## Öppen designfråga till review Burgundy-wash för faluröden? Nuvarande linje: nej — bläcket bär det, paletten expanderas inte unilateralt. Säg till om du vill se en variant med `--color-primary`-lavering på torpet. ## Verifiering (alla berörda ytor — mallen ändrades, så alla 8 landssidor) - Playwright mot `bun run dev` (`stilc-e3-pr1-verify.mjs`, skärmdumpar i `verktyg/shots/e3pr1-*.png`, lästa + computed styles mätta): - **sweden** @1280: tvåkolumn (466/378px-spår), `text-align: left`, svg `aspect-ratio: 4/3` + höjd = bredd×0.75, inline-flagga 22.4px — @720/@375: enkolumn centrerad, plansch max 340px - **denmark/germany/france/netherlands/poland/ireland/finland** @1280: pixelidentiska med master — verifierat genom stash→mät→pop: flaggans/eyebrowens boundingrects byteexakt lika (t.ex. DK flagga x=370.421875 y=287.484375 i båda). OBS: flagga-bredvid-eyebrow på fallback-sidorna är **förexisterande** (global `.eyebrow` är inline-block), inte en regression härifrån. - **/design/illustrations**: ny cell renderar 128/96/72/48px + båda mörka cellerna - Grindar (CI nere — lokalt): `svelte-check` 0 fel · vitest grönt · `i18n:check` ✅ (0 nya nycklar — planschen är dekorativ `aria-hidden`) · `wa:check` ✅ · `a11y:check` ✅ · `tokens:check` ✅ Wiki: ingen uppdatering (UI/styling — "do not update"-kategorin). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m --- _Generated by [Claude Code](https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m)_
  • e156184 Review fix: drop inert .hero--start on CountryPage illustrated hero Claude (AI) · 2026-07-17 · +4 −3
  • c8df555 Stil C etapp 3 PR 1 — Sweden country-page hero pathfinder Claude (AI) · 2026-07-17 · +255 −5

#81 Regelradar: produktions-runbook för provisionering (fas 06-kandidat 1, förberedelse)

2026-07-17 · Claude (AI) · 1 commits · 3 filer · +139 −3 · Funktioner

Detaljer
Förberedelsedelen av fas 06-kandidat 1 (Regelradar i prod). Själva provisioneringen + live-verifieringen körs med Jonas när Cloud Run-uppgifterna finns till hands — den här PR:en skriver proceduren som hittills saknats (wikins "still open"-punkt). ## Innehåll - **Ny wikisida `docs/wiki/architecture/regulatory-radar-ops.md`** — runbook med: - Konfigurationsreferens (alla `REGELRADAR_*`-envs med defaults, lästa av `RegelradarIngestConfig`; LLM-vägen där prod-default `ENLANTIS_USE_VERTEX=true` betyder att ingen API-nyckel behövs) - Secret Manager-steg för IMAP-lösenordet (samma mönster som `enlantis-db-password`) + exakta `gcloud run services update`-kommandon — viktigt eftersom `deploy.ps1` inte sätter några env-vars alls - §16-begränsningen: exakt EN `kind='mailbox'`-källa (processglobal `MailboxConfig`) - Stegvis aktivering (ingest först, auto-draft efter kedjecheck) - Verifieringskedjans checklista: poll → `detected` → auto-draft-`note` → advance/promote → publish → farmer-feed - Kill switches/rollback - **`regulatory-radar.md`**: stale "still open"-listan uppdaterad (SFS-kedjeextraktionen levererades i #78) och pekar nu på runbooken - **`log.md`**: en rad ## Grindar - `npm run wiki:lint` → OK (78 sidor, inga brutna wikilänkar) - Inga kodändringar (CI nere hela juli; ej relevant här) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m --- _Generated by [Claude Code](https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m)_
  • 23fcba0 Add Regelradar production provisioning runbook (fas 06-kandidat 1 prep) Claude (AI) · 2026-07-17 · +139 −3

#80 Småfixar C6/C9/C20 från 2026-07-17-granskningen

2026-07-17 · Claude (AI) · 1 commits · 10 filer · +209 −15 · Funktioner

Detaljer
Stänger de tre dokumenterade uppföljningarna från handoffens §1. ## C6 — atomär upsert i `RegDocumentRelationRepository` Select-then-insert racear mot `UNIQUE KEY uq_regrel (from_external_id, to_external_id, relation_type)` — två samtidiga `POST /api/admin/regelradar/relations` med samma trippel kunde ge 500. Nu raw-JDBC `INSERT … ON DUPLICATE KEY UPDATE id = id` (samma mönster som `FxRateRepository`; no-op-updaten bevarar befintliga redaktionella kanter orörda) följt av re-SELECT på trippeln. Route-idempotensen pinnad i `RegelradarIngestRoutesAuthSpec` (dubbel POST → 201 + 201, en rad). ## C9 — en hash-semantik i poll-vägen Evidens-hashen för amendment-kanter (SFS + CELLAR) beräknades med en andra, icke-trimmande SHA-256 medan versionsfingeravtrycket trimmar (`BlockDiff.contentHash`). Båda går nu genom `contentHash`. Mailbox-fallbackens `sha(subject+"|"+date)` är **avsiktligt orörd** — det är identitetsderivering, en trim-swap skulle ändra genererade external-ids (kommenterat vid hjälparen). Pinnad i `SfsAmendChainSpec`. ## C20 — SpeciesPage-fallbacken bevakad Alla 8 artsidor skickar hero-prop, så den centrerade fallbacken var en död, otestad väg. Ny dev-only-sida `/design/species-fallback` renderar riktiga `SpeciesPage` utan `hero` (404:ar i prod via `design/+layout.ts`), länkad från `/design/illustrations`, plus Playwright-verifieringsskript `c20-species-fallback-verify.mjs` med incheckade skärmdumpar. ## Grindar (CI nere hela juli — allt kört lokalt) - Backend: `testOnly *` → **3264 gröna** (varav 2 nya), 0 fallerade - Frontend: `svelte-check` 0 fel · vitest **2874 gröna** · `i18n:check` ✅ · `wa:check` ✅ · `a11y:check` ✅ · `tokens:check` ✅ - Visuellt (obligatoriskt): `/design/species-fallback` (centrerad, ingen `.hero-art`), `/home/species/cattle` (illustrerad tvåkolumn orörd, svg `aspect-ratio: 4/3`), `/design/illustrations` (länk) — skärmdumpar lästa, computed styles mätta, se `verktyg/shots/c20-*.png` Wiki: ingen uppdatering — ren bugfix utan arkitektur-/domänbeteendeändring. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m --- _Generated by [Claude Code](https://claude.ai/code/session_01E51ibXJedZUAJvii58QS4m)_
  • 5196e76 Fix C6/C9/C20 follow-ups from the 2026-07-17 review pass Claude (AI) · 2026-07-17 · +209 −15

#77 Species-adaptive farm-house area fields (all animal types)

2026-07-17 · Claude (AI) · 3 commits · 22 filer · +456 −144 · Infra/verktyg

Detaljer
## Problem On **Stallar & Områden** (`/app/farms/[id]/admin/areas`), the "Lägg till område" dialog always rendered pig-only fields — **Max grisar (deklarerat)** and **Viktklass** (pig weight bands) — even on a cattle barn. Reported by Jonas: adding an area to a nötkreatur barn should offer cattle-relevant options, not pigs. The whole stack was pig-shaped: `farm_house_area` carried `max_pigs` / `weight_band` (migration 0229, Dir 2008/120/EC), flowing form → DTO → row → the `/pen-capacity` compliance report. The parent barn already knows its species (`FarmHouse.animalType`), but the area dialog never read it. ## What changed Generalise the area model so fields adapt to the barn's species for **every** animal type. - **Migration 0246** renames `farm_house_area.max_pigs`/`weight_band` → species-neutral `max_head`/`category` (pure rename; existing pig rows keep their values). `FarmHouseAreaRow`, the three area DTOs and `PenCapacityRowDTO` (`maxHead`/`category`/`sqmPerHead`) follow. - **`frontend/src/lib/data/housingCategories.ts`** (new) is the single source of truth for per-species category options: pig weight bands, cattle (calf bands / heifer / bull / dairy_cow / suckler_cow), sheep, goat, poultry, horse, rabbit, camelid — **none for Bee/Other**. The area dialog reads `selectedHouse.animalType` and renders the matching **Category** dropdown (hidden when the species has no categories), plus a generic **Max animals (declared)** field. - **`WelfareWatchdog.pigLegalMinSqm`** is now reached through a species-dispatched `legalMinSqm(animalType, category)`. Pig keeps its Dir 2008/120/EC floors; calves get Dir 2008/119/EC floors (1.5 / 1.7 / 1.8 m²). Adult cattle / sheep / goat categories are **descriptive-only** (`None` → status `unknown`) — SJVFS national figures could **not** be verified from an authoritative source in this environment (egress to Jordbruksverket / EUR-Lex / lagen.nu is blocked), so they are intentionally not wired into the breach/compliant gate rather than risk false compliance verdicts. They slot into `legalMinSqm` later without further schema change. - Pen-capacity report + CSV export use the neutral field names; the callout/column copy is generalised so a multi-species report no longer reads as pig-only. - i18n: new `housing.*` generic field labels + `areaCategory.*` per-species category labels (en + sv; other locales fall back to en per the existing partial-locale design). ## Verification — full CI-equivalent suite run locally (green) The account is out of GitHub Actions minutes (runs fail at provisioning in ~3 s, master included), so the whole suite was run locally instead. **Frontend** (every `ci.yml` frontend step): - `svelte-check`: **0 errors** - `vitest`: **2874 passed** (160 files) - `i18n:check` ✅ · `wa:check` ✅ · `a11y:check` ✅ (0 failing) · `tokens:check` ✅ **Backend** (sbt 2.0.0-RC13, Scala 3.8.3 — bootstrapped from Maven Central, run on the sandbox's Java 21; `javaHome` falls back to system JVM when `JAVA25_HOME` is unset, and the Java 25 requirement is runtime-only Netty `--add-opens`, not compile): - `Test/compile`: **success, 0 errors** (main + all test specs compile with the renames + new lookups; needed `-Xss32m` for the deep inline-macro chains in the >20-field DTOs) - `sbt test`: **3240 tests passed, 0 failed, 10 ignored** - Directly-relevant specs green: `Dir2008120PigsSpec` (exercises `pigLegalMinSqm`, now reached via `legalMinSqm`) and `AnimalLocationRoutesAuthSpec` (constructs `FarmHouseAreaRow`). **Visual** (Playwright/Chromium against `bun run dev`): rendered the real category block via the production `categoriesFor`/`categoryLabelKey`/`$t` for Cow / Pig / Sheep / Chicken / Horse / Bee — Cow → cattle categories, Pig → weight bands unchanged, each species its own, Bee → no dropdown, generic "Max animals (declared)" throughout. ## Follow-up Wire verified SJVFS 2019:18 (cattle) / 2019:21 (sheep) / 2019:22 (goat) floor-space figures into `cattleLegalMinSqm` / `sheepLegalMinSqm` /
  • 1b043df Gitignore .sbt-boot/ (local sbt launcher for CI-less test runs) Claude (AI) · 2026-07-17 · +1 −0
  • ab42a8e Neutralise pen-capacity copy for multi-species report Claude (AI) · 2026-07-17 · +7 −7
  • 5b7d56d Species-adaptive farm-house area fields (all animal types) Claude (AI) · 2026-07-17 · +448 −137

#79 CI: manuell-only GitHub Actions + lokal testgrind (inga GitHub-minuter)

2026-07-17 · Claude (AI) · 2 commits · 5 filer · +234 −27 · Tester

Detaljer
## Varför GitHub Actions-minuterna är slut. `ci.yml` auto-kördes på **både** `push` till master **och** `pull_request` (två körningar per merge) och misslyckades på i princip varje körning — den brände minuter utan att ge något grönt "OK att merga". Perf-workflowsen är redan `disabled_manually`, och deploys går via GCP Cloud Build (inte Actions). Testerna själva går att köra lokalt utan kvalitetsförlust: backend-testerna är självförsörjande (in-memory `FakeRepos`, ingen MySQL), och frontend-checkarna är lokala bun/node-skript. ## Vad - **`.github/workflows/ci.yml`** → trigger ändrad till endast `workflow_dispatch` (manuell). Båda jobben (`frontend-tests`, `backend-tests`) behålls så CI:n går att starta för hand från Actions-fliken för en clean-room-körning. Den föråldrade header-kommentaren (påstod "disabled_manually" trots att workflowen var aktiv) rättad. - **`scripts/check-ci.mjs`** (ny) + root-`package.json` `check:ci` → en lokal runner som speglar `ci.yml` steg för steg: de sex frontend-vakterna (`check`, `test`, `i18n:check`, `wa:check`, `a11y:check`, `tokens:check`) + `sbt test`. Kör alla steg, skriver en pass/fail-sammanfattning, exit ≠ 0 vid fel. Föredrar `bun`, faller tillbaka på `npm`. **Hoppar över backend snyggt** när `sbt` saknas (t.ex. Windows-dev-boxen) — agent-sandboxen har Java 25 + sbt och grindar det där. Flaggor: `SKIP_BACKEND=1`, `SKIP_CHECKS=1`. - **`.githooks/pre-push`** (ny) → kör `check:ci` före varje push och blockerar vid fel. Självinstalleras via root-`package.json` `prepare` (`git config core.hooksPath .githooks`). Nöd-bypass: `SKIP_CHECKS=1 git push`. - **`CLAUDE.md`** → dokumenterar den lokala grinden och regeln att agenter kör `check:ci` innan de öppnar en PR och skriver resultatet i PR-beskrivningen (merge-signalen, istället för en Actions-grön bock). ## Så får man "OK att merga" framöver 1. **Agent-PR:er:** agenten kör `check:ci` och skriver `✅ alla gröna` i PR:en. 2. **Manuella pushar:** en push som gick igenom = godkänd (hooken blockerar annars). 3. **Manuellt:** `npm run check:ci` → sammanfattningstabell i terminalen. ## Verifiering Kört i sandboxen (utan `frontend/node_modules`, `sbt` saknas): - Runnern kör alla steg och skriver sammanfattningstabell; `i18n:check`, `a11y:check`, `tokens:check` blev **äkta gröna**; `check`/`test`/`wa:check` fallerade endast p.g.a. att `node_modules` inte var installerat (miljö, inte runner); backend visades korrekt som `⏭ hoppad (sbt not on PATH)` och räknades inte som fel; exit-kod 1 vid fel, 0 via `SKIP_CHECKS=1`. - `.githooks/pre-push` anropar runnern och blockerar (exit 1); `SKIP_CHECKS=1` kringgår (exit 0). - `prepare` sätter `core.hooksPath=.githooks`. - `ci.yml` `on:`-block innehåller endast `workflow_dispatch` (inga push/PR-auto-triggers). - Branch protection: CI är bevisligen inte en required status check (PR:er mergas löpande trots röd CI), så avstängd auto-körning blockerar inte merges. ## Not En GitHub-hanterad "Copilot code review"-workflow är också aktiv — den kan bara stängas i repo Settings (inte via en fil) om den drar credits. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01P484fjebj14MnJWQnU9JY7 --- _Generated by [Claude Code](https://claude.ai/code/session_01P484fjebj14MnJWQnU9JY7)_
  • 60a4b81 check-ci: install frontend deps if missing, PATH-based tool detection Claude (AI) · 2026-07-17 · +34 −14
  • 08bd2bc CI: move GitHub Actions to manual-only, add local test gate Claude (AI) · 2026-07-17 · +200 −13

#78 Regelradar Layer-3: auto-extract SFS amendment chains from crawled statute pages

2026-07-17 · Claude (AI) · 2 commits · 9 filer · +762 −196 · Funktioner

Detaljer
# What Fas 06 candidate 2 — **SFS amendment-chain auto-extraction** for the Regelradar Layer-3 legal-corpus graph. Migration `0234` introduced `reg_document_relation` (amendment-chain adjacency keyed by external CELEX/SFS ids). EU/CELEX edges already auto-populate from the CELLAR amended-by graph on every `cellar` poll; **SFS chains were editorial-only** (flagged as an open item in `other/REGELRADAR_PLAN.md` §12 since 2026-06-15). This PR closes that gap: SFS statute pages fetched by the `crawl` pillar now have their amendment references extracted automatically and written into the graph. ## How (mirrors the CELLAR path exactly) - **`service/regelradar/SfsAmendments.scala`** — new pure extractor, the SFS analogue of `CellarClient.amendersOf`. Harvests the standard Swedish change-act forms from the fetched page's normalized text: - explicit phrase `Lag/Förordning (YYYY:N) om ändring i … (base)` — with or without the spelled-out base number; - `SFS YYYY:N` tokens on amendment-context lines (containing `ändr…` or `träder i kraft`). Deliberately conservative: a change-act phrase naming a **different** base statute is skipped, bare cross-references and malformed numbers are silently ignored, no self-edges, output distinct + sorted (same contract as `CellarClient.amendersOf`). - **`RegelradarIngestService.pollCrawl`** now calls `upsertSfsAmendEdges` after `recordFetch` — the exact structural mirror of `pollCellar` → `upsertAmendEdges`. When the crawl source's `externalRef` is an SFS id (the seeded SFS statutes from migration `0235` have e.g. `SFS 2018:1192`), one `amended_by` edge per amender goes through the shared, generalized `upsertAmendEdges` helper (param renamed `celex` → `fromExternalId`; CELLAR call site unchanged). Best-effort — an edge failure never fails the poll tick. - **Idempotence / editorial safety:** edges go through the existing `RegDocumentRelationRepository.upsert` (unique `(from, to, type)`, existing row wins), so re-polling never duplicates and **editorial rows entered via `POST /api/admin/regelradar/relations` are never overwritten** (note/createdBy preserved verbatim — pinned by a test). ## Decisions taken (per instruction: mirror CELLAR, document instead of blocking) - **Edge direction:** `from = base statute, to = amending act, type = "amended_by"` — identical to the CELLAR edges. - **External-id canonical form:** `SFS YYYY:N` (matches the `reg_source.external_ref` seed format from migration `0235`). - **Extraction runs on every crawl poll** (not only on content-hash change) — same as the CELLAR path, which upserts amender edges on every poll. - **Scope:** only the `crawl` pillar hooks the extractor (SFS statutes are registered as `crawl` sources); `rss`/`mailbox`/`cellar` are untouched. - **Context gating over recall:** bare `(YYYY:N)`/`SFS YYYY:N` mentions outside an amendment context are *not* edges, to avoid polluting the graph with cross-references. Missed exotic phrasings can still be entered editorially, and false negatives are safer than false edges in a regulatory graph. ## Tests (local — GitHub CI is down all July, red runs expected) Full backend gate run locally from the worktree (`java -Xss16m -Xmx8g -jar sbt-launch.jar test`, no MySQL needed): ``` 3254 tests passed. 0 tests failed. 10 tests ignored. [info] Completed tests [success] elapsed time: 57 s, cache 5%, 26 disk cache hits, 417 onsite tasks ``` New coverage (12 tests): - `SfsAmendmentsSpec` (pure, 8): canonical-id normalization + rejection of non-SFS refs; explicit change-act phrase (incl. `SFS`-prefixed base and missing base attributed to the tracked statute); different-base phrase skipped; context-line tokens; bare cross-references excluded; no self-edges; malformed refs silently ignored; distinct + sorted output. - `SfsAmendChainSpec` (service, 4, mirrors `RegelradarAutoDraftHookSpec`'s in-memory-repo setup with live upsert semantics): crawl of an SFS statute page → `amended_by` edges with provenance (`sourceId`) +
  • e86136e Code-review fixes for SFS amendment-chain extraction (PR #78) Claude (AI) · 2026-07-17 · +343 −184
  • f85c7c9 Regelradar Layer-3: auto-extract SFS amendment chains from crawled statute pages Claude (AI) · 2026-07-17 · +419 −12

#76 KNOWN_BUGS: stäng B-015-resten + stil C etapp 2-granskningsfixar

2026-07-17 · Claude (AI) · 2 commits · 22 filer · +93 −60 · Buggfixar B-015

Detaljer
## Vad Två saker på samma uppföljningsbranch: **1. B-015-stängning (docs):** stänger den sista öppna delresten av **B-015** i `other/KNOWN_BUGS.md` — den dubblerade obligatorisk-asterisken från skärmdumpen 2026-07-09. Jonas beslut 2026-07-17: avskrivs utan repro (ingen ny observation; varje genomgång sedan dess visar exakt en asterisk). Återöppnas som ny bugg om den ses igen. Huvudbuggen (död "Spara vikt"-knapp) fixades redan 2026-07-11. **2. Granskningsfixar för stil C etapp 2** (`code-review --fix` på mergade PR #74-diffen; 8 finder-vinklar + adversariell verifiering): - **Hero-som-prop ersätter slug-registryt** (C11/C12): varje artsida importerar sin egen plansch och skickar den till `SpeciesPage` — det eagra `speciesHeroes`-registryt buntade alla 8 SVG-komponenter i varje artsidas chunk och växte det konsumentlösa `illustrations`-registryt. - **`aspect-ratio: 4/3`-fallback** på `.hero-art svg` (C10): bredd-enbart-SVG:n förlitade sig helt på motorns viewBox-härledda ratio; nu explicit block + height:auto + aspect-ratio. - **Delad `.hero--start`-modifierare i global.css** (C17): vänsterställda hero-varianter (h1 + .cta-row) ägs nu av en additiv global regel i stället för att varje sida lokalt slåss mot `.hero h1`-centreringen. - **Delad markbana i `illustrations/scene.ts`** (C16): kullens path var byte-identisk 16 gånger (wash + stroke × 8 filer); nu en konstant. - Beslutslogg + master-TODO uppdaterade. Skippade fynd (medvetet): 8×-komponentskalet behålls per set 1:s en-fil-per-motiv-linje (C19); centrerade fallback-heron förblir otestad död väg tills en art utan plansch finns (C20). ## Verifiering (lokalt — GitHub-CI nere, minuterna slut; röda checkar ignoreras) - `svelte-check`: 0 fel · `vitest`: 2874/2874 · `i18n`/`wa`/`a11y`/`tokens`: ✅ - **Visuellt: ALLA 8 artsidor** renderade i skarp sidkontext (Chromium mot `bun run dev`), mätta (356×267, ratio 1.333, `aspect-ratio: 4/3`, `justify-content: flex-start`) och skärmdumpar lästa — inklusive sheep/goats/layers/broilers som saknades i etapp 2:s ursprungliga verifiering (granskningsfynd C21). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Wnq9bVHnAGMfV2rxgamB85
  • 5c77672 Review fixes (stil C etapp 2): hero-as-prop, aspect-ratio fallback, shared modifiers Claude (AI) · 2026-07-17 · +87 −59
  • 00073eb KNOWN_BUGS: close the B-015 remainder (duplicated asterisk) without repro Claude (AI) · 2026-07-17 · +6 −1

#75 Webhooks admin: move HMAC code samples to ?raw snippets (fixes Vite express dep-scan error)

2026-07-17 · Claude (AI) · 2 commits · 5 filer · +125 −83 · Funktioner

Detaljer
## What Fas 06-kandidat 4 ("städkandidaten"): Vite's cold-start dependency scanner parsed the inline template-literal code examples on the webhooks admin page (`frontend/src/routes/app/farms/[id]/admin/webhooks/+page.svelte`) as real code. The Node.js sample contains `import express from 'express'`, so a cold `bun run dev` (empty `node_modules/.vite`) aborted with: ``` Error: The following dependencies are imported but could not be resolved: express (imported by .../admin/webhooks/+page.svelte?id=0) ``` ## Why / how The three HMAC-verification samples (Node.js/express, Python/Flask, curl/openssl) are now plain-text files under `./snippets/` imported with Vite's `?raw` suffix — the dep scanner never parses `?raw` assets as JavaScript, so the fake `express` import is invisible to it. Snippet content is byte-identical to the previous template literals (tabs, LF, unicode `≥`/`…`), so the page renders exactly as before. The explanatory comment moved up with the imports. ## Local verification (all green) GitHub Actions minutes are exhausted for July — **CI checks on this PR will be red/skipped and should be ignored**; everything below was run locally in the sandbox. - **Before/after proof:** cold `bun run dev` (after `rm -rf node_modules/.vite`) on the old page reproduces the express dep-scan error; with this change the same cold start is clean — `grep -i express` on the dev log finds nothing. - `bun run check` (svelte-check): **0 errors** (3209 files; pre-existing warnings only) - `bun run test` (vitest): **2874 passed / 160 files** - `bun run i18n:check`: all `$t()` keys covered - `bun run wa:check`: all `wa-*` boolean-attr bindings guarded - `bun run a11y:check`: 50 pairs checked, 0 failing - `bun run tokens:check`: no token drift (861 files) - **Visual verification (mandatory, markup touched):** rendered `/app/farms/farm-1/admin/webhooks` in headless Chromium (Playwright + mockApi/`?dev_user=` harness) against the dev server; opened the "Så här verifierar du X-Enlantis-Signature-huvudet" panel and screenshotted + read all three tabs. Node.js, Python and curl samples render exactly as before: dark code panels, tab indentation, `import express from 'express'` / `from flask import …` / `openssl dgst -sha256 -hmac` all present, no console errors. No wiki update: pure frontend build hygiene, no domain/architecture change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Wnq9bVHnAGMfV2rxgamB85 --- _Generated by [Claude Code](https://claude.ai/code/session_01Wnq9bVHnAGMfV2rxgamB85)_
  • cb86013 Move developers/webhooks code examples to ?raw snippet files (C18) Claude (AI) · 2026-07-17 · +67 −31
  • d437299 Webhooks admin: move HMAC code samples to ?raw snippet files Claude (AI) · 2026-07-17 · +58 −52

#74 Stil C etapp 2: artsidornas hero-planscher (8 motiv) + SpeciesPage-slot

2026-07-17 · Claude (AI) · 1 commits · 13 filer · +770 −2 · Design/UI

Detaljer
## Vad Stil C etapp 2 — marknads-heros för artsidorna, enligt grillade beslut (godkända av Jonas i sessionen): - **8 hero-planscher** (`frontend/src/lib/illustrations/SpeciesHero*.svelte`, 4:3, `viewBox 0 0 128 96`): nötkreatur (godkänd pathfinder), gris, får, get, häst, värphöns, slaktkyckling, bin. Receptet från set 1: bläcklinje 2 px `var(--ink)` + förskjuten salvia/ockra-lavering via Cartography-tokens, transparent (inga mörka band finns på marknadssidorna — `IllustrationPlate` förblir reserven). - **`speciesHeroes`-registry** nycklat på artsidans slug; `SpeciesPage.svelte` slår upp sin plansch själv → noll ändringar i de 8 sidfilerna, centrerad hero kvar som fallback. Ny `.hero--illustrated`-layout (text vänster / plansch höger, staplas <760 px) med överstyrning av globala `.hero h1`-flexcentreringen. - **Preview-sidan** `/design/illustrations` utökad med de 8 motiven; "mörk utan platta"-cellen ometiketterad som dokumenterat felläge. - **Beslutslogg** (komplettera / 4:3 bredvid text / mörkt läge uppskjutet / artsidorna komplett först) i `other/icon-style-proposals/README.md` + sessionsrad i `ENLANTIS_MASTER_TODO.md`. Formspråk: profilkropp + framifrån-vänt huvud (ekar godkända stil B-kons ansikte); artsignaler per motiv (juver/fläckar, tryne/knorr, ull+lamm, hängöron+skägg, man+svans, rede+ägg, fodersäck+pickande fågel, kupa+flygbanor). ## Status **Alla 8 motiv godkända av Jonas** (kon som pathfinder 2026-07-16, de 7 övriga via provkarta 2026-07-17) — PR:en är ready for review. Etapp 3 (landssidor, story-beats, feature-sidor) är medvetet utanför och grillas i egen omgång. ## Verifiering (CI på GitHub nere — minuterna slut, kört lokalt) - `svelte-check`: 0 fel (3217 filer; varningar preexisterande) - `vitest`: 2874/2874 gröna (160 filer) - `i18n:check` / `wa:check` / `a11y:check` (50 par, 0 fail) / `tokens:check`: alla ✅ - Visuellt i Chromium mot `bun run dev`: `/design/illustrations` + artsidorna cattle/pigs/bees/horses i skarp kontext, desktop + mobil (skärmdumpar granskade, computed check `.hero-art svg` ratio 1.333) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Wnq9bVHnAGMfV2rxgamB85
  • 7858eea Stil C etapp 2: species-page hero illustrations (8 motifs) + SpeciesPage slot Claude (AI) · 2026-07-16 · +770 −2

#73 Docs: refresh fas 06 handover for the next thread (post PR #72)

2026-07-16 · Claude (AI) · 1 commits · 1 filer · +88 −68 · Funktioner

Detaljer
Dokumentationsuppdatering efter att PR #72 (fyrspårs-leveransen + granskningspasset) mergats: handover-blocket i `other/FAS06_FIXPASS_PLAN.md` skrivs om för nästa tråd. - **Klart-sektionen** speglar det mergade läget (mjölk-KPI, HPCIA, Regelradar-dispatcher/hook/UI, B-015 parkerad, 10 granskningsfynd åtgärdade). - **Uppdragskandidater för nästa tråd:** Regelradar ops-provisionering (§16) + live E2E-genomgång, SFS amendment-chain-extraktion, B-015 (väntar skärmdump), express-depscan-städningen, samt den medvetet skippade `hpciaAcknowledged`-projiceringen som eftermonterbar idé. - **Reglerna** utökade med passets lärdomar: migreringsräknaren på 0245, wa-dialogens `--width`-fälla (nu även i DESIGN.md), de tre synkade HPCIA-nyckelordsspeglarna med svenska FASS-stavningar, och re-fetch-före-full-row-update-regeln efter långsamma (LLM-)anrop. - Lägesavsnittet "Läge 2026-07-16" kompletterat med granskningspassets resultat och mergen. Endast `other/FAS06_FIXPASS_PLAN.md` — ingen kod. (Branchen är omstartad från master efter mergen av #72; CI-checkarna failar fortsatt utan runner p.g.a. slut på Actions-minuter för juli — inte kodfel.) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01R5yx5wYP5GGesLeynx288L --- _Generated by [Claude Code](https://claude.ai/code/session_01R5yx5wYP5GGesLeynx288L)_
  • 09cca06 Docs: refresh next-thread handover after PR #72 merged Claude (AI) · 2026-07-16 · +88 −68

#72 Fas 06: HPCIA-flaggning, mjölk-KPI → A1, Regelradar auto-draft + dispatcher + ingest-kö-UI

2026-07-16 · Claude (AI) · 7 commits · 31 filer · +3 081 −464 · Buggfixar B-015

Detaljer
Levererar alla fyra kvarvarande fas 06-spår (scope avstämt med Jonas 2026-07-16: HPCIA fullskaligt med DB-kolumn + obligatorisk bekräftelse; Regelradar båda delspåren + riktig dispatcher; B-015 parkeras). ## Spår 2 — Mjölksidans KPI-rad → A1-receptet - `production/milk`: KPI-raden (4 tiles ur `herdStats`) på delade `.accent-tile` från `global.css`; sidans lokala tile-CSS borttagen (gridwrappern kvar). - Visuellt verifierad: `verktyg/milk-kpi-a1.mjs` **7/7 PASS** (computed styles: 4px accentlist, 2.1rem-värden; skärmdumpar 1440w + 700w lästa). ## Spår 1 — HPCIA-flaggning (EMA/AMEG kategori B, Reg 2019/6 art. 107) - **Migrering 0245**: `medication.substance_class` + idempotent ren-SQL-backfill (samma keyword-ordning som klassificeraren). - **`domain/HpciaClassifier`** (Scala) deriverar klassen server-side på medication-POST/PUT från `activeIngredient`/`name` — inget klientfält, kan inte spoofas. HPCIA = fluorokinoloner, 3:e/4:e gen cefalosporiner, kolistin. - **`$lib/compliance/hpciaClassifier`** (TS-spegel, extraherad ur AMU-månadsrapporten som nu importerar den): föredrar server-stämplad klass, keyword-fallback för offline/osyncade Dexie-rader. Divergensnät: identisk ~22-substansfixtur i bägge språkens tester. - **Behandlings- + bulkdialogen**: danger-callout + obligatorisk kryssruta ("användningen följer veterinärens förskrivning") som blockerar Spara för HPCIA-läkemedel; bekräftelsen följer eventet som nytt additivt fält `EventPayload.Treatment.hpciaAcknowledged` (gamla event dekodar — pinnat i codec-test). HPCIA-badge i behandlingslistan + medicinskåpet. Röstflödet behöver ingen egen gate (navigerar in i standarddialogen före medicinval). - **Bifångst**: behandlingsdialogens `min-width: 560px` överskred WA-dialogens standardbredd (496px) → alla fält klipptes med osynlig horisontell scroll. Fixad med samma `--width`-override som health-check-dialogen; overflow-koll tillagd i det visuella scriptet. - Visuellt verifierad: `verktyg/track-a-hpcia.mjs` **9/9 PASS** (callout/kryssruta, blockerad→upplåst spar, badges, ingen overflow; skärmdumpar lästa). ## Spår 4 — Regelradar: dispatcher + auto-draft + ingest-kö-UI - **`AiAgentDispatcher.live` config-grindad**: `AnthropicAiAgentDispatcher` (befintlig dual-transport `LlmClient`, `role.systemPrompt`/`model`, historik som textturer) när `CopilotLlmConfig.isConfigured`; deterministiska stubben annars (dev/CI bootar utan nyckel). Låser även upp AI Ops-chatt/scheman för riktiga anrop. - **Draft-orkestreringen flyttad** från routes till `service/regelradar/RegelradarDraft.scala` + ny `RegelradarDraftService`; `/draft`-endpointens HTTP-kontrakt (503/502) oförändrat. - **Auto-draft-hook** i `recordFetch` (enda chokepointen för alla fyra ingest-vägarna): best-effort daemon-fiber som fyller `reg_update_event.note` på nya `detected`-events — fäller/fördröjer aldrig ingest, advancerar aldrig `reviewState`. Ny flagga `REGELRADAR_AUTO_DRAFT_ENABLED` (default false) = dubbel kill-switch ovanpå dispatcherns config-gate. - **Nytt back-office-UI** `/app/admin/regelradar/ingest`: state-filtrerad kö, detaljdialog (block-diff + AI-utkastet renderat som etiketterade fält med defensiv JSON-parse), Draft/Advance/Promote, källpanel med poll-now. Root-admin-gate via delade admin-layouten; apiFetch; `.dialog-scroll`. - Visuellt verifierad: `verktyg/track-b-regelradar-ingest.mjs` **12/12 PASS** (skärmdumpar lästa). ## Spår 3 — B-015 (dubblerad asterisk): parkerad Jonas vet inte längre på vilken yta dubbleringen sågs → ingen repro möjlig; dokumenterat i `KNOWN_BUGS.md`. Inget blint fixförsök. ## Dokumentation `FAS06_FIXPASS_PLAN.md` nytt lägesavsnitt + handover; wiki uppdaterad (health-and-treatment, regulations/veterinary-medicines, ai-ops, regulatory-radar + log) — `wiki:lint` OK (77 sidor). CLAUDE.md:s migreringsrad → 0245. ## Lokala CI-resultat (GitHub Actions-minuterna är slut för juli — kör inte om de röda checkarna) | Grind | Resultat | |---|---| | Backend `sbt test` | **3155 passed, 0 failed** (10
  • 9b7b92f Code-review fixes (fas 06 four-track diff): Swedish INN spellings, lost-update races, shared HPCIA gate Claude (AI) · 2026-07-16 · +387 −216
  • cb6c984 Docs: fas 06 four-track delivery — state section, handover, B-015 parked, wiki Claude (AI) · 2026-07-16 · +101 −6
  • 6b4f337 Regelradar: back-office ingestion review-queue UI (/app/admin/regelradar/ingest) Claude (AI) · 2026-07-16 · +848 −28
  • 4496e5d Regelradar: Anthropic-backed AiAgentDispatcher + auto-draft hook on detected events Claude (AI) · 2026-07-16 · +656 −123
  • e2ebfbe HPCIA frontend: shared AMEG classifier + mandatory acknowledgement gate + badges Claude (AI) · 2026-07-16 · +566 −78
  • 4f9efea HPCIA backend: medication.substance_class (migration 0245) + treatment acknowledgement field Claude (AI) · 2026-07-16 · +399 −5
  • a13d170 Milk page KPI row: migrate to shared .accent-tile (A1 recipe) Claude (AI) · 2026-07-16 · +124 −8

#71 Stil C illustrationer: set 1 (fem app-interna motiv) + leverans

2026-07-16 · Claude (AI) · 3 commits · 16 filer · +582 −50 · Design/UI

Detaljer
Startar **stil C-etappen** — stora handritade illustrationer för tomma tillstånd och onboarding, en skild roll från stil B:s 16–24 px-funktionsglyfer (som mergades i #68/#69). Recept: **bläcklinje 2 px (`var(--ink)`) + förskjuten lavering (salvia/ockra, ~0.34) på papper, viewBox 96**, färgad via Cartography-tokens. Allt i denna PR grillades fråga för fråga och godkändes innan något ritades/kopplades in — inget i appen ersattes förrän konsten var godkänd. ## Grillade beslut - **Motivlista/omfång:** litet kärnset först — fem app-interna motiv (empty states + onboarding-toppar). Marknads-heros + per-art skjuts till etapp 2. - **Leverans:** ett `.svelte` per motiv (samma mönster som `src/lib/icons/farm/`), **inte** statiska SVG + resolver — stil C är flerfärgad och kan inte rida på `wa-icon`-pipen (som tvingar enfärg `currentColor`). - **Mörkt läge:** appen har inget mörkt tema; konsten är transparent och ligger på papper. `IllustrationPlate.svelte` (djupare pappersplatta) är byggd men **oanvänd** — reserverad för framtida mörka ytor. Ingen mörk variant ritas nu. ## Levererat - `frontend/src/lib/illustrations/` — fem motiv + `index.ts` (barrel + registry) + `IllustrationPlate.svelte`. - `frontend/src/routes/design/illustrations/` — dev-only förhandssida (storlekar + mörk platta). - `EmptyState.svelte` — valfri `illustration`-prop (företräde över `icon`; alla befintliga anropsställen orörda). ## Motiv → värdar Öronbricke-berättelse löper genom setet: **tom bricka (#1) → ifylld & stämplad (#5)**. | # | Motiv | Värd | |---|---|---| | 1 | `EmptyAnimalRegister` (öppen liggare + tom bricka) | djurlistans tomma läge + dashboardens första-djur-CTA | | 2 | `AllCaughtUp` (sol över kullar) | co-pilot / idag / uppgifter "allt klart" | | 3 | `EmptyProductionLog` (tom skördekorg, 1 motiv för ~20 loggar) | äggloggen (representativ) | | 4 | `WelcomeFarmstead` (gård på kulle) | dashboardens ingen-gård-CTA | | 5 | `FirstAnimalRegistered` (bricka ifylld + ✓) | komponent klar; payoff-inkoppling återstår | ## Verifiering Renderat i Chromium mot `bun run dev` via dev-only `/design/illustrations`: alla fem motiv renderar korrekt i appen (storlekar 128/96/72/48, papper + reserverad platta), tokens resolvar, mörk-platta-mekanismen fungerar. Samtliga berörda moduler kompilerar (Vite-transform 200, ingen felutdata). ## Återstår (uppföljning) - Välkomstguiden: `WelcomeFarmstead` (chooser) + `FirstAnimalRegistered` (payoff-steg). - Svep över de ~19 övriga produktionsloggarna (#3). - Marknads-heros (etapp 2, motiv 8–14). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01X3p6qVbFUx9AVUNfd7Stwe --- _Generated by [Claude Code](https://claude.ai/code/session_01X3p6qVbFUx9AVUNfd7Stwe)_
  • cc040f2 Kodgranskning: ta bort död CSS efter ikon→illustration-bytena Claude (AI) · 2026-07-16 · +0 −15
  • f065dcf Stil C set 1: koppla in välkomstguiden + skördeloggarna Claude (AI) · 2026-07-16 · +26 −27
  • f1aa4dd Stil C illustrationer: set 1 (fem app-interna motiv) + leverans Claude (AI) · 2026-07-16 · +556 −8

#70 Fas 06: Regelradar — live IMAP transport for the Layer-1 mailbox ingestor

2026-07-16 · Claude (AI) · 7 commits · 10 filer · +665 −154 · Funktioner

Detaljer
## What Track 2 from the fas 06 handover: `MailboxIngestor.poll` goes from config-gated NoOp to a **real IMAP client** — the last missing piece of the Regelradar Layer-1 newsletter pillar (everything downstream already worked via the admin `ingest-email` route). ### Transport (`integration/regulatory/MailboxIngestor.scala`) - **Angus Mail 2.0.3** (Eclipse's Jakarta Mail implementation) is the only new runtime dependency. - One poll: connect (IMAPS default; `mailbox.ssl=false` gives plain IMAP for the embedded test server), open the configured folder, fetch every **UNSEEN** message, flag them **`\Seen`**, close without expunge. `\Seen` is the transport-level dedup; the ingest service's content-hash change detection dedups again downstream, so re-delivered/un-flagged mail never creates duplicate update events. - Messages are reduced to `ParsedEmail` via the typed API (`fromMessage`): decoded headers + a MIME-multipart walk (text/plain preferred, text/html normalized through `HtmlNormalize`, attachments ignored) — no raw-bytes round trip that would drag boundary markers into the body text. - Connect/read timeouts 15s/30s so a wedged mail server can never hang the 6h poll worker; transport errors log a warning and yield `Nil` (poll stays `UIO`). The TODO_CONFIG NoOp posture is unchanged — unconfigured deployments behave exactly as before. ### Config - New `enlantis.regelradar.mailbox.port` (default 993) + `mailbox.ssl` (default true), env `REGELRADAR_MAILBOX_PORT`/`REGELRADAR_MAILBOX_SSL`; added to both `application-local.conf` and `application-prod.conf`. ### Docs - `REGELRADAR_PLAN.md` §16 (delivery writeup + go-live checklist: provision `reg-watch@…`, set the env vars, register mailbox sources — no code left), wiki `architecture/regulatory-radar.md`, `FAS06_FIXPASS_PLAN.md` new state section + handover (wiki-lint OK). ## Verification - New `MailboxImapSpec` runs the transport **end-to-end against an embedded GreenMail IMAP server** (test-scope dependency, dynamic port): unseen fetch + header/body parsing, multipart/alternative without boundary leakage, HTML-only normalization, `\Seen` incrementality (second poll empty), unreachable server → warning + `Nil`, TODO_CONFIG → NoOp. - **9/9** mailbox tests pass (`MailboxImapSpec` 5 + existing `MailboxParseSpec` 4); the full backend (704 sources) compiles clean with the new dependency. ## Go-live (ops, no code) Provision the shared mailbox, then set `REGELRADAR_MAILBOX_{HOST,USER,PASSWORD}` (+ optional `PORT`/`SSL`/`FOLDER`) and `REGELRADAR_INGEST_ENABLED=true`, and register `kind='mailbox'` sources in `reg_source`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Nkj1W5cX4cYXURr7bE6U4L --- _Generated by [Claude Code](https://claude.ai/code/session_01Nkj1W5cX4cYXURr7bE6U4L)_
  • 575ef3b Docs: handover for next thread — plan + execute all four remaining fas-06 tracks Claude (AI) · 2026-07-16 · +54 −20
  • 2a2786e Docs: Actions minutes exhausted for July — run CI gates locally (handover note) Claude (AI) · 2026-07-16 · +9 −1
  • 5ced527 CI retrigger probe: check whether runner provisioning recovered Claude (AI) · 2026-07-15 · +0 −0
  • edef20c CI retrigger after runner-provisioning outage Claude (AI) · 2026-07-15 · +0 −0
  • e32ff7b CI retrigger: previous run failed at runner provisioning (jobs died in 2s with no runner assigned) Claude (AI) · 2026-07-15 · +0 −0
  • 5b00714 Review fixes: flag \Seen only after successful ingest, per-message isolation, BODY.PEEK Claude (AI) · 2026-07-15 · +285 −107
  • f118e8e Regelradar: live IMAP transport for the Layer-1 mailbox ingestor Claude (AI) · 2026-07-15 · +317 −26

#69 Enlantis-egna ikoner: 297 glyfer + 17 alias ersätter Font Awesome per namn

2026-07-15 · Jonas · 2 commits · 623 filer · +8 231 −8 · Funktioner

Detaljer
## Vad Hela ikonspråket omritat i egna Enlantis-glyfer i Cartography-stil ("brännmärke": solid siluett i `currentColor` med `fill-rule=evenodd`-urtag, 40×40-rutnät) och aktiverat i appen via resolver-företräde — utan att röra ett enda av de ~3 000 anropsställena. - **`frontend/static/wa/icons/enlantis/`** — 314 SVG:er (297 glyfer + 17 aliasfiler, t.ex. `info-circle`→`circle-info`) + **eget** `manifest.json`. Eget manifest därför att `fetch-wa-icons.mjs` regenererar huvudmanifestet och skulle tappa nycklarna. - **`wa-icon-resolver.js`** — prövar `enlantis/<namn>` före `solid/<namn>`, endast solid/classic-familjen (varumärken i brands, t.ex. `bluetooth-b`, ritas aldrig om). Fallbackkedjan enlantis → vendorerad FA → FA-CDN är intakt. - **`other/icon-style-proposals/`** — glyfkällorna (`gen-icons.mjs` + `icons-rest-1..6.mjs`), granskningssidor (`enlantis-60/index.html` + Enlantis-stylade `enlantis.html`) och beslutslogg i `README.md`. ## Varför Egna, unika, lantliga ikoner i linje med appens tema i stället för Font Awesome. Kärnset om 60 godkändes efter iterationsrundor (hästen blev hästsko, stetoskopet veterinärväska, `tag` öronbricka, `robot` kompassros, `rocket` grodd ur såfåra); etapp 2 täcker resterande glyfnamn som appen använder. ## För granskaren - Öppna `other/icon-style-proposals/enlantis-60/enlantis.html` i webbläsaren för att se glyferna i Enlantis-kontext (banners, knappar, djurtabell, mörk yta). - En glyf justeras i `gen-icons.mjs`/skärvorna och `node gen-icons.mjs` regenererar SVG:er + granskningssidor + appinstallationen. - Rollback per ikon: radera filen ur `enlantis/` + namnet ur `enlantis/manifest.json` → FA-versionen används igen. - Cache-brasklapp: besökare med gammal `wa-icon-resolver.js` i webbläsarcache ser FA-ikoner tills cachen förnyas (ofarligt). - Verifierat: visuell genomgång i Chrome (dev-servern) + per-glyf-kontroller; ikoner utanför setet faller korrekt tillbaka till `solid/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 19df511 Kodgranskningsfix: extrahera delade SVG-hjälpare till icons-helpers.mjs Jonas · 2026-07-15 · +21 −7
  • 63eb8fa Enlantis-egna ikoner: 297 glyfer + 17 alias ersätter Font Awesome per namn Jonas · 2026-07-15 · +8 210 −1

#68 Fas 06: apiFetch debt paid down to zero — all 283 pending files migrated, real 401 gaps closed

2026-07-15 · Claude (AI) · 2 commits · 276 filer · +1 824 −2 146 · Funktioner

Detaljer
## What Track 1 from the 2026-07-15 handover (`other/FAS06_FIXPASS_PLAN.md`), delivered in full: the remaining apiFetch auth-wrapper debt is migrated down to **zero**. ### Debt migration (codemod + hand fixes) - **261 files / 698 literal `fetch('/api…')` call sites** rewritten to `apiFetch` (`$lib/api/fetch`): the wrapper injects `X-User-Id`; redundant manual `authHeaders()` header properties, spreads and imports were removed. - **22 files verified public-only** (only `/api/public/*`-class endpoints) stay on bare `fetch` and were delisted. - `api-fetch-guard-pending.json` is now **empty** — the B-026 regression guard protects all of `frontend/src`. - `/api/disease-alerts` added to the guard's `PUBLIC_ENDPOINT_PATTERNS` (documented open route in `DiseaseAlertsRoutes.scala`; its store keeps bare fetch, locked by its unit test). ### Real latent 401s closed - `admin/incidents` `saveIncident` — POST/PUT against a variable URL (never matched the guard's literal pattern), had no auth → now `apiFetch`. - `admin/support` `exportCsv` — went through `window.location.href`, which can never carry the `X-User-Id` header (guaranteed 401 on the root-admin endpoint) → authenticated fetch + blob download (filename matches the backend's `Content-Disposition`). - Shop product **food-info-autofill** — read `window.__userId`, which is set nowhere (header always empty). - `help/ai-copilot-architecture` **copilot agent toggle/dryrun** — no auth at all. - `organicStore.fetchOrganicCourses` — no auth at all. ### Docs - `KNOWN_BUGS.md` (B-026 class fully closed), `FAS06_FIXPASS_PLAN.md` (new "Läge 2026-07-15 kväll" + handover), `ENLANTIS_MASTER_TODO.md` one-liner, wiki `architecture/frontend.md` + `log.md` (wiki-lint OK). ## Verification - vitest **2867/2867** (incl. both guard tests with the emptied snapshot) - svelte-check **0 errors** - production build OK (build-output churn deliberately not committed) - Playwright smoke (`other/fas06-01-genomgang/verktyg/apifetch-smoke.mjs`, extended with 5 newly-migrated surfaces): **24/24** — every `/api` call from 8 surfaces carries `X-User-Id`; screenshots of admin-webhooks and health surfaces inspected (full app chrome, styled WA components). ## Notes - Pre-existing dev-only quirk (reproduced on HEAD, **not** a regression): Vite's cold dep-scan complains about `express`/`node:crypto` from the webhooks page's copy-paste code-sample template strings when `node_modules/.vite` is cold. Harmless (server still serves); noted in the plan file as a cleanup candidate (`?raw` imports). - First-run Playwright flakes under cold Vite compile (blank render / auth-miss on the first surface) — rerun before debugging; noted in the plan file. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Nkj1W5cX4cYXURr7bE6U4L --- _Generated by [Claude Code](https://claude.ai/code/session_01Nkj1W5cX4cYXURr7bE6U4L)_
  • b6fb241 Code-review fixes: no JSON content-type on file uploads, straggler migrations, dead helpers Claude (AI) · 2026-07-15 · +34 −51
  • 67d5ddb apiFetch debt paid down to zero: migrate all 283 pending files to the auth wrapper Claude (AI) · 2026-07-15 · +1 790 −2 095

#67 Docs: next-thread handover after PR #66 (apiFetch pass)

2026-07-15 · Claude (AI) · 1 commits · 2 filer · +41 −4 · Funktioner

Detaljer
Ren dokumentation. Överlämning till nästa fas 06-tråd efter att PR #66 (apiFetch-passet) mergats. - **`other/FAS06_FIXPASS_PLAN.md`** — ny paste-in-ruta "Handover till nästa tråd" högst upp (läsordning, vad som mergades i #66, de fem kandidatspåren, regler att minnas) + uppdaterad dateline. - **`other/ENLANTIS_MASTER_TODO.md`** — huvudpekaren "Latest known code work" namnger nu 2026-07-15 apiFetch-passet och pekar på den nya handovern. Ingen kodändring; inga tester påverkade. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01TdpJYP2cVDuVpUPx2k4fi3)_
  • 58ab9a8 Docs: add next-thread handover after PR #66 (apiFetch pass) Claude (AI) · 2026-07-15 · +41 −4

#66 Fas 06 fixpass: apiFetch auth-wrapper, CSP-städning, småbuggar, refaktorer

2026-07-15 · Claude (AI) · 7 commits · 66 filer · +1 998 −495 · Buggfixar B-008 B-029 B-046 B-060 B-026

Detaljer
Dagens fixpass (2026-07-15). Fem arbetspaket enligt `other/FAS06_FIXPASS_PLAN.md` ("Läge 2026-07-14 kväll") + grill-me-skillen. Alla klara, verifierade och pushade. ## Innehåll - [x] **grill-me + grilling-skills** installerade från mattpocock/skills (`.claude/skills/`). - [x] **Delad `FarmAdminGuard`** — areas- och personnel-admin-layouterna kollapsade från ~50 rader var till ~16, gemensam vakt med `allowedRoles`-prop. - [x] **apiFetch auth-wrapper (B-026-klassen)** — ny `$lib/api/fetch.ts` som injicerar `authHeaders()` (X-User-Id); 34 sidor + 28 komponenter migrerade från nakna `fetch('/api/…')`; regressionsvakt + pending-debt-snapshot. Bonusfynd: tre sidor läste fel `localStorage`-nyckel så deras X-User-Id alltid var tomt. - [x] **CSP-städning** — WA kit-CDN-domäner bort ur `SecurityHeadersMiddleware` (script-src/style-src → `'self' 'unsafe-inline'`) nu när Web Awesome är självhostat; testet inverterat. - [x] **Delad LLM-JSON-extraktor** — RegelradarDrafts strängmedvetna brace-matchning flyttad till `LlmJson.extractJsonObject`, återanvänd av ProductTranslator. - [x] **Småbuggar** — B-008 (hälsokontroll för "Other" osparbar → tomstate + notes-only-spara + migration 0244 seed), B-029-rest (dirty-koll gatar Spara), B-046 (livstidsmarginalens filtertext/tomstate i klarspråk), B-060 (djurslag på svenska via `animalTypeLabel` + `animalType.*` i18n). ## Verifiering Frontend: `npm run check` (0 nya fel), `npm test` (2867 gröna inkl. wrapper- + guard-tester), `i18n:check`. Backend: targeted `sbt testOnly` — SecurityHeaders 14, LlmJson 10, RegelradarDraft 10, ProductTranslator 2 = 36 gröna. Visuell Playwright-verifiering (mock-harness): apiFetch-sidorna (production/scanning, reports, djurdetalj) renderar utan 500 och alla autentiserade anrop bär X-User-Id (9/9); småbuggarna B-008/B-029/B-046/B-060 skärmbildsbekräftade. B-060:s djurlist-yta är kodgranskad (dev-server dynamic-import-flakighet på just den rutten; samma hjälpare bevisad live på två andra ytor). Bokföring uppdaterad: KNOWN_BUGS.md (4 buggar → Åtgärdade), FAS06_FIXPASS_PLAN.md (nytt lägesavsnitt), wiki frontend.md + log.md (`wiki:lint` OK). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • c980c56 Code-review cleanup: drop redundant Content-Type, use muted token Claude (AI) · 2026-07-15 · +1 −5
  • a7a73d1 Verify apiFetch surfaces + fix harness /api glob; update bookkeeping Claude (AI) · 2026-07-15 · +247 −91
  • fa6bf4f Fix B-008/B-029/B-046/B-060: health form, dirty-check, margin copy, species i18n Claude (AI) · 2026-07-15 · +747 −45
  • c00a073 Tighten CSP to self-hosted WA and share the LLM-JSON extractor Claude (AI) · 2026-07-15 · +136 −38
  • 36e2b74 Add apiFetch auth wrapper and migrate bare /api fetches (B-026 class) Claude (AI) · 2026-07-15 · +756 −239
  • b5b3dd0 Extract shared FarmAdminGuard from areas + personnel layouts Claude (AI) · 2026-07-15 · +84 −77
  • cb26b90 Add grill-me + grilling skills from mattpocock/skills Claude (AI) · 2026-07-15 · +27 −0

#65 Docs: fixpass status "Läge 2026-07-14 kväll" — handover for the next thread

2026-07-15 · Claude (AI) · 1 commits · 2 filer · +49 −2 · Funktioner

Detaljer
## Vad Handover-dokumentation inför nästa tråd (inga kodändringar): - **`other/FAS06_FIXPASS_PLAN.md`:** nytt lägesavsnitt **"Läge 2026-07-14 kväll"** — navklustret (#62), tysta-400-batchen + GDPR-exporten + Regelradar-auto-utkastet + code-review-fixarna (#63) och `KNOWN_BUGS`-bokföringen (#64) är mergade. Kvarvarande kandidatspår listade med pekare: CSP-städningen, `apiFetch`-wrappern (~18 sidor med latent 401 à la B-026), småkändisarna (B-008/015/029/046/060) och uppföljningarna (delad `FarmAdminGuard`, gemensam LLM-JSON-extraktor, mjölksidans KPI-rad, IMAP, HPCIA). Nya lärdomar noterade: `.dialog-scroll`-mönstret ("ingen osynlig scroll") och att Playwright headless döljer scrollbars. Föregående lägesavsnitt kvar som historik. - **`other/ENLANTIS_MASTER_TODO.md`:** huvudets pekar-rad uppdaterad så en ny session landar direkt på rätt lägesavsnitt. En ny tråd som följer planens "Start här" hittar nu dagens läge och nästa spår direkt. ## Efter merge Ingenting — bara `git pull`. Nästa tråd kan startas mot planen. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0139RSUz4cr5p6T1eyuu4DSu --- _Generated by [Claude Code](https://claude.ai/code/session_0139RSUz4cr5p6T1eyuu4DSu)_
  • ffb5f47 Docs: update fixpass status to evening 2026-07-14 (PRs 62-64 merged) Claude (AI) · 2026-07-15 · +49 −2

#64 KNOWN_BUGS housekeeping: move 15 fixed bugs to Åtgärdade (PR #62 + #63)

2026-07-15 · Claude (AI) · 1 commits · 1 filer · +271 −255 · Funktioner

Detaljer
## Vad Den utlovade städ-commiten efter att navklustret (#62) och parallellspåren (#63) mergats: flyttar de 15 fixade buggarna **B-001/020/026/027/030/043/047/050/051/053/058/059/061/062/065** från Öppna till Åtgärdade i `other/KNOWN_BUGS.md`, enligt filens egen konvention (hela diagnoshistoriken bevarad ordagrant, en `**ÅTGÄRDAD (2026-07-14, PR #6X):**`-rad per post med fixsammanfattning). B-024:s post annoteras att dess kvarvarande 401-del (spårad som B-026) nu är fixad — kontrastdelen låg redan under Åtgärdade. Ren dokumentändring — inga kodfiler. Sanity: 66 `### B-`-rubriker före och efter (34 öppna + 32 åtgärdade); netto +16 rader = de 15 ÅTGÄRDAD-raderna + B-024-noten. ## Efter merge Ingenting — bara `git pull`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0139RSUz4cr5p6T1eyuu4DSu --- _Generated by [Claude Code](https://claude.ai/code/session_0139RSUz4cr5p6T1eyuu4DSu)_
  • d9258ce KNOWN_BUGS housekeeping: move 15 fixed bugs to the resolved section Claude (AI) · 2026-07-14 · +271 −255

#63 Parallel tracks: bug batch (7 fixes), GDPR Art 20 export completion, Regelradar auto-draft, compliance tests

2026-07-14 · Claude (AI) · 17 commits · 16 filer · +1 872 −220 · Buggfixar B-001 B-050 B-053 B-058 B-059 B-027 B-026

Detaljer
## Vad Parallellspåren vid sidan av designpasset (PR #60) och navklustret (PR #62). Fyra spår i ordnade commits, noll överlapp med PR #60:s ytor (inga i18n-nycklar, ingen `global.css`, ingen `KNOWN_BUGS.md`). ### Spår 1 — buggbatch ("tysta 400 + logik", fixpassets PR C/D) - **B-026:** avelstavlan + Avel & reproduktion skickar auth-huvuden (`authHeaders()`) — sidorna gav HTTP 401 mot riktig inloggning. - **B-027:** Other-honor exkluderas inte längre tyst från avelstavlan; `classify()` degraderar utan datumberäkningar → Öppna-kolumnen. - **B-059:** org-/rådgivarvyernas länkar pekar på gårdens hem i stället för admin-attrappen. - **B-058:** personalvakten släpper in Managers (registret säger owner/Manager) och visar synlig nekad-ruta i stället för tyst `goto('/app')` — samma mönster som B-013-fixen. - **B-053:** "Fråga journalen"-tillförlitligheten mäter träffstyrka (1 träff = 75 %, 2 = 90 %) i stället för frågelängd — kanoniska exemplet visade 22 %, nu 90 %. - **B-050:** upgrade-nudge-routen loggar interna fel och svarar 500 i stället för att maskera allt som 400. - **B-001:** välfärdsnudgens djuplänk pekar på existerande `welfare/health-check?animal=<id>` (fliken den pekade på fanns aldrig), sidan förifyller + öppnar dialogen, och regeln accepterar `generic_health_check` så nudgen faktiskt släcks. ### Spår 2 — GDPR Art 20-exporten kompletterad (L-07-followup) Serverexporten omfattar nu även `shop_product`, `shop_order` (**anonymiserad** — endast order-id/status/belopp/valuta/tidsstämplar, ingen köpar-PII), `animal_group`(+medlemskap), `ir_submission`, `prescription`, `custom_field_definition/_value`, `document`-metadatamanifest (inga blobbar) och `farm_note` (ny default-impl `listByFarm`). `shop_review` förblir exkluderad (köparförfattad). Disclosure-texten + `DATA_EXPORT_SCHEMA.md` §7 uppdaterade; SchemaVersion kvar på "1". ### Spår 3 — Regelradar: auto-utkast från Compliance-agenten `POST /api/admin/regelradar/updates/:id/draft` (root-admin): kör Compliance-rollen via `AiAgentDispatcher`, parsar strikt JSON-utkast och sparar i nya `reg_update_event.note` (**migrering 0243** — planens antagna kolumn fanns inte; nu finns den, separat från diff-`summary` så `promote()`-kontexten aldrig förorenas). 503 på stub-dispatcher, 502 på oparsbar LLM-utdata. Människan granskar/promotar som förut. ### Spår 4 — efterlevnadstester Nya rena moduler + specar: `AmuBenchmark` (NL SDa-zoner: under median / signalzon / åtgärdszon + övre-kvartil-flagga, 10 fall inkl. exakta gränsvärden) och `OrganicCertificateRules` (utgång ≤30 d + art-täckningsmismatch, 9 fall). `MASTER_TEST_TODO` avbockad. ## Verifiering - **Backend: full `sbt test` — 3 144 gröna, 0 fel** (inkl. nya GdprExport-, RegelradarDraft-, NlAmuBenchmark-, OrganicCertificateRules- och utökade UpgradeNudge/CopilotNudge-specar). - **Frontend: vitest 2 860 gröna** (3 nya B-053-fall), svelte-check 0 fel, `i18n:check` grönt (noll nya nycklar = PR #60-säkerhetsbevis). - **Playwright 12/12** (`bugfix07-track1.mjs`, skärmbilder committade + skickade till Jonas): `X-User-Id` asserterat på båda events-anropen, Nasselisa (Other) i Öppna-kolumnen utan 401-banner, tillförlitlighet 90 %, hälsokontrolldialogen öppnas förifylld från djuplänken, Manager insläppt + ViewOnly får synlig nekad-ruta, inga attrapp-länkar på rådgivarsidan. ## Efter merge `KNOWN_BUGS.md`-statusflytten (B-001/026/027/050/053/058/059) görs i ett städ-commit efter att PR #60 mergats (filen rörs där). Regelradar-utkastets 200-väg kräver riktig LLM-konfig (`ANTHROPIC_API_KEY`/Vertex) — dev-stubben ger avsiktligt 503. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0139RSUz4cr5p6T1eyuu4DSu --- _Generated by [Claude Code](https://claude.ai/code/session_0139RSUz4cr5p6T1eyuu4DSu)_
  • c9aa7de Refresh dialog-scrollbar verification screenshot for the .dialog-scroll utility Claude (AI) · 2026-07-14 · +0 −0
  • 6dcb3d1 Apply code-review findings: auth 401 mapping, shared taxonomies, dedup Claude (AI) · 2026-07-14 · +141 −116
  • 27a7787 Fix token-drift guard: correct neutral-100 fallback to #f1f1f0 Claude (AI) · 2026-07-14 · +4 −4
  • 8e258f9 Health-check dialog: always-visible painted scrollbar (no invisible scroll) Claude (AI) · 2026-07-14 · +58 −2
  • 0391bb6 Fix health-check dialog clipping and kanban grey column background Claude (AI) · 2026-07-14 · +135 −3
  • 8715719 Update wiki: GDPR export contents, Regelradar auto-draft, session log Claude (AI) · 2026-07-14 · +19 −4
  • f17f173 Add compliance test coverage: NL AMU benchmark zones + organic cert rules Claude (AI) · 2026-07-14 · +280 −5
  • e990d3c Regelradar: compliance-agent auto-draft endpoint for detected changes Claude (AI) · 2026-07-14 · +321 −5
  • e7aac2c Complete GDPR Art 20 export: 10 previously omitted tables (L-07 followup) Claude (AI) · 2026-07-14 · +572 −26
  • 3a08376 Add Playwright verification for the bug batch (12/12 checks green) Claude (AI) · 2026-07-14 · +181 −0
  • 5f655b3 Fix B-001: welfare-nudge deeplink points at a page that exists and clears Claude (AI) · 2026-07-14 · +54 −3
  • a617413 Fix B-050: upgrade-nudge route stops masking internal errors as 400 Claude (AI) · 2026-07-14 · +23 −2
  • 4a63896 Fix B-053: ask-the-records confidence measures match strength, not length Claude (AI) · 2026-07-14 · +29 −1
  • 2809e48 Fix B-058: personnel guard admits Managers and shows a visible denied box Claude (AI) · 2026-07-14 · +36 −44
  • a615c8c Fix B-059: org and advisor views stop linking into the admin dashboard mockup Claude (AI) · 2026-07-14 · +4 −2
  • 8e2e672 Fix B-027: breeding board no longer silently excludes "Other" females Claude (AI) · 2026-07-14 · +7 −1
  • 44d20ad Fix B-026: breeding board and breeding page send auth headers Claude (AI) · 2026-07-14 · +8 −2

#62 Nav cluster: URL as single source of truth for selected farm (B-020/030/043/047/051/061/062/065)

2026-07-14 · Claude (AI) · 2 commits · 15 filer · +345 −107 · Dokumentation

Detaljer
## Vad Löser hela navigeringsklustret i ett svep, eftersom buggarna delade en rot: det fanns **två** osynkade "vald gård"-stores (en `writable` i `farmStore.ts` som topplisten skrev, och localStorage-backade `selectedFarmId` som resten av appen läser) plus en store→URL-effekt som tyst kunde skriva om adressraden. Topplisten kunde därför visa en annan gård än sidan — en efterlevnadsrisk när händelser registreras. **Ny modell: URL:en är enda sanningskälla.** - Dubblett-storen är borttagen; `selectedFarmId`/`selectedFarm` är enda vald-gård-storen (idempotent `set`, auto-välj-första flyttad hit). - Root-layouten synkar storen **från** ruttens gårds-id efter varje navigering; den omvända effekten är borttagen (**B-051**: historik-bakåt återställer nu både sida och val; **B-062**: breadcrumb följer alltid sidan). - Topplistens gårdsväljare **navigerar** (samma sökväg, nytt gårds-id) i stället för att sätta en store (**B-020**); sim-/demogårdar badgas SIM/DEMO i väljaren och på gårdskorten (**B-020 del 2**; `FarmDTO.isSimulated` exponeras, och liständpunkten slutade tappa `isDemo`/`wei*`). - Lägesväljaren navigerar till lägets startsida även vid klick på aktivt läge (**B-047**); loggan ger synlig respons vid samma-URL-klick (**B-061**); användarmenyns poster navigerar via `goto` — `wa-dropdown-item href` är inert i vår WA-version (**B-065**). - Gårdskortens "View Animals" navigerar till djurlistan (**B-030**). - DPA-kollen körs inte längre utan vald gård och frågar inte längre platshållar-UUID:t (garanterad 400 på varje sidladdning, **B-043**) — även städat på `legal/dpa`-sidan. ## Verifiering - **Playwright-scenarier på exakt felresorna: 15/15 gröna** (`other/fas06-01-genomgang/verktyg/nav-cluster-verify.mjs`): gårdsbyte navigerar + breadcrumb följer sidan; bakåt från 404 behåller gården; bakåt gård-2→gård-1 väljer om gård-1; aktivt-läge-klick → lägets hem; logga-klick scrollar till toppen; användarmenyns poster navigerar; View Animals → djurlistan; **0 anrop mot `farms/00000000-…`**; MegaMenyns 38 gårdslänkar pekar på URL-gården efter byte. - Skärmbilder committade och lästa (`shots/nav-01..12`), fyra nyckelbilder skickade till Jonas. - vitest 2 857 gröna, svelte-check 0 fel, `a11y:check` 50/50, `sbt compile` grönt, `wiki:lint` OK. - Inga nya i18n-nycklar; ingen fil delas med PR #60 (verifierat mot dess hunk-lista). ## Efter merge `KNOWN_BUGS.md`-statusflytten görs separat efter att PR #60 mergats (filen rörs där). Titta särskilt på: gårdsbyte via topplisten på en undersida, historik-bakåt efter 404, användarmenyn. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0139RSUz4cr5p6T1eyuu4DSu --- _Generated by [Claude Code](https://claude.ai/code/session_0139RSUz4cr5p6T1eyuu4DSu)_
  • fa325e7 Add nav-cluster verification harness, screenshots and wiki notes Claude (AI) · 2026-07-14 · +186 −0
  • 051dceb Fix nav cluster: make the URL the single source of truth for selected farm Claude (AI) · 2026-07-14 · +159 −107

#61 Docs: uppdatera fixpass-planens lägesavsnitt till 2026-07-14 med nästa spår

2026-07-14 · Claude (AI) · 1 commits · 2 filer · +43 −4 · Funktioner

Detaljer
## Vad Ren dokumentationsuppdatering (inga kodändringar), föranledd av Jonas fråga: *"om jag startat en ny tråd och bett den fortsätta — hade den hittat de tre spåren?"* Svaret var **delvis** — det här stänger gapet. - `other/FAS06_FIXPASS_PLAN.md`: lägesavsnittet var tre dagar gammalt (2026-07-11) och kände inte till WA-självhostningen (#55–#57), designpasset (#60), de stängda buggarna eller B-065. Nytt avsnitt **"Läge 2026-07-14"** dokumenterar mergat läge, `.accent-tile`-namnlärdomen och de tre kandidatspåren för nästa leverans: 1. **Navigeringsklustret** (B-020/047/051/061/062/065 — rekommenderat, B-062 är efterlevnadsrisk) 2. **PR C — tysta 400** (B-026 m.fl. + B-058/B-059 som bifångst) 3. **CSP-städning** (kit-domänerna ur `SecurityHeadersMiddleware.scala`) med pekare till var respektive spår är specificerat. Gamla lägesavsnittet kvar som historik. - `other/ENLANTIS_MASTER_TODO.md`: Fas 06-pekarraden i huvudet uppdaterad (07-11/14, självhostning + designpass + hänvisning till planens lägesavsnitt). En ny session som följer planens "Start här" hittar nu alla tre spåren direkt. ## Efter merge Ingenting — bara `git pull`. Nästa tråd kan startas mot planen. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JSMSpBWTAciyYAEjh7nuwa --- _Generated by [Claude Code](https://claude.ai/code/session_01JSMSpBWTAciyYAEjh7nuwa)_
  • c46401a Update fixpass plan status to 2026-07-14 with next delivery tracks Claude (AI) · 2026-07-14 · +43 −4

#60 Designpass: KPI-kort (A1/A2), mjölksidans sidhuvud (B1), sjukdomskorten (C1) + B-064

2026-07-14 · Claude (AI) · 3 commits · 32 filer · +756 −480 · Buggfixar B-064 B-063

Detaljer
## Vad Implementerar de designförslag Jonas godkände (`other/design-proposals/kpi-kort-och-sidhuvuden.html`): 1. **A1 — delad KPI-komponentklass** i `global.css` (etikett/värde/underrad + statuslister ok/warn/bad/info i semantiska tokens). Service-gap, SCC-larm, brunstdetektering och avel migrerade; död scopad CSS borttagen. **Efter code-review heter klassen `.accent-tile`** — se granskningscommiten nedan. 2. **A2 — ikonchip-KPI:er** på förflyttningsnavet (vendrade ikoner; &#34;Inlämnade&#34;-chipen medvetet neutral tills datat är kopplat — grön chip på okänd data vore vilseledande). 3. **B1 — mjölksidans sidhuvud:** EN vinröd primärknapp, &#34;Importera&#34;-meny (`wa-dropdown`, taggen tillagd i preload-blocket) som samlar de fyra importkällorna, analysvyerna som navigationspiller (inkl. villkorat Avelsindex). 4. **C1 — provtagningskorten** (BVD/IBR/MAP/TB): rubrik + undertitel, statuschip med ikon + text (4,55–4,63:1), luftiga nyckelrader, åtgärdslänk, strukturellt lika höjd (grid, uppmätt 419,5 px × 4). Sparkline/drilldown bevarade. 5. **B-064 åtgärdad:** `header-top`/`header-actions` får `flex-wrap` globalt. Verifierat vid 786 px (två rena rader) och 1280 px. 6. **B-063 stängd:** verkliga roten var tema-klasserna (PR #57); DPA-flödet live-bevisat ände-till-ände mot ren master. **Visuell verifiering enligt CLAUDE.md-regeln, av både implementerare och granskare oberoende:** skärmdumpar committade (`verktyg/shots/design-*.png`), lästa, jämförda mot förslags-mockarna; tokenvärden uppmätta (salvia/ockra-lister, papper-ytor). vitest 2 857 gröna, svelte-check 0 fel, a11y 50/50, i18n grönt. ## Code-review-fixar (commit `18ace94`) `/code-review` (hög nivå, åtta granskningsvinklar + verifiering) hittade och åtgärdade: - **Klasskollisionen `.stat-tile` (allvarligast):** den nya globala klassen återanvände namnet som ~24 sidor redan har som scopad CSS (grisar, bin, vikter, kalvar, vet, admin) — de globala `::part(body)`-reglerna läckte in i dem (samma mekanism som `.stat-card`-postmortem B-024). Klassen bytte namn till `.accent-tile`; gris- och bisidorna renderar åter sina ursprungliga kompakta kort (bevis: `fix-pigs-health.png`, `fix-bees.png`). - **B-064 fixad på rätt djup:** roten `.header-actions wa-button { flex: 1 }` (desktop) i `webawesome.css` är borttagen i stället för att lappas per sida; de två sidlokala motmedlen borttagna. Utan klippta etiketter på animals 375/786/1280, personnel 800, areas 900, vet/dashboard 800 (`fix-*.png`). - **Mjölkkorten:** Registrera-djuplänken (`?recordHerdTest=<kod>`) återställd på ALLA kort (även gröna) så rutinprov alltid registreras mot rätt sjukdomskod; historiken ligger kvar på statuschipen. De döda `class:danger/warn`-bindningarna kopplade till en 4 px accentlist i A1-språket. - **Städ:** oanvänd `--ok`-chipregel (movements), `.stats-row`-minmax enhetligt 180 px, oanvänd i18n-nyckel borttagen, mjölksidans dubblerade `.header-actions`-regel reducerad. - **Refuterat förslag:** &#34;använd `href` på `wa-dropdown-item`&#34; — WA 3.6.0 saknar href-stöd på dropdown-poster; mjölksidans `onclick`+`goto` är rätt mönster (navigering live-bevisad). Granskningen avslöjade i stället **B-065**: topplistens användarmeny (Profil/Inställningar/Prenumeration) använder just `href` och navigerar därför INTE — loggad i KNOWN_BUGS, hör till navigeringsklustret. ## Uppföljningar (nästa svep, ej blockerande) - Mjölksidans egen KPI-rad (SJUKDOMAR/FÖRSENADE/…) ovanför de nya korten är ännu oredesignad — samma A1-recept. - `production/pigs/kpis` och `production/goats` spot-checkas vid tillfälle. - C1-kortet delas med getternas `.disease-panel` som lib-komponent när nästa art behöver det (noterat, ej i denna PR). - B-065 (topplistens meny) fixas i navigerings-PR:n (B-020/047/051/061/062). ## Efter merge `git pull` + omstart av dev-servern + hård omladdning. Titta på: mjölksidan (sidhuvud + kort + Registrera-länk på gröna kort), servicegap/SCC/brunst/avel (KPI-lister), förflyttningsnavet, Djur-sidan vid smal fö
  • 1498b57 Fix flaky anonymisedKpis test: pin submittedAt timestamp Claude (AI) · 2026-07-14 · +4 −0
  • 18ace94 Apply code-review fixes to the design pass Claude (AI) · 2026-07-14 · +147 −124
  • fbca4ec Design pass: shared stat-tile (A1), icon-chip KPIs (A2), milk header (B1), disease status cards (C1), B-064 fix; close B-063 Claude (AI) · 2026-07-14 · +605 −356

#57 Fix: tema-klasserna på &lt;html&gt; + KPI-kortens klasskrock (B-024/B-052) + ikonstädning

2026-07-14 · Claude (AI) · 7 commits · 36 filer · +927 −240 · Buggfixar B-024 B-052

Detaljer
## Vad Tre saker, alla **visuellt verifierade av agenten i webbläsare** (skärmdumpar committade under `verktyg/shots/vfy-*`, nya obligatoriska regeln i `CLAUDE.md`): 1. **Tema-klasserna på `<html>` (kritisk):** `mellow.css`/`natural.css` gömmer alla design-tokens bakom `.wa-theme-mellow`/`.wa-palette-natural` — klasser som aldrig funnits i `app.html` (kitet injicerade färdiga värden och kringgick kravet). Enradsfix som återställer all komponentstyling: inramade fält, formaterade dialoger, stylade knappar. Verifierat på Lägg till artikel/läkemedel/djur, legal-qa och DPA-dialogen (`vfy-01..06`). 2. **B-024/B-052 lösta — KPI-kortens verkliga rotorsak:** en CSS-klasskrock. Globala `.stat-card` i `global.css` (vinröd platta + vit text, instrumentpanelens design) kolliderade med **29 sidors** egna ljusa KPI-kort med samma klassnamn → mörkgrå etiketter på vinröd botten. Fix: sidornas kort omdöpta till `.stat-tile`; globala plattan orörd. Verifierat: servicegap/avel/förflyttningsnavet nu ljusa och läsbara, instrumentpanelens plattor oförändrade (`vfy-07-*`). KNOWN_BUGS-posterna flyttade till Åtgärdade med ärlig diagnoshistorik (två felhypoteser innan den riktiga). 3. **Ikonstädning:** brands-bluetooth vendrade, väntfilen tömd — täckningsvakten bevakar nu varje ikonnamn utan undantag. Dessutom: ny hård regel i `CLAUDE.md` ("Visual verification is MANDATORY") — visuellt påverkande ändringar är inte klara förrän agenten renderat dem i riktig webbläsare, tittat på skärmdumparna och mätt stilarna. Beställd av Jonas efter att ostylade formulär slank igenom. Tester: vitest 2 857 gröna, a11y:check 50/50 kontrastpar, svelte-check 0 fel, i18n-check grönt. ## Efter merge `git pull` + omstart av dev-servern + hård omladdning. Kvarvarande designfråga (blockerar inget): märkesfärgen är avsiktligt **vinröd** sedan ommärkningen 4 juli — säg till om du vill ha tillbaka grönt, det är en liten separat ändring (+ `theme-color`-metan som fortfarande är grön). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JSMSpBWTAciyYAEjh7nuwa
  • fad32bc Design proposals: KPI tiles, milk page header, disease status cards Claude (AI) · 2026-07-14 · +248 −0
  • 3c0a70e WCAG contrast verification screenshots for the stat-tile fix (milk/cattle KPI pages) Claude (AI) · 2026-07-14 · +0 −0
  • 56b4451 Fix B-024/B-052: rename page-scoped KPI cards to stat-tile — global .stat-card collision Claude (AI) · 2026-07-13 · +273 −236
  • 28ab83c CLAUDE.md: mandatory visual verification rule + PR57 visual verification evidence Claude (AI) · 2026-07-13 · +40 −0
  • c6c1a02 Add browser-verification screenshot for the theme-class fix Claude (AI) · 2026-07-13 · +0 −0
  • e8421df Fix app-wide unstyled Web Awesome components: add theme/palette classes to <html> Claude (AI) · 2026-07-13 · +365 −1
  • 9d3f2c6 Empty pending-vendor exemptions now that brands bluetooth icons are vendored on master Claude (AI) · 2026-07-12 · +1 −3

#59 Webhooks: retry transient outbound-delivery failures

2026-07-13 · Claude (AI) · 4 commits · 7 filer · +566 −102 · Tester

Detaljer
## Why The live outbound-webhook dispatcher (`LiveImpl`, behind `ENLANTIS_WEBHOOKS_LIVE`) made a **single** POST attempt — a 5xx or network error was recorded `failed` immediately. Yet the row schema already carried the retry fields (`nextRetryAt`, `attempts`, `Status.Retrying`) and the repository already had `listPendingRetries`. This PR wires up the retry loop those were scaffolded for — the one genuine gap flagged during the earlier docs-reconciliation pass (#58). **Off by default:** everything here is inert under the default `NoOpLive` binding; behaviour only changes when `ENLANTIS_WEBHOOKS_LIVE=true`. ## What changed - **Pure classifier `WebhookDispatcher.decideOutcome`** — the retry/backoff/exhaustion decision, extracted so it's exhaustively unit-testable with no I/O: - 2xx → `delivered` - 404/410 → `failed` + auto-disable subscription - other 4xx → `failed` (call-site bug; keep subscription) - 5xx / IOException → `retrying` with exponential-backoff `nextRetryAt` (`min(60, 2^attempt)` min — same curve as `irRetryWorker`) while attempts remain, else `failed`. - **`WebhookDispatcher.retryPending`** drains due `retrying` rows (`listPendingRetries`), reloads the subscription + event, re-POSTs with the **same** delivery-id (+ a new `X-Enlantis-Delivery-Attempt` header), and advances the row in place. A vanished/inactive subscription or missing event terminally fails the row. - **`webhookRetryWorker`** in `Main.scala` — a 1-minute daemon tick mirroring `irRetryWorker`/`emailRetryWorker`. No-op under `NoOpLive`. - Attempt ceiling defaults to **5**, overridable via `ENLANTIS_WEBHOOKS_MAX_ATTEMPTS`. - Added **`WebhookDeliveryRepository.update`** (in-place row overwrite) mirroring `WebhookSubscriptionRepository.update`. ## Tests `WebhookRetrySpec` (new): - Exhaustive pure `decideOutcome` branch coverage + the `nextRetryDelay` backoff schedule. - `retryPending` terminal branches (missing / inactive subscription, missing event, not-yet-due) with in-memory fakes. - End-to-end re-POST against a localhost `HttpServer`: 200 → delivered (attempts incremented), 5xx-on-final-attempt → failed-exhausted, 5xx-with-attempts-left → retrying with advanced backoff. Updated `WebhookRoutesAuthSpec`'s fake for the new `update` method + a real `listPendingRetries`, and its env for the dispatcher's new `EventLogRepository` dependency. ## Wiki Updated `architecture/integrations-and-api.md` (Dispatcher section) + `log.md`. `wiki:lint` passes. ## Verification - ⚠️ **`sbt` is not available in this environment**, so this was **not** compiled/tested locally — CI (`sbt test` = compile + ZIO tests) is the verification oracle for this PR. The code mirrors existing repo idioms (Quill `updateValue`, the IR retry worker, the auth-spec fakes) and the retry decision is isolated in a pure function to keep the blast radius small. I'll watch CI and push fixes for any compile/test failures. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_019SDDGbbiwd8NPmLuwcBjSN)_
  • 088ba5e Review fixes: retry 408/429, correct stale webhook_delivery docstring Claude (AI) · 2026-07-13 · +13 −11
  • ff5e73e Test: implement retryPending in the 7 hand-rolled NoOpWebhookDispatcher fakes Claude (AI) · 2026-07-13 · +7 −0
  • 6280252 Fix: webhookRetryWorker has no .catchAll (retryPending returns UIO, cannot fail) Claude (AI) · 2026-07-13 · +3 −1
  • 65790b5 Webhooks: retry transient delivery failures instead of failing on first attempt Claude (AI) · 2026-07-13 · +543 −90

#58 docs: reconcile roadmap/TODO with shipped code (JWT, webhooks, Regelradar)

2026-07-13 · Claude (AI) · 1 commits · 10 filer · +76 −61 · Funktioner

Detaljer
## Why The roadmap/TODO docs had **drifted from the code**. Several items marked "to do" are in fact already shipped, and the outbound-webhook code comments still described a NoOp-only v1 that no longer matches the shipped `LiveImpl`. That makes future sessions (human and AI) either rebuild what exists or miss what actually remains. `CLAUDE.md` is explicit: *"Code is the source of truth; this file is the behavioural manifest."* This is a **documentation-accuracy pass — no behaviour change.** Every candidate was verified against the code before editing; genuinely-open items (country I&R integrations, future-phase features, GTM milestones) were left untouched. ## What changed **`other/ENLANTIS_MASTER_TODO.md`** - ✅ Flip **"Verify Kinde JWT on backend"** → done. `RouteAuth.resolveUser` honours `ENLANTIS_AUTH_MODE`: `jwt` mode (prod default) verifies the Kinde JWT `sub` via `JwtVerifier`; `header` mode (dev only) aborts boot under `ENV=prod` without `ENLANTIS_ALLOW_HEADER_AUTH=true`. - ✅ Flip **"TRACES NT pre-fill + ITAHC certificate capture"** → done (shipped in `af0fdc6`: `itahc_certificate` event + `tracesPreFill` util). Noted that the remaining *direct* DG-SANTE API push is a **business** blocker, not a code gap. - Added a header note: the manifest can lag code; latest un-narrated work is **Fas 06** (Web Awesome self-host + `KNOWN_BUGS` ledger, 2026-07-11/12). **`other/REGELRADAR_PLAN.md`** - Header said *"Phases 2–4 not started"* but §12–14 document them as **built and off-by-default**. Corrected to match, and listed the genuinely remaining work (live IMAP transport, compliance auto-draft, SFS amendment extraction, review-queue UI, live end-to-end verification). **Webhook code comments** (`WebhookDispatcher`, `WebhookDeliveryRepository`, `WebhookSubscriptionRoutes`, `Main`, `Models`) - The dispatcher already has two bindings: `NoOpLive` (default) and `LiveImpl` (behind `ENLANTIS_WEBHOOKS_LIVE`, real HMAC-signed POST + 404/410 auto-disable). Replaced the stale *"v1 NoOp / future LiveImpl"* wording. The **only** genuine gap left — the transient-5xx retry loop — is now described accurately. The `/webhooks/:id/test` response note is mode-agnostic and correct under either binding. **Wiki** - Corrected `architecture/auth-and-roles.md` and `architecture/integrations-and-api.md` (both previously described only the legacy `X-User-Id`-trust posture) to the mode-driven auth; added a `log.md` entry. `npm run wiki:lint` passes (77 pages, no broken links). ## Verification - Documentation/comment/string-only — no runtime surface to drive. - `npm run wiki:lint` → OK. - Re-grep confirms no remaining contradictory "v1 NoOp" / "not started" claims. - ⚠️ **`sbt` is not available in this environment**, so the Scala edits were kept strictly to comment blocks and one string literal (no new symbols, types, or control flow) — compile-safe by construction rather than by build. A reviewer with `sbt` can confirm with `sbt compile`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_019SDDGbbiwd8NPmLuwcBjSN)_
  • 35e2bf5 docs: reconcile roadmap/TODO with shipped code (JWT, webhooks, Regelradar) Claude (AI) · 2026-07-13 · +76 −61

2026-07-12 — 2 direkt-commits

Detaljer
  • 2912352 "Vendor brands icons" Jonas · 2026-07-12 · +202 −34
  • f6058e3 Added local WA icons instead of CDN for better offline performance. Jonas · 2026-07-12 · +662 −35

#56 Ikontäckning: byt felnamn till vendrade FA-namn, inventera Pro-namnen

2026-07-12 · Claude (AI) · 3 commits · 50 filer · +332 −206 · Funktioner

Detaljer
## Vad Uppföljning på ikonvendringen (f6058e3): täckningsvakten aktiverades av manifestet (313 ikoner) och hittade **44 icke-vendrade statiska ikonnamn**. Alla är nu åtgärdade: 1. **19 felnamn** (Bootstrap/Lucide-namn — samma buggklass som B-002:s tummar; har alltid renderat tomt): bytta till manifest-verifierade Font Awesome-namn (`alert-triangle`→`triangle-exclamation`, `settings`→`gear`, `x-mark`→`xmark`, `scale`→`scale-balanced`…). Kodgranskningens fyndfix ingår: offline-indikatorerna fick `triangle-exclamation` i stället för en (ljugande) ansluten-wifi-glyf. 2. **25 Font Awesome Pro-namn** (inget Pro-abonnemang finns — beslut: Free-ersättare): `bluetooth`/`bluetooth-b` (16 användningar) flyttade till FA:s **gratis brands-familj** (`family="brands"`, med familjestöd tillagt i hämtskript + täckningsvakt); övriga 23 kontextlästa ersättare som **alla redan finns i det vendrade manifestet** — `pig`→`piggy-bank` (befintlig konvention), `duck`/`turkey`→`feather`, `ear`→`tag` (öronmärkning), `farm`→`tractor`, `wheat`→`wheat-awn`, `sparkles`→`wand-magic-sparkles`, `shield-xmark`→`lock`, `wave-pulse`→`signal` m.fl. Vakten är aktiv utan undantag utom två: `brands/bluetooth` + `brands/bluetooth-b` ligger i `frontend/scripts/wa-icon-pending-vendor.json` tills nästa ikonhämtning (nätspärr i skrivmiljön). `other/KNOWN_BUGS.md` B-002 uppdaterad. Vitest 2 857 gröna, svelte-check 0 fel. ## Efter merge (Jonas) ```powershell git checkout master; git pull cd frontend; node scripts/fetch-wa-icons.mjs cd ..; git add frontend/static/wa/icons frontend/scripts/wa-icon-pending-vendor.json git commit -m "Vendor brands icons"; git push ``` (Töm väntfilen till `{}` om skriptet inte redan gjort det.) Sedan: omstart av dev-servern, hård omladdning, och svep över djursidorna/IoT-vågarna/Idag — inga tomma ikoner ska finnas kvar. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JSMSpBWTAciyYAEjh7nuwa
  • cb76260 Replace FA Pro-only icon names with Free equivalents; brands-family support Claude (AI) · 2026-07-12 · +245 −168
  • 2b4ba80 Icon review fix: offline indicators must not show a connected-wifi glyph Claude (AI) · 2026-07-12 · +2 −2
  • e919545 Icon coverage: map wrong-library icon names to vendored FA names, isolate Pro-only names Claude (AI) · 2026-07-12 · +85 −36

#55 Fas 06: självhosta Web Awesome (kit-CDN borttaget) + B-061–B-064, lägesuppdatering

2026-07-12 · Claude (AI) · 8 commits · 442 filer · +82 065 −5 143 · Buggfixar B-063 B-002 B-024 B-052 B-064 B-061 B-062

Detaljer
## Vad Startade som en lägesuppdatering inför trådbytet men växte till nästa fixleverans när Jonas live-testning av PR B avslöjade att **kit-CDN:ets molnkonfiguration driftat** (röda färger i stället för gröna, kitet laddar numera både default- och mellow-temat) — huvudmisstänkt för **B-063** (döda Spara/Avbryt trots PR B-fixarna), **återöppnade B-002** (tomma tumikoner) och **B-024/B-052** (mörkröda kort). ### Självhostningen (design: `other/FAS06_WA_SELFHOST_PLAN.md`, inkl. §9 avvikelser) 1. **Web Awesome laddas nu från en committad, versionspinnad kopia** av `@awesome.me/webawesome@3.6.0` under `/wa/dist-cdn/` (vendrad av `frontend/scripts/vendor-wa-dist.mjs`, `npm run wa:vendor`; paketet pinnat som devDependency + i `bun.lock`). Kit-skriptet, dev-proxyn i `vite.config.ts` och hooks-omskrivningen är borttagna. Appens tre egna tema-`<link>`-taggar är enda stilkällan (utredningen visade att kitets injicerade CSS var ren dubblering). 2. **Ikonerna** (finns aldrig i npm-paketet — alltid runtime-hämtning): `frontend/scripts/fetch-wa-icons.mjs` inventerar 340 statiska + 155 variabel-spårade namn och vendrar till `static/wa/icons/`; `wa-icon-resolver.js` slår upp lokalt först med dokumenterad fallback till Font Awesome Free-CDN:et (dedupade konsolvarningar). **Manifestet committas TOMT härifrån (nätspärr i skrivmiljön)** — se "Efter merge" nedan. 3. **Service workern** precachar dist-cdn + ikoner; `manifest.json` går via StaleWhileRevalidate så ikonvendringen slår igenom direkt. 4. **Testkedjan är nu äkta:** harnessets mockWebAwesome är borttagen — sandlådan testar exakt de tillgångar webbläsaren får (blinda fläcken bakom B-063). Repro + kallstartskontroll gröna mot den vendrade kedjan; nätverkslogg utan ett enda kit-anrop. Nya CI-vakter: ikontäckning (`waIconCoverage.test.mjs`, aktiveras när manifestet fyllts). 5. **Committade static-trädet ombyggt** så `sbt run` mot 8090 serverar den självhostade appen (inte kit-versionen). ### Dokumentation/bugglogg - `other/FAS06_FIXPASS_PLAN.md`: daterat lägesavsnitt (PR A+B mergade, rester, nästa steg). - `other/KNOWN_BUGS.md`: **B-061** (loggans samma-URL-no-op), **B-062** (topplisten visar fel gård — efterlevnadsrisk, B-020/B-051-familjen), **B-063** (döda knappar → trolig kit-drift), **B-064** (Djur-sidans ostylade knapprad kollapsar), **B-002 återöppnad** med ärlig mekanismnotering. ## Efter merge (Jonas) 1. Kör med riktig internetåtkomst: `cd frontend && node scripts/fetch-wa-icons.mjs` → committa `frontend/static/wa/icons/`-diffen (då aktiveras även CI-ikonvakten). 2. `git pull`, **starta om dev-servern**, hård omladdning. 3. Live-verifiera enligt `FAS06_WA_SELFHOST_PLAN.md` avsnitt 6: nätverksfliken ska sakna kit/ka-p-anrop; färgerna gröna igen; B-063-knapparna levande; B-002-tummarna synliga (efter steg 1). CSP-åtstramningen (ta bort kit-domänerna ur allowlisten i `SecurityHeadersMiddleware.scala`) är medvetet uppskjuten till separat PR efter live-verifieringen. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JSMSpBWTAciyYAEjh7nuwa
  • c414176 Self-host WA: final-review fixes — lockfile pin, rebuilt committed static, cache and warning polish Claude (AI) · 2026-07-11 · +43 597 −4 914
  • cdb3d2a Self-host WA: precache /wa/icons/ in the service worker (review finding) Claude (AI) · 2026-07-11 · +26 −0
  • c262407 Self-host Web Awesome: vendored 3.6.0 dist + local icon resolver, kit CDN removed Claude (AI) · 2026-07-11 · +37 731 −198
  • afbecf9 Design: self-host Web Awesome assets (fixes kit CDN drift class: B-063, B-002, B-024/B-052) Claude (AI) · 2026-07-11 · +549 −0
  • c2dcc3e KNOWN_BUGS: log B-064 (animals header row collapses), reopen B-002 (thumbs still empty live) Claude (AI) · 2026-07-11 · +39 −20
  • 9951437 KNOWN_BUGS: log B-063 — buttons still dead in Jonas's live env post-PR B; suspected kit CDN drift Claude (AI) · 2026-07-11 · +32 −0
  • bc209ab KNOWN_BUGS: log B-061 (logo same-URL no-op) and B-062 (top bar shows wrong farm) Claude (AI) · 2026-07-11 · +38 −0
  • 74e87b2 Fixpass plan: status update after PR A + PR B merge (handover to new thread) Claude (AI) · 2026-07-11 · +53 −11

#54 Fas 06 PR B — Web Awesome-mönstret: diagnos, repro och rotfix

2026-07-11 · Claude (AI) · 8 commits · 110 filer · +4 100 −1 347 · Buggfixar B-002 B-030 B-003 B-004

Detaljer
## Vad Fixpassets PR B — mönster 1 (Web Awesome-lagret). **Klar för granskning.** Åtgärdar 9 buggar: B-002, B-003, B-004, B-011, B-015, B-018, B-029 (footer-delen), B-032, B-055. ## Rotorsaker och fixar 1. **`toggleAttribute`-oscillatorn (B-055, B-003/004, B-011, B-015):** Web Awesomes `WebAwesomeFormAssociatedElement.willUpdate` speglar disabled med `toggleAttribute("disabled", this.disabled)`; appens `disabled={x || undefined}`-idiom gör force-argumentet `undefined` = ren växling → attributet flippar varje rendering. **Fix:** nytt pass i `waBindRewriter` (`svelte.config.js`) som boolean-tvingar disabled-uttryck på wa-taggar (679 ställen) **+** klientsveparen `waPreUpgradeAttrSweeper.ts` som vid komponentregistrering normaliserar bort pre-upgrade-artefakten `disabled="false"`/`checked="false"` (Sveltes attributväg före registrering — utan sveparen gav tvingningen en ny kallstartsregression, fångad och A/B-bevisad av slutgranskningen). 2. **Footer-slotten (B-018, B-029, B-032):** `WaDialog` slot-testar footern först efter första renderingen; dialoger som skapas med `open` redan sant visar den aldrig. **Fix:** `with-footer`-attribut på samtliga 145 dialoger med footer-slot (101 filer, codemod, oberoende omverifierad). 3. **B-002:** Bootstrap-ikonnamnen `hand-thumbs-up/down` → `thumbs-up/down`. ## Verifiering och vakter - Playwright-reprot (`other/fas06-01-genomgang/verktyg/prb-wa-repro.mjs`, härdat + falsifierbart): B-055-mönstret `0101010101` → `0000000000`; DPA-kryssrutan klickbar vid mount; footer-knappar synliga. Kallstartsscenariot committat som eget verktyg (`prb-wa-coldstart-check.mjs`, fördröjda WA-moduler) — grönt. - Per-yta-verifiering av B-011/B-015/B-029/B-032 med mockat API; vitest **2 855 tester gröna**; `vite build` rent; svelte-check 0 fel; wiki-lint OK. - **Nya regressionsvakter i CI:** `waDisabledRewriter.test.mjs` (omskrivarpasset + SWEPT_ATTRS-kontraktet), `waDialogFooter.test.mjs` (varje wa-dialog med footer-slot måste ha `with-footer`), `waPreloadCoverage.test.mjs` (varje använd wa-tagg måste finnas i app.html:s preload-block — listan bär last för tre mekanismer). - Arbetssätt: diagnos → adversariell granskning → omdiagnos (två avgörande experiment) → rotfix → granskning → ytverifiering → helbranchgranskning på mest kapabla modellen (fällde kallstartsregressionen, åtgärdad) → fullständigt /code-review-pass på hela PR-diffen (8 vinklar, 10 fynd, alla fixade; omskrivarrefaktorn verifierad byte-identisk över alla 829 .svelte-filer). ## Dokumentation - `other/FAS06_PRB_DIAGNOS.md` — fullständig diagnos (fyra trådar, belagt vs hypotes, kallstartssektionen). - `other/KNOWN_BUGS.md` — 9 poster flyttade till Åtgärdade; B-029 delad (dirty-kollen kvarstår), B-030 omdiagnostiserad (saknat `goto`, B-020-familjen), B-024/B-052 uppdaterade, `wa-drawer`-bevakningsnotis, B-046-mekanismnotis. - `docs/wiki/architecture/frontend.md` + `log.md` uppdaterade. ## Kvarstår utanför denna PR - **Tråd 4 (röda korten, B-024/B-052):** kräver verifiering i riktig webbläsare — 3-stegsreceptet finns i diagnosrapporten (DevTools på servicegap-sidan: färgschema + `--wa-color-neutral-10`). - B-024:s 401-del = B-026 (PR C); B-030/B-020 (gårdsnavigeringen) = PR D+. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JSMSpBWTAciyYAEjh7nuwa
  • bd10227 PR B code review pass: regression guards + rewriter/sweeper cleanup Claude (AI) · 2026-07-11 · +640 −100
  • 18eba6b PR B: fix cold-start regression — pre-upgrade attribute sweeper + doc corrections Claude (AI) · 2026-07-11 · +205 −19
  • cff208e PR B surface verification: move 9 fixed bug entries, fix B-002 icons, re-diagnose B-030 Claude (AI) · 2026-07-11 · +227 −154
  • fba0929 PR B root fix: disabled boolean coercion in waBindRewriter + with-footer on all footer-slot dialogs Claude (AI) · 2026-07-11 · +420 −147
  • a8b3521 PR B diagnosis: Thread 1 re-diagnosed — B-003/B-004 are the Thread 3 oscillator Claude (AI) · 2026-07-11 · +419 −268
  • de10c6c PR B repro: harden per code review — falsifiable scenario 2 overturns Thread 1 evidence Claude (AI) · 2026-07-11 · +567 −218
  • 5a1c1b6 PR B diagnosis: revise after adversarial review Claude (AI) · 2026-07-11 · +400 −441
  • b5ed9dc PR B diagnosis: Web Awesome pattern root causes + runnable Playwright repro Claude (AI) · 2026-07-11 · +1 222 −0

#53 Fas 06 fixpass PR A: de tysta behörighetsvakterna (B-013, B-014-del, B-042, B-054, B-056, B-057)

2026-07-11 · Jonas, Claude (AI) · 10 commits · 15 filer · +1 147 −612 · Buggfixar B-060 B-058 B-059 B-013 B-042 B-054 B-056 B-014 B-057

Detaljer
## Vad detta är Första leveransen i fas 06:s fixpass: **mönster 3 — de tysta behörighetsvakterna**. Tre olika hemsnickrade grindar kastade ut användare utan förklaring; nu följer alla ett och samma mönster: *laddar (spinner) → släppt in → synlig "Åtkomst nekad"-ruta*. Ingen tyst omdirigering någonstans. Överlämningsdokumentet `other/FAS06_FIXPASS_PLAN.md` (commit 134d787c från förra sessionen) följer med i denna PR som planerat. ## Fixade buggar | Bugg | Fix | Commit | |---|---|---| | **B-042** | `farmRoleStore` normaliserar backendens rollnamn (Administration→Manager, Farmer→Farmhand) — förutsättningen för all rollgrindning | ff74b2dd | | **B-013** | Stallhanteringens vakt (`admin/areas`) bytt från root-admin-e-postlista till rollkoll (owner/Manager) på **gården i adressen** — avblockerar hela stall-flödet | ff74b2dd + b461e4d0 | | **B-054** | Dashboard-attrappens vakt ärlig (serverstyrd status, nekad-ruta i stället för tyst `goto`); menyposterna admin-grindade via nytt `NavNode.adminOnly` | 951723aa | | **B-057** *(ny, hittad under passet)* | Bottenlistens standardflik "dashboard" pekade in i admin-attrappen för alla — flikkatalogen admin-grindad + `visibleSlots()`-filter | fdf3d774 | | **B-056** | Ny gemensam `admin/+layout.svelte` med statuskollen och den designade nekad-sidan; 15 per-sida-improvisationer borttagna (inkl. ai-ops 403-flippen) | 29036f18 | | **B-014** *(delfix)* | "Skapa stall"-genväg i Lägg till djur-dialogens inget-stall-ruta | 9509a483 | Två nya buggar hittades vid slutgranskningen och är **loggade som öppna** (B-058: personalvakten kastar ut Managers tyst — samma klass som B-013; B-059: org-/rådgivarvyerna länkar in i admin-attrappen). KNOWN_BUGS och wikin (auth-and-roles) uppdaterade i samma PR. ## Kvalitetsgrindar - vitest: 152 filer / 2836 tester gröna (16 nya: rollnormalisering, adminOnly-grindning, bottenlistfiltret) - `svelte-check`: 0 fel · `i18n:check`: grönt (2 nya nycklar) · `wiki:lint`: grönt - Varje delcommit + hela branchen är granskad av separata granskningsagenter; slutgranskningens enda viktiga fynd (vakten nycklade på lokalt vald gård i stället för adressens gård) är åtgärdat i b461e4d0 ## Att testa live (Jonas) 1. **B-013/B-014:** gå till en gård utan stall → Djur → Lägg till djur → klicka "Skapa stall" i rutan → du ska landa i stallhanteringen (inte gårdens översikt) och kunna skapa stall/område. 2. **B-054:** klistra in `…/farms/<id>/dashboard/copilot` som vanlig användare → tydlig "Åtkomst nekad"-ruta, ingen studs till Översikten; fliken "dashboard" ska inte synas i mobilens bottenlist. 3. **B-056:** `/app/admin` och `/app/admin/ai-ops` utan root-admin → samma designade nekad-sida på båda. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 8881f13 KNOWN_BUGS: B-060 — djurslagen på engelska i Lägg till stall (Jonas live-fynd) Jonas · 2026-07-11 · +21 −0
  • b411ec8 PR A kodgranskningsfixar: hydreringsvänta i stallvakten, retry på admin-statusfel, delad GuardGate, CTA-grindning Jonas · 2026-07-11 · +213 −68
  • b461e4d PR A slutgranskningsfixar: stallvakten nycklar på ruttens gård + i18n/kommentarsstäd + B-058/B-059 loggade Jonas · 2026-07-11 · +177 −72
  • af83bd0 KNOWN_BUGS: B-013/B-042/B-054/B-056 till Åtgärdade + wiki-notiser (PR A) Jonas · 2026-07-11 · +122 −76
  • 9509a48 B-014 (delfix): genväg till stallhanteringen i Lägg till djur-dialogen Jonas · 2026-07-11 · +37 −28
  • 29036f1 Fix B-056: gemensam admin-layout med ärlig statuskoll, per-sida-grindarna borttagna Jonas · 2026-07-11 · +118 −324
  • fdf3d77 Fix B-057: bottenlistens dashboard-flik dold for icke-admin Jonas · 2026-07-11 · +75 −5
  • 951723a Fix B-054: dashboard-vakten visar nekad + menyposten admin-grindad Jonas · 2026-07-11 · +61 −20
  • ff74b2d Fix B-042+B-013: rollnormalisering i farmRoleStore + stallvakten visar nekad i stället för tyst redirect Jonas · 2026-07-11 · +133 −16
  • 134d787 Fas 06: FIXPASS_PLAN — fullständig överlämning till fixpassets nya session Claude (AI) · 2026-07-11 · +190 −3

#52 Fas 06: appkartan — städrundan (manus 1–7 i hitta-dit-format)

2026-07-11 · Claude (AI) · 1 commits · 9 filer · +388 −154 · Funktioner

Detaljer
**Appkartans sista leverans.** Portion 1–10 mergades i #43–#51; detta är städrundan som lyfter de sju äldsta kapitlens demo-manus till samma standard som portion 8–10. ## Vad som ändrats Alla "Så visar du det"-avsnitt i portion 1–7 är omskrivna till **hitta-dit-formatet**: varje steg har nu - **Startpunkt** — var du står när steget börjar, - **Exakt klickväg** — med menyordens verkliga skärmtexter (verifierade mot i18n-tabellerna: Djur, Behandlingar, Vaccinationer, Försäljningar, Gårdens startsida, Visa alla funktioner, Avel kanban …), - **"Du ska se"** — kvittot på att man är rätt, - **Reservväg** — direktadress när klickvägen inte stämmer. Kända trasiga flöden pekar på sina B-nummer i buggloggen (B-013/014 stall, B-015 vikter, B-016 begravd redigering, B-018 läkemedelsdialogen, B-022 vaccinationsvägen, B-025/026 avelstavlan, B-034 årspaketet, B-035/037 slakt) — så den som övar manuset vet att det är appen, inte manuset, som felar. ## Övrigt - `00-INDEX.md`: hitta-dit-regeln gäller nu alla portioner. - `ARBETSLAGE.md`: **appkartan komplett** — kvar är endast det rekommenderade fixpasset (tre systemiska buggmönster, dokumenterade med repro-strategi). - Inga produktkodändringar — endast dokument under `other/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU --- _Generated by [Claude Code](https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU)_
  • 7dfc3c5 Fas 06: städrundan — manusen i portion 1–7 lyfta till hitta-dit-formatet Claude (AI) · 2026-07-11 · +388 −154

#51 Fas 06: appkartan — portion 10 (Bakom kulisserna) + rotorsak B-013

2026-07-11 · Claude (AI) · 4 commits · 4 filer · +353 −30 · Buggfixar B-056 B-013

Detaljer
**Sista portionen av appkartan** (portion 1–9 mergades i #43–#50). Kvar därefter: endast städrundan av manus 1–7. ## Portion 10 — Bakom kulisserna (`10-bakom-kulisserna.md`) - **Admin-världen:** de 27 root-admin-sidorna i fyra grupper (drift & support, tillväxt & affär, innehåll & regelverk, marknadsplatsens vakter) — inklusive Regelradar-granskningskön där regeländringar publiceras innan bönderna ser dem. - **Root-admin-modellen blottlagd:** två oberoende grindar — backendens e-postallowlist (`ENLANTIS_ROOT_ADMIN_USERS`) och en **hårdkodad** e-postlista i `frontend/src/lib/auth.ts`. Förklarar varför ägaren av allt ändå är utestängd från admin i sin lokala app; praktisk ruta i manuset. - **Simulatorn:** "a coverage harness wearing a game's skin" — tick-motorn skapar riktiga händelser genom det normala append-flödet, Coverage-Director styr slumpen mot ~144 katalogförda funktioner, sandlådan stänger av det utgående nätverksanropet men inte den inre maskinen. Plus varför simgårdar blandas omärkta i gårdslistan (frontend läser aldrig `is_simulated` — B-020). - **Komponentgalleriet** (dev-only, 404 i prod med avsikt) och **designmuseet** `/design` (fyra kompletta teman; Cartography vann). - **De olänkade ytornas system:** ~34 detaljsidor olänkade by design, ~20 byggda-men-orphans, tre världar helt utanför registret — och reachability-testet som tvingar varje `/app`-sida att ta ställning. ## Bugglogg — rotorsak till B-013 funnen Under kapitelarbetet föll en gammal gåta: **"Lägg till stall" som slängde användaren till översikten (B-013)**. Stallhanteringen `admin/areas/` bär samma kopierade root-admin-vakt som `dashboard/`-underträdet (B-054) — tyst `goto('/app')` för alla utom de hårdkodade e-postadresserna, trots att menyregistret säger roll ägare/Manager. B-014 (ingen väg att skapa första stallet) är en direkt följd. Uppgraderad från [otriagerad] till [ROTORSAK FUNNEN], korsreferens tillagd i B-054. ## Övrigt - `ARBETSLAGE.md` uppdaterad: endast städrundan kvar; nästa buggnummer B-056. - Alla relativa länkar upplösta; stickprov mot koden (SimTickEngine, sim-coverage-manifest, designrutterna, areas-vakten rad för rad). - Inga produktkodändringar — endast dokument under `other/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU --- _Generated by [Claude Code](https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU)_
  • 119f372 Fas 06: portion 10 — galleriets tre hyllor förklarade (app-klossar / marknads-kartografi / mock-skärmdumpar) Claude (AI) · 2026-07-11 · +23 −5
  • d6c018b Fas 06: portion 10 — sim/design/galleri uppgraderade till live-testade Claude (AI) · 2026-07-11 · +7 −7
  • 2dc9b47 KNOWN_BUGS: B-056 — admin-grindningen inkonsekvent per sida (index nekar, ai-ops släpper in); portion 10 uppdaterad Claude (AI) · 2026-07-11 · +31 −2
  • 46e5a03 Fas 06: appkartan portion 10 — Bakom kulisserna (sista kapitlet) + rotorsak B-013 Claude (AI) · 2026-07-10 · +292 −16

#50 Fas 06: appkartan — portion 9 (AI-skiktet)

2026-07-10 · Claude (AI) · 9 commits · 2 filer · +486 −30 · Buggfixar B-055 B-054 B-053 B-052 B-001 B-050 B-051

Detaljer
Nionde portionen av läromaterialet (portion 1–8 mergades i #43–#49). Näst sista kapitlet — kvar är portion 10 (Bakom kulisserna) och städrundan av manus 1–7. ## Portion 9 — AI-skiktet (`09-ai-skiktet.md`) De två AI-världarna utredda och isärhållna: - **Bondens copilot:** kärnan är en **deterministisk regelmotor** (karens, välfärd, avel, I&R-kön, vaccin, WEI, karens-mot-slaktdag-krock, Regelradar, grisspecifika P1-C-regler) — språkmodellen är bara ett frivilligt putslager med systemprompten "Do not add new facts" och tyst tillbakafall till regeltext utan API-nyckel. Dubbel transport (Anthropic direkt i utveckling, Gemini via Vertex AI i produktion) förklarad; v3-flerstegsagenten (byggd, flaggad av, enbart läsande verktyg); Fråga Copilot (deterministisk dirigering, ingen LLM); EU-förordnings-Q&A (LLM i strama tyglar: svar enbart ur medskickad lagtext med källcitat, stub utan konfiguration). - **Interna AI Ops:** kanban/roller/chatt för AI-kolleger på `/app/admin/ai-ops` (olänkad, root-admin) — skalet och kunskapslagret byggda, motorn en uttalad stub ("KB first"-sekvensen). - **Begreppskrocken:** kunskapsbas #1 (GCS-hinken `enlantis-kb` för AI-kollegerna) vs kunskapsbas #2 (hjälpcentret `/help/kb`, statiska artiklar, noll AI) — samma ord, ingen gemensam kod. **Manuset** (hitta-dit-format, ~4 min) inkluderar ett "ärlighetssteg": chattytan på `…/dashboard/copilot` är en ren attrapp utan serveranrop — bra att veta så ingen demo:ar fel sida. Den riktiga copiloten är nudge-tavlan. **Dokumentationsavvikelse noterad i kapitlet:** wiki/projektbeskrivning säger `gemini-2.5-pro`, koden mappar till `gemini-3.5-flash` — koden är auktoritativ. ## Index (`00-INDEX.md`) Portion 9 markerad **Skriven**; ordlistan utökad med **LLM** och **GCS**. ## Verifierat - Alla relativa länkar upplösta; stickprov mot koden (regelregistret i `CopilotService.scala`, `DefaultVertexModelMap`, menyetiketterna "AI-copilot"/"Fråga Copilot"/"Juridisk Q&A", att ingen AI-post ingår i något arbetsläges urval, stub-strängen i `AiAgentDispatcher.scala`). - Inga produktkodändringar — endast dokument under `other/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU --- _Generated by [Claude Code](https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU)_
  • 27e9ef7 KNOWN_BUGS: B-055 — legal-qa-knappen växlar varannan tangent (skarpaste beviset i döda-bindnings-mönstret); portion 9: EurLexQa-stubben live-bekräftad Claude (AI) · 2026-07-10 · +30 −2
  • fe02026 KNOWN_BUGS: B-054 (dashboard-underträdet tyst root-admin-utkastning) + portion 9 rättad: attrappen är admin-grindad Claude (AI) · 2026-07-10 · +38 −8
  • b709e5b Fas 06: portion 9 — manussteg 4/5 omskrivna: adressfältssteg förklarade utan 'klistra in reservadressen' Claude (AI) · 2026-07-10 · +16 −8
  • b3cb826 KNOWN_BUGS: B-053 — Fråga journalens tillförlitlighetsprocent mäter frågelängd, inte träffsäkerhet Claude (AI) · 2026-07-10 · +21 −0
  • 9271cbb KNOWN_BUGS: B-052 — servicegap-vyns KPI-kort oläsliga (WA-temadriftmönstret, sidkoden oskyldig) Claude (AI) · 2026-07-10 · +22 −0
  • 21c6d55 Fas 06: portion 9 — Fråga journalen (appens sidrubrik), live-test av mönsterreserven invävt Claude (AI) · 2026-07-10 · +20 −11
  • a1c4455 KNOWN_BUGS: B-001 komplettering — nudgen kvarstår efter klick (ren länk, inget sparas) Claude (AI) · 2026-07-10 · +9 −0
  • fc48359 KNOWN_BUGS: B-050 (upgrade-nudge 400, tysta-400-mönstret) + B-051 (back från 404 byter gård) Claude (AI) · 2026-07-10 · +42 −0
  • 584c39b Fas 06: appkartan portion 9 — AI-skiktet Claude (AI) · 2026-07-10 · +288 −1

#49 Fas 06: appkartan — portion 8 (Affär) + hitta-dit-regeln

2026-07-10 · Claude (AI) · 10 commits · 3 filer · +446 −7 · Buggfixar B-049 B-048 B-039 B-043 B-047 B-042

Detaljer
Åttonde portionen av läromaterialet (portion 1–7 mergades i #43–#48), skriven enligt Jonas nya nybörjarregel. ## 1. Portion 8 — Affär (`08-affar.md`) De tre penningflödena utredda: **gårdens ekonomi** (tolv finance-sidor, per-djur-resultat räknat ur loggen, "export först"-bokföring till Fortnox/DATEV/Exact/Sage), **gårdens försäljning** (butiken som medvetet eget bounded context utanför event-sourcingen, Stripe Connect med pengar direkt till gårdens konto, publika skyltytan `/farmers/<slug>`, spårbarhet djur→produkt→faktura, CSA med publik anmälan + autogiro, team/tid/lön, CRM-light), och **gårdens prenumeration** (fast pris per gård aldrig per djur, `PricingTiers.scala` som enda sanningskälla, 30-dagars trial, dunning-trappan). Kapitlets kärnrad: **`BillingGate` har en hårdkodad alltid-på-lista som gör all myndighetsefterlevnad permanent gratis oavsett betalstatus** — belagd i wiki och kod. **Manuset i nytt format:** varje steg har startpunkt, exakt klickväg med knapparnas skärmtext, "vad du ska se" och reservadress. ## 2. Hitta-dit-regeln (`00-INDEX.md`) Jonas regel 2026-07-10 inskriven: text och manus skrivs så att den som inte kan någonting hittar rätt (startpunkt/klickväg/du-ska-se/reservväg). Gäller portion 8+; portion 1–7:s manus uppgraderas i en städrunda. ## 3. `KNOWN_BUGS.md` — B-042 Rollnamns-mismatch upptäckt under kapitelarbetet: backend-rollerna heter Administration/Farmer medan frontendens grindning och projektdokumentationen säger Manager/Farmhand — rollstyrda vyer kan slå fel. Även noterat: dokumentationens prisbelopp är motstridiga (wiki vs featurekatalog) — koden är auktoritativ. ## Verifierat - Alla relativa länkar upplösta; stickprov mot koden (13 finance-poster, `AlwaysOnPrefixes` i BillingGate, PricingTiers som källa). - Inga produktkodändringar — endast dokument under `other/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU --- _Generated by [Claude Code](https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU)_
  • 25ef06b Fas 06: ARBETSLAGE.md — sessionsstatus inför kontextkomprimering Claude (AI) · 2026-07-10 · +69 −0
  • a5f57f9 KNOWN_BUGS: B-049 komplettering — kundportalen kan inte öppnas i dev, Pro+ utan pris Claude (AI) · 2026-07-10 · +6 −0
  • dc9a194 KNOWN_BUGS: B-049 förfinad — bekräftelsesektion under skärmkanten, alla knappar låsta av 400→NaN-kedjan Claude (AI) · 2026-07-10 · +9 −6
  • 74ea4c7 KNOWN_BUGS: B-049 uppdaterad — Byt till Pro-knappen död, uppgradering helt blockerad i dev Claude (AI) · 2026-07-10 · +6 −0
  • 10fa668 KNOWN_BUGS: B-049 — uppgraderingssidan trasig (layout, NaN-priser, engelska, 400) Claude (AI) · 2026-07-10 · +19 −0
  • 1ded1b3 Fas 06: portion 8 — testruta för att aktivera Pro+ (stub-läge + dev-genväg) Claude (AI) · 2026-07-10 · +10 −0
  • ac80d60 KNOWN_BUGS: B-048 — Kontor-lägets startsida är AgroVision-importen Claude (AI) · 2026-07-10 · +11 −0
  • 8f5d05d KNOWN_BUGS: B-039 stärkt — sålt djur Levande med 0 intäkt i per-djur-resultatet Claude (AI) · 2026-07-10 · +6 −0
  • b4aaed1 Fas 06: B-043–B-047 + manusformatet förklarat + gårds-id-instruktion Claude (AI) · 2026-07-10 · +66 −0
  • 836f497 Fas 06: appkartan — portion 8 (Affär) + hitta-dit-regeln + B-042 Claude (AI) · 2026-07-10 · +244 −1

#48 Fas 06: appkartan — portion 7 (I&R/efterlevnad) + förkortningar utskrivna i alla kapitel

2026-07-10 · Claude (AI) · 7 commits · 7 filer · +429 −56 · Buggfixar B-038 B-028 B-041

Detaljer
Sjunde portionen av läromaterialet (portion 1–6 mergades i #43–#47), plus en retroaktiv språkgenomgång enligt Jonas regel. ## 1. Portion 7 — Förflyttningar, I&R & efterlevnad (`07-forflyttningar-ir-efterlevnad.md`) - Inleds nu med **ordförklaring av I&R** (Identifiering & Registrering) efter Jonas fråga. - Förflyttningsnavet med åtta undersidor (inkl. TRACES-förifyllnaden); I&R-kön hela vägen (händelse → kö → landsväxel med skarp svensk CDB-klient + sju skal → 5-minutersdemon med backoff → 4h-eskalering → nudgar/push); "köar och gnäller men blockerar aldrig"-posturen; compliance-valvets 46 sidor grupperade med kärnpoängen **valvet genererar ur loggen, lagrar inte**; FCI + slaktkedjans hårda 409-stopp; Regelradars försiktighetsprincip; eko-verifieringen; källbelagd avsikt ("valvet ska tåla en inspektör utan efterarbete") + demo-manus. ## 2. Språkregel: förkortningar skrivs alltid ut (alla kapitel + index) Jonas regel — materialet är en guide för en ny utvecklare: - **Ny ordlista** i `00-INDEX.md` (20 poster: I&R, CDB, LWW, CRDT, WEI, AMU, FCI, CAP, EID, UELN, KPI, ADG, FCR, TRACES, CSA, CRM, NPS, DPA, WCAG, PWA) + regeln inskriven i mallbeskrivningen. - **Retroaktiv genomgång av portion 1–7**: varje förkortning definieras i klartext vid första användningen per kapitel. ## Verifierat - Alla relativa länkar upplösta; stickprov mot koden (46 compliance-undersidor, migrationer 0209/0211/0212, CdbClients TODO_CONFIG). - Inga produktkodändringar — endast dokument under `other/fas06-02-appkarta/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU
  • 6f5c41f KNOWN_BUGS: flytta B-038 till Åtgärdade Claude (AI) · 2026-07-10 · +11 −10
  • 4c03f96 Fix B-038: nationellt ID-exemplet motsade valideringen Claude (AI) · 2026-07-10 · +5 −5
  • c5eee26 Fas 06: KNOWN_BUGS B-028–B-041 + live-bekräftelser + manusrättningar Claude (AI) · 2026-07-10 · +127 −4
  • 733a634 Fas 06: portion 7 — exakta navigeringsvägar i hela demo-manuset Claude (AI) · 2026-07-10 · +30 −18
  • fe1fb84 Fas 06: skriv ut alla förkortningar + ordlista i indexet Claude (AI) · 2026-07-10 · +48 −18
  • e3345d5 Fas 06: portion 7 — ordförklaring för I&R vid första användning Claude (AI) · 2026-07-10 · +8 −0
  • 3e5ba40 Fas 06: appkartan — portion 7 (Förflyttningar, I&R & efterlevnad) Claude (AI) · 2026-07-09 · +200 −1

2026-07-10 — 1 direkt-commits

Detaljer
  • 7e004da Updates title on terminal window to Frontend/Backend with launch script Jonas · 2026-07-10 · +4 −2

#46 Fas 06: appkartan — portion 4 (Djuren) + bugglogg B-008–B-012

2026-07-09 · Claude (AI) · 4 commits · 2 filer · +364 −34 · Buggfixar B-015 B-017 B-013 B-014 B-008 B-012

Detaljer
Fjärde portionen av läromaterialet (portion 1–3 mergades i #43/#44/#45), plus fem nya poster i buggloggen från Jonas fortsatta live-testning. ## 1. Portion 4 — Djuren - **`other/fas06-02-appkarta/04-djuren.md`** — djurlistan (sök som träffar identifierare, filter, massåtgärder), Lägg till djur-dialogens villkorsstyrda fält, djurdetaljens elva flikar (inkl. den tomma Förflyttningar-fliken), identifierare som egna rader (åtta metodtyper), viktkedjan UI → `weight`-event → projektion → `animal_weight` — med 0239-historien om idempotensnyckeln som var tyst trasig tills simulatorn hittade den — platsflyttar via `movement`, LWW-regeln för skalära fält kontra append-only för vikter (CRDT-reglerna), artmetadatans spårningslägen (individ/grupp/hybrid), källbelagd avsikt och demo-manus. Förklarar också UI-texten "Fältändringar sparas som LWW-händelser" som Jonas mötte. - **`00-INDEX.md`** — portion 4 markerad som skriven. ## 2. `other/KNOWN_BUGS.md` — B-008–B-012 - **B-008** Hälsojournalens Spara omöjlig för djurtyp "Other": typkartan saknar Other → parametrarna hämtas aldrig (tyst) → minst-en-parameter-kravet stoppar spara; dessutom seedar ingen migration `health_check_parameter` — *verifierad i kod*. - **B-009** Knapplinjeringen på samma sida — *otriagerad*. - **B-010** Djurdetaljens fältkort: knapp utanför kortet, etikett över texten — *otriagerad*. - **B-011** Foderartikel-dialogens döda Spara utan feedback — *otriagerad*, med systemhypotes om wa-button-uppgradering + konsolkommandon för nästa testpass. - **B-012** `bun run dev` kraschar på express-import i webhooks-kodexempel — *verifierad i miljö*. ## Verifierat - Alla relativa länkar upplösta; stickprov mot koden (migration 0239 finns, LWW-kommentarerna, flikkatalogerna, `animalTypeMap`). - Inga produktkodändringar — endast dokument under `other/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU --- _Generated by [Claude Code](https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU)_
  • aa98ef2 Fas 06: B-015–B-017 + rätta portion 4 mot verkligt UI Claude (AI) · 2026-07-09 · +77 −33
  • 4dd5dba Fas 06: KNOWN_BUGS.md — B-013/B-014 (stallskapande: felnavigering + begravd väg) Claude (AI) · 2026-07-09 · +24 −0
  • b07c6f2 Fas 06: appkartan — portion 4 (Djuren) Claude (AI) · 2026-07-09 · +196 −1
  • 2ff348c Fas 06: KNOWN_BUGS.md — B-008–B-012 från fortsatt live-testning Claude (AI) · 2026-07-09 · +67 −0

2026-07-09 — 5 direkt-commits

Detaljer
  • f7c7bce Fas 06: B-026/B-027 — avelstavlans 401 och tysta Other-exkludering Claude (AI) · 2026-07-09 · +35 −3
  • 9852ee1 Fas 06: B-024/B-025 + rätta portion 6:s namnbruk och manus Claude (AI) · 2026-07-09 · +58 −7
  • 51ed2a1 Fas 06: appkartan — portion 6 (Avel & produktion) Claude (AI) · 2026-07-09 · +182 −1
  • 4c29c6b Fas 06: KNOWN_BUGS.md — B-018–B-023 + B-008-uppdatering Claude (AI) · 2026-07-09 · +73 −0
  • ecf60b5 Fas 06: appkartan — portion 5 (Hälsa & välfärd) Claude (AI) · 2026-07-09 · +192 −1

#45 Fas 06: appkartan — portion 3 (Idag & planering) + buggloggen KNOWN_BUGS.md

2026-07-09 · Claude (AI) · 2 commits · 2 filer · +281 −1 · Funktioner

Detaljer
Tredje portionen av läromaterialet (portion 1–2 mergades i #43/#44), plus den buggfil du bad om. ## 1. Portion 3 — Idag & planering - **`other/fas06-02-appkarta/03-idag-och-planering.md`** — de tre konkurrerande "hemmen" (tunna Översikten, den ~9000 px höga gårdsstartsidan, den avsedda Idag-sidan) och de fyra planeringsverktygen. Bakom ytan: nudge-motorns deterministiska regelpipeline (AI bara som språkpolering; deterministiska nudge-id:n; dedupe mot öppna uppgifter), widget-panelens serverlagring per gård+användare, och kapitlets nyckelpoäng — **kortväggens 45 kort är handkodade och står utanför registrets CI-skyddsnät**, vilket förklarar de döda länkarna. Jonas fältobservation 2026-07-09 invävd som belägg. - **`00-INDEX.md`** — portion 3 markerad som skriven. ## 2. `other/KNOWN_BUGS.md` — buggloggen startad (7 poster) Repot saknade buggfil; denna startar loggen. Fyra nya fynd från live-testningen 2026-07-09, felsökta till rotorsak: - **B-001** Välfärdsnudgens "Mark welfare check done" → 404: backend länkar till `animals/{id}/welfare` som aldrig byggts (`CopilotService.scala` ~rad 359) — *verifierad*. - **B-002** Tumknapparna grå rutor: ikonnamnen `hand-thumbs-up/-down` är Bootstrap-namn i ett Font Awesome-bibliotek (`today/+page.svelte` rad 370/380) — *verifierad*. - **B-003** DPA-kryssrutan går inte att kryssa i: `waBindRewriter` genererar `oninput` för `wa-checkbox`, men `WaCheckbox` (webawesome 3.6.0, verifierat i paketkoden) skickar bara `change` — bindningen fyrar aldrig. **Systemisk: ~80 `bind:checked`-ställen utan egen `onchange` potentiellt trasiga** — *verifierad*. - **B-004** "Bekräfta DPA på nytt" död: följdfel av B-003 (`!acknowledged` i disabled-villkoret). - **B-005/B-006** emb-pigs-kortet + farmId-racen (kända från genomgången, nu loggade). - **B-007** Övriga trasiga länkar/bilder från testningen — otriagerad samlingspost. Ingen produktkod ändrad — endast dokument. ## Verifierat - Alla relativa länkar upplösta; stickprov mot koden på master (rad 1374, rad 158/172, 45 nav-kort, nudge-endpointen, WA-paketets eventdispatch). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU
  • 90fa10b Fas 06: KNOWN_BUGS.md — buggloggen startad (7 poster från live-testning) Claude (AI) · 2026-07-09 · +95 −0
  • c9abf04 Fas 06: appkartan — portion 3 (Idag & planering) Claude (AI) · 2026-07-09 · +186 −1

#44 Fas 06: appkartan — portion 2 (Navigationen) + numrerade mappar

2026-07-09 · Claude (AI) · 2 commits · 45 filer · +219 −49 · Funktioner

Detaljer
Andra portionen av läromaterialet (resa 5-spåret; index + portion 1 mergades i #43), plus en mappnumrering du bad om. ## 1. Portion 2 — Navigationen - **`other/fas06-02-appkarta/02-navigationen.md`** — de fem navigationsytorna (megamenyn med sina två ansikten, topplisten, bottenraden, sökpaletten, webbkartan); hur allt drivs av ett enda register (`registry.ts`) med grindning på root-admin, roll och 20 features; arbetslägets "lins"-filosofi (lokalt sparad, tar aldrig bort — bara kurerar); bottenradens lägespreset kontra eget val; reachability-testet som gör "byggt men olänkat" till ett medvetet tillstånd; källbelagd designavsikt (progressive disclosure, one-thumb, artnära UX) och ett 3-minuters demo-manus. - **`00-INDEX.md`** — portion 2 markerad som skriven. ## 2. Numrerade mappar (steget syns i namnet) - `other/fas06-genomgang` → **`other/fas06-01-genomgang`** (steg 1) - `other/fas06-appkarta` → **`other/fas06-02-appkarta`** (steg 2) - Alla ~45 referenser uppdaterade: bildlänkar i `FAS06_GENOMGANG.md`, sökvägar i Playwright-verktyget (`verktyg/README.md`, `harness.mjs`), korslänkar i appkartan och handoverns folder-referenser. Arbetsnamnet `FAS06_APPKARTA.md` (rad 33 i handovern) lämnades orört — historisk proposal, aldrig skapad. ## Verifierat - Alla relativa länkar (bilder + korslänkar) upplösta mot filsystemet efter omdöpningen; inga gamla token kvar. - Renamesen registrerade som `git mv` (R/RM i status), inte delete+add. - Nyckelpåståenden i portion 2 stämda mot koden på master. - Inga produktkodändringar — endast dokument/bilder under `other/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU
  • 9ac0156 Fas 06: numrera mapparna efter steg (fas06-01-genomgang, fas06-02-appkarta) Claude (AI) · 2026-07-09 · +48 −48
  • c9c3dd8 Fas 06: appkartan — portion 2 (Navigationen) Claude (AI) · 2026-07-09 · +171 −1

#43 Fas 06: appkartan — index + portion 1 (Skelettet)

2026-07-09 · Claude (AI) · 1 commits · 2 filer · +361 −0 · Funktioner

Detaljer
Första leveransen i resa 5-spåret (`other/FAS06_RESA5_HANDOVER.md`): läromaterialet som ska ge överblicken *vad som byggts, var det bor och vad tanken var* — skrivet så att det fungerar som manus när man guidar någon annan genom appen. ## Innehåll - **`other/fas06-appkarta/00-INDEX.md`** — materialets syfte, den sexdelade mallen (vad finns / var det bor / bakom ytan / vad tanken var / status / "så visar du det"), och portionslistan (10 portioner, en i taget med diskussion emellan). - **`other/fas06-appkarta/01-skelettet.md`** — portion 1, kartan över kartan: appens sju världar (marknadssajt, Kinde-inloggning, onboarding, appen, portaler, admin, dolda ytor), repo-layouten, den tekniska ryggraden i fem idéer (händelselogg, projektioner, offline-först, roller, utbytbara integrationer), produkttanken enligt wikin, statusläge per värld och ett 3-minuters demo-manus. ## Form (avstämd med Jonas i sessionen) Mapp med kapitel · en portion per PR · tekniken invävd löpande · befintliga skärmdumpar återanvänds (sex bilder från `other/fas06-genomgang/`), nya turer bara där bild saknas. ## Verifierat - Alla relativa länkar (bilder + korslänkar) upplösta mot filsystemet. - Faktapåståenden stämda mot `frontend/src/lib/nav/registry.ts`, `frontend/src/routes/`, `docs/wiki/product/{feature-catalog,positioning-and-usp}.md`; [kod-läst]-märkning genomgående. - Inga produktkodändringar — endast nya dokument under `other/fas06-appkarta/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU --- _Generated by [Claude Code](https://claude.ai/code/session_01FdK4tYuqin21ifznN7kFBU)_
  • c8f02d0 Fas 06: appkartan — index + portion 1 (Skelettet) Claude (AI) · 2026-07-08 · +361 −0

#42 docs(fas06): handover för resa 5-fokus + Playwright-verktygen incheckade

2026-07-08 · Claude (AI) · 4 commits · 12 filer · +861 −11 · Infra/verktyg

Detaljer
## Context Efter genomgången i #41 har Jonas beslutat: **resa 5 först**. Nästa fokus är att han ska lära sig appen — vad som byggts, var det bor och vad tanken var — så väl att han kan förklara den för någon annan och guida den personen genom alla funktioner. Resa 1–4 och de fem diskussionsfrågorna parkeras tills dess. Detta är handovern till den nya tråden (repot är det enda som överlever mellan trådar). ## Ändring - **`other/FAS06_RESA5_HANDOVER.md`** — beslutet, nästa trådsuppgift (läromaterial/appkarta, portionsvis, formen stäms av med Jonas först), källhierarkin för "tanken med allt" (wiki → master-TODO → spec → nav-registret → done/ → genomgångens tabell), arbetsform, miljöfakta och startprompt att kopiera. - **`other/fas06-genomgang/verktyg/`** — Playwright-harnesset från steg 1 (mockat API på nätverksnivå, lokal Web Awesome-servering, `shotApp` för appens inre scroll, dev-inloggning) + resa-skripten + README med engångssetup och kända fällor, så nästa tråd slipper återuppfinna allt. Docs-only — ingen produktkod, inga tester berörs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Qt4UsCPpcShA5QgkaPfz7U --- _Generated by [Claude Code](https://claude.ai/code/session_01Qt4UsCPpcShA5QgkaPfz7U)_
  • 0538088 ci: single 'sbt test' invocation (sbt 2 client can't parse 'compile test') Claude (AI) · 2026-07-08 · +10 −7
  • 37efbbe ci: run sbt compile+test in one invocation Claude (AI) · 2026-07-08 · +7 −4
  • 7273887 chore: retrigger CI (sbt test dog direkt utan testutskrift — sbt-server-flake, docs-only diff) Claude (AI) · 2026-07-08 · +0 −0
  • d7da21b docs(fas06): handover för resa 5-fokus + Playwright-verktygen incheckade Claude (AI) · 2026-07-08 · +844 −0

#41 docs(fas06): genomgång av de fem användarresorna — diskussionsunderlag med skärmdumpar

2026-07-08 · Claude (AI) · 2 commits · 30 filer · +220 −0 · Funktioner

Detaljer
## Context Steg 1 enligt `other/FAS06_HANDOVER.md` (mergad i #40): gå igenom appen som var och en av de fem personerna och producera ett diskussionsunderlag i klarspråk — **inga byggbeslut, inga lösningsförslag**. ## Vad som gjorts Appen kördes live (Vite-dev + Playwright mot förinstallerad Chromium) med mockat API på nätverksnivå, eftersom backend inte går att köra i CCR-miljön. Web Awesome serverades lokalt (CDN spärrat av nätverkspolicyn). Allt som inte kunde live-testas är kod-spårat och märkt **[kod-läst]** i dokumentet. - **`other/FAS06_GENOMGANG.md`** — per resa: steg-för-steg med skärmdumpar, var det skaver, plus resa 5:s inventeringstabell (yta · var den bor · hur den nås i dag · skyddad eller ej). Avslutas med fem diskussionsfrågor. - **`other/fas06-genomgang/`** — 29 skärmdumpar från körningarna. ## Nyckelfynd (kortversion) - **Resa 1:** `/welcome`-guiden är redan stegvis (1 fält → 2–3 val → 4 fält) — men en äldre 6-stegsdialog på `/app` konkurrerar med den, och registreringen sker på extern Kinde-sida. - **Resa 2:** demo-gården **har** UI-ingång (onboarding-väljaren), men "demo" på marknadssajten betyder videor som alla är "Coming soon". - **Resa 3:** tre konkurrerande "hem"; gårdsstartsidans kortvägg är 26+ osorterade kort; "Lägg till djur" visar 4 fält i grundläget (max ~13 i värsta fallet — handoverns bild var värsta fallet). - **Resa 4:** `/sim` är färdig och på svenska men har noll ingångar och ingen inloggningsgrind på sidnivå. - **Resa 5:** fyra spridda admin-ingångar, ingen komplett; inventeringstabellen listar allt dolt som beslutsunderlag. - **Bifynd:** en trolig vilande race på gårdsstartsidan (farmId sätts efter första uppritningen) + ett nav-kort mot en sida som inte finns (`compliance/emb-pigs`). Endast dokumenterade — inte åtgärdade. Docs-only — ingen produktkod, inga tester berörs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Qt4UsCPpcShA5QgkaPfz7U --- _Generated by [Claude Code](https://claude.ai/code/session_01Qt4UsCPpcShA5QgkaPfz7U)_
  • 3ea9256 docs(fas06): bädda in skärmdumparna inline i genomgången Claude (AI) · 2026-07-08 · +56 −0
  • 58e3122 docs(fas06): genomgång av de fem användarresorna — diskussionsunderlag med skärmdumpar Claude (AI) · 2026-07-08 · +164 −0

2026-07-08 — 1 direkt-commits

Detaljer
  • 50d4635 docs(fas06): handover — user-story-driven redesign, beslutad riktning + steg 1 (#40) Jonas · 2026-07-08 · +54 −0

2026-07-07 — 6 direkt-commits

Detaljer
  • 1ab9fb4 refactor: move src/components → src/lib/components, drop $components alias (#39) Jonas · 2026-07-07 · +204 −203
  • 1718a5b fix(schedule): forward readonly + map calendar --lc-* onto house tokens (#35) Jonas · 2026-07-07 · +36 −3
  • 6ded671 chore(cleanup): drop 2 orphaned i18n keys; finish static dead-TW sweep (#38) Jonas · 2026-07-07 · +29 −37
  • 85c019e fix(fostering): send counterpart sow + sign piglets by direction (#37) Jonas · 2026-07-07 · +107 −8
  • f33ba89 docs(components): record Fas 02 consolidation decisions (keep separate) (#36) Jonas · 2026-07-07 · +42 −0
  • 2ab93f9 ci: make CI a real gate — drop chronic e2e job, fix vitest cmd + 7 backend specs (#34) Jonas · 2026-07-07 · +128 −75

#33 Add admin-only AI Ops link to top-right user menu

2026-07-07 · Claude (AI) · 1 commits · 28 filer · +94 −53 · Funktioner

Detaljer
## What Adds an **AI Ops** link to the top-right user dropdown menu (`TopBar.svelte`) that navigates directly to `/app/admin/ai-ops`. The link is only rendered for root-admin users. ## Why The AI Ops dashboard had no direct entry point from the app chrome — you had to type the URL. This surfaces it for admins so it's one click away, while keeping it hidden from regular users. ## How - **`TopBar.svelte`**: import `isRootAdmin` / `refreshRootAdminStatus` from `rootAdminStatusStore`; add an `$effect` that populates the root-admin flag when authenticated (the store caches for 5 min). Render the new dropdown item inside `{#if $isRootAdmin}`, set apart by a divider, using a `robot` icon. - Gated on `$isRootAdmin` to match the target page's own access gate (`/app/admin/ai-ops` fetches `/api/me/root-admin-status` and is root-admin only) — so the link never points somewhere the user can't reach. - **i18n**: added `nav.user.aiOps` ("AI Ops" across all 24 locales, since it's a product name) to the `nav` key shard + `_order.mjs`, regenerated the per-locale JSON via `npm run i18n:build`. ## Verification - `npm run i18n:check` ✅ all `$t()` keys covered - `svelte-check` ✅ 0 errors (pre-existing warnings in unrelated marketing pages only) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01GrbscoJ1P5cGiQEYombSKT --- _Generated by [Claude Code](https://claude.ai/code/session_01GrbscoJ1P5cGiQEYombSKT)_
  • 6971c73 Add admin-only AI Ops link to top-right user menu Claude (AI) · 2026-07-07 · +94 −53

#32 design(A2+A3): WA-bridge — warm definitions for the 62 undefined --wa-* tokens + gate coverage

2026-07-07 · Jonas · 4 commits · 333 filer · +1 734 −1 563 · Design/UI

Detaljer
## What **A2** — the app's "cold palette" root-cause fix. 62 of 114 referenced `--wa-color-*` tokens were undefined at runtime, so their cold Tailwind fallbacks (`#e5e7eb`, `#6b7280`, `#2563eb`, …) rendered live at ~1520 call sites. This defines all of them warm in `frontend/src/webawesome.css` `:root`. **A3** — removes the `--wa-` skip in `scripts/check-tokens.mjs`; the widened gate found 771 drifted (dead) fallbacks in 186 files, normalized mechanically to each token's resolved value via the gate's own `--json` output (pixel-neutral). ## Mapping rule Tailwind scale is **lightness-inverted** onto WA's warm decade ramps: `N00 → decade (100 − N·10)` (e.g. `-100`→`-90`, `-600`→`-40`). Each rung is `var(--wa-color-<fam>-<decade>, #warm)` — tracks the kit's ramp at runtime, warm literal as kit-load safety + static resolvability for the gate. Semantic roles map to house tokens. | Familj | 050/100 → | 300 → | 500 → | 600 → | 900 → | |---|---|---|---|---|---| | neutral (gray) | #f1f1f0 | #cac8c3 | #7a766a | **#5c594f** | #1f1d1a | | danger (red) | #f9f0ee / #f5e1dc | #daa193 | #a86252 | **#864839** | #36130b | | warning (yellow) | #f6f1ea / #eee5d7 | #c7ab7b | #8f703a | **#71541d** | #2a1b00 | | success (green) | #edf2ee / #dde8df | #98b79e | #53805d | **#366241** | #193922 | | info (natural blue) | #e0e5ef | #9fb0ca | #5c76a0 | #425981 | — | Semantic roles: `border/-default`→`--color-border`, `border-subtle`→`--color-border-light`, `border-strong`→`--color-border-strong`, `surface`→`--color-surface`, `surface-hover`→`--color-bg-hover`, `surface-subtle`→`--color-bg-subtle`, `bg-card`→`--color-bg-card`, `text-default/-primary`→ink, `text-secondary`→`--color-text-secondary`, `text-subtle`→`--color-text-muted`, `primary`→`--color-primary`, `primary-subtle`→brand-95, `info`→`--color-info`, `neutral-0`→`#fffdf7`. ## Contrast constraint (the 4 flagged targets) - `neutral-600` → decade-40 `#5c594f` = **6.2:1** — solves the flagged failure (the doc's interpolated `#8c8779` was too light; Tailwind-600's true lightness matches decade-40). - All `-600/-700` text rungs land on decade `-40/-30` → clear AA 4.5:1. - `-500` rungs + semantic bases sit at ~4.0:1 = icon/badge/large-text grade, **not** body text — documented in the CSS comment; body-text call sites should use `-600`. ## Verification - Runtime sweep (`getComputedStyle`, per `design-audit/wa-bridge/README.md`): **definedCount 52 → 114/114, emptyCount 62 → 0** ✅ - Visual review via Claude-in-Chrome (marketing home, `/app` shell, `/me`, `/release-notes`, swatch panel of every new ramp, outdoor-mode) — warm everywhere, no cold Tailwind blue/gray remnants. - Local gates (CI is not a gate): `svelte-check` 0 errors · vitest 2805 passed · `tokens:check` ✅ (now incl. `--wa-*`) · `i18n:check` ✅ · `a11y:check` 50 pairs 0 failing. ## Known residuals (out of scope, noted in design-audit) - Call sites using two-digit decade `-50` as a Tailwind light tint (e.g. `--wa-color-danger-50` expecting pastel, rendering base `#a86252`) — A1-analog per-site fixes. - 3-digit hex fallbacks (`#777`, `#ccc`) are dead but unflagged by the gate (6-digit contract). ⚠️ **Deliberate cold→warm visual change across ~1520 call sites — review the mapping table above before merging.** 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • ed4d5c3 fix(a11y): bump -500/base semantic tones used as text color to -600 (WCAG AA) Jonas · 2026-07-07 · +794 −794
  • 63abefb fix(tokens): widen + palette-anchor the WA drift guard; doc the neutral-ramp exception Jonas · 2026-07-07 · +69 −9
  • 42b9dbd feat(ci): tokens:check covers --wa-* (A3) + normalize 771 dead cold fallbacks Jonas · 2026-07-07 · +757 −760
  • d8faf90 fix(css): WA-bridge A2 — warm definitions for the 62 undefined --wa-* tokens Jonas · 2026-07-07 · +114 −0

#31 docs(design-audit): WA-bridge repro bundle + post-A2 roadmap

2026-07-07 · Jonas · 1 commits · 5 filer · +3 421 −0 · Design/UI

Detaljer
Persists the ephemeral A0 diagnostic artifacts so the phase-4 WA-bridge remediation (A2) is fully reproducible in a fresh session, plus the future roadmap. - `design-audit/wa-bridge/wa-static.json` — per-token fallback + static-def data (mapping raw material) - `design-audit/wa-bridge/wa-contrast.mjs` — standalone WCAG-AA calculator (now self-contained; reads co-located json) - `design-audit/wa-bridge/wa-contrast-results.json` — contrast findings output - `design-audit/wa-bridge/README.md` — how to regenerate the runtime sweep + data, and the A2 acceptance gate (re-run sweep → emptyCount 62→0) - `design-audit/ROADMAP.html` — self-contained post-A2 roadmap (6 phases, local-progress tracker) Docs/data only. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 1fc99ce docs(design-audit): WA-bridge repro bundle + post-A2 roadmap Jonas · 2026-07-07 · +3 421 −0

#30 docs(design-audit): WA-bridge runtime diagnostic + contrast findings

2026-07-07 · Jonas · 1 commits · 1 filer · +129 −0 · Design/UI

Detaljer
The Wave-0 (A0) deliverable behind the phase-4 WA-bridge work — durable so the next thread/session can pick up A2 without losing context. **Finding:** a runtime `getComputedStyle` sweep of the 114 `--wa-color-*` tokens the code references shows **62 are undefined at runtime** (the whole Tailwind-numeric `neutral/danger/warning/success -100..-900` scale). Their cold Tailwind fallbacks (`#6b7280`, `#e5e7eb`, `#2563eb`…) therefore **render live** — the root cause of the audit's long-standing "cold palette". Web Awesome only defines the decade scale (`-05..-95`). Contents: - Classification of all 114 tokens (defined / undefined / wrong-rung). - The lightness-inverted decade↔Tailwind mapping guidance for **A2** (Tailwind `-50`=lightest ≈ decade `-95`). - A **WCAG-AA contrast pass**: 16 foreground tokens already fail AA today with their cold fallbacks (a real a11y bug, not just aesthetics); 4 warm target values need care for body text. Motivates **A2** (define the missing rungs warmly in `webawesome.css` → ~1520 sites go warm at once) and **A3** (extend `tokens:check` to `--wa-*`). Docs-only. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • fe2bbde docs(design-audit): WA-bridge runtime diagnostic + contrast findings Jonas · 2026-07-07 · +129 −0

#29 refactor(css): restyle dead-Tailwind badges with tokens

2026-07-07 · Jonas · 1 commits · 6 filer · +180 −79 · Design/UI

Detaljer
## Summary Tailwind was removed from this project (2026-07-03, no config/dependency remains), but several severity/status badge helpers across the codebase still return Tailwind class strings like `bg-red-100 text-red-800`. Since Tailwind isn't loaded, these render as an unstyled `class` attribute with **zero visual effect** — status badges that look like plain unstyled text today. This is an **intentional visual change**: unstyled text → real colored badges/dots/banners. It restyles the four biggest, most reviewable clusters using house design tokens (not hardcoded hex), rather than just stripping the dead classes. ### New shared CSS (`frontend/src/global.css`) - **`.badge` / `.badge-{danger,warning,success,neutral,brand}`** — pale `--wa-color-*-fill-quiet` background + `--wa-color-*-on-quiet` text + `--wa-color-*-border-quiet` border. Same token pairing already used elsewhere (`ErrorMessage.svelte`, `OrganicSummaryCard.svelte`), each with a hardcoded fallback hex (renders correctly even before the Web Awesome kit script injects the real values). - Lives in `global.css`, **not** component-scoped `<style>` blocks: Svelte's per-component CSS pruning strips selectors it can't see literally in the template, and a class built at runtime inside a helper function's return string doesn't count — scoped rules would be silently dropped and the badges would stay unstyled again. - **`.svc-status-dot` / `.svc-status-cell` / `.svc-status-banner`** (solid `--color-success/-warning/-danger` fills — these are colour swatches with no label text on top, so pale badge fill would be wrong) and **`.tone-muted/-faint/-success/-warning/-danger`** (certified `--color-*-text` / `--color-text-secondary` / `--color-text-faint`) for the `/status` page. - Prefixed `svc-`/`tone-` rather than the more generic `status-`/`text-` because `status-dot`/`status-cell`/`text-*` class names are already used **scoped** inside other components' own `<style>` blocks (e.g. `help/performance-benchmarks`, `legal/security-roadmap`, `design/yard-light`) — adding an unscoped global rule with the same name would have leaked into those. ### Files done - **`routes/app/admin/aml/+page.svelte`** — `severityBadge`/`statusBadge` now return real `"badge badge-*"` strings. **`aml.test.ts` updated in the same commit** — it re-declares its own copy of the two helpers and asserted the old Tailwind literal strings 6 times; all assertions now check the new class strings. - **`routes/status/+page.svelte`** — overall-status banner, per-service status dot, 60-day uptime grid cells, incident severity badge, and all secondary/faint text (was `text-slate-*`/`text-green-*`/etc., all dead). Also deleted `serviceStatusClass`, a dead helper (defined, never called). - **`routes/app/admin/moderation/+page.svelte`** — rule severity/active badges and event action badge (inline ternaries). - **`routes/app/farms/[id]/settings/certifications/+page.svelte`** — organic-cert active badge, welfare-claim status badge, and the two save-result message text colors. ### Deferred (follow-up PR, not done here — kept this one reviewable) - `shop/*` pages - `KillSheetPage` Both still have dead Tailwind-looking classes; left out to keep this diff scoped to the 4 clusters named in the task. ## Verification - `bun test` — **150 test files / 2805 tests pass**, including the updated `aml.test.ts` (17 tests). - `bun run check` (svelte-check) — 0 errors repo-wide; **0 new warnings** on any of the 4 changed files (no unused-CSS-selector warnings, confirming the global.css approach avoids Svelte's scoped-CSS pruning trap). - `node scripts/check-contrast.mjs` — 50/50 contract pairs pass (unchanged; the new badge/tone tokens aren't in its fixed CONTRACTS list, since they reuse existing certified token pairs). - Live render check on `/status` (`bun run dev` + Chrome): confirmed the amber "Försämrad" banner, green/amber status text per service, and green/amber/gray 60-day grid cells all render with real color (not p
  • 5aed374 refactor(css): restyle dead-Tailwind badges with tokens Jonas · 2026-07-07 · +180 −79

#28 feat(nps): mount NpsPromptHost so NPS can actually be collected

2026-07-07 · Jonas · 1 commits · 1 filer · +21 −0 · Funktioner

Detaljer
NPS collection was **dead in production**: `NpsPromptHost` was authored but never mounted, so `shouldShowNpsPrompt`/`submitNps` could never fire — no user could submit an NPS score. Mounts it in `src/routes/+layout.svelte`, lazy-loaded, gated on `isAppRoute && $isAuthenticated && $selectedFarm` (same gate as `CopilotPanel`). The host itself defers 30s and applies the account-age (14d) / resubmit (90d) / reskip (30d) cadence + server-side cross-device dismissal check before opening the dialog. `accountCreatedAt` is left unset (the `Farm` type has no creation timestamp) — the host falls back to its own localStorage first-seen stamp. Verified: `svelte-check` 0 errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 78cd90b feat(nps): mount NpsPromptHost so NPS can actually be collected Jonas · 2026-07-07 · +21 −0

#25 fix(css): correct Class-A WA tint misuse (-50 base rendered as pale)

2026-07-07 · Jonas · 1 commits · 103 filer · +207 −207 · Design/UI

Detaljer
## What Web Awesome's `--wa-color-{primary,brand,danger,warning,success}-50` are the **solid BASE rung** of the WA *decade* scale (`-05`..`-95`, `-50` = middle/base), **not** a pale Tailwind-style `-50` tint. Verified runtime values (`frontend/static/wa/wa-overrides.css` + theme CSS): - `--wa-color-primary-50` → aliases to `--wa-color-brand-50` = **`#7a1f1a`** (solid burgundy) - `--wa-color-danger-50` = **`#a86252`**, `--wa-color-warning-50` = **`#8f703a`**, `--wa-color-success-50` = **`#53805d`** (all solid bases) ~205 call sites used `-50` as a **pale background/hover wash** (ghost-button hovers, "pale" status cards, table-row highlights, pale chips). Because `-50` *is* defined at runtime (to the solid base), each site's pastel-hex fallback (`#fef2f2`, `#fffbeb`, `#eff6ff`, …) never rendered — so ghost hovers painted solid burgundy, "pale mint" cards rendered mid-saturation green, etc. (Diagnostic: `design-audit/WA_BRIDGE_RUNTIME.md`, class (c).) ## Fix Replace each **pale-intent** `-50` **background** (and `--wa-color-surface-raised:` reassignment) with the genuinely-pale, warm `*-fill-quiet` semantic token WA actually defines for this purpose: | before | after | runtime tint | |---|---|---| | `--wa-color-primary-50` / `--wa-color-brand-50` | `--wa-color-brand-fill-quiet` | `#ecc5c0` | | `--wa-color-danger-50` | `--wa-color-danger-fill-quiet` | `#f5e1dc` | | `--wa-color-warning-50` | `--wa-color-warning-fill-quiet` | `#eee5d7` | | `--wa-color-success-50` | `--wa-color-success-fill-quiet` | (warm pale) | (primary maps to the **brand** family — `--wa-color-primary-fill-quiet` does not exist.) Existing pastel-hex fallbacks are preserved; bare tokens stay bare (no invented fallbacks). Only `background` / `--wa-color-surface-raised` lines were touched. **103 files, 207 usages.** Confirmed sites fixed: `university-partners/+page.svelte` (~397,404 ghost/secondary hover), `cdb-compliance/+page.svelte` (~179,186,297 pale status cards + badge chip), `components/FecrtRollupCard.svelte` (~369 `.row-resistant`). Changed areas: components (12 files), compliance pages incl. all country movement/tagging watchdog tables (29), production incl. bees/goats/pigs/milk boards (20), device CSV-import pages (7), admin (7), marketing + misc app routes (28). ## Not touched (intentional — `-50` used as solid/saturated color, not a pale wash) - `routes/country/_country-shared.css` — all 4 hits are solid-brand intent (eyebrow text `color:`, `.btn-primary` **solid** fill, `.btn-secondary` text/border-color). - `routes/eu-compliance/+page.svelte`, `routes/farm-shop/+page.svelte` — `.bullet-icon`/`.why-icon` `color:` (solid green). - `routes/press/releases/+page.svelte` — `.error-note` `color:` + dashed `border` (solid red). - `routes/release-notes/+page.svelte` — `.eyebrow` `color:` (solid). - `frontend/src/webawesome.css:263` — the `--wa-color-primary-50: var(--wa-color-brand-50)` alias **definition** itself (correct; must alias to brand base). - Not in scope: `-100`..`-900` numeric rungs (separate "undefined-rung" stream) and `routes/design/`. Note: this is an intentional cold/solid→warm-pale visual correction, not pixel-neutral. CI (svelte-check + vitest + a11y/token gates) runs automatically. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 61afaed fix(css): correct Class-A WA tint misuse (-50 base rendered as pale) Jonas · 2026-07-07 · +207 −207

#27 refactor(ui): compose single-purpose banners onto ui/Banner

2026-07-07 · Jonas · 1 commits · 3 filer · +51 −84 · Funktioner

Detaljer
## Summary Consolidates single-purpose banner components onto the canonical `ui/Banner.svelte` primitive, following the pattern `SystemAlertBanner.svelte` already uses. Each converted banner becomes a thin wrapper: its own props, dismissal logic, and mount sites are unchanged — only the visual chrome (surface, accent color, icon slot, dismiss button, aria role/live-region) is delegated to `ui/Banner`. ### Converted - **`frontend/src/lib/components/UpgradeNudge.svelte`** — done first, as requested: this was the worst offender, hardcoding amber hex (`#fffbeb` / `#78350f` / `#fcd34d`) directly in its `<style>` block instead of using tokens. Now composes `<Banner variant="warning" icon={null} role="complementary">`. **This is an intended small visual change** (amber hex → `--color-warning` token family) — layout, dismiss behavior, trial CTA, and the 14-day localStorage dismissal are all unchanged. `ui/Banner`'s `role` prop was widened to accept `'complementary'` (in addition to `'status'`/`'alert'`) so the wrapper could preserve its original ARIA role rather than being forced into `alert`/`status`. - **`frontend/src/components/TrialBanner.svelte`** — clean 1:1 fit. `#fff3cd` background maps onto the `warning` variant's token palette; dismiss button now uses `ui/Banner`'s built-in `dismissible`/`onDismiss` slot instead of a hand-rolled `wa-button`. No layout or behavior change beyond the color-token swap. ### Evaluated, not converted (flagged for follow-up) - **`frontend/src/components/StandstillBanner.svelte`** — currently a `wa-callout variant="danger"` block (title + hint paragraph), not a single-row strip. Forcing it onto `ui/Banner` would visibly change box→strip structure, which isn't pixel-neutral and wasn't blessed as an intended change like UpgradeNudge. - **`frontend/src/components/PublicPreviewBanner.svelte`** — bespoke dark-green gradient + pulsing-dot brand treatment on public marketing routes. `ui/Banner`'s variant palette (info/success/warning/danger/neutral) has no equivalent; forcing it would be a visible brand regression. - **`frontend/src/lib/components/CookieBanner.svelte`** — fixed-bottom GDPR/ePrivacy consent panel with a customise pane (3 checkboxes) and a 2–3 button group. Structurally nothing like a `ui/Banner` single-row strip, and it's legally load-bearing (Reg (EU) 2016/679) — not a good candidate for a forced conversion in this pass. `ReleaseNotesBanner` and `UpgradeBanner` were left untouched per instructions (owned by another stream / explicitly out of scope). ## Test plan - [x] `npm run check` (svelte-check) — 0 errors before and after; warning count unchanged (3184 files, 4861 warnings both runs); no new warnings introduced on the touched files beyond pre-existing `wa-button` a11y warnings that were already present on those same lines before the refactor. - [ ] CI (heavy gates — a11y contrast check, i18n check, vitest) — not run locally per instructions; awaiting CI. - [ ] Manual visual check of UpgradeNudge on `/app/farms/[id]/copilot`, `/admin/webhooks`, `/admin/custom-fields` (amber→token color shift expected there). - [ ] Manual visual check of TrialBanner on `/app/farms/[id]`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 3fe2123 refactor(ui): compose UpgradeNudge and TrialBanner onto ui/Banner Jonas · 2026-07-07 · +51 −84

#26 fix(sync): route sow cross-fostering through the offline outbox

2026-07-07 · Jonas · 1 commits · 1 filer · +12 −17 · Funktioner

Detaljer
## Summary - The sow page's foster modal (`submitFoster()` in `frontend/src/routes/app/farms/[id]/production/pigs/sows/[animalId]/+page.svelte`) POSTed a `fostering` event via raw `fetch(...)`, bypassing the Dexie-backed outbox (`frontend/src/lib/sync/outbox.ts`) that every other event write goes through. Offline submissions were silently lost instead of queued for retry — inconsistent with the app's local-first sync promise. - Routed it through `recordFostering()` in `frontend/src/lib/stores/fosteringStore.ts`, which already enqueues `fostering` events correctly via the outbox but previously had zero callers (its only prior consumer, `FosteringDialog.svelte`, is unmounted/dead and is left untouched — a separate stream may remove it). - Payload contents preserved 1:1: `fosteredAt`, `piglets` (`fosterCount`), `fromSowId` (`fosterDir === 'out' ? undefined : animalId`), and `notes` map verbatim to the previous raw-fetch body. Only the transport changed. ## Bonus fix uncovered along the way The old raw-fetch body sent `payload` as a **nested JSON object**. The backend's `AppendEventDTO`/`EventDTO` (`src/main/scala/com/enlantis/api/routes/EventRoutes.scala`) both type `payload` as `String` (a stringified JSON blob, parsed later by projections) — confirmed by `EventRoutes.scala` lines 28/47 and by `SowReproductiveCard.svelte`'s `parsePayload` doing `JSON.parse(e.payload)`. The old `submitFoster()` therefore very likely 400'd silently on every submit. `recordFostering()` already does `JSON.stringify(payload)` correctly, so this is fixed as a byproduct of the outbox routing. ## Out of scope (flagged, not fixed) Pre-existing content-level bugs in the modal, preserved as-is per the 1:1 mapping requirement: - `fosterDestSow` (the destination-sow input) is bound in the UI but was never read by `submitFoster()` — still true after this change. - `fromSowId: fosterDir === 'out' ? undefined : animalId` looks semantically off (points at the current sow itself rather than the counterpart), same as before. - `piglets` is always sent as `fosterCount` (unsigned), not flipped in sign for `fosterDir === 'out'` vs `'in'`, despite `fosteringStore.ts`'s doc comment stating `piglets` should be signed from the receiver's perspective. These are unrelated to the sync bug and were intentionally left untouched to keep this a surgical transport fix. The drag-and-drop litter board (`onDrop`/`undoLastFoster`) is untouched — it already routes through the outbox and uses a different event-type pair (`cross_foster_out`/`cross_foster_in` via `frontend/src/lib/util/crossFoster.ts`), which is out of scope here. ## Test plan - [x] `npm run check` (svelte-check) — 0 errors (pre-existing unrelated a11y warnings only) - [x] `npm test` (vitest) — 150 files / 2805 tests passed, including `crossFoster.test.ts` - [ ] Manual: submit the foster modal on a sow page while offline, confirm the op appears in the Dexie outbox and flushes on reconnect - [ ] Manual: confirm `fostering` events still project correctly in `SowReproductiveCard.svelte`'s PWM/parity calculations 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 1f4fbbf fix(sync): route sow cross-fostering through the offline outbox Jonas · 2026-07-07 · +12 −17

#22 chore: delete dead EntityCustomFieldsCard (both copies + story)

2026-07-07 · Jonas · 1 commits · 3 filer · +0 −478 · Funktioner

Detaljer
## Summary Deletes both dead copies of `EntityCustomFieldsCard` plus the associated story file. Neither is used by the running app. - `frontend/src/components/EntityCustomFieldsCard.svelte` — a self-fetching "v2 successor" that is never imported anywhere. - `frontend/src/lib/components/ui/EntityCustomFieldsCard.svelte` — presentational component referenced only by its own `.stories.ts`. - `frontend/src/lib/components/ui/EntityCustomFieldsCard.stories.ts` — Storybook story for the above, deleted alongside it. The app's actual custom-fields card is `frontend/src/components/AnimalCustomFieldsCard.svelte`, which is untouched and confirmed live. ## Grep evidence Repo-wide search for `EntityCustomFieldsCard` (before deletion), excluding docs: ``` $ grep -rl EntityCustomFieldsCard frontend/ frontend/src/lib/components/ui/EntityCustomFieldsCard.svelte frontend/src/lib/components/ui/EntityCustomFieldsCard.stories.ts ``` Only the ui/ copy and its own story reference the name — zero non-story importers of either `EntityCustomFieldsCard`. The `src/components/EntityCustomFieldsCard.svelte` copy doesn't even appear in this list because nothing (including itself) references the string `EntityCustomFieldsCard` — it's a fully orphaned file. Confirms `AnimalCustomFieldsCard` is the live component: ``` $ grep -rl AnimalCustomFieldsCard frontend/ frontend/src/lib/a11y.test.ts frontend/src/components/EntityCustomFieldsCard.svelte (unrelated coincidental match, being deleted) frontend/src/routes/app/farms/[id]/animals/[animalId]/+page.svelte ``` `+page.svelte:18` imports it (`import AnimalCustomFieldsCard from '$components/AnimalCustomFieldsCard.svelte';`) and renders it at line 830. `a11y.test.ts:146` covers it in the a11y test suite. Only doc mentions remain outside frontend/ (design-audit/AUDIT.md, other/ENLANTIS_MASTER_TODO.md, docs/design-system.md, design-audit/COMPONENTS.md, other/FARMBRITE_TODO.md) — historical breadcrumbs, out of scope per CLAUDE.md conventions. ## Test plan - [ ] CI build/typecheck passes with no broken imports - [ ] `npm run check` clean (no dangling references) - [ ] Storybook build unaffected (story file removed alongside its component) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 3728248 chore: delete dead EntityCustomFieldsCard (both copies + story) Jonas · 2026-07-07 · +0 −478

#23 chore: delete dead ReleaseNotesBanner

2026-07-07 · Jonas · 1 commits · 1 filer · +0 −29 · Funktioner

Detaljer
## Summary - Deletes `frontend/src/components/ReleaseNotesBanner.svelte` (29 lines) — zero importers repo-wide. - Release notes are already surfaced via the generic `ui/Banner` + a "What's new" nav badge in `src/routes/app/+layout.svelte`, which uses its own local state (`loadReleaseNote()`) plus `startReleaseNotePolling()` from `releaseNotesStore.ts` — not this component. ## Grep evidence (zero importers) ``` $ grep -rn "ReleaseNotesBanner" --include="*.svelte" --include="*.ts" --include="*.js" . | grep -v node_modules (no output — matches only the deleted file's own contents before deletion) ``` Confirmed both scoped to `frontend/src` and repo-wide before deleting. ## `releaseNotesStore.ts` — left untouched Checked whether the `unseen` / `dismissAll` exports (task hypothesis: consumed only by the banner) could also be trimmed: ``` $ grep -rn "\bunseen\b" src --include="*.ts" --include="*.svelte" | grep -v releaseNotesStore.ts src/routes/app/+layout.svelte:229: // O-06: start polling the unseen store (powers the What's new nav badge). (comment only, no import) $ grep -rn "\bdismissAll\b" src --include="*.ts" --include="*.svelte" | grep -v releaseNotesStore.ts src/routes/app/release-notes/+page.svelte:5: import { dismissAll } from '$lib/stores/releaseNotesStore'; src/routes/app/release-notes/+page.svelte:37: dismissAll(); ``` `dismissAll` has a live, independent consumer (`release-notes/+page.svelte`, called on mount to mark notes as seen when the full release-notes page is viewed). Per the task's own fallback instruction, the store is left untouched — only the dead component is removed. ## Test plan - [x] Repo-wide grep confirms zero references to `ReleaseNotesBanner` after deletion. - [ ] CI (svelte-check / vitest / i18n:check) green.
  • c2099c2 chore: delete dead ReleaseNotesBanner Jonas · 2026-07-07 · +0 −29

#24 refactor(cartography): shared Tone union + toneVar() map

2026-07-07 · Jonas · 1 commits · 6 filer · +55 −52 · Funktioner

Detaljer
## Summary Unifies the five incompatible `tone` prop unions across the Cartography Engine components into one shared type + a shared `toneVar()` map, with zero visual change and zero call-site change. New shared module: `frontend/src/lib/marketing/cartography/tone.ts` ```ts export type Tone = | 'ink' | 'ink-soft' | 'ink-faint' | 'rule' | 'burgundy' | 'sage' | 'ochre' | 'slate'; export function toneVar(tone: Tone): string; ``` Each component keeps its own (unchanged) prop subset via `Extract<Tone, ...>` and resolves through the shared `toneVar()` instead of its private `$derived` ternary: - `CompassMark` — `Extract<Tone, 'ink' | 'burgundy' | 'sage'>` - `ContourBand` — `Extract<Tone, 'sage' | 'slate' | 'ochre' | 'burgundy'>` - `MarginAnnotation` — `Extract<Tone, 'ink-soft' | 'ink-faint' | 'burgundy' | 'sage'>` - `PlotPin` — `Extract<Tone, 'burgundy' | 'sage' | 'ochre' | 'slate'>` - `RuleLine` — `Extract<Tone, 'rule' | 'ink-faint' | 'sage'>` Every tone -> CSS var mapping was cross-checked against each component's original ternary before extraction and is byte-identical: | Tone | CSS var | |---|---| | `ink` | `var(--ink)` | | `ink-soft` | `var(--ink-soft)` | | `ink-faint` | `var(--ink-faint)` | | `rule` | `var(--rule)` | | `burgundy` | `var(--color-primary)` | | `sage` | `var(--color-secondary)` | | `ochre` | `var(--color-accent-gold)` | | `slate` | `var(--color-accent-blue)` | No component had an ambiguous or conflicting mapping for a shared tone name (e.g. `sage` -> `--color-secondary` in all four components that use it) — no judgment calls were needed. ## Test plan - [x] `npm run check` (svelte-check): 0 errors; 0 warnings touching `cartography/` (pre-existing warnings elsewhere unrelated to this change) - [x] `npm test`: 150 files / 2805 tests passed - [x] Manual diff review: only the tone-resolution code changed per component; markup/CSS/styling untouched; call sites unchanged (props still accept the exact same literal subset as before) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 2d46395 refactor(cartography): shared Tone union + toneVar() map Jonas · 2026-07-07 · +55 −52

#20 feat(ci): tokens:check — freeze house-token fallback drift (phase 3 follow-up)

2026-07-06 · Jonas · 3 commits · 4 filer · +188 −8 · Design/UI

Detaljer
Adds the `tokens:check` lint gate that [PR #19](https://github.com/whiteancient/enlantis/pull/19)'s review flagged as the natural next step — freezes the fallback drift just cleaned so it can't silently re-enter. ## The gate `frontend/scripts/check-tokens.mjs`, wired in `package.json` and `ci.yml` right after `a11y:check`. **Zero-false-positive, no allowlist/baseline to maintain** — the plan's original "any new hex outside an allowlist" idea was too noisy (legit bare hexes exist: per-card accents, gradient stops, `#000` color-mix mixers). Instead it fails only on values that *provably* disagree with, or duplicate, a house token: - **A. Drifted fallback** — `var(--houseToken, #hex)` where `--houseToken` resolves at an unconditional `:root` (transitively) to a **different** hex. The token always wins, so the literal is dead + misleading drift. Fix: match it or drop it. - **B. Bare brand hex** — one of the 5 unambiguous Cartography hexes (`#7a1f1a` `#6e7a4a` `#2a2218` `#5d5240` `#efe6cf`) used raw instead of its token. Scope: `<style>` blocks + `.css` under `src/`, minus `routes/design/`. Skips token definitions, comments, and non-resolvable aliases (`color-mix`/`rgb`). ## Why `--wa-*` is deliberately out of scope Deferred to the phase-4 WA-bridge pass, for two reasons the gate itself surfaced: 1. `--wa-*` values depend partly on Web Awesome's **runtime** injection — not statically decidable in CI. 2. A **decade-vs-Tailwind convention confusion**: `--wa-color-primary-50` resolves to the brand **base** `#7a1f1a` (WA's decade scale, 05…50…95), but **~20 call sites** use it with the Tailwind assumption "50 = lightest tint" and fossil `#eff6ff`/`#2563eb` fallbacks. Mechanically agreeing those fallbacks would **cement a rendering bug, not fix it** — they need per-site design judgement. Logged for phase 4 in `design-audit/MIGRATION-PLAN.md`. ## Verification - **Green on master:** 853 files, 0 findings. - **Positive-tested:** flags a drifted house fallback (`var(--color-primary, #2563eb)`) and a bare `#7a1f1a`; does **not** flag a correct fallback (`var(--color-primary, #7a1f1a)`) or a deferred `--wa-*` one. - Runs under `bun` (CI runtime) and `node`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 88342c8 Added Jonas2 as root Jonas · 2026-07-06 · +1 −1
  • a8ca9eb harden(tokens:check): mask inert regions + resolve var-fallback token defs Jonas · 2026-07-06 · +20 −7
  • 1c2c795 feat(ci): tokens:check — freeze house-token fallback drift (phase 3 follow-up) Jonas · 2026-07-06 · +167 −0

#21 Redesign AI Ops kanban board with Cartography styling + Trello-style UX

2026-07-06 · Claude (AI) · 2 commits · 1 filer · +1 478 −336 · Funktioner

Detaljer
## Problem The `/app/admin/ai-ops` page shipped with inert legacy Tailwind class names (the Tailwind toolchain was removed 2026-07-03), so the whole page rendered effectively unstyled: no side margins, no card/column chrome, no visual hierarchy — the kanban board was a bare list of divs. ## What changed Full rebuild of the page with component-scoped CSS on the house design tokens (paper/ink surfaces, burgundy/sage/ochre accents, Iowan Old Style display + General Sans small-caps labels, squared radii, warm shadows). No new tokens, no hardcoded UI hex — role colors remain data-driven per role. ### Kanban board (the main event) - **Columns** with per-status accent dots (backlog ink / todo slate / in-progress ochre / blocked brick / review burgundy / done sage), count pills, dashed empty-state drop zones, horizontal scroll on narrow viewports. - **Cards** with a role-colored left edge + role chip, priority pills (urgent/high filled, low dashed, medium implicit), due-date chips (overdue red, &lt;48 h amber), assignee avatar chips, 2-line description preview, hover lift. - **Drag & drop, Trello-grade**: a drop indicator line shows the exact insertion slot; cards can be reordered *within* a column and moved across columns. Order persists via gap-10 `sortOrder` renumbering — optimistic local apply, only changed rows are PUT, then a reload reconciles. - **Per-column "+ Add card" composer** (Enter to add, stays open for consecutive adds, Esc cancels). - **Card detail modal**: title, markdown description with preview toggle, status, role, assignee, segmented priority, due date picker, updated/completed meta, delete. Esc or scrim click closes. - **Filter bar**: text search across title/body/assignee, role filter (incl. "Unassigned"), priority filter, clear button, live card count. Board switcher is now a segmented control. ### Rest of the page All other tabs were equally unstyled and got the same token treatment: chat (three-pane layout, burgundy user bubbles, ochre system notices), schedule (form row + run-history cards with status-colored edges), calendar panels, role grid + editor modal, knowledge base (sidebar + reading pane), and the CLI access panel (mode chips, styled env block + activity table). No backend changes; the page keeps using the existing `/api/admin/ai/*` surface (PUT already accepted `sortOrder`). ## Verification - `svelte-check`: 0 errors, no warnings in the changed file. - Playwright against mocked `/api/admin/ai/*` fixtures: board rendering at 1500 px and 800 px (no horizontal page overflow), card modal, composer, and synthesized HTML5 drag-and-drop — verified the dragged card lands in the right column at the right position and the correct `sortOrder`/`status` PUTs are issued. - The one failing vitest (`onboardingPrefsStore`) fails identically on a clean tree — pre-existing, unrelated. - Wiki untouched per CLAUDE.md (styling/UX-only change, no data-path or auth change). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01TqbcovtLukCciHtUH478aM --- _Generated by [Claude Code](https://claude.ai/code/session_01TqbcovtLukCciHtUH478aM)_
  • 66db241 Fix AI Ops kanban drag-reorder off-by-one + review follow-ups Claude (AI) · 2026-07-06 · +40 −14
  • 1cb2076 Redesign AI Ops kanban board with Cartography styling + Trello-style UX Claude (AI) · 2026-07-06 · +1 438 −322

#19 refactor(css): phase 3.1–3.2 — tokenize bare brand hexes + normalize fallback drift

2026-07-06 · Jonas · 3 commits · 169 filer · +727 −725 · Design/UI

Detaljer
Mechanical, **pixel-neutral by construction** CSS cleanup — fas 3.1–3.2 of the design-system migration plan (`design-audit/MIGRATION-PLAN.md`). No color *values* change on screen; this corrects the hex *drift* hiding in `var()` fallbacks. ## What - **3.1 — 17 sites:** bare brand-palette hex literals in `<style>`/CSS → house token (no fallback emitted). `#7a1f1a→var(--color-primary)`, `#6e7a4a→var(--color-secondary)`, `#2a2218→var(--ink)`, `#5d5240→var(--ink-soft)`, `#efe6cf→var(--paper)` on background/border and `var(--color-text-inverse)` on `color:` (property-aware). - **3.2 — 721 sites:** normalize *mismatched* hex fallbacks on `:root`-defined tokens to the token's current resolved value, e.g. `var(--color-primary, #2d4a32)` → `var(--color-primary, #7a1f1a)`. The fallback is **kept** (offline-PWA defensive default per the plan) — only its drifted value is corrected. `--color-primary` alone carried **11 different stale fallbacks** across the repo (`#667eea/#2563eb/#2d4a32/#8a1f3b/…`), all dead since it's always defined at `:root`. ## Why it's pixel-neutral Every normalized fallback references a token defined *unconditionally* at `:root` in a globally-loaded sheet (`global.css` / `webawesome.css` / `wa-overrides.css`, resolved transitively through aliases), so the fallback **never renders** in the app — only its documentary value moves. ~557 already-correct fallbacks were left untouched. ## Deliberately deferred (not this pass) - Fallbacks on tokens **not provably `:root`-defined in repo** (~1520, mostly `--wa-*` injected by Web Awesome at runtime) — needs runtime computed-style verification; belongs to the phase-4 WA-bridge pass. - `:root` aliases that don't bottom out at a literal hex (`--color-border` = `color-mix`, `--wa-color-primary-1xx`, …) — no literal target. - Token **definitions**, `/*comments*/`, `routes/design/*` palette showcase, `<script>`/SVG-attr hex strings — out of scope by rule. ## Gates - `svelte-check`: **0 errors** (4864 pre-existing warnings unchanged) - `a11y:check`: **50/50** (token definitions untouched) - `i18n:check`: green 169 files, 721 insertions / 721 deletions — all same-line hex-value edits. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • e2cb047 fix(css): review follow-ups — couple on-burgundy border to inverse text, finish gradient tokenization Jonas · 2026-07-06 · +3 −3
  • ac77778 docs(design-audit): mark phase 3.1–3.2 done (PR #19) Jonas · 2026-07-06 · +3 −1
  • 35b33b2 refactor(css): phase 3.1–3.2 — tokenize bare brand hexes + normalize fallback drift Jonas · 2026-07-06 · +721 −721

#18 AI Ops kanban: split into Development + Production boards

2026-07-06 · Claude (AI) · 2 commits · 8 filer · +157 −29 · Funktioner

Detaljer
## Context The ask was for a kanban board — **one for development, one for production** — usable by both humans and AI agents, free, and something we own and can extend ourselves. Rather than stand up a second self-hosted tool (Kanboard, vibe-kanban, etc.) with its own deployment, auth and foreign codebase, this builds on the kanban we already own: the AI Ops board (`ai_task`, `/api/admin/ai/tasks`, drag-and-drop UI at `/app/admin/ai-ops`), which is already reachable by AI agents via the `ai-tasks` Claude Code skill + CLI token. It was a single global board keyed only to AI roles; this generalises it into two named boards and adds a human/AI assignee. ## Changes - **Migration `0242-ai-task-board-assignee`** — adds `ai_task.board` (`VARCHAR(32)`, default `production` so every pre-existing card stays put) and `ai_task.assignee` (free-text, nullable), plus a `(board, status)` index. - **Backend** — `AiTaskRow`, the `AiTaskRepository` `search`/`list`/`update` signatures, and `AiTaskDTO`/`AiTaskUpsert` carry `board` + `assignee`. Valid boards live in `AiOpsRoutes.ValidBoards` (`development`, `production`) — not the schema — so a third board is a one-line change. `GET /tasks` gains an optional `board=` filter (omitted → all boards); `POST` defaults to `production`; `PUT` keeps the card's current board when `board` is omitted (so a plain column move never relocates it). - **Frontend** — the Board tab gets a **Development | Production** switcher and an **Assignee** field on the card form + a chip on cards. `assignee` is orthogonal to `role_id` (the AI-role dimension), so people and agents share one board. - **Skill** — `.claude/skills/ai-tasks/SKILL.md` documents `board` + `assignee`. - **Wiki** — `docs/wiki/architecture/ai-ops.md` + `log.md` updated. ## Verification - Backend `sbt compile` — succeeds, no errors (`[success] elapsed time: 862 s`). - Frontend `svelte-check` — 0 errors, no new warnings on the ai-ops page. - Not run here (no MySQL/`enlantis_v10` in this environment): live end-to-end board CRUD. The migration is a plain additive `ALTER TABLE`; Quill maps the new snake_case-safe single-word columns directly. ## Design note A short comparison of external options (Kanboard MIT, vibe-kanban Apache-2.0, 4gaBoards MIT, and why Planka 2.x / Focalboard were ruled out) is in the plan that drove this; happy to spin up an external tool instead if a standalone board is preferred over extending the one we own. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Nk24gZUhSf6QXRJe8xrAtd --- _Generated by [Claude Code](https://claude.ai/code/session_01Nk24gZUhSf6QXRJe8xrAtd)_
  • 45efb88 AI Ops calendar: label the board-scoped due-dates column Claude (AI) · 2026-07-06 · +3 −2
  • a27a920 AI Ops kanban: split into Development + Production boards Claude (AI) · 2026-07-06 · +154 −27

#15 chore: phase-1 cleanup — dead-weight removal + mount the 402 UpgradeBanner

2026-07-06 · Claude (AI), Jonas · 4 commits · 8 filer · +92 −2 645 · i18n

Detaljer
## What Phase-1 dead-weight work from the design audit, expanded per follow-up decisions. Each target was re-verified against current master (post #12/#13) before touching. ### Deletions (verified dead: zero importers, no Storybook story, superseded) - `AnimalCard` — legacy card, replaced by the species record cards - `AppNav` — legacy sidebar nav, fully replaced by `shell/` (TopBar + MegaMenu + BottomBar) - `lib/components/NpsBanner` — old NPS generation, superseded by `NpsPromptDialog` - `lib/components/WithdrawalBadge` — superseded by `WithdrawalChipStrip` - `lib/schedule/CalendarView` — **broken import** (`../TimedEvent/AllDayEvent.js` doesn't exist → throws on instantiation); unused; only self-referenced by its barrel export. Removed component + `index.ts` export. ### Mount the 402 UpgradeBanner (functional) The 402 billing-gate signal was wired end-to-end EXCEPT the UI: `recordBillingGate402` (api.ts:77) set `billingGateStore` on any premium-endpoint 402, but nothing rendered it — the gate fired **silently**, leaving a blocked user with no explanation. - Mount `<UpgradeBanner />` once at the top of the app content area (`routes/app/+layout.svelte`). Self-guards on the store (invisible until a gate fires); store carries `upgradeUrl` so it's farm-agnostic. - Clear the gate on `afterNavigate` → the banner is **transient** (shows on the page where the gate fired, gone when the user moves on) rather than following them across the app. - Added the missing `billing.gate.feature.unknown` i18n key (en/sv) — the banner's fallback for an unparseable 402 body; without it the raw key string would render. ### i18n copy fix - Drop the stale `Tailwind` mention from `apiSandbox.frontendStep.dev.body` (en + sv) — Tailwind was removed in #12. ## Verification - `svelte-check`: 0 errors (3184 files) - `npm run i18n:check`: green · `npm run a11y:check`: 50/50 · `vitest`: 2805 pass ## Deliberately NOT done — and why - **Re-point the 4 shadowed `lib/components/ui/*` stories → `$components` versions:** infeasible. The prop APIs diverge fundamentally — the `lib/ui` versions are **presentational** (fed fixtures: `status/reads`, `fields/editable`, `article/label`, `photoUrl/confidence`) while the `$components` versions are **self-fetching** (`{farmId}`, read stores/Bluetooth/backend; `$components/HelpButton` takes zero props). Re-pointing would break every story — they can't render meaningfully in Storybook's isolated environment. **Finding: these 4 `lib/ui` components are NOT dead weight** — they're the Storybook-documented presentational variants, doing their job as story subjects. Recommend keeping them; the real cleanup (make the app adopt the presentational versions + wire data at call sites) is a refactor, not a strip. - **214 dead-TW occurrences / app.css `.text-slate-*` selectors:** not mechanical — the tail encodes real intent (e.g. `aml` `severityBadge()` returns `bg-red-100`-style strings, asserted by `aml.test.ts`, that now render unstyled) and `text-slate-*` is still live in ~10 files where the app.css selectors darken it in outdoor mode. Per-page **restyle**, separate PR(s). - **Other unwired features** (`NpsPromptHost/Dialog`, `FosteringDialog`, `ReleaseNotesBanner`, `EntityCustomFieldsCard` v2): mount-or-delete product decisions, held. - **schedule other four unused** (`lc-button`, `lc-dropdown`, `CalendarViewTabs`, `CalendarNavControls`): coherent-but-unused; left pending a deliberate subsystem-prune call. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 59e279c fix(billing): add i18n labels for the 4 uncovered premium-gate features Claude (AI) · 2026-07-06 · +46 −30
  • 6bc5724 feat(billing): mount UpgradeBanner so the 402 gate is actually visible Jonas · 2026-07-05 · +42 −30
  • 758817c chore(schedule): remove dead CalendarView (broken import, unused) Jonas · 2026-07-05 · +0 −1 487
  • 9a97e70 chore(cleanup): phase-1 dead weight — verified-dead orphans + stale Tailwind copy Jonas · 2026-07-05 · +4 −1 098

#14 a11y: equal-width chip/pill groups + design-audit decision log

2026-07-05 · Jonas · 4 commits · 11 filer · +120 −56 · Design/UI

Detaljer
## What Two related pieces on top of the merged WCAG revision (#13): 1. **Equal-width chip/pill groups across marketing + docs surfaces** (`4b1bef9e`) — applies the DESIGN.md layout idiom (grouped cards/pills share equal width per row) to the remaining chip/pill groups on marketing and docs pages, closing out the layout gate from the a11y sweep. 2. **Design-audit status + decision log** (`2f7d2172`, docs only, `design-audit/`) — brings AUDIT.md and MIGRATION-PLAN.md up to date with reality after Tailwind removal (#12) and the WCAG revision (#13): - Tailwind: 6601 → 214 remaining dead-class occurrences (16 classes, listed) - WCAG plan delivered: contract tokens, `a11y:check` CI gate, focus-ring rollout, chart palette; notes that the Task 10 modal→wa-dialog conversions were reverted and remain open - **Phase-0 decisions recorded:** keep current radii, keep current `theme-color`, no new warm neutral ramp, shop stays white e-commerce → the recolor phases (3.3–3.4) are dropped from the migration plan; the remaining path is pixel-neutral tokenization, dead-weight cleanup and component consolidation ## Why The design-audit documents are the working roadmap for the upcoming progressive-disclosure redesign; without the decision log, the audit's canonical-color proposals read as pending work when they have in fact been explicitly declined. ## Reviewer notes - The docs commit touches only `design-audit/*.md` — no runtime code. - The chip/pill commit follows the grouped-cards rule in `frontend/DESIGN.md`; visual snapshots cover the affected pages. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 0f108c0 fix(test): drop shebang from check-contrast.mjs — unblocks its vitest suite Jonas · 2026-07-05 · +2 −1
  • cc5353f fix(review): address PR #14 review findings Jonas · 2026-07-05 · +80 −39
  • 2f7d217 docs(design-audit): record phase-0 decisions + post-Tailwind/WCAG status Jonas · 2026-07-05 · +15 −0
  • 4b1bef9 fix(a11y): equal-width chip/pill groups across marketing + docs surfaces Jonas · 2026-07-05 · +23 −16

#13 WCAG 2.2 AA accessibility contract + remediation

2026-07-05 · Jonas · 43 commits · 17 filer · +3 558 −1 443 · Design/UI

Detaljer
## WCAG 2.2 AA / EN 301 549 accessibility remediation Implements the §7 proposal from `other/WCAG_AUDIT_2026-07.md` (Rev 3) across 18 reviewed tasks. Each task was spec+quality reviewed; a final whole-branch review verdicted **ready to merge with fixes**, and that fix is included. ### The durable mechanism (the point of the branch) A **scheme-independent design-token "accessibility contract"** + a CI guard `frontend/scripts/check-contrast.mjs` (`npm run a11y:check`, wired into CI after `wa:check`). It parses the real design tokens, resolves `var()`/`color-mix()` chains, and enforces WCAG contrast ratios as **token-pair contracts** — **50 pairs, 0 failing**. Because rules bind token *names* to minimum ratios (never hex), a future palette swap stays compliant by construction: change the colours, run the guard, fix what it flags. The guard **fails closed** on unresolvable tokens (so a swap to `oklch()`/`rgb()` notation can't silently turn it into a no-op). ### Shipped & verified - **Contract tokens** (`--color-*-text`, `--color-on-*`, `--color-focus-ring`, `--color-border-strong`, `--tap-target-min`, `--chart-*`) — all additive, all meeting their claimed ratios. - **Systemic CSS:** global `prefers-reduced-motion` kill switch (O-6), focus-ring token on buttons/inputs/links/skip-link/WA (O-4), 24px tap-target floor + stronger input borders (O-5/P-8). - **Components:** SyncStatusBadge status icons + disclosure (P-6); 4 public popovers off custom `role="dialog"` (LocalePicker/MegaMenu/VoiceTreatment/CookieBanner); `FieldError` with `aria-invalid`/`aria-describedby` + static guard (U-1); `wa-*` shadow-DOM labels on 6 controls (U-4); autofocus fully removed (O-2); sr-only names + `aria-pressed` + fieldset (R-1/R-2/P-9); `/docs` + `/home/trust` contrast fixes (P-3); harness config (R-3/R-5); svelte-ignore triage (R-4); chart tokens (P-7 exemplars). - **Docs:** DESIGN.md/CLAUDE.md corrected; new normative rulebook `docs/wiki/architecture/accessibility.md`. - **Final live axe re-scan** (axe-core 4.10.2, WCAG 2.x AA): `/docs`, `/home/trust`, `/farmers`, `/home`, `/home/pricing`, `/release-notes`, `/case-studies` → **0 violations**. ### Known-open (documented in the audit register Rev 3 — intentional, out of this scope) - `/brand` has 3 contrast nodes: a stale `#16a34a` (old green) swatch — part of the deferred inline-hex work. - **Deferred follow-ups:** ~20 auth-gated custom modals → `<wa-dialog>` (O-1); the systemic `wa-*` label trap (~175 files, U-4); placeholder-only inputs (U-2); the 219-instance inline-hex sweep (P-7); the authenticated `/app` axe crawl; the external WCAG-EM audit. Tracked in `other/WCAG_AUDIT_2026-07.md` §Rev 3 and `.superpowers/sdd/task-10-deferred-modals.md`. ### Gates `npm run a11y:check` 50/0 · `wa:check` ✅ · `i18n:check` ✅ · `svelte-check` 0 errors · vitest **2799 passed**. (One commit, 36ee5d8c, fixes a *pre-existing* unrelated wa:check failure so the CI gate is green.) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • 0e05f9e fix(a11y): parse 4/8-digit alpha-hex in contrast guard, don't skip stops Jonas · 2026-07-04 · +18 −7
  • b6ed6de fix(a11y): restore focus on all CopilotPanel close paths, not just Escape Jonas · 2026-07-04 · +4 −1
  • 4b1e0c7 fix(a11y): harden contrast guard against fail-open/crash gaps Jonas · 2026-07-04 · +44 −4
  • 84f1da3 fix(a11y): restore focus to the opener on Escape (2.4.3) Jonas · 2026-07-04 · +20 −5
  • 2007d2a Design audit update Jonas · 2026-07-04 · +515 −336
  • 36ee5d8 fix(wa): guard 4 pre-existing unguarded wa-* boolean-attr bindings Jonas · 2026-07-04 · +4 −4
  • 0c0bb2b Fix contrast guard: fail closed on unresolvable contract endpoints Jonas · 2026-07-04 · +12 −3
  • f46d7a1 docs(a11y): audit Rev 3 implementation close-out + honest /brand correction Jonas · 2026-07-04 · +12 −2
  • c13a4fb refactor(a11y): tokenize priority/welfare hex maps in copilotStore (P-7 part 2) Jonas · 2026-07-04 · +12 −8
  • 149dfc8 feat(a11y): add guarded chart/series palette tokens (P-7 part 1) Jonas · 2026-07-04 · +17 −0
  • c5596c1 fix(a11y): svelte-ignore triage across 17 files (R-4) Jonas · 2026-07-04 · +44 −10
  • 15a8962 fix(a11y): audit harness excludes wa-details, skips openapi.json route stub (R-3, R-5) Jonas · 2026-07-04 · +17 −1
  • ccf9612 fix(a11y): live contrast fixes on RegulationCard eurlex link + OutboxMock footer-note (P-3) Jonas · 2026-07-04 · +3 −3
  • 0436add fix(a11y): slotted names for icon-only wa-buttons, aria-pressed filters, fostering fieldset (R-1, R-2, P-9) Jonas · 2026-07-04 · +10 −8
  • f22dac3 Remove autofocus from 4 /app route files (O-2 closure) Jonas · 2026-07-04 · +2 −4
  • 5a91a78 fix(a11y): remove autofocus (2.4.3) and CopilotPanel backdrop/role cleanup (O-2, O-3) Jonas · 2026-07-04 · +14 −8
  • c5fa2be feat(a11y): FieldError component + exemplar wiring (audit U-1) Jonas · 2026-07-04 · +89 −26
  • 8721930 Fix WCAG label violations on farmers directory page Jonas · 2026-07-04 · +12 −8
  • af49688 fix(a11y): wa-* controls get real labels via component API — wrapping labels don't pierce shadow DOM (U-4) Jonas · 2026-07-04 · +46 −69
  • 844ab70 docs(a11y): plan note — Task 6 border-radius removal rationale Jonas · 2026-07-04 · +2 −1
  • 58326f3 Revert "fix(a11y): convert 6 modal dialogs to wa-dialog (O-1, batch 3)" Jonas · 2026-07-04 · +155 −76
  • 1ac4fe0 Revert "fix(a11y): convert 5 modal dialogs to wa-dialog (O-1, batch 4)" Jonas · 2026-07-04 · +69 −48
  • 2fec98f fix(a11y): convert 5 modal dialogs to wa-dialog (O-1, batch 4) Jonas · 2026-07-04 · +48 −69
  • ae6f9f0 Fix: Remove dangling ARIA disclosure attributes from VoiceTreatmentPrimary idle button Jonas · 2026-07-04 · +0 −2
  • 768e4d9 fix(a11y): convert 6 modal dialogs to wa-dialog (O-1, batch 3) Jonas · 2026-07-04 · +76 −155
  • 95e94d0 Revert "fix(a11y): convert 4 modal dialogs to wa-dialog (O-1, batch 1)" Jonas · 2026-07-04 · +226 −84
  • 4fdbddb Revert "fix(a11y): convert 6 modal dialogs to wa-dialog (O-1, batch 2)" Jonas · 2026-07-04 · +123 −68
  • 52c41b9 fix(a11y): convert 6 modal dialogs to wa-dialog (O-1, batch 2) Jonas · 2026-07-04 · +68 −123
  • 217cd31 Convert non-modal popovers off custom role=dialog (Task 10 scoped) Jonas · 2026-07-04 · +47 −11
  • 2521363 fix(a11y): convert 4 modal dialogs to wa-dialog (O-1, batch 1) Jonas · 2026-07-03 · +84 −226
  • 9fa1b0c fix(a11y): SyncStatusBadge — status icons (1.4.1) + disclosure semantics with Escape/focus return (P-6, O-1) Jonas · 2026-07-03 · +25 −4
  • a29355a docs(a11y): correct DESIGN.md safe-color list, wire rulebook to live guard (P-2) Jonas · 2026-07-03 · +16 −11
  • dee74fc feat(a11y): 24px tap-target floor + 3:1 input borders (O-5, P-8) Jonas · 2026-07-03 · +12 −1
  • 1c7dd4b fix(a11y): drop needless border-radius from a:focus-visible (review finding, human-approved) Jonas · 2026-07-03 · +0 −1
  • 27c6696 feat(a11y): semantic focus-ring token applied to btn/inputs/links/skip-link/WA (O-4) Jonas · 2026-07-03 · +46 −4
  • 5c13cf8 feat(a11y): global prefers-reduced-motion kill switch (O-6) Jonas · 2026-07-03 · +30 −0
  • e73322b refactor(a11y): consolidate page-local --sh-* shop palette into token SSOT (P-10) Jonas · 2026-07-03 · +30 −25
  • 9d827cc Export formatPair helper to include note field in contrast-pair output Jonas · 2026-07-03 · +18 −2
  • b0e3280 feat(a11y): CI contrast contract gate (npm run a11y:check) Jonas · 2026-07-03 · +141 −1
  • 5cbbe26 feat(a11y): contrast-guard engine (parse/resolve/WCAG math) with tests Jonas · 2026-07-03 · +178 −0
  • deb602a feat(a11y): add WCAG contract tokens (audit 2026-07 §7.1) Jonas · 2026-07-03 · +50 −0
  • a507e1f docs(a11y): audit Rev 2 (post-Tailwind re-scan) + implementation plan Jonas · 2026-07-03 · +1 206 −22
  • f3b751c fix(footer): equal-width badge cards + DESIGN.md rule clarification Jonas · 2026-07-03 · +13 −2

#12 Remove the inert Tailwind toolchain; realize the /farmers card-grid design

2026-07-03 · Jonas · 12 commits · 50 filer · +7 291 −5 510 · Design/UI

Detaljer
## Summary Tailwind v4 was wired into Vite but **no stylesheet ever imported it** — it generated zero CSS, and the \"Tailwind 4\" stack claim in the docs misled sessions into writing ~460 dead utility-class tokens across 61 files. Decision (2026-07-03): Tailwind shall not be used. This PR removes it end-to-end with a proof-of-no-visual-change harness, and implements the card-grid design the dead classes always intended on the public /farmers pages. - **Disputes badges** (the *only* live Tailwind compile, via `@reference`+`@apply`) rewritten pixel-identically to plain scoped CSS first — removing the plugin without this breaks the build. - **Toolchain removed**: vite plugin, Storybook `viteFinal`, `@tailwindcss/vite` + `tailwindcss` deps. - **59 files stripped** of provably-dead classes via `frontend/scripts/strip-dead-tw.mjs` (allowlist-of-removals, script/style/comment masking, `--verify` mode); design intent archived in `other/TAILWIND_INTENT_INVENTORY.md`. - **/farmers + /farmers/[slug]** rewritten to their intended responsive card grids (scoped CSS per DESIGN.md layout primitives; neutral palette centralized in `--sh-*` props for the coming warm-neutral token sweep). - **Docs corrected**: CLAUDE.md stack line, new DESIGN.md \"Styling system — scoped CSS, never Tailwind\" section, wiki, competitor pages. Executes design-audit MIGRATION-PLAN Fas 1.1 (with the disputes correction the audit missed). - **Environment fixes surfaced en route**: committed `bun.lock` pinned `@sveltejs/kit` 2.66.0 which does not build with vite 6.4.3 (now 2.69.1); new `bunfig.toml` pins bun's hoisted linker (isolated layout breaks svelte transforms). ## Verification - Build determinism proven (two baseline builds byte-identical, 583 CSS artifacts). - After plugin removal + strip: **CSS artifact set byte-identical to baseline**; final delta is exactly the rewritten disputes chunk (declaration-equivalent to the compiled ground truth) + two new farmers stylesheets. - svelte-check delta 0 (0 errors / 4897 warnings, identical to baseline); vitest 2759/2759; i18n:check + wiki:lint green. - Farmers grid QA in-browser: 3 columns desktop / 2 @768px / 1 @390px, equal row heights, footer-pinned links, title ellipsis, 2-line clamp (DESIGN.md Layout gate). Deliberate visual change on exactly two routes (/farmers, /farmers/[slug]) — everything else provably unchanged. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
  • d313228 build: refresh committed static output after Tailwind removal Jonas · 2026-07-03 · +3 116 −2 939
  • f23e88a docs: remove Tailwind stack claims; document that Tailwind is not used Jonas · 2026-07-03 · +39 −18
  • a2a0eda feat(farmers): implement intended card grid design per DESIGN.md layout idiom Jonas · 2026-07-03 · +324 −63
  • 695bf86 chore(frontend): strip inert Tailwind classes — cluster 6 (shop-admin + stripe + disputes) Jonas · 2026-07-03 · +1 070 −896
  • f7006e9 chore(frontend): strip inert Tailwind classes — cluster 5 (public shop + newsletter) Jonas · 2026-07-03 · +493 −367
  • 12e99a9 chore(frontend): strip inert Tailwind classes — cluster 4 (kill-sheets + production) Jonas · 2026-07-03 · +428 −329
  • 122bfba chore(frontend): strip inert Tailwind classes — cluster 3 (billing/settings/advisor) Jonas · 2026-07-03 · +276 −188
  • 8b44319 chore(frontend): strip inert Tailwind classes — cluster 2 (root-admin) Jonas · 2026-07-03 · +533 −427
  • 36cb40e chore(frontend): strip inert Tailwind classes — cluster 1 (custom-fields + farm misc) Jonas · 2026-07-03 · +279 −201
  • 3a664e8 chore(frontend): add strip-dead-tw script + intent inventory scaffold Jonas · 2026-07-03 · +687 −0
  • 2729ddf chore(frontend): remove inert Tailwind plugin + deps; pin bun linker; fix stale lockfile kit version Jonas · 2026-07-03 · +19 −74
  • 190d4b8 fix(shop): rewrite disputes badges to plain scoped CSS (pixel-identical; unblocks Tailwind removal) Jonas · 2026-07-03 · +27 −8

#11 Minor fixing and documentation for upcoming Design system and WCAG update

2026-07-03 · Jonas · 6 commits · 8 filer · +10 743 −15 · Design/UI

Detaljer
Scouting, minor fixing and documentation for upcoming Design system and WCAG update
  • eae9559 Design audit docs Jonas · 2026-07-03 · +10 239 −0
  • e1e97fa WCAG audit docs Jonas · 2026-07-03 · +436 −0
  • ed56863 UX: Design-system rule — grouped cards share equal width/height Jonas · 2026-07-03 · +56 −8
  • 0af4189 UX: Increased country border to 1px for visibility Jonas · 2026-07-03 · +2 −1
  • ff7f42f UX: Removed hover select on EUReg map, changed to on click. Jonas · 2026-07-03 · +7 −4
  • 1965272 Fixed hovertext not visible on footer buttons Jonas · 2026-07-03 · +3 −2

#10 Implement locale handling improvements in onboarding wizard and home layout

2026-07-02 · Jonas · 1 commits · 17 filer · +239 −37 · Buggfixar

Detaljer
Fixed bug where language switched back to english when swedish was selected. - Refactor onboarding wizard to use `wizardLocaleFrom` for locale management, ensuring the displayed language reflects user selection and persists correctly. - Update home layout to prevent locale pinning to 'en' during client navigation without a language segment, addressing a bug that flipped Swedish sessions to English. - Introduce tests for `wizardLocaleFrom` to validate locale mapping logic. This commit enhances user experience by ensuring correct locale handling across the application.
  • bd7e62b Implement locale handling improvements in onboarding wizard and home layout Jonas · 2026-07-02 · +239 −37

#9 Added start script for frontend and backend

2026-07-02 · Jonas · 1 commits · 3 filer · +61 −8 · Funktioner

Detaljer
  • 06cff95 Added start script for frontend and backend Jonas · 2026-07-02 · +61 −8

2026-06-24 — 7 direkt-commits

Detaljer
  • bc1f4d8 simulator Jonas · 2026-06-24 · +249 −102
  • ce3d975 sim: headline score + herd-health gauge Jonas · 2026-06-24 · +91 −2
  • 9f30283 sim: victory state, game launcher, persisted hands-on progress Jonas · 2026-06-24 · +242 −38
  • c0f352b sim: game HUD — rank/XP, Kassa, objectives, hands-on actions, coach Jonas · 2026-06-24 · +685 −59
  • 0e6dec2 sim: add player-facing economy + hands-on action endpoints Jonas · 2026-06-24 · +373 −2
  • bd85436 simulator Jonas · 2026-06-24 · +9 746 −1
  • af0fdc6 simulator Jonas · 2026-06-24 · +518 −50

2026-06-22 — 2 direkt-commits

Detaljer
  • 66b502a simulator Jonas · 2026-06-22 · +718 −0
  • ce9f825 simulator Jonas · 2026-06-22 · +167 −21

2026-06-21 — 1 direkt-commits

Detaljer
  • ff01bc9 bugfixes Jonas · 2026-06-21 · +191 −67

2026-06-20 — 7 direkt-commits

Detaljer
  • 98f9d75 RegelRadar Jonas · 2026-06-20 · +566 −302
  • 4388973 RegelRadar Jonas · 2026-06-20 · +1 035 −0
  • 2de0673 RegelRadar Jonas · 2026-06-20 · +870 −178
  • 49cab79 Set ENLANTIS_ADMIN_TOKEN secret in Cloud Run deploy Jonas · 2026-06-20 · +1 −1
  • 18dc54a Don't reference compile task value in copyDepJars (sbt 2.x cache hash) Jonas · 2026-06-20 · +11 −6
  • 7121650 Stage Docker artifacts to fixed target/docker-dist (sbt 2.x layout fix) Jonas · 2026-06-20 · +29 −7
  • 84152f6 RegelRadar Jonas · 2026-06-20 · +158 −40

2026-06-19 — 2 direkt-commits

Detaljer
  • 8169b5a Fix Dockerfile runtime COPY path for Scala 3.8.3 bump Jonas · 2026-06-19 · +2 −2
  • 23c4d30 Bump sbt compile heap 4G->8G to fix Cloud Build OOM Jonas · 2026-06-19 · +4 −2

2026-06-18 — 4 direkt-commits

Detaljer
  • 077201f RegelRadar Jonas · 2026-06-18 · +349 −0
  • 4edf9d6 RegelRadar Jonas · 2026-06-18 · +998 −357
  • b7833f2 RegelRadar — auto-apply source-registry seeds on boot (migrations 0235/0236) Jonas · 2026-06-18 · +86 −46
  • c805cf4 RegelRadar Jonas · 2026-06-18 · +51 −479 747

2026-06-17 — 2 direkt-commits

Detaljer
  • 2dd1156 RegelRadar Jonas · 2026-06-17 · +717 −0
  • ab74f64 RegelRadar Jonas · 2026-06-17 · +1 202 −93

2026-06-15 — 10 direkt-commits

Detaljer
  • 7ce6f72 RegelRadar Phase 4 — crop vertical + international + radar-page UI Jonas · 2026-06-15 · +429 −37
  • e60cb47 RegelRadar Jonas · 2026-06-15 · +55 −10
  • 6561c4f RegelRadar Phase 3 — tie a change to the farm's own data + evidence packs Jonas · 2026-06-15 · +511 −12
  • 615df5d RegelRadar Phase 2 — P1 sources, rss kind, legal-corpus Layer-3 Jonas · 2026-06-15 · +859 −30
  • fa0898d RegelRadar Phase 1 — ingestion spine + P0 pillars Jonas · 2026-06-15 · +1 870 −9
  • 24c70a7 RegelRadar Jonas · 2026-06-15 · +64 −1
  • f36182c chore: remove leftover RegelRadar remote-build-log debris Jonas · 2026-06-15 · +0 −0
  • cb39e04 chore: remove accidentally committed log debris from RegelRadar commit Jonas · 2026-06-15 · +8 −151 422
  • c9a2948 RegelRadar Jonas · 2026-06-15 · +154 388 −0
  • 0bdf4f0 RegelRadar Jonas · 2026-06-15 · +2 589 −57

2026-06-02 — 2 direkt-commits

Detaljer
  • cf49865 docs(wiki): email/notifications + multi-site KPI pages; reconcile AI-Ops post-merge Jonas · 2026-06-02 · +394 −2
  • 2a79803 wip Jonas · 2026-06-02 · +13 684 −37

2026-06-01 — 6 direkt-commits

Detaljer
  • b41bcfd updates Jonas · 2026-06-01 · +1 −1
  • 6706238 updates Jonas · 2026-06-01 · +1 880 −0
  • a42aefc updates Jonas · 2026-06-01 · +441 −215
  • f142eab CDB pig compliance Phase 5: pen_recount (J1) + migration 0230 + wiki Jonas · 2026-06-01 · +530 −0
  • f4b5b74 wip Jonas · 2026-06-01 · +1 986 −86
  • ccafdb0 CDB pig compliance Phase 4b: movements/reporting (F7, F10, F11, F13) Jonas · 2026-06-01 · +822 −169

2026-05-31 — 9 direkt-commits

Detaljer
  • ad19d2b CDB pig compliance Phase 4a: movements (F6/F8/F9/F12) Jonas · 2026-05-31 · +534 −81
  • 37928ca wip Jonas · 2026-05-31 · +25 −130
  • d4e7e1f CDB pig compliance Phase 3c: Djurskydd (SMR 10) — Section 5 fully done Jonas · 2026-05-31 · +3 046 −81
  • 8ad0bb6 CDB pig compliance Phase 3b: add untracked J7/J13/J14 files Jonas · 2026-05-31 · +1 032 −0
  • d868275 wip Jonas · 2026-05-31 · +1 672 −58
  • 7b8abf6 wip Jonas · 2026-05-31 · +102 −140
  • 36a2494 CDB pig compliance: Phase 3 marking rules + form pickers (+ Phase 2 schema batch) Jonas · 2026-05-31 · +1 525 −163
  • 2f37331 wip Jonas · 2026-05-31 · +182 −0
  • aeec47b wip Jonas · 2026-05-31 · +1 477 −44

2026-05-30 — 3 direkt-commits

Detaljer
  • 3909b32 Bind NPS dismiss/respond to the invite's owner (authz) Jonas · 2026-05-30 · +101 −9
  • e8cf72a Require order ownership for withdrawal requests (authz) Jonas · 2026-05-30 · +78 −24
  • 0400959 Bind delivery-slot booking to the order's storefront (authz) Jonas · 2026-05-30 · +71 −7

2026-05-29 — 17 direkt-commits

Detaljer
  • 72b9420 Gate deletion of platform-default vaccine protocols (authz) Jonas · 2026-05-29 · +5 −2
  • 40efa94 Gate ungated mutations in Document/Food/HealthCheckParameter routes (authz) Jonas · 2026-05-29 · +177 −41
  • 75d3cef Gate user-directory lookups to self-or-root-admin (authz) Jonas · 2026-05-29 · +115 −10
  • 7e173ae updates Jonas · 2026-05-29 · +1 159 −1 134
  • bc22f9d updates Jonas · 2026-05-29 · +827 −2 269
  • 2574a89 Gate animal-weight create/delete behind requireWriter Jonas · 2026-05-29 · +18 −9
  • 3aace11 Gate unauthenticated mutations in previously-no-auth route files Jonas · 2026-05-29 · +161 −181
  • 60781c4 Gate previously-unauthenticated farm-private read endpoints (authz) Jonas · 2026-05-29 · +507 −222
  • 12c83d7 Require farm membership to read the animal register (authz) Jonas · 2026-05-29 · +94 −8
  • f437f3f Implemented Obisidian based Wiki for AI memory and context support. Jonas · 2026-05-29 · +1 197 −1
  • 56c30c8 Correct stale comment on farm animals list endpoint Jonas · 2026-05-29 · +5 −2
  • f62dfb2 Don't 500 on free-text administeredBy in treatment create Jonas · 2026-05-29 · +33 −1
  • 17ef682 Pass userId (not personId) into voice ParseContext.userId Jonas · 2026-05-29 · +1 −1
  • 26d584d Fix owner withdrawal-alert visibility (pass userId not personId) Jonas · 2026-05-29 · +1 −1
  • dd5971c Resolve treatment administeredByName via user→person lookup Jonas · 2026-05-29 · +63 −6
  • c552133 wip Jonas · 2026-05-29 · +544 −0
  • da89e4a wip Jonas · 2026-05-29 · +1 209 −519

2026-05-28 — 2 direkt-commits

Detaljer
  • 715de8a wip Jonas · 2026-05-28 · +306 −0
  • 3a75b49 wip Jonas · 2026-05-28 · +96 −75

2026-05-26 — 1 direkt-commits

Detaljer
  • 68eeb5d demo-farm onboarding branch (migration 0225) Jonas · 2026-05-26 · +776 −36

2026-05-25 — 6 direkt-commits

Detaljer
  • 71ceef2 translations Jonas · 2026-05-25 · +1 002 −0
  • c8eb867 translations Jonas · 2026-05-25 · +20 281 −990
  • 9f56be0 translations Jonas · 2026-05-25 · +322 −0
  • 98748c1 translations Jonas · 2026-05-25 · +6 617 −268
  • 119ba15 compliance gap Jonas · 2026-05-25 · +332 −0
  • 4d97f8a compliance gap Jonas · 2026-05-25 · +523 −53

2026-05-24 — 4 direkt-commits

Detaljer
  • fabb00f compliance gap Jonas · 2026-05-24 · +7 753 −0
  • a78d4c0 compliance gap Jonas · 2026-05-24 · +10 570 −612
  • 2398d69 compliance gap Jonas · 2026-05-24 · +3 542 −0
  • 1067dc6 compliance gap Jonas · 2026-05-24 · +3 142 −1 583

2026-05-23 — 17 direkt-commits

Detaljer
  • fdd3797 compliance gap Jonas · 2026-05-23 · +135 −0
  • 9534c04 compliance gap Jonas · 2026-05-23 · +407 −70
  • 8fd938f compliance(s09+s17): close 2 ignored tests — server-side PDF/ZIP exports (batch 15) Jonas · 2026-05-23 · +113 −16
  • c147b7b compliance(s16+s17): close 2 ignored tests — push notification fanout (batch 14) Jonas · 2026-05-23 · +232 −19
  • bf79980 compliance(s15): close 2 ignored tests — equine slaughter completeness (batch 13) Jonas · 2026-05-23 · +113 −19
  • a64b136 compliance(s13): close 3 ignored tests — bee organic (batch 12) Jonas · 2026-05-23 · +119 −26
  • dc53973 compliance(s14): close 6 ignored tests — eco-scheme dossier (batch 11) Jonas · 2026-05-23 · +750 −102
  • 460c037 compliance(s13): close 3 ignored tests — Annex VI organic certificate (batch 10) Jonas · 2026-05-23 · +402 −26
  • d10eca0 compliance(s06): close 4 ignored tests — restricted-zone biosecurity (batch 9) Jonas · 2026-05-23 · +421 −68
  • 41444c8 compliance(s01): close 3 ignored tests — retention-aware delete (batch 8) Jonas · 2026-05-23 · +257 −110
  • d265535 compliance(s17): close 6 ignored tests — GDPR breach + DSAR flow (batch 7) Jonas · 2026-05-23 · +483 −42
  • 3fa48fd compliance gap Jonas · 2026-05-23 · +303 −43
  • d112ddf compliance gap Jonas · 2026-05-23 · +125 −0
  • 952af5c compliance gap Jonas · 2026-05-23 · +478 −78
  • 73ee3c0 wip Jonas · 2026-05-23 · +1 496 −0
  • 6110467 wip Jonas · 2026-05-23 · +1 637 −0
  • 82c05d7 wip Jonas · 2026-05-23 · +1 036 −1 032

2026-05-22 — 6 direkt-commits

Detaljer
  • 4d04385 wip Jonas · 2026-05-22 · +12 937 −38
  • 72b1833 wip Jonas · 2026-05-22 · +87 −117
  • 7642878 better top Jonas · 2026-05-22 · +317 −224
  • 048b5f7 WebAwesome deprecated components Jonas · 2026-05-22 · +56 −0
  • 155a50a WebAwesome deprecated components Jonas · 2026-05-22 · +228 −192
  • bbfcb9d Lit Calendar Jonas · 2026-05-22 · +21 032 −15

2026-05-21 — 1 direkt-commits

Detaljer
  • 5fb9446 Deployment Jonas · 2026-05-21 · +206 −57

2026-05-20 — 3 direkt-commits

Detaljer
  • 233ec2d Integrate the schedule better Jonas · 2026-05-20 · +141 −818
  • 66c433f AgriVision Jonas · 2026-05-20 · +993 −888
  • 5d51336 Add temporary 'under construction' landing page at / Jonas · 2026-05-20 · +1 208 −1 099

2026-05-19 — 4 direkt-commits

Detaljer
  • 94ee856 AgroVision-switch P0/P1 follow-ups: per-animal batch mortality + 3 hygiene fixes Jonas · 2026-05-19 · +746 −96
  • 923c9c5 P2 — AgroVision-switch strategic moves: migration accelerators, NL, PWA polish Jonas · 2026-05-19 · +8 044 −500
  • 7aab0ee P1-D Genetics + P1-E Vet portal — AgroVision-switch P1 complete Jonas · 2026-05-19 · +7 637 −51
  • 47d08ec P1-G national I&R live + 21-spec pre-existing test-failure cleanup Jonas · 2026-05-19 · +602 −191

2026-05-18 — 5 direkt-commits

Detaljer
  • 12236b2 P1 head-to-head wins vs AgroVision: KPI tiles, Copilot rules, benchmarking, multi-site Jonas · 2026-05-18 · +11 018 −122
  • 61629c0 fix(P0-D,P0-G): batch finisher columns + slaughter-booking insert id Jonas · 2026-05-18 · +28 −1
  • bde25ac AgriVision: close 6 P0 switch-blocker gaps Jonas · 2026-05-18 · +2 533 −104
  • 7ca9517 AgriVision Jonas · 2026-05-18 · +11 919 −0
  • 877e5cf AgriVision Jonas · 2026-05-18 · +7 897 −1 070

2026-05-17 — 7 direkt-commits

Detaljer
  • 4de12ca UJ-090: annual flock census submit + PDF receipt (sheep + goats, Reg 21/2004 Art 7) Jonas · 2026-05-17 · +1 788 −80
  • 1523589 UJ-089: notifiable disease suspicion + movement standstill (Reg 2016/429 Art 18) Jonas · 2026-05-17 · +3 288 −46
  • e445ecb UJ-088: kill sheet reconciliation page (cattle/pigs/broilers) Jonas · 2026-05-17 · +2 556 −37
  • 3d5bf2b UJ-087: pasture rotation — grazing_ended event + rotation rest tracker + organic-compliance band Jonas · 2026-05-17 · +1 020 −273
  • 97a4db1 UJ-086: calf cohort weekly weighing + DLWG sparkline Jonas · 2026-05-17 · +2 218 −30
  • ce689ab UJ-085: sow weaning + re-service heat-check task (WEI tracking) Jonas · 2026-05-17 · +1 350 −30
  • b3d1fc4 UJ-084: mortality investigation / postmortem cause update (Reg 2016/429 Art 102) Jonas · 2026-05-17 · +1 963 −119

2026-05-16 — 20 direkt-commits

Detaljer
  • ed20758 UJ-083: ovine shearing event (Reg 2016/429 Art 102, Dir 98/58/EC welfare) Jonas · 2026-05-16 · +1 589 −81
  • 4fa0b11 UJ-082: AMU annual submission packet (Reg 2019/6 Art 108) Jonas · 2026-05-16 · +780 −137
  • ca8b9ac User Journeys Jonas · 2026-05-16 · +1 −1
  • 6dc1c15 User Journeys Jonas · 2026-05-16 · +272 −0
  • 8c10546 UJ-081: Mastitis SCC alert triage (Treat / Dry off / Cull / Snooze) Jonas · 2026-05-16 · +1 741 −39
  • 1e7e8fa Return 403 (not 500/400) when X-User-Id header is missing Jonas · 2026-05-16 · +15 −0
  • c7b4c3f UJ-080: Bulk milk tank pickup (tanker collection) Jonas · 2026-05-16 · +1 942 −36
  • 32ac171 Add frontend-preview launch config for Preview MCP UJ verification Jonas · 2026-05-16 · +41 −1
  • 4938e21 User Journeys Jonas · 2026-05-16 · +174 −49
  • ce23b6c User Journeys Jonas · 2026-05-16 · +20 −42
  • 95530e0 User Journeys Jonas · 2026-05-16 · +1 865 −0
  • 6f52b9e User Journeys Jonas · 2026-05-16 · +1 402 −182
  • 14b0816 Yearly Price Revision: replace CLI with siteadmin button Jonas · 2026-05-16 · +439 −120
  • 1899b1d User Journeys Jonas · 2026-05-16 · +248 −34
  • 2c23100 User Journeys Jonas · 2026-05-16 · +24 −1
  • a9bb6a1 Add vaccine lot/batch number for Reg (EU) 2019/6 Art 108 trace-back Jonas · 2026-05-16 · +122 −37
  • 7539bb7 User Journeys Jonas · 2026-05-16 · +493 −0
  • ba2362e User Journeys Jonas · 2026-05-16 · +283 −93
  • ec23343 User Journeys Jonas · 2026-05-16 · +468 −0
  • 383e30d User Journeys Jonas · 2026-05-16 · +380 −203

2026-05-15 — 19 direkt-commits

Detaljer
  • cf7e26f UJ-023: pre-fill organic toggle on add-animal modal for organic farms Jonas · 2026-05-15 · +15 −3
  • 98fff0e Add EventRoutesAuthSpec coverage for the two newly-gated read endpoints Jonas · 2026-05-15 · +187 −1
  • faec00c User Journeys Jonas · 2026-05-15 · +47 −0
  • 94fa329 User Journeys Jonas · 2026-05-15 · +1 755 −250
  • 125031c Fix N+1 query in farm-wide withdrawal calendar Jonas · 2026-05-15 · +17 −6
  • abe54ae Require farmer auth on event-log read endpoints Jonas · 2026-05-15 · +26 −5
  • 537c863 Stamp animal.sold_at on sale movements + filter sold from active list Jonas · 2026-05-15 · +67 −3
  • df10ce8 Add auth spec + tighten error mapping on bulk-import endpoint Jonas · 2026-05-15 · +130 −3
  • 8ba1060 User Journeys Jonas · 2026-05-15 · +555 −281
  • cda9554 Require farmer auth on animal bulk import endpoint Jonas · 2026-05-15 · +4 −1
  • 3bedd9b Add per-animal Reg 2018/848 conversion-period flag (UJ-023) Jonas · 2026-05-15 · +420 −38
  • fe7006a Prevent food-chain re-inclusion via animal update endpoint Jonas · 2026-05-15 · +12 −3
  • af7a5ce User Journeys Jonas · 2026-05-15 · +833 −314
  • 183e1ab User Journeys Jonas · 2026-05-15 · +29 −0
  • 49daa1a Route animal creation through event_log + duplicate-identifier guard Jonas · 2026-05-15 · +78 −10
  • d671562 Add RouteAuth gating to prescription/treatment routes Jonas · 2026-05-15 · +841 −104
  • ecabb84 Gate PrescriptionRoutes and TreatmentRoutes with RouteAuth Jonas · 2026-05-15 · +102 −67
  • 4c4f694 Consolidate service workers: port push handlers into static/sw.js Jonas · 2026-05-15 · +55 −190
  • 39fe43d Add GET /api/users/me handler Jonas · 2026-05-15 · +31 −0

2026-05-14 — 10 direkt-commits

Detaljer
  • ebc6680 Document accept-invite cold-load fix verification Jonas · 2026-05-14 · +3 −0
  • 54dbb90 Hydrate farmStore from server when Dexie is empty Jonas · 2026-05-14 · +76 −6
  • 504c647 Role-gate write CTAs across farm app for ViewOnly users Jonas · 2026-05-14 · +142 −58
  • b0374af User Journeys Jonas · 2026-05-14 · +440 −164
  • 8846ab1 Add registered organic certs section to compliance/organic page Jonas · 2026-05-14 · +733 −30
  • 14f09a0 Fix email_message UUID column type (CHAR(36) -> BINARY(16)) Jonas · 2026-05-14 · +29 −0
  • e66e43e Make projection replay idempotent and per-row fault-tolerant Jonas · 2026-05-14 · +29 −10
  • ea16e21 updates Jonas · 2026-05-14 · +673 −332
  • 8e27699 User Journeys Jonas · 2026-05-14 · +38 010 −0
  • ea6dbe0 User Journeys Jonas · 2026-05-14 · +10 424 −623

2026-05-13 — 1 direkt-commits

Detaljer
  • b2e8502 User Journeys Jonas · 2026-05-13 · +2 656 −23

2026-05-11 — 1 direkt-commits

Detaljer
  • 598c121 Stripe redesign v1 Jonas · 2026-05-11 · +4 594 −375

2026-05-10 — 1 direkt-commits

Detaljer
  • 020b2d8 Stripe redesign v1 Jonas · 2026-05-10 · +3 213 −256

2026-05-09 — 1 direkt-commits

Detaljer
  • c5e4a0b a11y fixed to 97% Jonas · 2026-05-09 · +2 645 −1 799

2026-05-08 — 4 direkt-commits

Detaljer
  • 33a97ee Add SiteFooter, design previews, SEO meta helper; ignore lit-event-calendar-src Jonas · 2026-05-08 · +2 030 −0
  • a3f8264 Dismiss release-notes banner on page visit Jonas · 2026-05-08 · +2 −0
  • b5973ac updates Jonas · 2026-05-08 · +1 438 −1 329
  • 2dfcad6 wip Jonas · 2026-05-08 · +3 624 −6 165

2026-05-07 — 77 direkt-commits

Detaljer
  • 49524e2 wip Jonas · 2026-05-07 · +7 793 −45
  • 8881481 wip Jonas · 2026-05-07 · +1 965 −358
  • 417cc08 FARMBRITE_TODO: refresh evening session log with W8 + final 429 KB article totals Jonas · 2026-05-07 · +2 −1
  • 0792a4e A-15 v8: IT 15→40 + PT 15→40 (50 new KB articles, parity push toward DE level) Jonas · 2026-05-07 · +2 825 −11
  • 04a85b2 FARMBRITE_TODO: log 2026-05-07 evening session — 22 commits, G1 GREEN, FR KB at DE parity Jonas · 2026-05-07 · +34 −1
  • 85792c9 A-15 v7: FR KB to DE parity (~36 more FR articles, total ~86) Jonas · 2026-05-07 · +2 681 −8
  • 917042f A-15 v6: 30 more FR KB articles (FR 20→50, target DE parity) Jonas · 2026-05-07 · +1 861 −5
  • 81c8cf8 A-16 v13: BG/ET/GA/HR/LT/LV/MT/SL fills in scanning+feeding+grazing+shipping (final pass on core in-app shards) Jonas · 2026-05-07 · +1 628 −1 620
  • a545fe8 B-04: Whisper-small upgrade flag (opt-in, ~244 MB, persisted in localStorage) Jonas · 2026-05-07 · +501 −39
  • 408723f A-16 v12: BG/ET/GA/HR/LT/LV/MT/SL fills in prescription/food/crops (~199×8 = ~1592 new translations) Jonas · 2026-05-07 · +1 932 −1 926
  • 498c63f A-18 TAXUD: VAT-rate auto-refresh daemon scaffold (TODO_CONFIG fallback, migration 0194) Jonas · 2026-05-07 · +514 −2
  • 6a35eaa W-02: 8 country I&R setup KB articles (operator/admin docs for go-live) Jonas · 2026-05-07 · +386 −69
  • 68da9fc A-16 v11: BG/ET/GA/HR/LT/LV/MT/SL fills in compliance (~92×8 = 736 new translations) Jonas · 2026-05-07 · +1 017 −1 017
  • f26ba70 SL: outreach kit (pitches + cold-email templates + objection cheatsheet + 1-pager + roundtable agenda) Jonas · 2026-05-07 · +260 −2
  • 7d4c11e A-15 v5: enable FR + IT + PT in help-KB runtime (50 previously-on-disk articles now served) Jonas · 2026-05-07 · +580 −7
  • 75992f1 A-15 v4: 30 KB articles in IT + PT (15 each across getting-started/compliance/i-and-r/farm-shop/billing) Jonas · 2026-05-07 · +1 129 −0
  • 4955aef A-16 v10: BG/ET/GA/HR/LT/LV/MT/SL fills in vaccination/mortality/cleaning (~105×8 = ~840 new translations) Jonas · 2026-05-07 · +966 −963
  • fe7667d G4 + G5: UX + performance audits (scaffold/estimates landed; real lab + Lighthouse measurements pending) Jonas · 2026-05-07 · +236 −2
  • 5ce1539 G3: differentiator audit (e-shop parity matrix on speed/VAT/traceability + verdict) Jonas · 2026-05-07 · +142 −1
  • 35d1c0a S-60/S-61: organic-cert authority verification router scaffold (5 country clients, migration 0193) Jonas · 2026-05-07 · +755 −2
  • 24f60c3 M-02: static CAP-vs-Farmbrite comparison page + recording script Jonas · 2026-05-07 · +1 134 −30
  • bdd556a G1: feature coverage audit (Farmbrite parity matrix, defensible omissions, beyond-Farmbrite list) Jonas · 2026-05-07 · +137 −1
  • 8c0b7e5 A-16 v9: BG/ET/GA/HR/LT/LV/MT/SL fills in customReports/dashboards/crm shards (~116 keys × 8 = ~928 new translations) Jonas · 2026-05-07 · +1 065 −1 062
  • 017ce30 W-02: UK BCMS client scaffold (TODO_CONFIG live-stub) — all 8 EU/UK countries wired Jonas · 2026-05-07 · +297 −15
  • 485af25 SL-12: target list of 60 EU co-ops, breed societies, integrators Jonas · 2026-05-07 · +83 −1
  • 276688d C-01: 12-journey benchmark harness scaffold (Playwright, click+time instrumentation) Jonas · 2026-05-07 · +356 −1
  • 4bdc7b0 SB-09: Stripe webhook signature verification audit + extra test cases Jonas · 2026-05-07 · +16 −2
  • 5da5536 FARMBRITE_TODO: log 2026-05-07 daytime session — 32 commits, 13 audit-closes Jonas · 2026-05-07 · +37 −40
  • 6cf3865 M-07 + S-70 + S-73: press release + setup-demo placeholder + food-press kit (migration 0192) Jonas · 2026-05-07 · +1 513 −0
  • f4df0a0 M-03: EU livestock compliance white paper + public landing Jonas · 2026-05-07 · +2 721 −60
  • 6058cb9 W-02: PL IRZplus client scaffold (TODO_CONFIG live-stub) Jonas · 2026-05-07 · +388 −74
  • 72e910b AUDIT-CLOSE: 25 already-shipped items across §6/§7/§9 Jonas · 2026-05-07 · +22 −22
  • de28a12 D-11 + B-01: launch-readiness dashboard + e-shop pilot runbook & test harness (migration 0190) Jonas · 2026-05-07 · +3 332 −2
  • 7197e4f O-01 + O-02 + O-03: support hiring plan + Front/Zendesk webhook scaffold + SLA tracker (migration 0189) Jonas · 2026-05-07 · +1 349 −2
  • 968c5c9 L-01 + M-08 + M-25: prelaunch legal status + influencer program + podcast scaffold (migration 0191) Jonas · 2026-05-07 · +4 214 −40
  • adc2cd6 M-26 + M-28: brand book v1 + 12 farm icons Jonas · 2026-05-07 · +1 834 −7
  • b1a6f1b D-01: ToS/Privacy/DPA DE/FR/NL/ES drafts (legal-counsel review pending) Jonas · 2026-05-07 · +2 489 −1 377
  • 248ef10 A-15 v3: +22 DE (DE→86 parity) + 20 FR articles (FR launch) Jonas · 2026-05-07 · +2 449 −7
  • 9b018e0 M-16: SEO long-tail blog scaffold + 30-article seed Jonas · 2026-05-07 · +1 542 −1
  • 4392c44 E-04: MySQL FULLTEXT storefront search + decision doc (migration 0188) Jonas · 2026-05-07 · +554 −8
  • 2e87def C-04 + fix: cold-start perf scaffold + MailchimpRoutesSpec stub Jonas · 2026-05-07 · +688 −1
  • 12b84b8 W-02: I&R country status transparency page Jonas · 2026-05-07 · +1 723 −73
  • 56c544b L-02 + L-03 + D-05: GDPR DPO + OSS registration + farmer VAT guide page Jonas · 2026-05-07 · +836 −5
  • 3d0b95a M-23 + M-24: marketing newsletter + creator outreach scaffold (migration 0185) Jonas · 2026-05-07 · +1 080 −1
  • 533973a F-30 v5 + F-26/F-27 audit: bidirectional Mailchimp polish + integrations index (migration 0186) Jonas · 2026-05-07 · +993 −26
  • eac66af SL-09 + O-04 + O-05: white-glove auto-flag + in-app help drawer + community forum starter (migration 0187) Jonas · 2026-05-07 · +1 551 −46
  • 2a2e45a A-16 v8d: movement shard for BG/ET/GA/HR/LT/LV/MT/SL Jonas · 2026-05-07 · +2 229 −717
  • b08b134 A-16 v8c: employees shard for BG/ET/GA/HR/LT/LV/MT/SL Jonas · 2026-05-07 · +368 −368
  • fcf3aeb chore: rebuild help-index + i18n outputs from prior commits Jonas · 2026-05-07 · +684 −13
  • b53fdd5 SB-02 + SB-05 + S-56: hosted Pricing Table embed + Enlantis OSS export + IOSS support (migration 0184) Jonas · 2026-05-07 · +850 −12
  • d3e6beb SL-02 + L-06 + M-29: signup speed audit + trademark filing detail + press kit page Jonas · 2026-05-07 · +406 −49
  • 63d16ca E-01 + E-12 + E-13 + E-14: bounded-context, secrets, backup, migration runbooks Jonas · 2026-05-07 · +804 −7
  • 98bad9a S-62 + SB-15 + SB-20: Connect chargesEnabled gate + co-op invoicing + multi-currency Prices (migrations 0182, 0183) Jonas · 2026-05-07 · +825 −4
  • e2491a3 E-09 + E-07 frontend wiring: cart abandonment capture/recovery + buyer SMS pref UI Jonas · 2026-05-07 · +445 −6
  • 8186023 M-04 v2: country one-pagers for PL/IT/ES/FI/AT/BE Jonas · 2026-05-07 · +3 148 −64
  • e5f5a2f A-15: +30 DE knowledge-base articles (DE coverage 34→64) Jonas · 2026-05-07 · +1 348 −0
  • 31b8a32 A-16 v7 (partial): deep in-app i18n start for BG/ET/GA/HR/LT/LV/MT/SL Jonas · 2026-05-07 · +2 196 −2 196
  • 33f38e7 E-09 + E-07: abandoned-cart recovery worker + notification-channel scaffold (migrations 0180, 0181) Jonas · 2026-05-07 · +1 604 −788
  • d56c475 E-05: image upload pipeline + derivatives (migration 0179) Jonas · 2026-05-07 · +290 −38
  • f00a615 AUDIT-CLOSE: Stripe payment methods + tax + subscription items (S-34..S-58, SB-01..SB-29) Jonas · 2026-05-07 · +34 −28
  • 3a46e7d FARMBRITE_TODO: log overnight session — 27 commits, ~45 IDs closed Jonas · 2026-05-07 · +43 −1
  • 4b31750 S-60/S-61: organic claim + welfare claim verification (Reg 2018/848 etc.) — migration 0178 Jonas · 2026-05-07 · +2 582 −21
  • c99c74c A-16 v6: BG/ET/GA/HR/LT/LV/MT/SL on 4 onboarding shards (~8 locales × 4 shards) Jonas · 2026-05-07 · +2 176 −1 855
  • d57869b chore: fix wave-5 test failures + audit-close shipped items Jonas · 2026-05-07 · +123 −31
  • b15ef66 S-64: profanity + illegal-product filter on product copy + farm slug — migration 0177 Jonas · 2026-05-07 · +1 713 −3
  • fec1f30 S-63: Stripe Identity for high-risk farmer verification — migration 0176 Jonas · 2026-05-07 · +3 232 −353
  • 136aeaf SB-16/17/18: Stripe add-ons + tax codes per Price + reverse-charge UX — migration 0172 Jonas · 2026-05-07 · +1 190 −7
  • 1189af3 SB-26/SB-27: authority co-funding + white-label partner billing — migration 0174 Jonas · 2026-05-07 · +1 877 −0
  • 357b966 SB-19: country-compliant supplemental invoice PDFs (SE/DE/FR + generic EU) — migration 0173 Jonas · 2026-05-07 · +1 664 −60
  • 631f677 F-19 v2: custom fields for field/batch/task/employee entities — migration 0175 Jonas · 2026-05-07 · +1 803 −72
  • c1583c2 chore: post-wave compile-fix + test-stub sweep Jonas · 2026-05-07 · +169 −98
  • 35949d5 A-16 v5: PT/EL/RO/HU/CS/SK for 6 onboarding shards (~6 locales × 6 shards) Jonas · 2026-05-07 · +575 −574
  • 916ad2f SB-14: advisor Stripe seats with quantity-based volume pricing — migration 0171 Jonas · 2026-05-07 · +2 796 −920
  • f857ee4 SB-11/SB-12: dunning state machine + grace period + read-only premium gating — migration 0170 Jonas · 2026-05-07 · +588 −86
  • b22e73c SB-22/23/24: 3-year price-lock + HICP cap + alpha-pilot grandfather — migration 0167 Jonas · 2026-05-07 · +143 −3
  • dd7fd1e SL-03: in-app one-click plan swap (annual↔monthly + tier upgrade) Jonas · 2026-05-07 · +2 202 −5
  • e6b0179 L-05: marketplace AML/risk admin dashboard — migration 0169 Jonas · 2026-05-07 · +1 713 −0

2026-05-06 — 97 direkt-commits

Detaljer
  • e65449d SB-25: VAT ID capture + VIES validation + Stripe TaxId push — migration 0168 Jonas · 2026-05-06 · +3 109 −86
  • 1b0cd5f S-08: add migration 0163, ShopProductPhotoRepository, test stubs (follow-up) Jonas · 2026-05-06 · +524 −8
  • 4aeb6f3 S-08: shop product photo gallery — migration 0163, ≤12 per product, owner CRUD + reorder Jonas · 2026-05-06 · +620 −57
  • 7d9e618 docs: tick W-09 and A-16 v4 in FARMBRITE_TODO Jonas · 2026-05-06 · +3 −2
  • 7d34982 A-16 v4 / W-09: deep i18n PL/IT/DK/FI for 14 in-app shards (~770 keys × 4 locales) Jonas · 2026-05-06 · +5 981 −4 363
  • 8f25909 S-11: farm-gate pickup config + checkout option — migration 0166 Jonas · 2026-05-06 · +826 −9
  • 7a21e80 F-30 v4: Mailchimp webhook bidirectional sync — migration 0165 Jonas · 2026-05-06 · +1 282 −25
  • f6b82ed S-18: wholesale price tier — migration 0164, buyer-verified B2B pricing with min-qty Jonas · 2026-05-06 · +4 006 −2 142
  • 79e4ed2 docs: update FARMBRITE_TODO A-16 row with v3 coverage note (2026-05-06) Jonas · 2026-05-06 · +2 −1
  • 7d911fe A-16 v3: deep i18n DE/ES/FR/NL — customReports, dashboards, crm, employees, movement Jonas · 2026-05-06 · +423 −416
  • 046cef3 FARMBRITE_TODO audit: tick shipped items (W-04/05/06/07/10, A-18/20, D-02/09, O-09/10, M-09) Jonas · 2026-05-06 · +13 −12
  • 8241dfd FARMBRITE_TODO: log evening session — 18 commits, 21 IDs closed Jonas · 2026-05-06 · +24 −2
  • 8b1088d SB-09/SB-10: Stripe webhook tier-projection + invoice ledger Jonas · 2026-05-06 · +965 −23
  • 5d7c478 O-08: wire full NPS 30/90/365-day milestone survey — migration 0161, farm route, admin comments Jonas · 2026-05-06 · +220 −3
  • d730f76 L-04: full marketplace + consumer T&C pages (v1.0, 2026-05-06) Jonas · 2026-05-06 · +308 −98
  • b3cd0a8 D-06: public insurance disclosure page at /legal/insurance Jonas · 2026-05-06 · +2 708 −778
  • fcabf88 O-06: release notes CMS — migration 0160, admin CRUD, /release-notes page, RSS feed, dashboard card Jonas · 2026-05-06 · +1 504 −39
  • 024c8ce L-07: schema doc, data-portability page, trust card, i18n keys, gap TODOs Jonas · 2026-05-06 · +1 693 −57
  • 80c126a L-07: data portability schema doc, page overhaul, trust card, and gap audit Jonas · 2026-05-06 · +1 −1
  • 7e7469b O-07: incident comms playbook + in-app system alert banner Jonas · 2026-05-06 · +2 447 −33
  • 8c8853c W-01: audit all 8 G2 EU pillar PDFs, mark [x], add compliance-index pillar section Jonas · 2026-05-06 · +295 −1
  • 9f5fc5e W-03/M-12: barn-mode live demo widget + i18n shards + offline-benchmark complete Jonas · 2026-05-06 · +409 −2
  • 761ac46 D-03: public security & compliance roadmap page at /legal/security-roadmap Jonas · 2026-05-06 · +1 538 −260
  • fc315bb SB-13: cancellation flow — migration 0159 + i18n _order fix Jonas · 2026-05-06 · +3 565 −3 226
  • 0210c4f D-04: public PCI-DSS scope page at /legal/pci-scope confirming SAQ-A Jonas · 2026-05-06 · +3 999 −262
  • 1cfd5ea D-02: publish public DPIA summary (/legal/dpia) and upgrade sub-processors page Jonas · 2026-05-06 · +2 636 −56
  • 82f9fc8 W-04: add /audit-log inspector trust page with mock event log table and i18n Jonas · 2026-05-06 · +70 −2
  • 95ee899 D-09: disaster recovery runbook + quarterly drill plan (RPO 15 min / RTO 4 h) Jonas · 2026-05-06 · +1 052 −0
  • f831217 W-05: promote PricingComparisonWidget to full 4-col/10-row on home page Jonas · 2026-05-06 · +1 622 −30
  • a913701 FARMBRITE_TODO: log 2026-05-06 evening cleanup pass Jonas · 2026-05-06 · +5 −1
  • 5435542 chore(ts): clean pre-existing TypeScript debt across 74 files (254 → 5 errors) Jonas · 2026-05-06 · +363 −324
  • 0e1eb84 docs: archive root deploy notes to done/ and refresh CLAUDE.md Jonas · 2026-05-06 · +2 −2
  • 0bc061b wip Jonas · 2026-05-06 · +83 −63
  • 3294c72 FARMBRITE_TODO: append A-15 close + D-08 status page (30 total) Jonas · 2026-05-06 · +7 −1
  • 1fa230f D-08: full public status page with per-service probes, incident log, RSS feed Jonas · 2026-05-06 · +2 331 −142
  • f23cd66 A-15: add final 3 KB articles (EN+SV+DE) to reach 206/200 target Jonas · 2026-05-06 · +621 −3
  • 7317a9e FARMBRITE_TODO: end-of-session log for 2026-05-06 daytime (27 commits) Jonas · 2026-05-06 · +66 −1
  • 90d54fc S-58/S-59: farmer invoice PDF download + food-info auto-fill from source event Jonas · 2026-05-06 · +1 031 −40
  • afcaf31 M-01: eu-compliance landing page — sync shard + homepage link Jonas · 2026-05-06 · +9 −3
  • 0f8a324 S-67: GDPR/ePrivacy cookie banner + analytics consent gate Jonas · 2026-05-06 · +139 −69
  • a09c1c2 S-72 + S-70: featured farm of month + farm-shop setup flow Jonas · 2026-05-06 · +2 268 −116
  • 6dfd466 S-71 + S-66: public farmers directory with organic filter and consumer withdrawal rights Jonas · 2026-05-06 · +501 −42
  • 2bc8dbe S-65: restricted products — geo-filtering, checkout enforcement, farmer warnings Jonas · 2026-05-06 · +1 704 −48
  • 0a0de93 A-16 v2: 616 keys × 4 locales (DE/ES/FR/NL) for core in-app surfaces Jonas · 2026-05-06 · +1 315 −1 314
  • 5cebb92 M-13/S-69: pricing comparison shard + farm-shop 4-column comparison table Jonas · 2026-05-06 · +563 −115
  • ac3a97d C-05: WCAG 2.2 AA accessibility audit pass — fix 7 blocking issues Jonas · 2026-05-06 · +371 −8
  • 7108b2b F-30 v3: CRM-light per-farm + Mailchimp sync (migration 0154) Jonas · 2026-05-06 · +2 549 −76
  • c8d8fee C-08: first-time onboarding flow — close gaps, tick done Jonas · 2026-05-06 · +828 −286
  • 7f9e906 S-20: storefront analytics — visits, conversion funnel, top products, abandoned carts, UTM/channel attribution Jonas · 2026-05-06 · +1 387 −99
  • dca7885 S-07: product variants — position, imageUrl, ZIO spec, i18n Jonas · 2026-05-06 · +503 −36
  • c14bbb6 S-06: storefront product categories, search, and filters Jonas · 2026-05-06 · +1 161 −60
  • 3702fff S-29: inventory-aware shop stock — migration 0150, item-level stock check + movement audit Jonas · 2026-05-06 · +831 −65
  • dfba841 S-17: member-only prices, gift wrapping, discount-code auth spec Jonas · 2026-05-06 · +916 −83
  • e539f10 A-18 follow-up: live ECB FX feed + per-country EU VAT at checkout Jonas · 2026-05-06 · +1 113 −50
  • 8f23bb6 B-04 follow-up: Whisper offline fallback for voice entry in the barn Jonas · 2026-05-06 · +2 514 −39
  • 10a4097 A-15: +74 KB articles EN+SV across 11 categories, reaching 197 total Jonas · 2026-05-06 · +5 530 −67
  • fc70305 A-06 / F-11: generic "other species" record type — test spec + i18n shard Jonas · 2026-05-06 · +592 −35
  • f1d3acf A-04: horse/equine parity — FCE idempotency spec + DE/ES/FR/NL headline i18n Jonas · 2026-05-06 · +350 −58
  • 4098182 A-03: broiler parity — catch date, vaccination quick-link, DE/ES/FR/NL headline keys Jonas · 2026-05-06 · +332 −95
  • 74a15ed A-02: laying hens to feature parity (egg grading, i18n, links) Jonas · 2026-05-06 · +1 220 −70
  • f6d3c36 A-01: pig parity audit — mark done + add DE/ES/FR/NL headline keys Jonas · 2026-05-06 · +184 −96
  • 27acfa3 W-08: add hardware-catalog smoke test; verify page reads correct data file Jonas · 2026-05-06 · +99 −0
  • 5ea2275 S-04: verify translation review link end-to-end (no code changes needed) Jonas · 2026-05-06 · +2 −2
  • 582a91e C-03: land perf wins — bundle gate passing, build unblocked Jonas · 2026-05-06 · +584 −437
  • ac6e28d wip Jonas · 2026-05-06 · +6 −0
  • 7705230 wip Jonas · 2026-05-06 · +14 −4
  • 7eaed48 FARMBRITE_TODO: end-of-session log for 2026-05-06 overnight (28 commits) Jonas · 2026-05-06 · +28 −12
  • 59367e5 C-03: perf budget — Lighthouse ≥95 + bundle ≤200 KB CI Jonas · 2026-05-06 · +201 −1
  • f6db4b4 S-25/S-26/S-27/S-28: one-tap "List as product" for slaughter batch, honey harvest, egg collection, live animal Jonas · 2026-05-06 · +1 497 −70
  • 374f969 S-30: order pickup auto-creates farmer task Jonas · 2026-05-06 · +650 −183
  • b1f4b9b S-13/14: delivery slots + CSA box scheme Jonas · 2026-05-06 · +3 023 −47
  • 35744c8 S-15: storefront magic-link sign-in + account pages Jonas · 2026-05-06 · +1 463 −75
  • be2e21f FARMBRITE_TODO: status tracker for 2026-05-06 overnight session (21 commits so far) Jonas · 2026-05-06 · +46 −7
  • 0c05a1e S-15/16/19: buyer accounts, reviews, newsletter signup Jonas · 2026-05-06 · +1 331 −79
  • 1ee14a3 S-22: Meta Commerce Manager product feed (public XML + CSV) Jonas · 2026-05-06 · +999 −53
  • c90b04e S-04: per-product translation review UI Jonas · 2026-05-06 · +3 631 −1 244
  • 57ba412 fix: replace nonexistent $lib/auth/userStore with localStorage pattern Jonas · 2026-05-06 · +3 −6
  • b334556 A-15: knowledge-base scaffolding + 30-article EN/SV/DE seed Jonas · 2026-05-06 · +6 031 −1 235
  • 74b1bb4 F-34: custom dashboards with 7 widget kinds (list-based ordering) Jonas · 2026-05-06 · +2 037 −6
  • 895750a W-08: hardware compatibility page (37 products) Jonas · 2026-05-06 · +571 −1
  • ee6865a C-07: Big-finger mode (56px hit targets, sunlight-contrast) Jonas · 2026-05-06 · +985 −36
  • 90aadf8 F-20: custom report builder (form-based MVP) Jonas · 2026-05-06 · +3 287 −102
  • 7620e3e F-27: Zapier connector scaffold (20 triggers, 10 actions) Jonas · 2026-05-06 · +1 859 −1
  • 85d4b4d C-09 + C-10: EmptyState + ErrorMessage components; convert 8 high-traffic pages Jonas · 2026-05-06 · +1 396 −130
  • e3bf66d B-07: insurance partner program scaffolding (commission-only) Jonas · 2026-05-06 · +1 052 −2
  • 07c0a3d A-05: rabbits / alpacas / llamas to working state Jonas · 2026-05-06 · +828 −37
  • 5c99baa A-07: crops-lite module + 253-entry crop catalog (Eurostat CN8 where confident) Jonas · 2026-05-06 · +1 605 −32
  • 667d044 S-21/23/24: embeddable widget, link-in-bio, QR generator Jonas · 2026-05-06 · +1 690 −74
  • d57c618 B-05: EUR-Lex regulation Q&A (Sonnet 4.6, hand-curated corpus) Jonas · 2026-05-06 · +1 618 −35
  • 6c7f56f B-04: voice entry (Web Speech API → Claude Sonnet 4.6 tool-use extraction) Jonas · 2026-05-06 · +716 −167
  • a72289f B-03: photo-triage v1 (lameness/FAMACHA/hoof) — Haiku 4.5 Jonas · 2026-05-06 · +582 −41
  • 8f85f50 A-16: native DE/ES/FR/NL translations for public marketing surfaces Jonas · 2026-05-06 · +1 570 −782
  • 7482142 A-14: audit confirms Capacitor scaffolding fully committed Jonas · 2026-05-06 · +14 −3
  • cb7bd8d A-20: public API docs portal at /developers Jonas · 2026-05-06 · +306 −0
  • 4d7c395 A-18: multi-currency display (EUR/SEK/DKK/PLN/NOK/GBP) Jonas · 2026-05-06 · +245 −28
  • f6c3b76 wip Jonas · 2026-05-06 · +0 −1
  • 24b6cfb wip Jonas · 2026-05-06 · +14 929 −2 700

2026-05-05 — 18 direkt-commits

Detaljer
  • 89629a3 FARMBRITE_TODO: update status tracker for 21:00-22:50 session (5 commits shipped) Jonas · 2026-05-05 · +8 −4
  • 5f3d08b i18n: fix all 6 check-i18n.mjs failures (5 unknown dynamic prefixes + shop.risk.unknown) Jonas · 2026-05-05 · +27 −1
  • 83c836e SL-04: email open-rate pixel tracking (migration 0132) Jonas · 2026-05-05 · +226 −3
  • 99827aa FARMBRITE_TODO: check off A-08/09/10/11/12/13/17 + B-02 + SL-01/04/05/06; update status tracker Jonas · 2026-05-05 · +23 −13
  • 39e50f3 F-19/A-11: route custom-field value writes through event_log (custom_field_set) Jonas · 2026-05-05 · +202 −15
  • 181eaac SL-05: hide UpgradeNudge for paid-tier farms by fetching billing status Jonas · 2026-05-05 · +87 −5
  • 8fcfea1 updates Jonas · 2026-05-05 · +403 −28
  • 9495d19 FARMBRITE_TODO: roll up 2026-05-05 evening session (10 commits, G2 green) Jonas · 2026-05-05 · +78 −1
  • e608efa SL-05 + SB-07: contextual upgrade nudges + 30-day Pro trial via Stripe Jonas · 2026-05-05 · +844 −3
  • 282772f F-19/A-11: custom fields per animal (lite) Jonas · 2026-05-05 · +4 −4
  • 5c1f298 A-19: audit + ship missing one-click compliance report PDFs Jonas · 2026-05-05 · +299 −7
  • cb9ebc0 SL-04: 8-email country-specific onboarding sequence Jonas · 2026-05-05 · +902 −270
  • 47e7bd9 SB-13: subscription cancellation flow with exit survey + reactivate Jonas · 2026-05-05 · +1 211 −33
  • ea1f95a polish: og PNG + PGP runbook + sitemap covering /country/* Jonas · 2026-05-05 · +211 −28
  • 05df2a7 B-02: AI Copilot v1 — rule-based nudges + Today view + feedback loop Jonas · 2026-05-05 · +1 431 −31
  • 0ceee71 F-26: self-service webhook management + secret rotation Jonas · 2026-05-05 · +117 −4
  • d5c88e5 SL-01: farm-size tier calculator on /pricing Jonas · 2026-05-05 · +1 031 −164
  • 0a23fad M-04: country one-pagers for SE/DE/IE/NL/FR/DK Jonas · 2026-05-05 · +3 449 −31

2026-05-04 — 87 direkt-commits

Detaljer
  • ea51364 updates Jonas · 2026-05-04 · +1 697 −1 783
  • 9a78ca6 DEC-07: flag /coop-partners white-label as not-yet-GA (dev-mode only) Jonas · 2026-05-04 · +37 −1
  • 4fabbc7 DEC-08: new-farmer promo — first year free on cheapest plan, 50% off elsewhere Jonas · 2026-05-04 · +660 −49
  • b73aeb2 FARMBRITE_TODO: record DEC-01..DEC-08 decisions (2026-05-04) Jonas · 2026-05-04 · +10 −10
  • 42fffaa FARMBRITE_TODO: roll up prior session logs (2159 → 708 lines) Jonas · 2026-05-04 · +6 −1 457
  • 92f5856 updates Jonas · 2026-05-04 · +9 −9
  • 3e72047 FARMBRITE_TODO: final tracker bump — 29 commits, 1693/0/62 baseline Jonas · 2026-05-04 · +9 −2
  • 089bd0e admin: quick-stats row on home + complaints CSV export Jonas · 2026-05-04 · +1 044 −31
  • fe6cdc0 admin: root-admin home dashboard linking every root-admin tool Jonas · 2026-05-04 · +1 101 −213
  • a00ad5a FARMBRITE_TODO: tracker bump for 3 late additions (vet/coop partners, data portability, trademark prep, changelog) Jonas · 2026-05-04 · +9 −2
  • f58e417 L-06 + security.txt verify + /changelog: trademark prep + static-serve test + public changelog page Jonas · 2026-05-04 · +975 −40
  • fd89bb4 M-21 + SL-12 + L-07: vet/co-op partner pages + data portability page Jonas · 2026-05-04 · +3 696 −36
  • 7d8b75d FARMBRITE_TODO — 2026-05-04 daytime session summary (23 commits) Jonas · 2026-05-04 · +99 −23
  • 527de0c F-43 + SB-03: pricing-comparison freshness tracker + Stripe portal link Jonas · 2026-05-04 · +1 053 −37
  • 78f7e36 Sitemap: add /security/disclosure + /university-partners Jonas · 2026-05-04 · +2 −0
  • 40af981 D-04 + security.txt + M-22: PCI scope doc + vuln disclosure + university partners page Jonas · 2026-05-04 · +2 681 −36
  • 143017c M-09 + O-05: case study scaffolds + community page + feedback→complaint loop Jonas · 2026-05-04 · +3 774 −33
  • 38b7356 Legal pages: sub-processors + security-roadmap + withdrawal-rights detail Jonas · 2026-05-04 · +4 240 −52
  • 15ea4bc O-10: user complaints logging + weekly top-3 digest (migration 0129) Jonas · 2026-05-04 · +1 699 −34
  • 2c3d859 W-04 + W-06 + L-04: regulator trust + EU data residency + draft marketplace/consumer T&C Jonas · 2026-05-04 · +4 240 −37
  • bbaff13 Fix admin churn page build: gate {@const} with {#if summary} Jonas · 2026-05-04 · +37 −21
  • 82f621b M-14 / M-29 / O-07 / D-09: price commitment + press kit + incident & DR runbooks Jonas · 2026-05-04 · +2 125 −32
  • b4eacf5 M-10: referral program (3 months free both sides, migration 0128) Jonas · 2026-05-04 · +2 310 −37
  • d0a965a i18n: repair build script — stub-fill missing shards, preserve direct-edit JSON keys Jonas · 2026-05-04 · +8 325 −796
  • 537de9a F-30 v2: real Mailchimp API client (verify, addMember, getLists) Jonas · 2026-05-04 · +930 −76
  • 956a851 O-09: churn root-cause logging on cancel/downgrade/delete (migration 0126) Jonas · 2026-05-04 · +1 174 −21
  • 4c73980 O-08: NPS survey at 30/90/365 days (migration 0125) Jonas · 2026-05-04 · +1 565 −4
  • 973ad23 M-13: pricing comparison widget on homepage (Enlantis vs Farmbrite vs Shopify) Jonas · 2026-05-04 · +507 −2
  • 4310334 M-12: public /offline-benchmark page (measured offline behaviour) Jonas · 2026-05-04 · +1 134 −10
  • 9ae5c59 M-11: public /barn-mode page (offline-first marketing) Jonas · 2026-05-04 · +894 −73
  • 7357d6a A-17: BDNI (FR) + NL I&R + CHR (DK) clients lifted to TODO_CONFIG live-stub Jonas · 2026-05-04 · +856 −29
  • 007fead E-06 v2: DKIM rotation tooling (migration 0124) Jonas · 2026-05-04 · +1 218 −3
  • 9577458 E-06 v2: MJML preprocessing for email templates + build script Jonas · 2026-05-04 · +868 −18
  • 4e21a42 S-68: admin backfill endpoint for legacy order risk_level Jonas · 2026-05-04 · +541 −9
  • a97a02e S-04: auto-translate coverage admin panel + bulk-translate-missing endpoint Jonas · 2026-05-04 · +655 −0
  • 0a08428 SB-28: admin resolve UI for reconciliation discrepancies (migration 0123) Jonas · 2026-05-04 · +1 221 −6
  • 5297186 FARMBRITE_TODO: open 2026-05-04 daytime session with 12-item queue Jonas · 2026-05-04 · +25 −1
  • 21438d1 wip Jonas · 2026-05-04 · +250 −47
  • dc152be FARMBRITE_TODO — overnight orchestrator session summary (52 commits) Jonas · 2026-05-04 · +304 −1
  • 04dd55e Polish: privacy footer + Built with Enlantis link on storefront Jonas · 2026-05-04 · +53 −28
  • e444286 Polish: noscript + version endpoint + CLAUDE.md migration line Jonas · 2026-05-04 · +197 −30
  • f4c9027 Polish: farmers directory category filter + checkout success message + og-default placeholder Jonas · 2026-05-04 · +77 −38
  • 3df51c6 Polish: image sitemap + <picture> lazy load + html lang attr Jonas · 2026-05-04 · +33 −11
  • 7bfa516 Polish: titles + robots.txt + RSS link on /farm-shop Jonas · 2026-05-04 · +121 −28
  • 819b77c Polish: newsletter CSV + storefront OG + What's new nav link Jonas · 2026-05-04 · +388 −42
  • 40b7c3a Polish: customer order timeline + dashboard today rollup + a11y label audit Jonas · 2026-05-04 · +564 −33
  • e429337 Polish: shop products CSV + storefront favicon + home vs-Farmbrite banner Jonas · 2026-05-04 · +240 −41
  • 04a786f Polish: sitemap + duplicate product + a11y skip link Jonas · 2026-05-04 · +249 −30
  • 277508d Polish: storefront/checkout analytics + newsletter pages + shop orders CSV (S-S1..S-S4) Jonas · 2026-05-04 · +1 039 −37
  • d0eaf00 S-69 — public /farm-shop landing page with vs-Farmbrite/Shopify comparison Jonas · 2026-05-04 · +496 −28
  • ef48015 S-72 — featured-farmer card on homepage + admin toggle Jonas · 2026-05-04 · +420 −29
  • 32cae9f SB-25 — capture + validate EU VAT ID at storefront setup Jonas · 2026-05-04 · +371 −32
  • e77af07 S-30 — auto-create pickup task on pickup-fulfilment order Jonas · 2026-05-04 · +177 −23
  • e61341d SB-28 — nightly Stripe balance-transaction reconciliation + discrepancy report Jonas · 2026-05-04 · +639 −2
  • 62c0a12 C-07 — global outdoor mode (56px hit targets, high contrast, motion auto-detect) Jonas · 2026-05-04 · +606 −28
  • 4db8f9f S-63 — Stripe Identity verification for storefronts Jonas · 2026-05-04 · +657 −29
  • 5c31127 SB-23 — HICP-indexed annual Stripe Price rollover (3% cap) Jonas · 2026-05-04 · +499 −1
  • 019410a S-28 — live-animal sale auto movement event + I&R submission Jonas · 2026-05-04 · +577 −31
  • 589a003 F-10 / A-06 — turkey + duck + goose batch production metrics Jonas · 2026-05-04 · +2 336 −32
  • 7d4d37e S-68 — Stripe Radar risk score on shop orders + admin badge Jonas · 2026-05-04 · +483 −30
  • ee7558e D-08 — public status page + uptime snapshot daemon Jonas · 2026-05-04 · +561 −30
  • 077f54e S-05 — multi-currency display on storefront (EUR/SEK/DKK/PLN/NOK/GBP) Jonas · 2026-05-04 · +199 −36
  • eaff4e3 SB-22 — 3-year price lock on stripe_subscription (pinned_price_id) Jonas · 2026-05-04 · +319 −9
  • 99c5530 S-48 — cash-on-pickup payment path (no Stripe) Jonas · 2026-05-04 · +283 −46
  • 8446c73 S-29 — inventory ↔ shop variant qty sync Jonas · 2026-05-04 · +289 −14
  • 2e21898 S-04 — product copy auto-translate via Claude Haiku + Accept-Language serving Jonas · 2026-05-04 · +712 −33
  • c649dd3 C-10 — error-message audit on top 12 frontend surfaces Jonas · 2026-05-04 · +195 −68
  • c0d4145 S-26/S-27 — promote honey harvest + egg collection to draft shop product Jonas · 2026-05-04 · +368 −5
  • 1777906 S-57 — per-product Stripe Tax codes (EU reduced-rate food VAT) Jonas · 2026-05-04 · +326 −29
  • a17a172 S-46 — Stripe disputes intake + evidence-response workflow Jonas · 2026-05-04 · +1 502 −32
  • 7c0c5af A-05 — rabbit + alpaca + llama working state (no I&R) Jonas · 2026-05-04 · +125 −28
  • 27fb386 S-03 — storefront branding (logo, hero, tagline, color, font) Jonas · 2026-05-04 · +437 −35
  • eb24741 S-06 — storefront product categories + search + filter chips Jonas · 2026-05-04 · +806 −33
  • 2126bae A-01 — pig batch production metrics (litter size, pre-weaning mortality, FCI) Jonas · 2026-05-04 · +863 −29
  • e7b7ecd A-03 — broiler production metrics (FCR, survival, EPI) Jonas · 2026-05-04 · +1 425 −29
  • 7990291 A-02 — laying-hen production metrics (eggs + mortality + hen-day %) Jonas · 2026-05-04 · +1 948 −30
  • 6938af2 S-44 — Stripe Invoicing for B2B (net-14/net-30 terms) Jonas · 2026-05-04 · +773 −28
  • 27a67eb A-04 — equine identification + FCE declaration PDF (Reg 2015/262) Jonas · 2026-05-04 · +1 254 −29
  • 61e3dd9 S-64/S-65 — profanity filter + restricted-product geo-gate Jonas · 2026-05-04 · +597 −29
  • 4a4c44c S-02 — schema.org Product JSON-LD + OpenGraph on public product page Jonas · 2026-05-04 · +119 −52
  • e183383 S-47 — refund flow + Stripe refunds API + auto-restock Jonas · 2026-05-04 · +1 694 −33
  • aaac277 S-13 — storefront delivery slot publishing Jonas · 2026-05-04 · +851 −29
  • 0ed886b S-66 — EU 14-day right of withdrawal (Dir 2011/83/EU) Jonas · 2026-05-04 · +1 678 −30
  • 1768e34 C-09 v2 — EmptyState sweep on remaining list screens Jonas · 2026-05-04 · +2 577 −32
  • f4c334d C-03 — re-baseline frontend perf-budget after lazy-i18n shrink Jonas · 2026-05-04 · +33 −231
  • d631724 E-05 v2 — TwelveMonkeys WebP transcoder + GCS uploader + multipart route Jonas · 2026-05-04 · +647 −51
  • 6fda255 F-46 CSA v3 — public storefront marketing pages + sign-up flow Jonas · 2026-05-04 · +1 392 −28

2026-05-03 — 10 direkt-commits

Detaljer
  • 53ce7e1 O-06 — release-notes RSS feed + in-app banner Jonas · 2026-05-03 · +416 −37
  • 20c72a7 C-08 v2 — wire onboarding wizard preferences to feature flags Jonas · 2026-05-03 · +1 167 −30
  • 49c3178 SB-21 v2 follow-through + S-18 v2 — Stripe Price mutation persistence + wholesale toggle Jonas · 2026-05-03 · +257 −61
  • ba32419 D-02 v2 — GDPR farm-deletion cool-off frontend Jonas · 2026-05-03 · +17 701 −28
  • f2170a7 wip Jonas · 2026-05-03 · +2 622 −32 359
  • bef5e3c F-46 CSA v3 — Stripe recurring billing on join + webhook sync Migration 0095: stripe_price_id on csa_program, stripe_customer_id + stripe_subscription_id on csa_member (idx on stripe_subscription_id). CsaStripeService: createForMember (Stripe Customer + Subscription when programme has stripePriceId), cancelForMember (at period end). Swallows StripeError so join never fails due to Stripe unreachability. CsaStripeService.live wired in Main.scala ZLayer stack. CsaPublicRoutes: Env gains CsaStripeService. POST /join calls createForMember when prog.stripePriceId.isDefined; sets member status='pending_payment'. CsaPublicProgramDTO gains stripeBilling flag. Programmes without stripePriceId keep current status='active' flow. CsaRoutes: CsaProgramDTO + CsaProgramSaveBody + POST/PUT handlers gain stripePriceId field so farm admins can configure Stripe billing. CsaMemberRepository: new getByStripeSubscriptionId method (Live + Fake). StripeWebhookRoutes: Env gains CsaMemberRepository. onSubscriptionUpdated syncs csa_member.status (active/pending_payment) from Stripe event. onSubscriptionDeleted flips csa_member to cancelled + sets cancelledAt. Test fixes: FakeRepos.FakeCsaMemberRepository stub; wired into StripeWebhookRoutesAuthSpec + GiftCardWebhookSpec + ShopCustomerV2Spec. 1418 / 0 / 62. Jonas · 2026-05-03 · +224 −48
  • f63ed23 C-09 empty-state design pass — EmptyState component + 5 screens - New EmptyState.svelte shared component (icon/title/body/ctaHref/ctaOnClick) with encapsulated placeholder-content styles matching house pattern. - i18n: tasks.empty.cta, inventory.empty.cta, calendar.empty.body, calendar.empty.cta, group.empty.cta, breeding.empty.cta (en+sv). - Wired into tasks, inventory, calendar, groups, breeding pages — each now shows icon + heading + body + primary CTA button/link on empty list instead of icon+text with no action. - calendar: CTA links to /tasks (calendar entries come from tasks/treatments). - breeding: CTA links to /animals (service requires an animal row). - npm run i18n:build green; pre-existing i18n:check baseline failures held flat. Jonas · 2026-05-03 · +692 −52
  • 6ae8030 E-06 v2 — email template admin editor UI - EmailTemplateRepository gains listAll(): Task[List[EmailTemplateRow]] (Quill query sorted by (templateKey, locale)); inMemory impl updated. - EmailTemplateRoutes: owner-only admin endpoints GET /api/farms/:id/email-templates — list (any farmer) GET /api/farms/:id/email-templates/:key/:locale — full template PUT /api/farms/:id/email-templates/:key/:locale — upsert override Validates key ∈ KnownKeys + locale ∈ SeedLocales; bumps version on PUT. DB override is global (not per-farm) in v1 — email_template has no farm_id FK; farm-scoping is purely for requireOwner auth routing. - Wired into Main.scala routes + imports. - Frontend: /app/farms/[id]/admin/email-templates page — accordion per template key × locale; inline subject/bodyHtml/bodyText editor with sandboxed iframe preview (sandbox=allow-same-origin, no scripts); Save → PUT endpoint; version label; unsaved-changes awareness. - 20 new i18n keys (emailTemplates.*) in build-i18n.mjs → rebuilt en.json + sv.json. - No new migration (email_template table from migration 0074). Jonas · 2026-05-03 · +1 001 −30
  • 868f3f8 SB-21 v2 — Stripe subscription price update on country-change confirm - StripeClient trait + StripeClientStub + StripeClientLive gain updateSubscriptionPrice(subId, priceId): retrieves existing sub item id then POSTs items[0][price] to /subscriptions/:id. - StripeConfig gains priceStarterReduced / priceProReduced / priceProPlusReduced (env STRIPE_PRICE_*_REDUCED); defaults to TODO_CONFIG stub posture. - BillingService trait gains applyCountryChange(farmId). Live impl: fetches farm billing country, resolves PPP tier, picks reduced price id via stripeConfig fields, calls updateSubscriptionPrice, updates local stripe_subscription.priceId. Swallows StripeError so the confirm never fails due to Stripe unreachability. - BillingCountryRoutes confirm handler now calls applyCountryChange after upsert; Env adds BillingService. The call is .ignore so UX is unaffected when Stripe is unconfigured. - 4 new specs (BillingServiceCountryChangeSpec): reduced→Stripe called, standard→skipped, no sub row→ok, no subscriptionId→ok. - All StripeConfig constructors in test files updated with TODO_CONFIG defaults for the new fields. 1418 / 0 / 62. Jonas · 2026-05-03 · +364 −27
  • 30dc86c wip Jonas · 2026-05-03 · +1 476 −1 176

2026-05-02 — 39 direkt-commits

Detaljer
  • 82eddf9 wip Jonas · 2026-05-02 · +330 −44
  • 1ebb81e C-03 polish — tighten perf-budget soft caps post lazy-i18n Jonas · 2026-05-02 · +355 −324
  • 1e963c4 C-08 v2 — wire onboarding preferences to consumers Jonas · 2026-05-02 · +452 −0
  • 6b59228 O-06 weekly release notes — git harvester + RSS + in-app banner Jonas · 2026-05-02 · +778 −1
  • ff3d45e FARMBRITE_TODO status tracker — append commit 31 (BillingGate v2) Jonas · 2026-05-02 · +15 −0
  • 6de02b4 BillingGate enforcement v2 — gate remaining premium surfaces Jonas · 2026-05-02 · +602 −24
  • dbe80fc S-57 reduced-rate VAT codes for food products Jonas · 2026-05-02 · +246 −4
  • a16686f cleanup some stuff Jonas · 2026-05-02 · +117 −113
  • ae67b31 FARMBRITE_TODO status tracker — append commit 29 (BillingGate enforcement) Jonas · 2026-05-02 · +28 −6
  • 2f20d48 BillingGate enforcement on premium routes (SB-12 follow-up) — compliance always-on Jonas · 2026-05-02 · +1 008 −54
  • 5969f9d FARMBRITE_TODO status tracker — append commits 22-28 + restart playbook Jonas · 2026-05-02 · +103 −41
  • 1accec5 First-time onboarding wizard (C-08) — 5-step ≤5min path to first animal Jonas · 2026-05-02 · +2 722 −75
  • 094a5d9 Bulk-edit animals (UX §5 row) — multi-select + preview + event-log execute Jonas · 2026-05-02 · +1 888 −29
  • 639a8b3 Country-compliant invoice generation (S-58) — Council Directive 2006/112/EC Art 226 + SE/DE/FR specifics Jonas · 2026-05-02 · +1 298 −33
  • 9a13386 Dunning + failed-payment grace (SB-11, SB-12) — 7-day grace, day-21 downgrade, compliance always-on Jonas · 2026-05-02 · +1 633 −62
  • 8ca7124 Copilot v3 auto-fanout + prune + S-16 review digest email (follow-ups) Jonas · 2026-05-02 · +772 −48
  • 3ccb95f Buyer account addresses + subscriptions + email verify (S-15 v2 polish) Jonas · 2026-05-02 · +2 120 −74
  • 5f55850 Gift card storefront purchase + issued email (S-17 v2 polish) Jonas · 2026-05-02 · +2 515 −31
  • 033b29d FARMBRITE_TODO status tracker — overnight session totals (21 commits) Jonas · 2026-05-02 · +242 −15
  • da67c4e Eastern-EU purchasing-power pricing (SB-21) — country-aware Stripe Price selection Jonas · 2026-05-02 · +1 033 −30
  • 62fe762 Cookie banner (S-67) + country one-pagers (M-04) — public marketing + GDPR consent Jonas · 2026-05-02 · +3 082 −29
  • ff75318 Food information for consumers (S-59) — Reg (EU) 1169/2011 Annex II + Art 30 Jonas · 2026-05-02 · +3 195 −30
  • 380cec3 Wire discount + gift card into checkout + webhook redemption (S-17 polish) Jonas · 2026-05-02 · +866 −36
  • d8f9a7f GDPR data export + deletion v1 (D-02 partial, L-07) — farmer-facing + buyer-facing Jonas · 2026-05-02 · +2 590 −74
  • 11b854f PDF font embedding (A-19 polish) — add LICENSE-FONTS.md (OFL 1.1) Jonas · 2026-05-02 · +113 −0
  • f0af8c9 Reviews + ratings (S-16) — pending-by-default, verified-purchase, helpful votes Jonas · 2026-05-02 · +2 216 −42
  • 732dc2e Shop customer accounts (S-15) — guest-default, magic-link + password, re-order Jonas · 2026-05-02 · +2 820 −33
  • b1f457e Onboarding email drip (SL-04) — 8-step sequence + unsubscribe Jonas · 2026-05-02 · +1 944 −76
  • 008d6fe Discount codes + gift cards (S-17) — backend core + admin UI Jonas · 2026-05-02 · +3 492 −53
  • fb3e2b1 Shipping rule engine v1 (S-12) — zones, weight bands, free-over, surcharges Jonas · 2026-05-02 · +3 187 −32
  • b645a7e EU compliance landing polish (S-21..S-24, M-01) — inline SVG illustrations + status accuracy Jonas · 2026-05-02 · +833 −95
  • a2db681 Copilot v3 — multi-step tool-use loop (ships dark, B-02 v3) Jonas · 2026-05-02 · +2 214 −42
  • d4a8a4b Fix bundle size — lazy-load i18n dictionaries (route 9.4 MB → 215 KB gz) Jonas · 2026-05-02 · +683 −252
  • 9cfbc8e Stripe Payment Element v2 (S-32..S-42 live path) Jonas · 2026-05-02 · +893 −9
  • aa847ad Fix capacitor import breaking web build + baseline perf budget Jonas · 2026-05-02 · +146 −14
  • c96a6b6 Performance budget CI gate (C-03) — bundle-size + Lighthouse on every PR Jonas · 2026-05-02 · +704 −1
  • ffee897 Annual-pack FCI year-rollup PDF — closes A-19 G2 gap Jonas · 2026-05-02 · +809 −6
  • 8f3742d Image upload pipeline v1 (E-05) — JPEG-only with WebP/AVIF swap-seam Jonas · 2026-05-02 · +1 732 −11
  • ff45d94 Email infrastructure v1 (E-06) — Postmark/Resend hand-rolled clients, file-system templates, suppression list, daemon retry Jonas · 2026-05-02 · +2 330 −4

2026-05-01 — 50 direkt-commits

Detaljer
  • 4128a6c Shipping carriers v1 (S-49..S-53) — TODO_CONFIG-aware stubs + label print + TSP route Jonas · 2026-05-01 · +3 535 −16
  • 031c6fe FARMBRITE_TODO status tracker — open overnight session Jonas · 2026-05-01 · +21 −2
  • bd31beb fixes Jonas · 2026-05-01 · +1 686 −4 330
  • 0a2a3ba wip Jonas · 2026-05-01 · +85 287 −107 350
  • 358fa1b FARMBRITE_TODO status tracker — append W-08 to daytime session Jonas · 2026-05-01 · +16 −1
  • 1afece2 Hardware compatibility page — W-08 Jonas · 2026-05-01 · +2 223 −9
  • 3e8f30c FARMBRITE_TODO status tracker — daytime session totals (15 commits) Jonas · 2026-05-01 · +234 −3
  • 81ffd8d Self-serve sales: farm-size calculator + upgrade flow + 30d trial — SL-01/SL-03/SL-06 Jonas · 2026-05-01 · +2 080 −30
  • dff49a2 EU compliance landing page — M-01 / M-02 Jonas · 2026-05-01 · +3 709 −9
  • bf10322 Stripe Tax + EU VAT/OSS/VIES — S-54..S-58 Jonas · 2026-05-01 · +2 402 −24
  • efacfa8 Storefront marketing widgets — S-21..S-24 + S-71 (B-01 v1.2) Jonas · 2026-05-01 · +2 113 −2
  • bcf6710 Photo-triage v1 — Claude vision lameness + FAMACHA + hoof (B-03 v1) Jonas · 2026-05-01 · +2 662 −10
  • 41d7688 Stripe Connect + Billing scaffold v1 — E-03 / SB-01..SB-13 Jonas · 2026-05-01 · +3 339 −16
  • 4cff733 Voice entry v2 — Claude LLM fallback (B-04 v2) Jonas · 2026-05-01 · +755 −18
  • 877ff5a Annual-pack ZIP — bundle the 5 remaining A-19 PDFs (G2 closure) Jonas · 2026-05-01 · +52 −21
  • df2942b Copilot v2 — Claude LLM rephrase + daily summary (B-02 v2) Jonas · 2026-05-01 · +1 187 −25
  • 6756413 Per-farmer e-shop polish — variant + image admin UI (B-01 v1.1) Jonas · 2026-05-01 · +2 105 −79
  • 3d7fc73 Fix npm run build — restore production build Jonas · 2026-05-01 · +11 −7
  • 689fd74 Capacitor wrappers for iOS + Android — A-14 / F-24 / F-41 Jonas · 2026-05-01 · +2 260 −10
  • 99e9c63 Per-farmer e-shop scaffold v1 — B-01 Jonas · 2026-05-01 · +4 377 −1
  • d42119f Localization v1: DE / FR / NL / ES — A-16 / W-09 Jonas · 2026-05-01 · +7 027 −3 439
  • fbce740 Fix Test/compile breakage on master — restores sbt test Jonas · 2026-05-01 · +41 −30
  • 0a41f72 Drop Shopify / WooCommerce / Etsy sync from scope — F-29 / B-06 Jonas · 2026-05-01 · +5 −7
  • 27122e9 FARMBRITE_TODO status tracker — overnight session totals (19 commits) Jonas · 2026-05-01 · +152 −12
  • 85b5fc7 Mailchimp connector + Zapier API support v1 — F-30 + F-27 Jonas · 2026-05-01 · +3 436 −32
  • 0b45231 EUR-Lex regulation reference + CSA scaffold v1 — B-05 + F-46 Jonas · 2026-05-01 · +6 968 −2
  • 6907508 BLE EID stick reader skeleton + pickup locations v1 — A-13 + F-45 Jonas · 2026-05-01 · +3 409 −1
  • 21bb835 Saved reports v1 — F-20 Jonas · 2026-05-01 · +3 679 −7
  • 10671ea Voice entry skeleton — B-04 v1 Jonas · 2026-05-01 · +1 866 −2
  • a6b8fa5 DE HIT + IE ICBF clients: TODO_CONFIG live-stub posture — A-17 / W-02 Jonas · 2026-05-01 · +731 −20
  • 994df99 Crops-lite v1 + 280-row crop catalog — A-07 / F-12 / F-13 Jonas · 2026-05-01 · +5 711 −1
  • 2d6a209 Customisable dashboard with widgets v1 — F-34 Jonas · 2026-05-01 · +3 698 −1
  • cf66b55 Knowledge base scaffold + 15 EN/SV seed articles — A-15 / F-38 v1 Jonas · 2026-05-01 · +4 771 −2
  • 85ad164 Public API docs portal — A-20 / F-25 / W-07 Jonas · 2026-05-01 · +11 479 −1
  • e14eee7 AI Copilot v1 (rule-based daily nudges) — B-02 first slice Jonas · 2026-05-01 · +2 244 −2
  • 82cddb9 Inventory v1 — feed / supplies / harvested produce — A-08 / F-15 Jonas · 2026-05-01 · +4 858 −25
  • 295a34a Unified farm calendar — F-18 Jonas · 2026-05-01 · +1 955 −4
  • 1bcc70c Per-animal P&L view — A-12 slice Jonas · 2026-05-01 · +2 446 −1
  • 9eeb45e Employees + timesheets + payroll export — A-09 / F-16 v1 Jonas · 2026-05-01 · +4 937 −1
  • bea3635 Generic 'Other' species record type — F-11 Jonas · 2026-05-01 · +310 −14
  • de69977 Webhook self-service UI + secret rotation — F-26 Jonas · 2026-05-01 · +2 585 −788
  • 8a7f753 Recurring tasks (RRULE subset) — A-10 / F-17 follow-up Jonas · 2026-05-01 · +1 545 −15
  • b849272 Tasks polish: /tasks/team + /tasks/all-tasks pages — A-10 / F-17 Jonas · 2026-05-01 · +1 435 −40
  • 25e8f22 Multi-currency display — A-18 / F-36 Jonas · 2026-05-01 · +263 −20
  • 6fa436f Tasks polish — priorities integration + per-assignee filter + calendar Jonas · 2026-05-01 · +1 217 −19
  • 3678c00 Custom fields for holding + farm entities — F-19 / A-11 follow-up Jonas · 2026-05-01 · +623 −26
  • 45a4242 TTF font embedding in PdfService — full EU 24 locale support Jonas · 2026-05-01 · +79 −33
  • d2df89f AHL keeper-record book PDF — A-19 slice 7 Jonas · 2026-05-01 · +498 −2
  • 0f88b83 Transport-welfare report PDF — A-19 slice 6 of 6 Jonas · 2026-05-01 · +1 553 −1
  • 6a3bfba Organic inspection pack PDF — A-19 slice 5 of 6 Jonas · 2026-05-01 · +404 −3

2026-04-30 — 20 direkt-commits

Detaljer
  • 8baa356 CAP conditionality dossier PDF — A-19 slice 4 of 6 Jonas · 2026-04-30 · +349 −5
  • 23953b0 FCI declaration PDF — A-19 slice 3 of 6 Jonas · 2026-04-30 · +1 103 −30
  • 80291ed FARMBRITE_TODO status tracker — final 17-commit session totals Jonas · 2026-04-30 · +16 −11
  • 9c02747 welfare PDF in the annual-pack ZIP Jonas · 2026-04-30 · +27 −0
  • 02f2dee AMU register PDF locale support — render in keeper's language Jonas · 2026-04-30 · +106 −27
  • ca916fa FARMBRITE_TODO status tracker — final 13-commit session totals + restart playbook Jonas · 2026-04-30 · +33 −3
  • 5a58853 welfare PDF locale support — render in keeper's language Jonas · 2026-04-30 · +126 −32
  • 5205cd3 FARMBRITE_TODO status tracker — 11-commit session totals Jonas · 2026-04-30 · +6 −3
  • 97cf91c i18n the AppNav strings — Farmbrite parity W-09 / F-35 Jonas · 2026-04-30 · +561 −27
  • 0b91976 FARMBRITE_TODO status tracker — final 9-commit session totals Jonas · 2026-04-30 · +14 −8
  • 9dee6f0 CustomFieldRoutesAuthSpec — 11 tests covering auth + lifecycle + leak guard Jonas · 2026-04-30 · +275 −0
  • 8b16a25 TaskRoutesAuthSpec — 10 tests covering auth + lifecycle Jonas · 2026-04-30 · +231 −0
  • 69bd681 FARMBRITE_TODO status tracker — 2026-04-30 evening session totals Jonas · 2026-04-30 · +21 −1
  • a710da9 open-task count badge in AppNav Jonas · 2026-04-30 · +76 −0
  • 926eb92 AMU register PDF (slice 2 of A-19) — same DTO, third format Jonas · 2026-04-30 · +155 −6
  • 2141901 PdfService unit tests — verify PDF generation at runtime Jonas · 2026-04-30 · +143 −0
  • ffe1507 welfare report PDF (slice 1 of A-19) + PdfService scaffold Jonas · 2026-04-30 · +1 076 −5
  • 89f4521 to-do tasks (v1) — Farmbrite parity F-17 / A-10 Jonas · 2026-04-30 · +1 631 −36
  • 584fc04 custom fields per entity (animal v1) — Farmbrite parity F-19 / A-11 Jonas · 2026-04-30 · +117 584 −7 531
  • 9e1ec0d competitor analysis Jonas · 2026-04-30 · +5 060 −56

2026-04-29 — 3 direkt-commits

Detaljer
  • 3ccf877 wip Jonas · 2026-04-29 · +18 826 −430
  • 117c96b wip Jonas · 2026-04-29 · +1 144 −925
  • 4bf3804 sessions 443-479: 37-session overnight sweep Jonas · 2026-04-29 · +266 915 −845

2026-04-28 — 3 direkt-commits

Detaljer
  • d93d85e sessions 419-442: 24-session evening sweep Jonas · 2026-04-28 · +35 556 −694
  • f46d64e add debug-json.txt Jonas · 2026-04-28 · +1 223 −0
  • 4b5cbe2 sessions 389-418: 30-session overnight sweep Jonas · 2026-04-28 · +46 143 −140

2026-04-27 — 17 direkt-commits

Detaljer
  • f9ffd53 backfill untracked: CLAUDE.md, calendar lib scaffolding, marketing pages, sbt plugin + gitignore tooling dirs Jonas · 2026-04-27 · +9 633 −0
  • 2d84cf7 land Feeding.notes (migration 0036) + VaccineSchedule.vaccineProduct (0037) Jonas · 2026-04-27 · +126 −15
  • d836d2b reinstate boot-time db migrations runner (closes baseline Main.scala:262 error) Jonas · 2026-04-27 · +313 −12
  • 38bbd9f post-overnight backfill: replace 5 CSV-export hacks with proper schema/route extensions Jonas · 2026-04-27 · +293 −22
  • 60832fb sessions 386-388: CSV exports for areas overview + per-animal feed history + admin metrics Jonas · 2026-04-27 · +385 −1
  • f6fa5ea sessions 383-385: CSV exports for pregnancy scanning + farrowing board + personnel Jonas · 2026-04-27 · +473 −5
  • aded8ad sessions 381-382: CSV exports for feed library + vaccination schedules Jonas · 2026-04-27 · +238 −4
  • 2b3f499 sessions 378-380: CSV exports for animal health timeline + houses+areas + personnel schedule Jonas · 2026-04-27 · +350 −6
  • dc27b23 sessions 375-377: CSV exports for pigs welfare + pigs health + animal locations Jonas · 2026-04-27 · +613 −2
  • 9e1e639 sessions 373-374: CSV exports for parasite events + breeding dashboard Jonas · 2026-04-27 · +282 −5
  • e64f535 sessions 370-372: CSV exports for aquaculture batch + prescriptions + animal register Jonas · 2026-04-27 · +370 −10
  • 548e5ac sessions 368-369: cross-herd KPI CSV + rabbit doe litter CSV exports Jonas · 2026-04-27 · +273 −0
  • 9b11cfb sessions 366-367: feed-plan print snapshot + 12-month honey harvest stacked-bar chart Jonas · 2026-04-27 · +661 −8
  • 22f1133 sessions 364-365: print snapshots for cattle heat-detection + breeding-indices Jonas · 2026-04-27 · +543 −35
  • bf09e8c sessions 361-363: print snapshots for honey + bees wintering + cattle SCC alert Jonas · 2026-04-27 · +914 −27
  • 8e013c8 sessions 358-360: print snapshots for sheep census + calf welfare + aquaculture Jonas · 2026-04-27 · +1 219 −18
  • e221767 sessions 343-357: sparklines + print snapshots + CSV exports across Phase-1/4 Jonas · 2026-04-27 · +3 064 −64

2026-04-26 — 2 direkt-commits

Detaljer
  • 6f488a5 sessions 216-342: 127 untracked sessions across Phase 1-4 Jonas · 2026-04-26 · +65 877 −59
  • 660b438 double insane 10hours Jonas · 2026-04-26 · +10 689 −336

2026-04-25 — 65 direkt-commits

Detaljer
  • 83c6218 session 215: SE Jordbruksverket CDB pig movement Excel template export Jonas · 2026-04-25 · +497 −2
  • 49620b3 session 214: CAP conditionality dossier generator (Phase-1 Compliance Vault) Jonas · 2026-04-25 · +783 −19
  • eb02a19 session 213: organic inspection pack generator (Reg 2018/848 Annex VI + Annex II) Jonas · 2026-04-25 · +999 −23
  • b2da4bf session 212: auto-generated movement document (Phase-1 Movements) Jonas · 2026-04-25 · +822 −7
  • 1147e37 session 211 + sessions 210-211 master TODO log Jonas · 2026-04-25 · +511 −15
  • 2b77d93 session 210: lean meat % import from slaughterhouse report (Phase-2 Pigs) Jonas · 2026-04-25 · +1 053 −1
  • b28ec15 session 209 + sessions 207-209 master TODO log Jonas · 2026-04-25 · +689 −9
  • 5f5edc5 session 208: append-only audit log endpoint + /compliance/audit-log page Jonas · 2026-04-25 · +602 −1
  • f818051 session 207: layer page {#if true} wraps for 4 const_tag clusters Jonas · 2026-04-25 · +324 −316
  • a39af63 updates Jonas · 2026-04-25 · +127 −138
  • 570209e reconstruct missing-on-disk apiary entity, AMU register, JsonFieldUtil Jonas · 2026-04-25 · +565 −0
  • b8ac11d updates Jonas · 2026-04-25 · +194 −194
  • 97fe5d6 compress master TODO session log to one-liners (sessions 95-206) Jonas · 2026-04-25 · +119 −117
  • 4ad19bb updates Jonas · 2026-04-25 · +89 −191
  • 799429f session 206: layer page Salmonella card {@const} placement fix + master TODO summary Jonas · 2026-04-25 · +70 −57
  • 6252b01 sessions 204 + 205: pig (sow) Coffey BCS + cross-species FECRT in parallel Jonas · 2026-04-25 · +2 542 −2
  • a42cc4d sessions 202 + 203: equine Henneke BCS + small-ruminant Russel BCS in parallel Jonas · 2026-04-25 · +3 244 −2
  • 3efae81 session 201: sweep farmId type narrowing across production pages Jonas · 2026-04-25 · +11 −9
  • d4107c7 sessions 199 + 200: cattle husbandry log + calving outcome (dystocia) in parallel Jonas · 2026-04-25 · +1 577 −6
  • d1d92de sessions 197 + 198: pig welfare scoring + layer FPD modules in parallel Jonas · 2026-04-25 · +2 899 −4
  • 29179ea session 196: cow hock + knee lesion scoring module + dashboard cadence Jonas · 2026-04-25 · +1 701 −5
  • a3988c7 session 195: cow udder hygiene scoring module + dashboard cadence Jonas · 2026-04-25 · +1 548 −5
  • a103c10 session 194: cow Body Condition Score (BCS) module + dashboard cadence Jonas · 2026-04-25 · +1 578 −3
  • 158c299 session 193: small-ruminant (sheep + goat) lameness module + dashboard cadence Jonas · 2026-04-25 · +1 471 −5
  • 0f4de87 session 192: equine lameness cadence row on cross-species dashboard Jonas · 2026-04-25 · +179 −4
  • 2f8ab16 session 191: cow lameness cadence row on cross-species dashboard Jonas · 2026-04-25 · +185 −3
  • a30d86d session 190: cow Sprecher 1-5 lameness module Jonas · 2026-04-25 · +1 274 −4
  • 6867847 session 189: reconstruct layerStore + broilerStore in parallel via subagents Jonas · 2026-04-25 · +1 061 −3
  • 914c141 session 188: reconstruct 4 missing-on-disk stores in parallel Jonas · 2026-04-25 · +729 −5
  • 431391c session 187: reconstruct missing-on-disk apiaryStore.ts Jonas · 2026-04-25 · +138 −4
  • 8813542 session 186: per-species drilldown chips on cross-species cadence dashboard Jonas · 2026-04-25 · +94 −3
  • 6add483 sessions 184 + 185: groupBySubjectId helper + 12-week overdue sparkline Jonas · 2026-04-25 · +172 −4
  • 6faf48b session 183: bee-hive Varroa monitoring cadence row on cross-species dashboard Jonas · 2026-04-25 · +245 −9
  • adf69c3 session 182: layer Salmonella NCP cadence row on cross-species dashboard Jonas · 2026-04-25 · +248 −10
  • c79ee07 session 181: bovine TB cadence on cattle herd-test watchdog Jonas · 2026-04-25 · +41 −7
  • cf0505d session 180: cross-species cadence-overdue dashboard CSV export Jonas · 2026-04-25 · +84 −4
  • b4a9161 session 179: equine FEI vaccination cadence row on cross-species dashboard Jonas · 2026-04-25 · +245 −20
  • b6f73c8 session 178: sheep + porcine + ovine ID cadence rows on cross-species dashboard Jonas · 2026-04-25 · +875 −41
  • df8ed55 session 177: cross-species cadence-overdue dashboard (cattle + goats) Jonas · 2026-04-25 · +500 −5
  • 9823b1f session 176: sparkline tooltip date label across 9 rollup cards Jonas · 2026-04-25 · +815 −21
  • f1b27bd session 173-175: 12-week sparklines on CAE + ARR + K222 rollups Jonas · 2026-04-25 · +327 −5
  • 416c879 session 172: cattle herd-test cadence book-now deep-link Jonas · 2026-04-25 · +37 −8
  • 3d77578 session 171: per-animal identifier-row check on OvineIdRollupCard Jonas · 2026-04-25 · +112 −27
  • 9f726e2 session 170: FAMACHA + FEC full event-log CSV exports Jonas · 2026-04-25 · +128 −4
  • f66a166 session 169: ovine ARR + caprine K222 full event-log CSV exports Jonas · 2026-04-25 · +148 −2
  • 796d524 session 168: 12-week non-compliance sparkline on FEI rollup Jonas · 2026-04-25 · +121 −4
  • ee8e60d session 167: porcine + ovine ID full event-log CSV exports Jonas · 2026-04-25 · +169 −5
  • 2dadd6b session 166: goat CAE rollup book-now deep-link Jonas · 2026-04-25 · +37 −6
  • c804d5f session 165: 12-week sparkline on ovine + caprine ID rollup Jonas · 2026-04-25 · +111 −4
  • 1873a11 session 164: 12-week sparklines on porcine-ID + equine-lameness rollups Jonas · 2026-04-25 · +214 −5
  • 1584ac0 session 163: farm-wide ovine + caprine passport rollup Jonas · 2026-04-25 · +823 −4
  • e9ace71 session 162: 12-week at-risk sparklines on FAMACHA + FEC rollups Jonas · 2026-04-25 · +220 −4
  • 12739bf session 161: goat CAE rollup OIE Ch 14.5 next-test cadence hint Jonas · 2026-04-25 · +196 −5
  • 18f9d29 session 160: farm-wide FAMACHA + FEC flock rollups on /production/sheep Jonas · 2026-04-25 · +767 −4
  • f722440 session 159: dedicated /production/sheep landing page Jonas · 2026-04-25 · +89 −8
  • 96d0d55 session 158: goat CAE rollup full-herd CSV export Jonas · 2026-04-25 · +64 −5
  • 33b30a2 session 157: clickable per-NSP/per-class bar drilldown on scrapie rollups Jonas · 2026-04-25 · +171 −8
  • db611f6 session 156: goat herd-test log CSV export Jonas · 2026-04-25 · +103 −4
  • ad3abb4 session 155: passport dashboard deep-links for horse/cattle/sheep/goat Jonas · 2026-04-25 · +53 −4
  • 09f3b2c session 154: cattle herd-test log CSV export Jonas · 2026-04-25 · +115 −5
  • 7b57ab6 session 153: farm-wide goat CAE status rollup Jonas · 2026-04-25 · +437 −5
  • f539502 session 152: porcine-ID rollup deep-link from passport dashboard Jonas · 2026-04-25 · +47 −12
  • bfbc7c1 session 151: scrapie rollup per-NSP-class stacked bar Jonas · 2026-04-25 · +186 −4
  • 85705d6 session 150: cattle BVD/IBR/Johne's per-disease split Jonas · 2026-04-25 · +368 −4
  • 6ea6359 session 149: goat kidding board CSV export Jonas · 2026-04-25 · +90 −7

2026-04-24 — 12 direkt-commits

Detaljer
  • fa5cc09 session 148: farm-wide porcine ID rollup Jonas · 2026-04-24 · +846 −7
  • ed065e7 session 147 — scrapie rollup per-genotype-pair distribution Jonas · 2026-04-24 · +208 −6
  • a6b7725 double insane 3x CSV exports Jonas · 2026-04-24 · +4 612 −13
  • 9b5ebd4 session 142+143: passport + FEI non-compliant CSV export Jonas · 2026-04-24 · +766 −8
  • 644632f session 140+141 scrapie + lameness CSV export Jonas · 2026-04-24 · +484 −7
  • bc49ff0 session 139: layer producer-code audit (Reg (EC) 589/2008) Jonas · 2026-04-24 · +2 958 −8
  • bb2f87d session 138 farm-wide equine lameness rollup Jonas · 2026-04-24 · +438 −6
  • 346d146 session 137 equine lameness log AAEP 0-5 Jonas · 2026-04-24 · +760 −4
  • 45c74e9 session 136 FEI farm-wide vaccination compliance rollup Jonas · 2026-04-24 · +487 −3
  • 6d20ab2 session 135 goat K222 rollup Jonas · 2026-04-24 · +507 −16
  • eb85bc5 session 134 goat PRNP scrapie genotyping Jonas · 2026-04-24 · +2 296 −2
  • 69ee964 double insane 4hrs Jonas · 2026-04-24 · +7 676 −354

2026-04-23 — 3 direkt-commits

Detaljer
  • 87eab9c double insane Jonas · 2026-04-23 · +62 −119
  • 68093e8 double insane Jonas · 2026-04-23 · +2 223 −33
  • f4bae37 double insane Jonas · 2026-04-23 · +5 078 −106

2026-04-22 — 3 direkt-commits

Detaljer
  • f935128 double insane Jonas · 2026-04-22 · +391 −22
  • 55f2121 double insane Jonas · 2026-04-22 · +473 −467
  • 3727559 updates Jonas · 2026-04-22 · +3 339 −191

2026-04-21 — 1 direkt-commits

Detaljer
  • b3d1a86 double insane Jonas · 2026-04-21 · +6 987 −1 557

2026-04-20 — 4 direkt-commits

Detaljer
  • 6a1341d double insane Jonas · 2026-04-20 · +1 411 −1 364
  • 710287c updates Jonas · 2026-04-20 · +9 162 −668
  • f523210 double insane Jonas · 2026-04-20 · +970 −69
  • 4ad7eca double insane Jonas · 2026-04-20 · +4 163 −483

2026-04-19 — 2 direkt-commits

Detaljer
  • 42fc9ab double insane Jonas · 2026-04-19 · +5 843 −764
  • d69ca0b double insane Jonas · 2026-04-19 · +17 154 −258

2026-04-18 — 1 direkt-commits

Detaljer
  • cd3d6e4 wip Jonas · 2026-04-18 · +4 060 −93

2026-04-17 — 3 direkt-commits

Detaljer
  • 6fc98ba wip Jonas · 2026-04-17 · +2 −2
  • 19d9984 updates Jonas · 2026-04-17 · +2 085 −1 616
  • 97cb62c updates Jonas · 2026-04-17 · +5 626 −1 034

2026-03-22 — 1 direkt-commits

Detaljer
  • 07ff4cb updates Jonas · 2026-03-22 · +8 931 −269

2026-02-15 — 1 direkt-commits

Detaljer
  • 283732f wip Jonas · 2026-02-15 · +2 481 −827

2026-02-13 — 2 direkt-commits

Detaljer
  • 1a473f9 updates Jonas · 2026-02-13 · +2 753 −178
  • 729236e wip Jonas · 2026-02-13 · +9 877 −4

2026-02-10 — 2 direkt-commits

Detaljer
  • 1f5a8a8 wip Jonas · 2026-02-10 · +37 −33
  • 54c3b03 wip Jonas · 2026-02-10 · +5 177 −226

2026-02-09 — 1 direkt-commits

Detaljer
  • 4d68ac3 updates Jonas · 2026-02-09 · +564 −25

2026-02-07 — 1 direkt-commits

Detaljer
  • c743a5d wip Jonas · 2026-02-07 · +6 555 −968

2026-02-06 — 1 direkt-commits

Detaljer
  • 9937938 updates Jonas · 2026-02-06 · +3 752 −1 618

2026-02-03 — 1 direkt-commits

Detaljer
  • 326935e updates Jonas · 2026-02-03 · +2 076 −15

2026-02-02 — 1 direkt-commits

Detaljer
  • b84e6a6 updates Jonas · 2026-02-02 · +2 286 −167

2026-01-19 — 1 direkt-commits

Detaljer
  • 05c8944 Add .gitignore file to exclude build artifacts, logs, and environment files Jonas · 2026-01-19 · +90 −0

2026-01-18 — 2 direkt-commits

Detaljer
  • f52f688 Updated to Bun as FE build system Jonas · 2026-01-18 · +1 596 −229
  • 6d3ff16 Very much WIP Jonas · 2026-01-18 · +448 −25

2026-01-16 — 1 direkt-commits

Detaljer
  • 2103699 updates Jonas · 2026-01-16 · +6 422 −322

2026-01-15 — 1 direkt-commits

Detaljer
  • ef17c15 New logo. This one is goooood Jonas · 2026-01-15 · +191 −892

2026-01-14 — 1 direkt-commits

Detaljer
  • bb9a137 restructure main layout to use <wa-page>. Still a lot of bugs in navigation Jonas · 2026-01-14 · +612 −195

2026-01-12 — 7 direkt-commits

Detaljer
  • a9a0e40 ugly logo Jonas · 2026-01-12 · +0 −6
  • 26d62ca ugly logo Jonas · 2026-01-12 · +1 562 −122
  • c038732 no secrets Jonas · 2026-01-12 · +0 −11
  • b44835d updates Jonas · 2026-01-12 · +38 −24
  • 36ce479 some removals Jonas · 2026-01-12 · +0 −1 633
  • d3f2dae AI has had fun Jonas · 2026-01-12 · +4 692 −556
  • 2668a52 Updated model Jonas · 2026-01-12 · +1 858 −286

2026-01-11 — 6 direkt-commits

Detaljer
  • 45ea3b5 Kinde login works Jonas · 2026-01-11 · +567 −20
  • f5a90eb Up and running @ office Jonas · 2026-01-11 · +111 −138
  • 8037eda Lit thrown out Jonas · 2026-01-11 · +632 −539
  • b00a40c Lit thrown out Jonas · 2026-01-11 · +230 −278
  • 9151639 before throwing out Lit Jonas · 2026-01-11 · +241 −160
  • fe633a1 It all compiles and runs Jonas · 2026-01-11 · +2 711 −645

2026-01-09 — 2 direkt-commits

Detaljer
  • df9eb6f Init 2 Jonas · 2026-01-09 · +4 330 −2
  • e35728b Init 1 Jonas · 2026-01-09 · +234 −0