Antern

A first-principles guide and worked engineering build

Engineering a CI-Triage Decision System

A red build at 02:47 looks like a classification problem. It is really a decision problem with asymmetric costs, unreliable evidence, correlated observers, and a human who still owns the release.

What this article builds. We start from an empty system and reconstruct the path from raw CI artifacts to a human-facing verdict. Along the way, we define labels, reject a convenient dataset on licensing grounds, catch leakage, build a run-level trust gate, compare three observers, design a typed evidence contract, test five fusion strategies, and stop a distillation run before spending money on a GPU.

This is also a record of how the system was built with AI assistance. The model wrote and inspected code, ran experiments, and proposed decisions. The human fixed the boundary, chose the costs, rejected flattering interpretations, and stopped work when the evidence did not support the next step.

Start here / No CI or machine-learning background required

What did the red light actually tell us?

A developer changes code and pushes a commit. Continuous integration (CI) runs a repeatable recipe: fetch the code, install dependencies, compile it, run tests, and save reports. A test checks an expected behavior. A build is one execution of that recipe. Red means some check failed. It does not identify the cause.

CommitA particular code revision
BuildEnvironment + compilation
TestsExpected vs observed
ReportsEvidence to investigate

Suppose CheckoutTest.appliesDiscount expected ₹900 but observed ₹1,000. Perhaps the discount code broke. Perhaps two tests share a database and one erased a fixture. Perhaps the database never started. These explanations require different repairs even though the dashboard displays the same red icon.

Triage means deciding what deserves attention and what evidence to collect next. A flaky test produces different outcomes across reruns of the same code under the studied conditions. That behavior may arise from ordering, timing, randomness, shared state, or environment instability. Calling it flaky does not mean the underlying bug is harmless.

The distinction that governs the whole system

“Has this test shown nondeterminism?” describes a test’s history. “Did today’s failure reveal a new defect?” describes an incident. A historically flaky test can still catch a real regression today. The Week 4 labels primarily support the first question. Using them to answer the second requires additional incident evidence and validation.

Three objects you must keep separate

From observation to consequence
ObjectExampleWhat it establishes
Observation1 failure in 20 recorded runsWhat happened under those conditions
EstimateA model emits a flakiness score of 0.8A learned association; probability meaning needs calibration
ActionInvestigate, rerun, or hold a releaseA policy choice involving costs, severity, and ownership

We will write x for evidence available at decision time, y for a later label, and p = P(y = 1 | x) for a conditional probability. Here y = 1 means flaky. The vertical bar means “given.” A probability is conditional on what the system observed; it cannot certify what the system never measured.

Choose your depth. Read the narrative and try the experiments on a first pass. Open the build notebooks for project-specific evidence. Follow the derivations and the production design near the end on a second pass. All interactive numbers marked “illustration” are teaching examples, not new project measurements.

The incident

The clock is part of the specification

It is 02:47. The release build is red and the release is due at 09:00. One failed test can mean a real defect, a flaky test, or a broken machine. Each interpretation leads to a different action.

00

One failure, four actions

The on-call engineer can stop the release, isolate the test, rerun the job, or investigate manually. A model cannot choose among those actions responsibly until the team writes down what its mistakes cost. That is why this build begins with the operational decision rather than a model.

02:47 · RED BUILD one test failed STOP real defect ISOLATE flaky test RERUN infra failure INVESTIGATE not enough evidence
The output space comes from the actions available to the engineer.

The system estimates. The engineer acts.

METHOD

The thinking loop used in every phase

A new component should not appear because an algorithm sounds suitable. It should appear because the current system cannot answer a concrete question. We will call these “observer questions”: the specific uncertainty a component is meant to reduce. Here is the seven-step loop used throughout.

Situation Question Failures Contract Hypothesis Evidence Decision what hurts? what is missing? what can lie? reads / emits before running what happened? keep / change THE RESULT CREATES THE NEXT QUESTION
A component is justified by the question before it and judged by the evidence after it.
Build notebook · Use this template on any system
1 · Situation
Describe the operational moment in plain language. Name the person, trigger, available action, and cost of delay.
2 · Question
Ask what the current design cannot tell you. Avoid naming a model yet.
3 · Failure map
List known-knowns, known-unknowns, unknown-knowns, and unknown-unknowns. Convert the important failure modes into refusal rules or tests.
4 · Contract
Write responsibility, reads, emits, refuses, constraint, and connection to the previous slice.
5 · Hypothesis
State what should happen and what observation would prove the idea wrong before running code.
6 · Evidence
Run the smallest experiment that separates the candidate explanations. Keep the command output and result artifact.
7 · Decision
Keep, discard, pivot, or abstain. Record the cost and the claim you can no longer make.

The rest of this article follows that loop. Expand a build notebook to inspect the project reasoning. File chips identify local artifact paths; they are provenance references, not public download links. The main explanation stands on its own.

Part I / Decision before prediction

Specify what the system may say

The first three phases lock down scope, loss, and measurement. This prevents a later model from redefining success around whichever number looks best.

PHASE 00

Draw the boundary

Build notebook · Reasoning path
Situation
A red build arrives at 02:47. The engineer needs help before 09:00, but an automatic action can ship a defect or stop a healthy release.
First question

What is the smallest useful thing the system can do without taking operational authority away from the on-call engineer?

Candidate answers

Automatically rerun the job. Automatically quarantine the test. Stop the release. Or estimate the probability that the failure is flaky.

The first three mutate the world. The probability is information the engineer can combine with incident context.

Decision
The system estimates P(flaky | current report, test history). The human chooses the action.
Artifact paths
PROBLEM.mddesign/00-start-here.md
Evidence created
A written boundary with one responsibility, two reads, one emitted probability, an abstention rule, and a list of forbidden actions.

Before choosing a model, ask: what decision is a person trying to make, and which part of that decision can the system safely inform?

The system reads the current CI report and a test's run history. It emits a probability that the failure is flaky. It may refuse when evidence is thin. It does not merge, quarantine, delete, or stop anything.

Inside the system

Read evidence, estimate probability, declare uncertainty, preserve provenance.

Outside the system

Choose the operational action and accept its consequence.

This boundary matters because a probability and an action have different owners. The model can be technically correct about uncertainty while the release policy still chooses to stop. Conversely, the model can be confident and the engineer can override it because the incident carries context the model does not have.

Why the four verdicts need more than a binary classifier

1 − P(flaky) is a probability of “not flaky” under the model’s label definition. It is not automatically P(real defect). Non-flaky cases can include infrastructure faults, deterministic test defects, and incomplete evidence. ABSTAIN is a decision to withhold a verdict, not a fourth physical cause. UNKNOWN at ingestion and a model timeout are different again.

The lab combines a run-level gate, binary observers, and a human boundary. It has not trained a validated four-cause incident classifier. That gap matters: never let the product UI give a binary model a broader meaning than its training labels support.

PHASE 01

Price the mistakes

Build notebook · Reasoning path
What Phase 00 leaves open
A probability has no operational meaning until the system knows what answers it may support and which error is worse.
Question
If the true cause is a defect, flaky test, or infrastructure failure, what happens when the system gives each possible answer?
Method

Draw a truth-by-verdict table. For every wrong cell, write the physical consequence first: “defect ships,” “release waits,” “engineer reruns,” or “test is quarantined.” Add a rough cost only after the consequence is clear.

Missing case
A first-time failure has no history and may have no supporting log. Three forced labels cannot express “I have no evidence.” That gap creates ABSTAIN.
Artifact paths
design/01-decision-and-cost.mddecisions/01-output-space.mdai-ledger/01-decision-and-cost.md
Rejected proposal
Always say “real defect” when uncertain. It protects against one expensive miss but applies delay cost to every uncertain case and eventually destroys trust.
Simple example

Imagine 100 uncertain builds. Stopping all 100 may avoid one $5,000 defect, but 99 unnecessary delays at roughly $500 already cost about $49,500. Conservative does not automatically mean cheap.

The external contract contains four verdicts: REAL_DEFECT, FLAKY, INFRA, and ABSTAIN. The fourth exists because a first-time failure with no logs and no history does not fit safely into the first three.

The project's explicit cost assumptions
Truth Wrong verdict Working cost
Real defect FLAKY or INFRA About $5,000
Flaky test REAL_DEFECT or INFRA About $500
Infra failure FLAKY Ongoing, not bounded
Any cause ABSTAIN About $750

These are estimates, not audited finance figures. Writing them down still improves the system because the team can challenge a visible assumption. An unwritten cost is harder to inspect and easier for an accuracy metric to erase.

Historical lab score = (5,000 × FN + 500 × FP) / N, with 1 = flakyAudit correction: the evaluator reverses the error costs implied by the operational story. A real defect called flaky is FP when 1 = flaky. The stored scores remain historical lab scores; they must not be interpreted as validated release-loss estimates.

ABSTAIN is cheap relative to shipping a defect, but it is not free. If every case is escalated, model risk falls while the system stops doing useful work. Coverage therefore has to sit beside risk.

Derive the decision threshold instead of inheriting 0.5

For this derivation only, assume two mutually exclusive incident states: flaky and real defect. Assume correct decisions have zero incremental cost, missed defects cost Cmiss, and unnecessary holds cost Chold. This deliberately simplified model is not the full four-verdict system. It also assumes p estimates the incident state, a stronger claim than the project’s historical flakiness label supports.

Risk(dismiss as flaky | x) = (1 − p) × Cmiss
Risk(hold as defect | x) = p × Chold
Dismiss only when (1 − p)Cmiss < pChold
Therefore p > Cmiss / (Cmiss + Chold)

