Active plan
60 / 62 US
54 done · 5 under observation
A living dashboard for the MLOps v2 plan. Progress weights user stories; local accreditation does not yet mean results are integrated into production.
Active plan
60 / 62 US
54 done · 5 under observation
The platform is ready and the campaign has completed computation. Its complements are locally accredited; the next step is to reconcile the cut and synchronize all six surfaces with authorization.
Now: G7 is next in epic G: the M74-E campaign is computed and reconciled locally with its four accredited complements; the re-derived figures and charts and the corrected prose wait in candidates, with the consistency guardian at zero. Integrate the documents, data and web in that order and validate the transaction before authorizing propagation and publication of the cut. D7, F2, F5, E5 and G4 remain under observation (0/2). A6 and D5 stay deferred.
Every bar uses the same weighting: done 100%, observing 75%, active 50%, and all other states 0%.
Isolated baseline, backports, minimum CI and cleanup with proven recovery.
Safe manual ingestion and a cron resilient to the official source being blocked.
Main protection, cron authentication, supply chain and web security.
Release integrity, observability and reproducible operations before training more models.
Reduce duplication and complexity only where value is demonstrated.
One canonical truth across data, paper, web, RAG, model card and production.
Answer the stability hypothesis with preregistered evaluation and positive or negative results.
Turn the platform and its results into a reproducible thesis, paper and defense.
Open an epic to see the expected outcome and available evidence for every story.
Keep main isolated from the preserved R9 work.
Port the diagram and generator with visual equivalence.
Integrate and compile the documents without losing branches.
Keep a strict ci-gate and linear history.
Keep R9 open and in draft pending an independent decision.
Clean only accredited targets and retain recovery receipts.
Record only completed facts and verified SHAs.
Inject the fetcher, apply one retry and type WAF blocking.
Validate, promote atomically and upload to S3 in create-only mode.
Separate source access from gates, CI and publishing.
Version source state and maintain one canonical issue.
Cover fetch, freeze and ingestion without depending on the Internet.
Optional time-boxed spike; the manual ritual remains the reliable path.
Publish 300 snapshots, 298 months and the 2026-09 cut under rule zero.
release/2026-09-158ec972c234Use a least-privilege GitHub App to publish through gates.
Retain locks, contracts, triage and SHA-pinned actions.
Verify toolchain and locks inside live jobs.
Require policy, consistency, lint and modeling success.
Reduce 17 production advisories to zero and document triage.
Distinguish blocked source from broken pipeline without losing alerts.
Measure the real CI critical path on a published SHA.
Block pushes when the lock does not represent the DAG.
f67edf9Reject empty artifacts and turn warnings and advisories into contracts.
0092af0Maintain a closed, atomic and fail-closed state feed.
004e478Break the card–manifest cycle with normalized identity.
f9c22a2Remove it only after the causal F2 campaign; never by hand.
Inventory, preserve and clean with conservative GC and receipts.
Observe two real rebuilds before deciding whether track_run stays.
80b3bfb · 0/2Create ENGINEERING.md with a source matrix and backlinks, without deleting yet.
5dd424bPublish a one-page system overview and SVG using canonical figures.
494bcfdCentralize categories and metadata with a golden master.
5fa14faRemove scraper duplication with pure functions.
25722feReplace mutable globals with a testable AuditReport.
bb64647Remove silent failures and log country and month for every skip.
df597ffExtract shared theme, language and saving from three generators.
68bf843Separate migrations, loading and governance while preserving the fingerprint.
17d0ebfRemove consumerless paths with anti-resurrection guards.
10b100dUse one verifiable definition of a dirty tree.
10b100dMeasure coverage and complexity across the real product.
309e214Prevent tooling from again exceeding half the product.
817afc3Remove the web literal and read key_facts fail-closed.
Show source blocking without promising automatic updates.
17eb7a9 · activa en el próximo corte gobernadoGuard macros, tables, proposal and documentation.
033c8a5Answer figures from JSON and require a coherent production pin.
Move literals and caveats into versioned facts.
7925dae · campaña causal calculada; facts pendientes de propagaciónAdd the cohort hook without duplicating data.
Correct stale counts and claims.
1c81b6fMechanize the data → web → production order.
1c81b6fCentralize the pre-training naïve scale.
816d088Build stability without using hold-out information.
967dfcbMeasure already-scored results by cohort without retraining.
e8b4629Train a registered ladder and document failure too.
850d4bdCompete against each cohort's naïve baseline under the canonical gate.
2ff4d07Carry results into thesis, web, RAG and model card under rule zero.
a53202eUnify universes and correct stale catalog entries and docstrings.
51a83ffCompile and review the three official documents from the documentary repository.
Document the blocked source and the semi-automatic ingestion, and retire the unattended-operation claim.
29ca6cbPublish the subsection even if the result is negative.
a53202ePropagate the cut and cohorts with derived caveats.
a8029f2 · corte publicado; campaña nueva calculada, descargo pendiente de reconciliaciónGenerate the presentation figures from the sealed artifacts, with provenance and anti-drift tests.
Reconcile the academic feedback and A6/A7 with the main document; then close M74-E locally before authorizing another campaign.
The M74-E campaign is computed and reconciled locally with its four accredited complements; the re-derived figures and charts and the corrected prose wait in candidates, with the consistency guardian at zero. Integrate the documents, data and web in that order and validate the transaction before authorizing propagation and publication of the cut.
local · reconciliaciónEach day has its own section showing what moved forward. The log changes only when verifiable evidence exists: a commit, CI, release or receipt.
An external audit found two defects in the figures. The parsimonious bar used a selection metric on hold-out axes, and one figure kept a fixed reference from an old run. The bar is now computed in the same metric and over the same series that are drawn, and the figure refuses to draw when it cannot. Four more campaign-dependent figures joined the reconciliation contract by exact name, and the reconciliation no longer closes if any is missing. A second audit showed that the ranking averaged some models over fewer series than the bar, which reversed the comparison. Only models with full coverage are now ranked, and partial ones are listed separately with their coverage. Each fix came with tests that failed beforehand and a new reconciliation; the earlier ones are kept and still verify. An audit confirmed the local closure. Nothing was propagated or published.
A reconciliation receipt gathers, in a disposable tree, the campaign's sealed outputs and the artifacts of the four complements, re-accrediting each with its reader, and seals layers, outputs and inventory. An external audit found that an output deleted after closing stayed accredited and that closing accepted stale outputs next to new ones; both are now rejected. The real reconciliation ran locally and re-derived the verdict, the canonical facts and the figures. From those artifacts the scientific prose of the thesis, the paper and the documentation was corrected in uncommitted candidates, with a table linking each sentence to its artifact. Some conclusions change direction, not just value, and will be published with the cut. The consistency guardian is at zero and the thesis compiles without warnings.
Reading a running transaction and the campaign path of the CRPS consumers now require the full transaction schema. The hold-out figures derive their window from the protocol and reject forecasts from another vintage. Syncing with publication enabled and no changes ends without consuming the permission. A second round rejects empty series identifiers and foreign staging and, by the author's decision, removes the direct path to mark a campaign published: published is reachable only from the accredited transaction.
The CRPS panel of figure F4 used to read whatever file was on disk. It now goes through the provenance reader and, following the author's decision, refuses a CRPS that is not accredited, stating the reason, instead of labelling it. The script checks accreditation before drawing any figure, so it never leaves figures regenerated halfway. An external audit found a public path that bypassed this check, and it was closed. Inside a campaign nothing changes, because the reference CRPS is sealed before the figures stage. No figure was regenerated: F4 will be regenerated within the propagation sequence, after the three pending gates are closed.
The reference CRPS of the distributional models, which feeds the CRPS panel of F4, had no receipt and no reader. It now has its own complement type. The receipt fixes the full protocol, including models, samples and seed, and the reader recomputes the substance against the catalogue of the sealed database. Two external audits found that, when a campaign vouched for the receipt, the consumer only checked its identity. They also found that some malformed inputs raised exceptions instead of being rejected. Both CRPS consumers now validate protocol, shape and substance on every path. The complement is locally accredited and its output is byte-identical to an independent run. The figures are not yet canonical or propagated.
The first champion CRPS complement was computed with correct provenance, but it was marked failed on substance. The forecasting library version pinned the initial state when sampling ARIMA/SARIMA, so the first step came out deterministic and the 95 % band had zero width. Sampling now uses the full predictive distribution, without changing the point forecast. The reader also requires an accredited transaction behind the receipt and integer counts. An external audit found that the sampler had lost its seed contract; it was restored, and the ARIMA and SARIMA factories are now seeded. The new complement is locally accredited and was reproduced byte for byte in a second isolated environment. The failed complement is preserved as evidence. Nothing was propagated or published.
The portal updated its dependencies to clear the high production advisories that were failing the weekly security check. A manual run of that check finished green on main. The interface test that failed intermittently now walks the page in bounded steps and leaves a diagnosis if it stalls. Its suite passed in continuous integration without retries. The production gate passes with no high or critical advisories; lower-severity and development advisories remain, recorded in the triage with their remediation plan or awaiting a patch.
The conformal comparison was recomputed using the deep intervals produced by the parent campaign, imported with accredited provenance and without retraining. Only the baseline comparison column changed; gamma and ACI coverage remained identical. The complement passed close, acceptance and read-time verification, and its receipt and evidence were sealed after file access was restored. Figure F4 and canonical figures have not yet been regenerated. The next task is to resolve the remaining limitations and prepare reconciliation across all six surfaces, with separate authorizations for propagation and publication.
All 42 E3 lanes ran in a separate worktree with seed 1, their own identity and receipts accredited against the plan and output hashes. The run completed without failures and verification re-accredited the entire set. The verdicts match the parent campaign. Original receipts were preserved without changing their identity or seed. C4 is locally accredited; its results are not yet canonical or propagated.
The local M74-F commit contains the provenance, identity, seed and campaign-isolation corrections, together with the complement contract and CLI. Seals check code, inputs, DuckDB content and SQL catalog, and imports against the parent's archived evidence. Tests using real Git and temporary databases, full validation suites and post-commit review passed. The tree is clean. The commit is neither on main nor backed up remotely; environments were not recertified and protected worktrees remained intact.
The fourth campaign completed every stage without failures and passed its input and output gates. Results and receipts were persisted, and the transaction finished as computed with consistency pending. The consistency guardian detected new figures that still require documentary reconciliation and propagation. Results were not published and the served cut was not modified. Evidence from earlier failed attempts and the parent campaign remains preserved.
M74-E reached 953cd00 with a verdict allowing a local campaign and a backup on its remote branch. Causes of the three failed attempts were corrected and their scenes preserved. This state alone authorized neither computation nor publication: the campaign remained stopped until an explicit order and continuous execution window were provided. R9 remained preserved.
The academic feedback and both weekly increments now live in the documentary authority, which is the only proof that counts: the main document carries them, compiles clean and was verified on the compilation surface as well. The change touches no data, models or scientific results; the published cut and its identity are unchanged. With that, the reconciliation stops being a debt and the work returns to campaign integrity, which is closed in an isolated tree: accrediting every forecast consumer, wiring the producer that is missing today and sealing the environments before any new campaign can be authorized. The two earlier failed runs remain diagnostic evidence and none of their outputs is reused as an official result.
Progress no longer waits for a meeting or an external answer. Technical decisions are resolved through reproducible evidence, while the owner retains only the material authorization boundaries. The immediate track incorporates all received academic feedback and reconciles A6 and A7 with the main LaTeX document: both packages were delivered and verified, but they are not considered integrated until their durable content lives in the documentary authority and compiles there. M74-E then resumes in its isolated environment to finish its guards and tests; only a clean local commit and a new preflight can support a separate authorization for another campaign. The two failed runs remain diagnostic evidence, and none of their outputs is reused as an official result. The published data cut and release do not change with this update.
A presentation is where figures age unnoticed: they are copied once and nobody checks them again. The defense material is now built by a program that reads the project's sealed files and assembles the slides from whatever it finds; if a fact a slide needs does not exist, the program stops and names it, instead of leaving a gap or inventing a number. Every figure appears with its unit and the population it refers to, and the last slide lists the fingerprints of the files everything came from. A set of automated checks watches for drift: they fail if a visible figure does not come from a file, if the stored material does not match what the program produces today, if any of those files changed since the last build, or if one of the qualitative claims stopped being true. The content covers the panel, the floor that is hard to beat, the value at several months out, the negative cohort result, the availability threat to the source and the decisions still open. The material is private and for internal use: it is not published on this site nor added to any repository of the project.
The paper defends an uncomfortable finding: on this panel, repeating the last published value is hard to beat, and no trained model beats it with significance. The natural objection is that such a floor is an effect of pooling very different series. The answer had already been measured and is now published: the panel was split into stable and unstable series under a rule written before any result was inspected, each group was trained separately, and the per-group selector had to clear the same bar as the system, at three horizons at once. None clears it, in either table, and in one group the selector picks the simple method itself, so it competes with its own reference. Segmenting, on its own, does not change who wins at one month. The paragraph carries no figure at all: the claim is qualitative because the finding is, and the numbers live in the thesis, derived from the sealed files. The supply limitation is added too: the official source rejects automated visits, so the monthly download is done by hand and enters through the same controlled procedure; that threatens timeliness, not the integrity of what has already been collected. The story stays under observation rather than finished, because the paper's headline figures are still waiting on the causal re-derivation, and the disclaimer that says so may only be retired once that measurement exists.
The official site that publishes the bulletins stopped accepting automated visits, so the last months were downloaded by hand and entered through the same controlled command as always. Reviewing the thesis to write that limitation turned up something more pressing: an appendix still claimed the system stays up to date without manual intervention, and that had stopped being true. The sentence is retired and replaced by what does hold. The new text draws a distinction that matters: what is at risk is freshness, that is, how promptly each new month arrives and, with it, the build-up of evidence for judging long-range forecasts; what is not at risk is the integrity of what has already been collected, because every page was frozen, verified and backed up, and the current cut has no missing month. What changes is who fetches the page, not how it is validated or stored. So the retired claim cannot creep back, an automated check now watches for it across every document of the project, not just the site, and it tells promising automation apart from describing manual work, which remains legitimate. The concrete state of the source is copied into no document: it is published in the file that travels with every cut, so that when it changes, it changes in one place only.
The thesis, the proposal and the paper used to live inside the data repository, mixed in with the code that produces the numbers. They now have a repository of their own, with its own automated check: whenever someone edits a text, the system compiles all three and says so if any of them stops building. The move was verified file by file, so that not one byte changed along the way. The data repository keeps only the five tables of figures the pipeline itself generates, and a check demands they be identical on both sides, so nobody edits one copy and leaves the other behind. With the move done, the three documents were compiled and reviewed in the author's editing tool, without a single error. From now on that tool is used to compile and look at the result, not to store changes: whatever should last enters through the repository, with its review, and is pulled from there. It is the same discipline that already governs the code, applied to the text.
The first finding was reassuring and worth stating plainly: the six ways the system decided which series enter the analysis all gave exactly the same result, the same seventy-four. There was no hidden discrepancy. What there was, was the possibility of them drifting apart one day without anyone noticing, and a few hand-written numbers that would have masked it; there is now a single authority that computes it and a check that, if any copy strays, says which one and by what. Then came three corrections of things that had stopped being true. One model was shelved as «does not work here» for a reason the previous step disproved by measuring it properly; it returns to the evaluated category, without thereby entering the production cycle or getting near deployment. A hybrid model produces no result at all on one of the two tables, and that is written down as an observed limitation, with its exact source and scope, without filling the gap or deleting the row. And three code comments claimed a certain method wins at long horizons when the repository's own measurement file says another one does; the claim is retired and points at where it is measured, rather than being swapped for a new name that would age just the same.
When the same number lives in the thesis, in a table, in a figure, in the model card and on the site, sooner or later four of those five go stale and nobody notices. That door is now closed: there is a single file holding the figures for the whole stage, derived from the four previous steps, and everything else is generated from it. Not one count, not one average, not one verdict is copied by hand, and an automatic check walks the new thesis text and fails if it finds a number written by hand. The wording keeps the result as it came out, which is still negative: no router wins; in one table the six- and twelve-month gains are not enough because the three-month one falls short; in another the large twelve-month gain does not save the cell, which loses at three; and the smallest group provides no evidence. The site and the assistant are ready to show these figures, but only once they travel sealed in a cut: the current cut does not carry them and, by rule, nothing is shown rather than inventing a cohort. The published cut was not regenerated; the new files enter the contract for future cuts with a closed, named exception for the current one.
The correction first, because it touches something already published. Reporting the previous step I wrote that changing how error is measured and how data is scaled was the cause of the old configuration spiralling. That cannot be claimed: several things change at once between the two configurations compared, so what is demonstrated is the whole bundle, not any of its pieces. And separating it carefully gives a different picture: what always helps is training on the month-to-month change; the rest of the bundle helps in one of the two tables and hurts in the other. The sentence is corrected here and in the internal records. What is new: a router was tested that, for each group of series and each forecast distance, uses whichever method did best in the past and is judged only on months it never saw. The conditions for calling it a winner were written back in July and were not touched: improve by a minimum margin, survive a demanding statistical test, and not get worse at any of the three distances. None of the four groups meets them. In one table the router improves considerably at six and twelve months, but not enough at three; in another it improves a lot at twelve months and is still disqualified for getting worse at three. And in the smallest group the router picks the simple method itself, so it ends up compared against itself. The negative result closes the step, and the router is not deployed: that would need a live trial and separate permission.
The previous step looked at old results; this one trains. Before switching anything on, what would be tried was written down and saved: three recipes and not one more, with every setting fixed, plus a list of context models that cannot be promoted to answers afterwards. The program refuses a recipe that is not on that list, so changing your mind halfway leaves a trace. Forty-two training runs went through a laptop processor, with no graphics card, each leaving a receipt saying what ran, on what material, how long it took, how much memory it used and which files it wrote. All forty-two finished. None beats the method of repeating the last known value within its own group. Two things did get settled, and they got settled because the shot was called first: the old configuration, which back in July spiralled into errors ten times worse, does come under control when several things are changed together; and training on the month-to-month change rather than the level matters on its own, sometimes by a factor of two. Fixing the spiral is not enough to win. The negative result is published all the same, because an experiment you only report when it goes your way is not an experiment.
With the series already split into stable and unstable, the cheap question came next: among the models already evaluated, does any of them beat the dumbest possible method —repeating the last known value— within its own group? The comparison was done group by group and against THAT group's dumb method, never against an average over all of them: the bar for a group that barely moves and one that advances every month are different numbers, and comparing against the average of the two is comparing against nothing. The rules of the contrast were written before looking at any number, and were checked by enumerating every possible case by hand on small samples. Of sixty-eight conclusive comparisons, sixty-eight came out worse than the dumb method and none better. A fourth group has only five series, too few for the test to say anything, and that is written down rather than forced into a conclusion. One uncomfortable detail is also published: in one group a model is identical to the dumb method down to the ninth decimal, and the test declares it significantly worse because it only looks at the direction of the difference, not its size; the verdict is left as it stood and published alongside how big that difference actually is. The result is negative and it is kept: splitting by stability, on its own and one month ahead, does not change who wins.
The director asked for stable and unstable series to be trained separately. The subtle risk is this: if deciding which series is stable involves looking at the final stretch later used for evaluation, any advantage found afterwards was bought in advance. Here the split is computed only from what precedes that stretch, and the test guaranteeing it replaces the entire final stretch with garbage in four different ways and demands that not one measure and not one label move. The rule was written down and frozen before looking at any result, and it is deliberately poor: two conditions on how often and how large the backward jumps are. There was a trap measured in August: calling series without backward jumps stable picked twenty-three that sit still more than half the time, and a series that sits still is easy to predict by definition. So stillness decides nothing and travels as a separate annotation: of the thirty-nine stable ones, twenty-eight sit still and eleven genuinely advance. What does not flatter is also stated: the second threshold today rules out no series on its own. None of this is a conclusion yet: it is the preparation, put on the record so the comparison that follows cannot be fitted to its own result.
The number every error is divided by to make it comparable across series lived copied in four places. Three agreed; two of them, the ones scoring the foundation models, did something different when a series was too degenerate to compute it: instead of admitting the scale does not exist, they returned a one. Dividing by one does not fail, does not warn, and leaves a number that looks like a comparable metric when it is really days, a thousand times larger, contaminating any average that includes it. Before touching anything the four were measured over eleven series: on the six that do have a scale they agree to the last decimal, and on the five that do not, two were lying. There is now a single implementation, it does not depend on the modelling libraries, and the two that lied exclude that series instead of inventing a number for it. Along the way, an impossible seasonal period stopped being computed in silence and is now rejected, and the test on which every published accuracy figure hangs —which until today was skipped entirely when those libraries were missing— finally runs.
Several documentation pages still counted bulletins in prose, a number that changes with every cut and had already changed; they now say what they mean without the count, which lives where it is generated. The document explaining the guard illustrated its typographic tolerance with a dead figure and described four kinds of rule when there are seven. The dated log entries are left as they are: they are history, not claims. And the publication order —data first, then the site, then production— stops being a habit: it is written down with its preconditions, stopping conditions, how each stage is verified and how it is undone, and a read-only command checks it before anything moves. It blocks if either tree is dirty, if a critical artifact is missing or tampered with, if the site declares a different cut than the published one, or if it runs ahead of the data; the site repository being unavailable is a failure, not a permission.
The thesis direction asks to split the panel into stable and unstable series and train each group separately. That will arrive as a field of the published cut, and today no series carries it. So the hook is left in place and nothing more: if the cut declares a series' cohort, the gallery shows it exactly as the file says; while it does not, the interface stays exactly as it is, without a single badge. An empty value, another type or an ambiguous structure renders nothing either: an invented label would be worse than none. The badge reads the same data the card already holds, downloading nothing extra, and keeps deep links, ordering and the dense mode.
The manuscript cites dozens of figures and the guard watched only four of those with an owner in the data: the system's prospective error, which appears five times, was watched by nobody, nor the empirical coverage, nor the starting point of the comparison. Three rules now tie them to their source, and each anchor was tested to capture its own figure and no other: the naive version of the first also swallowed another model's error. The disclaimer acknowledging the figures as provisional also stops being loose prose: the protocol they were measured under is declared, and the guard requires the text to follow —mandatory while they predate the causal correction, forbidden once they are re-derived— with both directions tested without running the campaign. What remains depends on that campaign, so the story stays under observation.
The assistant answered figures from memory: its index was built only from prose —the page, the model card, the audit— and never opened the files where the numbers live. That is why a sentence corrected on the site could keep being answered stale for days. A card in both languages is now derived from those files, with the unit and the population next to each figure, so a measurement is not quoted as if it were universal; if a field, a section or a type is wrong, the build stops instead of filling in with prose. Four engineering documents nobody indexed join in —evaluation, cleaning, promotion and the normative index— fetched at the cut's exact version, not summarised. And when the site reports fresh data, an index pointing at a different cut stops being a console note and becomes a build failure.
The site promised that publishing a bulletin was enough for the panel to bring itself up to date. That stopped being true on 6 August: the source went behind a firewall and the last two bulletins were loaded by hand. The sentence is gone from both languages and from the corpus the assistant answers with, where it was still alive word for word. In its place the site will report what the pipeline records, reading it from the state file itself rather than inferring it from prose: if the file is missing it stays quiet, if it is present and unreadable it says it could not verify it, and it never invents a block. That file does not travel in the published cut yet, and regenerating it would change the cut production serves, so it joins the next one and the story stays under observation until then.
The number of models in the comparison framework was the last headline figure typed by hand on the site, with a note explaining why: the file carrying it was not being downloaded. It turned out it did travel in the published cut, and as a critical artifact; all that was missing was consuming it. The site now reads it from there, validating the artifact against its contract before looking at it, and if the file is missing, the contract is broken or the value is not a positive integer, the build stops naming which of the three happened. There is no fallback: a wrong headline number is worse than a broken build. The served value does not change, because the typed one matched the artifact.
The claim that opened this epic —that tooling had grown to nearly twice the product— was measured by hand one afternoon in August and never checked again. Nothing in the repository counted its own size. Now every versioned file has exactly one declared role —product, tests, tooling, experiments, generated, plus evidence, data, prose, configuration and third-party code— and the split breaks if a file ends up with no role, if two rules claim it, if an exception is declared twice, or if it points at a file that no longer exists. Environments fall outside because they are not versioned, and that is checked rather than assumed. The tooling-to-product ratio is measured on every run against the ceiling the author set.
Three rules this repository already presupposed were never enabled, so what the tooling claimed to govern and what it governed were different things: 133 suppressions silenced nothing (mostly an import order in files that no longer manipulate the search path), the broad-catch policy leaned on a directive that never fired, and complexity was measured nowhere. It is now measured across the product, with the threshold set at the REAL maximum rather than an invented goal. The ratchet also stops publishing a suppression total that summed unrelated debts: each rule carries its own counter, and the worse catches (a bare except, a BaseException one) stop being invisible with metrics of their own. Twelve came to light; the linter flagged one.
The demonstrator's bands had a legacy fallback: when the quantile file or a cell was missing, they were estimated with a square root and a fixed scalar. It was not an equivalent alternative, it under-covered, and consumers could not tell a measured band from an estimated one; the empirical quantiles are now mandatory and a missing cell stops the build naming it. And the question «is the tree dirty?» had three separately written answers: it now has one, with the narrower scope documented in a single place instead of inferred from three functions.
The module that built the warehouse did three different jobs across a thousand lines: applying the migration chain, loading and canonicalising rows, and recording the build identity. They are now three modules with one responsibility each, with the entry point and its public surface untouched. The warehouse content fingerprint comes out identical under the same build identity, and the Parquet byte for byte. It is on main with continuous integration green.
Language and theme stop living in global variables that had to be rebound before each pass: they travel as an immutable context each figure receives, and drawing settings restore themselves even when something fails. Importing a generator no longer reconfigures Matplotlib for the whole process. All 72 gallery variants, the result figures and the 81 report pages come out identical to the previous ones, and so do the console output and the order they are emitted in. It is on main with continuous integration green.
The scrapers' six broad catches become specific exceptions: a source or format failure joins the monthly report with stage, month and country, while a programming defect no longer masquerades as a lost month and instead escapes and turns the run red. The debt ratchet stops counting itself and brings unjustified catches down to zero. It is on main with continuous integration green.
The report stops living in two module-level lists that accumulated between runs and becomes one object per invocation. The shortest-series selection now breaks ties on the full key with a stable sort, so it no longer depends on input order. Severities and verdict are unchanged.
Both scrapers carried the same per-country extractor written twice; it is now one, parameterized by column and classifier, with tested pure functions and row-for-row identical outputs across the whole bulletin archive.
Category rules, domains and metadata stop living in three places and move to a single authority, with equivalence proven against the previous classifiers over 24 years of real labels and byte-identical pipeline outputs.
A normative index declares each document's class, authority and consumers; a test fails on a broken link, on two documents claiming the same authority, or on a missing backlink. No document was deleted.
A page and an SVG generated from canonical sources explain the DAG, the cron, champion and shadow, the gates, drift, the guard and the manifest; the guard now watches the diagram too.
Monthly tracking merged with 5/5 CI; observation over two real rebuilds begins (0/2).
The single release_id contract is on main and will activate with the next real bulletin.
Empty artifacts, advisories and warnings are now covered by visible contracts and green CI.
The DVC hook blocks stale locks and the web audit records zero vulnerabilities.
Repositories, platform, evidence and recovery were verified without losing work.
Production was verified as fresh, with the snapshot archive, the panel months and the immutable release all aligned.