docs(steps): the in-order reading, and what validating it found
Spec §3.13.1 describes the rail as built: what it is made of (records the same pass makes as the links), what makes the fold possible (a guard naming the decision it belongs to), the items, and the words. `CLAUDE.md` names `api/program.ts` and what the guard reader now returns. `CHANGELOG.md` gets the user-facing feature and the three fixes under it. Both plans now say what happened: the 2026-08-29 plan carries a BUILT header with where the build differs from it (a guard's `branch`, reading a function once per rail, blocks as one kind carrying facts, loops needing a reading of their own), the answers to its open questions, and a §8 recording the six endpoints read against their source — plus the two gaps left open on purpose, a mongoose `product.save()` the effects table does not know and the nested `const handleX = async () => …` that is still not a node. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01REFyW9hmNrxhwN5wxRoAkC
This commit is contained in:
co-authored by
Claude Opus 5
parent
676030314a
commit
22f92a828b
@@ -1,8 +1,29 @@
|
||||
# Steps, in the code's order — plan
|
||||
|
||||
**Status:** plan, written 2026-08-29 on `feature/steps-servers` (tip `5797d7e`), for a fresh session. Nothing
|
||||
below is built. The Steps tab as it stands (spec §3.13, `src/ui-server/api/steps.ts`, `ui/src/views/StepsView.svelte`)
|
||||
is the base; everything here is a second *reading* of the same walk, not a new walk.
|
||||
**Status: BUILT** 2026-08-29 on `feature/steps-servers`, P0–P6. Written the same day (at tip `5797d7e`) as a plan for a
|
||||
fresh session; what follows is that plan, kept as written, with the notes below on where the build differs from it.
|
||||
The reading is `&view=order` on the Steps tab, `src/ui-server/api/program.ts` + `ui/src/lib/program-model.ts`, and
|
||||
spec §3.13.1 is the description of what was built.
|
||||
|
||||
**Where the build differs from the plan:**
|
||||
- **A guard carries the decision it belongs to** (`BranchGuard.branch`), not just its text and line — §4.1's
|
||||
"same `line`, same `text`" pairing does not tell one `switch` case from another, nor two `try`/`catch` blocks apart.
|
||||
It also carries how the arm it is in leaves (`armExit`) and, for an early exit, how the arm not taken leaves (`exit`),
|
||||
which is where `WireArm.ends` comes from.
|
||||
- **A function is read ONCE per rail** (`again` on the item), not redrawn at every call: expanding per path turned an
|
||||
87-step screen into 3,849 items and 618 KB. Once-only is 476 items and 94 KB (+3% wall clock on that picture).
|
||||
- **A step the walk entered reads on into its own body** under its box, so the rail holds the same steps the tree does
|
||||
(§4.1.7 only said this for boundaries under `through`).
|
||||
- **`WireItem`'s blocks are one kind with a discriminator** (`block: 'inline' | 'loop' | 'later' | 'together'`) rather
|
||||
than four item kinds, and they carry facts (`by`, `via`, `loop`) rather than words — the viewer says them.
|
||||
- **Loops needed their own reading** (`loopsForFile`), and loops and forks nest by which construct BEGINS first, since
|
||||
neither reading knows about the other.
|
||||
- The open questions of §7 were answered: order for functions/endpoints and the tree for screens (1); read each
|
||||
function once rather than capping the fold depth (2); a loop's body once, marked (3); the fork carries its condition
|
||||
and the arms say WHEN / WHEN NOT (4).
|
||||
|
||||
The Steps tab as it stood (spec §3.13, `src/ui-server/api/steps.ts`, `ui/src/views/StepsView.svelte`) is the base;
|
||||
everything here is a second *reading* of the same walk, not a new walk.
|
||||
|
||||
**The ask, in the maintainer's words:** on proshop's `POST /api/users/login` the picture draws `User.findOne · jwt.sign ·
|
||||
200 · 401` in one row under the handler. That is true — all four are one step from `authUser` — and it is not what a
|
||||
@@ -272,3 +293,36 @@ early exit, and the tree mode is byte-for-byte the picture it is today.
|
||||
3. **Loops.** Body once with `for each …`, or unrolled never. Proposal: once, marked.
|
||||
4. **Should the rail replace the pills' "→ …x" placement rule?** The rail's forks carry the condition once, on the
|
||||
fork; the arm's `WHEN` / `WHEN NOT` is the pill. The scenario rows in the panel stay as they are.
|
||||
|
||||
---
|
||||
|
||||
## 8. What the validation pictures found (2026-08-29, P5)
|
||||
|
||||
Read against the source, endpoint by endpoint. Every reading below is the one the rail draws today.
|
||||
|
||||
| Repo | Anchor | Reads as | Verdict |
|
||||
|---|---|---|---|
|
||||
| `bradtraversy/proshop_mern` | `POST /api/users/login` | `User.findOne` · fork on the password check · [`via generateToken` → `jwt.sign`, then `200`] \| [`401`], both arms answering | §1 exactly |
|
||||
| " | `POST /api/products/:id/reviews` | `Product.findById` · fork on `product` · [fork on `alreadyReviewed` → `400` \| `201`] \| [`404`] | three outcomes, right |
|
||||
| `gothinkster/node-express-realworld-example-app` | `POST /users/login` | `via login` → two `422` guards, `prisma.user.findUnique`, `if user` → `bcrypt.compare` → `if match` → `via generateToken`, then `403`; then the handler's own `200` | right, after the span fix |
|
||||
| `brocoders/nestjs-boilerplate` | `POST /auth/email/login` | `via validateLogin` → `findByEmail`, `!user` → `422`, two throwing guards, `bcrypt.compare`, then `sessionRepository.create` and `via getTokensData` → **`together Promise.all`** of two `jwtService.signAsync` | right, after dropping the `update` name-match |
|
||||
| `leerob/next-saas-starter` | `signIn` (server action) | drizzle `select`, `length === 0` → return, `!isPasswordValid` → return, **`together Promise.all`** of `setSession` (→ `signToken` → `SignJWT`) and `logActivity` (→ `db.insert`), then `redirectTo === 'checkout'` → the whole checkout session \| `/dashboard` | right, after the lending fix |
|
||||
| `fastapi/full-stack-fastapi-template` | `POST /login/access-token` | `via authenticate` → `session.exec`, `not db_user` → return, `not verified` → return, `updated_password_hash` → `session.add`/`commit`/`refresh`; then `not user` → `400`, `elif not user.is_active` → `400`; then `via create_access_token` → `jwt.encode` | right, after the `elif` fix |
|
||||
| `amniservices-mobile-app` | `/capture/review` | unchanged: 87 steps, 160 links, the tree, `defaultView: 'tree'` | the regression check |
|
||||
|
||||
**Three defects the pictures caught, all in the walk and both readings** — a hop's span taken from a call that was not
|
||||
the one asked for (an inline Express handler's edges carry the route's line, so the registration's span swallowed the
|
||||
body); a name-match the call as written disproves (`crypto.createHash('sha256').update(…)` followed into the caller's
|
||||
own `AuthService.update`); and a value lent nothing because one plain `references` edge counted as a body of its own
|
||||
(`const signIn = validatedAction(schema, async (data) => { … })` drew one call out of nine). Plus an `elif` whose body
|
||||
raises being read as ending the arm it is written in.
|
||||
|
||||
**Left open, deliberately:**
|
||||
- **A mongoose document's `product.save()` is not in the effects table** (`api/effects.ts`), so proshop's review
|
||||
endpoint draws its `201` but not the write before it. A JS rule for `<lowercase receiver>.save` would catch it and
|
||||
would also catch `canvas.save()` / `ctx.save()` / `sharp(...).toFile`-adjacent idioms in any web app — a call for
|
||||
the maintainer, not a silent widening.
|
||||
- **A nested `const handleX = async () => …` is still not a node** (the plan's §6): its sites belong to the enclosing
|
||||
component. Fixing it is an extractor change with a Rust kernel twin.
|
||||
- The **`via` name of an inlined helper is its bare name** (`via create`), which is ambiguous when two classes have a
|
||||
`create`. The panel disambiguates; the rail could say the class.
|
||||
|
||||
Reference in New Issue
Block a user