Lifecycle files
A lifecycle document declares the statuses of one artifact kind and the moves between them. They
live in the document tree under artifacts/lifecycles/, one file per kind. Any file name works,
because the kind: inside decides.
kind: data-migration # the kind this governs; omit for the tree's fallback
initial: draft # where `new` starts an artifact (required)
transitions: # status -> the statuses that may follow it
draft: [rehearsed, abandoned]
rehearsed: [applied, abandoned]
applied: [] # terminal
abandoned: []
requires: # status -> what reaching it costs
rehearsed:
- evidence: test_result # an evidence kind the protocol declares
or: [ess_conformance] # further kinds, any one of which pays instead
applied:
- evidence: approval
at_least: 2 # default 1
when: # status -> a date the artifact itself records
abandoned:
before: decide_by # reachable only while now is before the artifact's `decide_by`
descriptions: # status -> one line, shown by `board --format markdown`
draft: Written down, not yet run anywhere.
Keys
| Key | Required | Shape | Meaning |
|---|---|---|---|
kind | no | kebab-case kind | the kind governed; absent means this is the fallback for kinds with no nearer lifecycle (at most one per tree) |
initial | yes | status | the status an artifact is created at |
transitions | no | map of status → list of statuses | the legal moves; a status mapped to [] is terminal |
requires | no | map of status → list of {evidence, or, at_least} | evidence records of that kind, held for the artifact, needed to reach the status; or lists further kinds any one of which pays instead, each against at_least on its own |
when | no | map of status → {after, before} | each names a front-matter key of the artifact holding a date; after: due means not until the instant is past this artifact's due |
descriptions | no | map of status → string | what being at the status means |
More columns: swipe horizontally, or focus the table and use the arrow keys.
Unknown keys are refused. when compares against the instant move --at gives, which defaults to
now. An empty guard constrains nothing.
requires is deliberately smaller than a principle's evidence requirement. It counts records of a
kind and does not judge who produced them or whether they are independent. Those judgements belong
to the engine. One judgement is made before the count: on an artifact
that records a model_digest, an ESS conformance record (ess_conformance, ess_conformance_v2,
ess_conformance_coverage_v1) counts only when it passed and its spec_digest is that digest, and
a refused move names each record that did not count and why.
How a kind finds its lifecycle
- the lifecycle whose
kindis exactly the artifact's kind; - a lifecycle for a kind it specialises, nearest first. A kind's parent is named by its last hyphen
segment, so
weekly-digest→digestandcredential-blocker→blocker; - the tree's fallback lifecycle (the document with no
kind:).
Checking one
aep govern validate --root <tree> # the whole tree, lifecycles included
aep plan artifact lifecycle <kind> # the ladder as the store will apply it
The JSON Schema is
artifact-lifecycle.schema.json.
The shipped lifecycles are in
artifacts/lifecycles/.