Files
tilbudgivern/.hermes/plans/2026-08-18_133633-carpenter-walkthrough-fixes.md
2026-08-18 13:45:34 +02:00

19 KiB
Raw Blame History

Tømrer-gennemgang Fixes Implementation Plan

For Hermes: Use subagent-driven-development skill to implement this plan task-by-task.

Goal: Gør det månedlige mobile tømrerflow stabilt, statusmæssigt entydigt og reelt klar til PDF/Ordrestyring, mens opdateringen af STARK-priser behandles som et separat dataleverance-spor med Jannick.

Architecture: Ret først de deterministiske UI-state-fejl (PDF freshness og fælles readiness-model), derefter performance i projektindlæsningen og til sidst materialekoblingsflowet. Materiale-id'er må kun komme fra den eksisterende masterdatabase og kræver brugerbekræftelse ved tvetydige matches. STARK-import udføres først, når Jannick leverer en frisk fil; kode- og datarisiko blandes ikke sammen.

Tech Stack: React 18, Jest/React Testing Library, Node/Express, MySQL, Playwright, eksisterende smartPackageMaterialMatchService, PM2/GitHub Actions.


Nuværende baseline

Produktionstesten på projekt 392 viste:

  • Mobilflowet virker uden vandret overflow.
  • Geometri, økonomi og PDF genereres korrekt.
  • Kontrolleret total: 177.781,60 kr. inkl. moms.
  • PDF-preview: 2 A4-sider, 109.141 bytes, gyldigt %PDF-.
  • Loading-overlayet brugte op til ca. 5,7 sekunder i en kold gennemgang.
  • En ny PDF blev straks markeret som forældet.
  • Final Review blokeres af 11 materialer uden material_id.
  • ProjectFlow viste 12 ukoblede materialer, mens Final Review viste 11.
  • Topstatus viste 4/4 trin gennemført, samtidig med at afsendelse var blokeret.
  • STARK-prislisten viste seneste dato 26.11.2025; Alex afklarer nye priser med Jannick.

QA-rapport og evidens:

  • /home/alex/dogfood-output/tilbudsgivern-2026-08-18/report.md
  • /home/alex/dogfood-output/tilbudsgivern-2026-08-18/carpenter-audit.pdf
  • /home/alex/dogfood-output/tilbudsgivern-2026-08-18/screenshots/pdf-state-after-generation.png
  • /home/alex/dogfood-output/tilbudsgivern-2026-08-18/screenshots/loading-overlay-after-5s.png

Spor A — Kodefixes, som kan udføres nu

Task 1: Lås regressionsbaselinen med målrettede tests

Objective: Gør de fire observerede kode-/UX-fejl reproducerbare, før produktionskode ændres.

Files:

  • Modify: frontend/src/components/FinalReview.test.js
  • Create: frontend/src/utils/projectReadiness.test.js
  • Create: frontend/src/components/ProjectFlow.test.js
  • Modify: tests/monthly-carpenter-audit.spec.js

Step 1: Skriv en fejlende PDF freshness-test

Test at en PDF er fresh umiddelbart efter generering, og først bliver stale, når et input ændres efter genereringen.

Step 2: Kør testen og bekræft RED

Run:

cd frontend
CI=true npm test -- --watchAll=false src/components/FinalReview.test.js

Expected: FAIL — PDF bliver markeret stale på grund af pdfUrl-effekten.

Step 3: Skriv fejlende readiness-tests

Dæk:

  • samme antal ukoblede materialer i ProjectFlow og Final Review
  • forskellen mellem flowComplete, readyForReview og readyToSend
  • 4/4 gennemført må ikke betyde klar til afsendelse, når blokeringer findes

Step 4: Skriv fejlende loading/performance-test

Mock seks uafhængige ressourcekald med forsinkelse og bekræft, at de ikke må afvikles serielt.

Step 5: Commit tests alene

git add frontend/src/components/FinalReview.test.js \
  frontend/src/utils/projectReadiness.test.js \
  frontend/src/components/ProjectFlow.test.js \
  tests/monthly-carpenter-audit.spec.js
git commit -m "test: lock carpenter walkthrough regressions"

Task 2: Ret PDF freshness med en deterministisk input-signatur

