Skip to content

0.3.0

Choose a tag to compare

@github-actions github-actions released this 25 Aug 12:44
· 94 commits to main since this release

Changed — behaviour you may be relying on

  • A rule that compares against something nobody recorded is now unknown, not false. A condition evaluates to True, False or Unknown, and a rule holds only when True. The refusal is a new PreconditionUnobservable / InvariantUnobservable carrying every address it could not read, sorted, rather than the first. Nobody looked and it is wrong used to be one message; sending an operator to fix a review that was never written is what that cost.
  • exists is unchanged. Unknown is a property of the question, not of the operator asking it. Asking whether there is a value at an address is a question about the store, which the kernel can always answer — so exists stays two-valued and not: { exists: … } still means what it reads as. Only questions about a valueeq, ne, gt, gte, lt, lte, in, contains — can come back unknown, and only when there is no value to read. If a missing value should refuse plainly rather than stall the gate, guard the comparison in the same rule: all: [{exists: $fields.x}, {eq: [$fields.x, v]}], which False dominance decides.
  • A key present with nothing after it is not a value. review: with a blank after it is how YAML spells nobody filled this in, so exists reports false for it and a comparison against it reports unknown. Schema validation cannot catch this for a json-kind field, where null is legal. A null written as a literal in a definition is still a value.
  • all and any no longer short-circuit. Kleene's connectives are order-independent, so the answer is unchanged; what changes is that one refusal now names all three missing facts instead of three refusals naming one each. R-54's deterministic short-circuit clause was revised with the rest of the row, and the wording it replaced is quoted in the register.
  • entity's JSON refusal gains precondition_unobservable and invariant_unobservable, each with an unresolved array, and its definition refusal gains a defects array beside the existing defect. Exit codes are unchanged: a refusal is still 1.
  • CoreError::Definition now carries DefinitionErrors rather than one DefinitionError, and Registry::register/replace/EntityDefinition::validate return it. A caller that wants one defect reads .first().

Nothing about a lifecycle ladder changes. Every rule that never compares against a missing value evaluates exactly as it did — including both invariants in examples/order.yaml.

Added

  • Registration reports every defect, not the first. Registry::register, Registry::replace and EntityDefinition::validate return DefinitionErrors — a non-empty list of typed DefinitionErrors — and entity validate prints them all, so fixing a definition takes one pass rather than one run per fault. Value validation has reported every failing field since 0.1.0 (R-23); this is the same for the definition itself. A check whose prerequisite already failed is skipped, so a lifecycle with a duplicate rung is one finding rather than one per transition it invalidates. Comparing a DefinitionErrors to a single DefinitionError holds only when it carries exactly that one, which is what keeps a single-defect assertion honest.
  • Truth { True, False, Unknown }, public, with Kleene and/or/not and is_satisfied. The variant names and tables are taken from engineering-protocols' own aep-domain::predicate::Truth rather than designed here — two kernels that disagreed about what Unknown means would disagree about whether a gate passed.
  • docs/requirements.md gains R-57 (three-valued evaluation) and R-58 (which questions can be Unknown and which cannot), and docs/design/kernel-v0.1.md § 4.1 specifies both, including the rejected first draft that put the choice in the operator instead. R-50, R-51, R-53 and R-54 were revised; each replaced wording is quoted beneath its table.
  • The eight AEP lifecycles, as entity definitions. examples/aep/*.yaml expresses every lifecycle document engineering-protocols ships — story, epic, initiative, task, design, specification, architecture-decision-record, review-result — as data this kernel executes, one operation per edge of each ladder. Phase 1 of docs/design/engineering-protocols-adoption-v0.1.md; no rules yet, because a precondition worth writing needs a rule that can say unknown.
  • An equivalence test that makes the translation checkable, not asserted. crates/entity-yaml/tests/aep_lifecycles.rs compares each definition's (from, to) edge set against the upstream transitions map, read from a committed fixture pinned at 79b641c (crates/entity-yaml/tests/fixtures/aep-lifecycles/PIN.md) rather than from a sibling checkout. A definition that invents an edge and a ladder that grows one upstream both fail, by name. The gate runs it, and example-check now validates examples/aep/ too.
  • docs/roadmap.md — what order the adoption goes in, blocked on what, and the four decisions taken on 2026-08-25: phase 1 ships before phase 0 and is its evidence; the dependency arrow points from engineering-protocols to entity-core and never back; a present null will not count as a value; and an unobservable refusal will name every unresolved path.

Nothing in the kernel changed, and nothing here publishes a dependency in either direction.