Worked Example: Production Simulator¶
Page Maps¶
graph LR
family["Reproducible Research"]
program["Deep Dive Make"]
section["Determinism Debugging Self Testing"]
page["Worked Example: Production Simulator"]
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"]
This file gathers the module around the runnable course capstone.
Choose one command boundary¶
From the repository root, use the program wrapper:
From programs/reproducible-research/deep-dive-make/capstone/, use the capstone’s GNU
Make targets directly:
Do not mix the two working directories in one evidence note. Record the command, directory, revision, and initial worktree so another learner can replay the same request.
Predict the evidence route¶
Before running a demonstration, complete one row:
| Claim | Request | Expected evidence | Controlled challenge | Explicit limit |
|---|---|---|---|---|
| discovery order is canonical | discovery-audit |
sorted-list checks exit zero | inspect membership separately | does not prove membership policy |
| unchanged build converges | selftest-report |
convergence pass and query exit | hidden changing input | bounded to fixture and tools |
| schedules preserve declared artifacts | selftest-report |
matching governed inventories | controlled artifact drift | bounded to two tested schedules |
| generated rules remain optional | abstraction-contract-audit |
explicit/generated contract bundle | dishonest abstraction fixture | does not endorse every eval use |
The prediction keeps a large capstone from becoming a tour of commands without claims.
First see unstable discovery in isolation¶
Create three files in an empty directory:
Compare two Make variables:
SHELL_SRCS = $(shell find src -name '*.c')
STABLE_SRCS := $(sort $(wildcard src/*.c))
.PHONY: show
show:
@printf 'shell: %s\n' "$(SHELL_SRCS)"
@printf 'stable: %s\n' "$(STABLE_SRCS)"
The find command’s order is not a portable contract. Sorting a rooted file set gives
the build a canonical order. Stable order matters when the list influences link order,
manifests, generated source, or evidence comparisons.
Layout¶
capstone/
Makefile
mk/
common.mk
objects.mk
stamps.mk
macros.mk
rules_eval.mk
src/
main.c
dynamic/
dyn1.c
dyn2.c
include/
util.h
scripts/
gen_dynamic_h.py
tests/
run.sh
This simulator matters because it combines deterministic discovery, generation,
selftesting, and an optional eval surface in one place you can actually inspect.
The tree above is not hypothetical. It is
programs/reproducible-research/deep-dive-make/capstone/ in this repository.
A graph view of the simulator¶
flowchart TD
all["all"] --> app["app"]
app --> main["build/src/main.o"]
app --> util["build/src/util.o"]
app --> dyn1["build/src/dynamic/dyn1.o"]
app --> dyn2["build/src/dynamic/dyn2.o"]
main --> hdr["build/include/dynamic.h"]
main --> flags["build/flags.stamp"]
util --> flags
dyn1 --> flags
dyn2 --> flags
This graph is useful because it makes three Module 03 concerns visible at once:
- discovery affects which object targets exist at all
- generated headers must be trustworthy inputs, not fragile side effects
- semantic flags still belong in the object-file contract
What to inspect first¶
Start with these questions:
- which lists are rooted and sorted
- which generated artifacts are published atomically
- which hidden inputs are modeled
- which targets form the public build contract
This worked example is the concrete home for the rest of the module.
Read the simulator in this order¶
Makefilefor public targets and top-level intentmk/common.mkfor stable knobsmk/objects.mkfor rooted and sorted discoverymk/stamps.mkfor modeled hidden inputsmk/macros.mkfor small reusable invariantsmk/rules_eval.mkonly after the core build is already understood
That order matters because it keeps the graph visible before you look at optional abstraction.
Six demonstrations to run¶
Demonstration 1: Prove stable discovery¶
Read mk/objects.mk, then run:
gmake discovery-audit
mkdir -p ../../../../artifacts/learning/deep-dive-make/module-03
gmake -pRrq all > ../../../../artifacts/learning/deep-dive-make/module-03/capstone.database.txt
Query mode can return 1 when a target needs work; preserve that exit separately rather
than assuming the database was not written. In the saved database, find SRCS,
DYN_SRCS, OBJS, and DYN_BINS.
Write two conclusions:
- The lists are rooted and canonically ordered.
- The audit does not prove every matching
src/dynamic/*.cfile is an intended member.
Then read the repository layout and target contract to decide whether “every matching dynamic source is owned” is the actual policy. This separates ordering proof from membership review.
Demonstration 2: Prove a no-op run¶
Use a copied workspace or the selftest harness rather than cleaning a working tree that contains user state. After the first successful build, run:
The trace should contain no rebuilding work, and query exit should be zero. If it is
1, preserve the trace before another build changes the state. If it is 2, diagnose a
Make error rather than calling the graph non-convergent.
Demonstration 3: Trace one rebuild properly¶
Use an isolated copy. Change one declared source without changing its filename, predict the affected targets, and run:
Build a five-line causality note:
requested target:
changed prerequisite:
trace line naming the update:
downstream artifact inspected:
unchanged replay query exit:
Do not hand in the complete trace as the explanation. Select the line that joins the changed input to one rebuilt target, then show that the next unchanged request converges.
Demonstration 4: Prove the build system with selftest¶
Run:
This should prove convergence, serial/parallel equivalence over 13 declared artifacts,
the trace guardrail, hidden-input detection, and the quarantined eval check. It does
not prove runtime correctness; gmake test owns that separate claim.
Open the report under:
Read summary.txt before commands.txt, logs, inventories, or the workspace. The summary
tells you which deeper evidence is relevant.
Complete this ledger while reading:
| Check | Reached state | Command evidence | Result evidence | Honest non-claim |
|---|---|---|---|---|
| convergence | ||||
| serial/parallel equivalence | ||||
| trace guardrail | ||||
| hidden-input detection | ||||
| quarantined eval |
If a row is NOT_RUN, leave its evidence cells empty. Do not borrow evidence from a
different run.
Demonstration 5: Check that optional eval stays optional¶
Run:
Read mk/rules_eval.mk and write the explicit rule equivalent for one generated target.
Then inspect the abstraction audit under the repository artifact directory. The point is
to prove that:
- the core build does not depend on the generated demo surface;
- the enabled target family is finite and attributable;
- explicit and generated implementations preserve the governed contract;
- a dishonest generated surface is rejected by the audit’s own tests.
Demonstration 6: Read the negative test¶
Open tests/run.sh and find the hidden-input check. Before running it, explain what defect
the test introduces and what observation should make the test pass. A negative test is
useful only when you understand why failure is expected.
Write the detector logic in four lines:
healthy state accepted:
fault mechanism:
expected stopping boundary:
observation that counts as successful rejection:
If the fault triggers a different earlier error, it did not prove the intended detector.
Prove that the proof rejects dishonesty¶
Run:
The test suite runs one truthful case and one controlled schedule-drift case. It does not ask you to edit the live Makefile or remember to restore a defect. Inspect:
Start with summary.txt. It should name schedule equivalence as the stopping boundary.
Then read schedule-comparison.json; the changed path should be
build/include/dynamic.h. Confirm that trace, hidden-input, and eval checks are
NOT_RUN, not FAIL.
Finally, compare the two inventory entries for that path and inspect the preserved
parallel file under workspace/. This is mutation testing at a human scale: the test
proves that the schedule-equivalence gate is connected to the artifact contract it
claims to protect.
If you later design another controlled defect, require the same evidence shape:
Claim violated:
Expected stopping boundary:
Expected diagnostic category:
Observed report:
Later checks not reached:
Preserved evidence:
Build a causality note from one trace¶
Choose one rebuilt target and write no more than five lines:
- requested target
- prerequisite that changed
- trace line that names the reason
- downstream target made stale by that rebuild
- whether the next identical run converged
This bounded note is more useful in review than a complete terminal transcript. It turns diagnostic output into an explanation another learner can check.
Review the example without rerunning¶
After completing the demonstrations, pretend you received only the saved evidence. Ask:
- Can you identify the source revision and invocation directory?
- Can you tell which claims were reached?
- Can you distinguish a command log from an artifact inventory?
- Can you locate the first failed boundary in a rejected run?
- Can you tell which later checks did not run?
- Can you state why the negative case counts as a successful detector test?
- Can you name at least two unsupported conclusions?
If the answers require terminal history or memory, the evidence packet is incomplete.
Completion record¶
Discovery policy and observed list:
No-op query result:
One causal rebuild note:
Accepted selftest summary:
Rejected harness-test boundary:
Generated-rule explicit equivalent:
Abstraction audit decision:
Known environment and schedule limits:
This record is the handoff from the worked example to the exercise set.
What this example should teach you¶
By the time you finish this file, you should be able to point at the simulator and say:
- where determinism is enforced
- where debugging evidence comes from
- where the CI-facing target surface begins
- where selftest proves the build
- where abstraction is kept under control instead of controlling the build
- which claim is proved by each selftest section
- how a rejected selftest remains reviewable without rerunning it
- why the discovery audit proves order but still requires membership review
- how explicit/generated comparison bounds an abstraction claim