Cookbook: ship a feature end to end
One narrative that takes a small feature from a rough idea to a merged PR using Piyaz, brainstorm through composer.
Cookbook: ship a feature end to end
This page follows one feature from a sentence in your head to a merged pull request, using Piyaz the whole way. The feature is small and concrete: add CSV export to the reports page. Users can already view a report in the browser; they want a download button that writes the same rows to a CSV file.
The story (the feature, the task titles, the file paths) is illustrative. Every command, tool call, and status transition is real. The project identifier is DEMO, so task refs look like DEMO-1.
All tool snippets below are abbreviated and illustrative. Real responses carry more fields. The tool names, actions, and parameters match the MCP tool reference.
1. Brainstorm the idea into a project brief
You open a session and type /piyaz. You describe the feature in a sentence. Piyaz runs the brainstorm flow: it parses what you said, lists what is covered, and asks one focused question per turn about the gaps (which report formats, who can export, where the button lives). You answer. It pushes back where a choice is weak and proposes a default where you have none.
When the synthesis is ready, Piyaz presents it and waits for explicit approval before writing anything. You approve. It creates the project:
piyaz_project action='create'
title='Reports CSV export'
description='Add CSV download to the reports page. Authenticated users
export the current report view as a CSV file matching the on-screen
columns. Server streams the file; the client triggers the download.'
categories=['frontend', 'backend', 'api']The response carries the new projectId and the identifier DEMO. Every task you create from here gets a ref under that prefix.
2. Decompose into a small task graph
The brief is short and names a handful of features, so you decompose it inline rather than dispatching the decompose agent. Piyaz proposes a task list, you approve it, and it writes three tasks plus the edges between them.
Create the tasks
Each task gets a verb plus noun title, a two to four sentence description, two to four binary acceptance criteria, one category, and the three tag dimensions (work type, cross-cutting concern, tech).
piyaz_task action='create'
title='Add CSV serializer for report rows'
description='Convert a report result set into RFC 4180 CSV text.
Pure function over the existing report row type; no I/O.'
acceptanceCriteria=['Serializes all on-screen columns in order',
'Escapes quotes, commas, and newlines per RFC 4180']
category='backend'
tags=['feature', 'data-export', 'typescript']
priority='core'
estimate=3Two more follow: DEMO-2 adds the export endpoint, DEMO-3 adds the download button. The create response returns each ref.
Wire the dependency edges
The endpoint needs the serializer; the button needs the endpoint. Those are depends_on edges. The note is required and briefs the developer who will start the source task.
piyaz_edge action='create'
edgeType='depends_on'
sourceTaskId='<DEMO-2 uuid>'
targetTaskId='<DEMO-1 uuid>'
note='The endpoint calls the serializer to produce the response body.
Import it; do not re-implement CSV escaping in the handler.'A second edge wires DEMO-3 to DEMO-2. The result is a chain: DEMO-1 to DEMO-2 to DEMO-3.
Confirm the shape
A quick check shows the graph is what you expect.
piyaz_analyze type='ready'
# => DEMO-1 (no unfinished deps)
piyaz_analyze type='critical_path'
# => DEMO-1 -> DEMO-2 -> DEMO-3DEMO-1 is the only ready task because the other two depend on unfinished work. All three sit on the critical path. For the granularity and edge rules behind this step, see Decompose into tasks.
3. Compose the first task
You type /piyaz:composer. Composer bootstraps the session, asks you to paste its /goal harness so the loop has a clean exit, then picks the highest-value ready task on the critical path. Here that is DEMO-1. It emits a one-paragraph pick rationale, then dispatches four agents in sequence, each in a fresh context.
The composer orchestrator never writes task status itself. Every transition below belongs to a subagent. For the full design, see The composer pipeline.
Researcher (Phase 1)
The researcher reads the task, maps it to the codebase, checks library versions, and reads the project's conventions (test command, lint command, PR template). It applies sharpening refinements directly to the task (tighter description, binary acceptance criteria) and returns a brief. It writes refinement fields only; it never touches status, implementationPlan, or executionRecord.
Abbreviated brief:
Files to touch: src/reports/serialize.ts (new)
Existing patterns: src/reports/types.ts:12 ReportRow type to reuse
Conventions: test = bun test, lint = bun run lint, PR template present
Applied refinements: tightened AC 2 to name RFC 4180 explicitly
Confidence: 0.9Planner (Phase 2)
The planner reads the brief and the planning context, writes the unabridged implementationPlan, and owns the one transition that moves the task off draft.
piyaz_task action='update' taskId='<DEMO-1 uuid>'
implementationPlan='## Goal\nAdd a pure CSV serializer...'
status='planned'The task is now planned. The planner writes no other status.
Implementer (Phase 3)
The implementer reads the plan via piyaz_context depth='agent', claims the task, branches, writes code, and runs the project's checks until green.
piyaz_task action='update' taskId='<DEMO-1 uuid>' status='in_progress'It implements src/reports/serialize.ts, runs bun test and bun run lint, opens a PR using the repo's template with [DEMO-1] in the linked-task section, then marks the task in_review with the full Completion Protocol payload.
piyaz_task action='update' taskId='<DEMO-1 uuid>'
status='in_review'
executionRecord='Added serializeReportRows in src/reports/serialize.ts;
reuses ReportRow; escapes per RFC 4180. 14 unit tests, all green.'
decisions=['Pure function over ReportRow so the endpoint and any
future export path share one serializer']
files=['src/reports/serialize.ts', 'src/reports/serialize.test.ts']
acceptanceCriteria=[{ text: 'Serializes all on-screen columns in order', checked: true },
{ text: 'Escapes quotes, commas, and newlines per RFC 4180', checked: true }]
prUrl='https://github.com/acme/reports/pull/142'in_review is the implementer's terminal write. No agent promotes its own work to done.
Reviewer (Phase 4)
The review agent reads piyaz_context depth='review', forms a first-pass verdict from the diff alone, then reasons across five lenses (security, performance, reliability, observability, codebase standards). It is read-only over Piyaz. It returns one verdict: approve, request-changes, or block.
# Review verdict: approve
Task: DEMO-1 "Add CSV serializer for report rows"
PR: .../pull/142 (CI: green) | ACs: 2/2 satisfied
Security: no findings; serializer takes typed rows, no raw input.
Reliability: escaping covers quotes, commas, newlines (serialize.ts:31).The verdict is advisory. Composer surfaces it to you and stops; it does not flip status on the strength of the verdict.
4. The human gate, then propagate
You read the verdict and the PR on GitHub. The work is good, so you approve and merge, then move the task across the gate yourself. This in_review to done transition belongs to you, not to any agent. See Human in the loop for why.
piyaz_task action='update' taskId='<DEMO-1 uuid>' status='done'Composer propagates: it reads the task's edges, finds the downstream dependents, and refreshes any edge note the merged work changed.
piyaz_query type='edges' taskId='<DEMO-1 uuid>'
piyaz_analyze type='downstream' taskId='<DEMO-1 uuid>'
# => DEMO-2 now unblockedWith DEMO-1 done, DEMO-2 has all its dependencies satisfied and becomes ready.
5. Loop until the graph is done
Composer returns to step 1 and picks DEMO-2, the export endpoint. Same four phases: the researcher finds the route conventions, the planner writes the plan, the implementer wires the endpoint to call the serializer (the edge note told it to import, not re-implement), the reviewer checks authn on the new path and returns its verdict. You merge, flip the task to done, and propagation unblocks DEMO-3.
DEMO-3 adds the download button and the click handler that hits the endpoint. After it merges and you mark it done, piyaz_analyze type='ready' returns an empty set. That empty set is one of composer's stop conditions, so the loop exits. The feature is shipped: three PRs, three reviewed merges, one honest dependency graph that recorded every decision along the way.
What carried the work
- The task graph kept the order honest.
DEMO-2could not start untilDEMO-1shipped, and the edge note told its implementer to reuse the serializer. - The execution records and decisions meant each phase started from ground truth instead of guessing. The endpoint's implementer read why the serializer is a pure function before writing a line.
- The human gate stayed yours. Agents researched, planned, implemented, and reviewed; you owned every
in_reviewtodonemerge.
For the deep version of this pipeline, including failure handling, oversize detection, and the per-phase tool restrictions, read The composer pipeline. For parallel work across independent ready tasks, see Parallel dispatch.