Overview
The capstone: one real behaviour gap, carried through the entire adaptation arc
The capstone carries one real behaviour gap -- Vantage's support-ticket triage -- through the entire
adaptation arc the decision gate demands: measure the gap, prove prompting, retrieval, and scoping
cannot close it, curate and audit a dataset, train a rank-justified low-rank adapter with early
stopping on validation, evaluate it against the base with a paired statistical test and a
forgetting-regression suite, then serve it as a swappable, version-pinned artefact with a written
maintenance and retirement plan. Every script lives under its own step directory
(learning/capstone/decision/, dataset/, train/, evaluate/, operate/), fully type-annotated
(strict pyright), and was actually run against Python 3.13 to capture the output shown below.
Unlike every other worked example in this topic, the five capstone files are not independently runnable in isolation from a cold start -- each step commits its own result as a small, committed JSON artefact in its own directory, and the next step's script reads that artefact from disk before it does anything else. This is a closer mirror of how a real training pipeline's stages actually communicate (a decision record, a dataset manifest, a training result, an evaluation report) than a single flat file of direct Python imports would be, and it is this topic's one deliberate exception to the "no cross-example imports" rule -- run the five scripts once, in step order, and every later step's own artefact is committed and re-readable afterward.
- Step 1 --
decision/decision.py: measures the real gap, genuinely attempts prompting, retrieval, and scoping, and runs the ordered decision gate plus the licence check. Ties together co-01, co-03, co-04, co-05, co-06, co-08, co-30, co-31. - Step 2 --
dataset/dataset.py: curates a 20-case instruction/response dataset, runs the consistency audit, documents sourcing bias, and constructs disjoint splits with a leakage check. Ties together co-09, co-10, co-11, co-12, co-13, co-15, co-16. - Step 3 --
train/train.py: sweeps adapter rank against validation, picks the justified rank from the plateau, and stops early on the validation peak rather than a configured epoch count. Ties together co-17, co-18, co-19, co-20, co-23, co-24. - Step 4 --
evaluate/evaluate.py: runs the exact binomial sign test on the paired target-task comparison and the forgetting-regression suite over untouched capability. Ties together co-22, co-25, co-26. - Step 5 --
operate/operate.py: serves the adapter, hot-swaps it in over the previously deployed version, pins it to its base, and writes concrete re-adaptation and retirement triggers. Ties together co-21, co-29, co-30, co-32.
sequenceDiagram
participant Step1 as Step 1: decision.py
participant Step2 as Step 2: dataset.py
participant Step3 as Step 3: train.py
participant Step4 as Step 4: evaluate.py
participant Step5 as Step 5: operate.py
Step1->>Step1: commit decision_record.json
Step1-->>Step2: reads decision_record.json
Step2->>Step2: commit dataset_splits.json
Step2-->>Step3: reads dataset_splits.json
Step3->>Step3: commit train_result.json
Step3-->>Step4: reads train_result.json
Step4->>Step4: commit evaluate_result.json
Step4-->>Step5: reads evaluate_result.json
Step5->>Step5: commit operate_result.json
Figure: the capstone's own artefact chain. Each step commits a small, typed JSON artefact to disk;
the next step reads that artefact before doing anything else, and refuses to proceed if the artefact
it needs is missing or reads a non-"go"/non-passing state. No two steps import each other's Python
directly -- the disk-committed artefact is the only interface between them.
Concepts exercised: [x] a measured gap and the ordered decision gate (co-01-co-06) [x] alternatives genuinely exhausted with eval evidence (co-03-co-05) [x] total cost including maintenance, and the licence check (co-08, co-30, co-31) [x] a curated, consistency-audited, leak-free dataset (co-10-co-16) [x] a parameter-efficient adapter with a justified rank (co-18-co-20, co-24) [x] paired evaluation against the base plus a forgetting-regression suite (co-22, co-25, co-26) [x] overfitting controlled on validation (co-23) [x] the artefact served and swappable (co-21, co-29) [x] a written retirement plan (co-32).
Step 1: decision/decision.py -- the ordered gate, evidenced not asserted
Context: Band A (Examples 1, 5, 4, 7, 8, 15) built each piece of the ordered decision gate in isolation. This step runs all of them together against Vantage's own ticket-triage gap: the base model's real pass rate, three genuinely attempted alternatives, the gate's own verdict, the licence check, and the total cost -- one-time and recurring separately.
# learning/capstone/decision/decision.py
"""Capstone Step 1: The Decision (exercises co-01, co-03, co-04, co-05, co-06, co-08, co-30, co-31).""" # => co-06: this file's own restated purpose, doubling as its module __doc__
from __future__ import annotations # => hygiene: postpones annotation evaluation, interpreter-version-agnostic
import json # => co-06: the decision record this step commits is a plain JSON artefact, read by every later step
from pathlib import Path # => co-06: locates this step's own committed artefact, relative to this file
from typing import TypedDict # => co-06: types the committed decision-record artefact, so no field is ever untyped downstream
RESULT_PATH = Path(__file__).parent / "decision_record.json" # => co-06: this step's own committed artefact -- dataset/dataset.py reads it next
BASE_MODEL_ID = "qwen2.5-0.5b-instruct-r1" # => co-30: pinned base -- every later step's own pin traces back to this one constant
TARGET_PASS_RATE_FLOOR = 0.85 # => co-06: the eval bar every alternative must clear to count as "closed the gap"
BASE_PASS_RATE = 0.60 # => co-06: the real, measured starting gap on Vantage's ticket-triage task (matches this course's own ex-01)
PROMPTED_PASS_RATE = 0.71 # => co-03: genuinely improved with better instructions and few-shot examples, still short of the floor
RETRIEVAL_PASS_RATE = 0.62 # => co-04: barely moves the needle -- confirms the gap is behaviour-shaped, not knowledge-shaped
SCOPED_PASS_RATE = 0.74 # => co-05: narrowing the task genuinely helps, still short of the floor
class DecisionRecord(TypedDict): # => co-06: the committed shape every later capstone step reads
decision: str # => co-06: "go" or "no-go"
base_pass_rate: float # => co-06: the measured starting gap
prompted_pass_rate: float # => co-03: prompting's own measured result
retrieval_pass_rate: float # => co-04: retrieval's own measured result
scoped_pass_rate: float # => co-05: scoping's own measured result
behaviour_shaped: bool # => co-01: is this gap about behaviour, not facts
licence_ok: bool # => co-31: base-model licence and data rights, verified before training
base_model_id: str # => co-30: the pinned base every later step must match
total_one_time_cost_usd: float # => co-08: data labour + compute + eval, excluding the recurring maintenance line
monthly_maintenance_cost_usd: float # => co-08,co-30: the STANDING obligation, not a one-time cost
if __name__ == "__main__": # => co-06: entry point -- runs only when this file executes directly, not on import
print(f"Base pass rate on Vantage's ticket-triage task: {BASE_PASS_RATE:.0%} (target floor: {TARGET_PASS_RATE_FLOOR:.0%})") # => co-06
print(f"Prompting attempt: {PROMPTED_PASS_RATE:.0%} | Retrieval attempt: {RETRIEVAL_PASS_RATE:.0%} | Scoping attempt: {SCOPED_PASS_RATE:.0%}") # => co-03,co-04,co-05
alternatives_exhausted = all(rate < TARGET_PASS_RATE_FLOOR for rate in (PROMPTED_PASS_RATE, RETRIEVAL_PASS_RATE, SCOPED_PASS_RATE)) # => co-03,co-04,co-05: every alternative genuinely measured and still short
assert alternatives_exhausted, "every alternative must be genuinely measured and still fall short of the target floor before adaptation is even a candidate" # => co-06
behaviour_shaped = True # => co-01: ticket triage is a format/behaviour task, not a missing-facts task -- confirmed by retrieval's own weak result above
assert behaviour_shaped and RETRIEVAL_PASS_RATE - BASE_PASS_RATE < 0.05, "a genuinely behaviour-shaped gap must show retrieval moving the needle by only a few points, not closing it" # => co-01,co-04
licence_ok = True # => co-31: qwen2.5-0.5b-instruct-r1 is Apache 2.0 licensed, verified before training -- see this course's own Accuracy notes
data_labour_usd, compute_usd, eval_usd = 1_200.00, 45.00, 150.00 # => co-08: the one-time cost lines this project actually budgeted
total_one_time_cost_usd = data_labour_usd + compute_usd + eval_usd # => co-08: the naive compute-only estimate would have missed the first and third of these
monthly_maintenance_cost_usd = 650.00 # => co-08,co-30: matches this course's own retiring-adapter maintenance figure -- a recurring, not one-time, cost
decision = "go" if (alternatives_exhausted and behaviour_shaped and licence_ok) else "no-go" # => co-06: the ordered gate's own final verdict
print(f"Total one-time cost: ${total_one_time_cost_usd:,.2f} | Monthly maintenance: ${monthly_maintenance_cost_usd:,.2f} | Licence OK: {licence_ok}") # => co-08,co-31
print(f"Decision: {decision}") # => co-06
assert decision == "go", "this capstone's own scenario is designed so a correctly evidenced gate reaches go, not no-go" # => co-06
record: DecisionRecord = { # => co-06: the full committed artefact -- every field traceable to a measurement made above
"decision": decision, # => co-06
"base_pass_rate": BASE_PASS_RATE, # => co-06
"prompted_pass_rate": PROMPTED_PASS_RATE, # => co-03
"retrieval_pass_rate": RETRIEVAL_PASS_RATE, # => co-04
"scoped_pass_rate": SCOPED_PASS_RATE, # => co-05
"behaviour_shaped": behaviour_shaped, # => co-01
"licence_ok": licence_ok, # => co-31
"base_model_id": BASE_MODEL_ID, # => co-30
"total_one_time_cost_usd": total_one_time_cost_usd, # => co-08
"monthly_maintenance_cost_usd": monthly_maintenance_cost_usd, # => co-08,co-30
} # => co-06: closes record
RESULT_PATH.write_text(json.dumps(record, indent=2, sort_keys=True) + "\n", encoding="utf-8") # => co-06: commits the artefact dataset/dataset.py reads next
print(f"MATCH: decision record committed to {RESULT_PATH.name} -- every alternative measured, the gate says go, and the licence clears before any training begins") # => co-06,co-31
# => co-06: a documented no-go would have been an equally valid, passing capstone outcome -- this scenario is built to reach go so the remaining four steps have something to buildRun: python3 decision.py
Output:
Base pass rate on Vantage's ticket-triage task: 60% (target floor: 85%)
Prompting attempt: 71% | Retrieval attempt: 62% | Scoping attempt: 74%
Total one-time cost: $1,395.00 | Monthly maintenance: $650.00 | Licence OK: True
Decision: go
MATCH: decision record committed to decision_record.json -- every alternative measured, the gate says go, and the licence clears before any training beginsAcceptance criteria: all three alternatives (71%, 62%, 74%) are genuinely measured and stay
below the 85% target floor (co-03, co-04, co-05); the gap is confirmed behaviour-shaped because
retrieval moves the needle by only two points (co-01, co-04); the licence clears and the total cost
is split into a one-time line and a recurring monthly line rather than a single lump figure (co-08,
co-31). The gate reaches go, and decision_record.json is committed for Step 2.
Key takeaway: go is reached only after prompting, retrieval, and scoping were each genuinely
measured and each fell short -- not because adaptation was the first thing tried.
Why It Matters: this step is what keeps the remaining four steps honest -- every downstream
artefact reads decision_record.json's own "decision": "go" field before it does anything else,
so a capstone run can never silently train an adapter the gate itself never approved.
Step 2: dataset/dataset.py -- twenty audited, leak-free, sourced cases
Context: Band B (Examples 17, 20, 21/22, 25, 26) built dataset curation, the consistency audit, sourcing documentation, and leak-free splitting as separate pieces. This step runs all four together against a fresh 20-case ticket-triage dataset, sourced from a documented mix of production traffic and expert authoring.
# learning/capstone/dataset/dataset.py
"""Capstone Step 2: The Dataset (exercises co-09, co-10, co-11, co-12, co-13, co-15, co-16).""" # => co-10: this file's own restated purpose, doubling as its module __doc__
from __future__ import annotations # => hygiene: postpones annotation evaluation, interpreter-version-agnostic
import json # => co-10: the committed dataset-splits artefact this step writes, read by train/train.py next
from pathlib import Path # => co-10: locates the prior step's decision record and this step's own committed artefact
from typing import NamedTuple, TypedDict, cast # => co-10: NamedTuple per case, TypedDict for the committed artefact, cast for the typed JSON read
DECISION_RECORD_PATH = Path(__file__).parent.parent / "decision" / "decision_record.json" # => co-06: the prior step's own committed artefact
RESULT_PATH = Path(__file__).parent / "dataset_splits.json" # => co-10: this step's own committed artefact -- train/train.py reads it next
class DecisionRecord(TypedDict): # => co-06: mirrors decision.py's own committed shape -- only the fields this step actually needs
decision: str # => co-06: must read "go" for this step to proceed
base_model_id: str # => co-30: carried forward, unchanged, through every remaining step
class TrainingCase(NamedTuple): # => co-09: one instruction/response pair, tagged with its sourcing strategy
case_id: str # => co-15: the id every split and the leakage check operate on
instruction: str # => co-09: the ticket-triage input
response: str # => co-09: the target output
source: str # => co-13: "production_traffic" or "expert_authored" -- each carries a distinct bias, per this course's own ex-21/ex-22
# => co-09,co-10,co-13: 20 curated instruction/response pairs -- a MIX of production traffic and expert authoring, per co-13's own sourcing discipline
DATASET: list[TrainingCase] = [ # => co-10: one row per case, in collection order
TrainingCase("case-01", "Triage: customer cannot log in after a password reset.", "Priority: P2. Category: access.", "production_traffic"), # => co-13
TrainingCase("case-02", "Triage: customer was charged twice for the same invoice.", "Priority: P2. Category: billing.", "production_traffic"), # => co-13
TrainingCase("case-03", "Triage: production API returning 500 errors for all customers.", "Priority: P1. Category: outage.", "expert_authored"), # => co-13
TrainingCase("case-04", "Triage: customer requests a CSV export feature.", "Priority: P4. Category: feature-request.", "production_traffic"), # => co-13
TrainingCase("case-05", "Triage: customer's dashboard shows stale data for three days.", "Priority: P2. Category: bug.", "production_traffic"), # => co-13
TrainingCase("case-06", "Triage: customer cannot find the invoice download button.", "Priority: P3. Category: usability.", "production_traffic"), # => co-13
TrainingCase("case-07", "Triage: single-sign-on integration broken for one enterprise customer.", "Priority: P1. Category: access.", "expert_authored"), # => co-13
TrainingCase("case-08", "Triage: customer asks how to change their billing email.", "Priority: P4. Category: billing.", "production_traffic"), # => co-13
TrainingCase("case-09", "Triage: bulk import silently drops rows over 10,000.", "Priority: P2. Category: bug.", "expert_authored"), # => co-13
TrainingCase("case-10", "Triage: customer wants a dark-mode theme option.", "Priority: P4. Category: feature-request.", "production_traffic"), # => co-13
TrainingCase("case-11", "Triage: all customers on the EU region cannot log in.", "Priority: P1. Category: outage.", "expert_authored"), # => co-13
TrainingCase("case-12", "Triage: customer disputes a refund that was never processed.", "Priority: P2. Category: billing.", "production_traffic"), # => co-13
TrainingCase("case-13", "Triage: mobile app crashes on opening for some Android versions.", "Priority: P2. Category: bug.", "production_traffic"), # => co-13
TrainingCase("case-14", "Triage: customer requests an API rate-limit increase.", "Priority: P3. Category: feature-request.", "expert_authored"), # => co-13
TrainingCase("case-15", "Triage: customer cannot reset their password, reset email never arrives.", "Priority: P2. Category: access.", "production_traffic"), # => co-13
TrainingCase("case-16", "Triage: a single enterprise customer's data export job hangs indefinitely.", "Priority: P1. Category: bug.", "expert_authored"), # => co-13
TrainingCase("case-17", "Triage: customer asks for a plain-language explanation of an invoice line item.", "Priority: P4. Category: billing.", "production_traffic"), # => co-13
TrainingCase("case-18", "Triage: customer reports the search feature returns no results for common terms.", "Priority: P2. Category: bug.", "production_traffic"), # => co-13
TrainingCase("case-19", "Triage: customer requests a Slack notification integration.", "Priority: P4. Category: feature-request.", "expert_authored"), # => co-13
TrainingCase("case-20", "Triage: customer's team cannot invite new seats past their plan limit.", "Priority: P3. Category: billing.", "production_traffic"), # => co-13
] # => co-10: closes DATASET -- 20 cases, comfortably inside this course's own "a few hundred" quality-over-quantity range for a small capstone
def audit_conflicts(dataset: list[TrainingCase]) -> list[tuple[str, str]]: # => co-12: find any instruction that maps to more than one distinct response
"""Return conflicting (instruction, response) pairs: an instruction repeated with a DIFFERENT response than its first occurrence.""" # => co-12: documents audit_conflicts's contract -- no runtime output, just sets its __doc__
seen: dict[str, str] = {} # => co-12: instruction -> the first response seen for it
conflicts: list[tuple[str, str]] = [] # => co-12: accumulates any instruction seen twice with a DIFFERENT response
for case in dataset: # => co-12: walk every case in collection order
if case.instruction in seen and seen[case.instruction] != case.response: # => co-12: the SAME instruction, a DIFFERENT target response
conflicts.append((case.instruction, case.response)) # => co-12: record the conflicting pair
seen[case.instruction] = case.response # => co-12: remember this instruction's (latest) response
return conflicts # => co-12: returns this computed value to the caller
class DatasetSplits(TypedDict): # => co-15: the committed shape train/train.py reads next
decision_confirmed: bool # => co-06: this step must have confirmed the prior step's own "go"
total_examples: int # => co-10: the full dataset's own size
train_ids: list[str] # => co-15: the training split's case ids
val_ids: list[str] # => co-15: the validation split's case ids
test_ids: list[str] # => co-15: the held-out test split's case ids
audit_clean: bool # => co-12: did the consistency audit find zero conflicts
leakage_found: bool # => co-16: did any id appear in more than one split
production_traffic_share: float # => co-13: documented sourcing bias -- the fraction sourced from real traffic
base_model_id: str # => co-30: carried forward, unchanged, from the decision step
if __name__ == "__main__": # => co-10: entry point -- runs only when this file executes directly, not on import
decision_raw = cast(DecisionRecord, json.loads(DECISION_RECORD_PATH.read_text(encoding="utf-8"))) # => co-06: read the prior step's own committed artefact
assert decision_raw["decision"] == "go", "the dataset step must only proceed once the decision step's own gate reached go" # => co-06
print(f"Decision confirmed: {decision_raw['decision']!r} for base {decision_raw['base_model_id']!r}") # => co-06,co-30
conflicts = audit_conflicts(DATASET) # => co-12: run the consistency audit
audit_clean = len(conflicts) == 0 # => co-12: a clean audit means zero conflicting instruction/response pairs
print(f"Consistency audit conflicts: {conflicts} | Audit clean: {audit_clean}") # => co-12
assert audit_clean, "this capstone's own dataset must be free of planted or accidental conflicts before training proceeds" # => co-12
production_traffic_count = sum(1 for c in DATASET if c.source == "production_traffic") # => co-13: how many cases came from real traffic
production_traffic_share = production_traffic_count / len(DATASET) # => co-13: the documented sourcing bias this dataset carries
print(f"Sourcing: {production_traffic_count} of {len(DATASET)} cases from production traffic ({production_traffic_share:.0%})") # => co-13
train_cases, val_cases, test_cases = DATASET[:14], DATASET[14:17], DATASET[17:] # => co-15: 70/15/15, matching this course's own ex-25 split ratio
train_ids, val_ids, test_ids = [c.case_id for c in train_cases], [c.case_id for c in val_cases], [c.case_id for c in test_cases] # => co-15
print(f"Train: {len(train_ids)} | Validation: {len(val_ids)} | Test: {len(test_ids)}") # => co-15
assert len(train_ids) + len(val_ids) + len(test_ids) == len(DATASET), "every case must land in exactly one split" # => co-15
all_split_ids = train_ids + val_ids + test_ids # => co-16: every id across every split, concatenated
leakage_found = len(set(all_split_ids)) != len(all_split_ids) # => co-16: a leak is any id appearing more than once across splits
print(f"Leakage check -- duplicate ids across splits: {leakage_found}") # => co-16
assert not leakage_found, "no case id may appear in more than one split -- a leaked id would invalidate every downstream eval" # => co-16
splits: DatasetSplits = { # => co-15: the full committed artefact -- every field traceable to a check run above
"decision_confirmed": True, # => co-06
"total_examples": len(DATASET), # => co-10
"train_ids": train_ids, # => co-15
"val_ids": val_ids, # => co-15
"test_ids": test_ids, # => co-15
"audit_clean": audit_clean, # => co-12
"leakage_found": leakage_found, # => co-16
"production_traffic_share": production_traffic_share, # => co-13
"base_model_id": decision_raw["base_model_id"], # => co-30
} # => co-15: closes splits
RESULT_PATH.write_text(json.dumps(splits, indent=2, sort_keys=True) + "\n", encoding="utf-8") # => co-15: commits the artefact train/train.py reads next
print(f"MATCH: dataset splits committed to {RESULT_PATH.name} -- audit clean, splits disjoint, zero leakage") # => co-12,co-15,co-16
# => co-10,co-12,co-15,co-16: the dataset is the whole job -- every downstream step's result is only as trustworthy as this step's own audit and split disciplineRun: python3 dataset.py
Output:
Decision confirmed: 'go' for base 'qwen2.5-0.5b-instruct-r1'
Consistency audit conflicts: [] | Audit clean: True
Sourcing: 13 of 20 cases from production traffic (65%)
Train: 14 | Validation: 3 | Test: 3
Leakage check -- duplicate ids across splits: False
MATCH: dataset splits committed to dataset_splits.json -- audit clean, splits disjoint, zero leakageAcceptance criteria: this step refuses to proceed unless Step 1's own decision_record.json
reads "decision": "go" (co-06). The consistency audit finds zero conflicts (co-12); sourcing is
documented as 65% production traffic (co-13); the 70/15/15 split (co-15) shows zero duplicate
ids across the three splits (co-16). dataset_splits.json is committed for Step 3.
Key takeaway: twenty cases, audited clean and split with zero leakage, is what "the dataset is the whole job" looks like in practice -- not a size claim, a discipline claim.
Why It Matters: Step 3's own rank sweep and early-stopping decisions are only trustworthy because this step's audit and leakage check ran first -- a noisy or leaked dataset would make every later number in this capstone unfalsifiable, however clean the training run itself looked.
Step 3: train/train.py -- rank chosen from the sweep, epoch chosen by validation
Context: Band B (Examples 31, 40) built the rank sweep and early-stopping-on-validation separately. This step runs both together: a rank sweep against the audited validation split, and a per-epoch validation curve for the chosen rank, stopping at the true peak rather than the last epoch.
# learning/capstone/train/train.py
"""Capstone Step 3: The Training Run (exercises co-17, co-18, co-19, co-20, co-23, co-24).""" # => co-20: this file's own restated purpose, doubling as its module __doc__
from __future__ import annotations # => hygiene: postpones annotation evaluation, interpreter-version-agnostic
import json # => co-20: the committed training-result artefact this step writes, read by evaluate/evaluate.py next
from pathlib import Path # => co-20: locates the prior step's dataset splits and this step's own committed artefact
from typing import TypedDict, cast # => co-20: types the committed artefacts this step reads and writes
DATASET_SPLITS_PATH = Path(__file__).parent.parent / "dataset" / "dataset_splits.json" # => co-15: the prior step's own committed artefact
RESULT_PATH = Path(__file__).parent / "train_result.json" # => co-20: this step's own committed artefact -- evaluate/evaluate.py reads it next
class DatasetSplits(TypedDict): # => co-15: mirrors dataset.py's own committed shape -- only the fields this step actually needs
audit_clean: bool # => co-12: must be True before any training proceeds
leakage_found: bool # => co-16: must be False before any training proceeds
train_ids: list[str] # => co-15: how many training examples this run actually had
val_ids: list[str] # => co-15: how many validation examples this run's early-stopping check actually had
base_model_id: str # => co-30: carried forward, unchanged, from the dataset step
# => co-20: a rank sweep against held-out validation, base FROZEN throughout (co-18) -- matches this course's own ex-31 shape
RANK_SWEEP: dict[int, float] = { # => co-20: rank -> measured validation pass rate
1: 0.78, # => co-20: too little capacity
2: 0.85, # => co-20
4: 0.90, # => co-20
8: 0.94, # => co-20: the last big jump before the curve flattens
16: 0.945, # => co-20: diminishing returns already visible
32: 0.95, # => co-20: essentially flat
64: 0.95, # => co-20: plateaued -- more capacity buys nothing further on this dataset
} # => co-20: closes RANK_SWEEP
# => co-23,co-24: the CHOSEN rank's own per-epoch validation curve -- this is what early stopping actually watches
VAL_CURVE_FOR_CHOSEN_RANK: list[float] = [0.70, 0.84, 0.91, 0.88, 0.83] # => co-23: peaks at epoch 3 (index 2), then falls -- overfitting past that point
class TrainResult(TypedDict): # => co-20: the committed shape evaluate/evaluate.py reads next
chosen_rank: int # => co-20: selected from the sweep, not defaulted
rank_sweep: dict[str, float] # => co-20: the full sweep, for anyone auditing the choice later (JSON keys are always strings)
chosen_epoch: int # => co-23,co-24: selected by validation, not by a configured epoch count
val_curve: list[float] # => co-23: the full per-epoch curve the stopping decision was made from
val_pass_rate: float # => co-24: the chosen checkpoint's own validation pass rate
adapter_size_mb: float # => co-18: the adapter's own on-disk size at the chosen rank
base_model_id: str # => co-30: pinned, carried forward unchanged
if __name__ == "__main__": # => co-20: entry point -- runs only when this file executes directly, not on import
splits_raw = cast(DatasetSplits, json.loads(DATASET_SPLITS_PATH.read_text(encoding="utf-8"))) # => co-15: read the prior step's own committed artefact
assert splits_raw["audit_clean"] and not splits_raw["leakage_found"], "training must only proceed on an audited, leak-free dataset" # => co-12,co-16
print(f"Training on {len(splits_raw['train_ids'])} train / {len(splits_raw['val_ids'])} validation cases (audited, leak-free)") # => co-15
for rank, val_score in RANK_SWEEP.items(): # => co-20: show the whole sweep, in order
print(f" rank={rank}: validation pass rate {val_score:.1%}") # => co-20
ranks_sorted = sorted(RANK_SWEEP) # => co-20: walk the sweep in increasing rank order to find the plateau
chosen_rank = next(
r for i, r in enumerate(ranks_sorted[:-1]) if RANK_SWEEP[ranks_sorted[i + 1]] - RANK_SWEEP[r] < 0.01
) # => co-20: the first rank where ONE MORE step buys under 1 point -- the justified, not defaulted, choice
print(f"Chosen rank: {chosen_rank} (justified against the sweep -- one more doubling of rank buys under 1 validation point)") # => co-20
assert chosen_rank in RANK_SWEEP, "the chosen rank must be one of the ranks the sweep actually measured" # => co-20
assert chosen_rank == 8, "this capstone's own scenario is designed so the plateau is first reached at rank 8" # => co-20
for epoch, val_score in enumerate(VAL_CURVE_FOR_CHOSEN_RANK, start=1): # => co-23: show the chosen rank's own per-epoch curve
print(f" epoch {epoch}: validation pass rate {val_score:.0%}") # => co-23
chosen_epoch = max(range(1, len(VAL_CURVE_FOR_CHOSEN_RANK) + 1), key=lambda e: VAL_CURVE_FOR_CHOSEN_RANK[e - 1]) # => co-23,co-24: the epoch VALIDATION says is best, not the last epoch trained
chosen_val_pass_rate = VAL_CURVE_FOR_CHOSEN_RANK[chosen_epoch - 1] # => co-24: that epoch's own validation pass rate
print(f"Chosen epoch (early stopping on validation): {chosen_epoch} (val {chosen_val_pass_rate:.0%})") # => co-23,co-24
assert chosen_epoch == 3, "early stopping must select epoch 3, this scenario's own true validation peak" # => co-23
assert chosen_epoch != len(VAL_CURVE_FOR_CHOSEN_RANK), "the stopping point must be set by validation, not by training to the configured epoch count" # => co-23,co-24
adapter_size_mb = round(0.29 * chosen_rank, 2) # => co-18: adapter size scales with rank -- a small, composable artefact regardless
print(f"Adapter size at rank {chosen_rank}: {adapter_size_mb} MB") # => co-18
result: TrainResult = { # => co-20: the full committed artefact -- every field traceable to a decision made above
"chosen_rank": chosen_rank, # => co-20
"rank_sweep": {str(rank): score for rank, score in RANK_SWEEP.items()}, # => co-20: JSON object keys are always strings
"chosen_epoch": chosen_epoch, # => co-23,co-24
"val_curve": VAL_CURVE_FOR_CHOSEN_RANK, # => co-23
"val_pass_rate": chosen_val_pass_rate, # => co-24
"adapter_size_mb": adapter_size_mb, # => co-18
"base_model_id": splits_raw["base_model_id"], # => co-30: matches decision.py's own pin, carried through the dataset step unchanged
} # => co-20: closes result
RESULT_PATH.write_text(json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8") # => co-20: commits the artefact evaluate/evaluate.py reads next
print(f"MATCH: training result committed to {RESULT_PATH.name} -- rank chosen from the sweep, epoch chosen by validation, not defaulted") # => co-20,co-23,co-24
# => co-17,co-18,co-19,co-20,co-23,co-24: a parameter-efficient adapter, base frozen throughout, its capacity and stopping point both evidenced, not assumedRun: python3 train.py
Output:
Training on 14 train / 3 validation cases (audited, leak-free)
rank=1: validation pass rate 78.0%
rank=2: validation pass rate 85.0%
rank=4: validation pass rate 90.0%
rank=8: validation pass rate 94.0%
rank=16: validation pass rate 94.5%
rank=32: validation pass rate 95.0%
rank=64: validation pass rate 95.0%
Chosen rank: 8 (justified against the sweep -- one more doubling of rank buys under 1 validation point)
epoch 1: validation pass rate 70%
epoch 2: validation pass rate 84%
epoch 3: validation pass rate 91%
epoch 4: validation pass rate 88%
epoch 5: validation pass rate 83%
Chosen epoch (early stopping on validation): 3 (val 91%)
Adapter size at rank 8: 2.32 MB
MATCH: training result committed to train_result.json -- rank chosen from the sweep, epoch chosen by validation, not defaultedAcceptance criteria: rank 8 is chosen because it is the first point in the sweep where doubling
the rank again buys under one validation point (co-20) -- not a default. Epoch 3 is chosen because it
is the true validation peak, two epochs before the run's own final, worst-performing epoch (co-23,
co-24). train_result.json is committed for Step 4, carrying the same base_model_id pin unchanged.
Key takeaway: neither the adapter's rank nor its stopping epoch is a preset number -- both are read off a measured curve, and the script's own asserts prove that reading, not merely narrate it.
Why It Matters: Step 4's paired evaluation only means something if the model it evaluates was chosen this way -- a rank picked by habit or an epoch count picked by a training-script default would make the "is this adaptation justified" question in Step 4 depend on luck rather than evidence.
Step 4: evaluate/evaluate.py -- the exact test, and the regression suite, together
Context: Band C (Examples 35, 37, 68) built the paired comparison, the regression suite, and the exact binomial sign test separately. This step runs the sign test against a genuinely held-out eval set (distinct from Step 3's validation split) and the regression suite together, so a target-task win can never be reported without also checking for forgetting.
# learning/capstone/evaluate/evaluate.py
"""Capstone Step 4: Evaluation (exercises co-22, co-25, co-26).""" # => co-25: this file's own restated purpose, doubling as its module __doc__
from __future__ import annotations # => hygiene: postpones annotation evaluation, interpreter-version-agnostic
import json # => co-25: the committed evaluation-result artefact this step writes, read by operate/operate.py next
from math import comb # => co-25: an exact binomial sign-test needs the binomial coefficient, not an approximation -- matches this course's own ex-68
from pathlib import Path # => co-25: locates the prior step's training result and this step's own committed artefact
from typing import TypedDict, cast # => co-25: types the committed artefacts this step reads and writes
TRAIN_RESULT_PATH = Path(__file__).parent.parent / "train" / "train_result.json" # => co-20: the prior step's own committed artefact
RESULT_PATH = Path(__file__).parent / "evaluate_result.json" # => co-25: this step's own committed artefact -- operate/operate.py reads it next
class TrainResult(TypedDict): # => co-20: mirrors train.py's own committed shape -- only the fields this step actually needs
chosen_rank: int # => co-20: carried forward for the evaluation report
val_pass_rate: float # => co-24: the validation-time result, compared below against a genuinely held-out result
base_model_id: str # => co-30: pinned, carried forward unchanged
TARGET_TASK_PASS_RATE_BASE = 0.60 # => co-25: the unadapted base's own pass rate, matching decision.py's own BASE_PASS_RATE
TARGET_TASK_PASS_RATE_ADAPTED = 0.90 # => co-25: the adapted model's OWN held-out result -- close to but not identical to validation, since this is a genuinely separate set
DISCORDANT_B = 18 # => co-25: held-out cases the adapter fixed relative to the base, out of 20 discordant pairs
DISCORDANT_C = 2 # => co-25: held-out cases the adapter broke relative to the base, out of 20 discordant pairs
SIGNIFICANCE_THRESHOLD = 0.05 # => co-25: the conventional cutoff, matching this course's own ex-68
REGRESSION_PASS_RATE_BASE = 1.00 # => co-26: the base's own clean score on the untouched-capability regression suite
REGRESSION_PASS_RATE_ADAPTED = 0.90 # => co-22,co-26: the adapted model's own score on the SAME regression suite
FORGETTING_ALERT_THRESHOLD = 0.90 # => co-22: matches this course's own ex-37 alert line
def sign_test_p_value(b: int, c: int) -> float: # => co-25: the exact two-sided sign-test p-value on the b vs c discordant-pair counts -- matches ex-68's own function
"""Return the exact two-sided binomial sign-test p-value for `b` wins against `c` losses out of `b + c` discordant pairs.""" # => co-25: documents sign_test_p_value's contract -- no runtime output, just sets its __doc__
n = b + c # => co-25: only discordant pairs carry information
if n == 0: # => co-25: no discordant pairs means no evidence either way
return 1.0 # => co-25: returns this computed value to the caller
extreme = min(b, c) # => co-25: the smaller count is the "more extreme in the other direction" tail
one_sided = sum(comb(n, k) for k in range(extreme + 1)) / (2**n) # => co-25: probability of `extreme` or fewer under a fair 50/50 null
return min(1.0, 2 * one_sided) # => co-25: two-sided -- double the one-sided tail, capped at 1.0
class EvaluateResult(TypedDict): # => co-25: the committed shape operate/operate.py reads next
target_pass_rate_base: float # => co-25: the base's own headline number
target_pass_rate_adapted: float # => co-25: the adapted model's own headline number
discordant_b: int # => co-25: paired evidence FOR the adaptation
discordant_c: int # => co-25: paired evidence AGAINST the adaptation
p_value: float # => co-25: the exact test's own result
significant: bool # => co-25: does the improvement clear the significance threshold
regression_pass_rate_base: float # => co-26: the base's own regression-suite score
regression_pass_rate_adapted: float # => co-22,co-26: the adapted model's own regression-suite score
forgetting_detected: bool # => co-22: did the regression score cross the alert line
base_model_id: str # => co-30: pinned, carried forward unchanged
if __name__ == "__main__": # => co-25: entry point -- runs only when this file executes directly, not on import
train_raw = cast(TrainResult, json.loads(TRAIN_RESULT_PATH.read_text(encoding="utf-8"))) # => co-20: read the prior step's own committed artefact
print(f"Adapter: rank {train_raw['chosen_rank']} pinned to base {train_raw['base_model_id']!r} (validation pass rate {train_raw['val_pass_rate']:.0%})") # => co-20,co-24,co-30
print(f"Target-task pass rate on a GENUINELY held-out eval set: base {TARGET_TASK_PASS_RATE_BASE:.0%} | adapted {TARGET_TASK_PASS_RATE_ADAPTED:.0%}") # => co-25
p_value = sign_test_p_value(DISCORDANT_B, DISCORDANT_C) # => co-25: run the exact test, not the naive two-numbers comparison
significant = p_value < SIGNIFICANCE_THRESHOLD # => co-25: does the paired evidence actually clear the bar
print(f"Discordant pairs: {DISCORDANT_B} fixed, {DISCORDANT_C} broken -> exact p-value {p_value:.6f} (significant: {significant})") # => co-25
assert significant, "this capstone's own scenario is designed so the paired improvement is genuinely, exactly significant" # => co-25
forgetting_detected = REGRESSION_PASS_RATE_ADAPTED < FORGETTING_ALERT_THRESHOLD # => co-22: does the regression-suite drop cross the alert line
print(f"Regression suite: base {REGRESSION_PASS_RATE_BASE:.0%} | adapted {REGRESSION_PASS_RATE_ADAPTED:.0%} -> forgetting detected: {forgetting_detected}") # => co-22,co-26
assert not forgetting_detected, "this capstone's own scenario is designed so the adapted model stays within the regression-suite alert threshold" # => co-22
result: EvaluateResult = { # => co-25: the full committed artefact -- every field traceable to a measurement made above
"target_pass_rate_base": TARGET_TASK_PASS_RATE_BASE, # => co-25
"target_pass_rate_adapted": TARGET_TASK_PASS_RATE_ADAPTED, # => co-25
"discordant_b": DISCORDANT_B, # => co-25
"discordant_c": DISCORDANT_C, # => co-25
"p_value": p_value, # => co-25
"significant": significant, # => co-25
"regression_pass_rate_base": REGRESSION_PASS_RATE_BASE, # => co-26
"regression_pass_rate_adapted": REGRESSION_PASS_RATE_ADAPTED, # => co-22,co-26
"forgetting_detected": forgetting_detected, # => co-22
"base_model_id": train_raw["base_model_id"], # => co-30
} # => co-25: closes result
RESULT_PATH.write_text(json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8") # => co-25: commits the artefact operate/operate.py reads next
print(f"MATCH: evaluation result committed to {RESULT_PATH.name} -- statistically significant target-task gain, no forgetting on the regression suite") # => co-22,co-25,co-26
# => co-22,co-25,co-26: an adapted model is only as trustworthy as its WORST measured signal -- both the paired test and the regression suite had to clear their own barRun: python3 evaluate.py
Output:
Adapter: rank 8 pinned to base 'qwen2.5-0.5b-instruct-r1' (validation pass rate 91%)
Target-task pass rate on a GENUINELY held-out eval set: base 60% | adapted 90%
Discordant pairs: 18 fixed, 2 broken -> exact p-value 0.000402 (significant: True)
Regression suite: base 100% | adapted 90% -> forgetting detected: False
MATCH: evaluation result committed to evaluate_result.json -- statistically significant target-task gain, no forgetting on the regression suiteAcceptance criteria: the exact binomial sign test on 18-fixed/2-broken discordant pairs returns
p=0.000402, well under the 0.05 threshold (co-25); the regression suite stays at 90%, at
exactly the 90% alert threshold rather than below it, so forgetting_detected is False (co-22,
co-26). evaluate_result.json is committed for Step 5.
Key takeaway: the adaptation clears both bars at once -- a statistically significant target-task gain AND no detected forgetting -- not a strong number on one axis covering for a weak one on the other.
Why It Matters: Step 5's rollout decision reads this step's own significant and
forgetting_detected fields before it serves anything -- if either had gone the other way, the
capstone's own asserts would have stopped the pipeline here rather than let an unjustified adapter
reach production.
Step 5: operate/operate.py -- served, hot-swapped, pinned, and planned for retirement
Context: Band C (Examples 45, 46, 48, 75) built serving, hot-swapping, version pinning, and the maintenance plan separately. This step runs all four together: the newly evaluated adapter replaces the previously deployed one on the same loaded base, its pin is verified against every prior step, and both maintenance triggers are checked for concreteness before the capstone closes.
# learning/capstone/operate/operate.py
"""Capstone Step 5: Operation (exercises co-21, co-29, co-30, co-32).""" # => co-29: this file's own restated purpose, doubling as its module __doc__
from __future__ import annotations # => hygiene: postpones annotation evaluation, interpreter-version-agnostic
import json # => co-29: the committed operate-result artefact this step writes, closing the capstone's own artefact chain
from dataclasses import dataclass, field # => co-21: a served adapter is a small, composable artefact -- this class models exactly that shape, matching ex-45/ex-46
from pathlib import Path # => co-29: locates the prior steps' committed artefacts and this step's own committed artefact
from typing import TypedDict, cast # => co-29: types the committed artefacts this step reads and writes
EVALUATE_RESULT_PATH = Path(__file__).parent.parent / "evaluate" / "evaluate_result.json" # => co-25: the prior step's own committed artefact
TRAIN_RESULT_PATH = Path(__file__).parent.parent / "train" / "train_result.json" # => co-20: the training step's own committed artefact
RESULT_PATH = Path(__file__).parent / "operate_result.json" # => co-29: this step's own committed artefact -- the capstone's final artefact
class EvaluateResult(TypedDict): # => co-25: mirrors evaluate.py's own committed shape -- only the fields this step actually needs
target_pass_rate_adapted: float # => co-25: the newly evaluated adapter's own held-out pass rate
significant: bool # => co-25: must be True before this adapter is rolled out
forgetting_detected: bool # => co-22: must be False before this adapter is rolled out
base_model_id: str # => co-30: pinned, carried forward unchanged
class TrainResult(TypedDict): # => co-20: mirrors train.py's own committed shape -- only the fields this step actually needs
chosen_rank: int # => co-20: reported in the final operational summary
base_model_id: str # => co-30: pinned, carried forward unchanged
@dataclass(frozen=True) # => co-21: frozen -- a served adapter's identity is fixed once loaded, matching this course's own ex-45
class Adapter: # => co-21: a small, versionable artefact -- NOT a whole new model
name: str # => co-21: which adapter this is
base_model_id: str # => co-30: which base this adapter is pinned to
pass_rate: float # => co-25: this adapter's own measured pass rate, for the operational rollout decision
@dataclass # => co-29: a mock server -- ONE base model in memory, adapters attached and swapped on top of it, matching ex-45/ex-46
class MockServer: # => co-29: stands in for the real serving stack this course's prerequisite topic covers in depth
base_model_id: str # => co-29: the single loaded base model this server holds in memory
loaded_adapters: dict[str, Adapter] = field(default_factory=dict[str, Adapter]) # => co-21: adapters currently attached, keyed by name
active_adapter_name: str | None = None # => co-21: which adapter is currently routing requests
def load_adapter(self, adapter: Adapter) -> None: # => co-30: attach an adapter, rejecting a base mismatch at load time
"""Attach `adapter` to this server, raising if its `base_model_id` does not match the loaded base.""" # => co-29: documents load_adapter's contract -- no runtime output, just sets its __doc__
if adapter.base_model_id != self.base_model_id: # => co-30: an adapter trained against one base cannot simply be attached to another
raise ValueError(f"adapter {adapter.name!r} was trained against {adapter.base_model_id!r}, not the loaded base {self.base_model_id!r}") # => co-30
self.loaded_adapters[adapter.name] = adapter # => co-21: attach it -- cheap, because the adapter is small
def switch_to(self, adapter_name: str) -> None: # => co-21: the hot-swap itself -- no base reload
"""Set `active_adapter_name` to `adapter_name`, raising if it is not loaded.""" # => co-21: documents switch_to's contract -- no runtime output, just sets its __doc__
if adapter_name not in self.loaded_adapters: # => co-21: cannot switch to an adapter that was never loaded
raise KeyError(f"adapter {adapter_name!r} is not loaded") # => co-21
self.active_adapter_name = adapter_name # => co-21: the swap -- no base reload, no downtime
def trigger_is_concrete(trigger: str) -> bool: # => co-30,co-32: a real trigger names a checkable CONDITION, not a vague aspiration -- matches ex-75's own check
"""Return whether `trigger` reads as a concrete, checkable condition rather than a vague statement.""" # => co-30: documents trigger_is_concrete's contract -- no runtime output, just sets its __doc__
vague_phrases = ("as needed", "periodically", "when appropriate", "from time to time") # => co-30: the vague-language smells this illustrative check screens for
return not any(phrase in trigger.lower() for phrase in vague_phrases) and len(trigger) > 20 # => co-30: not vague AND substantive enough to be an actual condition
class OperateResult(TypedDict): # => co-29: the capstone's own FINAL committed artefact, closing the five-step arc
served: bool # => co-29: the adapter successfully loaded and served a request
hot_swap_verified: bool # => co-21: the swap from the previous adapter to this one succeeded
version_pinned: bool # => co-30: the served adapter's own base pin matches its training-time base
re_adaptation_trigger_concrete: bool # => co-30: the re-adaptation condition is checkable, not vague
retirement_trigger_concrete: bool # => co-32: the retirement condition is checkable, not vague
adaptation_justified_end_to_end: bool # => co-06: the capstone's own final verdict
if __name__ == "__main__": # => co-29: entry point -- runs only when this file executes directly, not on import
evaluate_raw = cast(EvaluateResult, json.loads(EVALUATE_RESULT_PATH.read_text(encoding="utf-8"))) # => co-25: read the evaluation step's own committed artefact
train_raw = cast(TrainResult, json.loads(TRAIN_RESULT_PATH.read_text(encoding="utf-8"))) # => co-20: read the training step's own committed artefact
assert evaluate_raw["significant"] and not evaluate_raw["forgetting_detected"], "operation must only proceed once the evaluation step's own bar is cleared" # => co-22,co-25
server = MockServer(base_model_id=evaluate_raw["base_model_id"]) # => co-29: ONE base, loaded once, matching the pinned base from every prior step
previous_adapter = Adapter(name="triage-v0", base_model_id=evaluate_raw["base_model_id"], pass_rate=0.75) # => co-32: the previously-deployed adapter this rollout replaces
new_adapter = Adapter(name="triage-v1", base_model_id=evaluate_raw["base_model_id"], pass_rate=evaluate_raw["target_pass_rate_adapted"]) # => co-25: THIS capstone's own newly trained and evaluated adapter
server.load_adapter(previous_adapter) # => co-29: the previously-deployed adapter, still loaded
server.load_adapter(new_adapter) # => co-30: the new adapter, rejected at load time if its base pin did not match -- it matches here
server.switch_to("triage-v0") # => co-21: start from the currently-deployed adapter, as a real rollout would
server.switch_to("triage-v1") # => co-21: the hot-swap -- no base reload between requests
print(f"Active adapter after rollout: {server.active_adapter_name} (previous: {previous_adapter.pass_rate:.0%}, new: {new_adapter.pass_rate:.0%})") # => co-21,co-25
hot_swap_verified = server.active_adapter_name == "triage-v1" and new_adapter.pass_rate > previous_adapter.pass_rate # => co-21: swap succeeded AND the new adapter genuinely beats the old one
assert hot_swap_verified, "the hot-swap must land on the new adapter, and the new adapter must genuinely beat the one it replaced" # => co-21,co-25
served = server.active_adapter_name == "triage-v1" # => co-29: the adapter is now actively serving
version_pinned = new_adapter.base_model_id == train_raw["base_model_id"] == evaluate_raw["base_model_id"] # => co-30: the SAME base pin, traced through every prior step
print(f"Served: {served} | Hot-swap verified: {hot_swap_verified} | Version-pinned to {new_adapter.base_model_id!r}: {version_pinned}") # => co-29,co-21,co-30
assert version_pinned, "the served adapter's base pin must match the base pin from every prior step, unchanged" # => co-30
re_adaptation_trigger = "the pinned base version is superseded by a new release the platform team deploys" # => co-30: matches this course's own ex-48/ex-75 scenario
retirement_trigger = "a retrieval-based or prompting-based alternative measurably beats this adapter on both pass rate and monthly cost" # => co-32: matches this course's own ex-49/ex-75 scenario
re_adaptation_concrete = trigger_is_concrete(re_adaptation_trigger) # => co-30: verify the re-adaptation condition is actually checkable
retirement_concrete = trigger_is_concrete(retirement_trigger) # => co-32: verify the retirement condition is actually checkable
print(f"Re-adaptation trigger concrete: {re_adaptation_concrete} | Retirement trigger concrete: {retirement_concrete}") # => co-30,co-32
assert re_adaptation_concrete and retirement_concrete, "both the maintenance and retirement triggers must be concrete, checkable conditions, not vague aspirations" # => co-30,co-32
adaptation_justified_end_to_end = all( # => co-06: the capstone's own final verdict, citing every step's own check
(served, hot_swap_verified, version_pinned, re_adaptation_concrete, retirement_concrete, evaluate_raw["significant"], not evaluate_raw["forgetting_detected"]) # => co-06
) # => co-06: closes the all(...) check
print(f"Adaptation is justified end to end: {adaptation_justified_end_to_end}") # => co-06
assert adaptation_justified_end_to_end, "every one of the five steps' own checks must hold for this capstone to close as justified" # => co-06
result: OperateResult = { # => co-29: the capstone's own FINAL committed artefact
"served": served, # => co-29
"hot_swap_verified": hot_swap_verified, # => co-21
"version_pinned": version_pinned, # => co-30
"re_adaptation_trigger_concrete": re_adaptation_concrete, # => co-30
"retirement_trigger_concrete": retirement_concrete, # => co-32
"adaptation_justified_end_to_end": adaptation_justified_end_to_end, # => co-06
} # => co-29: closes result
RESULT_PATH.write_text(json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8") # => co-29: commits the capstone's own final artefact
print(f"MATCH: operation result committed to {RESULT_PATH.name} -- served, hot-swapped, version-pinned, and both maintenance triggers concrete") # => co-21,co-29,co-30,co-32
# => co-06,co-21,co-25,co-29,co-30,co-32: five committed artefacts, one per step, chain into a single justified, evidenced adaptation -- exactly the arc ex-50 cites by example number, this time run as one real small projectRun: python3 operate.py
Output:
Active adapter after rollout: triage-v1 (previous: 75%, new: 90%)
Served: True | Hot-swap verified: True | Version-pinned to 'qwen2.5-0.5b-instruct-r1': True
Re-adaptation trigger concrete: True | Retirement trigger concrete: True
Adaptation is justified end to end: True
MATCH: operation result committed to operate_result.json -- served, hot-swapped, version-pinned, and both maintenance triggers concreteAcceptance criteria: the rollout swaps from triage-v0 (75%) to triage-v1 (90%) with no
base reload (co-21); the served adapter's base pin matches the pin carried unchanged from Step 1
through Step 4 (co-30); both the re-adaptation and retirement triggers pass the concreteness check
(co-30, co-32). adaptation_justified_end_to_end reads True, and operate_result.json closes the
capstone's own five-artefact chain.
Key takeaway: served, hot-swap verified, version-pinned, and both _trigger_concrete flags
all read True together -- the capstone's own final verdict is a conjunction of every prior step's
own evidence, not a separate closing claim asserted on its own.
Why It Matters: this is the same "justified adaptation is a cited arc, not a single retrospective claim" idea Example 50 demonstrated by citing sixteen prior examples by number -- run here as one real, ordered, five-file project instead, closing with a written maintenance and retirement plan rather than stopping at "it works."
← Previous: Band C: Evaluation, Distillation, and Operation · Next: Drilling →
Last updated July 25, 2026