Repository navigation
feat(template): a declared template rule, and the tmpl applet on the same renderer - #440
Merged
Merged
Conversation
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-template-driver
branch
from
August 29, 2026 21:35
d386399 to
168264d
Compare
raphaelvigee
force-pushed
the
raphaelvigee/core-utils-templating-engine
branch
from
September 3, 2026 16:26
a2abb37 to
cfb9516
Compare
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-template-driver
branch
from
September 3, 2026 16:26
168264d to
7ae3435
Compare
raphaelvigee
force-pushed
the
raphaelvigee/core-utils-templating-engine
branch
from
September 3, 2026 16:56
cfb9516 to
962ce7c
Compare
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-template-driver
branch
from
September 3, 2026 16:56
7ae3435 to
cceac42
Compare
raphaelvigee
force-pushed
the
raphaelvigee/core-utils-templating-engine
branch
from
September 3, 2026 17:30
962ce7c to
11d00de
Compare
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-template-driver
branch
from
September 3, 2026 17:30
cceac42 to
31f29a2
Compare
raphaelvigee
changed the base branch from
raphaelvigee/core-utils-templating-engine
to
raphaelvigee/coreutils-rest-of-toolbox
September 3, 2026 17:32
This was referenced Sep 3, 2026
This was referenced Sep 3, 2026
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-template-driver
branch
from
October 16, 2026 17:05
31f29a2 to
8c87364
Compare
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-rest-of-toolbox
branch
from
October 16, 2026 17:05
2f656af to
88dd8b5
Compare
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-rest-of-toolbox
branch
from
October 16, 2026 17:34
88dd8b5 to
12e3328
Compare
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-template-driver
branch
from
October 16, 2026 17:35
8c87364 to
67b48cc
Compare
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-rest-of-toolbox
branch
from
October 18, 2026 16:23
12e3328 to
9bc44ab
Compare
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-template-driver
branch
from
October 18, 2026 16:23
67b48cc to
f674e01
Compare
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-template-driver
branch
from
October 18, 2026 17:06
f674e01 to
def01c9
Compare
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-rest-of-toolbox
branch
from
October 18, 2026 17:06
9bc44ab to
34439fc
Compare
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-rest-of-toolbox
branch
from
October 18, 2026 18:54
34439fc to
8c77c8e
Compare
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-template-driver
branch
from
October 18, 2026 18:54
def01c9 to
caa04e5
Compare
raphaelvigee
added a commit
that referenced
this pull request
Oct 18, 2026
…ts past 16 MiB On aarch64-apple-darwin nearly every function's `__unwind_info` entry points at its FDE in `__eh_frame` (rustc emits epilogue CFI that compact unwind cannot express), and that pointer is 24 bits. Debug test binaries now carry more than 16 MiB of `__eh_frame`; ld64 954.16 — nix's, the last open-source release — writes the offset truncated without a word. libunwind trusts it and parses whatever sits at the wrapped offset. Depending on the bytes there it falls back to a full scan or crashes with `malformed DW_CFA_def_cfa_register DWARF unwind, reg too big` and SIGSEGV — on any panic, or any anyhow error under CI's RUST_BACKTRACE=1. It surfaced as the `engine` lib tests crashing on #440 and the `heph` lib tests on #446, on Test darwin/arm64 only, identically on rerun. Reproduced with a synthetic binary: ld64 writes hint 0x02e950 for an FDE at 0x102e950 and the walk dies with the CI signature; ld-prime and lld both write "no hint" and the walk completes. Xcode's ld-prime links in about half lld's time (heph debug binary 0.59s vs 1.19s, engine lib tests 0.47s vs 0.95s; ld64 1.51s / 1.07s) but is not open source, so nix cannot pin it. Use it when the machine has it, lld otherwise. - devenv: `ld64.heph` (`-fuse-ld=heph`) execs `$HEPH_DARWIN_LD`, which `enterShell` resolves once per shell — ld-prime (ld-1000+) found with `DEVELOPER_DIR` unset, else nix's `ld64.lld` (`pkgs.lld`). Resolving per link cost ~40ms; the exec costs ~2ms. - .cargo/config.toml: `-fuse-ld=heph` for aarch64-apple-darwin, repeating the build rustflags since a target entry replaces them. - tests/darwin_linker.rs: the test binary reads its own LC_BUILD_VERSION and fails unless lld or ld >= 1000 linked it. Verified red with the config bypassed. - ci-triage: the signature, as real-not-flake. This also changes the linker of the shipped macOS binary and cdylibs, and makes it the runner image's Xcode linker; bin_e2e covers them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0181d7hhbYWXT42Z1KQPM29Q
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-rest-of-toolbox
branch
from
October 18, 2026 21:42
8c77c8e to
4748115
Compare
Base automatically changed from
raphaelvigee/coreutils-rest-of-toolbox
to
master
October 18, 2026 21:42
…same renderer Filling in a config file should not require a shell. `template(name, src, out, vars)` renders a declared template with declared variables, in-process (`crates/builtins/src/plugintemplate`) — inputs and outputs fully declared, no `sed` pipeline, no quoting rules to get wrong. The renderer itself lives in `crates/template` so the rule and the `tmpl` applet are the same code rather than two implementations of one syntax. `tmpl` covers the mid-recipe case (and `envsubst`); the rule is the one to reach for, because a rule's inputs are in the cache key and a mid-recipe render's are not. Two constraints the renderer carries deliberately: - **No non-deterministic builtins.** No `now()`, no randomness — a template that can render differently from the same inputs is a target that can never be a cache hit. - **`TEMPLATE_FORMAT_VERSION` in the def hash**, so a minijinja upgrade that changes rendering invalidates rather than silently diverging. `vars` is hashed from a `BTreeMap` because it arrives as a `HashMap`, and hashing it in iteration order would move the def hash on every run. The rule and the applet land together because they are one feature: a shared renderer used two ways. Splitting them would put a crate in trunk with one caller and review the same rendering semantics twice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0181d7hhbYWXT42Z1KQPM29Q
A config file's values usually live somewhere else — a registry host in a
platform repo, a version in a release target — and copying them into
`vars` makes a second source of truth. `vars` values are now
`Deferred<String>`, so `${read://pkg:name}` works there exactly as it does
in an exec `run`: the host turns the reference into a hashed edge at parse
and hands the driver the producer's bytes at run.
The def hash takes each value's text as written. A plain value hashes
byte-for-byte as before, so no existing template target invalidates; a
reference contributes itself, and its producer's content reaches `hashin`
through the edge — `heph inspect def` never builds anything.
`${src://…}` is refused at parse, naming the variable. It is completed only
by the managed-driver layer, and this driver renders in-process with no
sandbox; letting it through would fail at run with a message blaming heph.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0181d7hhbYWXT42Z1KQPM29Q
raphaelvigee
force-pushed
the
raphaelvigee/coreutils-template-driver
branch
from
October 18, 2026 21:42
caa04e5 to
e776c81
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Filling in a config file should not require a shell.
template(name, src, out, vars)renders a declared template with declared variables, in-process — inputs and outputs fully declared, nosedpipeline, no quoting rules to get wrong.One feature, two surfaces
The renderer lives in
crates/templateso the rule and thetmplapplet are the same code rather than two implementations of one syntax:template()— the rule, and the one to reach for. Its inputs are in the cache key.tmpl— the applet, for the mid-recipe case, and it coversenvsubst.They land together because splitting them would put a crate in trunk with one caller and review the same rendering semantics twice.
Two constraints the renderer carries deliberately
now(), no randomness — a template that can render differently from identical inputs is a target that can never be a cache hit.TEMPLATE_FORMAT_VERSIONin the def hash, so a minijinja upgrade that changes rendering invalidates rather than silently diverging.varsis hashed from aBTreeMapbecause it arrives as aHashMap, and hashing it in iteration order would move the def hash on every run.varstake deferred valuesvarsvalues areDeferred<String>, sovars = {"image": "${read://infra:registry}/app"}works as in an execrun: the host adds a hashed edge at parse and hands over the producer's bytes at run. The def hash takes the text as written, so plain values hash byte-for-byte as before.${src://…}is refused at parse (this driver has no sandbox), andvars = Nonestill parses — the usual macro-forwarding shape. Hermeticity board: HERMETIC. Covered by unit tests and twocrates/e2e/tests/deferred.rscases (renders the bytes; re-renders when the value moves, def hash unchanged).The stack
Merge bottom-up, and
gh stack syncafter each one lands —masteris squash-only, so the rebase will conflict and the resolution rule in CLAUDE.md applies.PATH— breakingtemplaterule and thetmplappletPATHseam ← base, targetsmaster#445 (default-on) was folded into #446: the two flips only mean something together. Only #451 builds automatically: since #449 a stacked PR is skipped unless it carries
ci/force-ci. Every layer was checked locally on its own after rebasing ontomaster— fmt,cargo clippy --workspace --all-targets -D warnings, the touched crates' unit tests, and thee2e+enginesuites — not just at the top of the stack.🤖 Generated with Claude Code
https://claude.ai/code/session_0181d7hhbYWXT42Z1KQPM29Q