With $5,000 and $500, the threshold is 0.9091. At p = 0.8, dismissal has expected loss $1,000 and a hold has expected loss $400. The more likely class is flaky, yet a hold has lower expected loss. The costs explain why. On an equality, keep the more cautious action in this illustration. Threshold selection is separate from fitting a score; the scikit-learn threshold guide makes the same distinction.

Interactive 01 · Cost and action · illustration

A probability does not contain your policy

At 80%: dismiss $1,000; hold $400; review $750. Lowest assumed loss: hold. Dismissal threshold without review: 90.91%.

Review is modeled as a fixed fee with perfect resolution and no additional delay. At the default $750 it is never optimal: the best binary action costs at most $454.55. Lower review cost to $200 to create a review region. Real review can make mistakes and consume queue time.

The general rule is a*(x) = argmina Σy P(y | x)L(a, y): for each allowed action, add its losses across possible states, weighted by their probabilities, and choose the smallest. This supports three causes and a review action once the probabilities and loss table exist. It does not invent the missing probabilities for you.

Why abstention can be rational even when the toy cost says otherwise

The arithmetic above assumes reliable probabilities and a correct loss model. Missing logs, a new project, or an unvalidated score can violate those assumptions. Abstention can then be a policy constraint: “we do not issue a verdict outside the validated population.” If review is imperfect, its loss is its fee plus expected downstream mistakes and delay. Budget that honestly. A single $750 cell is not a full model of the review process.

PHASE 02

Build one evaluator

Build notebook · Reasoning path
What Phase 01 leaves open
We know which mistakes hurt. We still need a measurement system that prevents a later model from selecting its favorite metric.
Question
What can the current metric not tell us?
Ladder

Accuracy asks whether answers are right overall. Recall and precision expose the rare class. AUC checks ranking. Calibration checks whether a stated 0.8 behaves like 0.8. Cost-weighted risk maps errors to consequences. Risk-coverage checks whether low risk was purchased by abstaining on everything.

Architecture
Put the complete ladder behind one evaluate(probs, labels) call. A later observer cannot report accuracy without the rest of the evidence appearing beside it.
Artifact paths
design/02-measurement.mddecisions/02-metric-ladder.mdci_triage/metrics.pyexperiments/02-measurement.mdtests/test_metrics.py
Experiment
Construct an always-not-flaky predictor at the real base rate. Expected result: impressive accuracy and zero recall. Then construct a calibration-binning trap and keep the first failed construction visible.
Why the ladder exists

If 3 of 100 tests are flaky, a model that says “not flaky” 100 times is 97 percent accurate. It catches zero flaky tests. Accuracy answered its question correctly; we asked an incomplete question.

The evaluator accepts probabilities and labels. It does not know whether the probabilities came from a tree, a GRU, retrieval, or an LLM. Every observer passes through the same function.

# Condensed artifact interface; imports and helper bodies omitted.
# Stored cost weighting is historical, as explained above.
def evaluate(probs, labels, threshold=0.5):
    probs, labels = _check(probs, labels)
    preds = (probs >= threshold).astype(int)
    return {
        "accuracy": accuracy_score(labels, preds),
        "precision": precision_score(labels, preds, zero_division=0),
        "recall": recall_score(labels, preds, zero_division=0),
        "roc_auc": roc_auc_score(labels, probs) if len(set(labels)) > 1 else None,
        "brier": brier_score_loss(labels, probs),
        "ece_equal_width": _ece(probs, labels, "equal_width"),
        "ece_equal_freq": _ece(probs, labels, "equal_freq"),
        "cost_weighted_risk": _cost_weighted_risk(preds, labels),
        "risk_coverage": _risk_coverage(probs, labels, threshold),
    }
Accuracy Recall AUC Calibration Cost risk Risk / coverage hides minority ignores ranking ignores honesty ignores impact rewards refusal EACH STEP EXISTS BECAUSE THE PREVIOUS ONE IS BLIND
The metric ladder is a sequence of repaired blind spots, not a dashboard shopping list.

The first measurement experiment used an always-not-flaky predictor against a roughly 3 percent positive rate. It achieved more than 95 percent accuracy and exactly zero recall. A second experiment tried to expose a calibration binning trap, failed to construct it, then corrected the probability distribution and confirmed it. This pattern becomes the unit of work for the rest of the build: state the expected behavior, construct a case, run it, and keep falsification visible.

Read a score by reconstructing its denominator

With 1 = flaky, precision is TP/(TP+FP): among flagged tests, how many carry a flaky label? Recall is TP/(TP+FN): among flaky-labeled tests, how many did we flag? ROC AUC measures ranking across positive-negative pairs. None directly prices a release mistake. The operational two-state loss would be (5,000 FP + 500 FN)/N, the reverse of the stored lab weighting.

Calibration asks whether cases assigned a probability near 0.8 are positive about 80% of the time. Fit the model on training data, fit calibration using independent held-out predictions, choose the decision policy on validation data, and inspect the final frozen policy on an untouched test set. For small data, use a properly nested cross-validation design. The calibration documentation describes both calibration curves and the need to separate calibration data from model fitting.

Interactive 02 · Base-rate shift · illustration

The same detector can produce a different work queue

At 80% recall and a 5% false-positive rate: 240 true positives, 485 false positives, precision 33.1%.

The bar represents flagged cases, not all 10,000 cases. Rates are fixed assumptions to isolate prevalence. In practice these rates can shift too.

Coverage has two denominators

Execution coverage is completed requests divided by submitted requests. Decision coverage is issued verdicts divided by submitted requests. A completed request may abstain. Report both, plus the conditional error rate on issued verdicts and the total cost across all submitted requests.

Total cost / N = (loss on issued verdicts + review cost + execution-failure cost + inference cost) / N

A risk-coverage curve that drops refused cases from its denominator describes the accepted subset. It is useful, but it does not show total service cost. Nor is distance from 0.5 necessarily decision confidence when the action threshold is 0.9091. Sort by a validated risk or decision-margin estimate appropriate to the policy.

Small samples and calibration traps

Expected calibration error (ECE) partitions scores into bins and averages the gap between mean score and observed positive rate. Different bin edges can change the answer; sparse bins have uncertain rates. Brier loss averages (p − y)² and reflects more than calibration alone. Report a reliability diagram, bin counts, and class-specific behavior alongside aggregates. A single-class test set has no defined ROC AUC.

On 12 examples, one prediction changes accuracy by 8.33 percentage points. A tie at 8/12 justifies a precommitted spending stop; it does not prove that no larger model could learn. For uncertainty estimates, resample the independent unit—often test identity or project—not repeated rows that share a test. A confidence interval over one project cannot establish transfer to other projects.

Part II / Evidence before models

Decide what can be trusted

Most of the engineering work happens before model fitting. The project has to define a label, prove it can use the data, prevent joins from changing the population, remove leakage, and reject corrupted CI runs.

PHASE 03

A label with provenance

Build notebook · Reasoning path
What Phase 02 needs
An evaluator cannot tell us anything until “flaky” has a reproducible meaning.
Question
What observation would prove that identical code behaves nondeterministically?
Derivation

One failure does not prove flakiness. One pass does not prove stability. A pass and a failure across reruns of the same code is the smallest observable flip.

Provenance rule
Emit test_id, label, and num_reruns together. Refuse a missing rerun count. A label without sample size hides how hard the system looked.
Artifact paths
design/03-ground-truth.mddecisions/03-label-procedure.mdexperiments/03-rerun-bias.md
Evidence
The rule matched 26,765 of 26,765 source rows. Rerun counts varied widely, including one negative row with 10,000 reruns.
Simple example

Test A runs ten times and produces P P P F P.... The flip demonstrates differing outcomes under the recorded rerun conditions. Test B runs ten times and produces ten passes. We know what happened ten times; we do not know what would happen on run eleven or run ten thousand.

A test is labeled flaky when identical code produces both a pass and a failure across reruns:

IsFlaky = 1 ⇔ NumFailingRuns > 0 ∧ NumPassingRuns > 0

The rule matched all 26,765 rows in test_results.csv. Yet the two classes do not have equal certainty. An observed flip establishes nondeterminism under the recorded conditions; infrastructure and uncontrolled environment changes still need investigation. A test that never flipped only proves that no flip was observed within its rerun budget.

POSITIVE LABEL pass + fail observed a flip is direct evidence NEGATIVE LABEL no flip observed absence within a finite sample measured precision may understate true precision
Label uncertainty points in one direction: some negative labels may be missed positives.

This resembles a positive-unlabeled problem: some apparent negatives may be undiscovered positives. If observed positives are correct and errors only hide positives among negatives, measured precision on the same cases is a lower bound on true precision. That is a conditional statement. Infrastructure contamination can violate the first assumption, and selective rerunning can make the sample unrepresentative. Record rerun counts; do not turn this observation into an unconditional production guarantee.

How much can ten clean reruns tell you?

Assume each rerun independently fails with constant probability q. The chance of seeing no failures in n reruns is (1 − q)n. For a 1% failure probability and ten reruns, that is about 90.4%. A flaky test can look completely stable in a short observation window.

Interactive 03 · Observability · illustration

A label depends on how hard you looked

10 fresh reruns at q = 1%: 90.44% chance of no failure; 9.56% chance of observing both pass and fail.

For fresh reruns, P(observed flip) = 1 − (1 − q)ⁿ − qⁿ. If a failure is already established and all other assumptions hold, one passing rerun is enough to establish a flip; the observation scheme is different. This slider uses fresh reruns.

The independence assumption is strong. A shared machine outage can correlate outcomes; test order can change the per-run failure probability. Record commit, environment, seed, order, and run identity where possible. “Same code” controls only one source of variation.

