jonnifyagile agents
jonnify / Agile Agent Workflow / A2 · Decomposition / A2.07 / A2.07.2 · Split and test

A2.07 · Workshop · dive 2

Split and test

The web vision is too big for one rung. It is cut into nine vertical slices — then one rung is laid out as its four artifacts and proven by its Given/When/Then.

Serve the Portal to people splits into nine rungs, f6.1–f6.9, each shipping one capability over the unchanged Portal facade. Zoom into F6.6 (LiveView): every rung is carried by exactly four artifacts — a roadmap line, a spec, Given/When/Then stories, an agent brief. Switch the artifact on the right to read what each one contributes and the question it answers.

F6.6 · its four artifacts · select one

F6.6 · LIVEVIEW · ONE RUNG, FOUR ARTIFACTS phoenix.roadmap.md · the roadmap line the delivery view · place in the order, demo, harness, feedback f6.6.md · the spec what & prove · goal, scope, deliverables, invariants, done f6.6.stories.md · the stories acceptance · US0–US5, Connextra + Given/When/Then f6.6.llms.md · the agent brief agent instructions · references, topology, paste-ready prompt
roadmap — phoenix.roadmap.md · the roadmap line · answers: how it ships. Gives: F6.6's place in the order, its demo (search as you type; create without a reload), its harness (LiveViewTest), and the feedback it asks for. Four artifacts carry one rung.

One vision, nine vertical rungs

The vision for the Portal’s web surface is one line: serve the Portal to people. That is too big for one rung — it is a store, an authenticated learning area, real-time updates, and an operations dashboard at once. So it is cut into nine vertical slices, f6.1 through f6.9, each a capability a real role can use, each shipping over the system already running: F6.1 boot, F6.2 routing, F6.3 Ecto, F6.4 contexts, F6.5 HEEx views, F6.6 LiveView, F6.7 PubSub, F6.8 auth, F6.9 dashboard.

What keeps the cut vertical — a slice of value, not a layer of work — is a single rule every rung obeys. Each rung adds web surface and calls only the unchanged Portal facade, so nothing under the F5 engine ever changes. That is the master invariant, quoted from the roadmap:

The web layer calls only the Portal facade and renders only the closed %Portal.Error{} set. No controller, LiveView, plug, or template names Portal.Engine, a repo, or GenServer.call.

The rungs depend only downward: F6.5 renders what F6.3 and F6.4 make queryable, and F6.6 makes F6.5’s pages live. Splitting yields the slices; the rest of this dive shows how one of them — F6.6 (LiveView) — is laid out and proven.

The nine rungs are the same ladder the A2.07 workshop hub walks rung by rung. Each is specified, built, and proven in the companion course at /elixir/phoenix/liveview and its siblings. Here the work is the split and the test, not the build.

One rung, four artifacts — F6.6 (LiveView)

A rung is not one document. Every rung is carried by exactly four artifacts, and each answers one question. F6.6 delivers F6.5’s catalog made interactive without reloads — CatalogLive streams from the facade, a two-stage mount, a live search box via Portal.search_courses/1, and a live create form. Those four artifacts, written out, are below; the inspector in the hero highlights one at a time.

# F6.6 · LiveView — the four artifacts that carry one rung.
phoenix.roadmap.md   # the roadmap line — how it ships
  F6.6 | interactivity (live search, live create, streams)
       | search as you type; create without a reload
       | LiveViewTest (render_change/render_submit)
       | does the interaction feel right?

f6.6.md              # the spec — what & prove (Goal · Scope · Deliverables · Invariants · Done)
  Goal: After F6.6, the catalog is interactive without a page reload.
        CatalogLive (use PortalWeb, :live_view) streams the …

f6.6.stories.md      # the stories — acceptance (US0–US5, Connextra + Given/When/Then)
  As a learner, I want the course list to filter as I type,
  so that I find a course without submitting or reloading.   # F6.6-US1

f6.6.llms.md         # the agent brief — references, topology, paste-ready prompt
  the instructions an agent runs to build and self-check the rung.
roadmap
One line in phoenix.roadmap.md — place in the delivery order, the demo, the test harness, the feedback asked. Answers how it ships.
spec
f6.6.md — Goal, Rationale (5W), Scope, Deliverables, Invariants, Definition of Done. Answers what & prove.
stories
f6.6.stories.md — six Connextra stories (US0–US5), each with Given/When/Then and tagged INVEST, priority, size. Answers acceptance.
brief
f6.6.llms.md — the references, requirements, execution topology, and the paste-ready prompt. Answers agent instructions.

The four are not redundant: the roadmap orders the rung, the spec bounds it, the stories make it acceptable, and the brief makes it buildable by an agent. The next section runs the acceptance — the third artifact — for F6.6’s first story.

Proven, not asserted — F6.6-US1

A slice is ready not when it is small but when its acceptance can be run. F6.6’s first story carries its proof as Given/When/Then — quoted verbatim from f6.6.stories.md:

# F6.6-US1 · Search as I type
As a learner, I want the course list to filter as I type,
so that I find a course without submitting or reloading.

Given the search box, when I type, then phx-change="search"
  fires handle_event("search", params, socket).
Given the event, when it runs, then it filters through the
  Portal.search_courses/1 facade function and re-streams :courses
  with reset: true (the list is @streams.courses, never an assign — INV4).
Given each keystroke, when handled, then the rendered list narrows
  without a reload, and the view names only Portal.

Each line is a step that either runs or does not. Step the scenario below: each Given/When/Then reports PASS against a fixed model of the search box, the fired event, and the re-stream. That is the difference between a slice that asserts it works and one that proves it — the proof is executable.

F6.6-US1 acceptance · step Given → When → Then

GIVEN · the search box PASS · phx-change="search" is bound WHEN · I type → handle_event("search", …) filters via Portal.search_courses/1 · re-streams :courses reset: true THEN · the list narrows, no reload @streams.courses narrows · the view names only Portal
GIVEN · PASS — the search box: the live catalog binds phx-change="search". Typing will fire handle_event("search", params, socket). Step to When to run the event.

A slice converges when its acceptance can be run — on the Portal that proof is the verbatim Given/When/Then of the rung, not a claim that it works.

the principle

The contract is the spec. A slice is ready when its acceptance is written as Given/When/Then — so it is proven, not asserted. Splitting yields slices; the test is whether each can be run.

on the Portal · F6.6-US1

Typing fires phx-change="search" → handle_event("search", params, socket) → Portal.search_courses/1 → re-streams :courses with reset: true. The view names only Portal.

The four artifacts make the rung; the Given/When/Then makes it acceptable — the proof runs.

Recap — split into rungs, one rung proven

The web vision split into nine vertical rungs, each shipping one capability over the unchanged Portal facade. One rung, F6.6 (LiveView), was laid out as its four artifacts — roadmap line, spec, stories, brief — and its first story was proven by running its Given/When/Then: typing fires phx-change="search", the event filters through Portal.search_courses/1, and the list re-streams without a reload. A slice is ready when its acceptance can be run. The rungs are still in one heap — the last dive orders them into milestones.

Next in A2.07: A2.07.3 · Order the backlog — order the nine rungs by dependency into three milestones, a value ladder that stays runnable at every rung, and read the git history of how the specs evolved: specified, shipped, reconciled.

References

Primary sources for vertical slicing and executable acceptance, and the chapter moves this dive applies.

Sources

Related in this course