Bug fixed directly in code
This is the second story on What is cyber-truss: a problem found in the code and fixed there, with everything around it left behind.
The system
Section titled “The system”A web application with a paginated list.
| Artifact-set | Holds |
|---|---|
{PRD} |
The product requirements for the list feature |
{spec} |
The feature specification: intent and criteria for pagination |
{code, test} |
The pagination implementation and its tests, one unit of change |
{user docs} |
The help page describing how the list pages |
{mockups} |
The design mockups, including the page indicator |
Connections:
{PRD}to{spec}{spec}to{code, test}{spec}to{user docs}{spec}to{mockups}{mockups}to{code, test}
The last connection exists because the mockups constrain the rendered list in ways the
spec does not state, such as how the page indicator looks. On that edge {mockups} is the
specification of
{code, test}. Without it, a mockup could disagree with the code and no connection would
be strained.
Workflows
Section titled “Workflows”| Workflow | Input | Owned | Output | Shape |
|---|---|---|---|---|
| Feature delivery | none | {PRD}, {spec}, {code, test} |
none | chain |
| Docs update | {spec} |
{user docs} |
none | one link |
| Design update | {spec} |
{mockups} |
none | one link |
| Design implementation | {mockups} |
{code, test} |
none | one link |
Every set here can be revised in place, so every write is to an
owned set and nothing is an output. Feature
delivery starts at {PRD} and owns every set it spans. The other three each read one set
and own one, so
direction lives in the workflow:
design implementation can derive code from the mockups, and it can never change the mockups.
Every set keeps the default leash. A write that
removes or reverses part of a set’s
standing specification
stops for approval. The team does not loosen {spec}. Its page-count rule is what the
code, the docs and the mockups are all checked against, and a fix that rewrites it moves all
three. From inside the set a fix to a wrong rule looks the same as a change nobody wanted,
so one approval is cheaper than a rule reversed by a misread fix. The team does not loosen
{mockups} either. Reconciliation at the mockups could otherwise undo part of a designer’s
own change without asking anyone.
The help page’s use case is a user who wants to know how the list pages. Its criterion is
that the page describes the page-count rule {spec} states. The wording of the rule on the
page is expression, not a criterion, because no workflow reads {user docs} and is checked
against it.
The change
Section titled “The change”When the item count is an exact multiple of the page size, the list shows an extra empty last page. A developer notices, changes the page-count calculation to round up, and commits. The diff touches the code only. The tests never covered an exact multiple, which is how the bug shipped.
Variant A: the spec states the wrong rule
Section titled “Variant A: the spec states the wrong rule”The spec’s criteria say the page count is the item count divided by the page size, rounded down, plus one. That rule is the bug.
Expected run
Section titled “Expected run”- Lift. The change touches
{code, test}, which is the source. - Find candidates. Two workflows own
{code, test}, so both are upstream candidates: feature delivery and design implementation. No workflow reads{code, test}, so there are no downstream candidates. - Distill. Each candidate
reads the change within its own span.
Feature delivery reads it against
{spec}and{PRD}. Design implementation reads it against{mockups}, whose page indicator counts pages. Both state the same intent: every page shows at least one item, and the last page shows whatever remains. Neither abstains. - Ask the controllers above, nearest first. Each
answers for its own set
and never sees the diff.
- Feature delivery asks the
{spec}controller. The intent implies three criteria: the page count is the item count divided by the page size, rounded up; an exact multiple produces no empty page; an empty list shows one page with an empty state, not zero pages.{spec}states round down, plus one, which contradicts the first, and says nothing of the third, which is missing. The spec is affected. Feature delivery asks the{PRD}controller next. The PRD is too coarse to hold a page-count rule, and nothing is above it. The highest affected set is{spec}. These criteria are version 1 of the run’s criteria. - Design implementation asks the
{mockups}controller. The indicator shows the empty last page, so the mockups are affected. They are design implementation’s input, so it routes them to their owner, design update. Design update’s job asks the{spec}controller above the mockups, gets the same answer, and cannot write the spec either, so it routes to feature delivery, which already has that job.
- Feature delivery asks the
- Schedule. The
run ledger
holds three jobs. Feature delivery’s replay from
{spec}has no pending input and is ready. Design update’s job reads{spec}, which feature delivery is about to write. Design implementation’s reconciliation at the code reads{mockups}, which design update is about to write. - Replay from the highest affected set. Feature delivery
starts at
{spec}, not at{PRD}. The spec’s controller writes the round-up rule and the empty-list case. The round-up rule replaces round down, plus one, so the write reverses a criterion of the spec’s standing specification. The leash stops it for approval. The approver here is the product owner who keeps the PRD and the spec. On a solo project that is the developer who made the fix, which the model accepts. The ledger already holds design update’s job, so the product owner can see that the mockups will follow. The product owner approves, and pre-approves the rest of the run’s gates. Both are recorded as a decision under rule 4, and neither changes a criterion. The empty-list case only adds, so it needs nothing of its own. - Reconcile at the source. The code’s controller receives the developer’s fix, the
intent, and references to the criteria of
{spec}. Reconciliation keeps the round-up, and adds the empty-list case with its test, which the fix never considered. The report is a builder outcome, an improvement to the change. The code’s tests never stated a rule for an exact multiple, so the result removes nothing from{code, test}and the leash lets it proceed. - Propagate. The amended
{spec}is a change of its own, under the run’s intent. Docs update reads it, so it is a downstream candidate.{user docs}states the old page count, so docs update writes the new one. The help page’s criterion is that it describes the rule the spec states, and the write still meets it, so nothing is reversed and it proceeds. Design update’s job is now ready and amends the page indicator. The indicator drew an empty last page, and design implementation checks the code against that drawing, so the drawing is a criterion of{mockups}. The write reverses it, which would stop it for approval by default. The product owner’s pre-approval covers it, and its criteria pass, so it proceeds. The product owner is not asked the same question again as a drawing. Design implementation then reconciles the code against the mockups. The code already meets them, so it keeps everything and changes nothing.
The ledger schedules this work. It does not decide the outcome. The spec’s write waits on
approval, and every job that reads {spec} waits with it. Design implementation’s
reconciliation is an owned write that changes nothing, so
strain policy lets it proceed
before the mockups settle, against the criteria the mockups’ controller already derived. It
would reach the same code. Holding it until design update finishes only saves a cycle, so a
designer taking a week over the page indicator does not have to hold the code. Feature
delivery and design implementation both reconcile {code, test} against the same version 1
criteria, so their writes join at the code’s
controller.
Settled state
Section titled “Settled state”{PRD}is unchanged. It never stated a page-count rule.{spec}states the round-up rule and the empty-list case.{code, test}implements both, with tests for an exact multiple and an empty list.{user docs}and{mockups}match the spec.- One decision is recorded at
{spec}: the approval of the change of direction, and the pre-approval that carried it through{mockups}.
Status: Holds
Section titled “Status: Holds”The run settles. Every order settles in the same state. Each set that has to change is owned by one workflow that can reach it, and every write meets the same version 1 criteria, adding nothing they did not ask for. If the design review returned a new design, the workflows would disagree and further cycles would run, and those are bounded by the rules that make cycles come to rest.
The first stop is the right one. The spec stated the wrong rule, and a person accepts that it was wrong before three sets move to the new one.
The second stop would be ceremony, and pre-approval removes it. The leash still compares
the mockups’ write with what {mockups} stood for before the run, and it still finds a
reversal. What changes is that a person who already accepted the change said so for the rest
of the run. The write still has to pass its criteria, and a controller unsure of it still
asks. The team does not loosen the leash on {mockups} for every run. That would let
reconciliation undo a designer’s change unasked, and it would drop the stop in variant B,
where the mockups are the only place the change shows. See
Leash.
The status rests on the product owner choosing to pre-approve. Without it the default stops
at {mockups} again, and the run still settles, one ask later. A product owner who wants to
see the redrawn indicator can decline, and that is a choice, not a gap.
The docs write avoids a stop only because the help page’s criterion refers to the spec’s rule rather than restating it. Had the team written the rule into the page’s criteria, the docs write would stop a third time. Which wording a criterion should have is the open question of grain in Criteria are authored through use cases.
Nothing is replayed for nothing. Design implementation writes no code of its own. Its reconciliation finds the code already meets the criteria that reach it. The earlier reading left one point unsaid, that a workflow whose strain clears while it waits should check again. Under reconciliation that check is the job itself: it runs against the criteria as they stand when it starts.
Variant B: the spec says nothing about the boundary
Section titled “Variant B: the spec says nothing about the boundary”The spec’s criteria never mention how the page count is calculated.
Expected run
Section titled “Expected run”- Lift. The change touches
{code, test}, the source. - Find candidates. The same two upstream candidates as variant A, and no downstream ones.
- Distill. Both candidates state the same intent as in variant A.
- Ask the controllers above.
- Feature delivery asks the
{spec}controller. The spec states none of the three criteria, so all three are missing, and the spec is affected. The{PRD}controller answers that the PRD is too coarse. The highest affected set is{spec}. - Design implementation asks the
{mockups}controller. The mockups depict a list that is not an exact multiple and have no place for a page-count rule, so they are too coarse. Nothing is above them in design implementation’s shape, so nothing is affected and nothing is routed.
- Feature delivery asks the
- Replay from the highest affected set. Feature delivery starts at
{spec}, which gains the round-up rule and the empty-list case. The spec’s standing specification stated no page-count rule, so the write only adds, and the leash lets it proceed. - Reconcile at the source. The same as variant A: the code’s controller keeps the round-up and adds the empty-list case, and the result removes nothing. Design implementation reconciles the code against the mockups, which the code still meets, and changes nothing.
- Propagate. The amended spec has two downstream candidates.
{user docs}contradicts the new page count, so docs update writes it. As in variant A the help page still describes the rule the spec states, so the write proceeds. Design update runs from the amended spec as well, and the{mockups}controller finds the mockups still meet it, so nothing is written.
No write in this variant removes or reverses a stated criterion, so the run makes no stop. The developer’s commit is the only person’s act in it, and nothing the team had written down is overturned.
The mockups do not decide where the rule lands. If a mockup happens to depict an exact multiple, its controller answers that it is affected, design update is routed as in variant A, and design update amends the drawing once the spec is amended. That write reverses a drawing design implementation checks the code against, so it stops for approval. There the stop is the only one in the run, and it is the one place a stated rule is overturned, so it is not ceremony. Either way the rule lands in the spec, because missing strain comes from the criteria the spec’s controller derives from the intent, not from what a drawing depicts.
Settled state
Section titled “Settled state”The same as variant A, with no approval recorded.
Status: Holds
Section titled “Status: Holds”Missing strain gives the intent a home. The status rests on two judgements. The spec’s
controller derives the criteria from feature delivery’s intent, so an unstable distillation
or an unstable derivation produces unstable missing strain. And that {mockups} is too
coarse for a page-count rule is a call about
levels, which the
model has not said how to make across artifact types. The mockups’ own controller now makes
that call, which is where the knowledge of the set sits.
The run passing without a stop rests on a third judgement, the one variant A’s docs write rests on: that the help page’s criterion refers to the spec’s rule rather than restating it.
The stop on a mockup that depicts an exact multiple is why variant A cannot be settled by
skipping downstream stops. The same write to {mockups} is ceremony in variant A and the
only check in this case. What tells them apart is a person. In variant A the product owner
was asked at {spec} and pre-approved the rest. Here the spec’s write stops nobody, so no
approver has spoken, nothing is pre-approved, and the default stops at {mockups}.
What it tests
Section titled “What it tests”- Candidates found by lookup. Both variants find the same two upstream candidates. The amended spec finds docs update and design update downstream, with nothing having to notice the docs first.
- Missing strain. Variant B was a gap until the kind existed. Strain-only selection found nothing, because the spec stated no rule the fix could break.
- The leash against the
standing specification.
Variant A’s spec write reverses a stated rule and stops for approval. Variant B’s only
adds and proceeds. The mockups’ write in variant A reverses a drawing and proceeds only
because the product owner pre-approved it at
{spec}. In variant B the same kind of write has no pre-approval, and it stops. - Replacement under the leash. The round-up rule removes round down, plus one rather than joining it, and the write that makes that replacement is the one the product owner approves.
- The contradiction case. In variant A the code contradicts a rule
{spec}states, across two units of change. Which kind of strain that is remains open. - Controllers answer for their own sets along a workflow with no inputs. Feature delivery asks the spec’s controller, then the PRD’s, which is too coarse and passes the question up.
- How workflows are selected. Design update is reached because design implementation routes its affected input to the input’s owner, and the ledger holds it until the spec it reads is written.
- Direction lives in the workflow. Design implementation reads the mockups, so the page indicator can only change through the spec and design update.
- Criteria derived before the replay. The empty-list case comes from the criteria the spec’s controller derives, not from the fix, and reconciliation adds it to the fix.
- A triangle of connections.
{spec}reaches{code, test}directly and through{mockups}, so two workflows reconcile the same code, and their writes join at one controller. - A replay starts at the highest affected set.
Feature delivery replays from
{spec}, not from{PRD}, and the settled state leaves the PRD unchanged. - Obligations and tickets. The what-is story mentions a ticket that was never opened. In the workflow catalog a follow-up ticket is how a team records an obligation, not an artifact-set, so this example leaves it out.