There is also selection bias: engineers may rerun suspicious tests more often. Then num_reruns partly measures human attention. A model can learn the investigation policy rather than the behavior of the software. Use rerun budget as label provenance; admitting it as a predictive feature requires proving it exists at the intended decision time and does not encode the answer.

PHASE 04

Licence, join shape, and leakage

Build notebook · Reasoning path
What Phase 03 leaves open
Labels live in one file. Candidate features live in another. Before modeling, we need permission to use them and proof that the join does not change the population.
Questions

May we legally reuse this dataset? What key represents the same test in both files? Can rows disappear or multiply? Can a feature reveal the label directly or through missingness?

Failure map

Project spellings differ. Class and method separators differ. An inner join may lose projects. A many-to-many key may duplicate rows. History-derived features may be impossible to compute at live prediction time.

Artifact paths
decisions/04-dataset-choice.mddesign/04-data-and-licence.mdci_triage/data.pyexperiments/04-join-shape.mdtests/test_data.py
Expected reality
About 26,765 rows, 28 result projects, and a 3.157 percent positive rate. A large departure means the join or source coverage needs investigation.
Observed reality
26,142 rows, 25 joined projects, positive rate 0.0316, and 16 remaining features. The initial join returned zero rows and forced a project-name fix.

When two datasets must be joined, write the ways a row can disappear, duplicate, mismatch, or leak the answer before writing the join.

The most convenient dataset, IDoFT, had no detected license in the checked repository. Public access did not grant reuse rights. The build chose the FlakeFlagger dataset from Zenodo under CC BY 4.0 and kept attribution as part of the design.

The join looked simple until it returned zero rows. One file used names such as apache-commons-exec; the other used commons-exec. Normalizing test identity and matching the project suffix repaired the join. The finished table contained 26,142 rows across 25 projects with a positive rate of 0.0316.

key = normalized_project + "::" + normalized_class + "::" + normalized_method

assert joined_rows are within the expected source bounds
assert no feature is near-perfectly correlated with label
assert no feature's missingness reveals label

The last check found a real leak: flaky_source revealed the answer through missingness. It was non-null exactly where the label was positive. The team also removed history-derived modification windows that could not be recreated at prediction time from the available snapshots.

Engineering ruleLeak detection should inspect values and missingness. A categorical field can reveal the target even when numeric correlation code cannot parse it.

Freeze the world at the moment of prediction

Imagine the failing build arrives Tuesday at 02:47. Wednesday’s fix commit, Friday’s incident label, and a retrieval neighbor added next month are unavailable on Tuesday. A retrospective join can silently import all three. Every feature needs an event timestamp, an availability timestamp, and a rule that says why it was knowable at inference.

Before t₀Known history + eligible index
At t₀Current report + frozen features
After t₀Reruns + adjudicated outcome

Store later outcomes in the label table. A test’s identity is not enough to guarantee a safe join: a feature with the right ID but the wrong time can still leak. Normalize identifiers, assert key uniqueness before joining, report unmatched rows by project and class, and inspect any suffix matching for collisions. Expected row counts alone cannot detect a wrong one-to-one match.

PHASE 05

Gate poisoned runs before learning

Build notebook · Reasoning path
New observation
A CI archive contains test outcomes, but a compile failure, truncated log, or process crash can make many outcomes look like real test failures.
Question
Before an observer reads a run, how do we decide whether that run is evidence at all?
Contract

Read one maven.log. Emit a verdict and a reason containing measured counts. Refuse with UNKNOWN when the file is missing, unparseable, or shows a class that started without finishing.

Precedence
Check compilation failure before mass failure. If nothing compiled, reporting “many tests failed” sends the engineer to the wrong subsystem.
Artifact paths
design/05-infra-gate.mddecisions/05-infra-gate.mdci_triage/infra.pyexperiments/05-infra-damage.mdtests/test_infra.py
Evidence that changed the design
A real run had 13 failures among 163 tests, about 8 percent, with a shared failure shape. This falsified the first 15 percent threshold choice and motivated 5 percent.
Simple example

If a JVM crash causes 40 tests to fail together, counting those 40 as independent flaky-test observations poisons labels, retrieval neighbors, and sequence histories. The trust gate removes the run once, upstream of every observer.

A failed build can make many tests look broken even when they never ran. The trust gate reads maven.log and emits one verdict with a numerical reason. Precedence is fixed: BUILD_FAILED, LOG_TRUNCATED, MASS_FAILURE, TRUSTED, then UNKNOWN.

maven.log PRECEDENCE build failure? truncated? ≥ 5% failed? parseable? otherwise unknown VERDICT TRUSTED or rejected reason with actual counts observers trusted evidence only
A run is admitted or rejected before it contributes evidence.

The 5 percent threshold came from a real run with 13 failures out of 163 tests, about 8 percent, sharing SSL and query-parameter failure shapes. A proposed 15 percent threshold would have admitted that run, so it was lowered.

The gate later exposed a deeper lesson. The first parser read only the final Maven summary in a multi-module build. Another parser missed inline <<< ERROR! failure lines. These bugs silently removed failures from square-okhttp and distorted the sequence, retrieval, fusion, and distillation phases downstream.

Important implementation gapThe run-level gate is not wired into the tabular CSV path because the aggregate feature table has no run_id. The repository records this as a missing join, not as completed architecture.

A gate changes the population you claim to understand

Let G = 1 mean the gate admitted a run. Downstream metrics then describe P(error | G = 1). They do not describe all CI traffic. A strict gate can improve that score while refusing most difficult incidents. Report admission rates by project and failure type, review samples of rejected runs, and retain rejection reasons.

The 5% mass-failure rule is a heuristic motivated by an observed case. Many failures can also arise from a legitimate shared-code regression. Treat the gate’s diagnosis as evidence requiring inspection, not proof of infrastructure causation. A parser correction must invalidate affected histories, retrieval indexes, and evaluations together.

PHASE 06

Make the split answer a deployment question

Build notebook · Reasoning path
What we have
A leak-checked feature table and labels. The next temptation is to shuffle rows, train a baseline, and report one score.
Question before code
At deployment, does the system see new tests from a project it already knows, or a completely unseen project?
Two experiments

Random folds answer “can the model generalize to held-out tests when project-specific patterns appear on both sides?” Grouped folds answer “can it transfer to a project absent from training?”

Guardrail
No project may appear on both sides of a grouped fold. Training needs the classes required by the chosen classifier. ROC AUC requires both classes in the evaluation set; other metrics can still be reported on a single-class slice.
Artifact paths
design/06-splits-and-baseline.mddecisions/06-split-choice.mdci_triage/splits.pyexperiments/06-split-comparison.mdartifacts/results/baseline.json
Evidence
The same logistic-regression baseline moved from 0.753 random-split AUC to 0.547 grouped AUC. The split changed the claim by 0.206.
Analogy

Training on chapters 1 to 9 of one textbook and testing on chapter 10 is different from training on one textbook and testing on another author's book. Both are “held out,” but they measure different kinds of transfer.

A random split mixed tests from the same project across train and test. A grouped split held out whole projects. The model, rows, and features stayed fixed; only the question changed.

0.753Random-split AUC
0.547Grouped AUC
0.183Grouped fold standard deviation

The 0.206 gap is consistent with the model relying on project-specific patterns; it does not by itself isolate identity leakage from other distribution shifts. The grouped result also varied sharply depending on which project was held out. A single average would have hidden that instability.

At this point the team faced a deployment decision. Should one global model work cold on a new codebase, or should each team train a local instance on its own history? The first requires grouped evaluation. The second permits within-project evaluation but creates cold-start and model-maintenance costs. The system eventually chose the second shape and kept both results visible.

Interactive 04 · The deployment question

Change the split, change the claim

Random rows: project A, B, and C appear on both sides. This tests held-out rows from familiar projects; repeated identities can still leak.

Blue = train; burgundy = test, with text labels on every cell. Assignments are schematic, not the lab’s actual folds.

For a team’s future incidents, use a chronological boundary and build every historical aggregate and retrieval index as of that boundary. For unseen tests within a familiar project, separate test identities. For a new customer repository, hold out projects. These are different targets; choose the one implied by deployment and use additional splits as stress tests.

A strong evaluation records the target population, independent unit, time cutoff, allowed inputs, baseline, metric, and decision threshold before fitting. Keep feature selection, calibration, threshold tuning, and learned fusion inside the training/validation process. A final test set stops being a final test set once it repeatedly chooses the next design.

Part III / Three observers

Let different representations inspect the same case

The system tries numeric features, ordered run history, and failure-text retrieval. Each observer answers a different question. Their failures are as useful as their scores.

PHASE 07

Observer 1: tabular features and the pivot

Build notebook · How we arrive at a tabular observer
Start without an algorithm
The joined dataset gives one row per test and sixteen numeric columns. That is a tabular representation: each test is described by a fixed list of measurements.
Observer question
Can static properties of a test separate likely flaky behavior from deterministic failure?
Candidate models

Logistic regression gives a linear baseline. One decision tree is readable but unstable. Random forests average many trees. Gradient boosting adds trees sequentially, with each new tree correcting residual errors from the existing ensemble.

The lab chose histogram gradient boosting as one reasonable experiment. The architecture does not depend on it being the only acceptable algorithm.

Why this representation first
It is the cheapest view already available after ingestion. No log encoder, sequence construction, or neighbor index is required. A weak result can still become one bounded observer later.
Artifact paths
design/07-tabular-and-the-pivot.mdci_triage/tabular.pyexperiments/07-calibration-tradeoff.mddecisions/07-the-pivot.mdartifacts/results/tabular.json
Question created by the result
A 0.634 cross-project AUC says the static view transfers weakly. Do we add information, accept a weak vote, collect more projects, or change the deployment question?
One row, one view

