Skip to main content
A Project is a reusable, Workspace-scoped filesystem tree an agent works in. Mount it into a Session and its files appear at a path in the sandbox. A single uploaded file and a whole repository share one shape: a file is just a one-entry Project.

Two sources

A Git Project resolves through a Connection: Checkfu reads the repository using the acting Principal’s authorized provider identity and never handles a raw provider token. acted_as must own and hold use_connection on that Connection.

Immutable revisions

You never edit a Project in place. Every change produces a new immutable revision:
  • An uploaded Project gets a new revision when you push new files (with expected_version for optimistic concurrency).
  • A Git Project gets a new revision when you POST /v1/projects/{id}/refresh, which re-resolves the ref to an exact commit and records the commit_sha (and tree).
A revision records its content hash, file count, and byte size, so what an agent saw is inspectable long after the fact.

Create and refresh a Git Project

Create the Project with the Principal that owns the GitHub Connection:
Creation resolves and publishes revision 1, so the create response reports latest_revision: 1 and version: 2: the revision counter and the resource version are separate, and publishing that first revision already moved the version. Read GET /v1/projects/{id}/revisions/1 for its exact commit and tree. When the ref advances, refresh with the Project’s current version:
If the Project changed concurrently, expected_version fails with 409; read the Project again before retrying. If the ref still identifies the recorded commit and tree, refresh also returns a conflict rather than creating a duplicate revision. Use GET /v1/projects/{id}/revisions/{revision} for the exact revision and GET /v1/projects/{id}/revisions/{revision}/tree for its ordered file metadata. The public /file body endpoint serves uploaded revisions only. Git bodies stay behind the live Run lease so each read can re-check Connection authority and integrity immediately before staging.

latest, a pinned revision, or a published Git ref

A Session mounts a Project with latest, a pinned revision, or a Git ref policy. This is one half of the platform’s two freeze horizons:
  • latest re-resolves to whatever the newest revision is at the start of each Run, and is fixed for that Run. Refresh the Git Project between Runs and the next Run picks up the new commit.
  • pinned locks one revision for the Session’s life, regardless of later refreshes.
  • ref selects the newest immutable revision already published for that exact Git ref. It performs no provider I/O during Run preflight and fails closed when no such revision exists.
Either way, the exact revision a Run received is recorded in that Run’s session.mounts_resolved.

Repository Skills

At the same Run boundary, an eligible Git mount inspects exactly .claude/skills/<name>/SKILL.md in that frozen revision. Valid manifests use the same Agent Skills parser as catalog Skills, but they remain repository content at their mounted paths: Checkfu creates no Skill aggregate or release pin. Duplicate names remain available by path. Nested, rootless, package-local, malformed, and name-mismatched entries are not announced. When the Agent has the built-in read tool, Checkfu stages a read-only index at /environment/.checkfu/repository-skills.md and adds a bounded pointer to it. Without read, the pointer is omitted. Treat mounting as a trust decision: repository Skills are instruction input and may include contributions from people outside your organization.

Writable overlays and opt-in pull-request writeback

A mount is read_only or read_write, but even read_write does not mutate the Project. Runtime writes land in a disposable sandbox overlay: they exist for that Run and vanish with the sandbox. Publishing a new uploaded revision or pushing a Git tree is a separate, explicit, governed action. For a Git-backed Project under standard retention, a read_write mount can opt into Session-owned publication with writeback: { "kind": "pull_request" }. Checkfu captures the complete delta at Run settlement, closes the sandbox, and reconciles it without holding compute: one platform-derived branch, non-force exact-head updates, one pull request, and one immutable branch revision selected only by that Session mount. A missing policy remains byte-for-byte the disposable-overlay behavior; uploaded Projects and ZDR reject writeback. The policy accepts bounded static commit_message, pull_request_title, and pull_request_body overrides. Omitted values use deterministic platform defaults; an omitted body uses a bounded default PR template from .github/, the repository root, or docs/ at the frozen target ref when one is present. Provider credentials, hidden reasoning, and event history never enter the commit or PR body. Standard interactive Sessions attach one reusable Watch after the PR’s fresh baseline is known, so external comments, reviews, and checks can wake the same Session and the next writeback can advance the same branch and PR. D127 still excludes Automation-owned Sessions from Watch-driven runs; their writeback produces the PR receipt without claiming an unsupported feedback wake.

Write back to Git

A Git Project is not read-only. Two governed endpoints turn a tree you have staged into a branch and then a pull request, through the same Connection that refresh reads with. Checkfu still never handles a raw provider token, and acted_as must own the Connection and hold use_connection.
POST /v1/projects/{id}/push writes one commit and returns 201 with the new branch, its commit_sha, the base_revision and base_commit_sha it was built on, plus file_count and total_bytes. POST /v1/projects/{id}/pull-requests then takes head, base, title, and an optional body, and returns the pull request number and url. Provider validation refusals (no commits between the two branches, or a pull request already open for them) come back as a reasoned 409 validation.conflict. The merge itself stays human. Both calls require Idempotency-Key. Retry an interrupted response with the same key and the identical request: after the live lease expires, Checkfu returns the retained provider result without repeating the push or opening a second pull request. Three rules make the write safe to automate:
  • base_revision is a compare-and-swap. It names an immutable revision of this Project, and that revision’s exact commit must still head the Project’s source ref. If the ref moved, the push refuses with 409 validation.conflict rather than silently dropping the newer work. Refresh, rebuild the tree on the new revision, and push again.
  • A push only ever creates a branch. It never force-pushes and never updates an existing one, so a replayed push cannot overwrite a colleague’s branch. The destination ref is checked before any blob is uploaded, so a name collision fails early with 409 validation.conflict instead of after the whole tree is written.
  • Neither call touches the Project. The source ref is never advanced, no revision is published, and the Project’s version does not move. Adoption stays a human merge plus an explicit refresh. Each success appends one audit fact (project.pushed or project.pull_request_opened) that records coordinates, never file bodies.
A push manifest is capped at 64 files, lower than the 1,024 entries an uploaded revision accepts, because each file becomes its own content mutation against GitHub’s per-minute budget. The per-file and per-revision byte limits are the same as an uploaded revision’s; the limits table has both. Split larger work across pushes to separate branches.
Because a push stores no file bodies (the tree streams to the provider and Checkfu retains only coordinates and hashes), it works in a zero-data-retention Workspace, where publishing an uploaded revision does not.
Managed runs materialize both uploaded and Git Project revisions through a credential-free, integrity-checked handoff. A Git plan contains the pinned commit, tree, and ordered blob locators, not a provider credential. The API re-checks the live Connection PermissionAssignment for each lease-fenced blob read, and both the API and Runner verify its Git digest and byte count before staging it.

Next steps

Sessions and runs

How a mount resolves per Run, and the two freeze horizons.

Capabilities

The Connection a Git Project reads through.