diff --git a/docs/README.md b/docs/README.md index 46dda4d..9a5b25a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,9 +21,8 @@ | Doc | What's in it | | --- | --- | -| [`qfd.md`](qfd.md) | Quality Function Deployment — turns user-facing requirements into technical HOWs. | -| [`quality-house.md`](quality-house.md) | The 14 WHATs × 14 HOWs House of Quality, filled in. | -| [`quality-house-empty.md`](quality-house-empty.md) | The same chassis, blank — for re-scoring from scratch. | +| [`qfd.md`](qfd.md) | Quality Function Deployment — turns user-facing requirements into technical HOWs; §3 is the filled 14 WHATs × 14 HOWs House of Quality. | +| [`quality-house-empty.md`](quality-house-empty.md) | The House chassis, blank — for re-scoring from scratch. | ## Bench work diff --git a/docs/qfd.md b/docs/qfd.md index d41c838..0fe69bf 100644 --- a/docs/qfd.md +++ b/docs/qfd.md @@ -15,11 +15,12 @@ with the v0.2–v1.0 trajectory ([README](../README.md), glossary at [`../CONTEXT.md`](../CONTEXT.md). Format inspired by the classic House of Quality, kept compact. Strength -weights: **9** strong, **3** medium, **1** weak, blank none. The §3 -matrix and §4 roof — plus a guessed competitor perception zone — live -in [`quality-house.md`](quality-house.md); this file owns the WHAT/HOW -catalogues (§1, §2), the narrative reading of the numbers (§3, §4), and -the downstream sections (§5–§8). +weights: **9** strong, **3** medium, **1** weak, blank none. This one file +owns everything: the WHAT/HOW catalogues (§1, §2); the House diagram itself — +matrix, roof, basement Σ, and the guessed competitor perception zone — in §3; +the narrative reading of the numbers (§3, §4); and the downstream sections +(§5–§8). (The House was a separate `quality-house.md` until 2026-07-11, merged +into §3 to end the mirror-drift between the two files.) --- @@ -104,11 +105,580 @@ Breakdown + levers: [`notes/boot-time-budget.md`](notes/boot-time-budget.md). ## 3. House of Quality — WHATs × HOWs -The matrix (row × column = how strongly characteristic H advances requirement -W) and its Σ row — the weighted vote `Σ(weight × strength)` on which -characteristics deserve the most engineering attention — live in -[`quality-house.md`](quality-house.md). The Σ totals quoted below are -from its basement row. +This section is the House itself: §1's WHATs (rows) × §2's HOWs (columns), each +cell scoring how strongly a characteristic advances a requirement (9 / 3 / 1 / +blank). The roof carries the §4 HOW-vs-HOW correlations; the basement carries the +v0.1 targets (from §2), the weighted-vote sums `Σ = Σ(W weight × cell strength)`, +and rounded relative weights. The right-hand zone scores five products against +the WHATs (0–5, **guessed, not measured** — see +[Perception scores](#perception-scores-guessed)). The Σ totals quoted in the +priority list below come from the basement. + +> **Single source of truth.** The `\foreach` blocks in the diagram restate §1's +> weights and §2's targets — TikZ can't read the tables, so keep them in sync when +> either changes, and **recompute the basement Σ / Rel % here** (see +> [Regenerating](#regenerating)) rather than transcribing them from elsewhere. +> This mirror used to live in a separate `quality-house.md`; it was merged into §3 +> on 2026-07-11 so the two can no longer silently drift. + +For a blank version of the same chassis (WHATs, HOWs, importance, and v0.1 +targets kept; relations + roof + Σ basement left empty for practice), see +[`quality-house-empty.md`](quality-house-empty.md). + +```tikz +% ===================================================================== +% QFD "House of Quality" preamble +% ===================================================================== +\usetikzlibrary{arrows.meta, positioning, shapes.geometric, shapes.misc, calc, fit, backgrounds} + +\newif\ifqfdshowroof \qfdshowrooftrue +\newif\ifqfdshowbasement \qfdshowbasementtrue +\newif\ifqfdshowcompetitive \qfdshowcompetitivetrue +\newif\ifqfdshowlegend \qfdshowlegendtrue +\newif\ifqfdshowimportance \qfdshowimportancetrue +\newif\ifqfdshowcorrlegend \qfdshowcorrlegendtrue +\newif\ifqfdshowevallegend \qfdshowevallegendtrue + +\def\qfdNW{5} +\def\qfdNH{5} +\def\qfdWhatW{4.0} +\def\qfdImpW{0.9} +\def\qfdCmpW{3} +\def\qfdHdrH{2.6} +\def\qfdBasementN{4} + +\def\qfdWhatsTitle{Customer needs} +\def\qfdImpTitle{Imp.\ \%} +\def\qfdPerceptionTitle{Comparative evaluation} +\def\qfdPoorLabel{poor} +\def\qfdExcellentLabel{excellent} +\def\qfdAltOneLabel{Typoena} +\def\qfdAltTwoLabel{Competitor A} +\def\qfdAltThreeLabel{Competitor B} +\def\qfdRelTitle{Relation} +\def\qfdCorrTitle{Correlation} +\def\qfdEvalTitle{Evaluation} + +\tikzset{ + qfdthin/.style ={line width=0.35pt}, + qfdmed/.style ={line width=0.7pt}, + qfdstrong/.style={circle, draw, fill=black, + minimum size=7pt, inner sep=0pt}, + qfdmod/.style ={circle, draw, + minimum size=7pt, inner sep=0pt, line width=0.8pt}, + qfdweak/.style ={regular polygon, regular polygon sides=3, draw, + minimum size=8.5pt, inner sep=0pt, line width=0.7pt}, + qfdrel/.is choice, + qfdrel/S/.style={qfdstrong}, + qfdrel/M/.style={qfdmod}, + qfdrel/W/.style={qfdweak}, + qfdalt1mk/.style={circle, draw, fill=black, + minimum size=6pt, inner sep=0pt, line width=1pt}, + qfdalt1ln/.style={line width=1.2pt}, + qfdalt2mk/.style={regular polygon, regular polygon sides=3, draw, + fill=black, minimum size=6pt, inner sep=0pt, + line width=0.7pt}, + qfdalt2ln/.style={line width=0.7pt, dashed}, + qfdalt3mk/.style={rectangle, draw, fill=black, + minimum size=5pt, inner sep=0pt, line width=0.7pt}, + qfdalt3ln/.style={line width=0.7pt, dotted}, +} + +\newcommand{\qfdDrawGrid}{% + \foreach \c in {1,...,\qfdNHm} \draw[qfdthin] (\c, 0) -- (\c, -\qfdNW); + \foreach \r in {1,...,\qfdNWm} \draw[qfdthin] (0, -\r) -- (\qfdNH, -\r); + \foreach \r in {1,...,\qfdNWm} + \draw[qfdthin] (\qfdLeftEdge, -\r) -- (0, -\r); + \ifqfdshowroof + \foreach \c in {1,...,\qfdNHm} + \draw[qfdthin] (\c, 0) -- (\c, \qfdHdrH); + \fi + \ifqfdshowcompetitive + \foreach \r in {1,...,\qfdNWm} + \draw[qfdthin] (\qfdNH, -\r) -- (\qfdNH+\qfdCmpW, -\r); + \fi + \ifqfdshowbasement + \foreach \r in {1,...,\qfdBasementN} + \draw[qfdthin] (0, -\qfdNW-\r) -- (\qfdNH, -\qfdNW-\r); + \foreach \c in {1,...,\qfdNHm} + \draw[qfdthin] (\c, -\qfdNW) -- (\c, -\qfdNW-\qfdBasementN); + \fi +} + +\newcommand{\qfdDrawRoof}{% + \ifqfdshowroof + \foreach \k in {1,...,\qfdNHm} {% + \pgfmathsetmacro{\rx}{(\k+\qfdNH)/2} + \pgfmathsetmacro{\ry}{\qfdHdrH + (\qfdNH-\k)/2} + \pgfmathsetmacro{\lx}{\k/2} + \pgfmathsetmacro{\ly}{\qfdHdrH + \k/2} + \draw[qfdthin] (\k, \qfdHdrH) -- (\rx, \ry); + \draw[qfdthin] (\k, \qfdHdrH) -- (\lx, \ly); + }% + \draw[qfdmed] (0, \qfdHdrH) + -- (\qfdNH/2, \qfdApexY) -- (\qfdNH, \qfdHdrH); + \foreach \i in {1,...,\qfdNH} + \foreach \k in {1,...,\qfdNH} {% + \pgfmathtruncatemacro{\jj}{\i+\k} + \ifnum\jj>\qfdNH\relax\else + \pgfmathsetmacro{\xx}{\i + \k/2 - 0.5} + \pgfmathsetmacro{\yy}{\qfdHdrH + \k/2} + \coordinate (C-\i-\jj) at (\xx, \yy); + \fi + }% + \fi +} + +\newcommand{\qfdDrawScale}{% + \ifqfdshowcompetitive + \foreach \tk in {0,1,2,3,4,5} {% + \pgfmathsetmacro{\tx}{\qfdNH + (\tk+0.5)*\qfdCmpW/6} + \node[anchor=south, font=\scriptsize] at (\tx, 0.02) {\tk}; + }% + \node[anchor=south, font=\scriptsize\bfseries, align=center, + text width=\qfdCmpW cm] + at ({\qfdNH + \qfdCmpW/2}, 0.7) {\qfdPerceptionTitle}; + \node[anchor=north, font=\scriptsize\itshape] + at ({\qfdNH + 0.45}, -\qfdNW) {\qfdPoorLabel}; + \node[anchor=north, font=\scriptsize\itshape] + at ({\qfdNH + \qfdCmpW - 0.45}, -\qfdNW) {\qfdExcellentLabel}; + \fi +} + +\newcommand{\qfdDrawZoneTitles}{% + \ifqfdshowimportance + \node[rotate=90, anchor=west, font=\footnotesize\bfseries] + at ({-\qfdImpW/2}, 0.12) {\qfdImpTitle}; + \fi + \node[font=\scriptsize\bfseries, align=center, text width=\qfdWhatW cm] + at ({\qfdLeftEdge + \qfdWhatW/2}, + {\ifqfdshowroof \qfdHdrH/2 \else 0.6 \fi}) {\qfdWhatsTitle}; +} + +\newcommand{\qfdDrawFrames}{% + \begin{scope}[qfdmed] + \draw (\qfdLeftEdge, 0) rectangle (\qfdNH, -\qfdNW); + \ifqfdshowimportance \draw (-\qfdImpW, 0) -- (-\qfdImpW, -\qfdNW); \fi + \draw (0, 0) -- (0, -\qfdNW); + \ifqfdshowroof + \draw (0, 0) rectangle (\qfdNH, \qfdHdrH); \fi + \ifqfdshowbasement + \draw (0, -\qfdNW) rectangle (\qfdNH, -\qfdNW-\qfdBasementN); \fi + \ifqfdshowcompetitive + \draw (\qfdNH, 0) rectangle (\qfdNH+\qfdCmpW, -\qfdNW); \fi + \end{scope} +} + +\newcommand{\qfdDrawLegend}{% + \ifqfdshowlegend + \pgfmathsetmacro{\qfdLegX}{% + \qfdNH + \ifqfdshowcompetitive \qfdCmpW + 0.7 \else 0.7 \fi} + \pgfmathsetmacro{\qfdLegBottom}{% + -2.05 + \ifqfdshowroof \ifqfdshowcorrlegend - 2.55 \fi \fi + \ifqfdshowcompetitive \ifqfdshowevallegend - 2.20 \fi \fi} + \pgfmathsetmacro{\qfdLegY}{\qfdHdrH - 0.4} + \begin{scope}[shift={(\qfdLegX, \qfdLegY)}] + \draw[qfdmed, rounded corners=2pt] + (-0.15, 0.4) rectangle (4.5, \qfdLegBottom); + \node[anchor=west, font=\footnotesize\bfseries] at (0, 0.1) + {\qfdRelTitle}; + \draw[qfdthin] (0, -0.15) -- (4.35, -0.15); + \node[qfdstrong] at (0.22, -0.5) {}; + \node[anchor=west] at (0.5, -0.5) {Strong (9)}; + \node[qfdmod] at (0.22, -0.95) {}; + \node[anchor=west] at (0.5, -0.95) {Medium (3)}; + \node[qfdweak] at (0.22, -1.4) {}; + \node[anchor=west] at (0.5, -1.4) {Weak (1)}; + \ifqfdshowroof \ifqfdshowcorrlegend + \node[anchor=west, font=\footnotesize\bfseries] at (0, -2.10) + {\qfdCorrTitle}; + \draw[qfdthin] (0, -2.35) -- (4.35, -2.35); + \node[anchor=west] at (0, -2.70) {{$+\!+$}\quad very positive}; + \node[anchor=west] at (0, -3.05) {{$+$\phantom{$+$}}\quad positive}; + \node[anchor=west] at (0, -3.40) {{$-$\phantom{$-$}}\quad negative}; + \node[anchor=west] at (0, -3.75) {{$-\!-$}\quad very negative}; + \fi \fi + \ifqfdshowcompetitive \ifqfdshowevallegend + \pgfmathsetmacro{\qfdEvalTop}{% + -2.10 \ifqfdshowroof\ifqfdshowcorrlegend - 2.55 \fi\fi} + \node[anchor=west, font=\footnotesize\bfseries] + at (0, \qfdEvalTop) {\qfdEvalTitle}; + \pgfmathsetmacro{\qfdEvalSep}{\qfdEvalTop - 0.25} + \draw[qfdthin] (0, \qfdEvalSep) -- (4.35, \qfdEvalSep); + \pgfmathsetmacro{\qfdLegA}{\qfdEvalTop - 0.55} + \draw[qfdalt1ln] (0.05, \qfdLegA) -- (0.45, \qfdLegA); + \node[qfdalt1mk] at (0.25, \qfdLegA) {}; + \node[anchor=west, font=\bfseries] at (0.55, \qfdLegA) + {\qfdAltOneLabel}; + \pgfmathsetmacro{\qfdLegB}{\qfdEvalTop - 0.95} + \draw[qfdalt2ln] (0.05, \qfdLegB) -- (0.45, \qfdLegB); + \node[qfdalt2mk] at (0.25, \qfdLegB) {}; + \node[anchor=west] at (0.55, \qfdLegB) {\qfdAltTwoLabel}; + \pgfmathsetmacro{\qfdLegC}{\qfdEvalTop - 1.35} + \draw[qfdalt3ln] (0.05, \qfdLegC) -- (0.45, \qfdLegC); + \node[qfdalt3mk] at (0.25, \qfdLegC) {}; + \node[anchor=west] at (0.55, \qfdLegC) {\qfdAltThreeLabel}; + \fi \fi + \end{scope} + \fi +} + +\newenvironment{qfdhouse}{% + \begin{tikzpicture}[x=1cm, y=1cm, font=\scriptsize, + line cap=round, line join=round] + \ifqfdshowimportance + \pgfmathsetmacro{\qfdLeftEdge}{-\qfdWhatW-\qfdImpW} + \else + \pgfmathsetmacro{\qfdLeftEdge}{-\qfdWhatW} + \fi + \pgfmathsetmacro{\qfdApexY}{\qfdHdrH + \qfdNH/2} + \pgfmathtruncatemacro{\qfdNHm}{\qfdNH - 1} + \pgfmathtruncatemacro{\qfdNWm}{\qfdNW - 1} + \qfdDrawGrid + \qfdDrawRoof + \qfdDrawScale + \qfdDrawZoneTitles +}{% + \qfdDrawFrames + \qfdDrawLegend + \end{tikzpicture}% +} + +% --- Dimensions tuned for the typewriter QFD (14 W x 15 H) --- +\def\qfdNW{14} +\def\qfdNH{14} +\def\qfdWhatW{4.6} +\def\qfdImpW{0.7} +\def\qfdHdrH{5.0} +\def\qfdBasementN{3} +\def\qfdCmpW{3.4} +\qfdshowlegendfalse % we draw a 4-alternative legend manually + +\def\qfdWhatsTitle{User-facing requirements (W)} +\def\qfdImpTitle{Weight} +\def\qfdPerceptionTitle{Competitive perception\\(0–5, guessed)} +\def\qfdPoorLabel{poor} +\def\qfdExcellentLabel{excellent} + +% Perception-zone markers: shape + colour-blind-safe colour per product. +% Palette is Okabe-Ito (blue, vermillion, bluish green, reddish purple). +% Light fills + saturated outlines keep stacked markers legible. +\definecolor{qfdcTypoena}{RGB}{0,114,178} +\definecolor{qfdcRem}{RGB}{213,94,0} +\definecolor{qfdcFrw}{RGB}{0,158,115} +\definecolor{qfdcPom}{RGB}{204,121,167} +\definecolor{qfdcFrwS}{RGB}{86,180,233} + +\tikzset{ + qfdalt1mk/.style={circle, draw=qfdcTypoena, fill=qfdcTypoena!55!white, + minimum size=6.5pt, inner sep=0pt, line width=1.1pt}, + qfdalt1ln/.style={line width=1.2pt, qfdcTypoena}, + qfdalt2mk/.style={regular polygon, regular polygon sides=3, + draw=qfdcRem, fill=qfdcRem!55!white, + minimum size=7pt, inner sep=0pt, line width=0.9pt}, + qfdalt2ln/.style={line width=0.8pt, dashed, qfdcRem}, + qfdalt3mk/.style={rectangle, draw=qfdcFrw, fill=qfdcFrw!55!white, + minimum size=5.5pt, inner sep=0pt, line width=0.9pt}, + qfdalt3ln/.style={line width=0.8pt, dotted, qfdcFrw}, + qfdalt4mk/.style={diamond, aspect=1, draw=qfdcPom, + fill=qfdcPom!50!white, + minimum size=7pt, inner sep=0pt, line width=1.0pt}, + qfdalt4ln/.style={line width=0.8pt, dash dot, qfdcPom}, + qfdalt5mk/.style={regular polygon, regular polygon sides=5, + draw=qfdcFrwS, fill=qfdcFrwS!40!white, + minimum size=5.5pt, inner sep=0pt, line width=0.8pt}, + qfdalt5ln/.style={line width=0.7pt, dash dot dot, qfdcFrwS}, +} + +\begin{document} +\begin{qfdhouse} + + % ---------- WHATs (left column) ---------- + % Box width is 0.2 cm narrower than the column so labels have 0.1 cm + % clearance on each side and don't bleed into the Weight column. + \pgfmathsetmacro{\qfdWhatTextW}{\qfdWhatW - 0.2} + \foreach \r/\t in {% + 1/{W1 Sub-second visible response to typing}, + 2/{W2 Publishing is one deliberate action away}, + 3/{W3 Pulling power never corrupts the file}, + 4/{W4 Provisioning never interrupts a writing session}, + 5/{W5 Quick boot to a writing cursor}, + 6/{W6 Long sessions without crash, lag, drift}, + 7/{W7 Nothing on the device competes with prose}, + 8/{W8 The UI never moves except when I move it}, + 9/{W9 Codebase absorbs the planned roadmap}, + 10/{W10 I can repair or fork it with hobbyist tools}, + 11/{W11 Multi-day battery life (v0.8 onward)}, + 12/{W12 Local-only files coexist with git scope (v0.5+)}, + 13/{W13 Typography sets a writing-tool tone}, + 14/{W14 I can carry the device and write away from a desk}% + } + \node[anchor=west, font=\scriptsize, + text width=\qfdWhatTextW cm, align=left] + at ({\qfdLeftEdge + 0.1}, {-\r + 0.5}) {\t}; + + % ---------- Importance (raw 1-10 weight) ---------- + \foreach \r/\w in {1/10, 2/9, 3/10, 4/7, 5/6, 6/9, 7/8, 8/7, + 9/8, 10/5, 11/4, 12/5, 13/7, 14/8} + \node[font=\scriptsize] at ({-\qfdImpW/2}, {-\r + 0.5}) {\w}; + + % ---------- HOWs (rotated column titles) ---------- + \foreach \c/\t in {% + 1/{H1 Type latency}, + 2/{H2 Refresh area per keystroke}, + 3/{H3 Full-refresh cadence}, + 4/{H4 Boot latency (cold)}, + 5/{H5 Continuous-typing endurance}, + 6/{H6 Publish reliability}, + 7/{H7 Publish latency}, + 8/{H8 Save durability}, + 9/{H9 PSRAM heap headroom}, + 10/{H10 Firmware binary size}, + 11/{H11 Total stack budget}, + 12/{H12 Network reconnect time}, + 13/{H13 Idle / typing / push current}, + 14/{H15 Clean release build time}% + } + \node[rotate=90, anchor=west, font=\scriptsize] + at ({\c - 0.5}, 0.15) {\t}; + + % ---------- Relation matrix (S=9, M=3, W=1) ---------- + % W1 row 1: H1S H2S H3M H5M H9W H11W + \node[qfdrel/S] at ({1 - 0.5}, {-1 + 0.5}) {}; + \node[qfdrel/S] at ({2 - 0.5}, {-1 + 0.5}) {}; + \node[qfdrel/M] at ({3 - 0.5}, {-1 + 0.5}) {}; + \node[qfdrel/M] at ({5 - 0.5}, {-1 + 0.5}) {}; + \node[qfdrel/W] at ({9 - 0.5}, {-1 + 0.5}) {}; + \node[qfdrel/W] at ({11 - 0.5}, {-1 + 0.5}) {}; + + % W2 row 2: H6S H7M H9S H12S + \node[qfdrel/S] at ({6 - 0.5}, {-2 + 0.5}) {}; + \node[qfdrel/M] at ({7 - 0.5}, {-2 + 0.5}) {}; + \node[qfdrel/S] at ({9 - 0.5}, {-2 + 0.5}) {}; + \node[qfdrel/S] at ({12 - 0.5}, {-2 + 0.5}) {}; + + % W3 row 3: H8S + \node[qfdrel/S] at ({8 - 0.5}, {-3 + 0.5}) {}; + + % W4 row 4: H6M H12M + \node[qfdrel/M] at ({6 - 0.5}, {-4 + 0.5}) {}; + \node[qfdrel/M] at ({12 - 0.5}, {-4 + 0.5}) {}; + + % W5 row 5: H4S H10M + \node[qfdrel/S] at ({4 - 0.5}, {-5 + 0.5}) {}; + \node[qfdrel/M] at ({10 - 0.5}, {-5 + 0.5}) {}; + + % W6 row 6: H1M H3M H5S H6M H8M H9S H11M H12M + \node[qfdrel/M] at ({1 - 0.5}, {-6 + 0.5}) {}; + \node[qfdrel/M] at ({3 - 0.5}, {-6 + 0.5}) {}; + \node[qfdrel/S] at ({5 - 0.5}, {-6 + 0.5}) {}; + \node[qfdrel/M] at ({6 - 0.5}, {-6 + 0.5}) {}; + \node[qfdrel/M] at ({8 - 0.5}, {-6 + 0.5}) {}; + \node[qfdrel/S] at ({9 - 0.5}, {-6 + 0.5}) {}; + \node[qfdrel/M] at ({11 - 0.5}, {-6 + 0.5}) {}; + \node[qfdrel/M] at ({12 - 0.5}, {-6 + 0.5}) {}; + + % W7 row 7: H1M H2M H3M H13M + \node[qfdrel/M] at ({1 - 0.5}, {-7 + 0.5}) {}; + \node[qfdrel/M] at ({2 - 0.5}, {-7 + 0.5}) {}; + \node[qfdrel/M] at ({3 - 0.5}, {-7 + 0.5}) {}; + \node[qfdrel/M] at ({13 - 0.5}, {-7 + 0.5}) {}; + + % W8 row 8: H1W H2S H3S + \node[qfdrel/W] at ({1 - 0.5}, {-8 + 0.5}) {}; + \node[qfdrel/S] at ({2 - 0.5}, {-8 + 0.5}) {}; + \node[qfdrel/S] at ({3 - 0.5}, {-8 + 0.5}) {}; + + % W9 row 9: H10W H11W H15M + \node[qfdrel/W] at ({10 - 0.5}, {-9 + 0.5}) {}; + \node[qfdrel/W] at ({11 - 0.5}, {-9 + 0.5}) {}; + \node[qfdrel/M] at ({14 - 0.5}, {-9 + 0.5}) {}; + + % W10 row 10: H10M H13W H15W + \node[qfdrel/M] at ({10 - 0.5}, {-10 + 0.5}) {}; + \node[qfdrel/W] at ({13 - 0.5}, {-10 + 0.5}) {}; + \node[qfdrel/W] at ({14 - 0.5}, {-10 + 0.5}) {}; + + % W11 row 11: H13S + \node[qfdrel/S] at ({13 - 0.5}, {-11 + 0.5}) {}; + + % W12 row 12: H6W H8M + \node[qfdrel/W] at ({6 - 0.5}, {-12 + 0.5}) {}; + \node[qfdrel/M] at ({8 - 0.5}, {-12 + 0.5}) {}; + + % W13 row 13: H9M + \node[qfdrel/M] at ({9 - 0.5}, {-13 + 0.5}) {}; + + % W14 row 14: H4W H8M H12M H13S + \node[qfdrel/W] at ({4 - 0.5}, {-14 + 0.5}) {}; + \node[qfdrel/M] at ({8 - 0.5}, {-14 + 0.5}) {}; + \node[qfdrel/M] at ({12 - 0.5}, {-14 + 0.5}) {}; + \node[qfdrel/S] at ({13 - 0.5}, {-14 + 0.5}) {}; + + % ---------- Roof correlations ---------- + \node[font=\scriptsize] at (C-1-2) {$+\!+$}; % H1-H2 strong reinforce + \node[font=\scriptsize] at (C-1-3) {$-$}; % H1-H3 mild conflict + \node[font=\scriptsize] at (C-1-5) {$+$}; % H1-H5 mild reinforce + \node[font=\scriptsize] at (C-1-13) {$-$}; % H1-H13 mild conflict + \node[font=\scriptsize] at (C-2-3) {$+\!+$}; % H2-H3 strong reinforce + \node[font=\scriptsize] at (C-2-13) {$+$}; % H2-H13 + \node[font=\scriptsize] at (C-3-13) {$+$}; % H3-H13 + \node[font=\scriptsize] at (C-4-10) {$-$}; % H4-H10 boot vs binary + \node[font=\scriptsize] at (C-5-6) {$+$}; % H5-H6 + \node[font=\scriptsize] at (C-5-8) {$+$}; % H5-H8 + \node[font=\scriptsize] at (C-5-9) {$-\!-$}; % H5-H9 soak vs heap + \node[font=\scriptsize] at (C-6-7) {$+$}; % H6-H7 + \node[font=\scriptsize] at (C-6-9) {$-\!-$}; % H6-H9 push vs heap + \node[font=\scriptsize] at (C-6-12) {$+\!+$}; % H6-H12 + \node[font=\scriptsize] at (C-7-9) {$-$}; % H7-H9 + \node[font=\scriptsize] at (C-7-12) {$+\!+$}; % H7-H12 + \node[font=\scriptsize] at (C-9-10) {$-\!-$}; % H9-H10 heap vs binary + \node[font=\scriptsize] at (C-10-14) {$-\!-$}; % H10-H15 binary vs build + \node[font=\scriptsize] at (C-11-13) {$-$}; % H11-H13 + + % ---------- Basement: target / abs weight / rel weight % ---------- + \foreach \c/\tgt/\abs/\rel in {% + 1/{$\leq$200\,ms}/148/10, + 2/{$\leq$1 line}/177/11, + 3/{1 : 64}/144/9, + 4/{$\leq$5\,s}/62/4, + 5/{$\geq$1\,h}/111/7, + 6/{$\geq$95\,\%}/134/9, + 7/{$\leq$30\,s}/27/2, + 8/{100\,\%}/156/10, + 9/{$\geq$1\,MB}/193/12, + 10/{$\leq$2\,MB}/41/3, + 11/{$\leq$80\,KB}/45/3, + 12/{$\leq$30\,s}/153/10, + 13/{obs.}/137/9, + 14/{$\leq$7\,min}/29/2% + } { + \node[font=\scriptsize] at ({\c - 0.5}, {-\qfdNW - 0.5}) {\tgt}; + \node[font=\scriptsize] at ({\c - 0.5}, {-\qfdNW - 1.5}) {\abs}; + \node[font=\scriptsize\bfseries] + at ({\c - 0.5}, {-\qfdNW - 2.5}) {\rel}; + } + + % ---------- Basement row labels (in the margin below WHATs) ---------- + \foreach \k/\lbl in {1/{Target (v0.1)}, 2/{$\Sigma$ abs}, 3/{Rel.\ \%}} + \node[anchor=east, font=\scriptsize\itshape] + at ({-0.1}, {-\qfdNW - \k + 0.5}) {\lbl}; + + % ---------- Perception zone: 5 products x 14 WHATs (0-5 scores) ---------- + % Columns: \so=Typoena target, \st=reMarkable 2 + Type Folio, + % \sf=Freewrite Traveler, \sg=Pomera DM250, + % \sh=Freewrite Smart Typewriter. + % Pass 1: stash each score as a named coordinate so the profile lines + % below can reuse it without recomputing. + \foreach \r/\so/\st/\sf/\sg/\sh in {% + 1/4/1/4/5/3, + 2/5/4/4/2/4, + 3/4/4/2/2/2, + 4/5/2/2/5/2, + 5/4/3/4/5/4, + 6/3/3/4/5/4, + 7/5/2/5/5/5, + 8/4/3/4/5/4, + 9/4/3/2/1/2, + 10/5/4/2/1/2, + 11/1/5/5/4/5, + 12/3/1/2/3/2, + 13/3/5/2/2/2, + 14/2/4/5/2/5% + } { + \pgfmathsetmacro{\xo}{\qfdNH + (\so + 0.5)*\qfdCmpW/6} + \pgfmathsetmacro{\xt}{\qfdNH + (\st + 0.5)*\qfdCmpW/6} + \pgfmathsetmacro{\xf}{\qfdNH + (\sf + 0.5)*\qfdCmpW/6} + \pgfmathsetmacro{\xg}{\qfdNH + (\sg + 0.5)*\qfdCmpW/6} + \pgfmathsetmacro{\xs}{\qfdNH + (\sh + 0.5)*\qfdCmpW/6} + \coordinate (po-\r) at (\xo, {-\r + 0.5}); + \coordinate (pr-\r) at (\xt, {-\r + 0.5}); + \coordinate (pf-\r) at (\xf, {-\r + 0.5}); + \coordinate (pp-\r) at (\xg, {-\r + 0.5}); + \coordinate (ps-\r) at (\xs, {-\r + 0.5}); + } + + % Pass 2: profile lines per alternative. Drawn before markers so the + % dots sit on top of (not under) the line endpoints. + \draw[qfdalt1ln] (po-1) \foreach \r in {2,...,\qfdNW} { -- (po-\r) }; + \draw[qfdalt2ln] (pr-1) \foreach \r in {2,...,\qfdNW} { -- (pr-\r) }; + \draw[qfdalt3ln] (pf-1) \foreach \r in {2,...,\qfdNW} { -- (pf-\r) }; + \draw[qfdalt4ln] (pp-1) \foreach \r in {2,...,\qfdNW} { -- (pp-\r) }; + \draw[qfdalt5ln] (ps-1) \foreach \r in {2,...,\qfdNW} { -- (ps-\r) }; + + % Pass 3: markers on top of the lines. + \foreach \r in {1,...,\qfdNW} { + \node[qfdalt1mk] at (po-\r) {}; + \node[qfdalt2mk] at (pr-\r) {}; + \node[qfdalt3mk] at (pf-\r) {}; + \node[qfdalt4mk] at (pp-\r) {}; + \node[qfdalt5mk] at (ps-\r) {}; + } + + % ---------- Manual legend (5 alternatives, placed right of zones) ---------- + \pgfmathsetmacro{\qfdLegX}{\qfdNH + \qfdCmpW + 0.7} + \begin{scope}[shift={(\qfdLegX, \qfdHdrH - 0.4)}] + \draw[qfdmed, rounded corners=2pt] + (-0.15, 0.4) rectangle (5.1, -7.50); + % Relations + \node[anchor=west, font=\footnotesize\bfseries] at (0, 0.1) + {Relation}; + \draw[qfdthin] (0, -0.15) -- (4.95, -0.15); + \node[qfdstrong] at (0.22, -0.5) {}; + \node[anchor=west] at (0.5, -0.5) {Strong (9)}; + \node[qfdmod] at (0.22, -0.95) {}; + \node[anchor=west] at (0.5, -0.95) {Medium (3)}; + \node[qfdweak] at (0.22, -1.4) {}; + \node[anchor=west] at (0.5, -1.4) {Weak (1)}; + % Correlation + \node[anchor=west, font=\footnotesize\bfseries] at (0, -2.10) + {Correlation}; + \draw[qfdthin] (0, -2.35) -- (4.95, -2.35); + \node[anchor=west] at (0, -2.70) {{$+\!+$}\quad very positive}; + \node[anchor=west] at (0, -3.05) {{$+$\phantom{$+$}}\quad positive}; + \node[anchor=west] at (0, -3.40) {{$-$\phantom{$-$}}\quad negative}; + \node[anchor=west] at (0, -3.75) {{$-\!-$}\quad very negative}; + % Perception + \node[anchor=west, font=\footnotesize\bfseries] at (0, -4.20) + {Perception}; + \draw[qfdthin] (0, -4.45) -- (4.95, -4.45); + \draw[qfdalt1ln] (0.05, -4.80) -- (0.45, -4.80); + \node[qfdalt1mk] at (0.25, -4.80) {}; + \node[anchor=west, font=\bfseries] at (0.55, -4.80) + {Typoena (v0.1 target)}; + \draw[qfdalt2ln] (0.05, -5.25) -- (0.45, -5.25); + \node[qfdalt2mk] at (0.25, -5.25) {}; + \node[anchor=west] at (0.55, -5.25) {reMarkable 2 + Type Folio}; + \draw[qfdalt3ln] (0.05, -5.70) -- (0.45, -5.70); + \node[qfdalt3mk] at (0.25, -5.70) {}; + \node[anchor=west] at (0.55, -5.70) {Freewrite Traveler}; + \draw[qfdalt5ln] (0.05, -6.15) -- (0.45, -6.15); + \node[qfdalt5mk] at (0.25, -6.15) {}; + \node[anchor=west] at (0.55, -6.15) {Freewrite Smart Typewriter}; + \draw[qfdalt4ln] (0.05, -6.60) -- (0.45, -6.60); + \node[qfdalt4mk] at (0.25, -6.60) {}; + \node[anchor=west] at (0.55, -6.60) {Pomera DM250}; + \node[anchor=west, font=\scriptsize\itshape] at (0, -7.15) + {0 = poor, 5 = excellent}; + \end{scope} + +\end{qfdhouse} +\end{document} +``` + +### Reading the house + +- **Importance (left column)** is the raw 1–10 weight from §1, not a normalised + %, so adding stays cheap when a WHAT shifts. Sum of weights is 103; treat each + unit as ~0.97 % if you want a percentage view. +- **Roof** carries the §4 symbols translated into classical QFD glyphs: + `++` strong reinforcement (`◎`), `+` mild reinforcement (`○`), `−` mild + conflict (`×`), `−−` strong conflict (`⊗`). +- **Basement rows** are: v0.1 target → column sum (`Σ` of `weight × strength`) → + relative weight as integer % of total (1557). Relative weights round to 100. +- **H7, H10, H15** (Publish latency, binary size, build time) sit at the bottom + of the basement — knowingly-paid costs per §7, not signals to optimise harder. ### Top engineering priorities (from importance) @@ -160,6 +730,98 @@ user preference for faster iteration, not matrix-derived priority; if it pushes back against [ADR-001]'s "+5–10 min" pricing, the target moves before the runtime decision does. +### Perception scores (guessed) + +Five products on the 0–5 scale, scored against each WHAT. Reference +configurations: **reMarkable 2 + Type Folio**, **Freewrite Traveler**, +**Freewrite Smart Typewriter**, **Pomera DM250** (DM250 has a reflective +monochrome LCD, not e-ink — flagged in W1 / W8). "Typoena" is the v0.1 +target from §2, not measured yet. + +Freewrite Traveler scores assume the +[Sailfish firmware](https://getfreewrite.com/blogs/writing-success/freewrite-sailfish-firmware) +(released 2025-11-19), which rewrote the OS in Rust, cut keystroke latency +40–100 %, and trimmed power draw −30 % typing / −50 % idle on both +Traveler and Smart Typewriter Gen 3. Three rows rescored upward as a +result: W1 Traveler 3→4 / Smart 2→3 (Smart's larger panel still trails +Traveler by one notch), W5 both 3→4 (boot accelerated, no published +number), W9 both 1→2 (Rust rewrite explicitly unblocked features that +JS could not carry; still closed so neither reaches reMarkable's +hackable-Linux 3). + +| ID | WHAT (truncated) | Typoena | reM. | Frw.T | Frw.S | Pom. | Rationale (shortest defensible) | +| --- | ------------------------------------------------- | :-----: | :--: | :---: | :---: | :--: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| W1 | Sub-second response to typing | 4 | 1 | 4 | 3 | 5 | Typoena targets ≤200 ms; reMarkable e-ink visibly laggy on a typing-focused device — tested less responsive than Smart Typewriter, and latency is so load-bearing for W1 that it earns a 1 not a 2; both Freewrites post-Sailfish trimmed latency 40–100 % (Frw.T plausibly inside 200 ms; Frw.S still trails by one notch on larger panel); Pomera LCD ~zero. | +| W2 | Publishing is one deliberate action away | 5 | 4 | 4 | 4 | 2 | Ctrl-G atomic; reMarkable + Freewrite cloud-sync is one-tap but not git; Pomera = USB/SD copy or QR transfer. | +| W3 | Pulling power never corrupts the file | 4 | 4 | 2 | 2 | 2 | Typoena: atomic-rename + fsync. reMarkable journals. Freewrite + Pomera: forum reports of corruption on yank. | +| W4 | Provisioning never interrupts writing | 5 | 2 | 2 | 2 | 5 | Typoena v0.1: build-time config (dev-only). reM/Frw need Wi-Fi + account. Pomera: literally none. | +| W5 | Quick boot to a writing cursor | 4 | 3 | 4 | 4 | 5 | Typoena target ≤5 s. reMarkable cold-boots ~20 s (great from sleep). Both Freewrites accelerated post-Sailfish (no published number; were ~10–15 s e-ink wake). Pomera ~3 s. | +| W6 | Long sessions without crash / lag / drift | 3 | 3 | 4 | 4 | 5 | Typoena unproven (1 h target). Freewrite famously stable (both variants). Pomera firmware is decades-mature. | +| W7 | Nothing on the device competes with prose | 5 | 2 | 5 | 5 | 5 | reMarkable has apps, menus, drawing, PDFs. Freewrite + Pomera are single-purpose; Typoena by design. | +| W8 | The UI never moves except when I move it | 4 | 3 | 4 | 4 | 5 | reMarkable animates more; Typoena uses dirty-rects; Freewrites minimal motion; Pomera near-static LCD. | +| W9 | Codebase absorbs the planned roadmap | 4 | 3 | 2 | 2 | 1 | Modular Rust Typoena; reMarkable is hackable Linux; both Freewrites carry Sailfish (Rust rewrite explicitly unblocked features JS could not carry) but closed; Pomera closed firmware. | +| W10 | I can repair or fork it with hobbyist tools | 5 | 4 | 2 | 2 | 1 | Typoena: open BOM + ESP32. reMarkable: rooted Linux + community ROMs. Freewrite + Pomera: closed. | +| W11 | Multi-day battery life (v0.8 onward) | 1 | 5 | 5 | 5 | 4 | Typoena v0.1 = wall-powered (battery deferred). reMarkable + both Freewrites legendary (~4 weeks; Sailfish trimmed −30 % typing / −50 % idle). Pomera ~24 h. | +| W12 | Local-only files coexist with git scope | 3 | 1 | 2 | 2 | 3 | Typoena v0.5+ design. reMarkable cloud-only. Freewrites have local + Postbox but no VCS. Pomera = pure local. | +| W13 | Typography sets a writing-tool tone | 3 | 5 | 2 | 2 | 2 | Typoena v0.1: single mono (serif option in v1.0). reMarkable: rich type rendering. Freewrite + Pomera: utilitarian. | +| W14 | I can carry the device and write away from a desk | 2 | 4 | 5 | 1 | 5 | Typoena v0.1 wall-powered (ADR-008), no enclosure spec yet — desk-bound by design. reMarkable + Type Folio bag-friendly with bulk. Freewrite Traveler is the form-factor reference (~1.6 lb, folds). Smart Typewriter ~5 lb, desk-bound. Pomera DM250 pocketable foldable. | + +**Totals** (sum across 14 WHATs, no weighting): Typoena 52, Pomera 50, +Freewrite Traveler 47, reMarkable 44, Freewrite Smart Typewriter 42 +(Traveler pre-Sailfish 44; Smart pre-Sailfish 39; reMarkable W1 dropped +3→2→1 across two rounds of author testing — first to 2 after firsthand +typing, then to 1 once latency was recognised as the dominant W1 +signal). Pomera closing to within 2 of +Typoena is W14 doing what W14 should — surfacing the dimension on +which v0.1's tethered MVP loses ground that v0.8 is expected to +recover. The "Pomera + Wi-Fi + git + hackable BOM" framing from +`README.md` still holds and reads stronger. + +Weighted totals (Σ score × W weight) tell the same story with more +contrast — left as exercise; the unweighted view is enough to read the +picture. + +#### Caveats + +- **Single-rater bias.** All thirteen rows are scored from the project + author's POV. A reMarkable buyer would weight W11 (battery) at 10 and + W12 (git) at 1, flipping the totals. +- **Configuration matters.** Freewrite Smart Typewriter and Traveler are + both tracked; they diverge most on W1 / W5 because of display tech + (Smart's larger panel is slower to refresh). Traveler is still the + more direct competitor on form factor. +- **W3 / W6 Freewrite scores are anecdotal.** Forum reports, not bench + data. Treat the 2 / 4 as "we'd need to test this" rather than fact. +- **No price column.** Typoena-as-BOM is materially cheaper than the + competitors but cost is not a WHAT in §1, so it's absent here. + Worth a row if a v0.x WHAT ever calls it out. + +### Regenerating + +The matrix cells (`\node[qfdrel/{S,M,W}]`), roof symbols (`C-i-j` slots), +and basement Σ + Rel% are native to this file — re-score directly in the +TikZ source, then update the §3 priority list and §4 conflict list in +the narrative above to match the new picture. + +When §1 or §2 changes: + +1. Importance column → §1 weight column. +2. HOW titles + v0.1 targets → §2 target column. +3. Recompute basement Σ for any HOW whose column changed: per-cell + contribution = `(W weight) × (cell strength: 9 / 3 / 1 / 0)`. +4. Recompute relative weight: each Σ ÷ total Σ × 100, rounded to integer + percent. + +Perception scores are **not** derived from §1/§2 — they live only in +this file. Update them when (a) a competitor ships a relevant change, +(b) measurement replaces a guess, or (c) a WHAT is added/removed in §1. +Each score keeps its one-line rationale in the table above. + +If a renderer rejects the `tikz` fence, the file is still readable as +source — the placement comments name each WHAT, HOW, and cell. The +perception-scores table above is the human-readable fallback for the +right-hand zone of the diagram. + --- ## 4. Roof — HOW-vs-HOW tradeoffs @@ -167,9 +829,9 @@ before the runtime decision does. The roof shows where pushing one characteristic pushes another the wrong way. ASCII glyphs (with classical QFD equivalents): **`++`** strong reinforcement (`◎`), **`+`** mild reinforcement (`○`), **`−`** mild -conflict (`×`), **`−−`** strong conflict (`⊗`). The 14×14 roof matrix -lives in [`quality-house.md`](quality-house.md); the cells that -actually shape the design are called out below. +conflict (`×`), **`−−`** strong conflict (`⊗`). The 14×14 roof matrix is +in the §3 diagram; the cells that actually shape the design are called out +below. ### Conflicts that actually shape the design @@ -437,8 +1099,8 @@ These are the live tensions we are watching, not deciding harder: honest reading that "codebase absorbs the planned roadmap" is delivered by ADRs, not by a measurable characteristic. ID "H14" left as a gap (cross-doc HOW references survive without renumbering H15). - Total basement Σ drops 1674 → 1557, so rel% recomputed in - [`quality-house.md`](quality-house.md). + Total basement Σ drops 1674 → 1557, so rel% recomputed in the §3 + basement. - **HOWs renamed "characteristics," not "functions."** A function is a transformation (input → output); HOWs like H6 "success rate" and H10 "binary size" are *measures* of functions or properties of @@ -516,8 +1178,8 @@ README, the product/technical docs, and [ADR-003] are all updated to ~13 lines reality drifts from estimates. - The WHATs (§1) change rarely; the HOWs (§2) change with each release. When either changes, re-score the matrix and recompute the basement Σ - in [`quality-house.md`](quality-house.md); then check the §3 priority - list and §4 conflict list here still match the new picture. + in the §3 diagram; then check the §3 priority list and §4 conflict list + here still match the new picture. [ADR-001]: adr.md#adr-001-language-and-runtime--rust-on-esp-idf-rs-std [ADR-002]: adr.md#adr-002-ui-strategy--custom-widgets-on-embedded-graphics-not-ratatui diff --git a/docs/quality-house-empty.md b/docs/quality-house-empty.md index 1c234fe..de0f396 100644 --- a/docs/quality-house-empty.md +++ b/docs/quality-house-empty.md @@ -1,9 +1,9 @@ # Quality House — empty (training) -Same 14 WHATs × 14 HOWs chassis as [`quality-house.md`](quality-house.md), -with the **relations**, **roof correlations**, and basement Σ / Rel % -**deliberately blank**. Fill them in yourself, then compare against the -populated house to check your reading. +Same 14 WHATs × 14 HOWs chassis as the filled House in +[`qfd.md` §3](qfd.md#3-house-of-quality--whats--hows), with the **relations**, +**roof correlations**, and basement Σ / Rel % **deliberately blank**. Fill them +in yourself, then compare against the populated house to check your reading. Kept (definitional inputs from `qfd.md`): @@ -368,9 +368,10 @@ Blank (training surface): 4. **Basement Rel %.** Each Σ ÷ total Σ × 100, rounded to integer percent (should sum to ~100 across HOWs). -Once filled, diff your numbers against [`quality-house.md`](quality-house.md) -basement and roof to see where your reading agrees or differs — divergences -are usually the most interesting part. +Once filled, diff your numbers against the filled House in +[`qfd.md` §3](qfd.md#3-house-of-quality--whats--hows) (basement and roof) to see +where your reading agrees or differs — divergences are usually the most +interesting part. ## Gotchas diff --git a/docs/quality-house.md b/docs/quality-house.md deleted file mode 100644 index 3381cc1..0000000 --- a/docs/quality-house.md +++ /dev/null @@ -1,675 +0,0 @@ -# Quality House - -The 14 WHATs × 14 HOWs House of Quality. The roof carries function-vs- -function correlations; the basement carries v0.1 targets (mirrored from -[`qfd.md`](qfd.md) §2) plus the weighted-vote sums -(`Σ = Σ(W weight × cell strength)`) and rounded relative weights. The -narrative reading of these numbers lives in `qfd.md` §3 (priority list) -and §4 (conflict list). - -This file is the authoritative source for the matrix cells, the roof -correlations, and the basement Σ + relative weights. WHATs (§1) and HOWs -(§2) are mirrored here from `qfd.md`; if those diverge, trust `qfd.md`. - -Out of scope here: §5 component mapping (would need a second house), -§6 critical performance budget (already a curated rank), §7 tradeoffs -(narrative), §8 inconsistencies (history). - -For a blank version of the same chassis (WHATs, HOWs, importance, and -v0.1 targets kept; relations + roof + Σ basement left empty for practice), -see [`quality-house-empty.md`](quality-house-empty.md). - -The perception zone scores five products against the WHATs on a 0–5 scale. -**These are educated guesses, not measurements** — see the -[scoring rationale](#perception-scores-guessed) below for what each cell -is based on. Useful for self-positioning ("where do we land vs the -market"), not as a fair head-to-head buyer's guide. - -```tikz -% ===================================================================== -% QFD "House of Quality" preamble -% ===================================================================== -\usetikzlibrary{arrows.meta, positioning, shapes.geometric, shapes.misc, calc, fit, backgrounds} - -\newif\ifqfdshowroof \qfdshowrooftrue -\newif\ifqfdshowbasement \qfdshowbasementtrue -\newif\ifqfdshowcompetitive \qfdshowcompetitivetrue -\newif\ifqfdshowlegend \qfdshowlegendtrue -\newif\ifqfdshowimportance \qfdshowimportancetrue -\newif\ifqfdshowcorrlegend \qfdshowcorrlegendtrue -\newif\ifqfdshowevallegend \qfdshowevallegendtrue - -\def\qfdNW{5} -\def\qfdNH{5} -\def\qfdWhatW{4.0} -\def\qfdImpW{0.9} -\def\qfdCmpW{3} -\def\qfdHdrH{2.6} -\def\qfdBasementN{4} - -\def\qfdWhatsTitle{Customer needs} -\def\qfdImpTitle{Imp.\ \%} -\def\qfdPerceptionTitle{Comparative evaluation} -\def\qfdPoorLabel{poor} -\def\qfdExcellentLabel{excellent} -\def\qfdAltOneLabel{Typoena} -\def\qfdAltTwoLabel{Competitor A} -\def\qfdAltThreeLabel{Competitor B} -\def\qfdRelTitle{Relation} -\def\qfdCorrTitle{Correlation} -\def\qfdEvalTitle{Evaluation} - -\tikzset{ - qfdthin/.style ={line width=0.35pt}, - qfdmed/.style ={line width=0.7pt}, - qfdstrong/.style={circle, draw, fill=black, - minimum size=7pt, inner sep=0pt}, - qfdmod/.style ={circle, draw, - minimum size=7pt, inner sep=0pt, line width=0.8pt}, - qfdweak/.style ={regular polygon, regular polygon sides=3, draw, - minimum size=8.5pt, inner sep=0pt, line width=0.7pt}, - qfdrel/.is choice, - qfdrel/S/.style={qfdstrong}, - qfdrel/M/.style={qfdmod}, - qfdrel/W/.style={qfdweak}, - qfdalt1mk/.style={circle, draw, fill=black, - minimum size=6pt, inner sep=0pt, line width=1pt}, - qfdalt1ln/.style={line width=1.2pt}, - qfdalt2mk/.style={regular polygon, regular polygon sides=3, draw, - fill=black, minimum size=6pt, inner sep=0pt, - line width=0.7pt}, - qfdalt2ln/.style={line width=0.7pt, dashed}, - qfdalt3mk/.style={rectangle, draw, fill=black, - minimum size=5pt, inner sep=0pt, line width=0.7pt}, - qfdalt3ln/.style={line width=0.7pt, dotted}, -} - -\newcommand{\qfdDrawGrid}{% - \foreach \c in {1,...,\qfdNHm} \draw[qfdthin] (\c, 0) -- (\c, -\qfdNW); - \foreach \r in {1,...,\qfdNWm} \draw[qfdthin] (0, -\r) -- (\qfdNH, -\r); - \foreach \r in {1,...,\qfdNWm} - \draw[qfdthin] (\qfdLeftEdge, -\r) -- (0, -\r); - \ifqfdshowroof - \foreach \c in {1,...,\qfdNHm} - \draw[qfdthin] (\c, 0) -- (\c, \qfdHdrH); - \fi - \ifqfdshowcompetitive - \foreach \r in {1,...,\qfdNWm} - \draw[qfdthin] (\qfdNH, -\r) -- (\qfdNH+\qfdCmpW, -\r); - \fi - \ifqfdshowbasement - \foreach \r in {1,...,\qfdBasementN} - \draw[qfdthin] (0, -\qfdNW-\r) -- (\qfdNH, -\qfdNW-\r); - \foreach \c in {1,...,\qfdNHm} - \draw[qfdthin] (\c, -\qfdNW) -- (\c, -\qfdNW-\qfdBasementN); - \fi -} - -\newcommand{\qfdDrawRoof}{% - \ifqfdshowroof - \foreach \k in {1,...,\qfdNHm} {% - \pgfmathsetmacro{\rx}{(\k+\qfdNH)/2} - \pgfmathsetmacro{\ry}{\qfdHdrH + (\qfdNH-\k)/2} - \pgfmathsetmacro{\lx}{\k/2} - \pgfmathsetmacro{\ly}{\qfdHdrH + \k/2} - \draw[qfdthin] (\k, \qfdHdrH) -- (\rx, \ry); - \draw[qfdthin] (\k, \qfdHdrH) -- (\lx, \ly); - }% - \draw[qfdmed] (0, \qfdHdrH) - -- (\qfdNH/2, \qfdApexY) -- (\qfdNH, \qfdHdrH); - \foreach \i in {1,...,\qfdNH} - \foreach \k in {1,...,\qfdNH} {% - \pgfmathtruncatemacro{\jj}{\i+\k} - \ifnum\jj>\qfdNH\relax\else - \pgfmathsetmacro{\xx}{\i + \k/2 - 0.5} - \pgfmathsetmacro{\yy}{\qfdHdrH + \k/2} - \coordinate (C-\i-\jj) at (\xx, \yy); - \fi - }% - \fi -} - -\newcommand{\qfdDrawScale}{% - \ifqfdshowcompetitive - \foreach \tk in {0,1,2,3,4,5} {% - \pgfmathsetmacro{\tx}{\qfdNH + (\tk+0.5)*\qfdCmpW/6} - \node[anchor=south, font=\scriptsize] at (\tx, 0.02) {\tk}; - }% - \node[anchor=south, font=\scriptsize\bfseries, align=center, - text width=\qfdCmpW cm] - at ({\qfdNH + \qfdCmpW/2}, 0.7) {\qfdPerceptionTitle}; - \node[anchor=north, font=\scriptsize\itshape] - at ({\qfdNH + 0.45}, -\qfdNW) {\qfdPoorLabel}; - \node[anchor=north, font=\scriptsize\itshape] - at ({\qfdNH + \qfdCmpW - 0.45}, -\qfdNW) {\qfdExcellentLabel}; - \fi -} - -\newcommand{\qfdDrawZoneTitles}{% - \ifqfdshowimportance - \node[rotate=90, anchor=west, font=\footnotesize\bfseries] - at ({-\qfdImpW/2}, 0.12) {\qfdImpTitle}; - \fi - \node[font=\scriptsize\bfseries, align=center, text width=\qfdWhatW cm] - at ({\qfdLeftEdge + \qfdWhatW/2}, - {\ifqfdshowroof \qfdHdrH/2 \else 0.6 \fi}) {\qfdWhatsTitle}; -} - -\newcommand{\qfdDrawFrames}{% - \begin{scope}[qfdmed] - \draw (\qfdLeftEdge, 0) rectangle (\qfdNH, -\qfdNW); - \ifqfdshowimportance \draw (-\qfdImpW, 0) -- (-\qfdImpW, -\qfdNW); \fi - \draw (0, 0) -- (0, -\qfdNW); - \ifqfdshowroof - \draw (0, 0) rectangle (\qfdNH, \qfdHdrH); \fi - \ifqfdshowbasement - \draw (0, -\qfdNW) rectangle (\qfdNH, -\qfdNW-\qfdBasementN); \fi - \ifqfdshowcompetitive - \draw (\qfdNH, 0) rectangle (\qfdNH+\qfdCmpW, -\qfdNW); \fi - \end{scope} -} - -\newcommand{\qfdDrawLegend}{% - \ifqfdshowlegend - \pgfmathsetmacro{\qfdLegX}{% - \qfdNH + \ifqfdshowcompetitive \qfdCmpW + 0.7 \else 0.7 \fi} - \pgfmathsetmacro{\qfdLegBottom}{% - -2.05 - \ifqfdshowroof \ifqfdshowcorrlegend - 2.55 \fi \fi - \ifqfdshowcompetitive \ifqfdshowevallegend - 2.20 \fi \fi} - \pgfmathsetmacro{\qfdLegY}{\qfdHdrH - 0.4} - \begin{scope}[shift={(\qfdLegX, \qfdLegY)}] - \draw[qfdmed, rounded corners=2pt] - (-0.15, 0.4) rectangle (4.5, \qfdLegBottom); - \node[anchor=west, font=\footnotesize\bfseries] at (0, 0.1) - {\qfdRelTitle}; - \draw[qfdthin] (0, -0.15) -- (4.35, -0.15); - \node[qfdstrong] at (0.22, -0.5) {}; - \node[anchor=west] at (0.5, -0.5) {Strong (9)}; - \node[qfdmod] at (0.22, -0.95) {}; - \node[anchor=west] at (0.5, -0.95) {Medium (3)}; - \node[qfdweak] at (0.22, -1.4) {}; - \node[anchor=west] at (0.5, -1.4) {Weak (1)}; - \ifqfdshowroof \ifqfdshowcorrlegend - \node[anchor=west, font=\footnotesize\bfseries] at (0, -2.10) - {\qfdCorrTitle}; - \draw[qfdthin] (0, -2.35) -- (4.35, -2.35); - \node[anchor=west] at (0, -2.70) {{$+\!+$}\quad very positive}; - \node[anchor=west] at (0, -3.05) {{$+$\phantom{$+$}}\quad positive}; - \node[anchor=west] at (0, -3.40) {{$-$\phantom{$-$}}\quad negative}; - \node[anchor=west] at (0, -3.75) {{$-\!-$}\quad very negative}; - \fi \fi - \ifqfdshowcompetitive \ifqfdshowevallegend - \pgfmathsetmacro{\qfdEvalTop}{% - -2.10 \ifqfdshowroof\ifqfdshowcorrlegend - 2.55 \fi\fi} - \node[anchor=west, font=\footnotesize\bfseries] - at (0, \qfdEvalTop) {\qfdEvalTitle}; - \pgfmathsetmacro{\qfdEvalSep}{\qfdEvalTop - 0.25} - \draw[qfdthin] (0, \qfdEvalSep) -- (4.35, \qfdEvalSep); - \pgfmathsetmacro{\qfdLegA}{\qfdEvalTop - 0.55} - \draw[qfdalt1ln] (0.05, \qfdLegA) -- (0.45, \qfdLegA); - \node[qfdalt1mk] at (0.25, \qfdLegA) {}; - \node[anchor=west, font=\bfseries] at (0.55, \qfdLegA) - {\qfdAltOneLabel}; - \pgfmathsetmacro{\qfdLegB}{\qfdEvalTop - 0.95} - \draw[qfdalt2ln] (0.05, \qfdLegB) -- (0.45, \qfdLegB); - \node[qfdalt2mk] at (0.25, \qfdLegB) {}; - \node[anchor=west] at (0.55, \qfdLegB) {\qfdAltTwoLabel}; - \pgfmathsetmacro{\qfdLegC}{\qfdEvalTop - 1.35} - \draw[qfdalt3ln] (0.05, \qfdLegC) -- (0.45, \qfdLegC); - \node[qfdalt3mk] at (0.25, \qfdLegC) {}; - \node[anchor=west] at (0.55, \qfdLegC) {\qfdAltThreeLabel}; - \fi \fi - \end{scope} - \fi -} - -\newenvironment{qfdhouse}{% - \begin{tikzpicture}[x=1cm, y=1cm, font=\scriptsize, - line cap=round, line join=round] - \ifqfdshowimportance - \pgfmathsetmacro{\qfdLeftEdge}{-\qfdWhatW-\qfdImpW} - \else - \pgfmathsetmacro{\qfdLeftEdge}{-\qfdWhatW} - \fi - \pgfmathsetmacro{\qfdApexY}{\qfdHdrH + \qfdNH/2} - \pgfmathtruncatemacro{\qfdNHm}{\qfdNH - 1} - \pgfmathtruncatemacro{\qfdNWm}{\qfdNW - 1} - \qfdDrawGrid - \qfdDrawRoof - \qfdDrawScale - \qfdDrawZoneTitles -}{% - \qfdDrawFrames - \qfdDrawLegend - \end{tikzpicture}% -} - -% --- Dimensions tuned for the typewriter QFD (14 W x 15 H) --- -\def\qfdNW{14} -\def\qfdNH{14} -\def\qfdWhatW{4.6} -\def\qfdImpW{0.7} -\def\qfdHdrH{5.0} -\def\qfdBasementN{3} -\def\qfdCmpW{3.4} -\qfdshowlegendfalse % we draw a 4-alternative legend manually - -\def\qfdWhatsTitle{User-facing requirements (W)} -\def\qfdImpTitle{Weight} -\def\qfdPerceptionTitle{Competitive perception\\(0–5, guessed)} -\def\qfdPoorLabel{poor} -\def\qfdExcellentLabel{excellent} - -% Perception-zone markers: shape + colour-blind-safe colour per product. -% Palette is Okabe-Ito (blue, vermillion, bluish green, reddish purple). -% Light fills + saturated outlines keep stacked markers legible. -\definecolor{qfdcTypoena}{RGB}{0,114,178} -\definecolor{qfdcRem}{RGB}{213,94,0} -\definecolor{qfdcFrw}{RGB}{0,158,115} -\definecolor{qfdcPom}{RGB}{204,121,167} -\definecolor{qfdcFrwS}{RGB}{86,180,233} - -\tikzset{ - qfdalt1mk/.style={circle, draw=qfdcTypoena, fill=qfdcTypoena!55!white, - minimum size=6.5pt, inner sep=0pt, line width=1.1pt}, - qfdalt1ln/.style={line width=1.2pt, qfdcTypoena}, - qfdalt2mk/.style={regular polygon, regular polygon sides=3, - draw=qfdcRem, fill=qfdcRem!55!white, - minimum size=7pt, inner sep=0pt, line width=0.9pt}, - qfdalt2ln/.style={line width=0.8pt, dashed, qfdcRem}, - qfdalt3mk/.style={rectangle, draw=qfdcFrw, fill=qfdcFrw!55!white, - minimum size=5.5pt, inner sep=0pt, line width=0.9pt}, - qfdalt3ln/.style={line width=0.8pt, dotted, qfdcFrw}, - qfdalt4mk/.style={diamond, aspect=1, draw=qfdcPom, - fill=qfdcPom!50!white, - minimum size=7pt, inner sep=0pt, line width=1.0pt}, - qfdalt4ln/.style={line width=0.8pt, dash dot, qfdcPom}, - qfdalt5mk/.style={regular polygon, regular polygon sides=5, - draw=qfdcFrwS, fill=qfdcFrwS!40!white, - minimum size=5.5pt, inner sep=0pt, line width=0.8pt}, - qfdalt5ln/.style={line width=0.7pt, dash dot dot, qfdcFrwS}, -} - -\begin{document} -\begin{qfdhouse} - - % ---------- WHATs (left column) ---------- - % Box width is 0.2 cm narrower than the column so labels have 0.1 cm - % clearance on each side and don't bleed into the Weight column. - \pgfmathsetmacro{\qfdWhatTextW}{\qfdWhatW - 0.2} - \foreach \r/\t in {% - 1/{W1 Sub-second visible response to typing}, - 2/{W2 Publishing is one deliberate action away}, - 3/{W3 Pulling power never corrupts the file}, - 4/{W4 Provisioning never interrupts a writing session}, - 5/{W5 Quick boot to a writing cursor}, - 6/{W6 Long sessions without crash, lag, drift}, - 7/{W7 Nothing on the device competes with prose}, - 8/{W8 The UI never moves except when I move it}, - 9/{W9 Codebase absorbs the planned roadmap}, - 10/{W10 I can repair or fork it with hobbyist tools}, - 11/{W11 Multi-day battery life (v0.8 onward)}, - 12/{W12 Local-only files coexist with git scope (v0.5+)}, - 13/{W13 Typography sets a writing-tool tone}, - 14/{W14 I can carry the device and write away from a desk}% - } - \node[anchor=west, font=\scriptsize, - text width=\qfdWhatTextW cm, align=left] - at ({\qfdLeftEdge + 0.1}, {-\r + 0.5}) {\t}; - - % ---------- Importance (raw 1-10 weight) ---------- - \foreach \r/\w in {1/10, 2/9, 3/10, 4/7, 5/6, 6/9, 7/8, 8/7, - 9/8, 10/5, 11/4, 12/5, 13/7, 14/8} - \node[font=\scriptsize] at ({-\qfdImpW/2}, {-\r + 0.5}) {\w}; - - % ---------- HOWs (rotated column titles) ---------- - \foreach \c/\t in {% - 1/{H1 Type latency}, - 2/{H2 Refresh area per keystroke}, - 3/{H3 Full-refresh cadence}, - 4/{H4 Boot latency (cold)}, - 5/{H5 Continuous-typing endurance}, - 6/{H6 Publish reliability}, - 7/{H7 Publish latency}, - 8/{H8 Save durability}, - 9/{H9 PSRAM heap headroom}, - 10/{H10 Firmware binary size}, - 11/{H11 Total stack budget}, - 12/{H12 Network reconnect time}, - 13/{H13 Idle / typing / push current}, - 14/{H15 Clean release build time}% - } - \node[rotate=90, anchor=west, font=\scriptsize] - at ({\c - 0.5}, 0.15) {\t}; - - % ---------- Relation matrix (S=9, M=3, W=1) ---------- - % W1 row 1: H1S H2S H3M H5M H9W H11W - \node[qfdrel/S] at ({1 - 0.5}, {-1 + 0.5}) {}; - \node[qfdrel/S] at ({2 - 0.5}, {-1 + 0.5}) {}; - \node[qfdrel/M] at ({3 - 0.5}, {-1 + 0.5}) {}; - \node[qfdrel/M] at ({5 - 0.5}, {-1 + 0.5}) {}; - \node[qfdrel/W] at ({9 - 0.5}, {-1 + 0.5}) {}; - \node[qfdrel/W] at ({11 - 0.5}, {-1 + 0.5}) {}; - - % W2 row 2: H6S H7M H9S H12S - \node[qfdrel/S] at ({6 - 0.5}, {-2 + 0.5}) {}; - \node[qfdrel/M] at ({7 - 0.5}, {-2 + 0.5}) {}; - \node[qfdrel/S] at ({9 - 0.5}, {-2 + 0.5}) {}; - \node[qfdrel/S] at ({12 - 0.5}, {-2 + 0.5}) {}; - - % W3 row 3: H8S - \node[qfdrel/S] at ({8 - 0.5}, {-3 + 0.5}) {}; - - % W4 row 4: H6M H12M - \node[qfdrel/M] at ({6 - 0.5}, {-4 + 0.5}) {}; - \node[qfdrel/M] at ({12 - 0.5}, {-4 + 0.5}) {}; - - % W5 row 5: H4S H10M - \node[qfdrel/S] at ({4 - 0.5}, {-5 + 0.5}) {}; - \node[qfdrel/M] at ({10 - 0.5}, {-5 + 0.5}) {}; - - % W6 row 6: H1M H3M H5S H6M H8M H9S H11M H12M - \node[qfdrel/M] at ({1 - 0.5}, {-6 + 0.5}) {}; - \node[qfdrel/M] at ({3 - 0.5}, {-6 + 0.5}) {}; - \node[qfdrel/S] at ({5 - 0.5}, {-6 + 0.5}) {}; - \node[qfdrel/M] at ({6 - 0.5}, {-6 + 0.5}) {}; - \node[qfdrel/M] at ({8 - 0.5}, {-6 + 0.5}) {}; - \node[qfdrel/S] at ({9 - 0.5}, {-6 + 0.5}) {}; - \node[qfdrel/M] at ({11 - 0.5}, {-6 + 0.5}) {}; - \node[qfdrel/M] at ({12 - 0.5}, {-6 + 0.5}) {}; - - % W7 row 7: H1M H2M H3M H13M - \node[qfdrel/M] at ({1 - 0.5}, {-7 + 0.5}) {}; - \node[qfdrel/M] at ({2 - 0.5}, {-7 + 0.5}) {}; - \node[qfdrel/M] at ({3 - 0.5}, {-7 + 0.5}) {}; - \node[qfdrel/M] at ({13 - 0.5}, {-7 + 0.5}) {}; - - % W8 row 8: H1W H2S H3S - \node[qfdrel/W] at ({1 - 0.5}, {-8 + 0.5}) {}; - \node[qfdrel/S] at ({2 - 0.5}, {-8 + 0.5}) {}; - \node[qfdrel/S] at ({3 - 0.5}, {-8 + 0.5}) {}; - - % W9 row 9: H10W H11W H15M - \node[qfdrel/W] at ({10 - 0.5}, {-9 + 0.5}) {}; - \node[qfdrel/W] at ({11 - 0.5}, {-9 + 0.5}) {}; - \node[qfdrel/M] at ({14 - 0.5}, {-9 + 0.5}) {}; - - % W10 row 10: H10M H13W H15W - \node[qfdrel/M] at ({10 - 0.5}, {-10 + 0.5}) {}; - \node[qfdrel/W] at ({13 - 0.5}, {-10 + 0.5}) {}; - \node[qfdrel/W] at ({14 - 0.5}, {-10 + 0.5}) {}; - - % W11 row 11: H13S - \node[qfdrel/S] at ({13 - 0.5}, {-11 + 0.5}) {}; - - % W12 row 12: H6W H8M - \node[qfdrel/W] at ({6 - 0.5}, {-12 + 0.5}) {}; - \node[qfdrel/M] at ({8 - 0.5}, {-12 + 0.5}) {}; - - % W13 row 13: H9M - \node[qfdrel/M] at ({9 - 0.5}, {-13 + 0.5}) {}; - - % W14 row 14: H4W H8M H12M H13S - \node[qfdrel/W] at ({4 - 0.5}, {-14 + 0.5}) {}; - \node[qfdrel/M] at ({8 - 0.5}, {-14 + 0.5}) {}; - \node[qfdrel/M] at ({12 - 0.5}, {-14 + 0.5}) {}; - \node[qfdrel/S] at ({13 - 0.5}, {-14 + 0.5}) {}; - - % ---------- Roof correlations ---------- - \node[font=\scriptsize] at (C-1-2) {$+\!+$}; % H1-H2 strong reinforce - \node[font=\scriptsize] at (C-1-3) {$-$}; % H1-H3 mild conflict - \node[font=\scriptsize] at (C-1-5) {$+$}; % H1-H5 mild reinforce - \node[font=\scriptsize] at (C-1-13) {$-$}; % H1-H13 mild conflict - \node[font=\scriptsize] at (C-2-3) {$+\!+$}; % H2-H3 strong reinforce - \node[font=\scriptsize] at (C-2-13) {$+$}; % H2-H13 - \node[font=\scriptsize] at (C-3-13) {$+$}; % H3-H13 - \node[font=\scriptsize] at (C-4-10) {$-$}; % H4-H10 boot vs binary - \node[font=\scriptsize] at (C-5-6) {$+$}; % H5-H6 - \node[font=\scriptsize] at (C-5-8) {$+$}; % H5-H8 - \node[font=\scriptsize] at (C-5-9) {$-\!-$}; % H5-H9 soak vs heap - \node[font=\scriptsize] at (C-6-7) {$+$}; % H6-H7 - \node[font=\scriptsize] at (C-6-9) {$-\!-$}; % H6-H9 push vs heap - \node[font=\scriptsize] at (C-6-12) {$+\!+$}; % H6-H12 - \node[font=\scriptsize] at (C-7-9) {$-$}; % H7-H9 - \node[font=\scriptsize] at (C-7-12) {$+\!+$}; % H7-H12 - \node[font=\scriptsize] at (C-9-10) {$-\!-$}; % H9-H10 heap vs binary - \node[font=\scriptsize] at (C-10-14) {$-\!-$}; % H10-H15 binary vs build - \node[font=\scriptsize] at (C-11-13) {$-$}; % H11-H13 - - % ---------- Basement: target / abs weight / rel weight % ---------- - \foreach \c/\tgt/\abs/\rel in {% - 1/{$\leq$200\,ms}/148/10, - 2/{$\leq$1 line}/177/11, - 3/{1 : 64}/144/9, - 4/{$\leq$5\,s}/62/4, - 5/{$\geq$1\,h}/111/7, - 6/{$\geq$95\,\%}/134/9, - 7/{$\leq$30\,s}/27/2, - 8/{100\,\%}/156/10, - 9/{$\geq$1\,MB}/193/12, - 10/{$\leq$2\,MB}/41/3, - 11/{$\leq$80\,KB}/45/3, - 12/{$\leq$30\,s}/153/10, - 13/{obs.}/137/9, - 14/{$\leq$7\,min}/29/2% - } { - \node[font=\scriptsize] at ({\c - 0.5}, {-\qfdNW - 0.5}) {\tgt}; - \node[font=\scriptsize] at ({\c - 0.5}, {-\qfdNW - 1.5}) {\abs}; - \node[font=\scriptsize\bfseries] - at ({\c - 0.5}, {-\qfdNW - 2.5}) {\rel}; - } - - % ---------- Basement row labels (in the margin below WHATs) ---------- - \foreach \k/\lbl in {1/{Target (v0.1)}, 2/{$\Sigma$ abs}, 3/{Rel.\ \%}} - \node[anchor=east, font=\scriptsize\itshape] - at ({-0.1}, {-\qfdNW - \k + 0.5}) {\lbl}; - - % ---------- Perception zone: 5 products x 14 WHATs (0-5 scores) ---------- - % Columns: \so=Typoena target, \st=reMarkable 2 + Type Folio, - % \sf=Freewrite Traveler, \sg=Pomera DM250, - % \sh=Freewrite Smart Typewriter. - % Pass 1: stash each score as a named coordinate so the profile lines - % below can reuse it without recomputing. - \foreach \r/\so/\st/\sf/\sg/\sh in {% - 1/4/1/4/5/3, - 2/5/4/4/2/4, - 3/4/4/2/2/2, - 4/5/2/2/5/2, - 5/4/3/4/5/4, - 6/3/3/4/5/4, - 7/5/2/5/5/5, - 8/4/3/4/5/4, - 9/4/3/2/1/2, - 10/5/4/2/1/2, - 11/1/5/5/4/5, - 12/3/1/2/3/2, - 13/3/5/2/2/2, - 14/2/4/5/2/5% - } { - \pgfmathsetmacro{\xo}{\qfdNH + (\so + 0.5)*\qfdCmpW/6} - \pgfmathsetmacro{\xt}{\qfdNH + (\st + 0.5)*\qfdCmpW/6} - \pgfmathsetmacro{\xf}{\qfdNH + (\sf + 0.5)*\qfdCmpW/6} - \pgfmathsetmacro{\xg}{\qfdNH + (\sg + 0.5)*\qfdCmpW/6} - \pgfmathsetmacro{\xs}{\qfdNH + (\sh + 0.5)*\qfdCmpW/6} - \coordinate (po-\r) at (\xo, {-\r + 0.5}); - \coordinate (pr-\r) at (\xt, {-\r + 0.5}); - \coordinate (pf-\r) at (\xf, {-\r + 0.5}); - \coordinate (pp-\r) at (\xg, {-\r + 0.5}); - \coordinate (ps-\r) at (\xs, {-\r + 0.5}); - } - - % Pass 2: profile lines per alternative. Drawn before markers so the - % dots sit on top of (not under) the line endpoints. - \draw[qfdalt1ln] (po-1) \foreach \r in {2,...,\qfdNW} { -- (po-\r) }; - \draw[qfdalt2ln] (pr-1) \foreach \r in {2,...,\qfdNW} { -- (pr-\r) }; - \draw[qfdalt3ln] (pf-1) \foreach \r in {2,...,\qfdNW} { -- (pf-\r) }; - \draw[qfdalt4ln] (pp-1) \foreach \r in {2,...,\qfdNW} { -- (pp-\r) }; - \draw[qfdalt5ln] (ps-1) \foreach \r in {2,...,\qfdNW} { -- (ps-\r) }; - - % Pass 3: markers on top of the lines. - \foreach \r in {1,...,\qfdNW} { - \node[qfdalt1mk] at (po-\r) {}; - \node[qfdalt2mk] at (pr-\r) {}; - \node[qfdalt3mk] at (pf-\r) {}; - \node[qfdalt4mk] at (pp-\r) {}; - \node[qfdalt5mk] at (ps-\r) {}; - } - - % ---------- Manual legend (5 alternatives, placed right of zones) ---------- - \pgfmathsetmacro{\qfdLegX}{\qfdNH + \qfdCmpW + 0.7} - \begin{scope}[shift={(\qfdLegX, \qfdHdrH - 0.4)}] - \draw[qfdmed, rounded corners=2pt] - (-0.15, 0.4) rectangle (5.1, -7.50); - % Relations - \node[anchor=west, font=\footnotesize\bfseries] at (0, 0.1) - {Relation}; - \draw[qfdthin] (0, -0.15) -- (4.95, -0.15); - \node[qfdstrong] at (0.22, -0.5) {}; - \node[anchor=west] at (0.5, -0.5) {Strong (9)}; - \node[qfdmod] at (0.22, -0.95) {}; - \node[anchor=west] at (0.5, -0.95) {Medium (3)}; - \node[qfdweak] at (0.22, -1.4) {}; - \node[anchor=west] at (0.5, -1.4) {Weak (1)}; - % Correlation - \node[anchor=west, font=\footnotesize\bfseries] at (0, -2.10) - {Correlation}; - \draw[qfdthin] (0, -2.35) -- (4.95, -2.35); - \node[anchor=west] at (0, -2.70) {{$+\!+$}\quad very positive}; - \node[anchor=west] at (0, -3.05) {{$+$\phantom{$+$}}\quad positive}; - \node[anchor=west] at (0, -3.40) {{$-$\phantom{$-$}}\quad negative}; - \node[anchor=west] at (0, -3.75) {{$-\!-$}\quad very negative}; - % Perception - \node[anchor=west, font=\footnotesize\bfseries] at (0, -4.20) - {Perception}; - \draw[qfdthin] (0, -4.45) -- (4.95, -4.45); - \draw[qfdalt1ln] (0.05, -4.80) -- (0.45, -4.80); - \node[qfdalt1mk] at (0.25, -4.80) {}; - \node[anchor=west, font=\bfseries] at (0.55, -4.80) - {Typoena (v0.1 target)}; - \draw[qfdalt2ln] (0.05, -5.25) -- (0.45, -5.25); - \node[qfdalt2mk] at (0.25, -5.25) {}; - \node[anchor=west] at (0.55, -5.25) {reMarkable 2 + Type Folio}; - \draw[qfdalt3ln] (0.05, -5.70) -- (0.45, -5.70); - \node[qfdalt3mk] at (0.25, -5.70) {}; - \node[anchor=west] at (0.55, -5.70) {Freewrite Traveler}; - \draw[qfdalt5ln] (0.05, -6.15) -- (0.45, -6.15); - \node[qfdalt5mk] at (0.25, -6.15) {}; - \node[anchor=west] at (0.55, -6.15) {Freewrite Smart Typewriter}; - \draw[qfdalt4ln] (0.05, -6.60) -- (0.45, -6.60); - \node[qfdalt4mk] at (0.25, -6.60) {}; - \node[anchor=west] at (0.55, -6.60) {Pomera DM250}; - \node[anchor=west, font=\scriptsize\itshape] at (0, -7.15) - {0 = poor, 5 = excellent}; - \end{scope} - -\end{qfdhouse} -\end{document} -``` - -## Perception scores (guessed) - -Five products on the 0–5 scale, scored against each WHAT. Reference -configurations: **reMarkable 2 + Type Folio**, **Freewrite Traveler**, -**Freewrite Smart Typewriter**, **Pomera DM250** (DM250 has a reflective -monochrome LCD, not e-ink — flagged in W1 / W8). "Typoena" is the v0.1 -target from `qfd.md` §2, not measured yet. - -Freewrite Traveler scores assume the -[Sailfish firmware](https://getfreewrite.com/blogs/writing-success/freewrite-sailfish-firmware) -(released 2025-11-19), which rewrote the OS in Rust, cut keystroke latency -40–100 %, and trimmed power draw −30 % typing / −50 % idle on both -Traveler and Smart Typewriter Gen 3. Three rows rescored upward as a -result: W1 Traveler 3→4 / Smart 2→3 (Smart's larger panel still trails -Traveler by one notch), W5 both 3→4 (boot accelerated, no published -number), W9 both 1→2 (Rust rewrite explicitly unblocked features that -JS could not carry; still closed so neither reaches reMarkable's -hackable-Linux 3). - -| ID | WHAT (truncated) | Typoena | reM. | Frw.T | Frw.S | Pom. | Rationale (shortest defensible) | -| --- | ------------------------------------------------- | :-----: | :--: | :---: | :---: | :--: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| W1 | Sub-second response to typing | 4 | 1 | 4 | 3 | 5 | Typoena targets ≤200 ms; reMarkable e-ink visibly laggy on a typing-focused device — tested less responsive than Smart Typewriter, and latency is so load-bearing for W1 that it earns a 1 not a 2; both Freewrites post-Sailfish trimmed latency 40–100 % (Frw.T plausibly inside 200 ms; Frw.S still trails by one notch on larger panel); Pomera LCD ~zero. | -| W2 | Publishing is one deliberate action away | 5 | 4 | 4 | 4 | 2 | Ctrl-G atomic; reMarkable + Freewrite cloud-sync is one-tap but not git; Pomera = USB/SD copy or QR transfer. | -| W3 | Pulling power never corrupts the file | 4 | 4 | 2 | 2 | 2 | Typoena: atomic-rename + fsync. reMarkable journals. Freewrite + Pomera: forum reports of corruption on yank. | -| W4 | Provisioning never interrupts writing | 5 | 2 | 2 | 2 | 5 | Typoena v0.1: build-time config (dev-only). reM/Frw need Wi-Fi + account. Pomera: literally none. | -| W5 | Quick boot to a writing cursor | 4 | 3 | 4 | 4 | 5 | Typoena target ≤5 s. reMarkable cold-boots ~20 s (great from sleep). Both Freewrites accelerated post-Sailfish (no published number; were ~10–15 s e-ink wake). Pomera ~3 s. | -| W6 | Long sessions without crash / lag / drift | 3 | 3 | 4 | 4 | 5 | Typoena unproven (1 h target). Freewrite famously stable (both variants). Pomera firmware is decades-mature. | -| W7 | Nothing on the device competes with prose | 5 | 2 | 5 | 5 | 5 | reMarkable has apps, menus, drawing, PDFs. Freewrite + Pomera are single-purpose; Typoena by design. | -| W8 | The UI never moves except when I move it | 4 | 3 | 4 | 4 | 5 | reMarkable animates more; Typoena uses dirty-rects; Freewrites minimal motion; Pomera near-static LCD. | -| W9 | Codebase absorbs the planned roadmap | 4 | 3 | 2 | 2 | 1 | Modular Rust Typoena; reMarkable is hackable Linux; both Freewrites carry Sailfish (Rust rewrite explicitly unblocked features JS could not carry) but closed; Pomera closed firmware. | -| W10 | I can repair or fork it with hobbyist tools | 5 | 4 | 2 | 2 | 1 | Typoena: open BOM + ESP32. reMarkable: rooted Linux + community ROMs. Freewrite + Pomera: closed. | -| W11 | Multi-day battery life (v0.8 onward) | 1 | 5 | 5 | 5 | 4 | Typoena v0.1 = wall-powered (battery deferred). reMarkable + both Freewrites legendary (~4 weeks; Sailfish trimmed −30 % typing / −50 % idle). Pomera ~24 h. | -| W12 | Local-only files coexist with git scope | 3 | 1 | 2 | 2 | 3 | Typoena v0.5+ design. reMarkable cloud-only. Freewrites have local + Postbox but no VCS. Pomera = pure local. | -| W13 | Typography sets a writing-tool tone | 3 | 5 | 2 | 2 | 2 | Typoena v0.1: single mono (serif option in v1.0). reMarkable: rich type rendering. Freewrite + Pomera: utilitarian. | -| W14 | I can carry the device and write away from a desk | 2 | 4 | 5 | 1 | 5 | Typoena v0.1 wall-powered (ADR-008), no enclosure spec yet — desk-bound by design. reMarkable + Type Folio bag-friendly with bulk. Freewrite Traveler is the form-factor reference (~1.6 lb, folds). Smart Typewriter ~5 lb, desk-bound. Pomera DM250 pocketable foldable. | - -**Totals** (sum across 14 WHATs, no weighting): Typoena 52, Pomera 50, -Freewrite Traveler 47, reMarkable 44, Freewrite Smart Typewriter 42 -(Traveler pre-Sailfish 44; Smart pre-Sailfish 39; reMarkable W1 dropped -3→2→1 across two rounds of author testing — first to 2 after firsthand -typing, then to 1 once latency was recognised as the dominant W1 -signal). Pomera closing to within 2 of -Typoena is W14 doing what W14 should — surfacing the dimension on -which v0.1's tethered MVP loses ground that v0.8 is expected to -recover. The "Pomera + Wi-Fi + git + hackable BOM" framing from -`README.md` still holds and reads stronger. - -Weighted totals (Σ score × W weight) tell the same story with more -contrast — left as exercise; the unweighted view is enough to read the -picture. - -### Caveats - -- **Single-rater bias.** All thirteen rows are scored from the project - author's POV. A reMarkable buyer would weight W11 (battery) at 10 and - W12 (git) at 1, flipping the totals. -- **Configuration matters.** Freewrite Smart Typewriter and Traveler are - both tracked; they diverge most on W1 / W5 because of display tech - (Smart's larger panel is slower to refresh). Traveler is still the - more direct competitor on form factor. -- **W3 / W6 Freewrite scores are anecdotal.** Forum reports, not bench - data. Treat the 2 / 4 as "we'd need to test this" rather than fact. -- **No price column.** Typoena-as-BOM is materially cheaper than the - competitors but cost is not a WHAT in `qfd.md` §1, so it's absent here. - Worth a row if a v0.x WHAT ever calls it out. - -## Reading the house - -- **Importance (left column)** is the raw 1–10 weight from `qfd.md` §1, not - a normalised %, so adding stays cheap when a WHAT shifts. Sum of weights - is 103; treat each unit as ~0.97 % if you want a percentage view. -- **Roof** carries the §4 symbols translated into classical QFD glyphs: - `++` strong reinforcement (`◎`), `+` mild reinforcement (`○`), `−` mild - conflict (`×`), `−−` strong conflict (`⊗`). -- **Basement rows** are: v0.1 target → §3 column sum (`Σ` of - `weight × strength`) → relative weight as integer % of total (1557). - Relative weights round to 100. -- **H7, H10, H15** (Publish latency, binary size, build time) sit at the bottom - of the basement — knowingly-paid costs per `qfd.md` §7, not signals to - optimise harder. - -## Regenerating - -The matrix cells (`\node[qfdrel/{S,M,W}]`), roof symbols (`C-i-j` slots), -and basement Σ + Rel% are native to this file — re-score directly in the -TikZ source, then update the §3 priority list and §4 conflict list in -`qfd.md` to match the new picture. - -When `qfd.md` §1 or §2 changes: - -1. Importance column → §1 weight column. -2. HOW titles + v0.1 targets → §2 target column. -3. Recompute basement Σ for any HOW whose column changed: per-cell - contribution = `(W weight) × (cell strength: 9 / 3 / 1 / 0)`. -4. Recompute relative weight: each Σ ÷ total Σ × 100, rounded to integer - percent. - -Perception scores are **not** derived from `qfd.md` — they live only in -this file. Update them when (a) a competitor ships a relevant change, -(b) measurement replaces a guess, or (c) a WHAT is added/removed in §1. -Each score keeps its one-line rationale in the table above. - -If a renderer rejects the `tikz` fence, the file is still readable as -source — the placement comments name each WHAT, HOW, and cell. The -perception-scores table above is the human-readable fallback for the -right-hand zone of the diagram.