Skip to main content

0.13 — the ladder is data

· 5 min read
0.13 — the ladder is data | AEPRelease 0.13.0 takes the status ladder every plan item climbs out of Rust and puts it in a YAML file, decided by entity-core — a separate, IO-free kernel. The consequence: modelling a kind of work this repository never anticipated stops needing a pull request here and a release.AEPfield-noteaepfield-noteadopterdevelopertry-spec-driven-developmentunderstand-safe-agentic-codingresearch

Source-owned field note · website/blog/2026-08-25-1444-the-ladder-is-data.md · revision 824869fecbd2

Every plan item in the planning store sits at a status and climbs a ladder: draft → proposed → active → implemented → accepted. Until 0.13.0 that ladder was a hand-written lookup in Rust, and the consequence for anybody using the tool was concrete and annoying: a team whose work does not fit the shapes we happened to think of had to send a pull request to this repository and wait for a release.

It is a YAML file now.

What changed​

# artifacts/lifecycles/story.yaml
kind: story
initial: draft
transitions:
draft: [proposed]
proposed: [active]
active: [implemented]
implemented: [accepted]
accepted: []

Put a <kind>.yaml in your own document tree, point --root at it, and new, move, board, lifecycle and validate all understand the kind. No crate changes on either side.

$ protocol artifact lifecycle blocker
blocker starts at open
cleared -> nothing
open -> cleared

Who decides​

Not this repository. crates/plan/aep-backend-markdown/src/kernel.rs hands the definition and the attempted move to entity-core, a kernel that lives in a separate repository and is taken here as a git-pinned dependency.

entity-core is IO-free by test, not by convention. A banned-token scan over its own sources refuses the tokens for filesystem, network, clock, process, thread and randomness, and its entire dependency list is serde and serde_json. It cannot open a file, read a clock or reach a network.

It is worth saying what that does not mean, because the phrase "runtime" invites the wrong reading:

entity-core doesentity-core does not do
take an entity type as data and answer is this move permittedstore anything — every byte the planning store writes is written here, by aep-backend-markdown
evaluate a rung's conditions against values handed to itread a clock — the instant is read at the edge and passed in as an argument
refuse an undeclared status by nameknow what other artifacts exist, or resolve a reference

The arrow points one way and never back: nothing from this repository appears in a manifest of entity-runtime's, at any version. A kernel that depended on its adopter could be shaped by one, and its verdicts would stop being a general answer.

Why this was safe to do​

Because it is reversible, and that was built before the dependency was taken.

crates/plan/aep-backend-markdown/tests/kernel_equivalence.rs holds the kernel's verdict identical to the hand-written lookup it replaced, across every kind-and-status pair. Delete the module and the lookup is still standing behind it. A dependency you cannot undo is a decision somebody else gets to make later.

The vocabulary is open to authors, not to typos​

ArtifactKind and ArtifactStatus both gained an Other(String) variant, so a ladder may declare rungs nobody here named. It is deliberately not open to any string: a status is accepted because some ladder declares it, not because it parses.

That distinction earned its keep two releases later, when a new ladder was written starting at drafted — one letter from the built-in draft, a typo wearing a vocabulary's clothes. It was caught and corrected in 0.23.1.

Openness is also not the default answer everywhere. evidence_kinds stays closed on purpose: an open evidence vocabulary would let a caller invent the kind of proof a gate is asking for. Which vocabularies are open, which are closed, and what each closure buys is written down rather than inferred.

What this bought​

Everything in the eleven releases after it. A rung that costs evidence, a rung that opens on a date, three new ladders — and, in 0.23.0, a whole new kind of artifact added as a YAML file with no Rust change at all, which was the point of the exercise.

See Lifecycles, decided as data for the full model.