Reproducibility and Provenance Pinning
Status: Open — agent brainstorm, not yet reviewed by the operator
Problem
Section titled “Problem”When jackin loads a role today (agent-smith, the-architect, or any third-party role), it clones the role repo and tracks whatever sits at the tip of the default branch. Every jackin load runs git pull and silently picks up whatever the upstream pushed since the last run.
That has three concrete consequences:
- Behaviour drifts between runs without operator awareness. Yesterday
the-architectbehaved one way; today the upstream merged a refactor and now it behaves another. The operator did not request, see, or approve the change. - Past runs are not reproducible. If something worked Tuesday and broke Thursday, nothing on disk records which role-repo commit Tuesday’s run actually used. The session state, the launch summary, and the config file all describe “the role” but not “this specific snapshot of the role”.
- Trust is granted to a moving target. The operator may have marked a role
trusted = truelast month after reading the code at one commit. The remote has since moved 40 commits. The trust flag still saystrue, but it is now applied to code that has never been reviewed.
Why it matters
Section titled “Why it matters”This is the foundation for treating role repos as supply-chain artefacts rather than “always-latest from the internet”. Without pinning, the audit trail in ~/.config/jackin/config.toml does not capture what was actually run — only which repo it came from. That is fine for prototyping but rules out reproducing a session, bisecting an agent regression, or trusting a third-party role beyond a single read-through.
Proposed approach (brainstorm — pending operator approval)
Section titled “Proposed approach (brainstorm — pending operator approval)”Pin role repos to an immutable commit SHA on first resolve, prefer human-readable tags when present, and require an explicit jackin load --update to advance the pin (which also resets trust so the operator re-confirms against the new code). This is the “Option 3 (hybrid)” sketch from the original brainstorm; the open questions in the previous draft of this page are folded into the implementation sketch below.
Schema sketch
Section titled “Schema sketch”Add two optional fields to the role-source entry in config.toml:
[roles.agent-smith]git = "https://github.com/jackin-project/jackin-agent-smith.git"commit = "a1b2c3d4e5f6…" # resolved SHA, runtime-writtenversion = "v1.2.3" # tag at HEAD, when one existstrusted = trueBoth fields are optional and additive. A role that has never been resolved omits both; the runtime fills them in after the first successful clone. commit is the source of truth for what gets checked out; version is purely cosmetic (launch summary line).
Runtime behaviour sketch
Section titled “Runtime behaviour sketch”- First resolve (no
commitrecorded): clone the repo, then capture HEAD SHA viagit rev-parse HEADand any tag exactly at HEAD viagit describe --tags --exact-match HEAD(best-effort; absence is fine). Persist both back into the operator’sconfig.toml. - Subsequent loads (with
commitset):git fetch(no merge), thengit checkout <commit>in detached-HEAD mode. No auto-pull, no auto-advance. jackin load --update(new flag):git fetch, then re-pin. Resolution rule: if the repo has any tag, pick the highest semver-sortedv*tag; otherwise re-pin to the default branch’s HEAD. Resettrusted = falseso the operator must re-confirm trust against the new code.--role-branch <name>(existing flag) keeps its current bypass behaviour — does not write a pin, used for ad-hoc PR testing.- Built-in roles follow the same rules; the only special case is that
trusted = trueis asserted by the binary on every sync. - Launch summary prints
Role: <key> @ <version-or-short-sha>so the operator sees the resolved version on every run.
Config schema migration
Section titled “Config schema migration”Per the project’s pre-release schema rule (see AGENTS.md), any change to a serde-bearing config struct requires a version bump and a migration registry entry, even when the change is purely additive. This work would bump CURRENT_CONFIG_VERSION from v1alpha2 to v1alpha3, add a no-op MigrationStep, ship a new from-v1alpha2/ fixture, and re-bake the existing from-legacy/ and from-v1alpha1/ fixtures so their migration chains end at v1alpha3. Existing operator config files keep parsing without modification because the new fields are Option<String> with serde defaults.
Open questions for operator review
Section titled “Open questions for operator review”- Is the
--updateresolution rule “highestv*tag, fall back to default branch HEAD” the right default? Or should the operator have to opt into tag-following with a separate flag, with the default being “advance to default-branch HEAD”? - Should
--updatealso accept an explicit target — e.g.jackin load --update v1.3.0to pin to a specific tag, orjackin load --update <sha>to pin to a specific commit? - Should re-pinning always reset
trusted = false, or only when the new commit is outside the previously-trusted commit’s ancestry (i.e. a force-push or unrelated commit)? The current sketch is conservative (“any movement re-prompts trust”); a finer-grained rule is possible but adds complexity. - For built-in roles, should the binary ship a default pin (so a fresh install lands on a known-good commit rather than HEAD), or continue to resolve on first load? Shipping defaults adds release-engineering work but removes the first-launch network surprise.
Files that would change
Section titled “Files that would change”Code:
src/config/mod.rs— addcommit,versionfields toRoleSource.src/config/migrations.rs— version bump and registry step.src/config/roles.rs—sync_builtin_agentsmust update fields in place rather than overwriting the wholeRoleSource, so a re-pinned built-in survives a binary upgrade.src/runtime/repo_cache.rs— capture-pin helper, fetch+checkout-detached path for pinned commits,--updatepath, persist callback into config.src/runtime/launch.rs— wire the--updateflag, include resolved version/SHA in the launch summary.
Docs:
- This page — convert from brainstorm to design proposal once approved, then to “Resolved” once shipped.
- A new operator-facing note for
--updateon thejackin loadpage. - A new timeline entry on the schema-versions reference page for
v1alpha3.
Related files
Section titled “Related files”src/config/roles.rs—resolve_agent_source();AgentSource(defined insrc/config/mod.rs) would gain the new fields.src/runtime/repo_cache.rs— repo checkout logic; calls into config layer to resolve source.src/manifest/mod.rs— version/provenance metadata.