Skip to content

v0.15.0

Latest

Choose a tag to compare

@github-actions github-actions released this 17 Jul 03:19

Golavo v0.15.0 — One home for each rule

Golavo 0.15.0 changes no forecast, no source boundary, and no seal. It changes where the
app's rules live. Three of them had drifted into being kept in several places at once,
and each copy was one edit away from disagreeing with the others. Every committed sample
artifact reproduces byte-for-byte on this release, which is the evidence that the engine
underneath is untouched.

This is a release for the codebase's future, not for this week's page.

Why an architecture release

The most important promise Golavo makes is that a forecast never sees the future. Until
now, the app kept that promise in four places.

The rule is: train on everything before kickoff - 1s, from this fixture's own source,
never the fixture's own row, and fail closed if a later row survives. The guard that
enforces the last part was always central and always machine-checked. But the cutoff was
derived independently in the match analysis, the retrospective, the seal path, and the
evaluation folds — and the scoping half existed twice, character for character, in the
server's analysis path and in the core retrospective. Nothing checked that the two copies
agreed. A comment in one of them said it matched "the app's own on-demand path" and named
the other file; that promise was the only thing holding them together.

That class of leak has already bitten this project three times in a single day, always the
same way: stale data hid the problem, and fresh data woke it up.

Now there is one module. leak_safe_training_view returns a fixture's training history and
the instant it was cut off at, and the four facts are inseparable inside it — you cannot
get the rows without the cutoff, or the cutoff without the scoping, or either without the
guard having run. The seal path calls the same module and only tightens the boundary
(min(as_of, kickoff - 1s)), never loosens it.

What else moved

Cached reads. Six surfaces each hand-rolled the same dance: retry across a data
refresh, key a private cache, re-check the snapshot before trusting a hit, stamp the
provenance. Worse, the base module hardcoded the names of the modules caching on top of
it in order to invalidate them — so a seventh cached read would be invalidated only if
somebody remembered to add it to a tuple, and if they forgot, the app would serve a retired
data generation after a refresh and look exactly like it was working. Readers now register
themselves. Forgetting is no longer possible.

Background jobs. The retrospective borrowed the AI read's job store, which meant a
football backtest reported "Assembling the evidence" until the endpoint stamped over it,
and the UI minted an AI job id and rewrote its prefix with a regular expression. Each kind
of work now declares its own stages and its own id space.

The trust table. The retrospective's skill fold scores five model families; the
backtest offers four, dropping bivariate Poisson because it is numerically identical to
independent Poisson and would imply two opinions where there is one. The React view knew
that rule too, and re-applied it. If either side had changed, the page's two tables would
have quietly contradicted each other. The sidecar now sends the four voices and names what
it dropped.

What you will notice

Almost nothing, which is the point.

  • The retrospective's trust table is unchanged on screen, but the API now sends four
    families rather than five and names the omitted one in a new omitted_families field.
    Anything reading trust.models for a five-family list should read omitted_families.
  • The retrospective's job routes now accept only their own lane's job ids. A job id from
    the AI lane submitted to the retrospective's start or cancel route returns 400 where it
    was previously accepted and acted on. Golavo's own UI has always minted the right id, so
    this only affects a client that hand-rolled one.
  • Competition analytics is cached per minute rather than recomputed per request.
  • Sealed forecasts, replays, outlooks, and evaluation folds are unchanged.

Verification

  • 808 Python tests and 177 UI tests pass.
  • The 8 committed sample artifacts (sealed, scored, abstained, voided) validate unchanged —
    the seal engine now runs through the shared leak-safe view and produces identical bytes.
  • Provenance, source-registry, license-isolation, correction-isolation, research-isolation
    and context-pack validators all pass.
  • New machine-checked guards: the leak-safe view's cutoff, scoping and self-exclusion; the
    snapshot reader's staleness and self-registration (every shipped cached read is named);
    each job lane's stage vocabulary; that two concurrent retrospective runs cannot cross
    their packs; and that the client's contract types cannot drift from the frozen schema.