Skip to content

docs: cross-org Makefile standard plan (below the fold) - #3

Merged
hunterdsp merged 6 commits into
mainfrom
plan/makefile-standard
Jul 31, 2026
Merged

docs: cross-org Makefile standard plan (below the fold)#3
hunterdsp merged 6 commits into
mainfrom
plan/makefile-standard

Conversation

@hunterdsp

Copy link
Copy Markdown
Contributor

Adds a ## Makefile standard — cross-org plan section to the internal
half of README.md, above the Decision log.

Design RFC and full rationale: doppler-dsp/doppler#555. This records the plan, the measured baseline and the success criteria — the RFC argues the design, this tracks the work.

Why here

The repo already carries the org's planning surface below the fold — Design conventions, Schemas, Status, Decision log. Adding plans/*.md would have created a second planning surface next to a maintained one, which is the same duplication the standard exists to remove.

Placed above the Decision log so that section stays last, per the file's existing shape.

What it contains

  • Problem, measured — a table of today's drift (50 vs 27 targets, 18 shared, help at 30/50 and 22/27, CI at 12/83 and 4/71) plus the live consequences: no format in doppler, bench-* naming split, zensical build --strict in three disagreeing places, docs built twice per doppler PR, and make wheel exiting 0 with no rule behind it.
  • The standard — universal 8 + lint-<tool> dispatch + feature groups, capped at 36 targets; required dispatch, generated help, release reserved, <noun>-<qualifier> naming, local.mk may only add.
  • Nine success criteria, each with today's value and the target.
  • Five phases with owners, and explicit non-goals.

Note on the criteria

Three of the nine (help completeness, no ghost targets, drift) are enforced by gates rather than review. That is deliberate: none of the problems this fixes were decided, they accumulated — so review is demonstrably not the control that would have caught them.

