Lesson 16 / 21
Repository-Defined Desired State
The same change flow runs in two arrangements — manual application and committing to the repository — and across 180 changes the two arrangements' traceability, unrecorded-change rate, end-of-period drift, and steps per change are counted. The review gate's marginal value — the defect it alone catches, the clean change it wrongly holds, and the 796 hours it adds — is measured separately.
Published Updated
In this lesson
- Runs the same change flow in two arrangements: manual application and committing to the repository
- Compares the unrecorded-change rate and the number of drifted items at period's end across both arrangements
- Measures the review gate's marginal value, its wrongful holds, and the time it adds
Contents
The previous topics established how a release is decided: which metric promotion looks at, at what threshold a percentage rollout stops, how many steps a rollback plan has. All of these decisions shared one thing — in the end, someone pressed a button in an interface or typed a shell command. The decision itself was on record; its application was not.
This lesson measures that gap. The question is: does the desired state the cluster is running exist in writing somewhere, or only in the running system’s memory? The repository-defined desired state principle is a single sentence — the definition of what runs in an environment lives in a repository, and it changes through no path outside that repository. The principle itself is short; its bill is long. This lesson draws up the bill.
The measurement is over the regional measurement network and is fictional: the meter-reading
collector, verifier, billing, and work-order services, across three environments. What runs is a
model built with node, not a real system.
RD1. The desired state is 60 items: four services × three environments × five fields (replica count, threshold, version, network rule, schedule). RD2. 180 changes are generated over ninety days; the generator is self-written, seed 20260801 is visible. RD3. A portion of the changes are urgent (off-shift intervention); 20% carry a known defect: format, threshold, environment, runtime. RD4. In the manual arrangement, recording is voluntary — the person applying it writes it down somewhere if they choose to. In the repository arrangement, the commit record mandatorily carries who, when, and rationale fields. RD5. In the repository arrangement too, a portion of the urgent changes bypass the repository; the principle is not applied 100%. RD6. There are two gates and both are only on the repository path: the schema validator (automated) and review (human).
The Same Change, Two Paths
// flow.mjs — a 90-day change flow modeled over the (fictional) regional measurement // network. The same flow runs in two arrangements: manual application and committing // to the repository. What runs is a model, not a real system; seed is visible. export const generator = (t) => { let s = t >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; }; const SERVICE = ["collector", "verifier", "billing", "workorder"]; const ENV = ["development", "staging", "production"]; const FIELD = ["replica", "threshold", "version", "network-rule", "schedule"]; export const ITEM = SERVICE.flatMap((s) => ENV.flatMap((e) => FIELD.map((f) => `${s}/${e}/${f}`))); export function flow(seed = 20260801, count = 180) { // 90-day change flow const r = generator(seed), list = []; for (let i = 0; i < count; i++) { const item = ITEM[Math.floor(r() * ITEM.length)]; const urgent = r() < 0.22; // off-shift intervention const p = r(); // known defect set: 20% const defect = p < 0.06 ? "format" : p < 0.12 ? "threshold" : p < 0.16 ? "environment" : p < 0.20 ? "runtime" : null; list.push({ i, day: Math.floor(r() * 90), item, urgent, defect }); } return list; } // repo=false: every change is applied manually, recording is voluntary. // repo=true : the change is committed to the repository; some urgent ones still skip it. export function arrange(list, { repo, seed = 7717 }) { const r = generator(seed); const actual = new Map(), ledger = new Map(); // item -> version number let who = 0, rationale = 0, unrecorded = 0, skipped = 0, step = 0, reviewed = 0; for (const d of list) { const repoPath = repo && !(d.urgent && r() < 0.35); const s = (actual.get(d.item) ?? 0) + 1; actual.set(d.item, s); if (repoPath) { // branch, change, commit, ledger.set(d.item, s); // proposal, review, who++; rationale++; reviewed++; step += 7; // merge, apply } else { skipped++; step += 2; // write command, apply const records = r() < (d.urgent ? 0.25 : 0.7); if (records) { who++; if (r() < 0.45) rationale++; } else unrecorded++; if (r() < (d.urgent ? 0.1 : 0.3)) ledger.set(d.item, s); // logged later into the ledger } } let drifted = 0; for (const [k, s] of actual) if (ledger.get(k) !== s) drifted++; return { n: list.length, who, rationale, unrecorded, skipped, reviewed, drifted, touched: actual.size, step: step / list.length }; } if (import.meta.url === `file://${process.argv[1]}`) { const list = flow(); const urgent = list.filter((d) => d.urgent).length; const defective = list.filter((d) => d.defect).length; console.log(`seed 20260801 | ${list.length} changes / 90 days | ${ITEM.length} items`); console.log(`urgent: ${urgent} | carrying a known defect: ${defective}`); const HEAD = ["arrangement", "who", "rationale", "unrecorded", "skipped", "reviewed", "drifted item", "step"]; const W = [21, 5, 11, 12, 9, 10, 14, 6]; const row = (h) => h.map((v, j) => j ? String(v).padStart(W[j]) : String(v).padEnd(W[0])).join(""); console.log("\n" + row(HEAD)); for (const repo of [false, true]) { const x = arrange(list, { repo }); console.log(row([repo ? "repository commit" : "manual application", x.who, x.rationale, x.unrecorded, x.skipped, x.reviewed, `${x.drifted}/${x.touched}`, x.step.toFixed(2)])); } }
seed 20260801 | 180 changes / 90 days | 60 items urgent: 35 | carrying a known defect: 36 arrangement who rationale unrecorded skipped reviewed drifted item step manual application 117 54 63 180 0 45/56 2.00 repository commit 175 172 5 9 171 1/56 6.75
The traceability columns diverge in the expected direction. In the manual arrangement, 117 of the 180 changes have who-and-when information landing somewhere, but only 54 carry a rationale; 63 changes — 35% of the flow — were applied leaving no trace at all. In the repository arrangement, rationale climbs to 172, because a commit record cannot be left empty: the who, when, and why fields are the change’s own carrier, not a separately filled-out form.
The real number sits three columns from the end. Over ninety days, 56 of the 60 items were touched; in the manual arrangement, at period’s end, 45 of those 56 items differ from the repository’s value. In other words, the document that describes the system is wrong for four-fifths of it. In the repository arrangement, the drifted-item count is 1, and that one item is left over from the nine urgent changes that bypassed the repository.
The operational weight of this gap is heavier than a stale document. In the manual arrangement, the only place that knows an item’s correct value is the running system itself; when an environment is rebuilt, or an item is reset, there is no source to fall back to. If the billing service’s production threshold is changed by hand one night and no one writes it down, that threshold is no longer anyone’s decision — it is an accident. The first payoff of repository-defined desired state is not auditing, it is reversibility: for 55 of the 56 items, the repository’s value is the system’s last known correct state.
What the Review Gate Holds Only By Itself
// gate.mjs — ledger of the repository-path review gate: what it holds, what it holds // ONLY BY ITSELF, what it misses, wrongly holds, and the time it adds. Same flow, new seed. import { flow, generator } from "./flow.mjs"; const SCHEMA = { format: 1, threshold: 0, environment: 0, runtime: 0 }; // schema validator const REVIEW = { format: 0.85, threshold: 0.55, environment: 0.75, runtime: 0.05 }; function run(list, { escape, seed = 4231 }) { const r = generator(seed), actual = new Map(), ledger = new Map(); const byEnv = {}, byDefect = {}; let skipped = 0, gated = 0, schemaOnly = 0, marginal = 0, both = 0, passed = 0, wrong = 0, minutes = 0, triggered = false; const apply = (d, withRepo) => { const s = (actual.get(d.item) ?? 0) + 1; actual.set(d.item, s); if (withRepo) ledger.set(d.item, s); return s; }; for (const d of list) { const e = (byEnv[d.item.split("/")[1]] ??= { gated: 0, marginal: 0, wrong: 0, min: 0 }); const k = d.defect ? (byDefect[d.defect] ??= { n: 0, review: 0, schema: 0, passed: 0 }) : null; if (k) k.n++; const skip = r() < (d.urgent ? 0.35 : (escape && triggered ? 0.5 : 0)); triggered = false; if (skip) { // ungated path skipped++; const s = apply(d, false); if (k) { passed++; k.passed++; } if (r() < 0.2) ledger.set(d.item, s); continue; } gated++; e.gated++; const wait = d.urgent ? 5 + r() * 55 : 25 + r() * 575; // review wait time minutes += wait; e.min += wait; const schema = d.defect ? r() < SCHEMA[d.defect] : false; const review = d.defect ? r() < REVIEW[d.defect] : false; if (k && (schema || review)) { // held, not applied if (schema) k.schema++; if (review) k.review++; if (schema && review) both++; else if (schema) schemaOnly++; else { marginal++; e.marginal++; } continue; } if (!k && r() < 0.06) { // clean but held wrong++; e.wrong++; triggered = true; minutes += wait * 0.6; e.min += wait * 0.6; // second round } apply(d, true); if (k) { passed++; k.passed++; } } let drifted = 0; for (const [a, s] of actual) if (ledger.get(a) !== s) drifted++; return { skipped, gated, schemaOnly, marginal, both, passed, wrong, drifted, min: minutes / gated, hours: minutes / 60, byEnv, byDefect }; } const row = (w, cells) => cells.map((v, j) => j ? String(v).padStart(w[j]) : String(v).padEnd(w[0])).join(""); if (import.meta.url === `file://${process.argv[1]}`) { const list = flow(), defective = list.filter((d) => d.defect).length; const W_MAIN = [8, 9, 7, 13, 13, 6, 8, 7, 9, 12]; console.log(`${list.length} changes, ${defective} known defect; two gates: schema + review`); console.log("\n" + row(W_MAIN, ["escape", "skipped", "gated", "schema only", "review only", "both", "passed", "wrong", "drifted", "min/review"])); for (const escape of [false, true]) { const x = run(list, { escape }); console.log(row(W_MAIN, [escape ? "yes" : "no", x.skipped, x.gated, x.schemaOnly, x.marginal, x.both, x.passed, x.wrong, x.drifted, x.min.toFixed(0)])); } const x = run(list, { escape: false }); const W_DEFECT = [13, 7, 8, 8, 8]; console.log("\n" + row(W_DEFECT, ["defect", "count", "schema", "review", "passed"])); for (const [ad, k] of Object.entries(x.byDefect)) console.log(row(W_DEFECT, [ad, k.n, k.schema, k.review, k.passed])); const W_ENV = [13, 7, 13, 14, 13, 16]; console.log("\n" + row(W_ENV, ["environment", "gated", "review only", "wrongly held", "total hours", "hours/marginal"])); for (const [ad, e] of Object.entries(x.byEnv)) console.log(row(W_ENV, [ad, e.gated, e.marginal, e.wrong, (e.min / 60).toFixed(1), e.marginal ? (e.min / 60 / e.marginal).toFixed(1) : "-"])); console.log(`\ntotal review load: ${x.hours.toFixed(0)} hours / 90 days`); }
180 changes, 36 known defect; two gates: schema + review escape skipped gated schema only review only both passed wrong drifted min/review no 13 167 1 13 11 11 7 4 286 yes 20 160 1 15 9 11 9 7 270 defect count schema review passed threshold 8 0 3 5 format 12 12 11 0 environment 11 0 9 2 runtime 5 0 1 4 environment gated review only wrongly held total hours hours/marginal staging 67 6 2 312.5 52.1 development 44 1 2 193.7 193.7 production 56 6 3 289.9 48.3 total review load: 796 hours / 90 days
Both gates do work, but their ledgers do not resemble each other. The schema validator held 12 format defects; review would have caught 11 of those too, so the number the schema alone holds is 1. Review held 24 defects and caught 13 of them by itself — these are the defects that would pass whether or not the other gate existed. The marginal-value ranking is clear: schema gate 1, review 13.
The defect-class table shows why. All 12 format defects were held, none got through; both gates see this class, and the schema gate does it in seconds. Threshold and environment defects are invisible to the schema: the value is valid, the field is correct, only the number or the target is wrong. Only a human reading catches these — 3 of 8 threshold defects, 9 of 11 environment defects, were held.
The 11 defects the gate misses also read by class: 5 threshold, 2 environment, 4 runtime. The runtime class let four of its five defects through, because this class cannot be seen by reading the written text — the value only turns out wrong once the service meets real data. This is a boundary standing in front of a static gate, and extending the review’s duration does not push that boundary back.
The wrongly-held column is the gate’s other face: 7 clean changes were stopped, and each one produced a second round. The average wait per review is 286 minutes; over ninety days that is 796 hours total, roughly a nine-hour wait load per day. This time is not a labor cost — it is lead time itself: after a change is written, it goes nowhere for an average of four hours and forty-six minutes.
The Measured Trace of Escaping the Gate
The table’s second row models the behavior wrongful holding produces in the team: after a change is stopped for no reason, the change that follows it becomes more likely to go through the ungated path, even if it is not urgent. The change skipping the gate rises from 13 to 20, and the drifted item at period’s end rises from 4 to 7. The number of defects that reach production is 11 in both runs; at the scale of 180 changes, this difference sits under the generator’s noise and should be presented that way. The measurable effect is not in the defect count — it is in the drift the gate produces itself: when trust in a gate falls, the loss shows up first in traceability.
The environment table, in turn, points directly at the gate that should be removed. 44 changes headed to the development environment were reviewed, those reviews totaled 193.7 hours, and only 1 defect was caught marginally — 193.7 hours per defect. The same measure is 48.3 hours in production, 52.1 in staging. What is more, the 2 wrongful holds in the development environment are twice the marginal catch: here the gate stops more for nothing than it holds.
The decision that follows does not touch the principle, it touches the gate: development-environment changes keep being committed to the repository, the review gate comes off. This distinction is the most practical conclusion this lesson carries — committing to the repository and review are not the same thing. The traceability, rationale, and reversibility payoff comes from the commit record; the only thing the gate contributes is the defect it holds, and that is priced very differently by environment.
The Principle’s Price: Extra Steps Per Change
The first table’s last column is the principle’s direct bill. Manual application is 2 steps per change: a command is written, applied. The repository path is 7 steps — a branch is opened, the file is changed, committed, a proposal is opened, review is waited on, merged, applied. The mixed average is 6.75; the difference is 4.75 steps per change, 855 extra steps over ninety days.
Not all of these steps carry equal weight. Six of them are the writer’s own minutes; only one — the review wait — depends on someone else’s calendar, and the entire measured 286 minutes comes from there. This is why the urgent path continues to exist: 13 of the 35 urgent changes skipped the gate, because a four-hour wait at midnight extends the outage. Applying the principle 100% brings the measured unrecorded-change rate down from 5 to 0, but adds four hours to the urgent intervention’s duration; whichever side is chosen cannot be defended without measuring it.
Summary
- In the manual arrangement, 63 of the 180 changes (35%) were applied leaving no trace at all, and rationale existed for only 54 changes; in the repository arrangement, unrecorded changes fell to 5 and unjustified changes to 8.
- Of the 56 items touched at period’s end, 45 had drifted from the repository’s value in the manual arrangement, versus 1 in the repository arrangement; the principle’s first payoff is not auditing, it is reversibility.
- The schema gate held 12 defects but caught only 1 of them by itself; review held 24, catching 13 by itself — the marginal-value ranking is 1 against 13.
- The gate missed 11 defects (5 threshold, 2 environment, 4 runtime) and wrongly held 7 clean changes for nothing; it added 286 minutes per review, 796 hours over ninety days.
- In the development environment, the gate caught 1 marginal defect in 193.7 hours and stopped 2 clean changes; the same measure is 48.3 hours in production. This gate is removed in the development environment; the commit record stays.
- The principle’s step cost rises from 2 to 6.75 per change; only one of the extra steps depends on someone else’s calendar, and the entire measured wait comes from there.
Next Step
This lesson measured where the desired state is written and showed the value of the repository’s writing through the drifted-item count. The question left open is the next lesson’s subject: who carries that writing in the repository over to the cluster? In the arrangement so far, the carrier is the pipeline — the pipeline connects to the cluster and writes. There is an alternative: an agent running inside the cluster reads the repository and closes the gap itself. The items to measure are set: the pipeline’s authority surface over the cluster, the detection delay of a manual change, the direction of the network connection, and what happens when the pipeline goes down.
To keep your progress and take notes, Log in
My notes
Log in to take notes.