19 KiB
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,readyForReviewogreadyToSend 4/4 gennemførtmå ikke betydeklar 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_startedproject_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ørtKlar til review — ikke klar til afsendelse11 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_idprice_sourceprice_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æftpr. 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.jsbackend/src/services/starkImportService.jsPOST /api/stark/uploadGET /api/stark/statusGET /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_pricesfor 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_importsvarende 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: 31–60 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/healthog 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
- PDF freshness — lille, deterministisk og direkte testfejl.
- Fælles readiness-model — fjerner 11/12- og “klar/blokeret”-forvirringen.
- Projektindlæsning/performance — parallelisering og måling.
- Materiale-koblingsflow — løser den reelle Ordrestyring-blokering sikkert.
- Jannicks STARK-fil — valider/importér som separat datagave.
- 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.