Module 07: Build Architecture, Layered Includes, and Build APIs¶
Architecture defects are often successful builds. A private helper creates the main artifact but omits verification. A release include changes ordinary compilation. Two sources collapse into one output, and Make faithfully builds the one target it was given.
Module 07 teaches you to review those failures as ownership contracts:
- external callers depend on declared targets
- policy values propagate only through their owned target branches
- output paths preserve source ownership as the repository grows
- macros remain inside boundaries that can be inspected and tested
The standard is not “the Makefiles are organized.” It is:
A maintainer can name each owner, follow each consumer, and inspect evidence that the boundary holds.
The architecture questions this module now tests¶
The executable audits establish four existing contracts. The reading and practice extend them with five questions that become important as a repository grows:
- Does the public API define supported variables, outputs, evidence, and failure meaning, or only target names?
- Can two requested branches give a shared prerequisite conflicting target-specific policy?
- Is the source-to-output mapping total, injective, owner-preserving, and stable for every supported discovery root?
- Can a reviewer follow macro text through both
evalexpansion stages and identify every generated target and parse-time side effect? - Do include layers follow an allowed dependency direction, or do policy, graph, publication, and interface layers reach back into one another?
Those are not requests for more files. They are tests of whether the architecture still has one explainable owner for each decision.
flowchart TD
api["public target and parameter API"] --> graph["owned graph"]
policy["policy layer"] --> graph
roots["declared discovery roots"] --> mapping["total, injective mapping"]
mapping --> graph
macro["bounded rule generator"] --> graph
graph --> publication["process-local candidate publication"]
publication --> evidence["promised artifacts and proof"]
review["dependency-direction review"] -.checks.-> api
review -.checks.-> policy
review -.checks.-> mapping
review -.checks.-> macro
Begin with the executable audits¶
From the repository root:
cd programs/reproducible-research/deep-dive-make/capstone
gmake architecture-contract-audit
gmake abstraction-contract-audit
The bundles are written under the repository’s artifacts/ directory:
artifacts/audit/reproducible-research/deep-dive-make/architecture-contracts/
artifacts/audit/reproducible-research/deep-dive-make/abstraction-contracts/
Read each bundle in this order:
route.txt- its named audit guide
summary.tsv- one case in
report.json - that case’s trace
- its specimen and generated workspace
The architecture audit contains six cases:
| Boundary | Defect model | Control model |
|---|---|---|
| build API | private caller bypasses verification | declared caller receives full evidence |
| include ownership | release include mutates global flags | release target scopes its policy |
| output ownership | two sources collapse to one path | source namespaces survive in outputs |
An aggregate PASS includes three deliberately reproduced defects. Always quote the
finding and observation, not only the result column.
The abstraction audit then asks a narrower question: what changed when repeated explicit rules became a macro?
| Model | Required observation |
|---|---|
| explicit rules | target, prerequisite, policy, and publication contracts are visible |
| bounded macro | the same contract survives and the call domain remains explicit |
| opaque macro | hidden targets and parse-time policy mutation are reproduced |
This bundle has three PASS rows, including the opaque counterexample. A passing
counterexample means the audit observed the promised defect; it does not approve that
design.
flowchart LR
claim["ownership claim"]
defect["successful defect"]
control["enforced control"]
trace["trace"]
artifact["artifact evidence"]
judgment["review judgment"]
claim --> defect
claim --> control
defect --> trace
control --> trace
trace --> artifact --> judgment
One serious-session route¶
Allow about six hours if you are studying independently.
| Time | Work | Evidence to keep |
|---|---|---|
| 45 minutes | run both audits and interpret all nine rows | annotated summaries |
| 50 minutes | trace private and declared callers | help, caller, and artifact comparison |
| 60 minutes | compare global and target-scoped policy | four flag artifact values |
| 55 minutes | derive both source-to-output mappings | source/output counts and owner files |
| 55 minutes | compare explicit, bounded, and opaque generation | database map and side-effect table |
| 65 minutes | execute the tangled-layout refactor | before/after contract table |
| 70 minutes | complete the ten exercises | reproducible review packet |
| 30 minutes | run both self-tests and write the review | rejection results and remaining risk |
If you have only one hour, run the audit, read summary.tsv, and follow one family from
specimen to trace to artifact. Do not skim every page and omit the evidence.
Entry diagnostic¶
Before reading the lessons, answer these against one Make repository:
| Prompt | Your initial answer |
|---|---|
| Which variables may callers set, and which values are rejected? | |
| What artifacts and evidence does each public target promise? | |
| Can an ordinary and specialized goal share a prerequisite built under different policy? | |
| Can two distinct selected sources ever map to one output key? | |
What text remains after the first and second expansion of one eval macro? |
|
| Which include directions are forbidden? |
Keep the answers. The module is incomplete if the final answers are only more polished phrasing. Each answer should point to a rule, database entry, artifact, or rejection test.
Follow four contracts through the build¶
flowchart TD
caller["external caller"] --> public["declared target"]
public --> graph["owned graph"]
base["base policy"] --> graph
release["release specialization"] --> release_branch["release branch"]
sources["rooted sources"] --> map["namespace-preserving map"]
map --> graph
explicit["explicit rule contract"] --> bounded["bounded macro"]
bounded --> graph
graph --> evidence["verification and owned artifacts"]
For every arrow, name:
- the file or target that owns the decision
- the consumer that relies on it
- the evidence that would change if the boundary leaked
Do this before moving files. A new include layout is not evidence that ownership improved.
Build one architecture packet¶
Keep a single packet:
build-architecture-review/
public-api.md
policy-propagation.md
source-output-map.tsv
generated-rule-map.txt
layer-dependencies.tsv
acceptance.md
evidence/
The packet must answer:
| Claim | Minimum evidence |
|---|---|
| caller uses supported API | help or contract manifest, caller command, promised outputs |
| parameter is owned | origin/flavor/value, validation behavior, affected artifacts |
| specialization is unambiguous | isolated ordinary/specialized runs and combined-goal challenge |
| mapping preserves owners | selected-source and unique-output counts plus reverse-owner lookup |
| macro preserves contract | explicit baseline, expansion trace, database map, artifacts |
| layer direction holds | include/variable/rule dependency map and a deliberate forbidden-edge rejection |
An absent row is not “obvious architecture.” It is an unreviewed boundary.
Independent-study checkpoints¶
Pause after each pair of lessons:
- after public API and layering, explain target and variable ownership to a reviewer using
only
public-api.mdandpolicy-propagation.md - after discovery and macros, reconstruct one output path and one generated rule from their explicit inputs
- after architecture review, inject one forbidden dependency direction and require the gate to reject it
- after the worked example, ask another learner to locate the evidence for each accepted boundary without opening the narrative first
These checkpoints replace instructor interpretation with durable artifacts.
Study route¶
Read the pages in this order on the first pass:
- Public Targets and Build API Design
- Layered Includes and Responsibility Boundaries
- Discovery, Namespacing, and Repository Growth
- Macros, call, and Reuse Without Opaqueness
- Architecture Review and Maintenance Discipline
- Worked Example: Refactoring a Tangled Build Layout
- Exercises
- Exercise Answers
- Glossary
The first three pages share the architecture audit bundle. The macro lesson uses the abstraction audit to compare explicit, bounded, and opaque rule generation. The review lesson and worked example combine both evidence families into one acceptance decision.
The ten files in this module¶
- Overview (
index.md) - Public Targets and Build API Design
- Layered Includes and Responsibility Boundaries
- Macros, call, and Reuse Without Opaqueness
- Discovery, Namespacing, and Repository Growth
- Architecture Review and Maintenance Discipline
- Worked Example: Refactoring a Tangled Build Layout
- Exercises
- Exercise Answers
- Glossary
How to use the file set¶
| If you need to... | Start here |
|---|---|
| define which targets humans and automation may rely on | Public Targets and Build API Design |
split a large build into mk/*.mk layers without hidden mutation |
Layered Includes and Responsibility Boundaries |
| reduce duplication without turning the build into a macro puzzle | Macros, call, and Reuse Without Opaqueness |
| keep source discovery and target names stable as the repository grows | Discovery, Namespacing, and Repository Growth |
| review a build layout before it becomes institutional folklore | Architecture Review and Maintenance Discipline |
| see the whole module in one realistic refactor story | Worked Example: Refactoring a Tangled Build Layout |
| test your own understanding | Exercises |
| compare your reasoning against a reference | Exercise Answers |
| stabilize the module vocabulary | Glossary |
Commands to keep close¶
Run these from capstone/ unless a lesson says otherwise:
gmake architecture-contract-audit
gmake architecture-contract-selftest
gmake abstraction-contract-audit
gmake abstraction-contract-selftest
gmake --trace verify
gmake -pRrq -f Makefile
gmake -n all
The self-tests matter because they mutate disposable controls and require both audits to reject dishonest evidence.
Learning outcomes¶
By the end of this module, you should be able to:
- distinguish a reachable target from a declared public promise
- define supported target parameters, outputs, evidence, and failure meaning
- prove that external callers receive every artifact promised by their target
- explain how include parsing and target-specific variables produce different policy scope
- detect shared-prerequisite ambiguity across specialized goal branches
- detect output collisions by mapping cardinality and artifact owner identity
- prove mapping totality, injectivity, reversibility, and extension stability
- compare an explicit rule contract with bounded and opaque generation
- trace both
evalexpansion stages and use process-local publication candidates - keep macros only when expanded rules, policy, and public surfaces preserve that contract
- reject forbidden dependency directions between architecture layers
- write a reproducible architecture review that cites findings, mutations, limits, and risk
Exit standard¶
Do not move on until all of these are true:
- you can explain why
PRIVATE_TARGET_DEPENDENCE_REPRODUCEDis a defect despite exit zero - you can identify the artifact proving
CROSS_LAYER_POLICY_LEAK_REPRODUCED - you can derive where the flat mapping loses one source owner
- you can show equal source and unique-output counts in the namespaced control
- you can prove that a bounded macro preserves the explicit prerequisite and policy contract
- you can explain why target-specific policy is unsafe for one shared artifact requested under conflicting branches
- you can identify the hidden target and policy side effect in the opaque counterexample
- you can show that generated publication candidates cannot collide across concurrent recipe processes
- you can reject one forbidden include-layer dependency with a named diagnostic
- you have completed all ten exercises without relying on an instructor
gmake architecture-contract-selftestpassesgmake abstraction-contract-selftestpasses- your review packet names a claim, observation, conclusion, limit, controlled mutation, and at least one unresolved risk