git-ents.gitmain
⌘K
foforge
commit 1ac01b9
model: rename `Ents-Ref` trailer to `Advance-ref`

This fits Git trailer naming conventions more closely, and also has the benefit of implying one of our invariants: commits to meta refs MUST be fast-forward only.

Joseph D. Carpinelli · 1 month ago

Reviews

No reviews of this commit yet — record a verdict below.

Start a review

verdict

docs/abstractions.adoc @@ -39,7 +39,7 @@ Trees stay pure struct representations — no version marker entry. Ref-level metadata that is not entity content belongs in commit-message trailers, because the commit is already the storage unit. -Two trailers are reserved: `Schema-Version:` for explicit encoding detection if it is ever needed, and `Ents-Ref:` for refname binding (see 4). +Two trailers are reserved: `Schema-Version:` for explicit encoding detection if it is ever needed, and `Advance-ref:` for refname binding (see 4). Versioning or binding in the tree would pollute both the schema and the merge path. *Tip invariant:* the tip of a meta-ref is always readable by the binary that owns the entity type; a non-owning binary treats the tree as opaque and degrades to generic display (`git store show`); history is archival. @@ -67,7 +67,7 @@ A commit signature proves authorship; it does not prove placement. The difference is recovered explicitly: -* *Refname binding* — an `Ents-Ref:` trailer names the ref the commit was authored for, checked at verification; without it, a signed commit could be replayed as the tip of a different meta-ref. +* *Refname binding* — an `Advance-ref:` trailer names the ref the commit was authored for, checked at verification; without it, a signed commit could be replayed as the tip of a different meta-ref. * *Anti-replay* — meta-refs advance fast-forward-only in the DAG sense (the new tip descends from the old), enforced by atomic CAS on the ref store; the parent hash is the freshness binding, so no nonce is needed. *Tip invariant:* the tip of a meta-ref is signed by a member authorized for that refname. @@ -91,7 +91,7 @@ [arabic] . The new tip is signed by a member authorized for this refname. -. The `Ents-Ref:` trailer matches the refname. +. The `Advance-ref:` trailer matches the refname. . The new tip descends from the old tip. . The update commits via atomic CAS.
docs/design.adoc @@ -27,7 +27,7 @@ Mutations are author-signed commits, not push certificates. A push cert signs a transition and evaporates at the transport layer; a commit signature replicates with the repo and verifies in every clone, forever. Verification evidence is repository state, like everything else. -Refname binding lives in an `Ents-Ref:` trailer; anti-replay is fast-forward-plus-CAS, with the parent hash as the freshness binding. +Refname binding lives in an `Advance-ref:` trailer; anti-replay is fast-forward-plus-CAS, with the parent hash as the freshness binding. Because writing and verifying are separated, local is genuinely offline-first: the local store accepts any write and the gate merely annotates. You can author while unenrolled, work against an unfetched member list, and accumulate meta-refs the canonical store would reject.
docs/faq.adoc @@ -150,7 +150,7 @@ Revocation is a state, never a deletion — deleting the entity would merely make their old signatures unverifiable, which is the opposite of what an audit needs. *I want to verify that nobody tampered with forge state.* -Every meta-ref tip must be signed by a member authorized for that refname, bound to the ref by an `Ents-Ref:` trailer, descending from the previous tip. +Every meta-ref tip must be signed by a member authorized for that refname, bound to the ref by an `Advance-ref:` trailer, descending from the previous tip. `git ents members check` verifies this from any clone, with no server's cooperation. *I want to know who did what, and when.*
docs/spec/overview.adoc @@ -67,7 +67,7 @@ Every meta-ref mutation is an author-signed commit whose signature replicates with the repository and verifies offline in any clone. -A signature proves authorship, not placement, so an `Ents-Ref:` trailer +A signature proves authorship, not placement, so an `Advance-ref:` trailer binds the commit to the ref it was authored for, and adopting someone else's commit onto a canonical ref is always a merge, never a cherry-pick, so the author's signature survives intact in ancestry. @@ -75,7 +75,7 @@ === 5. Gate Verification is a pure function over ref-store reads — is the new tip -signed by a member authorized for this refname, does its `Ents-Ref:` +signed by a member authorized for this refname, does its `Advance-ref:` trailer match, does it descend from the old tip, does the update commit via atomic CAS — evaluated identically at three call sites: hosted CAS (mandatory, failure aborts the write), local UI verdict (advisory), and
crates/ents-model/src/trailer.rs @@ -4,7 +4,7 @@ //! commit's trailers, never inside the tree (`meta-ref.typed-tree`) — a //! comment's author and timestamp are the running example //! (`model.comment`). Two trailers are reserved: `Schema-Version:`, for -//! explicit encoding detection if it is ever needed, and `Ents-Ref:`, which +//! explicit encoding detection if it is ever needed, and `Advance-ref:`, which //! `ents-gate` (phase 3) compares against the refname actually being //! updated to bind a signature to its placement. //! @@ -17,7 +17,7 @@ /// The reserved trailer key binding a mutation commit to the refname it was /// authored for. -pub const ENTS_REF: &str = "Ents-Ref"; +pub const ADVANCE_REF: &str = "Advance-ref"; /// The reserved trailer key for explicit encoding detection. pub const SCHEMA_VERSION: &str = "Schema-Version"; @@ -25,14 +25,14 @@ /// The two reserved trailers read from (or written to) a mutation commit's /// message, per `meta-ref.trailers`. /// -/// A malformed `Ents-Ref:` value — one that fails gitoxide's own refname +/// A malformed `Advance-ref:` value — one that fails gitoxide's own refname /// validation — parses as absent rather than as an error: a commit message /// is untrusted input, and rejecting a bad binding is `ents-gate`'s job /// (`gate.tip-signed`), not this type's. // @relation(meta-ref.trailers, scope=file) #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct Trailers { - /// The refname the commit was authored for, if the `Ents-Ref:` trailer + /// The refname the commit was authored for, if the `Advance-ref:` trailer /// is present and well-formed. pub ents_ref: Option<FullName>, /// The raw `Schema-Version:` value, if present. @@ -47,7 +47,7 @@ /// ``` /// use ents_model::trailer::Trailers; /// - /// let message = b"Enroll jdc\n\nEnts-Ref: refs/meta/member/jdc\n"; + /// let message = b"Enroll jdc\n\nAdvance-ref: refs/meta/member/jdc\n"; /// let trailers = Trailers::parse(message); /// assert_eq!(trailers.ents_ref.expect("present").as_bstr(), "refs/meta/member/jdc"); /// ``` @@ -59,7 +59,7 @@ let mut trailers = Self::default(); for trailer in body.trailers() { - if trailer.token.eq_ignore_ascii_case(ENTS_REF.as_bytes()) { + if trailer.token.eq_ignore_ascii_case(ADVANCE_REF.as_bytes()) { if let Ok(name) = FullName::try_from(trailer.value.to_string()) { trailers.ents_ref = Some(name); } @@ -87,13 +87,13 @@ /// ents_ref: Some(name), /// schema_version: None, /// }; - /// assert_eq!(trailers.render(), "Ents-Ref: refs/meta/member/jdc\n"); + /// assert_eq!(trailers.render(), "Advance-ref: refs/meta/member/jdc\n"); /// ``` #[must_use] pub fn render(&self) -> String { let mut out = String::new(); if let Some(name) = &self.ents_ref { - out.push_str(ENTS_REF); + out.push_str(ADVANCE_REF); out.push_str(": "); out.push_str(&name.as_bstr().to_string()); out.push('\n'); @@ -118,12 +118,12 @@ #[rstest] #[case::both( - b"Subject\n\nBody line.\n\nEnts-Ref: refs/meta/member/jdc\nSchema-Version: 1\n", + b"Subject\n\nBody line.\n\nAdvance-ref: refs/meta/member/jdc\nSchema-Version: 1\n", Some("refs/meta/member/jdc"), Some("1") )] #[case::ents_ref_only( - b"Subject\n\nEnts-Ref: refs/meta/issues/42\n", + b"Subject\n\nAdvance-ref: refs/meta/issues/42\n", Some("refs/meta/issues/42"), None )] @@ -133,7 +133,7 @@ Some("refs/meta/comments/1"), None )] - #[case::malformed_ref_is_absent(b"Subject\n\nEnts-Ref: not a refname\n", None, None)] + #[case::malformed_ref_is_absent(b"Subject\n\nAdvance-ref: not a refname\n", None, None)] // @relation(meta-ref.trailers, scope=function, role=Verifies) fn parse_reads_reserved_trailers_only( #[case] message: &[u8],