Imagine a row such as test_length=42, third_party_libraries=3, resource_optimism=0.18, plus thirteen other measurements. The tree learns partitions of this feature space. It does not know whether yesterday's run passed, what exception appeared, or which old failure looks similar. Those missing views motivate later observers.

HistGradientBoostingClassifier reads 16 leak-checked numeric features. It handles missing values internally. The repository does not implement the earlier idea of per-row abstention for missing features, and the design document was corrected to say so.

0.634Cross-project raw AUC
0.630Calibrated AUC
0.880Per-project AUC

Sigmoid calibration improved ECE from 0.0312 to 0.0225 but slightly reduced AUC. The original hypothesis said ranking would remain identical. It did not, because CalibratedClassifierCV(cv=3) trained three smaller base models and averaged their outputs. It did more than apply one monotone transform to a fixed score vector.

The 0.88 figure is not an improved global model. It comes from random folds within each project, evaluated on unseen tests from a known project. Nineteen projects had enough positives; six were skipped. This pivot trades cold-start generalization for local history.

What the numeric observer actually learns

A tabular row turns a test into numbers such as execution duration or coverage statistics. A decision tree partitions that feature space with questions of the form “is duration greater than this value?” Gradient boosting adds trees to improve the current predictions. It can discover interactions without you enumerating every combination.

That strength can also hide shortcuts. “Long runtime” may identify one repository’s tooling rather than a general cause of flakiness. Feature importance describes how this fitted model uses its inputs; it does not establish that changing a feature would change flakiness. Compare against a simple baseline, inspect errors by project, and remove suspicious features in an ablation before telling a causal story.

PHASE 08

Observer 2: sequence, reformulation, discard

Build notebook · How we arrive at a sequence observer
What tabular loses
A count such as “12 failures in 1,000 runs” removes order. It cannot distinguish twelve failures clustered yesterday from twelve failures spread across two years.
Observer question
If we preserve pass/fail order, can earlier behavior predict a flip in later runs?
Concrete construction

Take one test's ordered history. Show the model a prefix such as P P P F P P. Hide the later suffix. Label the example positive if the suffix contains a pass-to-fail or fail-to-pass transition.

Why a recurrent model is plausible
A recurrent unit updates a hidden state as each outcome arrives. In principle it can distinguish patterns with the same totals but different order. The lab used a very small GRU. An LSTM, temporal convolution, or hand-built recency features would be alternative experiments.
The answer-key trap
The global flaky label is defined from the full pass/fail history. Giving the full history to the model lets it reimplement the label rule. Prefix-predicts-suffix prevents that circularity.
Control before complexity
Count failures in the prefix. This costs almost nothing. The GRU must beat it by 0.05 AUC and retain more than a few distinct calibrated probabilities.
Artifact paths
design/08-sequence-observer.mdci_triage/sequences.pyexperiments/08-heuristic-control.mdtests/test_sequences.pyartifacts/results/sequence.json
Same counts, different order

P P P F F F and P F P F P F contain three passes and three failures. A tabular count sees the same row. A sequence representation can see one late cluster and one alternating pattern. The experiment asks whether that extra structure predicts the withheld future.

Choose a sequence model only after you can name what order contains that an aggregate destroys.

A naive sequence model would read the same pass/fail history used to define IsFlaky. It could learn the label rule directly: spot at least one pass and one failure. The build avoided that circularity by asking the prefix to predict whether the future suffix would flip.

VISIBLE PREFIX P P P F P P F P P P ··· WITHHELD SUFFIX P P F P ··· no run id may appear on both sides
The reformulation removes the answer key from the input.

Only one original project had enough trusted, nameable failures to build positive sequence examples. The small GRU achieved raw AUCs around 0.43 to 0.54. A free control, failure count in the prefix, scored roughly 0.52 to 0.58 and beat the GRU in nearly every fold.

The keep rule had been written before training: the GRU had to beat the control by at least 0.05 without collapsed calibrated probabilities. It failed. The team discarded it and retained the heuristic. More epochs or a larger hidden state were rejected because 23 distinct tests did not contain enough diversity for capacity tuning to repair.

What would make order worth modeling?

Consider P P P F F F and F P F P F P. Both fail three times in six runs. The first might suggest a change point; the second might suggest alternation. A count-based feature cannot distinguish them. A recurrent model maintains a hidden state: hₜ = update(hₜ₋₁, outcomeₜ), then predicts from that state. A GRU uses learned gates to control what it retains and replaces.

The model only earns its complexity if those ordering patterns predict a held-out future target better than counts do. Keep the prefix and future suffix separate; never derive an input statistic from the suffix you are trying to predict. Randomize order as a diagnostic: if performance is unchanged, the learned system may not need sequence structure. Discarding this observer after it lost to its simple control is a valid engineering result.

PHASE 09

Observer 3: retrieval and the parser correction

Build notebook · How we arrive at a retrieval observer
What the first two views lose
Tabular features compress a test into numbers. The sequence view compresses each run into pass or fail. Neither preserves the exception message that tells an engineer what actually broke.
Observer question
Have we seen a failure that reads like this one, and what did that historical test turn out to be?
Mechanism

Convert every historical failure message into an embedding. For a new message, retrieve the nearest messages, remove duplicate test identities, inspect their labels, and use the flaky fraction as a probability.

Why retrieval can help rare classes
A trained model compresses all cases into shared parameters. A rare exception can be averaged away. Retrieval keeps the rare example itself available for a direct match.
Failure modes first

The query can retrieve itself. Ten runs of one test can become ten fake votes. A one-class index can report perfect-looking precision. Weak neighbors can manufacture confidence. Passing tests have no real Maven failure text, which can force a synthetic-text shortcut.

Guardrails
Leave the query test out, deduplicate by test identity, require both classes in the index, require at least three distinct neighbors, and expose the selected neighbors for inspection.
Artifact paths
design/09-retrieval-observer.mddecisions/09-index-contents.mdci_triage/retrieval.pyexperiments/09-retrieval-comparison.mdtests/test_retrieval.py
A five-neighbor vote

Suppose the five nearest distinct tests have labels FLAKY, FLAKY, NOT, FLAKY, NOT. Retrieval emits 3/5 = 0.60 and the neighbor identities. If all five rows came from repeated runs of one test, they count as one opinion, not five.

Retrieval embeds a failure message, finds nearby historical cases, deduplicates neighbors by test identity, and returns the fraction labeled flaky. It refuses to build a single-class index and abstains when it cannot find enough distinct tests.

query = embed(failure_text)
neighbors = nearest(index, query)
neighbors = dedupe_by_test_identity(neighbors)

if len(neighbors) < min_distinct:
    return ABSTAIN

p_flaky = mean(neighbor.label for neighbor in neighbors[:k])

The first corpus used real exception text for failures and synthetic “test passed” sentences for the other class. That created a possible style shortcut. The corrected square-okhttp corpus used real trusted exception text for both classes: 102 cross-run deterministic tests and 76 tests labeled flaky by the ground-truth table.

0.9831Corrected neighbor-label agreement@5
0.5730Majority baseline
178Queries, zero abstentions

The pre-registered prediction expected performance to fall when the style cue disappeared. It did not. That prediction was falsified. The result is still within-project. A separate historical 17-project reference scored 0.474 against a 0.715 majority baseline, so the article cannot claim cross-project retrieval works.

Deployment interpretationThe corrected within-project result shows strong neighborhood label agreement in this corpus. It does not establish calibrated probabilities, release safety, or performance on future incidents. The separate cross-project reference is evidence that transfer needs its own evaluation.

Similarity is a representation choice, not a causal explanation

An embedding maps text to a numeric vector. Cosine similarity compares vector direction. Nearest-neighbor search retrieves cases close under that representation; it does not know whether the exception actually has the same cause. Library names, boilerplate, and duplicated stack frames can dominate similarity.

The vote 3/5 = 0.6 is a local class fraction, not automatically a calibrated incident probability. Distinct test identities prevent literal duplicate votes, but five tests from one shared helper can still carry one correlated failure. Inspect neighbor distance, shared code, label provenance, and age. Minimum neighbor count alone does not ensure relevant neighbors.

Read the 0.9831 correctly. The function named precision_at_k computes the mean fraction of neighbors whose label matches each query’s label. It includes negative queries. That is neighborhood label agreement, not positive-class precision of the final detector, not 98.31% incident accuracy, and not probability calibration. The name comes from the artifact; the interpretation must come from the code. The evaluator also uses a different minimum-neighbor check from the voting helper, an edge case to reconcile before reuse.

Part IV / Fusion in detail

Combine evidence without counting the same mistake twice

Fusion is not a vote over three anonymous numbers. The system needs to know where each probability came from, whether it is calibrated, whether evidence exists, and which observers share upstream dependencies.

PHASE 10A

Make every observer sign the same contract

Build notebook · Reasoning path
What Phase 09 creates
Three observers now emit probabilities, but each has different missing-data behavior, provenance, runtime, and calibration status.
Question
What is the minimum evidence object a fusion policy needs so it cannot confuse a number with a trustworthy opinion?
Required fields

Who produced it? Which case? What probability? Is it calibrated? Did the observer have evidence? What source did it read? What did it cost? How long did it take?

Why typed
If calibrated or has_evidence is optional prose, a later strategy can forget it. A frozen dataclass makes omission a construction error and prevents mutation after the observer signs the record.
Artifact paths
design/10-architecture-independence.mdci_triage/contracts.pytests/test_contracts.py
Why has_evidence is separate