Objective: En netop genereret PDF skal være frisk, og kun reelle inputændringer må gøre den stale.

Files:

  • Create: frontend/src/utils/pdfSourceSignature.js
  • Create: frontend/src/utils/pdfSourceSignature.test.js
  • Modify: frontend/src/components/FinalReview.js:367-371,1198-1280,1348-1369
  • Modify: frontend/src/components/FinalReview.test.js

Step 1: Skriv en ren signaturfunktion

buildPdfSourceSignature skal serialisere de normaliserede værdier, som faktisk sendes til /api/pdf/generate:

  • tilbudstekst og kundebeskrivelse
  • normaliseret geometri
  • materialer: id, navn, antal, enhed og pris
  • udlejning
  • arbejdsopgaver og timer
  • gemt økonomimodel

Ignorér React object identity, tidsstempler og UI-only state.

Step 2: Verificér RED

Run:

cd frontend
CI=true npm test -- --watchAll=false src/utils/pdfSourceSignature.test.js

Expected: FAIL — modulet findes endnu ikke.

Step 3: Implementér minimal signatur

Gem generatedPdfSignature sammen med pdfUrl efter et succesfuldt response. Beregn:

const isPdfStale = Boolean(pdfUrl && generatedPdfSignature !== currentPdfSignature);

Fjern effekten på FinalReview.js:1357-1369, som inkluderer pdfUrl i dependencies og derfor straks kalder setIsPdfStale(true) efter generering.

Step 4: Kør fokuserede tests

cd frontend
CI=true npm test -- --watchAll=false \
  src/utils/pdfSourceSignature.test.js \
  src/components/FinalReview.test.js

Expected: PASS.

Step 5: Commit

git add frontend/src/utils/pdfSourceSignature.js \
  frontend/src/utils/pdfSourceSignature.test.js \
  frontend/src/components/FinalReview.js \
  frontend/src/components/FinalReview.test.js
git commit -m "fix: keep newly generated PDF preview fresh"

Task 3: Parallelisér og mål projektindlæsningen

Objective: Fjern unødvendig seriel ventetid og hold loading-overlayet under en dokumenteret SLA.

Files:

  • Create: frontend/src/services/projectHydrationService.js
  • Create: frontend/src/services/projectHydrationService.test.js
  • Modify: frontend/src/components/ProjectFlow.js:222-265,311-595,807-839
  • Modify: frontend/src/components/ProjectFlow.test.js
  • Modify: frontend/src/utils/quoteFlowTelemetry.js
  • Modify: tests/monthly-carpenter-audit.spec.js

Step 1: Skriv en timing-test

Mock geometri, arbejdsløn, materialer, udlejning og pakker med kendte delays. Bekræft at samlet tid svarer omtrent til det langsomste kald, ikke summen.

Step 2: Kør RED

cd frontend
CI=true npm test -- --watchAll=false src/services/projectHydrationService.test.js

Step 3: Implementér parallel hydration

Efter ét projekt-check hentes følgende med Promise.allSettled og fælles AbortController:

  • /api/customer-projects/:id/geometry
  • /api/customer-projects/:id/labor
  • /api/customer-projects/:id/materials
  • /api/customer-projects/projects/:id/rentals
  • /api/customer-projects/:id/packages

Bevar nuværende session fallbacks. Ignorér resultater fra et tidligere projekt, hvis brugeren skifter projekt under indlæsningen.

Step 4: Fjern dobbelt projekt-verificering

selectExistingProject har allerede hentet projektet før loadProjectData; send det verificerede payload videre i stedet for at hente samme projekt igen.

Step 5: Tilføj performance telemetry

Registrér:

  • project_hydration_started
  • project_hydration_ready
  • varighed og langsomste ressource

Ingen kunde-fritekst må logges.

Step 6: Acceptance

  • Kold mobilindlæsning: mål p95 over fem runs.
  • Mål: overlay væk på < 5.000 ms; stretch < 3.000 ms.
  • Ved overskridelse skal UI vise hvilken ressource, der stadig indlæses, i stedet for et generisk overlay.

Step 7: Commit

