0.4.0
Added
-
examples/aep/gains the evidence preconditions phase 3 asks for.story'simplementandarchitecture-decision-record'sacceptnow cost at least onetest_result, evaluated three-valued — so nobody presented one refuses as unobservable naming$args.evidence.test_result, and a count was presented and it is short refuses as failed. The first sends somebody to produce a record; the second to argue about the one that exists. That distinction is the whole ofengineering-protocolsgap-register:39, and it is why three-valued rules were built before this.Only the guarded operations declare an
evidenceargument, so passing one toproposeis refused as an argument the operation does not take — the schema doing its job rather than a special case. No edge changed.The equivalence test gained a second half to match: a rung the pinned ladder charges for must be charged for here too, paired by
(target status, evidence kind). Not by count or wording — upstream declaresat_leaston a status and these definitions declare agteon a verb-named operation, so pinning the sentence would pin a translation rather than the claim. Verified by deleting the precondition and watching it fail.The pin moves to
engineering-protocolsa193caa, whereartifacts/lifecycles/story.yamlnow carries that requirement for real.scripts/check-upstream-pin.pyfound the drift on its own, two days after being written for exactly this.
Changed — behaviour you may be relying on
entity graphtakes several definition files and two more formats. The positional argument is now a list,--referencesswitches the subject, and--formatacceptssvgandhtmlbesidetextanddot.textis byte-identical to before. Two DOT details changed: the graph is named"<entity> v<version>"rather than"<entity>", so two versions of one entity no longer produce two files claiming to be the same graph; and each node emits itslabelexplicitly, because a node's id and its label are separate things in the reference graph and both have to survive a quote. Passing several files without--referencesis a usage error rather than a guess.
Added
-
entity-graph, a fourth crate, and the picture nobody could draw before.entity graph --referencesdraws entity types as boxes andreffields as the edges between them — the reason typed references were built first.Graph::lifecycledraws whatgraphalways drew; both go through one layout and four emitters.No layout engine. Calling graphviz would make a drawing depend on which
dotis installed, so a picture could change without the definition changing — and a picture nobody can reproduce is not reviewable in a pull request. The layering is integer arithmetic: longest-path from the entry, with back edges classified first by depth-first search so a ladder that loops still lays out. Every coordinate is ausize; a test scans the crate's own sources for floats, IO, clocks and hash maps, and another reads the manifest to hold it to its single dependency.A target type nothing declares is still drawn, marked as undeclared: leaving it out would hide exactly what
Registry::validate_allrefuses. -
Typed references between entities. A field may be
type: refwith anentitynaming the type it points at, so a definition can say that an order'scustomeris a customer and a story'sepicis an epic.inverselabels how the other side reads the edge;acyclicdeclares that it may not form one.examples/references/is a mutually-referencing pair, andentity inspectshows the target, the label and the flag whether they are written on the field or on an array'sitems.Cardinality is the array machinery that already exists — one reference is
type: ref, several istype: arraywithitemsof kindref. An earlier draft had arelations:block besideschemawith its owncardinalitykey; it was two ways to say one thing, which is the defect this model refuses everywhere else, and it was dropped.docs/design/kernel-v0.1.md§ 3.5 records that.The kernel checks the declaration and the shape of an identity, and stops. Whether an instance carrying that identity exists, what state it is in, what revision — those are questions about another instance, and
executeis handed exactly one (R-01). Resolving one by lookup would mean the same inputs could produce different decisions at different moments, which is the property that makes a decision replayable (R-02). Resolution stays the shell's.Registry::validate_allasks the one cross-definition question the kernel can answer: does everyref, at any depth in a schema or an operation's arguments, point at a type the registry holds? It reports every missing target rather than the first. It is not part ofregister, because two types that reference each other are ordinary and a registration-time check would make them impossible to register in either order.entitycalls it once the registry is assembled.R-20 gains
ref; R-26 covers the three new constraints; R-27 and R-28 are new. -
examples/aep/vision.yaml, and a check that notices when upstream moves.engineering-protocols0.14.0 added a ninth lifecycle — a vision isdesign's ladder withimplementedremoved, because a vision is replaced rather than finished. The pinned fixture and the definitions follow it, and the equivalence test now covers nine ladders and 73 edges.The reason it needed noticing at all is the interesting part: nothing was red.
pin-checkholds the committed fixture against its ownPIN.mdand says nothing about whether that fixture is still what upstream ships, so this repository was green while its equivalence test asserted agreement about eight ladders and nine existed.scripts/check-upstream-pin.pyanswers the other question — a ladder whose rungs moved, one upstream ships and nothing pins, one pinned and gone — and.github/workflows/upstream-pin.ymlruns it weekly against a fresh clone.It is not a gate step, deliberately.
task checkreaches no network, and a check that had to clone somebody else's repository would make every local run depend on being online. Drift gets its own red run rather than arriving as a puzzling failure somewhere else.