A placeholder 0.5 can mean “the model genuinely estimates fifty-fifty” or “the observer could not run.” Those states must fuse differently. The record carries both the numeric slot and the evidence flag.

@dataclass(frozen=True)
class EvidenceRecord:
    observer: str
    case_id: str
    probability: float
    calibrated: bool
    has_evidence: bool
    read: str  # the checked-in contract uses a source-description string
    cost_dollars: float
    latency_ms: float

The constructor rejects probabilities outside [0,1], negative cost, negative latency, and an empty read declaration. Absence of evidence is carried explicitly rather than hidden as a neutral-looking probability. All downstream strategies receive exactly three records, one per observer.

Tabular numeric features Sequence prefix heuristic Retrieval failure text EVIDENCE RECORD probability · calibrated has_evidence · reads cost · latency · case_id validated at construction Fusion same input for all strategies
The contract separates observer implementation from decision policy.

A richer LLM prompt might include the raw log, test name, or retrieval neighbors. This experiment deliberately withheld them. Otherwise a better LLM result could come from having more information rather than from better fusion.

How one fusion case is assembled

build_fusion_cases() begins with the Phase 4 feature table and its test identifiers. It holds out square-okhttp for tabular scoring, loads deterministic-test exclusions and cached trusted runs once, and builds the corrected real-text retrieval corpus from that same cache. It then joins the tabular and retrieval populations by test identity.

The label join is defensive. If the Phase 4 label and corrected retrieval label disagree for the same test, construction raises instead of choosing one silently. Only shared, aligned tests enter evaluation. The tabular model is fit on every other project. Sequence evidence uses only the first 30 trusted outcomes. Retrieval leaves the query test out of its own index before finding neighbors.

shared_ids = align_or_raise(phase04_labels, retrieval_labels)
tabular.fit(other_projects)

for test_id in shared_ids:
    tabular_p = tabular.predict(test_features)
    sequence_p = mean(first_30_trusted_outcomes)
    retrieval_p = vote(index_without(test_id), k=5)
    emit(three_evidence_records)

The embedding model is loaded once. Corpus text and all query text are embedded in batches. The earlier implementation loaded the sentence-transformer inside the per-case path, which caused a 120-second smoke run to time out. Batching fixed the execution path without changing the experiment population.

When sequence or retrieval has no evidence, its record carries has_evidence=False and a placeholder probability of 0.5. Every strategy filters on has_evidence. This detail prevents the placeholder from becoming a real vote.

PHASE 10B

Model shared dependencies, not just agreement

Build notebook · Reasoning path
Naive belief
If three observers agree, confidence should increase.
Hidden assumption
Their errors must be able to arise from different sources. Three functions reading one broken parser are three outputs but one piece of evidence.
Dependency audit

Tabular reads the Phase 4 aggregate feature table. Sequence and retrieval both read raw archives after the Phase 5 parser and trust gate. They use different transformations but share an upstream failure domain.

Rule
Agreement counts as independently corroborated only when evidenced observers span at least two dependency groups. No observer may read another observer's output.
Artifact paths
decisions/10-independence.mdtests/test_fusion.py
Evidence later
The Maven parser bug moved sequence, retrieval, fusion, and teacher-corpus conclusions together. That incident validated the need to model shared dependencies.

Count independent failure domains, not the number of boxes in the diagram.

The tabular observer reads the aggregate feature CSV. Sequence and retrieval both depend on raw archives and the trust gate. They may use different representations, but a parser bug can move both together. Two agreeing numbers can therefore be one upstream mistake repeated twice.

Feature CSV Raw archives parser + trust gate Tabular Sequence Retrieval Agreement? count dependency groups, not heads
Sequence and retrieval are separate observers but not fully independent evidence sources.
INDEPENDENT_GROUPS = [
    {"tabular"},
    {"sequence", "retrieval"},
]

An override requires agreement across at least two groups. This is the same principle used in reliable services: replication does not protect against a shared dependency failure.

Check that the numbers estimate the same event. The sequence training task asks whether a future suffix will flip. The fusion assembly shown above uses a mean of binary failures in the first 30 outcomes, a per-run failure fraction. Neither is automatically the same quantity as the tabular score for a test’s observed flakiness label. A test that fails on 1% of runs can still be unequivocally flaky once a flip is observed. Putting all three numbers in a field called probability does not make them comparable. A production contract must name the target event and horizon, then validate any mapping onto a common target before fusion. A calibration flag alone cannot repair a target mismatch.

Why counting votes can count a bug three times

Suppose two sensors each have a 10% error rate. If their errors were independent, both would be wrong on 1% of cases. If a shared parser makes them fail together, their joint error could be 10%. Knowing their individual error rates does not tell you which world you are in.

Interactive 05 · Failure propagation · illustration

Break one upstream dependency

Shared labels
Feature table → tabular
Archive parser + gate
Parser → sequence
Parser → retrieval
Records → fusion → explanation
The two data paths differ, but all observers share the label procedure. Different representations do not establish statistical independence.

Dependency groups are a practical warning system, not a mathematical proof of independent errors. Measure paired mistakes on the same held-out cases. If you learn a combiner, train it on out-of-fold observer predictions rather than their fitted training predictions. Compare against the best individual observer, remove each observer in turn, and keep the population fixed across comparisons.

PHASE 11A

Five strategies, one input

Build notebook · How we arrive at fusion
New situation
For one test, tabular may say 0.15, sequence may have no evidence, and retrieval may say 0.90. The human needs one final probability or an explicit refusal.
Question
How should several bounded observers become one decision without hiding disagreement or paying for an LLM on every case?
Candidate policies

Trust the most confident observer. Average available probabilities. Use fixed threshold rules. Use cheap rules on agreement and escalate disagreement. Or ask an LLM to arbitrate every case.

Fair comparison
Every strategy receives the same three EvidenceRecord objects. The LLM does not receive test name, log text, neighbor text, or ground truth. Otherwise “better fusion” and “more input” become indistinguishable.
Performability gate
Before ranking policies, require at least one aligned case where two evidenced observers differ by 0.5 or more. If every observer repeats one collapsed signal, five policies are five names for one answer.
Artifact paths
design/11-fusion-and-arbiter.mddecisions/11-arbiter-rules.mdci_triage/fusion.pyexperiments/11-fusion-comparison.mdtests/test_fusion.py
Threshold-aware disagreement

Compare 0.49 vs 0.59 with 0.89 vs 0.99. Both gaps are 0.10. At the lab’s 0.5 threshold, the first pair straddles the boundary and the second does not. At the illustrative cost-derived threshold of 0.9091, the second pair straddles it instead. You must specify the action threshold before interpreting disagreement.

Fusion strategies compared in the lab
Strategy Mechanism Main risk
Most confident Choose probability farthest from 0.5 Rewards overconfidence
Mean Average evidenced records only Treats correlated evidence equally
Threshold rules Apply deterministic agreement and abstention rules Rigid at boundary cases
Escalation LLM Use rules normally, call LLM on disagreement Partial coverage, cost, latency
LLM everything Ask the model to fuse every case Same problems on every request

Absolute probability distance is the lab’s disagreement heuristic. It measures numerical separation, not threshold crossing by itself; a decision-aware policy must also check which side of its actual cost-derived threshold each score falls on. The case 0.49 versus 0.59 matters operationally even if a logit-space measure would describe 0.89 versus 0.99 differently.

The LLM sees only the records and must return JSON with a probability and one-sentence reason. Its probability is marked uncalibrated. A 30-second timeout is an execution error. It is not an abstention, because the model never completed a decision.

Bug found during handoffrun_comparison originally counted LLM timeouts as abstentions. That made broken execution look like cautious model behavior. The code and test were corrected; the stored result remains as-run so the reporting bug is not erased.

Why timeout handling reaches the process group

The LLM strategy invokes a command-line client as a subprocess. A normal child-process timeout can kill the direct child while one of its descendants still owns the standard-output pipe. The parent then waits for an end-of-file marker that never arrives. The implementation starts the client in a new session and kills the complete process group on timeout.

proc = subprocess.Popen(
    ["claude", "-p", prompt, "--output-format", "text"],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True,
    start_new_session=True,
)

try:
    stdout, _ = proc.communicate(timeout=30)
except subprocess.TimeoutExpired:
    os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
    proc.communicate()  # reap the process group
    raise TimeoutError("arbiter timed out")

A dedicated test replaces the process with a hanging stub and asserts that killpg receives the process-group identifier and SIGKILL. Another test forces an arbiter failure and checks that the report increments n_errors while leaving n_abstained at zero.

PHASE 11B

What the corrected fusion run can support

Build notebook · Experiment reading procedure
Population
178 square-okhttp cases aligned between Phase 4 truth and the corrected real-text corpus. The disagreement gate passed.
Sample
20 cases selected with seed 0 and roughly balanced ground truth. This bounded paid calls and kept the exact case export reusable.
Local execution
Most-confident, mean, and threshold strategies completed 20 of 20 cases.
LLM execution
Escalation completed 8 of 20. LLM-everything completed 10 of 20. The rest hit the 30-second execution timeout.
What cannot be concluded
Accuracy on an 8-case or 10-case surviving subset cannot be ranked against accuracy on all 20. Timeout changes coverage and may select cases non-randomly.
Artifact paths
artifacts/results/phase11-cases.jsonartifacts/results/fusion.jsonai-ledger/11-fusion-and-arbiter.md

Before comparing scores, compare denominators, coverage, execution errors, cost, and latency.

The corrected square-okhttp export aligned 178 cases and passed the predeclared variation gate. A stratified sample of 20 cases, roughly balanced by ground truth, went through all five strategies.

