A repository with no implementation code directs the whole project
The third article in the "Hora Kit design" series. When you start a project with Hora Kit, you get a repository that directs the whole project even though it holds not a single line of application code. We write about this nested git repository layout, which we call the orchestration-repo layout, and about its git model.
1. Introduction
This article is the third 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.
When you start a project with Hora Kit, a repository named <myproject>-app is created. People who look inside for the first time are usually puzzled: this repository contains not a single line of application code. And yet, this repository directs the whole project.
We call this way of managing a project the orchestration-repo layout (we call it the オケレポ layout, short for orchestration repository). It means a layout that separates the repository used for orchestration (the orchestration repo) from the repositories that hold the project's actual code. In this article we write about that layout and why we chose it. git comes up as well, so if you are someone who struggles with branch workflows day to day, try reading this while comparing it with your own team.
2. Nested git repositories

The outer repository (<myproject>-app) holds the spec and the /hora skill. /hora clones the backend / frontend repositories from renchan-boilerplate and furo-boilerplate-nuxt inside it, reads the spec, and implements.
myproject-app/
specs/<version>/spec.md What to build. Written by humans and the 2 skills that write on their behalf
.hora/ Record of what ran. The state itself, and its history
docs/stack/ Stack handbook
kit/skills/hora-setup/ The only skill this repository writes itself
.claude/ Generated by npm install. Not tracked by git
backend/ ← gitignored. An independent git repository
frontend-employee/ ← gitignored. An independent git repository
The inner repositories are gitignored, so cloning the outer repository does not bring the implementation code with it.
Incidentally, the nesting is not for git's sake but for Claude Code's. A session cannot write outside its working directory, so any repository it needs to touch has to be inside.
3. Who may write what
The essence of this layout is not the shape of the directories but "who can write".
| Directory | Who writes | Everyone else |
|---|---|---|
specs/ | Humans, and the 2 skills that write on their behalf: /hora-spec (one approved section at a time), /hora-plan (one approved edit at a time) | Read only |
.hora/ | The skill that records its own work, and hora-digester, which writes the digests. Each package's installer writes only its own record | Humans only read |
| Implementation repositories | /hora-setup, which creates them and fills in values; hora-implementer, which writes code and tests; the main session, which handles all git operations | — |
The reason we drew this boundary with directories is that a boundary protected by structure is harder to break than one protected by rules. Even if you instruct "the AI must not rewrite the spec", an agent will break that in some situations. It was more reliable to have the spec in a separate repository with only two write paths.
The docs explain the meaning of this boundary like this: "what is protected is not 'the act of writing' but the fact that a requirement never enters specs/ without a human actually reading its wording."
4. .claude/ is a generated artifact. The ignore list is an allowlist
When you run npm install, hora:init runs and places @openreachtech/hora and the 4 skill packages under .claude/.
"hora:init": "hora-core install && hora-skills-ort-core install && hora-skills-ort-renchan install && hora-skills-ort-furo install && hora-skills-ort-support install && node kit/scripts/equip-own-skills.mjs",
"postinstall": "npm run hora:init"
What lands here is not the project's source but each package's build output. That is why .gitignore ignores the two payload directories wholesale.
#### implementation repositories, fetched or adopted by /hora-setup
/*-backend*/
/*-frontend*/
#### the kit equipped by postinstall (regenerated, not authored here)
/.claude/agents/*
/.claude/skills/*
# The five equip manifests. These are the only entries under .hora/ that are ignored
/.hora/equip-core.json
/.hora/hora-skills-ort-core.json
/.hora/hora-skills-ort-furo.json
/.hora/hora-skills-ort-renchan.json
/.hora/hora-skills-ort-support.json
The /hora-setup skill that the repository writes itself is also placed under kit/skills/, and a hook copies it into .claude/ like everything else. So everything under .claude/ is a generated artifact no matter who wrote it, and there is nothing to un-ignore by name.
The direction "ignore wholesale" is deliberate. A form that "lists and rejects the names each package distributes" is also conceivable, but the names each package distributes change with every release, so a deny list written against today's names goes stale silently. And a deny list that no longer matches says nothing about no longer matching. The placed items simply, quietly, start getting committed. An allowlist does not break that way.
5. Never link into the implementation repositories
Of the cautions written in the docs' quick start, this is the easiest one to trip over.
If you put a relative link from the spec to a file in an implementation repository, it opens fine on your own disk. But because the implementation repositories are gitignored, that link is broken in everyone else's clone. Copy what you need into specs/<version>/.
6. The git model
All git operations happen in the main session: either /hora itself or a skill it ran. Agents never touch git.

Branches with the same name are cut in both the outer and the inner repositories.
| Cut when | Merged when | |
|---|---|---|
feature/<id> on the backend line | Entering checkpoint 3 | Passing checkpoint 9 |
feature/<id> on the frontend line | Entering checkpoint 10 | Passing checkpoint 17 |
Merging happens at the gate boundary, not after acceptance (checkpoint 18). There is a reason for this: the acceptance suite covers every feature present at that point and can fail on any of them, so waiting for acceptance would leave this feature's branch open across the work on other features. What acceptance finds comes back on a retake/ branch. "Merged, but later found to be lacking" already has a name, and that is exactly it.
Branch names are deliberately descriptive.
| Kind of change | Branch name |
|---|---|
| Implementing one feature | feature/<feature-id> |
| A new dependency | install/<package>-<version> |
| Updating a dependency | update/<package>-to-<version> |
| Extending the E2E environment (checkpoint 17) | update/e2e-<what>-for-<feature-id> |
| Redoing something already implemented | retake/<member>-of-<class>-for-<why> |
Dependencies get their own branch because there are files, such as package-lock.json, that two changes cannot edit cleanly at the same time. We simply use the way human teams avoid conflicts: "one at a time, merge before starting the next."
7. The only skill this repository writes itself
/hora-setup alone is distributed by the boilerplate, not by a package.
What this skill does is about the stack from start to finish. Which repositories are needed, what satisfies them, what to read once they have arrived. A package that does not know the stack cannot hold those answers.
The answers themselves, however, are not inside the skill either. They live in docs/stack/, and the skill reads them at run time. When the stack changes, that directory changes, and the skill needs no editing.
8. Summary
We list three things we consider to be advantages of this layout.
First, the spec and the records have a lifetime separate from the implementation. Even if you recreate the implementation repositories, specs/ and .hora/ remain. Conversely, you do not need to hand the implementation code to someone who can read the spec.
Second, git log .hora/ becomes the project's history itself. What ran, what stopped, and what remained as a question can be read without being mixed into the implementation commits.
Third, the outer repository alone tells you "what this product can do right now". specs/ holds what we decided to build, and .hora/acceptance/ holds what has passed acceptance.
Nested git repositories probably look strange at first, but once you see that the split is by "who can write", it starts to look natural. We hope this is useful to anyone considering a similar layout.
The original sources are here. https://github.com/openreachtech/hora-boilerplate/blob/main/docs/architecture.ja.mdhttps://github.com/openreachtech/hora-core/blob/main/kit/skills/hora/references/commits.md