pub fn is_git_ref_name(s: &str) -> Result<(), String>Expand description
Predicate: assert that s is a valid Git ref name under the
git check-ref-format --allow-onelevel rule set — the canonical
shape every typed :fonte (:tipo git …) :tag / :branch value
carries. The contract — modeled on the git check-ref-format
grammar the Git porcelain enforces at clone/fetch/checkout time,
with the multi-component requirement waived (:tag "v0.1.0" and
:branch "main" are both single-component refs, the canonical
leaf form for caixa’s :fonte pin axes):
- 1..=
GIT_REF_NAME_MAX_LEN(255) bytes — the POSIXNAME_MAXfilesystem-component limit Git’s loose-ref.git/refs/<cat>/<name>storage tops out at; - no ASCII control characters (
0x00..=0x1F,0x7F) — Git’s refname parser rejects them, and the\r/\narms are the canonical “the paste-from-doc spans multiple lines” footgun; - no whitespace (space, tab) — Git’s refname parser rejects them
too; a
:tag "v0.1.0 "(trailing space, from a copy-paste) silently passes string emptiness checks and fails atgit fetch origin tag 'v0.1.0 'with a quoting-confused error far from the source caixa.lisp; - no non-ASCII bytes (
>= 0x80) — Git’s refname rules predate UTF-8 normalization (NFC vs NFD on APFS silently rewrites the ref body, breaking the lacre’s content addressing); the intersection-floor every realistic Git host accepts is ASCII identifiers + the small punctuation set below; - no
~,^,:,?,*,[,\anywhere — Git reserves these for revision-grammar expressions (HEAD~3,HEAD^,:/searched, glob wildcards, refspec brackets, Windows-path backslash); - no
@{sequence — Git’s reflog grammar (HEAD@{2 hours ago},branch@{upstream}); - the bare
@is not a valid refname (it’s the alias forHEAD); - no
..anywhere (Git’s<rev1>..<rev2>range syntax + the./..parent-traversal footgun); - per
/-separated component: must not begin with.(Git refuses to follow loose.git/refs/<cat>/.<name>files), must not end with.lock(Git’s atomic-rename guard suffix), must not be empty (//rejected by the no-empty-component arm below); - no leading
/, no trailing/, no consecutive//; - no trailing
.on the whole ref (Git rejects<name>.); - no
refs/heads/orrefs/tags/prefix — the canonical “I copied the fully-qualified ref name out ofgit show-refinstead of the leaf” footgun (per [theory/FLAKE-DEDUP.md][fd]BranchNameconstructor rules); the caixa-resolver prepends the category prefix at clone time, so an author-side:branch "refs/heads/main"resolves to a literal ref namedrefs/heads/refs/heads/mainon disk.
Returns the parser-shaped reason on rejection (without wrapping in
any error variant) so each per-axis caller — DepSource::validate
for the :fonte :tag / :fonte :branch axes at validate time,
the future per-pin gate on lacre.lisp resolved-ref axes, the
future M4 per-dep CR materializer’s per-pin validator — wraps the
same reason in its own typed *Invalid { <axis>, reason } variant.
The reason wording is axis-agnostic (“git ref names reject ASCII
control characters”) so every call site reading the same diagnostic
points at the same rule; drift between any two axes’ rule
enforcement is a build error visible at this predicate, not a
per-renderer “this passed validate but git fetch rejected at
clone time” surprise.
Empty input is rejected here (defensively) and at each call site
via the narrower crate::DepError::FontePinEmpty variant — the
same empty-first cascade is_dns_1123_label,
is_gateway_api_http_path, is_wit_world_ref,
is_nats_subject, and is_wasi_keyvalue_slot all carry.
:rev is intentionally NOT routed through this predicate — its
author-surface shape is a hex commit-ID ([0-9a-f]+), not a
refname; a dedicated is_git_oid predicate on the parallel
hex-shape trajectory carries the reproducibility contract. Routing
:rev through is_git_ref_name would admit :rev "main",
defeating the reproducibility contract :rev carries vs.
:branch / :tag. The reverse mis-slot — a canonical OID
(40-char SHA-1 or 64-char SHA-256 lowercase hex) pasted into the
:tag / :branch slot — is closed by this predicate too: a
pre-emption arm below rejects any value whose width and byte set
match the canonical OID shape, surfacing the cross-axis mis-slot
at validate time with a diagnostic pointing the author at the
:rev slot. The two predicates’ valid sets intersect at exactly
the empty set, structurally.
Lifted as a typed substrate-side primitive on the same trajectory
the peer value-shape predicates (is_dns_1123_label,
is_gateway_api_http_path, is_wit_world_ref,
is_nats_subject, is_wasi_keyvalue_slot) already follow —
the typed slot’s valid set matches the Git porcelain’s accepted
set, structurally. The sixth value-shape primitive to land in
crate::render, and the first to gate a non-K8s downstream
landing surface (git CLI invocation from caixa-resolver, vs. the
K8s apiserver / NATS server / WASI kv backend for the prior five).
[fd]: pleme-io/theory/FLAKE-DEDUP.md §1 BranchName
§Errors
Returns the parser-shaped reason naming the specific violation
(length / control-char / forbidden-char / component-shape / prefix),
without wrapping in any error variant — every caller maps the same
String into its own typed *Invalid { <axis>, reason } enum
variant.