Corrected own run, with unequal coverage kept visible
Strategy Completed Accuracy on completed subset ECE
Most confident 20 / 20 0.000 1.000
Mean 20 / 20 0.500 0.165
Threshold 20 / 20 0.500 0.500
Escalation LLM 8 / 20 0.625 0.150
LLM everything 10 / 20 0.500 0.220

The two LLM rows cannot be ranked against the complete local rows. Their scored subsets may be easier, harder, or simply different. Twelve escalation calls and ten all-LLM calls timed out. Mean latency for the partial LLM rows was measured in seconds, while local strategies were effectively immediate.

A historical precomputed run over 74 cases showed best-single, weighted, and stacking strategies at 0.905 accuracy. A naive LLM arbiter scored 0.432; a calibrated version improved to 0.784 but still trailed the local methods. That reference is useful context, but it is not the corrected own run.

The valid fusion conclusion is about architecture and failure handling. The corrected experiment does not establish a five-way winner.

Part V / Distillation in detail

Compress explanation behavior, not decision authority

The expensive model is asked to explain the already-fused evidence. It never becomes the only artifact shown to the operator, and a student is trained only if a cheap baseline first proves that the corpus contains learnable signal.

PHASE 12A

Separate the decision layer from the explanation layer

Build notebook · How the distillation question appears
What Phase 11 emits
A probability, verdict, strategy name, evidence records, cost, latency, and sometimes a short reason. This is machine-readable but awkward during an incident.
Human need
The on-call engineer needs a short explanation: which evidence supports the verdict, where observers disagree, and why the system abstained or escalated.
First answer
Call a capable language model with the failure text and detector evidence. Ask it to produce grounded prose and forbid facts absent from the input.
Question created by that answer
Do we want to pay the large-model cost, accept its latency, and depend on its availability for every explanation?
Distillation idea

Use the capable model as a teacher offline. It labels a bounded corpus with verdicts, confidence, and explanations. Train a smaller local student to imitate that explanation behavior. The decision still comes from fusion.

Safety boundary
The explanation may never be the only artifact shown to the human. Raw evidence and fusion output remain visible. A fluent explanation does not gain decision authority.
Artifact paths
design/12-slm-and-the-ledger.mdtools/phase12_prepare.pytests/test_phase12_pipeline.py
Machine output to operator explanation

Raw: tabular=0.14, sequence=no evidence, retrieval=0.92, fusion=ABSTAIN. Explanation: “A similar historical failure points toward flakiness, but the static observer disagrees and no sequence history is available. The system is escalating instead of treating one source as decisive.” The human can compare that sentence with the records.

Evidence 3 records + text Fusion verdict decision source Teacher LLM labels + reasons Corpus JSONL Student only if gate passes operator always sees raw evidence too
Distillation is downstream of the decision and remains auditable against raw evidence.

The teacher prompt receives real failure text and the three evidence records. Ground truth is withheld. The teacher emits a structured label and explanation. The operator-facing output pairs prose with the raw records so a fluent sentence cannot replace its evidence.

This division also limits what a failed student can damage. If the student collapses to one class, the decision layer still exists. Explanation quality can be evaluated, replaced, or removed without changing the triage verdict.

Separate label learning from explanation quality

The pilot asks the teacher to supply both a label and prose. The cheap TF-IDF gate measures label prediction. It does not measure whether an explanation is accurate, complete, useful, or faithful to the fixed fusion verdict. A future student would need a separate explanation evaluation; the current artifact has no such student result.

Start with a deterministic template: “Retrieval found three distinct historical tests; tabular evidence disagrees; the system abstained.” An LLM must improve usefulness beyond that baseline without adding unsupported causes. Require each factual clause to reference an evidence ID. Count unsupported claims, contradictions with the verdict, omitted disagreement, and valid refusals on an independently reviewed sample.

Logs and retrieved text are untrusted input. They may contain secrets or sentences that look like instructions. Redact sensitive material before model calls, delimit evidence, deny tool authority to the explanation component, validate structured output, and check evidence references. An instruction in a stack trace must never become an instruction to the triage service. JSON validity alone cannot establish factual grounding.

PHASE 12B

Do the memory arithmetic before renting a GPU

Build notebook · Reasoning path
Question
If a small student needs fine-tuning, what occupies GPU memory and which engineering technique reduces which term?
Do arithmetic first
For 3 billion parameters in 16-bit storage, weights alone are 3B × 2 bytes = 6 GB. Training also needs gradients, optimizer state, activations, runtime buffers, and temporary tensors.
Technique mapping

LoRA reduces trainable gradients and optimizer state by freezing the base and training adapters. Quantization reduces the base-weight term. Checkpointing reduces saved activations by recomputing them. None is a generic “make training smaller” switch.

Decision
A quantized LoRA-style setup looks plausible on a 24 GB GPU. Plausible is not authorization. The corpus must first beat a cheap text baseline.
Artifact paths
decisions/12-memory-ledger.mdexperiments/12-finetune-vs-tfidf.md
Why optimizer state surprises people

A 3B model may be called a “6 GB model” because its 16-bit weights occupy about 6 GB. Full Adam training can add roughly 6 GB of gradients and 24 GB of moment estimates before counting activations. Model storage and training memory are different budgets.

The project estimated a 3 billion parameter model in 16-bit precision. The point was not to predict one exact peak-memory number. It was to identify which technique attacks which term.

Memory ledger · 3B parameters

Full fine-tuning, before activations

Weights 3B × 2 bytes about 6 GB
Gradients 3B × 2 bytes about 6 GB
Adam moments 3B × 2 states × 4 bytes about 24 GB
Activations batch, length, layers, checkpointing variable
Subtotal before activations and runtime overhead about 36 GB

LoRA freezes the base model and trains small adapters. It removes base-model gradients and optimizer states from the training burden, but it does not make the frozen weights disappear. Quantization reduces the weight term by storing the base model at lower precision. Activation checkpointing reduces stored activations by recomputing them, trading compute for memory.

Full FT: W + G + O + A
LoRA: W + Gadapter + Oadapter + A
Quantized LoRA: Q(W) + Gadapter + Oadapter + Acheckpointed

A rough 4-bit base is about 1.5 GB before quantization metadata and runtime buffers. The project estimated a QLoRA-style job might fit within a 24 GB GPU, but that estimate was never validated by an actual training run because the data gate failed first.

What the memory subtotal leaves out

The 36 GB subtotal assumes 16-bit weights and gradients plus two FP32 Adam moment buffers. Some mixed-precision implementations also maintain FP32 master weights, adding about 12 GB for 3B parameters. Optimizer choices, allocator behavior, attention implementation, sequence length, and temporary buffers change the peak. Decimal GB and binary GiB differ too. Measure peak allocated and reserved memory for the actual configuration.

LoRA represents an update to a weight matrix as a product of two smaller matrices: ΔW = BA, where a rank r much smaller than the matrix dimensions reduces trainable parameters from d × k to r(d + k). The frozen base still participates in the forward pass. The original LoRA paper describes this low-rank adaptation mechanism. Reduced training state does not automatically imply a faster or better model.

PHASE 12C

Measure teacher cost, then make the student earn a GPU

Build notebook · The complete spend and evidence gate
Initial estimate

The rendered prompt was 1,146 characters and 141 words, estimated at roughly 190 to 290 tokens. Using model-list prices, the first paper estimate was about $0.0008 to $0.001 per call. Five hundred teacher examples looked like roughly fifty cents.

Smoke call
One real CLI call cost $0.0174, around 17 to 22 times the estimate. Its usage record showed 7,679 cache-creation input tokens, 17,964 cache-read input tokens, output tokens, and thinking tokens. The CLI session carried far more context than the visible prompt.
Human decision
Do not hide the miss and do not run 500 calls. Reduce the pilot to 60 and use measured cost for the next decision. At the smoke-call rate, 500 calls would be about $8.70.
Historical pilot
60 calls cost $1.297603. The teacher emitted 53 NOT_FLAKY, 7 ABSTAIN, and 0 FLAKY. A one-class teaching signal cannot train a useful detector, so work stopped.
Corrected pilot
After the parser repair, 178 valid upstream cases contained both classes. The bounded 60-case batch cost $1.474191, parsed 60 of 60, and produced 33 FLAKY plus 27 NOT_FLAKY teacher labels against a hidden 30/30 ground-truth sample.
Learnability gate
Split by test identity: 48 train, 12 test. Majority accuracy was 0.6667. TF-IDF plus class-balanced logistic regression also scored 0.6667. The precommitted rule required a win, not a tie.
Final decision
Stop before GPU rental. Keep the paid corpus and JSONL export for audit. Do not claim a student model exists.
Artifact paths
experiments/12-distillation-cost.mdexperiments/12-finetune-vs-tfidf.mdartifacts/results/distill-corpus.jsonartifacts/results/slm.jsonai-ledger/12-slm-and-the-ledger.md

The exact teacher message shape

You are labelling training data for a CI triage explanation model.
Given the real failure evidence and detector evidence below, write a grounded
2-4 sentence explanation for an on-call engineer. Decide whether the test is
FLAKY or NOT_FLAKY. Do not invent facts absent from the evidence.
State confidence between 0 and 1.

Test: {test_fqn}
Failure evidence:
{failure_text}

Detector evidence:
{evidence}

Respond with ONLY this JSON object:
{"explanation": "...", "verdict": "FLAKY" or "NOT_FLAKY",
 "confidence": <float 0-1>}

The true label is used to balance and evaluate the pilot but is not inserted into this message. The teacher must reason from the same failure and detector evidence that would exist at inference time.