git add frontend/src/services/projectHydrationService.js \
  frontend/src/services/projectHydrationService.test.js \
  frontend/src/components/ProjectFlow.js \
  frontend/src/components/ProjectFlow.test.js \
  frontend/src/utils/quoteFlowTelemetry.js \
  tests/monthly-carpenter-audit.spec.js
git commit -m "perf: parallelize project hydration"

Task 4: Indfør én fælles readiness-model

Objective: ProjectFlow og Final Review skal vise samme antal, samme blokeringer og tydelige niveauer for færdighed.

Files:

  • Create: frontend/src/utils/projectReadiness.js
  • Create: frontend/src/utils/projectReadiness.test.js
  • Modify: frontend/src/components/ProjectFlow.js:118-191,807-812
  • Modify: frontend/src/components/FinalReview.js:220-290
  • Modify: frontend/src/components/FinalReview.test.js

Step 1: Definér eksplicitte tilstande

Returnér mindst:

{
  completedSteps,
  totalSteps,
  flowComplete,
  readyForReview,
  readyToSend,
  blockingChecks,
  attentionChecks,
  unlinkedMaterialCount
}

Step 2: Brug samme materialekilde

ProjectFlow må ikke tælle fra et gammelt packageData.materials, mens Final Review tæller fra persisted/editable materials. Normalisér én gang og brug samme projektlinjer som kilde.

Step 3: Ret teksten

Eksempel:

  • 4/4 trin gennemført
  • Klar til review — ikke klar til afsendelse
  • 11 materialer kræver kobling

Vis aldrig Alt er klar, når readyToSend === false.

Step 4: Verificér tests

cd frontend
CI=true npm test -- --watchAll=false \
  src/utils/projectReadiness.test.js \
  src/components/FinalReview.test.js \
  src/components/ProjectFlow.test.js

Step 5: Commit

git add frontend/src/utils/projectReadiness.js \
  frontend/src/utils/projectReadiness.test.js \
  frontend/src/components/ProjectFlow.js \
  frontend/src/components/FinalReview.js \
  frontend/src/components/FinalReview.test.js \
  frontend/src/components/ProjectFlow.test.js
git commit -m "fix: unify carpenter flow readiness status"

Task 5: Byg et sikkert materiale-koblingsflow

Objective: Tømreren skal kunne løse ukoblede materialer uden at opfinde eller automatisk godkende forkerte masterdata-id'er.

Files:

  • Modify: backend/src/services/smartPackageMaterialMatchService.js
  • Modify: backend/__tests__/smartPackageMaterialMatchService.test.js
  • Modify: backend/src/services/projectMaterialService.js
  • Modify: backend/src/__tests__/projectMaterialService.test.js
  • Modify: backend/src/routes/customerProjects.js
  • Create: backend/__tests__/projectMaterialMatchingRoute.test.js
  • Create: frontend/src/components/MaterialLinkReview.js
  • Create: frontend/src/components/MaterialLinkReview.test.js
  • Modify: frontend/src/components/FinalReview.js:268-274,1733-1743

Step 1: Tilføj read-only match-preview endpoint

Forslag:

POST /api/customer-projects/projects/:projectId/materials/match-preview

Input: konkrete projektmateriale-id'er. Output pr. linje:

  • kandidatens master material_id
  • SKU, navn, enhed, leverandør og pris
  • score og status: matched_sku, matched_name, ambiguous, unmatched

Ingen writes i preview-kaldet.

Step 2: Test sikkerhedskontrakten

  • SKU exact match må foreslås med score 1.
  • Tvetydige navne må ikke auto-godkendes.
  • Ukendte materialer forbliver ukoblede.
  • Kun aktive materialer med brugbar pris må foreslås.

Step 3: Udvid projektmateriale-update sikkert

ProjectMaterialService.updateProjectMaterial skal kunne gemme:

  • material_id
  • price_source
  • price_source_updated_at

Brug parameteriseret SQL og verify, at id'et findes i mastertabellen før update.

Step 4: Byg review-UI

Fra blokeringen i Final Review åbner Ret koblinger en mobilvenlig liste:

  • original projektlinje
  • bedste kandidat og score
  • søg/ændr kandidat
  • Bekræft pr. linje
  • batch-knap kun for eksplicit valgte linjer

High-confidence forslag er forslag, ikke skjulte automatiske writes.

Step 5: Genberegn readiness efter save

