v0.5 scope — rtb-vcs v0.2 git-operations slice¶
Status: IMPLEMENTED — all eight commits landed (PRs #42, #49, #50, #51, #52, #53, #54, plus this docs commit). Component page at docs/components/rtb-vcs.md; framework spec §9 aligned. A4 was obsoleted mid-flight (consistent shell-out architecture removed the need for git2-fallback).
Parent contract: rust-tool-base.md §9 (VCS & release providers) and §16 (roadmap, "0.5" line).
Predecessor: 2026-04-23-rtb-vcs-v0.1.md — release-provider slice (shipped in 0.2.0).
Workspace target: the next workspace version after the 0.4.1 (App
1. Motivation¶
The framework spec §9 calls for a Repo type and a TokenSource so downstream tools can clone, walk, blame, diff, and authenticate against the repositories whose releases they already consume. The release-provider slice (v0.1, shipped at 0.2.0) is read-only and metadata-only — there is no way for a downstream tool built on RTB to ask "what changed since the last release?" or "who last touched this file?" without re-importing gix directly and paying for its full API surface.
Concretely, the gap blocks four downstream stories:
rtb-cli-binproject generation + regeneration (v0.6). The scaffolder has two git interactions: (i) atrtb new, auto-initialise a git repository for the generated project (git init+ initial commit), and (ii) atrtb regenerate, detect drift between the generated tool's current working tree and the regenerated template output (adiffop) and surface it cleanly. WithoutRepo, the scaffolder has to shell out togitor pullgixin directly.rtb-airelease-note summarisation. The plan in framework spec §16 is forrtb-aito summarise the git log between two release tags. Todayrtb-aihas no way to walk commits; it would have to depend ongixdirectly, bypassing the framework's auth and concurrency story.- Downstream tools that just need git. Any operator-flow tool built on RTB that wants to clone-and-inspect a repository (e.g. a code-review CLI, a release-prep tool, a dependency-graph walker) has to roll its own
gixwrapper. The framework promises aRepotype; v0.5 is when we deliver it. - Downstream tools that need git as a foundation. Beyond the three named consumers,
Repois a deliberately foundational abstraction — tool authors building anything from CI-orchestrators to repo-statistics dashboards should be able to compose richer behaviour on top ofRepowithout re-importinggixand re-deriving the framework's auth/concurrency conventions.
Foundational, not curated¶
This last bullet is load-bearing. The GTB equivalent (pkg/vcs/repo in go-tool-base) started life over-engineered for an earlier incarnation, got pared back to a sensible abstraction, and then quietly became the load-bearing piece that downstream tools layered their own git-based functionality on top of. The lesson for v0.5: design the surface as a foundation that consumers compose on top of, not as a curated facade that exposes only what the named consumers (scaffolder, release-notes) need today. That framing pulls the A3 cut up — see §7.
v0.5 closes the gap by adding a thin Repo abstraction over gix (with git2 as a feature-gated fallback for ops gix cannot yet perform) plus a TokenSource for auth that integrates with the v0.4.1 typed-config story.
2. The design tension¶
The framework spec §9.2 fixes some shape ("gix is the primary backend"; "Repo::spawn_blocking(...) runs blocking gix operations inside tokio::task::spawn_blocking"; "fallback to git2 only for operations gix cannot yet perform"). What it does not fix:
- The public surface (sync vs async, ownership of the underlying
gix::Repository, error model). - How
TokenSourceintegrates with the post-v0.4.1 typed-config world — spec §9.3 referencesapp.config.auth.env/app.config.auth.keychain/app.config.auth.value, which assume a fixed config shape that no longer exists. - The minimum-viable feature set: which ops ship in v0.5 vs deferred to v0.6+?
- Whether
git2-fallbackis opt-in or always-on.
These are the seven open questions in §7. Resolving them defines the slice.
3. Sketch of the proposed surface¶
The shapes below are proposed, not committed. They exist so the open questions in §7 have something concrete to refer to. Resolutions in §8 will pin the final shape before TDD begins.
3.1 Repo type (read paths)¶
//! crates/rtb-vcs/src/git/mod.rs
pub struct Repo {
inner: gix::ThreadSafeRepository,
path: PathBuf,
}
impl Repo {
/// Initialise a new repository at `path`. The scaffolder's
/// `rtb new` flow uses this to git-init a freshly generated
/// project before its initial commit.
pub async fn init(
path: impl AsRef<Path>,
opts: InitOptions,
) -> Result<Self, RepoError>;
/// Open an existing repository at `path`. Discovers `.git` if
/// `path` is a subdirectory.
pub async fn open(path: impl AsRef<Path>) -> Result<Self, RepoError>;
/// Clone `url` into `dst`. Auth resolved via the supplied
/// `TokenSource` for HTTPS URLs; SSH uses the host's agent.
pub async fn clone(
url: &str,
dst: impl AsRef<Path>,
opts: CloneOptions,
) -> Result<Self, RepoError>;
/// Iterate the commit graph reachable from `revspec` (e.g.
/// `HEAD`, `main..feature`, `v0.4.0..HEAD`). Returns an async
/// stream so very long histories don't materialise as a `Vec`.
pub fn walk(&self, revspec: &str) -> Result<CommitWalk<'_>, RepoError>;
/// Diff two tree-ish references. Returns a structured `Diff`
/// (file additions/deletions/hunks) rather than a raw patch
/// string — callers that need patches call `Diff::to_patch`.
pub async fn diff(&self, a: &str, b: &str) -> Result<Diff, RepoError>;
/// `git blame`-style line authorship for `path` at `revspec`.
pub async fn blame(&self, path: &Path, revspec: &str) -> Result<Blame, RepoError>;
/// Repository status — staged / unstaged / untracked file lists.
pub async fn status(&self) -> Result<RepoStatus, RepoError>;
}
3.2 Repo type (write paths)¶
impl Repo {
/// Fetch from `remote` (default `origin`). Auth via `TokenSource`.
pub async fn fetch(&self, remote: &str, opts: FetchOptions) -> Result<(), RepoError>;
/// Check out `revspec`. Refuses dirty working trees by default;
/// `CheckoutOptions::force(true)` overrides.
pub async fn checkout(&self, revspec: &str, opts: CheckoutOptions) -> Result<(), RepoError>;
/// Stage `paths` and create a commit.
pub async fn commit(&self, paths: &[&Path], message: &str) -> Result<Oid, RepoError>;
/// Push `refspec` to `remote`. **`git2-fallback` feature only**
/// (gix push is experimental as of gix 0.66; we re-evaluate when
/// gix stabilises it). Returns `RepoError::PushUnsupported` when
/// the feature is disabled.
#[cfg(feature = "git2-fallback")]
pub async fn push(&self, remote: &str, refspec: &str) -> Result<(), RepoError>;
}
3.3 Auth — reuse rtb-credentials::Resolver¶
Per A2 resolution, no new auth trait. Auth-requiring ops accept a &CredentialRef (already declared by the host tool's typed config since v0.4) and resolve it through rtb-credentials::Resolver::resolve — which already returns SecretString and walks the env → keychain → literal → fallback-env precedence chain documented in rtb-credentials v0.1.
//! crates/rtb-vcs/src/git/auth.rs — thin glue, not a parallel trait.
use rtb_credentials::{CredentialError, CredentialRef, Resolver};
use secrecy::SecretString;
/// Resolve `cref` for git auth, mapping `CredentialError` into
/// `RepoError::Auth` so call sites get a single error type.
pub(crate) async fn resolve_for_git(
resolver: &Resolver,
cref: &CredentialRef,
) -> Result<SecretString, RepoError> {
resolver.resolve(cref).await.map_err(RepoError::Auth)
}
The auth-requiring Repo methods (clone, fetch, push) take &CredentialRef directly:
impl Repo {
pub async fn clone(
url: &str,
dst: impl AsRef<Path>,
opts: CloneOptions,
auth: Option<&CredentialRef>, // None = anonymous / SSH-agent
) -> Result<Self, RepoError>;
pub async fn fetch(
&self,
remote: &str,
opts: FetchOptions,
auth: Option<&CredentialRef>,
) -> Result<(), RepoError>;
}
The Resolver itself is constructed by the host tool (typically via Resolver::with_platform_default()) and threaded into operations via CloneOptions / FetchOptions or carried on the Repo instance — finalised in implementation. The point is that the seam is rtb-credentials, not a parallel rtb-vcs::TokenSource trait.
For ad-hoc cases where a downstream tool just wants "use $GITHUB_TOKEN" without typed config, they construct a CredentialRef { fallback_env: Some("GITHUB_TOKEN".into()), ..Default::default() } and pass it in. No special-case helper needed — the existing CredentialRef shape already covers it.
3.4 Error model¶
RepoError: thiserror::Error + miette::Diagnostic, following the same shape as ProviderError and CredentialError. Per A8 resolution, gix::Error and git2::Error are wrapped, not leaked — every variant is either a domain error or carries a stringified cause so we can swap the backend without breaking the public API.
Variants:
Io { path: PathBuf, source: std::io::Error }— filesystem path issues with the offending path attached.OpenFailed { path: PathBuf, cause: String }—Repo::opencould not load the repository atpath.causecarries the backend's stringified error.InitFailed { path: PathBuf, cause: String }—Repo::initcould not create the repository atpath.CloneFailed { url: String, cause: String }— clone setup or transport failure (URL parse, refs negotiation, network).FetchFailed { remote: String, cause: String }— fetch transport / refs failure.CheckoutFailed { revspec: String, cause: String }— checkout could not switch torevspec.CommitFailed { cause: String }— commit creation / staging failure.PushFailed { remote: String, refspec: String, cause: String }— push transport / refs failure (only reachable withgit2-fallback).RevspecNotFound { revspec: String }— caller-facing for bad rev references that don't resolve to a known object.DirtyWorkingTree { paths: Vec<PathBuf> }— checkout / commit guard tripped; the listed paths are dirty.Auth(rtb_credentials::CredentialError)— credential resolution failed (wrapsrtb-credentials's existing error; not a backend leak becausertb-credentialsis part of the framework's stable public surface).PushUnsupported— push attempted withgit2-fallbackdisabled.
The internal mapping from gix::Error / git2::Error to these variants lives in crates/rtb-vcs/src/git/error.rs and is private. Test suites assert on variant shape (assert!(matches!(err, RepoError::CloneFailed { .. }))) rather than backend internals.
3.5 Module layout¶
crates/rtb-vcs/src/
├── lib.rs # existing — release-provider re-exports
├── config.rs # existing — ReleaseSourceConfig etc.
├── release.rs # existing — ReleaseProvider etc.
├── http.rs # existing
├── github.rs # existing
├── gitlab.rs # existing
├── ... # other backends
└── git/ # NEW — entire module gated on the `git` Cargo feature
├── mod.rs # Repo type + InitOptions/CloneOptions re-exports
├── auth.rs # pub(crate) glue calling rtb-credentials::Resolver
├── error.rs # pub(crate) gix/git2 → RepoError mapping (A8)
├── walk.rs # CommitWalk async stream
├── diff.rs # Diff value type
├── blame.rs # Blame value type
├── status.rs # RepoStatus
├── init.rs # init impl
├── clone.rs # clone impl
├── checkout.rs # CheckoutOptions + impl
├── fetch.rs # FetchOptions + impl
├── commit.rs # commit impl
└── push.rs # cfg(feature = "git2-fallback") push impl
3.6 Cargo features¶
[features]
# Existing release-provider feature set unchanged.
default = ["github", "gitlab", "gitea", "codeberg", "direct", "bitbucket", "git"]
# NEW: opt-out for tools that only need release-provider behaviour.
# Default-on because the framework spec promises `Repo`; tools that
# truly only want releases can `default-features = false, features = ["github"]`.
# Pulls in gix + rtb-credentials (auth glue for clone/fetch/push).
git = ["dep:gix", "dep:rtb-credentials"]
# NEW: opt-in for push / advanced ops gix can't yet do. A4 may move
# this to default-on; for now opt-in keeps the libgit2 C dep off the
# critical path for cross-compile builds.
git2-fallback = ["dep:git2", "git"]
4. Implementation outline¶
(Provisional — sequencing finalised in §8 once open questions resolve.)
- Foundation.
git/mod.rswithRepo::open+Repo::status,RepoErrorenum,auth.rsskeleton. Async surface backed bytokio::task::spawn_blocking. - Read paths.
walk,diff,blameagainst fixture repos created viagix-testtools. - Write paths (safe).
fetch,checkout,commit. Dirty-tree guards. git2-fallback+push. Behind the feature flag; CI matrix includes a "fallback enabled" lane.- TokenSource integration.
CredentialRefblanket impl,from_envhelper, docs. - Docs. Component page (
docs/components/rtb-vcs.mdextension), concept page (docs/concepts/vcs.md), framework-spec §9 amended to match shipped shape.
5. Out of scope (deferred)¶
- Submodule support. gix submodule support is incomplete; defer to v0.6+ or until gix stabilises it.
- LFS. Large-file storage is a v1.0+ concern; tools needing LFS shell out to
git-lfsthemselves at v0.5. - Worktree management (
git worktree add/remove). Useful but not blocking any v0.6 work; defer. Repowrite surface beyond commit/push. No merge, rebase, cherry-pick, revert at v0.5. The release-note + scaffolder consumers don't need them.- Custom transports. Tool authors who need HTTP-proxy-only or self-signed-cert transports configure
gixdirectly via an escape hatch (Repo::from_gix(gix::Repository)) — TBD in A6.
6. Public-API stability impact¶
This is an additive slice — no existing rtb-vcs types change. The only sensitivity is around the git Cargo feature: making it default-on means existing tools that depend on rtb-vcs without default-features = false start pulling gix (≈ 2.5 MiB binary growth, ≈ 30 s extra compile). The alternative — git opt-in — keeps the existing compile profile but contradicts the framework spec's "the framework provides Repo" promise. A7 decides.
rtb-update, the only current consumer, is unaffected — it imports rtb_vcs::ReleaseProvider etc., none of which move.
7. Open questions¶
Resolution policy: each question stays open until the human reviewer fills in the Resolution stub below it. Implementation does not begin until A1–A7 are all marked RESOLVED. New questions raised mid-review get appended as A8+ without renumbering.
- A1 — Sync vs async public surface. gix is blocking;
tokio::task::spawn_blockinglifts it into async. Three options: - (a) Public surface is
async fn; every method internally callsspawn_blocking. Matches the framework spec's "async surface". Adds tokio dependency tortb-vcs(currently tokio-free for the release-provider backends that usereqwestdirectly). - (b) Public surface is blocking; callers wrap themselves. Simpler API; punts the concurrency story onto every consumer.
- © Both —
Repois blocking,Repo::async_view() -> AsyncRepowraps it. Doubles the API surface. -
Resolution (2026-05-12): (a) async, internal
spawn_blocking. Matches framework spec §9.2 wording. The A2 resolution already forcesrtb-vcsto grow atokiodependency (to callrtb_credentials::Resolver::resolvefor auth), so the marginal cost of full async is zero. Going blocking would require ablock_ondance internally just to call the auth resolver — strictly worse. Async also matches every otherrtb-*crate that does I/O. -
A2 — Auth seam location. Either:
- (a) Lives in
rtb-vcs::authas a newTokenSourcetrait.rtb-vcsdepends onrtb-credentialsfor the blanketimpl TokenSource for CredentialRef. Concentrates VCS auth in one place but introduces a parallel trait. - (b) Reuse
rtb-credentials::Resolverdirectly — auth-requiringRepomethods take&CredentialRef,rtb-vcscallsresolver.resolve(cref).await. No new trait. -
Resolution (2026-05-12): (b) reuse
rtb-credentials::Resolver. Per user steer "we should be utilising rtb-credentials" —Resolver::resolvealready returnsSecretStringand walks the documented precedence chain. Introducing a parallelTokenSourcetrait would duplicate that surface without adding capability. §3.3 updated to match. -
A3 — How foundational? Given the "foundation, not curated facade" framing in §1, this is really about how much of the foundational vocabulary we lay down in v0.5 vs v0.5.x point releases. Three cuts:
- (a) Curated MVP:
open+walk+diff+status. Covers release-notes + drift detection; insufficient as a foundation (noinit, noclone, nocommit— downstream tools building "something more" still have to importgix). - (b) Foundational read + light write: +
init+clone+commit+blame. Covers the scaffolder's project-init flow, release-notes, drift detection, and gives downstream tools a sensible vocabulary to compose on. Doesn't includefetch/checkout/push. - © Full surface: +
fetch+checkout+ (undergit2-fallback)push. Full operator-flow capability in one slice; significantly expands the test corpus. -
Resolution (2026-05-12): © full surface. §3.2 ships in v0.5 in full. Rationale: foundational means foundational — punting
fetch/checkout/pushto a v0.5.x point release would force every downstream tool that needs them to either wait or re-importgix, exactly the failure mode §1 calls out. Acknowledged cost: expanded test corpus + thegit2-fallbackdecision in A4 becomes load-bearing for push. -
A4 —
git2-fallbackdefault. Withgixpush experimental, push is the only documented driver for the fallback. Either: - (a) Opt-in. Tools that need push enable it explicitly. Keeps the default compile profile lean (~+1.2 MiB binary).
- (b) Default-on. Push "just works" for everyone. Pulls libgit2 (C dependency) into every RTB build.
- Resolution (2026-05-12): (a) opt-in. libgit2 is the most common source of cross-compile pain in the Rust git ecosystem (musl + windows lanes). Pushing is a minority operation among the foundation's consumers; making everyone pay the cross-compile cost is the wrong default. Discoverability is preserved by
RepoError::PushUnsupportedcarrying a help-laden diagnostic that names the feature flag. We re-evaluate when gix's push stabilises. -
Obsoleted (2026-05-19): all v0.5 write paths (
commit,fetch,checkout,clone-with-auth) shipped via shell-out togit. Push followed the same pattern in commit 7 — no libgit2 needed. Thegit2-fallbackfeature is intentionally not declared onrtb-vcs.RepoError::PushUnsupportedstays in the#[non_exhaustive]enum for backwards compatibility but is no longer produced. -
A5 — Test strategy. gix has
gix-testtoolsfor fixture-repo creation. Either: - (a) Use
gix-testtoolsdirectly. Bakes a test-only gix dep; fast and idiomatic. - (b) Hand-roll fixture creation via shell scripts invoked from build.rs. Decouples from gix internals but reinvents what gix already provides.
- © Pre-commit a set of
.git/-bare fixture repos undertests/fixtures/. No build-time setup; slowest test feedback loop but most reproducible. -
Resolution (2026-05-12): (a)
gix-testtools. Standard ecosystem choice;gixis already in[dependencies]per A7 so the test-only dep is implicit. ©'s binary-blob commits are a separate ongoing maintenance burden we don't take on for foundational tests. -
A6 —
Repoescape hatch for advanced transports. Some tools need custom HTTP proxies, self-signed certs, or other transport tweaksRepo's API won't surface. Either: - (a) Expose
Repo::from_gix(gix::Repository) -> RepoandRepo::into_gix(self) -> gix::Repository. Leaksgixinto the public API; tightly couples downstream tools to a specific gix major version. - (b) No escape hatch. Tools with exotic transport needs depend on
gixdirectly and don't usertb-vcs::git. - © Per-method options via builders (
CloneOptions::with_http_proxy(...), etc.). Adds API surface but keepsgixprivate. -
Resolution (2026-05-12): (b) no escape hatch initially. A8 just decided to wrap the backend; offering a
from_gix/into_gixescape contradicts that decision in the same release. Tools with genuinely exotic transport needs depend ongixdirectly and skiprtb-vcs::git— the foundation does not claim coverage of every edge case. We can add ©-style options when a concrete consumer asks for them, without breaking the public surface (option-builder additions are non-breaking). -
A7 —
gitCargo feature default. Either: - (a) Default-on. Matches the framework spec's "the framework provides
Repo" promise; ~+2.5 MiB binary growth, ~+30s compile for tools that don't use it. - (b) Opt-in. Existing tools unaffected; tools that want
Repoaddfeatures = ["git"]. Contradicts framework spec wording (would need an amendment). -
Resolution (2026-05-12): (a) default-on. Per user steer "Repo should be default on". The framework spec §9 is the contract —
Repois part of the framework's promise and should not require downstream tools to opt into a feature flag to access it. The ~+2.5 MiB / ~+30s compile cost is acknowledged; tools with hard binary-size budgets candefault-features = falseand re-enable only the release-provider backends they need. -
A8 — Backend error wrapping. Either:
- (a) Surface
gix::Error/git2::Errordirectly asRepoError::Gix(_)/RepoError::Git2(_). Cheaper to implement; downstream tools can match on backend internals. - (b) Wrap into semantic variants (
OpenFailed,CloneFailed,FetchFailed, …) with stringifiedcause. Decouples the public API from gix/git2 versions; tests assert on variant shape rather than backend internals. - Resolution (2026-05-12): (b) wrap. Per user steer "wrapper in case we want to swap out gix at some point in the future" — a backend swap (gix → git2-primary, or some future Rust git library) must not break downstream pattern-matching. §3.4 updated to list the wrapped variants and notes the private mapping in
crates/rtb-vcs/src/git/error.rs.
8. Slicing plan¶
Per A3 resolution = ©, seven commits with feature-flag boundaries between them so partial progress always passes CI:
feat(vcs): Repo skeleton + RepoError + auth foundation—git/mod.rswithRepo::init+Repo::open+Repo::status,RepoError, auth integration withrtb-credentialsper A2 resolution. Test against fixture repos per A5. (init+opengo together because they're both constructors and the scaffolder needsinitfrom day one.) Shipped 2026-05-13 (PR #42).feat(vcs): walk + diff read paths— async commit-walk stream, structuredDiff. Drift-detection lands here. Originally bundled withblame, butgix0.72 exposes blame only as the rawgix-blamere-export (noRepository::blame_fileconvenience) — splitting it out keeps this commit focused on the gix-Repository-method APIs and lets commit 2b decide between shelling out togit blame(deterministic, immediately available) or wrappinggix-blamedirectly (consistent backend, more work). Shipped 2026-05-14 (PR #49).feat(vcs): blame read path—Repo::blame(path, revspec) -> Blame. Backend chosegix-blamedirectly (consistent backend; the wrap-not-leak A8 contract means the choice doesn't reach the public surface — we can swap to a shell-out fallback later without breaking consumers). Shipped 2026-05-14 (PR #50).feat(vcs): clone + commit write paths—Repo::clone(anonymous in this commit; HTTPS auth viartb-credentialsdeferred to commit 5b alongsidefetch/pushauth where the unified credentials-helper plumbing exists for all three ops),Repo::commit(path-staging + message; backend shells out togitper A8 wrap-not-leak —CloneOptions+Repo::commitsignature are forward-compatible). Shipped 2026-05-15 (PR #51).feat(vcs): fetch + checkout write paths—Repo::fetchandRepo::checkout(with dirty-tree guard +forceoverride). Both shell-out togitper the v0.5 write-path backend choice; anonymous-only in this commit, with auth landing in commit 5b alongside the auth-on-clone retrofit. Shipped 2026-05-15 (PR #52).feat(vcs): unified auth for clone+fetch(5b) — extendsCloneOptions/FetchOptionswith acredential: Option<CredentialRef>field +with_credentialbuilder; resolves viaResolver::with_platform_default(); plumbs the resultingSecretStringtogitvia an inline-c credential.helper='!f() { echo username=x-access-token; echo password=$RTB_VCS_GIT_TOKEN; }; f'snippet (process env carries the token; argv carries only the helper script).GIT_TERMINAL_PROMPT=0prevents interactive prompts on auth failure. Push picks up the same helper in commit 7.feat(vcs): push write path—Repo::push(remote, refspec, PushOptions)via shell-out togit push. Reuses commit 6's auth wiring (-c credential.helper=...+RTB_VCS_GIT_TOKENenv). The originally-plannedgit2-fallbackfeature is dropped — the consistent shell-out architecture for all v0.5 write paths means we don't need libgit2 (see A4's obsolete note).docs: rtb-vcs v0.2 docs + framework-spec §9 alignment— component page extension, concept page, spec amendment.
9. Resolutions¶
Resolved 2026-05-12. The canonical resolution text lives under each open question in §7 — this section is the at-a-glance summary.
| # | Question | Resolution |
|---|---|---|
| A1 | Sync vs async public surface | (a) async, internal spawn_blocking. A2 already forces a tokio dep so the marginal cost is zero, and block_on-ing the auth resolver internally would be strictly worse. |
| A2 | Auth seam location | (b) reuse rtb-credentials::Resolver — no parallel TokenSource trait. Auth-requiring Repo methods take &CredentialRef; Resolver::resolve already walks the documented precedence chain. |
| A3 | How foundational? | © full surface — init + open + clone + walk + diff + blame + status + commit + fetch + checkout + push all ship in v0.5. Punting fetch/checkout/push would force every downstream tool needing them to re-import gix, exactly the failure mode §1 calls out. |
| A4 | git2-fallback default |
(a) opt-in. libgit2 is the biggest cross-compile pain point; pushing is a minority op. RepoError::PushUnsupported carries a help-laden diagnostic naming the feature flag. |
| A5 | Test strategy | (a) gix-testtools — standard ecosystem choice; gix is already in [dependencies] so the test-only dep is implicit. |
| A6 | Repo escape hatch |
(b) no escape hatch initially. A from_gix/into_gix would directly contradict A8's wrap-the-backend decision; tools with exotic transport needs depend on gix directly. Option-builder additions remain non-breaking and can land later when a concrete consumer asks. |
| A7 | git Cargo feature default |
(a) default-on. Framework spec §9 promises Repo; ~+2.5 MiB / ~+30s is acknowledged. Tools with hard binary-size budgets default-features = false and re-enable only the release-provider backends they need. |
| A8 | Backend error wrapping | (b) wrap into semantic variants. Decouples the public API from gix/git2 versions so we can swap backends without a public-API break. Tests assert on variant shape, not backend internals. |
Downstream framework-spec amendments triggered by these resolutions¶
- §9.2 (Git) — pin
Repoasasync(matches the existing wording), confirmgit2-fallbackis opt-in, nameRepo::init / open / clone / walk / diff / blame / status / commit / fetch / checkout / pushas the v0.5 surface. - §9.3 (Token resolution) — drop the
app.config.auth.env|keychain|valueshape (stale since v0.4.1), replace with "auth-requiringRepomethods accept a&CredentialRefand resolve throughrtb_credentials::Resolver". - §16 (Roadmap) — flip the "0.5" pending line to a "shipped" line once the slicing plan in §8 lands.