Estimated batch cost = calls × estimated cost per call
Observed batch cost = Σ provider-reported costi
500-call projection after smoke = 500 × $0.0174 ≈ $8.70

The initial estimate treated the prompt as the billed request and predicted roughly $0.001 per call. One smoke call cost $0.0174. The CLI carried system prompts, tool definitions, project context, cache operations, and thinking overhead that the rendered user prompt did not show.

The spend correction
Step Estimate or result Decision
Paper estimate about $0.001 per call Plan 500 examples
Smoke call $0.0174, about 17 times higher Cut pilot to 60
Historical 60-call pilot $1.2976, zero FLAKY teacher labels Do not train
Corrected 60-call pilot $1.4742, 33 FLAKY and 27 NOT_FLAKY Run cheap learnability gate

The corrected pilot sampled 60 of 178 valid square-okhttp cases, balanced 30/30 by hidden ground truth. All 60 teacher calls parsed. A group-disjoint split produced 48 training rows and 12 test rows. The student gate was intentionally cheap: TF-IDF over unigrams and bigrams with a class-balanced logistic regression.

0.6667Train-majority baseline
0.6667TF-IDF held-out accuracy
$1.4742Corrected teacher batch

The rule said TF-IDF had to beat the majority baseline before any GPU work. It tied. The stop condition fired. No pod was rented, no LoRA job ran, and no student result exists for the corrected corpus.

A separate historical fine-tune reference had collapsed to predicting FLAKY on every example. It matched a majority baseline on one majority-FLAKY slice and scored zero on a deterministic slice. This is evidence about that historical run, not a substitute result for the corrected pipeline.

The useful outcomeThe distillation phase saved money by failing early. A complete corpus and a stopped GPU plan are more credible than an invented student score.

The corpus pipeline has its own safety boundary

Importing phase12_prepare.py does not call a teacher or start a GPU job. Running it without --run-teacher validates the source cases and exits. The paid path is therefore an explicit command-line action rather than an accidental side effect of a test or import.

# Read and validate the Phase 11 export
rows = load_cases("phase11-cases.json")

# Choose 30 + 30 using hidden ground truth
pilot = balanced_cases(rows, limit=60, seed=0)

# Paid operation, allowed only by an explicit flag and budget
labelled, spend = label_with_teacher(
    pilot,
    model="claude-haiku-4-5-20251001",
    budget_usd=2.0,
)

# Keep test identities disjoint
train, test = balanced_group_split(labelled, test_fraction=0.2)

# Gate before GPU work
result = run_baseline(train, test)

The loader requires a case identifier, ground-truth label, real failure text, and Phase 11 observer records. It rejects conflicting labels for a repeated identity and rejects a corpus that does not contain both classes. The teacher never receives the hidden label. It sees the test identity, real failure evidence, and normalized observer lines containing probability, calibration state, and evidence availability.

The response parser accepts only FLAKY or NOT_FLAKY, checks confidence is between zero and one, requires an explanation, and preserves the provider's actual cost field. This last field matters because token estimates had already failed. The batch loop stops admitting calls when accumulated spend reaches the budget. This is a soft admission limit: the last admitted call can take the total over budget. A hard cap would need a conservative reservation for each call and a provider-side limit where available.

Why the split follows test identity

Repeated examples from one test could share package names, exception text, and detector patterns. If one landed in train and another in test, the baseline could memorize the test family. balanced_group_split() holds the complete test_fqn on one side and asserts that both labels remain in both partitions.

The final JSONL files retain prompts, evidence, teacher label, confidence, explanation, token counts, and cost. They are audit records as much as training data. If the student later produces a suspicious explanation, the team can trace it to the teacher example rather than guessing what the model absorbed.

Part VI / AI-native engineering method

Give the model work, keep judgment accountable

AI assistance increased the speed of implementation and inspection. It did not remove the need to decide what the system is allowed to do, what counts as evidence, or when an experiment no longer supports further spend.

18

The human and AI division of work

Who owned what in this build
Human-owned judgment AI-assisted execution
Set the system boundary and retain the final action Draft contracts, functions, tests, and experiment harnesses
Choose and challenge the cost table Compute metrics and inspect returned artifacts
Decide which deployment question a split should answer Implement grouped and stratified folds
Precommit keep, discard, and spend gates Train controls, models, and calibration layers
Reject flattering interpretations and scope changes Search logs, trace parser behavior, and propose fixes
Stop paid work when the gate fails Prepare teacher prompts, parse outputs, and emit JSONL

The collaboration used a repeated loop:

Human defines the question → AI implements a bounded slice → experiment tries to falsify it → human reviews the evidence → tests preserve the decision

The AI also made mistakes. It proposed redundant metrics, invented an attribution before checking the real record, wrote docs that claimed unimplemented behavior, undercounted teacher cost, and initially treated a timeout as abstention. These errors became useful only because the workflow preserved design records, experiment predictions, result artifacts, and rejected proposals.

19

One parser bug can become four false discoveries

The most important lesson in the repository appeared late. The Maven parser dropped legitimate failures in a multi-module project. Four phases then produced separate-looking conclusions from the same damaged evidence.

Parser bug failures disappear Sequence: no signal Retrieval: collapse Fusion: no variation Teacher: no positives CORRECTION restore parser · rerun gates preserve historical artifacts separate corrected claims
Four apparent model findings were partly one data-path failure repeated four times.

After the parser repair, square-okhttp produced a two-class real-text retrieval corpus, a fusion population with variation, and a teacher pilot with both classes. The sequence observer still failed its own control, but the other three conclusions changed materially.

This is why an AI-native build needs provenance beyond a final notebook. A result must retain which parser, gate, corpus, split, and execution path produced it. When an upstream assumption changes, the team needs to know which downstream claims expire together.

Preserved as historical

Earlier results remain evidence of the failure mode and the reasoning at that time.

Reported as corrected

New runs are labeled by scope, population, coverage, and whether paid components completed.

Beyond the lab / Proposed design, not implemented infrastructure

Turn the experiment into a service with a memory

The laboratory can run files through functions. A service must also survive duplicate build events, missing artifacts, stale indexes, overloaded reviewers, model failures, and changes to the parser. The design below is an extension of the project. These queues, stores, and monitors are not claimed as shipped features.

Two paths: online decisions and delayed evidence
Online · bounded by the incident deadline
Build eventVerify source; deduplicate
Artifact snapshotHash + parser version
Trust gateReject / unknown / admit
ObserversIndependent reads; time budgets
PolicyVersioned loss + fallback
Review UIVerdict + evidence + status
Offline · delayed truth, no automatic feedback into a live verdict
AdjudicationOutcome + uncertainty
EvaluationFrozen cohorts + baseline
Candidate versionShadow → review → promotion

Walk one incident through that design

At 02:47 an event arrives for build B-184, commit c7a, and our checkout test. The service assigns a stable request key from repository, commit, build attempt, and test identity. A duplicate delivery finds the existing request. A deliberate rerun has a different attempt ID; deduplication must not erase it.

Raw artifacts are saved immutably with hashes. The parser writes a derived snapshot with its version. If the log is truncated, the run becomes UNKNOWN and routes to artifact repair or review. It does not fabricate passing outcomes for missing test lines.

For an admitted run, each observer receives only its allowed inputs as of 02:47. Tabular emits a score, retrieval emits neighbors and a raw vote, and the history observer either supplies its approved baseline or records unavailable evidence. The discarded GRU does not return through a production diagram simply because there is an empty box.

The policy validates case IDs, score ranges, timestamps, and calibration metadata before computing a result. An optional explanation runs after the verdict. If that call fails, the UI still displays the verdict and structured evidence. If a required decision component fails, the UI displays degraded status and the designated review route.

Interactive 06 · Incident walkthrough · illustration

Follow B-184 from red build to review

1 · Receive2 · Gate3 · Observe4 · Decide5 · Explain6 · Learn
02:47 · Receive B-184. Freeze commit c7a, build attempt, artifact hash, and decision-time cutoff.

Advance at your own pace. The numbers and incident identity are invented to make the proposed design concrete.

Make the evidence record sufficient for replay

// Proposed extension; not the current Python dataclass
{
  "case_id": "repo/c7a/B-184/attempt-1/CheckoutTest.appliesDiscount",
  "as_of": "2026-09-07T02:47:00+05:30",
  "artifact_hash": "sha256:…",
  "parser_version": "parser-v2",
  "gate_version": "gate-v1",
  "observer_version": "retrieval-v3",
  "index_snapshot": "history-before-B-184",
  "label_definition": "observed-flip-v1",
  "calibration_version": null,
  "status": "completed",
  "has_evidence": true,
  "probability": 0.6,
  "policy_version": "review-policy-v1"
}

Keep provenance structured when downstream code needs to compare it. A free-text read field is useful for humans but cannot reliably enforce freshness or dependency rules. A Boolean calibrated says little without the calibration population, method, version, and validation date. Store the original verdict and the later correction as separate events.

Failure semantics in the proposed service
StateMeaningResponse
AbstainedCompleted but policy refused a verdictReview with reason and available evidence
Unknown inputArtifact cannot establish what ranRepair ingestion or review; preserve raw file
Execution errorRequired component did not finishMark degraded; apply explicit fallback
Stale evidenceSnapshot violates freshness contractRecompute or refuse; never silently reuse
Human overrideOperator chose another actionRecord reason; do not treat it as ground truth

Budget time, capacity, and the next observation

If independent observers run concurrently, their critical path is approximately the slowest observer, plus ingestion, policy, and queue overhead. If an arbiter runs only after disagreement, its time adds to those requests. Measure end-to-end p50 and p95 latency on the actual traffic mix; summing component p95 values does not produce a reliable service p95.

