DEV Community

Cover image for Building with mini, Part 4/9: The main loop — next plan do done
Stanislav Kremeň
Stanislav Kremeň

Posted on

Building with mini, Part 4/9: The main loop — next plan do done

pycalc has a vision (Part 2) and a backlog (Part 3) — and still not a single line of Python. Today we fix that. Four commands form the heart of mini: propose, break down, build, close. One turn of the loop = one phase = one commit.

next — propose

mini next
Enter fullscreen mode Exit fullscreen mode

next is the entry point of the loop, and the only step that begins with a question: do you have your own idea, or do you leave it to the agent? I left it to the agent — and it's worth showing what it did with last episode's backlog:

The backlog has two items so far ([1] ** and %, [2] reading from stdin), but both are explicitly out of scope for this version (non-goals), so neither fits the first phase.

This is exactly the interplay that project.md and todo.md exist for: the backlog offered itself, the non-goals stopped it. Instead, three candidates came up — tokenizer with positions, a CLI skeleton, a walking skeleton — each with its trade-off:

I propose candidate 1: Tokenizer with positions. Why not the walking skeleton: it sounds tempting, but for a project this small the rewriting overhead outweighs the benefit — the pipeline has only three pieces, and in three phases it will be end-to-end anyway. The trade-off of candidate 1: until there's a CLI, you can't poke at anything by hand; verification is purely through tests.

After approval the agent saves the phase itself, via mini next --apply --title … --goal …. State is never changed by the model's hand:

Phases:
  [proposed] > 1. Tokenizer with positions

  Next: mini plan (break it down) or mini do (run directly)
Enter fullscreen mode Exit fullscreen mode

plan — break down

mini plan
Enter fullscreen mode Exit fullscreen mode

plan breaks the phase into steps — a short title plus a one-sentence detail (that pairing is deliberate: only the titles go into later steps' prompts, the detail is fetched when needed). For the tokenizer it produced five steps, from the Token NamedTuple through the number scanner into Decimal to the unittest table. And here too a decision surfaced that would otherwise only emerge in the code:

.5 and 2. are deliberately rejected (simplest rule with exact error positions; trade-off: .5 is valid in many calculators — say so if you want it supported and I'll adjust step 3).

Approved, saved (mini plan --apply reads the steps from stdin):

[planned]  > 1. Tokenizer with positions
  Steps:
    [todo]     Token type and CalcError in pycalc.py
    [todo]     tokenize() loop: operators, parens, whitespace
    [todo]     Number scanning into Decimal
    [todo]     Invalid character raises CalcError with position
    [todo]     unittest case table in test_pycalc.py
Enter fullscreen mode Exit fullscreen mode

do — build

mini do
Enter fullscreen mode Exit fullscreen mode

Only now does code get written. The session gets the phase, the steps and a reference to the project — not the whole conversation history. The agent ticks steps off as it goes (mini do --apply --step-done "<title>"), so mini status shows live progress, and at the end it writes a run report to .mini/run/phase-001.md: the verdict, the step statuses, and notes beyond the plan. Those notes are the most valuable thing in the report:

  • I deliberately rejected str.isdigit() — it accepts Unicode digits like ٣, which Decimal() would happily parse too. A helper restricts numbers to ASCII 0-9; there's a test that 1+٣ raises an error at position 2.
  • I verified the "no input ever produces an unhandled exception" claim with a fuzz over 20,000 strings — only CalcError was ever raised.
  • One of my own test expectations was off by one (10.25+0.75 positions); the tokenizer was correct, the test data was fixed.

All five steps done, 27 test cases green. And note what didn't happen: no REPL, no sin, no colors. The non-goals hold.

done — close

done is the loop's human gate — the one place mini insists on a person. State, incidentally, now reminds you itself that there's something to close:

[doing]    > 1. Tokenizer with positions
            ⚠ stuck: phase "doing", but it has no open steps — close it via mini done
Enter fullscreen mode Exit fullscreen mode
mini done
Enter fullscreen mode Exit fullscreen mode

The agent summarizes the phase from the run report, proposes an entry for CHANGELOG.md, and asks the one thing that matters: does it work for you? Verification is my job, not its:

python3 -m unittest -v
...
Ran 3 tests in 0.001s
OK
Enter fullscreen mode Exit fullscreen mode

After confirmation it writes the CHANGELOG (Keep a Changelog format, Unreleased section) and calls mini done --apply. That does three things at once: moves the state, writes the phase memory to .mini/memory/phase-001.md (goal, steps, run report — what the next phase will find useful), and wraps the entire phase's work into a single commit:

git log --oneline
a01eb36 Phase 1: Tokenizer with positions
9cf1df1 init: mini project setup
Enter fullscreen mode Exit fullscreen mode

The version isn't bumped by default and nothing is pushed — both are opt-in (--bump patch --push), no surprises on the remote.

Phases:
  [done]       1. Tokenizer with positions (took 2m 6s)

  Next: mini next (proposes the first phase)
Enter fullscreen mode Exit fullscreen mode

Two minutes six seconds from do to commit. And the backlog? Still 2 open items — the loop never touched it, because it never came up.

Why a loop, not one big prompt

This is where the whole architecture from Part 0 pays off. Every step of the loop starts from saved state, not from a conversation: plan doesn't need to know what was said in next, it reads the phase from .mini/. Between phases /clear is recommended — the context is thrown away, because everything that matters survives in files: state in state.json, experience in the phase memory, history in git and the CHANGELOG. The conversation is disposable; the state is permanent.

A trade-off, to put it fairly on the table: one phase = one commit means a half-done phase isn't in git. If a do session crashes mid-way, the code on disk stays (so do the report and the ticked steps), but the commit doesn't — which is why keeping phases small helps, exactly as next enforces (1-3 days, one verifiable goal).

And about those three human stops (approving the proposal, approving the plan, verifying in done): for one phase a day they're priceless, for five phases in an evening they're a chore. That's precisely why mini auto exists — but more on that in a later episode.

Next time

The loop can build. Next we'll look at the two human checkpoints around it — discuss, when a phase needs talking through first, and verify, when "the tests passed" isn't enough and it calls for a human UI/UX review.


mini is open source: npm install -g mini-orchestrator, then mini install-commands in your project. Source and docs on GitHub.

Top comments (0)