ADR 0031: Git-Native Process CodeRepositoryBranch Context
Date: 2026-08-22
Status: Accepted and implemented; aligned with platform ADR-046 for the immediate CodeRepository cutover
Partially superseded by ADR 0035: Independent Git source and platform execution context, accepted and implemented on metatables_removal.
Network-free source discovery removes mandatory platform enrichment for local
Git facts. Missing Environment errors are deferred to operations that require one;
Environment scope remains derived from the registered current branch.
Canonical Git facts, source drift checks, runtime target verification, and real
branch-owned operation guards remain in force. ADR 0034 separately removes the
historical MetaTables/DataSource implementation described in this record.
Current contract:
CodeRepository,CodeRepositoryBranch, andGitHubRepositoryBindingare the canonical public names. The Project-era SDK ontology is retired and must not be reintroduced.
Context
Application code runs in local checkouts and platform-built runtime images. The SDK
previously used two identity mechanisms: local .env plus Git for developer
work, and authenticated CodeRepositoryBranch values projected into process environment
for deployed work. That split allowed source identity to depend on mutable
configuration even though both locations execute a Git checkout.
Platform ADR-0037 makes the checked-out Git repository the single source of truth for repository, branch, and commit identity. Runtime authorization remains backend-owned and target-derived; Git identity never grants permission.
Ontology
| Concept | Responsibility |
|---|---|
| Git worktree | Identifies the repository, attached branch/ref, and exact commit executed by this process |
GitHubRepositoryBinding |
Maps canonical Git repository identity to the logical platform CodeRepository |
CodeRepository |
Repository-level platform aggregate; it does not authorize a branch or Environment |
CodeRepositoryBranch |
Persisted platform identity for the exact CodeRepository and Git branch |
OrganizationEnvironment |
Environment derived from the resolved CodeRepositoryBranch |
CodeRepositoryBranch.metatables_data_source |
Optional physical MetaTables DataSource for that exact branch |
| Runtime credential or human token | Authenticates and authorizes the caller; it does not select source identity |
Decision
Superseded in part by ADR 0035: source-only callers use
get_git_source_context()without platform enrichment. The original composition entry point below remains available.
The original decision defined one CodeRepository-context entry point:
from mainsequence.code_repository_context import get_code_repository_context
get_code_repository_context()
It uses the same algorithm in a local shell, CLI process, Job, supported ResourceRelease, runtime-agent release, or CodeRepository Executor:
containing Git worktree
-> canonical non-secret repository identity
-> attached refs/heads/<branch>
-> exact full HEAD commit
-> backend resolve-git-context contract
-> GitHubRepositoryBinding
-> CodeRepository
-> exact CodeRepositoryBranch
-> OrganizationEnvironment
-> optional CodeRepositoryBranch MetaTables DataSource
Authentication mode affects credentials only. The resolver does not choose a local or deployed identity path.
Frozen source snapshot
The first call resolves and freezes:
repository_root
canonical_repository_identity
repository_branch
repository_ref
commit_sha
code_repository_uid | None
code_repository_branch_uid | None
organization_environment_uid | None
metatables_data_source | None
status
process_id
Initialization is protected by a process-local lock. Concurrent callers receive the identical immutable object. Resolution errors are cached. A forked child discards inherited state and resolves once for its own process.
A deliberate git switch takes effect in the next process. Later validation in
the same process rejects a changed repository, branch, or HEAD as source-context
drift; it does not silently retarget platform work.
Valid unresolved states
ADR 0035 removes the branch prerequisite for Environment-owned resources. The historical paragraph below remains applicable only to branch-owned targets.
Resolution itself does not fail merely because no visible CodeRepositoryBranch maps to the repository and branch:
code_repository_branch_not_registered
The canonical backend action deliberately exposes one not-found result rather
than leaking whether the repository or only its branch is registered. This
permits ordinary work on local repositories and new feature branches. Only an
operation that requires branch-linked platform context calls
require_code_repository_branch_context() and fails before its backend request. This
includes both CodeRepositoryBranch-owned objects and CodeRepository-facing operations
on Environment-owned resources whose Environment is resolved from the current
CodeRepositoryBranch.
No unresolved state may fall back to main, a sibling CodeRepositoryBranch, a
repository-level DataSource, an Environment inferred from a DataSource, collection
order, or a caller-supplied CodeRepositoryBranch UID. The removed
repository-level default MetaTables DataSource contract is not restored.
Retired environment contract
ADR 0035 adds typed Python configuration for development Environment scope. It does not restore these retired environment variables or make them source identity.
The SDK does not read, write, or inject these values as source identity:
Caller-supplied repository, branch, and Environment identity variables
Local .env stores only unrelated user configuration plus supported endpoint
and authentication transport. Setup and refresh remove stale occurrences of all
four variables. Existing process values are ignored, not treated as fallbacks.
The SDK still supports authentication variables such as
MAINSEQUENCE_ENDPOINT, MAINSEQUENCE_AUTH_MODE, access/refresh tokens, and
runtime credentials. They answer where and as whom to call, not which repository
source is running.
Branch-owned consumers
Current-branch Job, CodeRepositoryImage, ResourceRelease, CodeRepositoryResource, CodeRepositoryExecutor, migration, platform-managed MetaTable, and TimeIndexTableUpdater workflows consume the frozen context. Ordinary branch-owned collections are scoped to the resolved CodeRepositoryBranch. Explicit administrative enumeration uses separately named admin APIs.
An instance method does not make an arbitrary CodeRepositoryBranch instance
runtime routing authority. Current-branch methods validate the instance UID
against the UID returned by get_code_repository_context() and build their
request from the resolved UID.
An explicitly targeted operation is different. GitHub issue listing and
creation use the receiving CodeRepositoryBranch as the target resource, which
may differ from the process-frozen branch. The SDK sends that target UID through
the nested route without accepting a separate Environment, repository, or
provider-binding selector. The backend derives ownership from the target and
remains the authorization boundary. For runtime credentials, it constrains the
target to the authenticated runtime's Organization Environment and intersects
that boundary with the responsible user's branch permissions. Targeting a
resource does not replace or mutate the process-frozen runtime context.
Parent-derived operations retain the persisted parent's ownership instead of inventing another branch selector. Backend authorization remains the final enforcement boundary. Current-branch operations require the Git-resolved branch to equal the authenticated runtime target; explicitly targeted operations must enforce their documented target and Environment boundary instead.
Environment-owned resources resolved from the current CodeRepositoryBranch
ADR 0035 preserves this branch-derived Environment contract. A missing Environment is valid during general source/context discovery and raises only when an Environment-owned operation requires it. Runtime target authority and SDK-owned wire fields are preserved.
Object ownership and operation context are separate concerns. Secret,
Constant, logical Bucket, Artifact, and MetaTable belong to one
OrganizationEnvironment; they are not owned by one CodeRepositoryBranch.
CodeRepositories and branches participating in the same Environment may use the same
resources.
CodeRepository-scoped SDK operations nevertheless resolve that Environment only through the frozen current CodeRepositoryBranch:
actual Git worktree and attached branch
-> get_code_repository_context()
-> exact persisted CodeRepositoryBranch
-> OrganizationEnvironment
-> Environment-owned resource operation
This rule covers CodeRepository-facing create, lookup, list, search, update, and delete
operations for Secrets, Constants, Buckets, and Artifacts. It also covers
MetaTable registration and import, including external_registered MetaTables.
An external MetaTable may select its physical DataSource, but that DataSource is
not Environment identity and cannot override the Environment derived from the
CodeRepositoryBranch.
The public CodeRepository-facing SDK API accepts neither
organization_environment_uid nor a caller-supplied CodeRepositoryBranch UID.
The SDK resolves the current CodeRepositoryBranch once and transports its derived
Environment UID as an SDK-owned wire field:
{
"organization_environment_uid": "<branch-derived Environment UID>"
}
This is a transport detail, not user-selected authority. The SDK owns the field
and rejects attempts to supply or override it. For local human credentials, the
value comes from the authenticated resolve-git-context response. For a
deployed JobRun or Knative runtime, the same Git-native resolution is verified
against the authenticated runtime target. Resource endpoints must reject a
transported Environment that disagrees with that runtime target.
Create requests carry the SDK-owned field in their body. List, detail, patch, delete, and detail-action requests carry it as a query parameter. Both forms have the same semantics: the Environment came from the frozen current CodeRepositoryBranch and is not a public selector. Backend endpoints must normalize these inputs through one Environment resolver before filtering or resolving a known resource UID.
An unregistered local branch remains valid for unrelated development. A
CodeRepository-facing operation on one of these Environment-owned resources calls
require_code_repository_branch_context() and fails before its request because no
Environment can be derived. It must not fall back to main, another branch, an
Environment UID, a DataSource, or Organization-wide enumeration.
Explicit Organization-administration APIs may allow an administrator to select an Environment without a Git worktree. Such APIs are separate from the CodeRepository-facing SDK contract and must be named and authorized as administrative surfaces; they are not a fallback for CodeRepository code.
Backend responses expose the derived
organization_environment_uid and
organization_environment_name as required read-only projections. The
SDK models parse those fields but never use them as write authority.
Coordinated Implementation Plan
- [ ] Add one backend CodeRepositoryBranch-context resolver for Environment-owned resource operations. It must accept SDK-resolved local context, derive context from authenticated deployed runtimes, verify equality when both exist, and return the canonical Environment.
- [ ] Apply that resolver to Secret, Constant, Bucket, and Artifact list, detail, create, update, and delete paths. Known public UIDs must not bypass the same Environment boundary.
- [ ] Change CodeRepository-facing external MetaTable registration and import to derive Environment from CodeRepositoryBranch context. Preserve the explicitly selected physical DataSource without treating it as Environment identity.
- [x] Add one SDK helper that attaches the branch-derived Environment to request
bodies or query parameters. The helper calls
require_code_repository_branch_context()and rejects caller overrides. - [x] Route
Secret,Constant,Bucket, andArtifactSDK methods and their CLI commands through that helper. Remove direct Environment selection from CodeRepository-facing command and method signatures. - [x] Route external
MetaTable.register()through the same helper instead of skipping Environment injection. Keep managed MetaTable, migration, and TimeIndexTableUpdater paths on the existing shared resolver. - [x] Add read-only Environment UID and name projections to all affected SDK response models while preserving strict rejection of unknown fields.
- [x] Test SDK request injection, caller override rejection, canonical response parsing, missing Environment context, and existing process context locking.
- [ ] Add coordinated backend integration coverage for local human credentials, deployed JobRun credentials, deployed Knative credentials, mismatched runtime context, cross-Environment known-UID access, and same-Environment sharing.
- [x] Update CLI documentation and agent skills to describe branch-derived Environment resolution and remove instructions that ask CodeRepository users to choose an Environment UID.
Consequences
- Local and deployed CodeRepository code use one resolution algorithm.
- Source identity cannot be changed by environment injection.
- Every CodeRepository-sensitive consumer sees one repository, branch, and commit for the complete process run.
- Unregistered branches remain usable for local development but cannot create, mutate, or enumerate branch-owned platform resources or CodeRepository-facing Environment-owned resources that require branch-derived context.
- Supported runtime images must preserve a sanitized attached Git checkout. Images without that contract must be rebuilt; the SDK has no permanent environment fallback.
- Repository mapping uses the canonical
POST /api/v1/code-repository-branches/resolve-git-context/action. That action also verifies exact equality with an authenticated JobRun or Knative runtime target.
Rejected Alternatives
Keep a deployed environment projection
Rejected because it recreates a second mutable source of repository and branch identity and conflicts with platform ADR-0037.
Resolve on every operation
Rejected because it repeats Git/backend work and lets one process change ownership after a branch switch.
Fail every operation on an unregistered branch
Rejected because local Git development does not require a persisted CodeRepositoryBranch. Enforcement belongs at branch-owned platform operations.
Accept explicit CodeRepositoryBranch UIDs for current-repository work
Rejected because callers could select a branch that differs from the source actually executing. Explicit CodeRepository UIDs retained by some CLI commands are assertions against Git resolution, never selectors.
Required Invariants
- Git is the only SDK source of repository, branch/ref, and commit identity.
- Exactly one SDK entry performs current-run platform mapping.
- Resolution runs once per process and once again after a fork.
- Detached HEAD, missing Git metadata, missing/ambiguous remote identity, and source drift fail explicitly.
- Unregistered repository or branch state does not abort unrelated local work.
- Branch-owned operations and CodeRepository-facing Environment-resource operations reject unresolved context before backend calls.
- CodeRepository-scoped resource APIs never accept caller-selected CodeRepositoryBranch or Environment identity.
- The four retired identity variables never influence source resolution.
- No CodeRepositoryBranch, Environment, sibling-branch, or DataSource fallback exists.
- Backend authorization remains authoritative and must verify deployed target equality.