Skill v1.0.0
currentAutomated scan100/100version: "1.0.0" name: hora-build description: Build one feature by taking it through the eighteen checkpoints — spec, backend, frontend, acceptance — in order, one gate at a time. Runs at the root of the hora repository, one feature per invocation. Invoked by /hora, or directly as /hora-build.
hora-build
Take one feature through the eighteen checkpoints, in order.
Read ../hora/references/structure.md (the layout, the invariants, where and how a command runs) and references/checkpoints.md (the checkpoint list itself) before starting. This skill is read-only on `specs/`; checkpoint 1 is where a problem found there is routed to the two skills that may write it (references/checkpoints.md, checkpoint 1).
One feature at a time, never two
Nothing here ever runs alongside another feature's checkpoints. One feature goes from checkpoint 1 to checkpoint 18, and only then does the next one start:
| a feature reaches acceptance while its author still remembers it | rather than at the end of the version, alongside twenty others | |
| a break shows up in the run that caused it | checkpoint 18's unit suites cover every feature so far, so a regression fails immediately | |
| one branch per repository is open at a time | no shared, uncommitted state between two features to untangle |
Where to start
If a backend row is declared, and an equipped skill covers exclusive row-id allocation, clear that allocator's lock unconditionally before touching any checkpoint (its own file describes clearing a stale lock). Nothing holds that lock across invocations, so one still standing at the start of a run is leftover.
Then allocate this feature's id prefix once through that skill, before its first implementing checkpoint, and hand that same prefix to every agent that works in the backend row. The allocator returns the same prefix for the same requester however often it is asked, so allocating here costs one call and leaves the lock free — where several units run at once, asking for themselves would queue them behind each other's lock.
1. Read .hora/tasks/<version>/_plan.md2. Take the first feature "Whether a feature is ready to build", laterin this file, says yes to3. Open .hora/tasks/<version>/<feature-id>.md4. Take the first checkpoint that is [ ]5. Run it. Then the next one. Stop when the file has no [ ] left
Report the decision in one line before starting work, as in "building #attendance, from checkpoint 6 of 18". Inside a frontend gate the line names its row: "building #attendance, from checkpoint 11 (frontend-admin) of 18".
In a file that carries several frontend gates, step 4 takes the first `[ ]` in file order, so the rows run in target order and 18 runs once, after the last (references/checkpoints.md, "A feature that names several frontend rows carries one frontend gate per row").
A feature whose only open checkpoint is 18 writes no code, and cuts no branch. That is what a section declaring <!-- built: frontend --> looks like on adoption: everything up to the acceptance gate is marked not-applicable, so this skill goes straight to /hora-accept.
Step 2 skips an entry whose checkpoint 18 is covered by the sweep entry. Where every specified feature carries built:, /hora-plan collapses the per-feature gates into a single adoption sweep (../hora-plan/SKILL.md, "collapses to one sweep"). Taking one anyway would run the twenty individual gates the collapse exists to replace.
A listed feature is never entered, and it has nothing to resume from. A section carrying <!-- baseline: inventoried --> sits under ## Not accepted with no checkbox, so step 2 skips it with no special case (../hora-plan/SKILL.md, "_plan.md — the order"). Its eighteen empty boxes are not a feature nobody started — the feature file's provenance header says which of the two this is, and opening one rebuilds code that is already serving users.
If no feature is ready and some are unfinished, and no open question holds any of them back, that is a dependency cycle or a reference to an `id` that does not exist. Raise it as a contradiction question (blocking: yes) and stop.
A listed feature is not one of the unfinished ones. It carries no checkbox, so a version with nothing left but listed entries is finished rather than deadlocked (../hora/SKILL.md, "Deciding where you are").
A `depends` naming a listed feature is satisfied by the running code, never by a checkbox. Read it as satisfied, and read the dependent's own Rests on: #<id> (not accepted) line for what its pass rests on.
[wing] Whether a feature is ready to build
Yes, where its entry in `_plan.md` is `[ ]` and every one of its `depends` is satisfied: [x] in this version, finished in an earlier one (look back through past versions in .hora/tasks/ for a revived feature's dependencies), or listed. /hora takes its next feature from this section through step 2 above. /hora-fast takes one from here too, and opens a gate earlier where the section below says yes.
[wing] Whether a gate may open while a dependency is unfinished
Yes, where every unfinished dependency has reached what this step waits on. Only /hora-fast reads this section. /hora builds one feature to acceptance before the next, so a feature it picks has no unfinished dependency.
| This feature's | Opens once each unfinished dependency has | |
|---|---|---|
| spec gate (1, 2) and checkpoints 3 to 8 | nothing, once /hora-fast's foundation is fixed | |
| checkpoint 9 | merged its backend gate into release/<version> | |
| frontend gate (10 to 17), in each row | merged its frontend gate in the same row, where it has one | |
| acceptance gate (18) | passed its own 18, which finishes it |
What the dependent's backend uses from its dependency is in the foundation before either opens. The contracts, the shared tables, the master seed and every module /hora-plan marked Imports: are built there, and /hora-plan walked every use case through the contracts field by field. Checkpoint 9 walks the use cases as actual calls, so it is the first step that needs the dependency's running code.
A dependent's tests reach its dependency through the foundation, never through the dependency's operations. A row they need in a dependency's table is seeded in the dependent's own band (references/checkpoints.md, checkpoint 5), and the call itself is walked at checkpoint 9.
A dependency's later gate that changes code a dependent built on reopens the dependent. Clear the dependent's first checkpoint that reads the changed code, in every row whose code reads it, with reopened-by: dependency.
Running one checkpoint
0. Look back for the line of the last checkpoint marked [x]. Where it is noton screen, write it now, before anything else (step 10)1. Read the checkpoint's entry in references/checkpoints.md — its exitcondition, the work it delegates, and when it does not apply2. Decide whether it applies. If it does not, write the reason and mark it[x] with the n/a comment. Move on3. Match that work against the equipped skills, take a digest of each, andrecord both (below). This is the main session's step, never an agent's4. Cut this repository's feature/<feature-id> branch, if this is the firstcheckpoint in this gate (see ../hora/references/commits.md)5. Run it:an interactive checkpoint (1, 2, 9, 11, 17, 18)-> the main session runs it. Never an agentan implementing checkpoint (3-7, 10, 12-16)-> hora-implementer, one agent per unit of this checkpoint's work,started together (below), each given that checkpoint's exitcondition, the skill names and digest paths from step 3, and thisfeature's row-id prefix. One agent takes several consecutivecheckpoints only where "Whether consecutive checkpointsmay go to one implementer" says yesan auditing checkpoint (8)-> hora-verifier, read-only, given the skill names to invoke in fullAND the change set to audit ("The change set of a checkpoint",below). The audit skills run over that set, never the wholerepositorywhile an agent runs, the next checkpoint is not entered, not even toprepare it (references/checkpoints.md, "The order is a rule")6. Gather the units: regenerate every aggregation file their registrationsname, then handle whatever else they reported that is not code (below) —a dependency, a conflict-proof change, a new identifier, a contract onewanted to change7. Lint, as "How to lint a checkpoint's files", below, saysstill fails -> fix it, retry (up to five attempts; see "A lint rule contradiction")8. Test, where the checkpoint's exit condition names tests (6, 16, 18), as"How to test a checkpoint's files", below, saysfails, from something code could fix -> fix it, retryfails, from something no code change could fix (the middleware is notrunning, a network call reached nothing, the database was alteredoutside this run) -> stop retrying immediately, without spending theretry limit. Report it as a `lacked-environment` question and stop thefeature theredies, leaving no result at all (the process was killed, the machine ranout of memory) -> not a code failure either. Report it as a`lacked-environment` question that names the configuration the rundied under (below), and stop the feature there9. Verify the exit condition actually holds — with hora-verifier for anythinga reading of the code can settle, handed the marked change set (below);in conversation for the four gates that check against use cases. At 6and 16, where step 8's suite is itself the proof, the verifier is usuallyskipped (below). At 15, the main session also opens every screen in areal browser, since no reading of the code settles a layout(references/checkpoints.md, checkpoint 15). Met or not, add this run tothe line's run record (below)sent back -> write the send-back line, then re-enter where the verdictsays:⏳️ [<n>] <feature> checkpoint <k> sent back to checkpoint <j> | <the findings, or the shortfall> | -> <what comes next>10. Write [x] into the feature file, and in the same reply the pass line:✅️ checkpoint <k> passed | <the one fact it established> | -> <what comes next>🎯 <feature> accepted | checkpoint 18 passed: <the fact> | -> <the next feature> (at 18)Commit at the gate boundary, not here11. Move to the next checkpoint
The line is written where the box is, so neither goes without the other. The shapes above are all a pass or a send-back needs; /hora-progress holds the rules behind them — what n counts, what may be said between two lines — and is read wherever one of those is in doubt. Step 0 is what catches a line that was missed: a box with no line behind it is written up before the next checkpoint starts, never left for the closing report.
Step 10's split matters. The checkbox is written the moment the checkpoint passes, so an interrupted run resumes at the right place; the commit happens once per gate, so git log .hora/ stays readable (../hora/references/commits.md, "Committing .hora/").
[wing] How to lint a checkpoint's files
cd into this checkpoint's repository, then `npx eslint --fix` on exactly the files it touched, every unit's together, then `npx eslint` on the same files for what remains. --fix clears the mechanical violations without an agent round trip; only what it cannot fix is worth one.
[wing] How to test a checkpoint's files
From that same repository, `npx jest` on exactly the files this checkpoint wrote, with the output written to a file and read from there (below).
Step 3 — matching a checkpoint to the skills that cover it
`references/checkpoints.md` names no package skill, and it never may (../hora/references/structure.md, "No hora file ever names one of those skills"). So the match is made here, once per checkpoint, against what is actually equipped:
1. Take the work the checkpoint's "Delegate to" row states2. Read the descriptions of the skills equipped under .claude/skills/3. Pick every one whose description covers that work, on the surface thischeckpoint's repository requires (hor- backend, hof- frontend, hoc- and hos- either)4. Take a digest of each one (below)5. Write the names, and the package and version those digests were derivedfrom, into the feature file against this checkpoint6. Hand the names and the digest paths to the agent, in its assignment
-[x] 15. UI <!-- skills: <every name you matched, comma-separated>; digests: <the package and version each came from> -->
The example above carries no real name on purpose. This is a hora file, so the rule it states applies to it too.
Match on what a description says, never on what a name sounds like.
Where a checkpoint's row says "every skill covering X" (12 and 15), read the descriptions exhaustively. The package ships a family there — one skill per existing component, one per CSS convention — and a partial match builds a screen against four of eleven conventions with nothing saying so.
Record it even when nothing matched. An empty list is the evidence that the gate ran without its conventions; no list at all is indistinguishable from a checkpoint nobody thought about. Report the gap by name in the closing report too.
Never let an agent do this matching. An agent would pick differently on a rerun, and nothing downstream could say which set the first run used.
A checkpoint line's run record
A checkpoint line carries a second comment at its end, holding what running the checkpoint cost. The first comment stays exactly as it is: /hora-plan reads <!-- n/a: … --> by its text.
-[x] 6. Actual API <!-- skills: …; digests: … --> <!-- cleared: 1; reopened-by: 9; agents: 4; agent-time: 840s; verify-time: 240s; wall-time: 1310s -->
| Field | Meaning | |
|---|---|---|
cleared: | how many times this line went back to [ ]. Omitted while it is 0 | |
reopened-by: | who cleared it, in order | |
agents: | how many hora-implementer and hora-verifier agents this line started, over every run | |
agent-time: | seconds of wall clock from starting them to the last one returning, over every run | |
verify-time: | the hora-verifier share of agent-time: | |
wall-time: | seconds of wall clock from entering the checkpoint to writing its box, or to stopping short, over every run. On 1, 2, 9 and 11 it is mostly a person answering | |
agent-tokens:, verify-tokens: | the same split, only where the Agent tool reports tokens | |
at: | each implementation repository's short HEAD when step 9 judged, as backend=3f2a1c9, with + where the working tree held uncommitted changes. The newest run only, never summed |
Add this run to it whether the checkpoint passed or not — the moment step 9 has judged, or the moment the feature stops short of it. A checkpoint settled in conversation carries only cleared:, reopened-by: and wall-time:. `at:` tells a reader which verdict is stale: a judged file that changed since it no longer stands behind the box. The record survives whoever rewrites the line, and a clear adds to it (../hora/references/structure.md, "What lives in .hora/").
Step 3 — the digest each matched skill is read through
A matched skill can run to thousands of lines, and it stays resident in the agent's context for every turn. A checkpoint's cost is close to that resident size multiplied by its turn count. .hora/digests/<skill-name>.md is the short form.
1. For each matched skill, find the record under .hora/ that names it. There isone record per equipped skills package, the file is named after the package,and its version: is the version of that package now installed2. For each skill matched above, use .hora/digests/<skill-name>.md while itsheader names that package and that version3. For the rest, start hora-digester — one agent per skill, all in onemessage — and use the files they write4. Hand those paths to the agent, alongside the skill names
Nothing matched, nothing digested. When the match above came back empty, this step is skipped whole — there is no version to read and no digest to take — and the checkpoint runs on what it stated itself.
The package and version in the header are what keep a digest honest. A digest holds only while it names what it came from, so updating one package leaves that package's digests to be rewritten before they are read again — and leaves the others alone.
A digest names the skill it came from, and the agent reads that skill whenever a question stays open (../../agents/hora-digester.md), so a convention a digest states too thinly costs one read.
Record the version alongside the names, so the checkpoint stays re-derivable after the next package update.
A verifier at step 9 is handed the same digests its implementer had. Judging a checkpoint against a fuller text than the implementer was given fails work for a convention nobody handed it.
Checkpoint 8's audit skills are invoked in full, and a digest has no part in it. A step whose skill is the criteria runs that skill whole (../hora/references/structure.md, "How the match is made").
Step 5 — splitting a checkpoint into units
Five checkpoints divide into units whose files are exclusive, and each unit gets its own implementer, all started together in one message. One agent writing six resolvers carries a context that grows across all six; six agents each carry one.
| Checkpoint | One unit is | |
|---|---|---|
| 3 | one table, and one operation's API surface | |
| 5 | one module | |
| 6 | one operation | |
| 12 | one component | |
| 15 | one screen |
Exclusive files are what make this safe, so a file two units would both write belongs to one of them. Give it to the unit that owns it, or run the checkpoint whole.
Everything shared stays with the main session:
| an aggregation file | regenerated once at step 6, from the folder scan and the units' registrations | |
| this feature's row-id prefix | allocated once through the equipped allocator skill, with the feature's id as the requester, and handed to every unit working in the backend row | |
| lint, and the tests | steps 7 and 8, run once over every file the units touched together |
Each unit is handed the slice of step 3's match its own work needs. At 12 the matched set is a family — one skill per existing component — so handing all of it to every unit puts twenty-odd digests in each context. Record the full set against the checkpoint as always, and name which unit received which subset.
The exit condition stays whole. A unit is a slice of the work, never a slice of the gate: step 9 verifies the checkpoint's own condition once, across everything the units produced, and one checkbox covers all of them. Where the condition asks for something no single unit can see — checkpoint 5's confirmation that every module checkpoint 6 imports resolves — the main session gathers it at step 6.
A checkpoint holding one table, one module, one operation or one component runs as a single agent.
Why this parallelism holds where feature-level and checkpoint-level parallelism do not. Two tasks running at once in one working tree each need their own commit, and an aggregation file rewritten by the later one lands in the earlier one's commit. Units of a checkpoint share one commit — the gate's — and the aggregation file belongs to the main session. Two features, and two checkpoints, still never run alongside each other.
[wing] Whether consecutive checkpoints may go to one implementer
No. Each implementing checkpoint gets its own implementer runs, and step 9 verifies each one on its own evidence.
Step 8 — output that survives the run, and the run that dies
Capture test output in a file, and read the file. Output collected behind a pipe lives in memory until the run ends, and a suite can end by taking the whole machine down. Written to a file as it is produced, the output survives to the line where the run stopped.
A run that dies without a result is the third kind of failure, and it is an environment one. It is recorded as a lacked-environment question, and what makes the record worth writing is the configuration it names: how many workers ran, what per-worker memory ceiling they were given, and what else was resident on the machine. A record without those is "it died", and the next run dies the same way. What the right values are is the package's knowledge; naming what this run died under is this skill's job.
The change set of a checkpoint
What hora-verifier is handed at checkpoint 8, and at step 9 wherever it runs: this feature's change set, derived from the repository, never from what an implementer reported. From inside this checkpoint's repository:
base=$(git merge-base release/<version> HEAD)git diff --name-only "$base" # tracked, since the branch pointgit ls-files --others --exclude-standard # untrackedgit log --name-only --format= --grep="^spec: <version>#<id>$" HEAD # landed, on a retake
plus the operations and endpoints this feature declares in .hora/contracts/<version>/. The third line is empty until the gate lands, so mid-gate the set is the working tree. It assumes each commit keeps its spec: trailer in the history; where the equipped git conventions squash, it comes back empty or wider, and either is absorbed below.
Hand `base` over with the set. At step 9 the verifier reads git diff "$base" -- <file> for a marked tracked file before the file itself.
Mark the set from the reports this session holds, and hand the marks over with it.
| Mark | Files | |
|---|---|---|
this-checkpoint | in the touchedFiles of any implementer this checkpoint started — the fix loop's and earlier runs' in this gate included | |
unreported | in the first two lines, in no report of this gate, and not written by this session |
The rest carry no mark. A marked file is what the verifier judges; the rest is the map, and the verifier reads of it what the exit condition and the contract reach. An unreported file is neither a question nor a failure: nobody vouched for it, so the verifier reads it. A run entered from a fresh /hora invocation holds no earlier reports, so it marks this-checkpoint only.
The set is a starting point, never a ceiling. What the exit condition requires and the set does not show, the verifier reads outside it.
Step 9 — when the suite is the verification (checkpoints 6 and 16)
At 6 and 16 the exit condition names tests, and step 8 just ran them. What a verifier adds at these two gates is catching a test that is missing or was weakened, and both are cheaper to check directly:
1. Map every unit's testsWritten, together, against the acceptance criteriathis checkpoint covers. Every criterion carries a test file that exists andran in step 8's suite. A criterion with none -> back to an implementer,with the shortfall named. This is the main session's own read, never anagent's — and where the checkpoint was split, the union of the units iswhat the criteria are read against. For each criterion, name the test thatwould fail if that behavior broke (`../../agents/hora-verifier.md`, "A testexists" is not enough to pass); a criterion with none goes back the same way2. Did step 8's fix loop touch any test file?no -> the checkpoint is verified; write [x]. The implementer never runsthe tests (its own file forbids it), so a suite that passedwithout a test being edited afterwards was never exposed to theloosen-until-green failure modeyes -> spawn hora-verifier after all, scoped to exactly the test filesthe fix loop touched, judging missingTests / weakenedTests only
What this skips is the re-derivation, never the standard. A test loosened, skipped or deleted to make the suite pass still fails the checkpoint. The rest of these exit conditions — the stub's class name and interface at 6, the stub left intact at 16 — is a two-file read the main session does itself.
Which checkpoints the main session must run itself
1, 2, 9, 11 — the ones that talk to a person. An agent cannot ask anyone anything. Handing checkpoint 2 to an agent turns "settle this with the author" into "the agent decided", which is invariant 2.
17 and 18 — the ones that drive the whole system. Neither fits an agent scoped to one checkpoint's files.
What an implementer agent may not do
hora-implementer writes code and tests, for the one checkpoint — or the one unit of it — that it was handed, and nothing else. It never touches git, never writes .hora/, never writes specs/, never installs a dependency, and never edits a file outside its own scope.
| It reports | This skill does | |
|---|---|---|
dependencies | installs it on an install/ branch, merges, rebases the feature branch, continues | |
conflictProof | applies it on an update/ branch, merges, rebases, continues | |
newIdentifiers | appends them to .hora/glossary.md, with any workaround name and why | |
contractDrift | raises a contradiction question (blocking: yes). Never edits the contract | |
registrations | regenerates that aggregation file from its folder, and records where insertion was the only option | |
reinvention | raises a reinvention question (blocking: no) | |
upstreamDefect | raises an upstream-defect question (blocking: no), naming what would let the workaround be removed again. Where the workaround reaches files this feature does not own, it goes on an update/ branch like any conflict-proof change; where a newer version of the package is the answer instead, on an update/<package-name>-to-<version> branch | |
specIssues | takes it to checkpoint 1's procedure, or raises a question | |
missingSkill | records the gap against the checkpoint in the feature file, continues without it, and names it in the closing report. Never substitutes a different skill |
A reported dependency or conflict-proof change pauses the checkpoint where it is. A separate agent applies it on its own branch, it merges into release/<version>, and feature/<feature-id> rebases onto the new tip before work continues.
What the verifier's report drives
hora-verifier returns a judgment, never a fix (../../agents/hora-verifier.md, "What to return").
An `unmet` and the audit's `findings` at 8 are send-backs, and each is reported as one, by step 9's line. A shortfall in the tests goes back to an implementer inside the checkpoint, and is not.
| It reports | This skill does | |
|---|---|---|
met | writes [x] and moves on | |
unmet, with sendBackTo | clears the checkpoints from sendBackTo on and re-enters there. `sendBackTo` is required whenever anything is unmet; a report missing it goes back to the verifier, never into a guess | |
missingTests / weakenedTests | the checkpoint is not passed — back to an implementer agent, with the shortfall named | |
findings (checkpoint 8) | an implementer fixes each one not accepted ("Whether an audit finding may be accepted without a person", below), then the audit runs again — scoped to the fix, never a fresh full re-scan: confirm each prior finding is resolved, and re-audit the files changed since the prior run's at: (the same set step 7 lints and step 8 tests), together with any shared surface that fix reached — a contract caller it rewired, a guard it moved — since those can carry a new finding into a file the fix did not itself edit. An accepted finding is recorded as a question, never left as a silent pass | |
contractDrift | raises a contradiction question (blocking: yes). Never edits the contract | |
specIssues | takes it to checkpoint 1's procedure, or raises a question | |
specAssumptions | records each as a spec-assumption question (blocking: no) |
The verification record
Every checkpoint 8 run appends one block to `.hora/verification/<version>/<feature-id>.md`, and no block is ever rewritten.
## Run 2, checkpoint 8, re-audit<!-- at: backend=3f2a1c9 -->| Finding | Verdict | Evidence ||---|---|---|| F1 | resolved | `closeMonth` checks the role before the write || F2 | carried | its files unchanged since Run 1 || F3 | accepted | the `audit-finding` question that accepted it |
`carried` is this skill's, never the verifier's. A finding judged in an earlier run whose files git diff --quiet <that run's at:> -- <files> finds unchanged is carried and not handed again. Every clear of checkpoint 8 drops every `carried`, so the next run hands every finding. accepted comes only from an audit-finding question, and only as the section below allows.
[wing] Whether an audit finding may be accepted without a person
No. A finding is accepted only by a person answering its audit-finding question, with what living with it costs in front of them. Every finding nobody accepted is fixed, and the audit runs again until none is left.
What every checkpoint follows
How to write a resolver, a migration, a component or a test is not here — that lives in the skills references/checkpoints.md names.
Follow the contract
For each server, the contract in .hora/contracts/<version>/ is authoritative for the providing side and the consuming side both. Wanting to change a contract mid-checkpoint means raising a question, not changing it. A contract is derived once, before implementation, and pinned.
Use the glossary's identifiers
.hora/glossary.md holds the names. When a new concept gets one, check it against @openreachtech/eslint-config's naming rules first, then append it — including the workaround chosen for a forbidden name, and why. Without that record, somebody later restores the naive name and lint fails.
File and folder names, and import order
How a file is named and how imports are ordered are the package's conventions, and neither is restated here (../hora/references/structure.md, "The division of labor"). At step 3, match the equipped skills whose descriptions cover naming and import order, and hand them to the agent.
Lint does not enforce all of it, so the conventions hold because the matched skills are followed, not because a check would catch a miss.
Aggregation files are regenerated
An aggregation file that bundles classes for export (index.js and the like) is derived. When a class is finished, scan its folder and rewrite the whole file. Do not insert one line.
This skill is what rewrites it, at step 6, once the checkpoint's units have finished — the folder is the one thing several units share. An implementer drops its own class into the folder and names it under registrations.
Every regeneration starts with this banner, unchanged. It is the only thing that never comes from the folder scan — write it first, every time.
/** ___ ___ _ _ ___ _____ ___ ___ ___ _____* | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _|* | |) | (_) | | .` | (_) || | | _|| |) | | | |* |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_|** _____ _ _ ___ ___ ___ ___ _ ___* |_ _| || |_ _/ __| | __|_ _| | | __|* | | | __ || |\__ \ | _| | || |__| _|* |_| |_||_|___|___/ |_| |___|____|___|** Code generated by /hora. DO NOT EDIT.*/export { default as Base } from './lib/Base.js'export { default as Zoo } from './lib/zoo.js'export { default as RpaFlow } from './lib/models/RpaFlow.js'export { default as User } from './lib/models/User.js'
The order is the one in the previous section. An export name matches its file name.
The reason to regenerate is idempotency.
| Inserting | Regenerating | ||
|---|---|---|---|
| depends on the previous content | yes. It assumes what its own line is added to | no. Decided by the folder's contents alone | |
| a missing export line | goes unnoticed | is always picked up by the next regeneration | |
| the rule for where to insert | every implementer has to know it | only the generator has to know it |
Nothing handwritten may be mixed in — an aliased export, a re-export of an external package, excluding a particular class. Any one of these makes the file underivable. To use an external package, import it in the file that uses it.
On finding an aggregation file with something underivable mixed in, do not regenerate: insert only the one line, at the position the import order gives.
| Is the mixture documented? | Treatment | |
|---|---|---|
Yes — specs/ or an already-resolved question says this file may carry it | Insert and move on. `blocking: no` | |
| No — nothing says so | `blocking: yes`. An approved exception cannot be told from an accidental edit |
Once a human has documented the mixture as expected, also remove the banner if the file still carries it. The banner claims the file is purely derived.
Conflict-proof files are reported, not written directly
Some files are neither derivable by a folder scan nor safe for a checkpoint to edit on its own.
The line that decides an aggregation file from this one: does the change add a new file for a scan to pick up, or edit an existing shared file's own content? A derived class piling into its own folder is the first. A change to what a shared ancestor itself provides — a getter added to the Base class — is always the second.
Known instances, beyond package.json/package-lock.json:
.env.development a new environment-variable keydocker-compose.development.yml a new profile to enablethe Base class (or equivalent) a getter/method meant for every derived class
More may turn up in the real tree — the question above decides it, not this list.
Report the change needed; do not make it yourself. State what the file needs, under conflictProof. This skill applies it on an update/ branch and commits it there (../hora/references/commits.md, "Commit messages").
A lint rule contradiction
Rarely, two lint rules conflict outright — fixing one violation only trips the other, with no version of the code that satisfies both. This is a defect in that repository's own eslint.config.js (never the outer root's, which does not lint that repository at all), and it has happened for real.
Never stop and hand this to whoever is running this for their own project. That config is not something an ordinary user of this template can be expected to untangle.
Detecting a genuine loop needs every lint error this fix loop has ever seen, not only the latest. Keep every reported violation (rule, file, line) from every attempt. The moment a newly-reported violation exactly matches one already kept, that is proof of a loop — act on it immediately, without waiting for the retry limit.
The retry limit is five attempts per checkpoint. Reaching it without an exact repeat is handled the same way. There is no way to tell the two apart from here, and stopping to ask a human over a trivial style rule is not worth it either way.
Either trigger resolves identically: from every distinct violation kept so far, pick whichever rule sits lowest on the protection order below, cut an adhoc/<rule-name>-in-<filename> branch, add a files-scoped override disabling that one rule for that one file in that repository's own `eslint.config.js`, and merge it in like any other branch. Report it as an eslint-exception question — blocking: no, but fail-loud: name it on its own in the closing report. Reset the retry count and run lint again.
Which rule gets disabled follows a fixed protection order, most-protected first:
1. no-restricted-syntax (never disable this over the others)2. any other no-restricted-* (no-restricted-imports, and the like)3. any rule that is neither no-restricted-* nor @stylistic/*4. @stylistic/* (formatting only) (disable this first, given the choice)
When more than one distinct rule lands on the same, lowest tier, break the tie mechanically, in this order: prefer the rule with no configurable options; then the one with fewer; then whichever name sorts first alphabetically. Never leave this to judgment — a reader of an existing adhoc/ override can only tell whether the right rule was disabled if the same fixed order always produces the same answer.
One override is one self-contained block, a `// TODO:` comment directly above it, never merged into another block:
// TODO: Kick out this block after resolved the issue.{files: ['<filename>',],rules: {'@stylistic/xxxx': 'off',},},
Keeping every adhoc/ override in its own block is what makes it possible to find and remove later.
When a feature finishes
Checkpoint 18 passing is what finishes a feature. Then:
1. Set the feature's entry to [x] in _plan.md2. Commit .hora/ for the acceptance gate3. Report: the feature, how many checkpoints applied, how many were n/a and why,every question it raised — id, category, blocking value, one line, and alink to the file — and git status for every repository it touched
Name and link every question, never count them (../hora/references/structure.md, "Citing a question in a report"). A feature's run can raise a reinvention, a spec-assumption and an eslint-exception without ever stopping.
Never set `[x]` while any checkpoint in that feature is still `[ ]`. A missed checkbox is picked up on the next run; one set by mistake is never revisited.
That same rule is why a listed feature's entry carries no checkbox at all rather than a set one. All eighteen of its checkpoints are [ ], so [x] would claim a pass nothing earned, and [ ] would put it in the queue step 2 takes its next feature from (../hora-plan/SKILL.md, "_plan.md — the order").
References
| File | Content | |
|---|---|---|
references/checkpoints.md | the eighteen checkpoints — order, exit conditions, delegates, when each does not apply | |
../hora/references/structure.md | the layout, the invariants, where and how a command runs, the division of labor, how progress is reported, the closing report | |
../hora/references/asking.md | a check, a proposal or a question, at the checkpoints that run in conversation | |
../hora/references/commits.md | branches, commit granularity, merging, hotfix catch-up | |
../hora/references/done-criteria.md | what "done" means for a checkpoint, a feature and a version | |
../../agents/hora-implementer.md | writes code and tests for one checkpoint, or for one unit of one | |
../../agents/hora-verifier.md | adversarially checks one checkpoint's exit condition. Read-only | |
../../agents/hora-digester.md | writes one equipped skill's digest into .hora/digests/ |