Devostasis 0.1.8
Roadmap targets B5 and B1: the first accepted band ordering, and the format
and runner that make a conformance case executable. No threshold, window or
gauge changed, and no rule was retuned.
- Band ordering (target B5,
PV-BAND-ORDER-001). Three Vitals now declare
which of two of their own bands is better: Clutter
CLEAN > LIGHT > CLUTTERED > HEAVY, FlowMOVING > CONGESTED > GRIDLOCKED
for a live queue, IntegrityCLEAN > FLAKY > FAILINGand
SPARSE > SPARSE_MIXED.delta.jsontherefore emitsIMPROVEDand
WORSENEDwhere the order applies. Pulse, Horizon, Direction and Debt
declare no order at all, and neither doesNO_QUEUEagainst a live queue or
an Integrity evidence state against a verdict: those transitions stay
CHANGED. No order is declared across Vitals or across projects, and none of
this is a step towards an aggregate. - A direction is only ever reported about exact measurements. The pair must
beCOMPARABLE, therule_idunchanged and both sidesAVAILABLEwith
EXACTband semantics; aDEGRADEDband is a bound, and a move between
bounds is not an improvement. The rule version boundary and observability
transitions keep precedence, and a gauge that moved inside a band is still
UNCHANGED. Every row that could have been ordered but was not says why:
BAND_ORDER_NOT_DECLARED,BAND_ORDER_INCOMPARABLEor
BAND_ORDER_NOT_ELIGIBLE. devostasis.delta.v2carries the two new classes and names the ordering
it applied inband_order_contractandband_order_version, so a stored
delta says under which order its classes were decided. This is the fourth
and, with the consumer surface now frozen, the last planned move of bundle
identity: every row of the consumer surface in the ROADMAP is stable.- Executable conformance vectors (target B1). A conformance case can now be
written as JSON and executed:devostasis.vectors.v1with avitalkind
that evaluates one Vital over raw observation envelopes and adeltakind
that compares two snapshots, a runner, adevostasis vectorscommand and a
published schema (docs/spec/vectors.md). It fails
closed: an unknown kind, an unknown key, an unknown comparison status, a
malformed envelope or a duplicate case id is an error, never a skipped case
that looks like a pass. The schema declares the partial evidence envelope a
vector actually states, and a test holds it to the envelopes the corpus
writes, so the runner and the published schema cannot accept different files. - The ordering ships as fifteen vectors, not as prose.
ORDER-01..15live
intests/vectors/band-order.json, run inside the ordinary test suite and in
CI through the command line, and the conformance table cites them as
vector:ORDER-nn. A third drift guard now checks both directions: a citation
without a vector fails, and a vector nobody cites fails. - Those fifteen carry the meanings the contract gives them. The first
version of this release derived the cases from the contract's rules instead
of transcribing itsRequired conformance casessection, which moved every
identifier fromORDER-02onward onto a different claim — a research
identifier is never reused, andconformance.mdsays so on the same page.
PV-SPEC-001found it; the cases are now transcribed, and the two accepted
pairs the corpus never executed (NO_QUEUEagainstGRIDLOCKED,
SPARSE_MIXEDtoFAILING) are executed. Semantics did not change: the
ordering itself was conformant, and no band, threshold or rule moved.
The split, adjacency and precedence cases this implementation wanted beyond
the accepted set are kept under localDEV-ORDERids, which belong to no
research unit. A fourth guard holds eachORDER-nnto the accepted case name
and to the band pairs that case names, because the first two guards pass
happily while every identifier means something else. - A vector case can state more than one pair.
given.comparisonsis a
list, each entry with its ownexpect, and the case passes only when all of
them do. Accepted cases are written that way — "GRIDLOCKED → CONGESTED → MOVINGfollows the WORSENED/IMPROVED direction" is six comparisons, "every
unequal Pulse band pair" is twelve — and splitting one across several vectors
would split its identifier.ORDER-01..15now execute 85 comparisons between
them.devostasis.vectors.v1is unchanged for the single-pair shape it
already had. - The vectors of
PV-TEST-001are still owed by the research process. The 70
named cases without a test remain open (debt D-1), but what was missing on
our side is now built, so those vectors arrive executable instead of needing
translation. - A documented pin that names a tag nobody published is now a red build.
tests/test_release_pins.pyproves the pins agree withpyproject.toml; it
cannot prove the tag they name exists, because on a release branch that tag
legitimately does not exist yet. Nothing closed the window afterwards, and
0.1.8 fell into it: every pin saidv0.1.8while master carried no such tag,
so the install command in the README failed for anyone who ran it. A new
Released pins resolveworkflow runs daily on master and resolves every
documented pin against the remote. Daily rather than per push, because the
window is legitimate for as long as it takes to tag a merge and not a day
longer. The self-observation workflow could never have caught this: it passes
devostasis-ref: ${{ github.sha }}, which is right for its purpose and means
the one live exercise ofobserve-self.ymloverrides the input that goes
stale (issue #18). - Bookkeeping: the 0.1.7 section still said "unreleased" after v0.1.7 was
tagged and released, which is the exact drift debt D-4 names.
Alongside the two targets, the defects the external review of 0.1.7 found
(#12, finding 5). Each was
reproduced before it was fixed. No contract, threshold or rule changed.
- A malformed title no longer ends a fleet run.
_titlecrashed with
IndexErroron a title that is only whitespace and withAttributeErroron
one of the wrong type. Neither is aRegisterError, so neither reached the
collector's error boundary._titleis now total for provider payload — a
commit message, change-request or issue title of the wrong type is a missing
title — while a register title of the wrong type is aRegisterErrorand
reaches the snapshot asERROR/INVALID_REGISTER, which is what the
register contract says it should be. - Linkage evidence is the exception, and it is now declared. A title or
body of the wrong type is a missing title everywhere except where the target
marker lives:_target_refsjoined the raw fields, so a non-string there
raisedTypeErrorunderplanning.source = file, and reading it as an
absent link would report a project unlinked on evidence nobody could parse.
Unreadable title, body or milestone is nowLinkageEvidenceError, a
CollectionError, so the project fails explicitly with its reason while the
rest of the fleet is still observed. A change request with no marker is
still simply unlinked, because absent evidence is a fact and unreadable
evidence is not. - A successful response that is not JSON is a declared provider failure.
UrllibTransport.getraisedjson.JSONDecodeErrorout of every handler on
an HTTP 200 with an unreadable body. It is nowApiFailurewith
MALFORMED_RESPONSE, so it becomes an observation status like every other
provider failure. - One project's failure costs one project.
run_allhad no boundary of its
own, so anythingrun_projectdid not anticipate stopped every project
queued behind it, skipped the entity-tag cache write and left the fleet index
stale. Each project now fails on its own, keeping its reason and its
unsuccessful outcome, so the run still exits non-zero.