II · Relationship
The Payload
The one thing the row exists to show
AI agents come with design skills now: UI skills, UX skills, act as a senior product designer, for Claude, for Codex, for whatever model you host yourself. I use them. And I'm seeing more and more interfaces come out of them looking finished: clean spacing, a real design system, sensible components. They look nice. Then you use them, and they fail the test that matters.
Here's one design session with my AI agent, and how I reviewed what it made.
I was building myself a command center. My tasks live as GitHub issues, so the command center is mostly lists of issues, in columns. I gave Claude the instruction to build it: three columns, in motion, waiting on me, done this week, every open issue across my repos, follow my design system and write the contract into every component. It built the whole thing: a full design system, contracts documented in every component file, the works. It knows me, after all. Then I opened it with real data, took a screenshot, and asked:
Can you spot the problem? Don't solve anything yet.
It went straight for the clever answer. The data was lying: issues marked "in motion" that hadn't moved in 28 days, a state machine hole, green dots on dead work. Smart find. Real bug, even.
Wrong answer.
I told it: maybe, but that's nearly not as important. Look at the most common thing on this page. The issue tile. I would argue the most basic, foremost, primal CONTRACT with the user is to show the issue title.
And when I look at the screen: in the left column, in motion, I see maybe two words. In the middle one, waiting on me, three characters. Three. On the right, done this week, barely one word.
<figure style="margin:1.6rem 0;text-align:center"><svg width="400" height="196" viewBox="0 0 400 196" role="img" aria-label="three issue rows from the three columns of a command center, each column a different width; the four labels keep their full width every time, and the titles are cut to two words, three letters, one word"><text x="8" y="13" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="600" fill="#3a3631" text-anchor="start">In motion column</text><text x="93.4" y="13" font-family="Inter, system-ui, sans-serif" font-size="7.5" font-weight="400" fill="#8f887d" text-anchor="start" font-style="italic">title: “Retry failed payments before the nightly sync”</text><rect x="8" y="18" width="286" height="28" rx="4" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="266" y="35.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">28d</text><circle cx="215" cy="32" r="3" fill="#5b8a5a"/><text x="221" y="35.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">in motion</text><text x="152" y="35.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">core / sync</text><text x="98" y="35.5" font-family="'JetBrains Mono', ui-monospace, monospace" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">core#212</text><text x="18" y="35.5" font-family="Inter, system-ui, sans-serif" font-size="9" font-weight="500" fill="#7a1f1f" text-anchor="start">Retry failed…</text><path d="M300 32 h8" stroke="#7a1f1f" stroke-width="1" fill="none"/><text x="312" y="35.5" font-family="Inter, system-ui, sans-serif" font-size="9" font-weight="600" fill="#7a1f1f" text-anchor="start">2 words</text><text x="8" y="77" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="600" fill="#3a3631" text-anchor="start">Waiting on me column</text><text x="111.8" y="77" font-family="Inter, system-ui, sans-serif" font-size="7.5" font-weight="400" fill="#8f887d" text-anchor="start" font-style="italic">title: “Review the new invoice layout”</text><rect x="8" y="82" width="236" height="28" rx="4" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="216" y="99.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">3d</text><circle cx="165" cy="96" r="3" fill="#c08a2e"/><text x="171" y="99.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">waiting</text><text x="102" y="99.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">core / sync</text><text x="48" y="99.5" font-family="'JetBrains Mono', ui-monospace, monospace" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">core#198</text><text x="18" y="99.5" font-family="Inter, system-ui, sans-serif" font-size="9" font-weight="500" fill="#7a1f1f" text-anchor="start">Rev…</text><path d="M250 96 h8" stroke="#7a1f1f" stroke-width="1" fill="none"/><text x="262" y="99.5" font-family="Inter, system-ui, sans-serif" font-size="9" font-weight="600" fill="#7a1f1f" text-anchor="start">3 letters</text><text x="8" y="141" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="600" fill="#3a3631" text-anchor="start">Done this week column</text><text x="116.39999999999999" y="141" font-family="Inter, system-ui, sans-serif" font-size="7.5" font-weight="400" fill="#8f887d" text-anchor="start" font-style="italic">title: “Upgrade the print queue”</text><rect x="8" y="146" width="258" height="28" rx="4" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="238" y="163.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">1d</text><circle cx="187" cy="160" r="3" fill="#9a948a"/><text x="193" y="163.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">done</text><text x="124" y="163.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">core / sync</text><text x="70" y="163.5" font-family="'JetBrains Mono', ui-monospace, monospace" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">core#175</text><text x="18" y="163.5" font-family="Inter, system-ui, sans-serif" font-size="9" font-weight="500" fill="#7a1f1f" text-anchor="start">Upgrade…</text><path d="M272 160 h8" stroke="#7a1f1f" stroke-width="1" fill="none"/><text x="284" y="163.5" font-family="Inter, system-ui, sans-serif" font-size="9" font-weight="600" fill="#7a1f1f" text-anchor="start">1 word</text><path d="M118 48 v4 H284 v-4" stroke="#8f887d" stroke-width="1" fill="none"/><text x="201" y="61" font-family="Inter, system-ui, sans-serif" font-size="7.5" font-weight="400" fill="#8f887d" text-anchor="middle">the labels: never shrink</text></svg><figcaption style="font-size:.8rem;color:#a8a298;font-style:italic;margin-top:.4rem">One row from each of the three columns. The columns are different widths. The four labels never shrink, so the width a column doesn’t have comes out of the title.</figcaption></figure>
Does this honor the contract?
Each row had a title and four small labels beside it: which repository the issue belonged to, where in the project it sat, its status, and how old it was. When a column gets narrow, something on the row has to give. Every one of those labels had been set never to shrink and never to wrap. The title was the only thing allowed to get shorter. So when the space ran out, the title paid for everything. The least important content was the most protected. The reason for the row's existence got three characters.
And the labels weren't even honest about what they cost. The repository's name showed up twice on the same row, in two different labels. The status showed up twice too, as a colored dot and as a word. Every repeat took width from the title, on every row, hundreds of times per screen.
<figure style="margin:1.6rem 0;text-align:center"><svg width="400" height="112" viewBox="0 0 400 112" role="img" aria-label="one issue row with annotations: four labels set never to shrink, the title the only thing allowed to get shorter, the repository and the status each shown twice"><rect x="70" y="40" width="250" height="28" rx="4" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="292" y="57.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">28d</text><circle cx="241" cy="54" r="3" fill="#5b8a5a"/><text x="247" y="57.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">in motion</text><text x="178" y="57.5" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">core / sync</text><text x="124" y="57.5" font-family="'JetBrains Mono', ui-monospace, monospace" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">core#212</text><text x="80" y="57.5" font-family="Inter, system-ui, sans-serif" font-size="9" font-weight="500" fill="#7a1f1f" text-anchor="start">Ret…</text><path d="M124 74 v4 H310 v-4" stroke="#8f887d" stroke-width="1" fill="none"/><text x="217.0" y="90" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="middle">set never to shrink, never to wrap</text><path d="M78 74 v4 H104 v-4" stroke="#7a1f1f" stroke-width="1" fill="none"/><text x="78" y="104" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#7a1f1f" text-anchor="start">the title: the only thing allowed to get shorter</text><path d="M136 38 C 136 22, 190 22, 190 38" stroke="#8f887d" stroke-width="1" fill="none"/><text x="163.0" y="22" font-family="Inter, system-ui, sans-serif" font-size="7.5" font-weight="400" fill="#8f887d" text-anchor="middle">the repository, twice</text><path d="M241 38 C 241 28, 264 28, 264 38" stroke="#8f887d" stroke-width="1" fill="none"/><text x="280" y="32" font-family="Inter, system-ui, sans-serif" font-size="7.5" font-weight="400" fill="#8f887d" text-anchor="start">the status, twice</text></svg><figcaption style="font-size:.8rem;color:#a8a298;font-style:italic;margin-top:.4rem">What the row was protecting.</figcaption></figure>
Then I pushed one more level, because the fix it proposed still kept every row to a single line: why? Each column is a single-column list of tiles. Nothing aligns across them. How important is it REALLY that every tile is exactly the same height?
It isn't. At all. Rows of equal height earn their keep in a table, where the eye runs along a row and down a column at the same time, and everything has to line up. A single column of tiles has nothing to line up with. What makes the tiles look like they belong together is that they're built the same way: the same padding, the same line between them, the same order inside. Their height can change. Keeping every row to one line is a habit from spreadsheets, carried into a place where it buys nothing and costs the title.
<figure style="margin:1.6rem 0;text-align:center"><svg width="400" height="210" viewBox="0 0 400 210" role="img" aria-label="the same three issue tiles after the review: each title shown in full on top, wrapping onto more lines where the column is narrow, and a second row underneath with the status, the reference and the age; the tiles are different heights"><text x="8" y="13" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="600" fill="#3a3631" text-anchor="start">In motion column</text><rect x="8" y="18" width="286" height="40" rx="4" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="18" y="35" font-family="Inter, system-ui, sans-serif" font-size="9" font-weight="500" fill="#3a3631" text-anchor="start">Retry failed payments before the nightly sync</text><circle cx="21" cy="48.2" r="3" fill="#5b8a5a"/><text x="28" y="51" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">in motion</text><text x="77.6" y="51" font-family="'JetBrains Mono', ui-monospace, monospace" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">core#212</text><text x="129.6" y="51" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">28d</text><text x="8" y="77" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="600" fill="#3a3631" text-anchor="start">Waiting on me column</text><rect x="8" y="82" width="236" height="40" rx="4" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="18" y="99" font-family="Inter, system-ui, sans-serif" font-size="9" font-weight="500" fill="#3a3631" text-anchor="start">Review the new invoice layout</text><circle cx="21" cy="112.2" r="3" fill="#c08a2e"/><text x="28" y="115" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">waiting</text><text x="68.80000000000001" y="115" font-family="'JetBrains Mono', ui-monospace, monospace" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">core#198</text><text x="120.80000000000001" y="115" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">3d</text><text x="8" y="141" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="600" fill="#3a3631" text-anchor="start">Done this week column</text><rect x="8" y="146" width="258" height="40" rx="4" fill="#fff" stroke="#d8d2c8" stroke-width="1"/><text x="18" y="163" font-family="Inter, system-ui, sans-serif" font-size="9" font-weight="500" fill="#3a3631" text-anchor="start">Upgrade the print queue</text><circle cx="21" cy="176.2" r="3" fill="#9a948a"/><text x="28" y="179" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">done</text><text x="55.6" y="179" font-family="'JetBrains Mono', ui-monospace, monospace" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">core#175</text><text x="107.6" y="179" font-family="Inter, system-ui, sans-serif" font-size="8" font-weight="400" fill="#8f887d" text-anchor="start">1d</text><path d="M150 10 h-8 q-4 0 -4 4 v6" stroke="#7a1f1f" stroke-width="1" fill="none"/><text x="154" y="13" font-family="Inter, system-ui, sans-serif" font-size="7.5" font-weight="400" fill="#7a1f1f" text-anchor="start">the title gets the whole width</text><text x="8" y="202" font-family="Inter, system-ui, sans-serif" font-size="7.5" font-weight="400" fill="#8f887d" text-anchor="start" font-style="italic">The tags go underneath, each fact once. Every title fits now; a longer one would take a second line.</text></svg><figcaption style="font-size:.8rem;color:#a8a298;font-style:italic;margin-top:.4rem">The same tiles, the same widths, after the review. The title gets its own row; the tags sit underneath.</figcaption></figure>
What it was really about
The agent never looked at the screen. It could have; they all can now. The question is whether they do, and when they do, what they're looking for. This one worked with what it could see: fields, parameters, class names. To it, issue.title was a string, like every other string on the row. It never asked how long a title usually is, or what one looks like beside four labels in a column a third of the screen wide. I only found the problem because I opened the thing with real data in it and looked.
The repository showed up twice for the same reason. The issue number already carried it, core#212, and the agent put a label with the repository's name right beside it. In the code, those were two different fields. On the screen, they said the same thing twice.
Underneath, it was a question of who leads. Everything on that row was in a relationship with everything else, and nobody had decided who the row was about. Ask it the question this book keeps asking, what is this?, and the answer is an issue, and an issue is known by its title. That's its nature. The status, the age, where it lives: that's what it means right now and where it belongs. It all matters, but it matters about the title. The labels are its company, not its rivals, and when the room runs out, the company steps back.
Every small room on a screen has this question in it. The email whose subject gets twelve characters because the sender, the date, the chips and the paperclip all keep their seats. The file called "Quarterly_report_v3_FIN…pdf" beside a full column of dates nobody came for. The thing you came for is the one thing allowed to break, because nobody told the rest who they were serving.