Suppose 100 incidents arrive per hour and the policy sends 25% to review. At ten minutes per review, that is 250 minutes of work per hour. Three reviewers provide only 180 minutes. The queue grows even if the model’s accepted-case score looks excellent. Review capacity belongs in the policy evaluation.

A rerun is an experiment that may change the next decision. Its value is the current minimum expected loss minus the expected minimum loss after seeing the rerun result, minus rerun cost and delay. Estimating that requires a model of how rerun outcomes relate to causes; a pass is not an automatic pardon. This is the bridge from classification to sequential decision-making.

Derivation: the value of another observation

Let R*(x) = mina Σy P(y|x)L(a,y) be the lowest expected loss with today’s evidence. A proposed experiment produces outcome z. Its expected value before cost is R*(x) − Σz P(z|x)R*(x,z). Run it only when this exceeds its compute cost, human effort, and cost of delay, subject to operational constraints.

Updating the cause estimate requires a likelihood: P(y|x,z) ∝ P(z|y,x)P(y|x). Without knowing how likely a passing rerun is under each candidate cause, “it passed on retry” does not tell you how far to move your belief. Under a correct model and freely optional use of new evidence, information cannot worsen the optimal decision before acquisition cost: you can ignore it. In practice, wrong likelihoods, delayed evidence, and changes to the environment can invalidate that idealization.

Roll out against the current workflow

Start in shadow mode: record what the service would recommend while the existing process retains authority. Compare review time, harmful recommendations, refusal rate, execution failures, and total cost per incident. Then trial the advisory UI on a bounded cohort with a rollback to the prior workflow. Monitor by project and gate outcome; aggregate averages can hide one broken customer.

Feedback is selectively observed: cases routed to review get more investigation than dismissed cases. Audit a sample across decision categories, with human authorization and appropriate operational controls. Retraining only on reviewed cases would teach the model from a population selected by its own previous policy.

Use the method on your next project

Generalize the questions, then choose the components

The reusable part is the dependency between decisions: action defines loss; loss defines evaluation; evaluation requires labels; labels require a trustworthy observation process; only then can a model earn a role. Another domain may need one observer and no LLM. Copying all three observers would copy the lab’s shape without its reasoning.

The same design questions in three domains · illustrative applications
QuestionCI triageSupport routingInvoice review
What happens next?Investigate or rerunAssign team or ask for detailApprove for review or flag mismatch
What can be wrong?Dismiss a defectDelay an urgent customer issueMiss a duplicate or block a valid invoice
Who creates truth?Reruns + incident adjudicationResolved case + corrected routingReconciled invoice + reviewer evidence
What can leak?Future fix or rerunsFinal team in ticket textPost-review approval field
What split matters?Future build / new projectFuture tickets / new accountFuture invoices / unseen vendor
What should refuse?Missing log or stale historyMissing request contextUnreadable amount or missing purchase order

A design brief you can fill in before asking AI to code

Person and moment: Who needs to decide, and by when?
Allowed actions: What can change in the world? Who owns it?
Prediction target: What exactly does y mean? What does it exclude?
Loss table: For each truth/action pair, what consequence follows?
Observation: How is y established, and which cases go unobserved?
As-of inputs: What is actually available at the decision time?
Evaluation: Which unit is held out, and what deployment claim follows?
Baseline: What is the simplest existing policy to beat?
Refusal: Which missing evidence or unsupported population blocks a verdict?
Experiment: What result would make us keep, discard, or stop?
Operations: What happens on timeout, duplicate events, or stale state?
Evidence: Which immutable artifacts let another person inspect the claim?

Use AI to make a falsifiable slice concrete

Give the assistant a narrow contract and ask it to expose assumptions before implementation. Supply the relevant schema and a small redacted fixture. Its response should include the proposed code, an independent test case, a command to run, and a result artifact. A plausible explanation is not evidence that the command ran.

You are implementing one CI-triage evaluation slice.
Target: y=1 means observed flaky behavior, not current defect cause.
Inputs: probabilities, binary labels, and stable case IDs.
Illustrative loss: false positive=5000, false negative=500.
Do not alter labels, expand scope, call paid APIs, or select a model.

First, derive the 2x2 confusion table in plain language.
Then implement the evaluator and reject invalid probabilities.
Hand-compute this fixture before using a metrics library:
labels = [0, 0, 1, 1]; predictions = [1, 0, 0, 1].
Expected FP=1, FN=1, TP=1, TN=1; cost per case=1375.
Add a second fixture with unequal FP and FN counts to catch swaps.
Report commands actually run, outputs, and any untested assumptions.

The first fixture checks the arithmetic but cannot catch reversed costs because FP and FN are equal. A second fixture with two false positives and zero false negatives must cost 10,000/4 = 2,500 across four cases. This is why the human should supply an independent oracle rather than ask the same assistant to validate its own assumptions.

A useful review prompt after the implementation

“Try to falsify this result. Trace one raw row through parsing, joining, feature construction, prediction, and scoring. Identify fields unavailable at prediction time. Check whether repeated identities cross the split. State what the reported number cannot establish. Do not change the acceptance criterion to fit the result.”

Keep a short decision ledger: hypothesis, expected result, actual output, interpretation, and next action. When a parser changes, ask AI to enumerate downstream artifacts that depend on it. Recompute those artifacts under new version IDs. Retain prior outputs as historical evidence rather than editing numbers inside old JSON.

When should an LLM become part of the product?

First use AI to help build and inspect the system. A runtime model adds a different dependency: provider availability, response variability, cost, latency, and exposure to untrusted text. It earns that role only through a comparison on the same eligible cases against a cheap alternative. Explanations can start as templates; arbitration can start as explicit rules. Add the model where the measured benefit exceeds its operating burden.

Check your understanding

A test is 95% likely to be flaky. Can you release?

The historical flakiness estimate does not resolve today’s failure cause or the release’s other defects. Even the simplified cost derivation only compares two actions for one incident under stated assumptions. Inspect the incident evidence, loss model, and release policy. Never average test scores into a release authorization.

Three observers agree after a parser change. What should you check first?

Whether they read artifacts derived from the same parser, whether the labels are shared, and whether cached histories or indexes were rebuilt consistently. Agreement is weak corroboration when a single upstream error explains all three outputs.

The model has 99% accuracy but abstains on 90% of traffic. What is missing?

Its denominator, execution coverage, accepted-case mix, missed-defect count, review load, and total cost across all arrivals. The accepted 10% might be trivial. Compare the complete workflow with the current baseline.

The teacher’s explanation sounds correct. What would make it evidence?

Each factual claim must be supported by the recorded inputs, and the explanation must preserve disagreement and the actual verdict. Review it against independent adjudication where available. Fluency, valid JSON, and the teacher’s own confidence are insufficient.

Sources and claim boundaries

What is measured, derived, and proposed

Measured in the Week 4 artifact: dataset joins, observer metrics, corrected fusion completion counts, teacher spend, and the baseline tie. These are stored experimental results, not a fresh model-training run for this revision. Historical, corrected, and reference experiments remain separate.

Derived in this article: the two-state cost threshold, rerun-observation probabilities, base-rate illustration, and queue-capacity example. Their assumptions appear beside the arithmetic. Proposed: the production service, replay schema, monitoring, and AI implementation brief. These are guidance for extending the project, not claims about existing infrastructure.

  1. Alshammari, Morris, Hilton, and Bell: FlakeFlagger dataset, Zenodo 4450723. Source data and attribution; CC BY 4.0. The linked dataset description reports its study population; this article’s row and project counts refer to the local files and joins and should not be conflated with that study summary.
  2. Scikit-learn: tuning the decision threshold. Separating statistical prediction from the action rule.
  3. Scikit-learn: probability calibration. Reliability diagrams, held-out calibration, and interpretation of probability scores.
  4. Hu et al.: LoRA—Low-Rank Adaptation of Large Language Models. The adapter mechanism behind the memory discussion.
20

How to read and reproduce the repository

Read the project in dependency order rather than filename order:

  1. PROBLEM.md for the incident, boundary, and cost assumptions.
  2. design/00 through design/13 for slice responsibilities and explicit gaps.
  3. decisions/ for the choices that constrain implementation.
  4. experiments/ for predictions written before results.
  5. ci_triage/ and tests/ for the actual mechanism and invariants.
  6. artifacts/results/ for executed outputs, including partial coverage.
  7. ai-ledger/, KNOWNS.md, and FAILURES.md for rejected proposals, corrections, and handoff risks.

The core dependency chain is:

Boundary → cost → evaluator → label → ingestion → trust gate → split → observers → evidence contract → fusion → explanation → handoff

A practical rerun should begin with the cheap invariants and local strategies. Paid teacher calls and GPU work need separate budget gates. The checked-out artifact used Python 3.12 dependencies including pandas, scikit-learn, PyTorch, sentence-transformers, pytest, and the Anthropic client. The result files are the evidence for the numbers in this article. They should not be rewritten to make corrected code and historical output look artificially consistent.

Current verification boundaryThis revision checked selected implementation paths and stored results against the Week 4 artifact, and checked the added theory against the linked primary sources. The website build and article interactions were validated separately. No fresh model-training run or full Python project test run is claimed; new examples are explicitly labeled as illustrations.

The finished artifact is not one model. It is a chain of contracts that makes weak evidence, failed experiments, partial execution, and human judgment visible.

Learn to build systems that can disagree with their own results.

Antern's AI-Native Engineering Sprint teaches the engineering around the model: problem boundaries, evidence, evaluation, orchestration, failure handling, cost, and human review.

Apply to Antern