The lint fan-out listed lint-php twice, once before and once after
lint-playwright. Two commits added it independently: 82218087c1 for the PHP and
Ruby syntax checks, 6fee7ca136 for the per-language unit suites, and the second
did not see the first because lint-playwright had since been inserted between
them.
The wasted slot was the harmless half. parallel.sh keys target_start,
target_dur, target_rc and target_log by target NAME, so a repeat overwrites the
first run's log path and exit code, and the final pass reads each name once.
Two consequences: drain_one prints the second run's log twice and the first
run's output never appears, and a failing first invocation is reported green as
soon as the second one passes. A duplicate therefore turns a target into a blind
spot rather than into a slow one.
The gate showed the symptom plainly, two lint-php rows with an identical
0m 03s, which reads as a rendering quirk rather than as one target run twice.
Both halves are fixed: the list loses the repeat, and parallel.sh refuses a
duplicate before it loads the environment or starts a single make, naming the
offender and why a repeat cannot be tolerated. The two callers are Makefile:440
and Makefile:822; the test list has no duplicates, so the guard is inert there.
Negative control: `parallel.sh lint-php lint-python lint-php` exits 2 with
"duplicate target(s): lint-php" and launches nothing. The full quality-high run
afterwards dispatches 15 lint targets with lint-php once and stays green.
Residual: the guard covers scripts/make/parallel.sh only. scripts/tests/dns/
parallel.sh is a separate script with its own argument handling and was not
examined.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
drain_one() prints each target's log the moment that target finishes, so with
four suites running concurrently the output of the red one lands somewhere
between three green ones. The rc table at the end names which target failed
but not why, and finding the assertion meant grepping several thousand lines
of interleaved output.
The failed targets' logs are now replayed after the summary, each between
markers carrying the target name, followed by a FAILED TARGETS line. A green
run prints nothing extra.
parallel.sh mirrors its per-target wall-clock table into
GITHUB_STEP_SUMMARY, so make lint and make test render a target/status/
duration overview on the run page. The pytest runner writes junit XML
per suite into build/test-reports and test.yml renders them via
utils/github/pytest_summary.py: headline counts with total time,
failures opened with durations, every test collapsed slowest-first.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With env.development and env/ci.env both gone, env/default.env was the
last inhabitant of the directory. Hoisting it to the repo root removes
the empty-shell indirection: the SPOT is now [default.env](default.env),
sitting next to .env (its generated companion) and other repo-root
configuration artefacts like compose.yml, Dockerfile, and Makefile.
Path updates land in:
- generator: cli/meta/env/__main__.py (REPO_ROOT / "default.env")
- workflow: .github/workflows/test-install-make.yml (awk reads from
default.env, error message updated in lockstep)
- lint tests: test_used_keys_declared.py and test_infinito_prefix.py
switched from globbing env/*.env to targeting default.env directly;
test_default_env_keys_handled.py points at the new path
- drift test: test_inventory_vars_file_spot.py whitelist + STATIC_ENV
- smoke test: tests/integration/meta/env/test_dotenv_generator.py
STATIC_ENV + every docstring/error message
Doc-string and prose references in utils/env/*, scripts/*,
cli/administration/deploy/development/{common,coredns}.py,
Makefile, Dockerfile, and every doc page that mentioned env/default.env
are rewritten in the same pass so future readers see one path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rename env/static.env → env/default.env across the entire repo
(string label, doc links, Python file-path constants) — the
`parse_static_env*` function names stay as a stable API surface.
Sort all `*.env` / `*.env.j2` entries alphabetically (52 in
default.env, 4 in ci.env, 46 of 49 j2 templates; 3 templates
with Jinja2 `{% %}` control flow are skipped because conditional
blocks would tear apart under a naive sort). Sort all 83
Makefile target blocks alphabetically; .PHONY group headers
stay in place.
Collapse the two remaining multi-line Makefile recipe comments
(dotenv-force, test-signed) to single-line WHY comments.
Add four lint tests under tests/lint/filesystem/ to keep the
invariants enforced going forward:
- env/test_alphabetical_sort.py
- env/test_single_line_comments.py (# nocheck: markers exempt)
- makefile/test_alphabetical_targets.py
- makefile/test_single_line_comments.py (# nocheck: markers exempt)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the single `make -j ... --output-sync=target` invocation with
a shell-level fan-out that times each target individually and reports
a status table at the end. Output for each target is buffered to a
log file and dumped under a ✅/❌ header as soon as that target
finishes (preserving the per-target grouping `--output-sync` gave us).
Non-zero exit codes propagate; the run aborts at the first failure
once all already-launched children drain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`make test` and `make lint` now delegate to a generic parallel-make
wrapper that caps `-j` at INFINITO_WORKER_CPU, syncs per-target
output, and prints wall-clock elapsed time. `make test` gains the
full `lint` sweep and drops the phantom `test-deploy` target (PHONY
without recipe); pre-commit hooks consolidate around a single
`make test` invocation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>