After the Chat: Spec, Tickets, Implement, and the Code Review That Keeps AI Coding Honest
Skills For Real Engineers Series 3: Once the Requirements Are Clear, How to Take the Main Flow Step by Step to Delivery?
Picking up from the previous post.
The second post talked about grill-with-docs: first check facts, discuss the goal, trade-offs, and boundaries clearly, then confirm both sides understand identically.
Questions are asked, and I nod. Now it's time to write code, right?
Yes. But if this work needs to be done in multiple sessions, or even continues in another conversation, "we already agreed" becomes unreliable. The chat contains both final decisions and recommendations I rejected. Someone who takes over reading "upgrading the logging system" may not know why I also kept the old implementation.
This post continues: how to turn the discussion into a specification, split it into tasks, then move forward with implementation and verification, and do review.
grill-with-docs → to-spec → to-tickets → implement
├─ implementation and verification
├─ code-review
└─ commit
Note that code-review is already a required closing step of implement, so there's no need to call it again after each task ends. Small tasks don't need to go through the full path; more on that later.
The example continues with my open-source project RxStudy. The logging code and documentation are real, but the logging spec and tasks are teaching examples distilled from the scenario; this round's inspection did not find a complete call chain of the last three skills in RxStudy, so I'm not writing the example as if it were the actual implementation at the time.
Also, I'll intersperse real calls from other projects, anonymously explaining the context at the time. Those experiences do not belong to RxStudy. In the call displays, I only unify the skill entry point in the tools as /skill-name, restore escaped file names, and do not expand my instructions; excerpts of the assistant's original replies and summaries of historical processes are labeled separately.
1. After the discussion is clear, why organize a spec?
Assume we've already confirmed: keep the old logs, the new system integrates independently; select the mode at compile time, no runtime switching; the new logs can be exported, failures must be reported, and the old directory must not be packaged in.
These words are scattered across dozens of chat turns, so it's still possible to continue working. But to check "what exactly have I promised to deliver this time," I have to flip back and forth, and also distinguish what's already been voided.
That's what to-spec does. Its opening is very direct:
Do NOT interview the user; just synthesize what you already know.
Meaning: use the existing discussion and code understanding to organize the spec, don't restart the interview. to-spec source
Cough, right after being grilled, if someone asks me "please describe your requirement" again, I really would close the window.
1.1 No re-interview, but still confirm how to test
The skill additionally reads code it hasn't seen, uses project terminology, and respects relevant ADRs. Then it proposes from which interface to verify behavior and asks me to confirm.
The source calls this position seam. In the logging example, think of it as: call the export interface and check the returned ZIP and errors, rather than reaching inside to assert how many times a private function was called.
It also requires preferring existing positions when possible, observing from a higher level, and reducing scattered test entry points. This confirms the verification convention, not restarting the whole requirement discussion.
Here's a real call. On October 9, 2026, I discussed an app entry refactor in another project: the entry file had too many responsibilities, and Repository was created at startup; I wanted it created lazily.
Earlier, we confirmed turn by turn: split entry responsibilities; unify configuration creation rules, create on first use, reuse by scope; tests can independently replace dependencies. Request timing and page state stay unchanged, navigation behavior stays unchanged, and the verification entry was already confirmed.
When we got to to-spec, I only said:
Run /to-spec
An excerpt of the assistant's original reply:
The test seam was already confirmed in the previous round; this time it will be reused directly.
Then it organized the confirmed discussion into a spec, published it to the locally configured task tracker, marked it as ready-for-agent, did not re-interview, and did not modify business code. That's a summary of the historical process.
The command is short, but the preceding discussion is not. If you only enter this phrase in a blank session, the assistant doesn't know which options were rejected or whether the testing approach is confirmed. A short instruction can continue because the basis is still in the context.
Back to RxStudy, it's configured with GitHub Issues, so under that configuration "publishing a spec" means creating an Issue. The storage method differs from the local practice above, but the organizing process the skill requires still applies. If a task system isn't configured, it prompts you to run the initialization skill first.
1.2 What should the spec leave behind?
The source template includes problem, solution, user stories, implementation decisions, test decisions, out of scope, and supplementary notes. User stories require sufficient coverage of functionality; below is only an excerpt relevant to this post:
## Problem
When troubleshooting network issues, detailed logs are needed and must be exportable for review.
When introducing a new system, the old system's behavior and fallback must still be preserved.
## Delivery requirements
1. Developer can choose the logging system before compilation, effective after restart.
2. In Modern mode, callers receive a ZIP address containing the new logs.
3. On export failure, callers get a clear error; the new system does not export the old directory.
## Confirmed decisions
The two systems are independent; only one is enabled per startup.
Debug provides detailed network output; Release keeps only failure summaries.
Export callbacks run on the main queue; successful ZIPs are cleaned up by the caller after use.
## Testing decisions
Observe behavior from log output and export interface; inspect actual ZIP contents and error results.
Isolated regression and engineering builds are reported separately; device system logs are verified separately.
## Out of scope
No runtime switching, upload service, or export page; no replacement of the old export entry.
This is teaching organization, not a published spec. A complete spec would also cover other confirmed behaviors, such as redaction strategy.
Specs and tasks generally don't write specific file paths or code snippets, to avoid becoming quickly stale after implementation changes. This version allows keeping key fragments from prototypes that are more accurate than text, such as state machines or types, but doesn't require moving the entire demo in.
Figure 1: The spec preserves the overall commitment; tickets take over one batch of delivery. Related evidence still must be findable.
As a side note on version differences: in the RxStudy baseline used here, the configuration source selects .modern, while the logging docs and test scripts still require the default .legacy. These don't match, so you can't keep treating "default old mode" as the source-of-truth fact for that commit. Requirements formed in discussion and the current implementation must also be cross-checked separately this way.
2. Spec and tickets, what problems do they solve respectively?
The spec answers "what overall do we promise to deliver"; a ticket answers "when this batch finishes, what specific capability is added."
Taking export as an example, the spec includes mode selection and output strategy, and also export and compatibility boundaries; a ticket can be responsible only for the normal export path, but that path must be done completely.
Below is a complete teaching ticket. A and B are just article numbers, not real Issue numbers.
# A: Modern mode can record and export logs
## Source and background
Related to "logging refactor and export" spec and its testing decisions.
Provides developers with offline review capability for new logs while preserving the old system.
## This batch delivers
After selecting Modern and launching, write a specified log, call the export interface,
and get a ZIP that is unzippable and contains that log.
## Acceptance criteria
- [ ] The most recently written specified log appears in the unzipped file.
- [ ] The ZIP still exists when returned; callers can read it.
- [ ] Completion callback is on the main queue.
- [ ] Temporary snapshot is cleaned; original logs are not deleted by export.
- [ ] The agreed isolated regression verifies the above behavior.
## Blockers
None; can start immediately.
## Scope and verification conventions
No new upload or page, no replacing the old export entry.
Error branch expansion is in ticket C; this ticket still handles basic failures.
Inherits the spec's mode isolation restrictions; device system logs are not part of this ticket's verification results.
The to-tickets remote task template provides parent task and delivery content, and also lists acceptance criteria and blocker relationships. Expanding background, scope, and verification conventions here is my suggestion for follow-on tasks; it's not that every field is mandated by the template.
There's also no need to copy the whole spec into every ticket. Key constraints can be briefly reiterated; other decisions are found through explicit references. Only "implement export feature" as six characters leaves the next person guessing: export which directory? When is the success file deleted? Can the old entry be changed?
3. to-tickets: every ticket must be verifiable
to-tickets can split tasks from a plan, spec, or current discussion. When given a spec path or an Issue reference, it reads the full content and comments; if it hasn't seen the code, it explores first. to-tickets source
It emphasizes tracer bullet, meaning vertical slices: narrow scope, but complete from entry to result, doable and verifiable when finished, sized to fit a new context.
3.1 Don't mistake parts being ready for capability existing
"Write a file manager," "write a compression layer," "hook callbacks" sounds like a clear division, but the first three tickets can all be done and the caller still can't export a single file.
I prefer splitting by behavior.
| Teaching ticket | What can be verified when done | Blocked by |
|---|---|---|
| A: Record and export | Write specified log, get valid ZIP, contents checkable | None |
| B: Distinguish build output | Debug has detailed network content, Release keeps only failure summary | A |
| C: Export error feedback | Errors returned for uninitialized, no logs, compression failure, etc.; temp artifacts cleaned correctly | A |
| D: Preserve old mode and fallback | Old behavior preserved after switching back; new interface doesn't implicitly enable Modern; two directories isolated | A |
This assumes A delivers a runnable new log entry and a normal export path, so the other three depend on it; there's no phantom connection among B, C, D. Each carries its own verification; you can't add a final "write the tests for all" ticket to prove completion for all previous tasks.
D also doesn't mean you can break the old system earlier. Preserving the old implementation is a constraint throughout; D specifically delivers full assurance for mode switching and fallback scenarios.
Figure 2: Arrows indicate blocking relationships; no mutual blocking doesn't mean there won't naturally be conflicts when editing the same file.
This example has no UI, so there's no need to add a page to satisfy "vertical". Completeness means passing through the necessary parts to complete that behavior, not requiring every ticket to include UI, network, database.
3.2 Confirm before publishing after splitting
The skill displays titles and blocking relationships, explains the delivered behavior, and asks whether granularity is appropriate, dependencies are real, and whether to merge or split further. Publish only after confirmation.
In the earlier app entry refactor, after the spec was generated, I only said:
Run /to-tickets
It didn't write to disk directly; instead it first gave a draft of five tickets. Below is an anonymized historical split summary, not a verbatim quote:
| No. | This batch delivers | Blocked by |
|---|---|---|
| 01 | Split entry responsibilities, keep existing behavior, related tests continue passing | None |
| 02 | Introduce explicit runtime mode and on-demand assembly, first get one startup flow through, keep legacy entry compatible | 01 |
| 03 | Migrate remaining app-scope dependencies and related tests | 02 |
| 04 | Migrate session-scope dependencies, verify session switching and old request isolation | 02 |
| 05 | Delete old constructor entry, complete full test and static analysis | 03, 04 |
The actual dependency structure is:
01 → 02 → 03/04 → 05
The assistant then asked whether granularity and blocking relationships are acceptable, and my exact reply was "confirmed." Only after that did it publish five local tasks and record acceptance criteria and dependencies.
This is not a universal ticket-splitting template, nor is it an execution record of the RxStudy log tasks. It shows the arrangement during a migration: first make new and old coexist, two migration batches share a prerequisite, and only at the end can the old entry be deleted. My sending just one command doesn't skip the split confirmation in the middle.
Remote: one Issue per ticket, created in dependency order, starting with platform-native blocking relationships; when the source is an existing Issue, it's associated as a subtask. Local: one Markdown file per ticket, with dependencies recorded in the text. The skill clearly does not close or modify the parent Issue.
Execution picks tickets whose blockers are complete, not by smaller number. If necessary prerequisite refactoring exists, the skill requires doing it first; but you can't sneak in a round of large unrelated cleanup.
3.3 Why does full-repo migration have an exception?
If you're just renaming a shared interface but simultaneously breaking many callers, forcefully splitting "one ticket per page" might make every ticket fail its checks.
The source provides expand–contract: first coexist new and old forms, then migrate batch by batch by package or directory, and finally delete the old form only after all batches complete. If a batch still cannot independently keep passing, then share an integration branch and close with a final integration verification task; you can't claim every batch is independently mergeable.
RxStudy's CocoaPods/Tuist dual build involves many files, but that's an engineering and dependency collaboration problem; you can't apply this exception just because "the changes are many."
4. implement: how to move forward after getting a ticket?
implement's body is also short. Given a task reference, it first fetches the task and reports the title; if the reference is ambiguous, it asks. At least it lets me notice early: did you pick the wrong ticket?
Then several explicit rules: call tdd at pre-agreed locations as much as possible; frequently do type checks and single test file checks; at the end run the full test suite; call code-review, then commit to the current branch. implement source
So it writes code, obviously, but the work doesn't stop at generating code.
4.1 The agreed tests, don't change them mid-writing
The dependent tdd requires first confirming test location, then advancing as "one failing test, one minimal implementation," testing external behavior. tdd source
In the export example, I want to see "the ZIP really contains the just-written log," not just "the compression function was called once." If you swap the internal implementation and behavior stays the same, tests shouldn't all break along with it.
When actually taking on a well-written ticket, the instruction can be even shorter. Still from the October 9, 2026 record, I executed another closing task:
/implement execute 08-controller-removal-review.md
This numbered task already existed. This round I directly specified the file; the assistant first located and read it, then confirmed implementation scope and verification criteria. An excerpt of its opening reply:
First locate and read
08-controller-removal-review.md, confirm implementation scope and verification criteria, then modify, review, and commit to the current branch.
The historical closing report recorded test and static analysis results, and also recorded standard and spec review as well as local commit, while clearly stating device verification had not yet been run. This is a summary of the historical session report; this round did not re-verify those results, nor can they prove RxStudy passed checks.
Also distinguish the order: this 08 task came first; the entry refactor discussion and ticket split introduced earlier came later, part of a new round of work. They are not one complete loop from spec to implementation of the same work.
This practice makes me feel the real time-saver is when the ticket already has a goal and scope, plus verification conventions, and the file can be found. Without those, "execute that ticket" is still just making the assistant guess from scratch.
4.2 Verification results must speak human
RxStudy's logging regression entry uses real logs, network, and compression dependencies, and runs isolated tests in a temporary copy without starting a simulator.
Project docs record these checks, and also record limits on full-project and device verification. This post only did a read-only inspection; it did not rerun them. The previously mentioned default-mode inconsistency will also trigger the script's default-value assertion, so you can't see the entry and claim the current run passes.
Source type checks and isolated regression each prove different things; CocoaPods/Tuist project builds and device system log observation each have their own verification scope. The two build modes must also be checked separately; passing one can't sign off for the other.
When tools or dependencies are missing, I'd like results written as "which passed, which were blocked, which not yet verified." The skill requires complete tests, but that doesn't mean you can declare completion when the environment fails.
5. code-review: after writing, check whether it was done right
implement explicitly requires calling code-review after completion; the latter can also review a branch or PR independently. It splits into two directions:
| Direction | What it checks |
|---|---|
| Standards | Whether coding standards in project docs are followed, and whether code smells listed in that version are present |
| Spec | Whether spec requirements are missing, whether implementation is correct, whether anything beyond scope was done |
This version runs two parallel sub-agents that review separately, then aggregates the results side by side; it doesn't merge the two directions into one score. Code smells are judgment hints; explicit project standards take precedence. code-review source
Log code could be beautifully named and pass type checks yet export the old directory together; or all features implemented but violate project standards. Those two kinds of problems can't cancel each other out.
In teaching review, I would follow the acceptance criteria: did the error feedback get missed? Is the ZIP deleted too early? Did someone add upload without permission? Review must be able to compare against task and spec; it can't just leave a "overall looks fine."
5.1 One scope issue that can't be skipped
This version requires a fixed comparison base and reads the diff up to HEAD:
git diff <fixed-point>...HEAD
It confirms the reference exists, the diff is non-empty, then finds spec and standards sources. It therefore must not be interpreted as "automatically review all uncommitted changes in the current directory." And implement says review then commit, but the two documents don't detail how to arrange commit state in between.
My suggestion is to explicitly set the review base and spec sources in actual use, then confirm which changes are actually in the comparison scope. If you find something missed in review, first solve the scope problem; don't count an empty diff as pass, and don't invent a hidden mechanism for the source code.
After the review finds issues, checking, fixing, and rerunning affected verification is also my usage suggestion. Calling review once doesn't mean the problem is fixed. The final commit only records the changes; pushing, opening a PR, or merging require separate processes.
6. When to continue the chat, when to open a new conversation?
ask-matt explicitly recommends: keep discussion, spec, and ticket splitting in continuous context; don't clear or compress arbitrarily before splitting tickets; after splitting independent tasks, each implement can start from a new context. Process and context explanation
The first half is still organizing decisions and their reasons; clearing too early tends to leave "what to do" while losing "why it can't be done that way." In the second half, tasks already carry delivery requirements, so exploration details from old tasks are easier to set down.
A new session can start like this:
/implement <task reference>
Continue with this ticket; first read the associated spec and comments, confirm all blocker tasks are done.
Goal, what can't change, and test conventions are all in there; if there's a conflict, point it out.
Don't guess what we were trying to do based on the current code.
That information must actually exist and be accessible. Only a parent task title or a dead link doesn't qualify as an independent task.
When space runs low you don't have to hold on. Phase boundary explanation allows compressing or handover at appropriate phases. I first check retained goals and confirmed decisions, then check rejected options and unresolved questions, confirm evidence locations, and continue. The second post said if discussion goes poorly you can start over; here I add: confirmed basis must either be carried along or explicitly re-discussed; don't silently drop it.
7. Small tasks don't need full paperwork every time
By this point, someone may start to get a headache: to change one line, also spec, tickets, meetings?
No. ask-matt's branch is clear: multi-session work goes through spec and tickets; single-session work can go directly into implement in the current context.
For example, if the existing log has a typo and the goal and impact are clear, there's no need to create a long spec. "Help me optimize logs" is also six characters, but its meaning isn't fixed, so that still needs discussion first.
Whether something is small depends on actual behavior and risk, not prompt length. When an anomaly already exists, first establish a verifiable problem and diagnostic basis; you can't substitute walking through skill names for finding the cause.
What I like about this workflow is being able to check at any time: what's promised, what this ticket completes, how results are proven, and whether the review matches the requirements.
Getting to this point in the flow moves "we've discussed clearly" forward a bit toward "this thing is actually done well." As for how many pages of documentation or how many skills were called, I think that's secondary.
Appendix
While I was writing and organizing, Skills For Real Engineers' skill also updated; the main flow added /retro as a skill for reviewing issues like repeated file searches and missed checks in this session, as a closing loop of the main flow. I haven't used it yet, so I dare not explain much.
References
This post verified the official repository on October 19, 2026, with main pointing to the following fixed commit; later updates may change behavior. The skill source uses the repository's MIT license; this post only does short excerpts and explanations.