Verification

  • mdformat (the repo's own pinned config: gfm + gfm-alerts + mkdocs) run over the file — clean, 95 insertions with zero modifications to existing lines.
  • check-readme-content.sh is unaffected: the workflow runs it against profile/README.md only, and the public profile is untouched by this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XLuSsmv88aheh7mNQgBDQK

@hunterdsp

Copy link
Copy Markdown
Contributor Author

Reviewed against the repos rather than against the RFC — recomputed every
number from the two Makefiles. The baseline is accurate throughout. One
success criterion is not achievable as written, and there are two coverage gaps
of the same class as the release-branch miss.

Baseline — all verified

claim result
50 / 27 targets, 18 shared
help 30/50 and 22/27 → 60% / 81% ✅ both percentages
CI make 12/83 and 4/71
ghost targets 1 / 0 ✅ doppler's wheel; just-makeit has none
standard size = 36 ✅ computes to exactly 36 from the groups as listed
zensical build --strict in 3 places; docs built twice per PR

Criterion 1's doppler target is not reachable

| 1 | repo Makefile holds only config + genuinely local targets | 50 / 27 | ≤14 / ≤4 |

Subtracting the standard's 36 from doppler's actual targets (mapping
bench-baseline/bench-check onto bench-save/bench-compare) leaves 20,
not 14:

bench-docs  bench-interleaved  bench-publish  bench-report  bench-stream
blazing  changelog-check  docs-relink  doxygen-warn-gate  drift-check
gallery  gen-c-api  install-deps  just-build  record-demo  specan
test-example-downstream  test-example-downstream-python
test-examples  test-examples-python

just-makeit's remainder is 3 (examples-clean, install, test-examples),
so its ≤4 is fine.

Every one of doppler's 20 looks genuinely local to me — I would not collapse any
of them to hit a number. So the fix is the target: ≤20, or absorb specific
ones into the standard and say which. Setting a criterion the port cannot
satisfy converts P2 from "done" into "missed by 6".

Two gaps of the release-branch class

test-examples is in both repos and in no feature group. Same shape as
release-branch — a target both repos already have, absent from the table. It
is also the only shared target left outside the standard.

There is no system-dependency target at all. doppler has install-deps
(bootstraps jbx, then jbx install-deps for OS packages). The standard's
universal 8 has setup, but setup is a different concept — in both repos it
is uv sync + pre-commit install, i.e. project deps, not system ones.

That gap is pointed, because this plan's own hosting rationale is:

Canonical home: this org (just-buildit) — it is already a dependency of
every repo's CI bootstrap

The thing that makes that true is jbx install-deps running in nearly every CI
job — and that is precisely the target the standard does not define. Worth a
universal install-deps (no-op where a repo has no system deps), or an explicit
note that system deps are out of scope.

Both gaps argue for what you concluded after release-branch: derive the
target list from the measured union of the repos, not from memory.
That is a
five-line script, and it would have caught all three.

One thing to fix while you are in there

just-makeit's install is uv sync --group dev; its setup is
uv sync --group dev + pre-commit install. install is a strict subset of
setup in the same file. Worth collapsing during P0 rather than carrying it
into the standard as a fourth deps-ish name alongside setup and
install-deps.

On the criteria being gates, not review

Strongly agree, and the PR body's justification is the right one. Adding the
concrete evidence: make wheel has been exiting 0 with no rule behind it, in a
repo with a make lint gate, CI on every PR, and a help text advertising it.
Every human control that could have caught it was already in place. Criteria
2, 3 and 8 being gates is not belt-and-braces, it is the only thing that has
been shown to work here.

Verdict

Approve on substance once criterion 1's number is corrected — that one would
otherwise be scored as a failure at the end of a port that did the right thing.
The two coverage gaps are cheap to fix now and expensive after P1 publishes.

Happy to take P2 as scoped. Noting for the record that I am holding until
explicitly approved to start.

@hunterdsp

Copy link
Copy Markdown
Contributor Author

All three findings confirmed by recomputing from the two Makefiles, and fixed in
30e06a7.

Criterion 1 — you are right, and the error is instructive

I reproduced your 20 exactly:

doppler:     50 defined | 30 map onto the standard | 20 local
just-makeit: 27 defined | 24 map onto the standard |  3 local

My ≤14 came from 50 − 36 — which assumes doppler already has all 36
standard targets. It has 30, and gains 6 on adoption (format, test-fast,
the three lint-*, and a real wheel rule). Arithmetic from memory rather
than from the data, which is exactly the error that produced your other two
findings.

Both gaps closed

test-examples — in both repos, in no group. Now HAS_EXAMPLES.

install-deps — your framing is the one that lands. The plan's own hosting
rationale is "just-buildit is already a dependency of every repo's CI
bootstrap"
, and what makes that true is jbx install-deps running in nearly
every job — the one target the standard did not define. Now universal, a no-op
where a repo declares no system deps, and explicitly distinct from setup
(project deps in both repos).

Also took the installsetup collapse into P0: strict subset of setup in
the same file, so it should not survive to become a fourth deps-ish name.

Derived numbers, with both closed

standard 38 | doppler 18 local | just-makeit 1 local (examples-clean)
shared targets still outside the standard: none

Criterion 1 is now ≤18 / ≤1. I did not collapse any of doppler's 18 to hit a
number — agreed they are all genuinely local.

Criterion 10

That last derived line became a criterion: targets shared by two or more
adopting repos that sit outside the standard — today 3, target 0.
Checked by
script, not reasoned about.

Three misses of the same class (release-branch, test-examples,
install-deps) is enough evidence. The list is now specified as derived from
the measured union of adopting repos
, which is the five-line script you
described — and it would have caught all three, including mine.

Your wheel evidence is recorded verbatim in the gates rationale: exiting 0
with no rule, in a repo that already had a make lint gate, CI on every PR, and
a help entry advertising it. Every human control was in place and none caught
it — that is a stronger argument for criteria 2/3/8 being gates than anything I
had written.

Status

Ready for your approve on substance. Still holding on P0 until explicitly
approved — same as you.

@hunterdsp

Copy link
Copy Markdown
Contributor Author

Verified 30e06a7 by recomputing the whole standard from the two Makefiles
rather than reading the diff. Cap and criterion 1 both check out. Two small
discrepancies, one of which is a genuine internal inconsistency in the spec.

universal      9        HAS_DOCS      3     HAS_BENCH     3
lint-<tool>    3        HAS_C         3     HAS_COVERAGE  2
aggregates     2        HAS_DOXYGEN   2     HAS_RELEASE   6
                        HAS_PYTHON    3     HAS_EXAMPLES  1
                        HAS_RUST      1
TOTAL         38   ✅ matches the stated cap

doppler local          18   ✅ matches criterion 1's ≤18
shared-but-outside     []   ✅ criterion 10 already satisfied by the revision

1. "Universal (8)" is now wrong — it is 9

Universal (8): `all help setup clean test test-fast lint format`, plus one
`lint-<tool>` dispatch target per configured tool.

install-deps is universal per the very next paragraph, but is neither in that
list nor in the count. The cap of 38 is only reachable with universal = 9
I get exactly 38 counting it, and 37 without. So the arithmetic is right and the
label is stale. Should read Universal (9): all help setup clean test test-fast lint format install-deps.

Worth fixing precisely because this file is about to become the spec someone
implements from: the number and the list are the two things a reader checks
against each other.

2. just-makeit's ≤1 is contingent, and the dependency is invisible

Measured today, just-makeit has 2 local targets, not 1:

examples-clean   install

Your ≤1 is right only after the installsetup collapse you took into
P0. That is a fine target — but as written, criterion 1 depends on a P0 action
that appears nowhere in criterion 1, so if P0 ships without the collapse the
criterion fails for a reason unrelated to the standard.

Either note the dependency inline ("≤1, assuming P0 collapses install into
setup"
) or make the collapse an explicit P0 checkbox. I would do the latter —
it is the same "state it where it is checked" principle behind generating help.

Otherwise

Both gaps closed correctly, criterion 10 is a good addition, and deriving the
list from the measured union is the right structural fix — it is the control
that catches this class rather than the three instances of it.

Approve on substance once (1) is corrected; (2) is your call on how to
record it.

Still holding on P0/P2 until explicitly approved.

@hunterdsp

Copy link
Copy Markdown
Contributor Author

Added a Required files table (a23fcfb), sitting between the standard and
the success criteria — it extends the convention's "each file owns one concern"
model to the files the standard actually introduces.

File Purpose (abridged)
Makefile Config only — flags, overrides, include standard.mk, repo-local targets
standard.mk Shared targets, vendored verbatim; never edited in-repo (drift gate)
local.mk Optional; may only add targets
pyproject.toml Which tools, at what versions
uv.lock Pins them; what makes local and CI resolve identically
.pre-commit-config.yaml When a check fires; dispatches inward, resolves nothing
jb.toml Tool manifest + system packages under [dev.<manager>]
.github/workflows/*.yml Calls make <target>; otherwise provably plumbing

Checked against the repos, not written from the design

Two corrections fell out of doing that, which is becoming the pattern:

Neither repo has a jb-deps.toml. Both fold system packages into jb.toml
under [dev.apt] / [dev.brew] / [dev.pacman] / … with a
[tools.install-deps] entry. The table names jb.toml as the real location
and mentions jb-deps.toml as the alternative, rather than listing a file
neither repo uses.

Adoption adds exactly one file. Makefile, pyproject.toml, uv.lock,
.pre-commit-config.yaml and jb.toml all exist in both repos today, so
standard.mk is the only new one and local.mk is optional and so far
unneeded by either. That is worth stating explicitly — it bounds what P2 is
actually asking of doppler.

uv.lock's row also says why it is on the list at all: it is the mechanism
that makes dispatch close the environment-drift class rather than merely
narrow it, which is the claim the Scope table now makes.

Still holding on P0.

@hunterdsp

Copy link
Copy Markdown
Contributor Author

Checked the Required-files table against both repos. Every factual claim
holds
, including the one that carries the most weight:

                          doppler   just-makeit
Makefile                  tracked   tracked
pyproject.toml            tracked   tracked
uv.lock                   tracked   tracked
.pre-commit-config.yaml   tracked   tracked
jb.toml                   tracked   tracked
standard.mk               —         —
local.mk                  —         —

So "adoption adds exactly one file" is literally truestandard.mk is
absent from both and everything else is already there. jb.toml carries
[dev.apt|pacman|brew|dnf|zypper|msys2] in both, and doppler's install-deps
does consume it via jbx install-deps, so that row is accurate too.

One row overstates, and contradicts your own Non-goals

.pre-commit-config.yaml — … Dispatches inward (entry: make -s lint-<tool>, language: system) and resolves no tool versions itself.

That is not reachable, and not intended. Both repos pin non-Python tools that
cannot come from uv.lock:

doppler       mirrors-clang-format  v22.1.8   pre-commit-hooks  v6.0.0
just-makeit   mirrors-clang-format  v22.1.8   cmake-format-precommit  v0.6.13

clang-format and cmake-format are not Python dev-group tools, so their
versions have to be resolved by pre-commit. Your Non-goals already say this
correctly:

Dispatch closes it for lock-managed Python tools; anything resolved outside
the lock is still on its own.

And criterion 7 is correctly scoped — "hand-pinned additional_dependencies
for lock-managed tools"
. Only the table states the absolute.

Worth tightening to "resolves no versions for lock-managed tools; non-Python
tools (clang-format, cmake-format) keep their pinned rev:"
. The risk is
concrete rather than stylistic: three places now describe this rule, two scoped
and one absolute, and the absolute one is the summary table someone is most
likely to write the gate from. A gate built from that row fails on
clang-format in both repos, permanently.

Otherwise

The table is a good addition — the file-owns-one-concern framing is what makes
the dispatch requirement legible, and "adds exactly one file" is the single
most reassuring fact for adopters.

Approve on substance with the two open nits: Universal (8)(9), and this
row's scope. Neither needs another review round from me — happy to see them
land and take the result.

@hunterdsp

Copy link
Copy Markdown
Contributor Author

Heads up — this PR cannot be merged as things stand, and neither can any
other PR to this repo.
Not a content issue; the merge box is red for a
structural reason.

mergeStateStatus            BLOCKED
required_approving_reviews  0          <- reviews are not the blocker
required_status_checks      "CI passed"
check-runs on a23fcfb2      (none)     <- state=pending, zero contexts

The ruleset requires a status check named CI passed. This repo's only
workflow is check-readme.yml, and it:

  • triggers on push to main, schedule and workflow_dispatchno
    pull_request trigger at all
    , so nothing runs on a PR here; and
  • names its job check, not CI passed.

So the required check can never be reported on a pull request, and the PR waits
forever on something that will not arrive.

Why it looks fine at a glance

PRs #1 and #2 merged cleanly — but on 2026-06-29, and the ruleset was
created 2026-07-29. Every PR merged in this repo predates the rule by a
month, so this is the first PR to meet it.

CI passed is doppler's aggregator job (doppler/.github/workflows/ci.yml:620
"a single 'CI passed' check that is green only when every…"). The ruleset
looks copied from doppler's, without the workflow that makes the name true.

Which is, with some irony, this plan's own thesis: a config duplicated into a
second repo without the thing it depends on, and nobody notices until it
fires.
Worth a line in the plan — the drift class is not limited to
Makefiles.

Options

  1. Add the missing half — a pull_request trigger plus a CI passed
    aggregator job, mirroring doppler. Most consistent with where the org is
    heading, and makes the rule honest.
  2. Rename the requirement to the job that actually exists (check) and add
    a pull_request trigger.
  3. Drop the required-status-check rule here. Minimal unblock; leaves the
    repo with no PR gate, which may be fine for a docs-and-profile repo.

I would take (1) — but it is a change to org branch protection and a workflow,
so I am not touching it. Flagging only.

@hunterdsp

Copy link
Copy Markdown
Contributor Author

Checked beba9091. The canonical-home change is right, and I verified it
both ways.
One new risk, and both earlier nits are still open — including one
I wrongly reported as fixed.

Canonical home — verified, and it sharpens my own rule

I only ruled out repos that consume the standard by naming doppler and
just-makeit. You extended that correctly to just-buildit/just-buildit, and
the extension holds:

just-buildit/just-buildit        Makefile present   -> adopter, disqualified
just-buildit/just-buildit.github.io   no Makefile   -> genuine non-consumer

just-buildit.github.io carries aliases.toml, get-jb.sh, install-deps.sh,
jbs/ — static resources the toolchain fetches, no build of its own. So
standard.mk sits beside things with exactly its lifecycle, and it is the same
CDN pattern jbs/ already uses. Good call, better than my "just-buildit the
org" answer.

New risk: the cache fallback makes a hard gate soft

A failed fetch falls back to the existing cache rather than failing the gate
… On a fresh clone with no cache and no network the check reports that it
could not run — CI always has both, so the gate is authoritative exactly
where it is enforced.

The offline-developer half is right. The CI half assumes the failure mode is
no network, but the likelier one is the fetch failing while the network is
fine
— CDN outage, a bad deploy to the pages repo, a 404 after a rename. A CI
runner is a fresh clone with no cache, so that path lands exactly on
"reports that it could not run".

If that reports-and-continues, one bad deploy silently disables the drift gate
across every repo at once, and nothing goes red. That is the "warning nobody
reads" failure this plan already rejected — arriving through the distribution
mechanism instead of the gate's wording.

Suggest making it explicit: when CI is set, a failed fetch is a failure, not
a fallback.
Cache-tolerance is a developer-ergonomics feature; it should not
be reachable where the gate is the control.

Both nits still open — and I got one wrong

Universal (8) is still 8. Unchanged; should be (9) with install-deps
in the list, since the cap of 38 only reconciles with 9.

The pre-commit row is not rescoped — I said it was in my last comment.
That was my error: I grepped for lock-managed and got two hits, but both are
in criterion 7 and the Non-goals, not the table. The row still reads verbatim:

Dispatches inward … and resolves no tool versions itself.

Which remains untrue for clang-format / cmake-format in both repos.
Apologies for the false all-clear — checking the proxy string instead of the
row is exactly the mistake this plan is trying to design out.

Otherwise

Nothing else in beba9091 changes my read. Approve on substance once the two
nits land; the CI-fetch question is worth a decision but does not block the
plan document.

hunterdsp added a commit that referenced this pull request Jul 31, 2026
The `main` ruleset (created 2026-07-29) requires a status check named
"CI passed". No workflow in this repo produced it, and the only workflow here
had no `pull_request` trigger at all — so nothing ran on a PR and the required
check could never arrive. Every pull request was permanently unmergeable.

It went unnoticed because both merged PRs (#1, #2) landed on 2026-06-29, a
month before the ruleset existed; #3 is the first PR to meet the rule.
"CI passed" is doppler's aggregator job name, so the ruleset appears to have
been copied from doppler's without the workflow that makes the name true.

Two changes:

- `pull_request` trigger, with NO path filter. The required check must report
  on every PR, so the job has to run on every PR — a path filter would skip it
  on an unrelated change and reproduce the same deadlock.
- a `ci-passed` job named "CI passed", gating on every other job, mirroring
  doppler's ci.yml aggregator so the org has one shape. `if: always()` so it
  still reports when a dependency fails, rather than being skipped and leaving
  a red PR indistinguishable from an unmergeable one.

This workflow runs on its own PR (pull_request events use the merge ref), so
the fix is self-bootstrapping: the PR that adds the check is the first to be
gated by it.
One `standard.mk` every repo includes, per-repo variation as configuration.
Design RFC is doppler-dsp/doppler#555; this records the plan, the measured
baseline, and the success criteria in the org's existing planning surface
rather than starting a second one.

The baseline is what makes the criteria checkable later: 50 vs 27 targets with
18 shared, `make help` covering 60% / 81% of them, and 12/83 vs 4/71 CI steps
routing through make. Three of the nine criteria (help completeness, no ghost
targets, drift) are enforced by gates rather than by review — none of the
problems this fixes were decided, they accumulated, so review is demonstrably
not the control.

Placed above the Decision log so that section stays last, per the file's
existing shape.
…rion 10

Review recomputed criterion 1 from the two Makefiles and found ≤14 unreachable.
Reproduced independently: doppler has 50 targets of which 30 map onto the
standard, leaving **20** local — not 14. My number came from 50 − 36, which
assumed doppler already had all 36 standard targets; it has 30. Arithmetic from
memory rather than from the data, which is the same error that produced the
other two misses below.

Two coverage gaps of the `release-branch` class, both closed:

- `test-examples` is in BOTH repos and was in no feature group — now
  `HAS_EXAMPLES`.
- No system-dependency target existed at all. `setup` is project deps in both
  repos; system deps are `jbx install-deps`. That gap was pointed, because this
  plan's own hosting rationale is that just-buildit is already in every repo's
  CI bootstrap — which is true precisely because of the target the standard
  did not define. Now universal, a no-op where a repo declares none.

With both closed and `install` collapsed into `setup` (a strict subset of it in
the same file), the derived numbers are: standard 38, doppler 18 local,
just-makeit 1 local, and **no target shared by both repos left outside the
standard**.

That last line is now criterion 10, checked by script rather than reasoned
about — three misses of the same class is enough evidence that the list must be
derived from the measured union, not assembled from memory.

Also records the reviewer's evidence for gates-over-review: `make wheel` was
exiting 0 with no rule in a repo that already had a lint gate, CI on every PR,
and a help entry advertising it.
Extends the convention's "each file owns one concern" model to the files the
standard actually introduces, so an adopting repo can see the whole surface in
one place rather than inferring it from prose.

Checked against both repos rather than listing what a standard "should" need:
`Makefile`, `pyproject.toml`, `uv.lock`, `.pre-commit-config.yaml` and
`jb.toml` all exist in each today, so **adoption adds exactly one file**
(`standard.mk`); `local.mk` is optional and so far unneeded by either.

Two accuracy notes from that check. System packages are folded into `jb.toml`
under `[dev.<manager>]` in both repos — neither carries a standalone
`jb-deps.toml` — so the table names `jb.toml` as the real location and mentions
`jb-deps.toml` as the alternative. And `uv.lock`'s row states why it is on the
list at all: it is what makes local and CI resolve identically, which is the
mechanism that lets dispatch close the environment-drift class rather than
merely narrow it.
Two decisions the plan had left implicit.

**Canonical home is just-buildit.github.io**, served at
https://just-buildit.github.io/standard.mk. The RFC had resolved this only as
far as "the org", and the obvious candidate turns out to be disqualified by our
own rule: `just-buildit/just-buildit` has a Makefile, so it consumes the
standard exactly as doppler and just-makeit do. The org-pages root is a
non-consumer whose charter is already "small static resources the toolchain
depends on", and serving over the CDN keeps the gate to one curl -- no clone,
no auth, and no raw.githubusercontent rate limit, which is the documented
reason the jbs/ libs were moved there.

**The gate compares against a cache**, refreshed when missing or stale, falling
back to the cached copy when the fetch fails. Offline is therefore not a broken
make lint, while a real difference still fails. Fresh clone with neither cache
nor network reports that it could not run; CI always has both, so the gate is
authoritative where it is enforced.
The cache fallback made a hard gate soft. It was reasoned from the failure mode
"no network", but the likelier one is the fetch failing while the network is
fine — a CDN outage, a bad deploy to the pages repo, a 404 after a rename.

Under the cached design that degrades silently into "compared against something
older", and on a CI runner (a fresh clone, so no cache) it lands on the
"could not run" path. One bad deploy would disable the drift gate across every
adopting repo at once, with nothing going red — which is the warning-nobody-reads
failure this plan already rejects, arriving through the distribution mechanism
instead of the gate's wording.

No cache: `make lint` fetches https://just-buildit.github.io/standard.mk every
time and fails if it cannot. A gate that cannot reach its reference has not
passed — it has not run, and it should say so. Same reasoning as the gate
failing rather than warning.

Offline `make lint` is the cost, and it is the right one to pay: the gate is
enforced in CI, which has the network, and a developer who cannot reach the CDN
has not been told their vendored copy is clean — only that it could not be
checked.
Two review nits, both cases of a summary line disagreeing with the detail it
summarises.

`Universal (8)` listed eight names while `install-deps` was declared universal
in the very next paragraph — and the stated cap of 38 only reconciles with nine
(9 universal + 3 lint-<tool> + 2 aggregates + 24 across the feature groups).
The list and the count now agree, and both agree with the cap.

The `.pre-commit-config.yaml` row claimed it "resolves no tool versions
itself". That is not reachable and was never intended: clang-format and
cmake-format are not Python dev-group tools, so pre-commit must resolve them,
and both repos pin them with a `rev:` today. Criterion 7 and the Non-goals were
already correctly scoped to lock-managed tools; only this row stated the
absolute — and it is the summary table someone would most likely write the gate
from, where a gate built on it would fail on clang-format in both repos
permanently.
@hunterdsp
hunterdsp force-pushed the plan/makefile-standard branch from 389ceef to b6d8b1e Compare July 31, 2026 00:11
@hunterdsp
hunterdsp merged commit b46ae95 into main Jul 31, 2026
2 checks passed
hunterdsp added a commit to just-buildit/just-makeit that referenced this pull request Jul 31, 2026
P0 of the cross-org Makefile standard (RFC doppler-dsp/doppler#555, plan
just-buildit/.github#3). One canonical standard.mk that every repo in
just-buildit and doppler-dsp includes, with per-repo variation expressed as
configuration rather than as a fork. just-makeit is the prototype adopter and
its Makefile now defines zero targets; examples-clean, its one local target,
lives in local.mk.

Three gates hang off `lint`, which is what CI runs, and each was sabotaged to
prove it fails: standard-check (drift; fails rather than skips when it cannot
reach canonical, and is inert until P1 publishes it), help-check (every target
documented, every rule listed), ghost-check (no .PHONY entry without a recipe
or prerequisites). A flag turned on with its command variable empty is a
parse-time error, because that case is invisible to every gate.

Renames, none with a caller outside the Makefile: install folded into setup,
check-version -> version-check (and it now requires the version manifests to
agree with each other, not just with VERSION=), build -> wheel.

Found by prototyping in one repo first: standard.mk rejected the GNU make 3.81
that macOS ships, and the gates parse a database whose wording changed in 3.82.
Both would have shipped to every adopter. Found by review: HAS_EXAMPLES carried
an examples-clean that doppler cannot supply, HAS_COVERAGE was missing
coverage-gate, and the 38-target cap agreed with the plan by coincidence rather
than construction — now 35 user-facing + N dispatch + 3 enforcement, checked
against the RFC group table target-for-target.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant