Capstone Extension Guide¶
Page Maps¶
graph LR
family["Reproducible Research"]
program["Deep Dive Make"]
section["Capstone"]
page["Capstone Extension Guide"]
capstone["Capstone evidence"]
family --> program --> section --> page
page -.applies in.-> capstone
flowchart LR
orient["Orient on the page map"] --> read["Read the main claim and examples"]
read --> inspect["Inspect the related code, proof, or capstone surface"]
inspect --> verify["Run or review the verification path"]
verify --> apply["Apply the idea back to the module and capstone"]
Use this page before changing the capstone. The goal is not to freeze the repository. The goal is to keep new work readable, provable, and clearly owned two years from now.
Start with a behavior delta¶
Write the change as observable behavior before choosing files:
current caller behavior:
requested caller behavior:
behavior that must remain unchanged:
new failure that must be rejected:
evidence required before adoption:
“Add a script” or “split the Makefile” describes implementation movement. It does not say what the repository should do differently or why a learner should trust the result.
What must survive every change¶
Unless the course itself is being redesigned, a capstone change should preserve these properties:
- truthful dependency modeling
- atomic publication for real outputs
- serial and parallel equivalence where the build claims it
- a small public target surface
- a repository shape you can still audit end to end
If a proposed change weakens one of those, treat that as a design change, not routine maintenance.
Classify the change by contract impact¶
| Change class | Example | Minimum review obligation |
|---|---|---|
| internal mechanism | replace repeated recipe text with a bounded macro | prove public behavior and expansion remain equivalent |
| graph contract | add a generated input or change an edge | prove clean, affected-change, unrelated-change, and unchanged rerun behavior |
| public API | add, rename, or alter a supported target | update help, callers, contract audit, and compatibility decision |
| environment contract | require a new tool or feature | add an early capability gate and observable identity |
| evidence contract | change a report field or acceptance rule | preserve schema intent and prove dishonest evidence is rejected |
| release contract | change membership, install mapping, or candidate identity | exercise production, consumer verification, rejection, and containment |
| ownership handoff | move behavior to another tool or service | define immutable input, receipt, retries, and proof on both sides |
The class is determined by what callers and reviewers must now rely on, not by the number of edited lines.
Put changes in the right place¶
| If you are changing... | Start in... | Why |
|---|---|---|
| public target names or user-facing help text | capstone/Makefile |
the public contract belongs at the top level |
| tool, shell, or portability policy | capstone/mk/contract.mk |
policy should stay explicit and centralized |
| reusable recipe helpers | capstone/mk/macros.mk or capstone/mk/common.mk |
reuse belongs in named helpers, not copied shell |
| discovery or object selection | capstone/mk/objects.mk |
graph membership should have one owning file |
| hidden inputs or rebuild state evidence | capstone/mk/stamps.mk |
state tracking should stay reviewable |
| proof behavior | capstone/tests/run.sh |
proof belongs to the harness, not to prose |
| a new failure specimen | capstone/repro/ plus Capstone Proof Guide |
failure specimens should stay isolated from the healthy build |
Build an impact map before editing¶
flowchart LR
behavior["Behavior delta"]
callers["Affected callers"]
owner["Owning boundary"]
graph["Changed inputs or outputs"]
proof["Proof obligations"]
docs["Learner route and handoff"]
behavior --> callers --> owner --> graph --> proof --> docs
For each arrow, name one file or record. If the proof arrow has no destination, the change is not ready to implement.
Good capstone changes¶
These usually improve the repository:
- clarifying a target description or route page
- adding one more explicit dependency or boundary file
- improving a proof message without weakening the check
- adding a distinct repro that exposes a real failure class
- tightening file ownership so review requires less guessing
Risky changes¶
Slow down when a change does any of these:
- adds abstraction that hides the graph more than it reduces repetition
- turns internal helper behavior into an undocumented public surface
- weakens selftest because one environment is inconvenient
- mixes proof residue into source or release identity
- adds repository bulk without adding a sharper lesson
Worked extension: add generated build metadata¶
Assume the requested behavior is:
appshould embed a build channel selected byCHANNEL=stable, and changing the channel should rebuild only the generated metadata and its consumers.
This is not merely “add a generator.” It changes the graph and environment contract.
Define ownership¶
| Responsibility | Durable owner |
|---|---|
| accepted channel values and default | explicit policy near the public contract |
| channel identity visible to Make | content-bearing manifest or signature-specific stamp |
| generated header bytes | one script under scripts/ |
| generated-header target and publication | one Make rule |
| consumer relationship | depfile or explicit prerequisite |
| rebuild-scope proof | focused incremental test |
| release membership | existing package policy, changed only if the header belongs in the candidate |
Do not read CHANNEL only inside a recipe. Make cannot invalidate an existing output
from state it never modeled.
Predict the evidence¶
Before implementation, write the expected matrix:
| Transition | Expected work |
|---|---|
clean → CHANNEL=stable |
generate metadata and build consumers |
| stable → stable | no metadata generation or consumer rebuild |
| stable → candidate | regenerate metadata and rebuild only consumers |
| candidate → candidate | no work |
| source change unrelated to metadata | rebuild only its ordinary consumers |
The matrix guards against both stale output and the lazy repair of rebuilding everything.
Choose publication behavior¶
The generator writes to a process-local candidate. The Make rule renames the candidate only after successful generation. A failing generator must leave the previous accepted header intact or leave no header on a clean build.
Add rejection evidence¶
Test at least:
- an unsupported channel is rejected before a misleading artifact is published
- a generator failure does not expose partial header content
- unchanged state converges
- a channel change produces the predicted bounded rebuild set
Only after these checks should help text or learner routes present the feature as part of the capstone contract.
Worked handoff: move remote publication out of Make¶
The capstone intentionally stops at local release acceptance. If a surrounding system later owns publication, keep Make responsible for producing an accepted candidate and an immutable digest-bound record. The receiver owns upload, remote retry, and remote receipt.
The boundary needs:
| Sender provides | Receiver returns |
|---|---|
| candidate path or transport reference | destination identity |
| candidate digest | received digest |
| local acceptance record | publication status |
| package-policy identity | remote receipt identity |
Make may invoke the receiver, but it should not infer remote success from a zero exit alone. The durable handoff evidence is a receipt bound to the same candidate digest.
Review checklist before you commit¶
Answer these in your own words:
- what review or maintenance question became easier after this change
- which file now owns the behavior
- which proof route still corroborates the claim
- whether the filename and commit message would still make sense later
- whether the change can be understood without oral history
If you cannot answer those, the change is probably not placed cleanly yet.
Require an executable rollback¶
Rollback is not “revert if needed.” Record:
- which public behavior returns to its earlier contract
- which generated outputs or evidence schemas become incompatible
- how stale artifacts are detected rather than silently reused
- which proof route must pass after rollback
For an ownership handoff, keep the old producer available until the receiver can prove equivalent identity, failure reporting, and retry behavior. Retirement follows evidence; it does not create it.
Best rerun commands¶
From repository root:
make PROGRAM=reproducible-research/deep-dive-make capstone-walkthrough
make PROGRAM=reproducible-research/deep-dive-make inspect
make PROGRAM=reproducible-research/deep-dive-make test
make PROGRAM=reproducible-research/deep-dive-make proof
Those four routes cover entry, public contract, executable proof, and steward review.
Add the narrow route for the changed boundary before running all four. A generated-input change needs incremental evidence; a release-policy change needs release selftests; an architecture change needs the architecture contract audit.
Extension handoff¶
Finish with:
behavior delta:
change class:
owning files:
public compatibility decision:
new negative control:
proof route and evidence path:
rollback trigger and command:
remaining unsupported claim:
The handoff is complete when another maintainer can challenge the change without guessing which command, artifact, or acceptance rule matters.