Efter en bekræftet kobling genindlæses projektmaterialerne, og både ProjectFlow og Final Review bruger den fælles readiness-model.

Step 6: Verificér

cd backend
npm test -- --runInBand \
  __tests__/smartPackageMaterialMatchService.test.js \
  src/__tests__/projectMaterialService.test.js \
  __tests__/projectMaterialMatchingRoute.test.js

cd ../frontend
CI=true npm test -- --watchAll=false \
  src/components/MaterialLinkReview.test.js \
  src/components/FinalReview.test.js

Step 7: Commit

git add backend/src/services/smartPackageMaterialMatchService.js \
  backend/__tests__/smartPackageMaterialMatchService.test.js \
  backend/src/services/projectMaterialService.js \
  backend/src/__tests__/projectMaterialService.test.js \
  backend/src/routes/customerProjects.js \
  backend/__tests__/projectMaterialMatchingRoute.test.js \
  frontend/src/components/MaterialLinkReview.js \
  frontend/src/components/MaterialLinkReview.test.js \
  frontend/src/components/FinalReview.js
git commit -m "feat: add reviewed project material linking flow"

Spor B — Nye STARK-priser med Jannick

Task 6: Modtag, valider og importer ny STARK-prisliste

Objective: Opdatér prisgrundlaget uden at blande en ekstern dataleverance ind i kodefixene.

Owner: Alex + Jannick for dataleverancen; implementer/operatør for validering og import.

Existing Files/Endpoints:

  • backend/src/routes/starkImport.js
  • backend/src/services/starkImportService.js
  • POST /api/stark/upload
  • GET /api/stark/status
  • GET /api/stark/import-history

Input fra Jannick:

  • Frisk STARK CSV
  • Prisens gyldighedsdato
  • Bekræftelse af netto/brutto-fortolkning
  • Enhedsdefinitioner
  • Eventuelle udgåede produkter

Step 1: Preflight uden produktionswrite

Kopiér filen til et isoleret testmiljø og verificér:

  • mindst fem semikolonseparerede kolonner
  • unik SKU/produktnummer
  • gyldige positive priser
  • kendte enheder
  • ingen uventet massiv produktdeaktivering

Step 2: Tag database-backup

Backup mindst:

  • stark_materials_cache
  • aktive material_prices for STARK
  • importhistorik

Step 3: Importér med eksisterende endpoint/service

Upload filen via POST /api/stark/upload. Gem batchId, statistik, errors og warnings.

Step 4: Post-import gates

GET /api/stark/status skal vise:

  • latest_import svarende til den nye levering
  • forventet produktantal og kategorier
  • ingen nul-/negative priser i aktive produkter

Sammenlign 20 repræsentative tagmaterialer før/efter. Afvigelser over en aftalt procent kræver manuel godkendelse.

Step 5: Opdatér readiness-advarslen

Final Review skal læse samme statusendpoint og vise en konkret freshness-policy, fx:

  • grøn: ≤ 30 dage
  • gul: 3160 dage
  • blokerende/rød: > 60 dage, hvis fast pris skal sendes

Den endelige grænse aftales med Alex/Jannick.

Step 6: Dokumentér importen

Gem batch-id, filhash, gyldighedsdato, rækkeantal og godkender uden at committe selve prisfilen, hvis den er fortrolig.


Spor C — Samlet regression, rollout og drift

Task 7: Gør månedlig tømrer-audit til release-gate

Objective: Bevis at det observerede produktionsflow er stabilt efter rettelserne.

Files:

  • Modify: tests/monthly-carpenter-audit.spec.js
  • Modify: .github/workflows/ci.yml
  • Create: docs/qa/CARPENTER_WALKTHROUGH_RELEASE_GATE.md

Step 1: Opdatér testen uden at skjule performanceproblemer

Testen skal logge faktisk hydration-tid og fejle på en eksplicit SLA i stedet for Playwrights implicitte 5-sekunders expect-timeout.

Step 2: Tilføj acceptance checks

  • ingen 5xx API-responses
  • ingen console errors
  • én fælles unlinked-material count
  • ny PDF viser PDF er opdateret
  • PDF preview starter med %PDF-
  • PDF er højst 3 sider
  • ingen vandret mobil-overflow
  • ingen afsendelse, hvis readiness er blokeret
  • afsendelse kan aktiveres, når alle materiale-id'er og prisfreshness er i orden

