Deterministic, model-free vital signs for software repositories.
Devostasis reads a repository through its forge API and reports seven named states, the Vitals: Pulse, Flow, Integrity, Clutter, Horizon, Direction and Debt. Every run produces an immutable, verifiable bundle with the current state, a deterministic comparison against the previous run and a plain-language report. No language model is involved at any point, there is no health score, and evidence that could not be collected never makes a project look healthier.
The name is development plus homeostasis: the goal is a stable, honest reading of where a project actually stands, so that people and autonomous development systems can react to it.
Repository dashboards usually fail in one of four ways:
- They collapse everything into one number. A green badge hides a red workflow; a "health 82" hides which of five different questions is bad.
- They treat missing data as zero. A denied API call becomes "no branch protection"; a disabled issue tracker becomes "no backlog"; a rate-limited query becomes "no activity".
- They can be gamed by noise. Retrying CI until it passes erases a real failure; closing valid issues improves a backlog metric; mass-linking every change to one milestone looks like focus.
- They summarise with a model whose output changes from run to run and cannot be audited afterwards.
Devostasis is built against those failures:
- Seven orthogonal questions, seven named bands. No arithmetic ever combines them into an authoritative scalar. Correlations between Vitals are declared as metadata instead of hidden.
- Unknown is not zero. Every observation carries an explicit status
(
AVAILABLE,PARTIAL,UNAVAILABLE,FORBIDDEN,UNKNOWN,ERROR) and a freshness. A Vital without sufficient evidence saysUNKNOWN, or emits a conservativeDEGRADEDbound with the set of bands still possible. - Same evidence, same output. Evaluation is a pure function of the recorded observations and a declared policy version. Two implementations computing the same bundle get the same identity.
- History is immutable and self-contained. A bundle can be verified and re-rendered years later from its own files, without the original configuration or provider access.
- Anti-gaming rules are part of the contract. A revision that failed verification keeps its failure in the history window even if a retry later passes. A zero queue is not "good flow". Full milestone linkage is a fact, not a compliment.
| Vital | Question it answers | Bands |
|---|---|---|
| Pulse | How intense is recent observable activity? | DORMANT, QUIET, STEADY, SURGING |
| Flow | What is the state and friction of the change-request queue? | NO_QUEUE, MOVING, CONGESTED, GRIDLOCKED |
| Integrity | What does automated verification say about recent revisions? | UNINSTRUMENTED, NO_RECENT_RUNS, NO_DECISIVE_RUNS, SPARSE, SPARSE_MIXED, FLAKY, CLEAN, FAILING |
| Clutter | How much unresolved stale residue is observable? | CLEAN, LIGHT, CLUTTERED, HEAVY |
| Horizon | Is future work explicitly declared, and how far ahead? | UNDECLARED, DECLARED, VISIBLE, EXTENDED |
| Direction | Is active change work explicitly traceable to declared targets? | NO_ACTIVE_CHANGE, UNDECLARED, SCATTERED, MIXED, FULLY_LINKED |
| Debt | How much explicitly registered maintenance obligation is unresolved? | UNINSTRUMENTED, CLEAR, PRESENT |
Bands describe state, not virtue. A mature project may be temporarily
CONGESTED; a young project may be CLEAN and UNINSTRUMENTED at the same
time. The exact rules, windows and thresholds are in
docs/spec/vitals.md.
Every report opens with a status card that places each band on a 0-100 gauge of the phenomenon it describes, so gradation inside a band is visible:
Horizon ████████░░ 84 EXTENDED
Clutter █░░░░░░░░░ 10 LIGHT
Direction █████░░░░░ 50 MIXED
Flow █░░░░░░░░░ 10 MOVING
Integrity ██████░░░░ 62 FLAKY
Debt █░░░░░░░░░ 11 PRESENT
Pulse ████████░░ 82 SURGING
Gauges are a versioned normalization (docs/spec/gauges.md)
persisted as gauges.json; the band stays the semantic truth, the gauge is
the position inside it, and nothing is ever summed into a health score.
Every bundle also carries demand.json
(docs/spec/demand.md): one level per Vital from
CRITICAL, HIGH, MEDIUM, LOW, MINIMAL or UNRESOLVED, taken from a versioned
band-to-level table you can override, plus an attention order that ranks the
Vitals by level and then by their canonical order (gauges are shown but never
compared across Vitals, because they measure different phenomena). Autonomous
development systems consume it to decide what to work on; the report shows it
as the "Attention" section.
UNRESOLVED means evidence was missing and must never be read as "nothing to
do".
Horizon, Direction and Debt read explicit planning metadata only: GitHub
milestones with due dates and milestones set on pull requests, or two small
register files committed to the repository, targets.json and debt.json
(docs/spec/registers.md), with pull requests linked
to targets by a Target: <id> line. A repository without any of those
declares nothing and gets UNDECLARED or UNINSTRUMENTED, which is a fact
about its metadata, not about its code.
Devostasis uses the register files on itself: see
.devostasis/targets.json and
.devostasis/debt.json, which are the worked example
to copy.
The display configuration chooses which Vitals appear, whether the card
shows bars, numbers or band names, and which report sections are rendered
(docs/configuration.md).
One successful run of one project writes one immutable bundle:
| Member | Content |
|---|---|
snapshot.json |
The authoritative machine state: seven Vitals with band, evaluation status, inputs, derived metrics, diagnostics. |
gauges.json |
The 0-100 position of every band on the scale of its phenomenon, under a versioned normalization contract. |
demand.json |
One demand level per Vital and the attention order, for consumers that decide where to work. |
delta.json |
Deterministic comparison with the previous bundle: BASELINE, COMPARABLE, HISTORY_GAP or INCOMPARABLE, plus per-Vital transitions, including IMPROVED and WORSENED where the Vital declares a band ordering. |
activity.json |
Normalized activity since the previous successful bundle: revisions, change requests, work items, verification, releases, capability changes. |
observations.json |
Every raw observation with its status, coverage and evidence references, so the snapshot can be recomputed. |
effective-config.json |
The exact configuration that shaped the bundle, in canonical form. |
report.md |
A neutral, deterministic report rendered only from the files above. |
manifest.json |
Versions, project identity, receipts, member digests and the identity preimage from which bundle_id is recomputed. |
Bundles live in an append-only history store, normally a private companion
Git repository, next to a convenience latest/ copy and a fleet overview:
projects/
README.md fleet overview for people
index.json the same fleet as data, for machines
github.com/<owner>/<repo>/
latest/ convenience copy, never authoritative
history/YYYY/MM/DD/<bundle_id>/ immutable bundles
index.json
projects/index.json (docs/spec/history-and-reports.md)
gives a control plane the bands, gauges and demand levels of every project
without parsing Markdown. It adds no meaning to the bundles it points at, and
it carries neither an aggregate nor a cross-project ordering: which project
comes first is the consumer's policy, and no accepted contract defines it.
Requires Python 3.12 or newer. The runtime uses the standard library only.
pip install git+https://github.com/drevendev/devostasis@v0.1.9Observe one repository (a GitHub token is read from DEVOSTASIS_GITHUB_TOKEN,
GITHUB_TOKEN, GH_TOKEN or gh auth token; public repositories work
without one, with a low rate limit):
devostasis observe --repo owner/name --out observations.json
devostasis evaluate --observations observations.json --out snapshot.jsonRun a fleet from a configuration file and persist bundles into a store:
devostasis run --config devostasis.json --store ./historyVerify any bundle from its own contents, or re-render its report:
devostasis verify --bundle history/projects/github.com/owner/name/latest
devostasis render --bundle history/projects/github.com/owner/name/latestExecute the conformance vectors of a checkout (docs/spec/vectors.md):
devostasis vectors --path tests/vectorsA minimal configuration:
{
"schema": "devostasis.config.v1",
"config_version": "2026-09-05.1",
"store": { "path": "." },
"projects": [
{ "repo": "owner/name" },
{ "repo": "owner/other", "debt": { "labels": ["type:refactor"], "mapping_version": "1" } }
]
}A project can also observe itself from its own GitHub Actions, with no secret at all, and branch its next steps on the demand levels:
permissions:
contents: read
issues: read
pull-requests: read
actions: read
checks: read
jobs:
vitals:
uses: drevendev/devostasis/.github/workflows/observe-self.yml@v0.1.9
decide:
needs: vitals
runs-on: ubuntu-latest
steps:
- run: echo "work on ${{ needs.vitals.outputs.attention }}"See docs/configuration.md for every option and docs/deployment.md for both deployment shapes: the fleet observer with a companion history repository, and self-observation.
The fleet overview written to projects/README.md:
| Project | Observed at | Comparison | Attention | Pulse | Flow | Integrity | Clutter | Horizon | Direction | Debt | Report |
|---|---|---|---|---|---|---|---|---|---|---|---|
| acme/widget | 2026-09-05T12:00:00Z | BASELINE | integrity HIGH | SURGING 82 | MOVING 10 | FLAKY 62 | LIGHT 10 | EXTENDED 84 | MIXED 50 | PRESENT 11 | report |
A complete synthetic bundle is checked in under examples/sample-bundle; its report.md shows what a human reads.
evaluation_statusis as important as the band.AVAILABLEmeans the band is exact.DEGRADEDmeans a conservative bound: thepossible_bandslist says what could still be true.UNKNOWNmeans no band at all.- Integrity works on immutable revisions, not on runs. One revision
contributes at most one verdict to the 14-day sample; once a revision was
observed to fail, that failure stays in its history for the rest of the
window even if a retry of the same revision passes. A newest revision whose
verification outcome is unknown makes the whole Vital
UNKNOWNrather than inheriting an older pass, and one to three decisive revisions are a sparse sample that says so. NO_QUEUEfor Flow,UNDECLAREDfor Horizon and Direction, andUNINSTRUMENTEDfor Integrity and Debt are descriptive states, never healthy defaults.- Direction
FULLY_LINKEDand DebtPRESENTare neutral facts. The renderer never relabels them as aligned, on track, healthy or unhealthy. - A changed band is
IMPROVEDorWORSENEDonly where that Vital declares an ordering: Clutter's chain, Flow's live queue, and Integrity's two verdict families. Everything else isCHANGED, and the row says why. An order is never declared across Vitals or across projects, and never becomes a score. - Comparisons are only ever
COMPARABLEwhen both bundles share the same contract versions and the same semantic configuration. A missing or corrupt previous bundle yieldsHISTORY_GAP, never "unchanged". - Thresholds are provisional calibration constants under an explicit policy version. They are not tuned to make any single repository look right.
Version 0.1.x is the minimum viable engine: GitHub only, seven Vitals, immutable bundles, a filesystem history store with a fleet index, a Markdown report, the demand interface, gauges, the accepted band ordering and an executable conformance vector format. The ROADMAP lists what is deliberately deferred (a GitLab adapter, additional instruments such as test state, coverage and deployments, an HTML renderer, a calibration corpus) and which accepted research judgements are still waiting to be adopted. The CHANGELOG records every contract and policy version change.
The contracts implemented here come from an independent research process that produced and reviewed them unit by unit before any code existed. The specifications are reproduced in docs/spec and the mapping from each contract to its research unit is in docs/spec/PROVENANCE.md. Disagreements between the implementation and the specification are reported back to that process rather than patched ad hoc.
MIT.