Chapter 24
Readiness is derived, not declared
Let review and production revise a visible readiness rule instead of storing a verdict.
On this page
Between case preparation and ammunition production there is a line.
The first working rule said that no case crossed unless six facts held:
- It is clean.
- The primer pocket is clear.
- The primer pocket is prepared.
- It has been sized.
- Its length is within the caliber's specification.
- It has no observed defects.
The first version of that list had five facts. Sized was missing. Review
caught the omission before a case crossed.
That correction is part of the rule's provenance. It prevents a tidy six-item list from looking as though it arrived whole. Production later corrected the list again. The derivation can be wrong. Its advantage is that it can be wrong in a place we can inspect.
A ready bin would hide the question
There is no bin labeled READY at the bench. There is no green sticker on a
case and no ready = true flag in the model.
A sticker records that somebody reached a conclusion at one moment. It does not carry the parts of the conclusion. If the material is mixed, reworked, or damaged later, the sticker keeps asserting an old decision. When it lies, it cannot say which fact changed or which rule was incomplete.
Readiness is derived at the crossing from current facts and current policy.
The answer has provenance because its inputs and rule remain visible.That does not eliminate judgment. Defect-free depends on what the operator
can observe and how the acceptance policy interprets it. The magnifier can
improve the observation without making the decision. Derivation separates the
judgment from the boolean; it does not pretend judgment disappeared.
Give each truth an owner
The fact table makes that separation inspectable:
| Question | Fact or measurement owner | Policy owner | Decision owner |
|---|---|---|---|
| Is it the expected caliber? | case carries caliber identity | current run and caliber specification | classifier; gate keeps only a bypass tripwire |
| Is it clean? | case observation | preparation acceptance practice | readiness gate derives from recorded fact |
| Is the primer pocket clear? | case observation after decapping | preparation acceptance practice | readiness gate |
| Is the primer pocket prepared? | case observation after swaging | preparation acceptance practice | readiness gate |
| Has it been sized? | case preparation history or observation | preparation sequence | readiness gate |
| Is its length acceptable? | case carries measured length | caliber specification owns maximum | readiness gate |
| Is it free of observed defects? | operator observation, aided by magnifier | defect acceptance policy | readiness gate |
The case owns facts about itself. It does not own the number that makes a length acceptable. That number belongs to a caliber specification. The gate owns the derivation. The operator owns judgments no object can honestly make from absent observations.
When the answer is wrong, the table gives us a search space. Was the
measurement wrong? Was the policy wrong? Was the derivation incomplete? Did
material bypass the boundary? A ready flag answers none of those questions.
Write the truth table before the gate
The gate's most important cases can be stated without Ruby:
| Scenario | Known facts | Policy/context | Expected result |
|---|---|---|---|
| All six facts hold | all true | matching caliber, standard maximum length | admit |
| Several preparation facts fail | clean false, sized false; others true | matching caliber | reject with dirty and unsized |
| Nothing was established | only caliber known | matching caliber | reject every unestablished readiness fact |
| Wrong context bypasses classification | all readiness facts true | case caliber differs from specification | reject with wrong_caliber tripwire |
| Policy changes | measured length 1.755 | maximum changes from 1.760 to 1.750 | verdict changes from admit to reject |
The table refuses two easy lies. A failure is not necessarily singular, and unknown is not a pass.
A gate that knows nothing still chooses a default. Passing unknown material is quiet, fast, and frictionless until the missing evidence becomes expensive inside production. This gate treats an unestablished fact as failed. Clean until shown dirty would be a choice. Dirty until shown clean is also a choice. The test pins the one this system intends.
Test the rule before the implementation
The first unit test asks the incoming query and checks its result:
it "admits a case when all six readiness facts hold" do
admission = gate.assess(cartridge_case)
expect(admission).to be_admitted
expect(admission.reasons).to be_empty
end
The correction receives its own regression test. It exists because the rule was wrong once:
it "rejects an unsized case even when every other fact holds" do
admission = gate.assess(cartridge_case(sized: false))
expect(admission).to be_rejected
expect(admission.reasons).to eq([:unsized])
end
Several failures return together because the gate asks independent questions:
it "reports every failed fact in one admission result" do
admission = gate.assess(cartridge_case(clean: false, sized: false))
expect(admission.reasons).to contain_exactly(:dirty, :unsized)
end
And unknown is made explicit rather than left to Ruby's truthiness or a constructor default:
it "rejects a case whose readiness facts were never established" do
admission = gate.assess(CartridgeCase.new(caliber: "5.56"))
expect(admission).to be_rejected
expect(admission.reasons).to contain_exactly(
:dirty, :primer_pocket_obstructed, :primer_pocket_unprepared,
:unsized, :over_maximum_length, :defects_present
)
end
Only after those tests describe the decision does the gate need an implementation. Its public protocol is deliberately small:
def assess(cartridge_case)
Admission.new(reasons: failed_checks(cartridge_case))
end
Admission is admitted when its reasons are empty. Rejection remains ordinary
data, with every reason available to the operator and the later learning loop.
Make the policy move
Constructor injection is not proof of a joint. The specification could be accepted and ignored while a maximum length remains hardcoded inside the gate. The test must swap the collaborator and observe a changed verdict:
it "consults the injected specification rather than a built-in limit" do
tighter = CaliberSpecification.new(caliber: "5.56", maximum_case_length: 1.750)
tighter_gate = described_class.new(caliber_specification: tighter)
admission = tighter_gate.assess(cartridge_case(length: 1.755))
expect(admission).to be_rejected
expect(admission.reasons).to eq([:over_maximum_length])
end
The full spec is spec/production_readiness_gate_spec.rb.
Now the joint is visible. The case's measured length stays 1.755. Policy
moves from 1.760 to 1.750. The verdict changes. Facts and policy are in
different hands.
The collaborator is a real value object, not a mock. Nothing interesting is gained by asking whether the gate sent a particular internal message. The behavioral consequence is the contract.
Trust the boundary and keep a tripwire
The gate also rejects a case whose caliber differs from its specification. At first glance that duplicates the classifier from Three surprises, three exits.
It does not own the same decision. The classifier separates expected, wrong-context, and foreign material. The gate has no bins and knows nothing about foreign objects. Its caliber check is a cheap tripwire at its own door: if material visibly bypassed the promised path, stop.
The moment the gate began routing wrong-caliber material, the system would have two classifiers. Two owners for one decision invite two answers. Trusting the upstream guarantee and refusing an obvious breach are compatible. Reimplementing the upstream boundary is not.
Then production corrected the rule
The six-fact model was useful before the physical line ran. It separated facts from policy, failed closed, and exposed the missing sizing requirement during review. Then real brass reached the Dillon.
Approximately twenty cases became scrap while the sizing setup was calibrated. After that proof, fifty cleaned, once-fired Lake City 5.56 cases entered a bounded run. Every case went through the headspace gauge. Forty-eight passed. Two left the accepted set for rework, and two accepted replacements restored the set to fifty before the RCBS swager processed it.
That work exposed three facts the first derivation did not express well:
- The first cleaning removes range dirt before handling and sizing.
- Sizing adds lubricant, so release needs a second cleanliness claim: free of process residue before loading.
- The final case must be completely dry, including its pocket and interior.
The gauge also separated sized from sizing produced an accepted headspace result. Transformation history and measured output are not the same fact.
The current obligations can be stated, but the exact final predicate remains open. The fifty cases are mechanically prepared through swaging. They are not ready by declaration: they still owe final cleaning, complete drying, and final inspection. The Lab Ledger carries the full obligation list as it stands.
This chapter does not silently add fields to the Ruby miniature to make it look current. Its six-fact gate preserves the earlier design conversation and its regression test. The physical rule is still learning. Updating the executable model before the second cleaning produces evidence would replace one visible omission with a confident guess.
Derive one of your statuses
Find a status in a system you know: ready, approved, deployable,
verified, complete.
Replace the word with a table:
| Required fact | How it is observed | Who owns the fact | Who owns policy | Unknown behavior |
|---|---|---|---|---|
Then write five cases before implementation:
- every fact holds;
- one fact fails;
- several facts fail;
- no fact was established;
- the policy collaborator changes while the facts stay fixed.
If you cannot identify the inputs to the status, it is not yet a derivation. It is a remembered verdict.
What the derivation cannot promise
The tests prove the earlier six-fact rule and its defaults. They do not prove that rule contains every fact physical production needs. Review changed it once; production changed it again. The final cleaning and live run may change it further.
Nor can a gate improve a bad observation. If a split is present and the
operator records defects: [], the derivation will be consistently wrong.
The next chapter is about building the ability to know into the system, and
about carrying what production teaches back to the decisions that produced
it.