Step 3: Kør fem gange

cd tests
for i in 1 2 3 4 5; do
  PLAYWRIGHT_BASE_URL=http://127.0.0.1:4032 \
  npx playwright test monthly-carpenter-audit.spec.js --project=chromium --reporter=list || exit 1
done

Expected: 5/5 PASS og dokumenteret p95 hydration-tid.

Step 4: CI-gate

Gør tømrer-audit blokerende for relevante PR'er. Fjern continue-on-error for denne gate. Artifact-upload må fortsat være best-effort.

Step 5: Commit

git add tests/monthly-carpenter-audit.spec.js \
  .github/workflows/ci.yml \
  docs/qa/CARPENTER_WALKTHROUGH_RELEASE_GATE.md
git commit -m "ci: gate releases on carpenter walkthrough"

Task 8: Samlet kvalitetssikring og produktion

Objective: Verificér hele systemet og deploy uden at miste rollback-muligheden.

Step 1: Fuld lokal gate

npm run lint
npm run build
npm run test-minimal
cd frontend && CI=true npm test -- --watchAll=false
cd ../tests && npx playwright test monthly-carpenter-audit.spec.js --project=chromium --reporter=list

Expected:

  • backend: alle suites grønne
  • frontend: alle suites grønne
  • tømrer-audit: PASS
  • PDF: frisk status, gyldig og ≤ 3 sider

Step 2: Uafhængigt code review

Kør security scan og independent staged-diff review. Ingen blocking security- eller logic findings må være åbne.

Step 3: PR og CI

Opret én kode-PR for Spor A/C. Hold STARK-dataimportens driftslog separat. CI skal være grøn før merge.

Step 4: Deploy

  • backup database
  • merge til main
  • byg frontend
  • restart PM2 uden --update-env
  • verificér /api/health og produktion HTTP 200

Step 5: Produktionstest

Genkør auditprojekt 392. Kontrollér:

  • samme material count overalt
  • ingen ukoblede linjer efter review
  • frisk STARK-status efter Jannicks fil
  • PDF frisk efter generering
  • Ordrestyring-knap kun aktiv ved readyToSend

Step 6: Rollback-kriterier

Rollback ved:

  • 5xx i tømrerflow
  • ændret økonomisk total uden godkendt datagrundlag
  • mistede projektmaterialer
  • PDF uden %PDF- eller > 3 sider
  • falsk readyToSend

Prioriteret rækkefølge

  1. PDF freshness — lille, deterministisk og direkte testfejl.
  2. Fælles readiness-model — fjerner 11/12- og “klar/blokeret”-forvirringen.
  3. Projektindlæsning/performance — parallelisering og måling.
  4. Materiale-koblingsflow — løser den reelle Ordrestyring-blokering sikkert.
  5. Jannicks STARK-fil — valider/importér som separat datagave.
  6. Release-gate og samlet produktionstest.

Risici og beslutninger

  • Ingen automatisk materiale-id-gætning: Tvetydige matches kræver menneskelig bekræftelse.
  • Ingen skjult prisopdatering: Jannicks fil importeres først efter backup, validering og afvigelseskontrol.
  • Performance måles koldt og varmt: En enkelt hurtig cachet gennemgang er ikke bevis.
  • Readiness skal være fail-closed: Ukoblede materialer eller forældet prisgrundlag må ikke fremstilles som afsendelsesklar.
  • PDF-signatur må være stabil: Brug normaliserede forretningsværdier, ikke React object references.

Definition of Done

  • Månedlig tømrer-audit passerer 5/5.
  • Ingen API-/konsolfejl.
  • Hydration p95 er under aftalt SLA.
  • PDF er frisk efter generering og stale først efter en reel ændring.
  • ProjectFlow og Final Review viser samme unlinked-material count.
  • Projekt 392 kan få alle relevante materialer koblet via bekræftet review.
  • Ny STARK-status er verificeret efter Jannicks leverance.
  • Ordrestyring-afsendelse er kun mulig, når readyToSend === true.
  • Fuld lint, build, backend, frontend og Playwright gate er grøn.