Human overview · for understanding
The 8 hand-found funnel bugs + the two pipeline columns + the label rename, turned into an executable red→green checklist. · 2026-06-22
The 8 hand-found funnel bugs + the two pipeline columns + the label rename, turned into an executable red→green checklist.
Master summary — the gist in 30 seconds
Inputs: the founders' bug report + the live codebase. Outputs: checklist.md (14 boxes) + 2 ADRs + 14 QA stubs in board-card-qa + the cold-start prompt for the builder. The two ⚠️ blockers are BUG-01 (6 duplicate 'Send' clicks → API spam, no loading state) and BUG-06 (card doesn't move stage after Send and after Sign).
flowchart LR
A[Founders<br/>live test] --> B[8 bugs +<br/>FR-01/02]
B --> C{Code trace<br/>D1-D4}
C --> D[11 locality<br/>clusters]
D --> E[checklist.md<br/>14 check-IDs]
E --> F[board-card-qa<br/>14 stubs]
E --> G[2 ADRs]
F --> H[Instance 3<br/>red to green]
G --> H
Inputs: stage_key contract_signed / deposit_paid (already canonical GHL stages). Outputs: two new UI columns. The map lives in FOUR places that must change in lockstep — _STAGE_TO_COL (twice, board_view.py + dash.py), _COL_TO_STAGE, and the JS STAGES/MOVE_STAGES arrays.
contract_signed and proposal_sent both pointing at the SAME column. Splitting the column is the fix. The known duplication of _STAGE_TO_COL is the trap: miss one copy and a card silently vanishes.flowchart TB S1[proposal_sent] --> C1[Sign FUP] S2[contract_signed] -->|was| C1 S2 -->|now| C2[Proposal Signed<br/>Fizetes FUP] S3[deposit_paid] -->|was| C3[Ongoing build] S3 -->|now| C4[Payment Arrived<br/>Adatbekero FUP] C2 -.overdue >3d.-> R((red border))
proposal_sent (flows.py:2653), Sign → contract_signed (flows.py:766), Pay → deposit_paid (flows.py:647) all already fire. The card looked stuck because of the collapsed column (Cluster 1) + the 120s board cache with no client auto-reload.Inputs: a correct backend stage write. Outputs: a visible card move. The fix is a CONVENTION: every board-visible transition emits push_dash_event + busts the WEB-container board cache, and the frontend calls loadBoard(true) on success. No client-side auto-poll (that's a hard invariant).
flowchart LR W[stage write<br/>_sync_stage] --> E[push_dash_event] W --> X[bust board cache] X --> R[loadBoard true<br/>fresh=1] E --> R R --> V((card moves<br/>no F5))
Inputs: N rapid clicks. Outputs: exactly 1 arm + 1 log row. Reuse the existing generate-button disable pattern (dash_js.py:1271-1279) for the arm/route/send buttons; make api_seq_arm a no-op if already armed.
flowchart LR
C[6 clicks] --> B{button<br/>disabled?}
B -->|yes| N[5 no-ops]
B -->|1st| A[arm POST]
A --> I{already<br/>armed?}
I -->|yes| K[ok, no re-stamp]
I -->|no| S[stamp once<br/>1 log row]
Inputs: a re-delivered self-sent mail. Outputs: dropped as outbound, no reply row, sequence intact. Guard in handle_missive_incoming BEFORE classify: if the newest from-addr _is_ours → drop and return.
_is_ours is available — not inside the pure router. Logging the send at send-time also fixes the wrong-order timestamp.
flowchart TB
M[self-sent mail<br/>re-ingested] --> G{from is ours?}
G -->|yes| D[drop: outbound<br/>no reply row]
G -->|no| C[classify +<br/>route_inbound]
C -.bug.-> X[cancel seq +<br/>resurface lead]
D --> OK((sequence +<br/>stage intact))
handle_own_booking creates the Meet first; if that fails it early-returns 'calendar_failed' BEFORE logging the booking. So a GCal blip = no touchpoint, no stage move, nothing to recover.Inputs: a booking + a failing Meet API. Outputs: the booking still logged + an ok-with-warning toast. Restructure the failure branch to log internally + sync stage + tell the rep to send a link by hand.
flowchart LR
B[book ZZ lead] --> M{Meet created?}
M -->|yes| L1[log Call booked<br/>+ appt_booked]
M -->|no, was| Z[early-return<br/>nothing logged]
M -->|no, now| L2[STILL log +<br/>warn toast]
price_after_expiry, so the strike-through never rendered. BUG-08: a measured board-perf trim. FR-02: rename 'Megbízó dátum' → 'Aláírás dátuma'.Each is a one-seam fix: call sequences.stop in manual paths, set price_after_expiry = price × 1.25, baseline-then-trim the worst board offender, one approved copy edit.
mindmap
root((4 sharp fixes))
BUG-02
manual action<br/>clears dashed border
BUG-04
set price_after_expiry<br/>strike-through renders
BUG-08
baseline then<br/>trim worst offender
FR-02
Megbizo datum to<br/>Alairas datuma
Inputs: checklist.md + 14 board-card-qa stubs. Outputs: 14 green boxes + a Run log. Everything runs ZZ-only, send-blocklisted, AUTOSEND off — the 8 real deals are never touched.
flowchart LR
ST[stub] --> WR[write assert]
WR --> RED{fails red?}
RED -->|no| WR
RED -->|yes| ED[edit cluster]
ED --> DP[deploy once]
DP --> GR{PASS?}
GR -->|no| ED
GR -->|yes| RF[refactor<br/>stay green]
RF --> TK((tick + log))