Angular took the most turns of the four clients, 390 against React's 319, and the most wall time, 68 minutes, and it is the only client where a retry changed code. It is also the client whose sessions wrote the most precise bug reports. Angular 22 scaffolds a zoneless app by default, and three times the model ran into the same consequence: the screen is redrawn a moment after the event, not during it, and a test typing at full speed arrives in the gap. This part walks the sixteen sessions and quotes the model working that out.
- Read CLAUDE-angular.md.txt; the stack block is the only difference from the React file
- Check your Node version against the error below before you run anything: the Angular CLI refuses older 22.x releases
- Open sessions.csv and filter the client column to angular: 16 rows
- Clone the repository and read
clients/angular/AngularClient/src/app/list-view.ts, where most of the app lives - Find the two
detectChangescalls and the autofocus directive; each one is a session's answer to the same timing problem - Run step 14 yourself with the command at the end
Before the first session: Node
The scaffold is ng new from @angular/cli 22.2.5 with CSS
styles, no SSR and npm. It did not run on this PC's Node 22.19.0. The CLI printed
"The Angular CLI requires a minimum Node.js version of v22.22.3 or v24.15.0 or v26.0.0"
and exited with code 3. The comparison kept the system Node for everything else and
gave the Angular and React clients a portable Node 22.23.4, unpacked from the official
zip and put first on the PATH for their sessions and measurements only. If your Angular
22 install fails in the first minute, this is the first thing to check. The scaffold
itself is worth a look: no zone.js in the dependencies, TypeScript 6.0
where the Vite template has 7.0, Vitest for unit tests, a Prettier config, and no lint
target, which the sessions reported in nearly every summary: the project has no lint
script, so none was run.
The sixteen sessions
| Step | Turns | Meter | Wall | Verdict |
|---|---|---|---|---|
| 0 Shell | 29 | $1.01 | 250 s | pass |
| 1 Lists | 25 | $0.63 | 181 s | pass |
| 2 Tasks | 12 | $0.39 | 92 s | pass |
| 3 Detail panel | 22 | $0.84 | 586 s | pass |
| 4 Filters | 35 | $0.95 | 446 s | pass |
| 5 Search and sort | 19 | $0.43 | 144 s | pass |
| 6 Drag-and-drop reorder | 18 | $0.45 | 152 s | pass |
| 7 Subtasks | 41 | $0.93 | 366 s | pass |
| 8 Bulk actions | 25 | $0.59 | 140 s | pass |
| 9 Undo | 25 + 27 | $0.56 + $0.73 | 1041 s | fail, then pass after a two-line fix |
| 10 Keyboard shortcuts | 28 | $0.74 | 176 s | pass |
| 11 Dark mode | 18 | $0.27 | 104 s | pass |
| 12 Import and export | 23 | $0.41 | 125 s | pass |
| 13 Recurring tasks | 17 | $0.44 | 106 s | pass |
| 14 The 10,000-item list | 26 | $0.70 | 195 s | pass |
Fourteen first-time passes, one retry, no hand fix, no dependency added in fifteen steps. Steps 4 and 7 took 35 and 41 turns, both because the model's own full-suite runs showed an earlier step's test failing and it stopped to investigate. Step 9's first verdict failed on a step 9 test with a sixty-second timeout; the retry could not reproduce that, found a different failure that did reproduce, and fixed it. All three episodes are the same bug.
The bug Angular 22 hands to an assistant
Angular 22 is zoneless out of the box. Change detection runs when Angular schedules it, shortly after the event handler returns, not inside the handler. For a human clicking, the gap is invisible. For a Playwright test that clicks and types in the same millisecond, it is a hole. The model met it in step 1: "Angular only redraws the page a moment after a click, so the test started typing before the editor existed." Its answer was an explicit tick after the click and a directive that focuses and selects the editor as soon as it is drawn. In step 7 the rename test lost its first letter: "The app focused the inline title editor one frame late", and the fix was the same directive on every inline editor. In step 4 the checkbox test flickered in full runs and the model diagnosed the race correctly but, following its instructions, did not patch around it: "I didn't change the app to get around it (for example by keeping a just-completed row on screen), since CLAUDE.md says not to work around tests." The retry session of step 9 finally caught it with a trace, called it what it was, "This was a real (small) bug in the app, not in the test.", and made the toggle redraw synchronously: "the app now redraws straight after the instant update, inside the click itself." One line plus a comment, and the suite stayed green through step 14.
The same checkbox test flaked for React and Blazor WebAssembly as well, and their retry
sessions found nothing to fix. Angular is the client where the gap was wide enough to
reproduce every time the whole suite ran, and the client where the fix is a call to the
framework's change detector. If you build zoneless Angular with an assistant, expect to
see detectChanges appear in click handlers, and decide whether you want a
signal-driven design instead before the second step.
What the AI got wrong: the redraw timing above, three times before it was fixed for good. A dark-mode flash it chose not to fix: the theme is applied when Angular starts, so "a dark-mode user can briefly see the light page on load." It said so and left it, because the spec did not ask for an inline script; React's and both Blazor builds added one unasked. Export does not use the API's file name: the buttons fetch the file and name it from the sidebar, which happens to match. It left the scaffold's empty stylesheet in place because deleting needed a permission it did not have, and recreated the shared files by content when the copy was refused, then checked their hashes. 44 denied shell commands, the most of the four, and the most turns spent on the harness rather than the framework. And one bug the suite could not see: the detail panel is never on screen. The router-outlet element is a child of the shared grid and takes its second column, 329 pixels of nothing, so the main column is pushed into the third column and the panel lands on a new row below the viewport, on a page that does not scroll. Every panel test passed, because Playwright treats an element that exists and has a size as visible wherever it is. The screenshots above show the gap; a person found it in the first minute.
What the AI chose
Standalone components, as the scaffold gives them; a small store for the lists shared by
the sidebar and the redirect; the main column in one component with its template
beside it. Routing uses a parent route with two empty children so opening the panel
changes the child route and leaves the loaded list alone, which is the right Angular
answer and the one the React build reached by sharing a route. No Angular CDK: the
drag-and-drop is the browser's own, like the other three clients, and the
virtualization is hand-rolled again, in the model's words "I wrote it myself rather than
using a library." It added two touches the others did not: the visible range only
changes when a new row reaches the top, so most scroll events cause no re-render, and
every row carries aria-posinset and aria-setsize so a screen
reader knows its place in the ten thousand. The first row appeared about 200
milliseconds after the click. Nothing was built with signals beyond what the scaffold
provides; the state lives in class fields and the model drives change detection by
hand where it had to. A team that wanted signals would have had to ask.
What it tells you about building Angular with an assistant
The framework did not block the model; it slowed it. Angular's conventions gave the build more files and clearer boundaries than React's single component, at the price of more turns per step and one real timing bug that a zoned Angular app would never have shown. The model's bug reports were the best of the four clients, with line numbers, reproduction counts and a stated level of confidence, and that is worth something when you are the one reading the summary. The practical advice is the same as for React, with one Angular line added: say whether the app is zoneless or not and how you want state to flow, or the assistant will reach for the change detector every time a test types faster than Angular redraws.
The meter: 16 sessions, 390 turns, $10.08 at list price, the
retry included because it changed code. The meter is Claude Code's own
total_cost_usd; on a Max plan it is a measure, not a bill.
Checkpoint: clients/angular in
the repository,
commits "step 00: Shell" to "step 14: The 10,000-item list"; the step 9 retry is its
own commit, two lines in list-view.ts.
Do it yourself
Run the last step again with the published prompt and compare your transcript with the row above.
- Check
node -v: 22.22.3 or newer, or 24.15.0, or 26. Otherwise install one beside your system Node and put it first on the PATH for this folder only. - Clone the repository, start the API on Angular's port and check out the step 13 commit of
clients/angular:dotnet run --project api -- --urls http://localhost:16040 --db run/angular.db - Install the client and the suite once:
npm --prefix AngularClient install, thennpm installine2e, and copy.env.client.exampleto.env.client. - Sign in to Claude Code and run step 14 from the repository root:
powershell -NoProfile -ExecutionPolicy Bypass -File harness/run-step.ps1 -Client angular -Step 14 -Run 1 -RunClaude harness/run-claude.ps1 - Run the frozen suite yourself:
npx playwright testine2e, and run it a second time in full; the checkbox test is the one to watch. - Open
list-view.tsat the new commit and count thedetectChangescalls.
What you should see: a verdict of 61 passed, a turn count in the twenties, and a hand-rolled window of about 40 rows above and below the viewport.
Frequently asked
- Why did the Angular CLI refuse to run on Node 22.19?
- Angular CLI 22.2.5 requires Node 22.22.3 or newer, 24.15.0 or newer, or 26, and exits with code 3 otherwise. A portable Node 22.23.4 first on the PATH for the Angular folder fixes it without touching the system install.
- Why did an Angular retry change code when the React retries did not?
- Angular 22 is zoneless by default, so the screen is redrawn a moment after a click rather than during it. A test clicking the checkbox and reading the row in the same millisecond found the old state every time the whole suite ran. The retry session saw it in a trace and made the toggle call detectChanges inside the click; React's optimistic update had no such gap.
- Did Claude Code use Angular CDK or signals?
- Neither. Drag-and-drop uses the browser's own events, the 10,000-row list is a hand-written window with aria-posinset on every row, and state lives in class fields with explicit change detection where the sessions needed it. The prompt asked for features, not for an architecture, and that is what it got.
Next: Part 5, the numbers for all four clients and the seven hypotheses settled one by one. The two Blazor builds are in Part 2.