- 1. Introduction
- 2. There is no state file. The checkboxes are the state
- 3. A feature file, as it actually is
- 4. /hora determines its current position every time
- 5. Separate the moment of writing from the moment of committing
- 6. Who can write
- 7. Questions flow in one direction
- 8. How to read .hora/ in maintenance
- 9. Summary
A re-entrant orchestrator with no state file
The sixth article in the "Hora Kit design" series. Hora Kit has no state file like state.json. The state is the .hora/ directory itself, and Markdown checkboxes are the state. We write about the design of a re-entrant orchestrator that determines its current position on every run, and why that pays off in maintenance.
1. Introduction
This article is the sixth in a series explaining the design of Hora Kit, the AI development framework that we (Open Reach Tech) released as open source. The main article is here.
We open-sourced Hora Kit, an AI development framework for truly automated development
A note on terminology. The only command you type in Hora Kit is
/hora. Depending on the situation,/horacalls/hora-spec(writes the spec),/hora-setup(creates the implementation repositories),/hora-plan(fixes the version and writes the feature list and contracts),/hora-build(takes one feature through the 18 checkpoints), and/hora-accept(performs acceptance) in order. Only/hora-hotfixis not called by/hora; a person runs it directly. The docs call/hora-spec"the deciding side" and the rest "the building side", and this article uses those names as well.
In the main article we wrote that "what we value most in maintenance is that records keep accumulating in .hora/". In this article we write about what is inside .hora/ and why it differs from an ordinary "state file".
If you have ever built an orchestrator yourself, you have probably struggled with where and how to keep the state. Hora Kit's answer is a little unusual, so try reading this while thinking about what you would do.
2. There is no state file. The checkboxes are the state
Hora Kit has nothing like state.json. The state is the .hora/ directory itself, and the Markdown checkboxes inside it are the state.
.hora/
tree/<repository>.md What /hora-setup actually read on site, and the tag at the time it read it
digests/<skill-name>.md The conventions of one equipped skill, shortened. With originating package and version
spec/<version>/_stages.md /hora-spec's own record of how far it has progressed
spec/<version>/_assets.md What stage 0 read, from where, and as of which commit
spec/<version>/_divergence.md Discrepancies between documents and code. One line per item
tasks/<version>/
_plan.md The implementation order of features, and the acceptance tasks
<feature-id>.md One feature and its 18 checkpoints
contracts/<version>/ One file per server whose consumers are elsewhere
questions/<version>/open.md Append-only. Answered by editing specs/
acceptance/<version>/
<feature-id>.md Every acceptance run for a feature. 1 run = 1 appended block
_sweep.md The sweep of the whole version
glossary.md Append-only. Not split by version
hotfix/<hotfix-id>.md Record of what a hotfix skipped
The history of what ran can be followed with git log .hora/. The docs say "there is nowhere else that records it, and there does not need to be". The only exception is the equip-*.json files that each package's installer writes; these are the installer's state, not the project's, so they are gitignored.
3. A feature file, as it actually is
One file per feature, and the contents are checkboxes like this.
# #attendance Attendance recording and listing
<!-- spec: attendance @ sha256:abc123... -->
<!-- repositories: backend, frontend-employee -->
## Spec gate
- [x] 1. Draft or confirm the specification
- [x] 2. Verify the use cases can be met
## Backend gate
- [x] 3. DB and API schemas
- [x] 4. Stub API
- [ ] 5. The modules the implementation needs
...
- [x] 7. Worker <!-- n/a: this feature triggers no background job -->
The comment on the first line holds the hash of the relevant section of the spec. When the spec changes, the hash changes, and the next /hora-plan detects it.
The reason for this format is that humans can read it, diffs can be read, and git can manage it. A JSON state file is convenient for machines, but it is not suited to tracing "when did checkpoint 5 pass" with git log -p. With Markdown checkboxes, a person can read the history as it is.
4. /hora determines its current position every time
There is no assumption that a project finishes in one session. /hora is started and resumed as many times as needed, and every time it determines for itself where it is.

Every time, the first things it does are in this order.
Are the implementation skills in .claude/skills/ If not → stop and ask for equipment
0. git fetch origin --prune everywhere. Check whether a hotfix has entered main
1. Is there a spec for that version If not → /hora-spec
2. Are all the declared repositories present If not → /hora-setup
3. Always run /hora-plan
4. Are there unresolved blocking questions If so → stop and say what to fix
5. Are there unfinished features in _plan.md If so → /hora-build on the first one that is ready
6. All features done, and the sweep has not passed → /hora-accept (the whole version)
7. The sweep has passed → merge into main
Before starting work, it reports its judgment in one line. For example, something like "continuing 1.0.0. 4 of 11 features done, building #payroll from checkpoint 6."
Step 3 runs every time, even if the feature list already exists. The spec keeps moving during implementation, with sections added, changed, and withdrawn, and there is no route by which that reaches the plan other than reconciling every time.
5. Separate the moment of writing from the moment of committing
This is a small but important part of the design.
| When | Why | |
|---|---|---|
Writing a checkpoint's [x] | The moment it passes | An interrupted run must resume from the checkpoint where it stopped |
Committing .hora/ | Once per gate (after 2 / 9 / 17 / 18) | 18 commits per feature is a history no one reads |
Combine them and one of these two properties is lost. Committing on every pass bloats the history 18-fold so no one reads it, and not writing until the gate means an interruption midway costs 5 checkpoints of rework. The cost of keeping them separate is zero, so we keep them separate.
6. Who can write
| Directory | Written by |
|---|---|
specs/ | Humans, and the 2 skills that write on their behalf |
.hora/ | The skill that records its own work, hora-digester, and each package's installer |
| Implementation repositories | /hora-setup, hora-implementer, the main session |
Humans do not write to .hora/. They only read it. Conversely, the kit does not write to specs/. /hora-spec and /hora-plan write only the wording a person has just read and approved.
7. Questions flow in one direction
questions/<version>/open.md is an append-only file.
When /hora-build cannot satisfy a checkpoint's exit condition, it does not decide on its own; it writes a question here and stops. When a person answers, they do not write a reply in this file; they edit specs/.
.hora/questions/open.md ← Written by the skill. "This part of the spec is undecided"
│
▼ A person reads it
specs/<version>/spec.md ← Edited by the person. The answer becomes the wording of the spec
│
▼ The next /hora-plan reconciles
.hora/tasks/ ← The plan is updated
The idea is that every question is a hole in the spec, so the answer should be written in the spec. An answer given in conversation, "yes, do it this way", is gone in the next session. An answer written in the spec does not disappear.
8. How to read .hora/ in maintenance
When tracing "why was this decision made" for a feature built a while ago, the places to look are fixed. .hora/questions/<version>/open.md holds what was asked about that feature. .hora/acceptance/<version>/<feature-id>.md holds what acceptance pointed out and which checkpoint it was sent back to. When a checkpoint passed can be seen with git log .hora/tasks/<version>/<feature-id>.md.
There is no need to dig through conversation logs. The records are structured, and they are in git. This is the part we value most in maintenance.
9. Summary
In designing a re-entrant orchestrator, there are three things we consider important: keep the state in a form humans can read (Markdown with checkboxes was enough), separate "the moment of writing the state" from "the moment of committing the state", and write the answers to questions in the spec rather than in conversation.
We hope this is useful to anyone trying to build a similar mechanism.
The original source is here. https://github.com/openreachtech/hora-core/blob/main/docs/architecture.ja.md