From b726d68d641476b71ad54fd5a464568015ce01c5 Mon Sep 17 00:00:00 2001 From: leyoonafr Date: Fri, 28 Aug 2026 12:30:54 +0800 Subject: [PATCH] docs: add MVP product and architecture documentation (#2) --- CONTEXT.md | 221 +++++ README.md | 10 + .../0001-isolate-codex-host-integration.md | 27 + ...use-system-git-behind-repository-engine.md | 28 + .../mvp-technical-architecture.md | 783 ++++++++++++++++++ docs/product/mvp-prd.md | 645 +++++++++++++++ 6 files changed, 1714 insertions(+) create mode 100644 CONTEXT.md create mode 100644 docs/adr/0001-isolate-codex-host-integration.md create mode 100644 docs/adr/0002-use-system-git-behind-repository-engine.md create mode 100644 docs/architecture/mvp-technical-architecture.md create mode 100644 docs/product/mvp-prd.md diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..3dfba53 --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,221 @@ +# Codex Git + +Codex Git is the local Git workspace presented for the Current Project. Its +language keeps Repository-wide facts separate from the state and actions of each +registered Worktree. + +## Workspace + +**Current Project**: +The local macOS project currently selected as the source of the Git workspace. +It may or may not belong to a Repository. +_Avoid_: Active project, open folder + +**Repository**: +One Git repository, including its shared objects, references, configuration, and +registered Worktrees. +_Avoid_: Repo, project, folder + +**Worktree**: +One registered checkout of a Repository with its own Working Tree, Index, and +HEAD. +_Avoid_: Workspace, checkout, task + +**Main Worktree**: +The primary Worktree of a Repository. It is a Git role, not a claim about its +Branch name or how the Worktree is used. +_Avoid_: Main Branch, root Worktree + +**Linked Worktree**: +Any registered Worktree that is not the Main Worktree. +_Avoid_: Secondary Repository, child Worktree + +**Available Worktree**: +A registered Worktree whose Working Tree can currently be inspected and used for +the capabilities allowed by its Git state. +_Avoid_: Healthy Worktree, active task + +**Unavailable Worktree**: +A registered Worktree that cannot currently be inspected or acted on, while its +registration remains relevant for diagnosis. +_Avoid_: Deleted Worktree, invalid Worktree + +**Worktree Generation**: +One continuous identity lifetime of a registered Worktree. Removing, moving, or +recreating a Worktree begins a different generation even if a path is reused. +_Avoid_: Worktree version, refresh generation + +## Provenance + +**Provenance**: +Optional evidence about who or what created or owns a Worktree. Provenance never +determines whether a Worktree belongs to the Repository or which Git capabilities +it has. +_Avoid_: Worktree type, Git source + +**Codex Task Worktree**: +A Worktree whose association with a Codex task is proven by stable Codex-owned +metadata. +_Avoid_: Codex-looking Worktree, task Branch + +**Scheduled Worktree**: +A Worktree whose scheduled lifecycle is proven by stable Codex-owned metadata. +_Avoid_: Automation Branch, scheduled-looking Worktree + +**Permanent Worktree**: +A Worktree whose permanent lifecycle is proven by stable Codex-owned metadata. +_Avoid_: Long-lived Worktree, manually named Worktree + +**External Worktree**: +A Worktree whose non-Codex origin is proven by stable Codex-owned metadata. +_Avoid_: Manual Worktree, unknown Worktree + +**Unclassified Worktree**: +A Worktree for which provenance evidence is absent, unstable, or conflicting. +_Avoid_: External Worktree, other Worktree + +## Git state + +**Working Tree**: +The checked-out files of one Worktree. +_Avoid_: Workspace files, local files + +**Index**: +The staged snapshot belonging to one Worktree and proposed for its next Commit. +_Avoid_: Staging area, staged files + +**HEAD**: +The current Commit position of one Worktree, either attached to a Local Branch or +detached. +_Avoid_: Current Branch, latest Commit + +**Local Branch**: +A named local reference to a Commit. +_Avoid_: Branch when local or remote-tracking kind matters + +**Remote-tracking Branch**: +A locally cached reference representing the last fetched state of a Branch in a +Remote. +_Avoid_: Remote Branch, live Branch + +**Upstream**: +The configured Remote-tracking Branch against which a Local Branch is compared +and to which its ordinary Pull and Push are directed. +_Avoid_: Remote, origin, destination Branch + +**Unpublished Branch**: +A Local Branch without an Upstream that is eligible to be published to a +confirmed same-name Branch on a selected Remote. +_Avoid_: New Branch, local-only Branch + +**Detached HEAD**: +A Worktree state in which HEAD identifies a Commit without being attached to a +Local Branch. +_Avoid_: No Branch, anonymous Branch + +**Initial Repository State**: +A Repository state before the first Commit exists. +_Avoid_: Empty Branch, broken HEAD + +**Clean Worktree**: +A Worktree with no Conflict and no difference among HEAD, Index, and Working +Tree, including no Untracked File. +_Avoid_: Safe Worktree, unchanged Repository + +**In-progress Git Operation**: +A Git-managed Repository state indicating an unfinished operation whose +completion or recovery is outside the MVP. +_Avoid_: Busy Worktree, lock + +**Branch Occupancy**: +The association between a Local Branch and the registered Worktree in which it is +currently checked out. +_Avoid_: Branch lock, Branch owner + +## Changes and review + +**Changed File**: +One path-and-baseline observation in a Worktree. The same path may be represented +by more than one Changed File when it differs across multiple baselines. +_Avoid_: Dirty file, modified path + +**Conflict**: +A Changed File whose Index has unresolved entries. +_Avoid_: Merge error, unstaged change + +**Staged Change**: +A Changed File representing a difference from HEAD to Index. +_Avoid_: Staged File + +**Change**: +A Changed File representing a difference from Index to Working Tree. +_Avoid_: Unstaged File, modification + +**Untracked File**: +A Working Tree path that is not represented in the Index. +_Avoid_: New Change, unstaged file + +**Diff Baseline**: +The exact pair of Git states compared for one Changed File review. +_Avoid_: File version, diff type + +**Commit Draft**: +The unsubmitted Commit message associated with one Repository and Worktree. +_Avoid_: Commit, message template + +## Operations and outcomes + +**Local Mutation**: +An operation that may change one Worktree's Index, HEAD, or Working Tree without +contacting a Remote. +_Avoid_: Local command, file operation + +**Branch Switch**: +A Repository-coordinated operation that changes the Branch or detached position +of one Worktree. +_Avoid_: Checkout, Branch change + +**Remote Operation**: +An operation that communicates with a configured Remote and may change shared +references or transfer Git objects. +_Avoid_: Network command, sync + +**Refresh**: +A local observation that produces current Repository and Worktree state without +contacting a Remote. +_Avoid_: Fetch, reload + +**Reconciliation**: +A fresh observation after an attempted mutation that establishes what Git state +actually resulted. +_Avoid_: Refresh when outcome recovery is meant, rollback + +**Succeeded**: +An operation outcome in which the requested effect is verified in reconciled +state. +_Avoid_: Completed + +**Rejected**: +An operation outcome in which a current precondition prevents execution and no +requested mutation begins. +_Avoid_: Failed, invalid + +**Failed Known**: +An operation outcome in which execution does not achieve the requested effect and +reconciled state is known. +_Avoid_: Error, rejected + +**Partial Success**: +An operation outcome in which independently reportable requested effects have a +mixture of verified success and failure. +_Avoid_: Failed, mostly succeeded + +**Unknown Outcome**: +An operation outcome in which interruption, timeout, or ambiguous process state +prevents the product from proving whether the requested effect occurred. +_Avoid_: Failed, cancelled + +**Busy**: +A Rejected outcome indicating that a conflicting operation lane is already in +use and the new mutation was not queued. +_Avoid_: Pending, waiting diff --git a/README.md b/README.md index e2bbe1e..6ad01f6 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,15 @@ Codex Git is a planned local Git surface for Codex Desktop. This repository currently contains only the initial application scaffold; Worktree discovery, Git commands, Codex injection, packaging, and other product features are not implemented. +## Product and architecture + +- [Domain language](./CONTEXT.md) +- [macOS MVP product requirements](./docs/product/mvp-prd.md) +- [MVP technical architecture](./docs/architecture/mvp-technical-architecture.md) +- Architecture decisions: + - [Isolate Codex host integration behind a Host Adapter](./docs/adr/0001-isolate-codex-host-integration.md) + - [Use the system Git CLI behind a local Repository Engine](./docs/adr/0002-use-system-git-behind-repository-engine.md) + ## Requirements - macOS @@ -43,4 +52,5 @@ packages/host-adapter Host Adapter boundary packages/host-adapter/* Codex CDP and standalone adapter placeholders tests/ Reserved contract, integration, and end-to-end layers ``` + # codex-git diff --git a/docs/adr/0001-isolate-codex-host-integration.md b/docs/adr/0001-isolate-codex-host-integration.md new file mode 100644 index 0000000..29d3872 --- /dev/null +++ b/docs/adr/0001-isolate-codex-host-integration.md @@ -0,0 +1,27 @@ +--- +status: accepted +--- + +# Isolate Codex host integration behind a Host Adapter + +Codex Git will expose one Host Adapter interface with standalone and Codex CDP/DOM +adapters. The standalone adapter is the dependable fallback; the Codex adapter is +an explicitly unsupported, replaceable integration that may mount the same Git +surface in compatible Codex Desktop builds without letting host details enter the +product modules. + +## Considered options + +- Build directly against Codex renderer structure. This couples every product + module to an undocumented host that can change without notice. +- Ship only a standalone surface. This is stable but does not provide the intended + top-level Codex `Git` experience. +- Isolate host behavior behind an adapter seam. This preserves the intended + experience while keeping compatibility failure local and recoverable. + +## Consequences + +The Codex adapter must fail closed, validate compatibility before mutation, clean +up everything it mounts, and fall back to standalone operation. No official Codex +extension capability is assumed, and installation/runtime documentation must +disclose the trusted-local-process CDP boundary. diff --git a/docs/adr/0002-use-system-git-behind-repository-engine.md b/docs/adr/0002-use-system-git-behind-repository-engine.md new file mode 100644 index 0000000..035e626 --- /dev/null +++ b/docs/adr/0002-use-system-git-behind-repository-engine.md @@ -0,0 +1,28 @@ +--- +status: accepted +--- + +# Use the system Git CLI behind a local Repository Engine + +Codex Git will obtain Git semantics from the user's system Git executable, but +only the local Repository Engine may discover repositories, construct arguments, +start Git processes, or interpret their results. The browser surface communicates +through typed product operations and never receives general process authority. + +## Considered options + +- Implement Git behavior with a JavaScript or Rust library. Library coverage and + behavior would diverge from the user's configured Git, hooks, signing, credential + helpers, and Worktree semantics. +- Let the UI invoke Git directly. This exposes filesystem and process authority to + an untrusted presentation surface and spreads correctness rules across callers. +- Put system Git behind one deep Repository Engine module. This preserves native + behavior while concentrating validation, concurrency, redaction, and recovery. + +## Consequences + +The Repository Engine owns a small typed interface and all Git execution policy. +It must use literal arguments and bounded input/output, preserve configured hooks, +signing, and credential helpers, never invoke a shell, and reconcile state after +every attempted mutation. Packaging may supervise the Engine but must not fork or +reimplement its Git behavior. diff --git a/docs/architecture/mvp-technical-architecture.md b/docs/architecture/mvp-technical-architecture.md new file mode 100644 index 0000000..bdf4e05 --- /dev/null +++ b/docs/architecture/mvp-technical-architecture.md @@ -0,0 +1,783 @@ +# Codex Git MVP technical architecture + +## Status and scope + +This document defines the architecture for the macOS MVP in the +[product requirements](../product/mvp-prd.md). It uses the canonical language in +[`CONTEXT.md`](../../CONTEXT.md) and records two accepted, hard-to-reverse choices: + +- [Isolate Codex host integration behind a Host Adapter](../adr/0001-isolate-codex-host-integration.md) +- [Use the system Git CLI behind a local Repository Engine](../adr/0002-use-system-git-behind-repository-engine.md) + +The architecture covers the source-mode runtime and the later thin macOS package. +It does not add product behavior beyond the PRD and does not choose detailed UI +styling. + +## Architectural drivers + +1. Git correctness spans a Repository containing independent Worktree Working + Trees, Indexes, and HEADs plus shared objects, refs, configuration, and + Worktree registration. +2. Codex tasks and external Git processes may change those states at any time. +3. The browser surface cannot safely hold filesystem or process authority. +4. Codex Desktop has no official extension point assumed by this MVP for adding + the required top-level surface. +5. The supported fixture requires bounded, selected-first observation rather than + serial full-Repository reads after every event. +6. Process exit is not sufficient proof of a mutation outcome after interruption, + timeout, hook/signing behavior, or partial multi-target work. + +## Architecture rules + +- Keep the unsupported Codex CDP/DOM implementation behind the Host Adapter seam. +- Keep the standalone adapter functional against the same host interface and Git + surface. +- Run all Git reads and mutations in the local Repository Engine. +- Let the browser express typed product intent only; it never executes a shell or + constructs Git arguments. +- Use opaque IDs and coherent, versioned snapshots across every untrusted seam. +- Never authorize a mutation from UI state without fresh local precondition + checks. +- Discover Worktrees from Git registration; provenance neither filters discovery + nor grants Git capabilities. +- Reject conflicting operations as Busy rather than silently queueing user + mutations. +- Reconcile every state axis affected by every attempted mutation, including + cancellation, interruption, and timeout. +- Never remove external locks, rewrite history, auto-stash, prune by default, or + collect credentials. +- Keep modules deep: callers learn small product interfaces while discovery, + validation, Git invocation, redaction, coordination, and recovery remain local + to their owning implementation. + +## Process model + +```mermaid +flowchart LR + subgraph Host[Host process] + Launcher[Launcher / package supervisor] + Standalone[Standalone Host Adapter] + Codex[Codex CDP/DOM Host Adapter] + end + + subgraph Browser[Sandboxed browser renderer] + UI[Git Surface] + Store[Repository Store] + end + + subgraph Local[Local loopback process] + Protocol[Versioned protocol module] + Engine[Repository Engine] + Refresh[Refresh coordinator] + Ops[Operation coordinator] + Native[Native action resolver] + Redact[Diagnostic redactor] + end + + subgraph System[Local system] + Git[System Git CLI] + FS[Repository files and metadata] + Mac[Allow-listed macOS navigation] + Remote[Configured Git Remotes] + end + + Standalone --> UI + Codex -->|opaque sandboxed iframe| UI + UI --> Store + Store <-->|tokened HTTP and SSE| Protocol + Protocol --> Engine + Engine --> Refresh + Engine --> Ops + Engine --> Native + Engine --> Redact + Refresh --> Git + Ops --> Git + Git <--> FS + Git <--> Remote + Native --> Mac + Launcher --> Protocol + Launcher --> Standalone + Launcher -. optional .-> Codex +``` + +### Launcher and later package supervisor + +The source launcher composes adapters, the local server, and the surface. The +packaged application uses Tauri 2 only as a thin supervisor for bundled runtime +processes, chooses an ephemeral loopback port, passes launch secrets in memory +where possible, monitors health, and tears down listeners and CDP connections. It +does not contain Git product behavior or reimplement Repository Engine behavior in +Rust. + +### Local loopback process + +The loopback process is the authority for Repository identity, filesystem access, +Git processes, current snapshots, operation admission, native actions, redaction, +and recovery. It owns all mutable backend session state, including Commit Drafts +and duplicate-operation records. + +### Sandboxed browser renderer + +The renderer owns presentation, selection, filters, accessible interaction, and a +single Repository Store. It treats server snapshots as authoritative and submits +typed intent with the opaque targets and revisions it observed. It has no Node.js +filesystem or child-process access. + +## Official capability versus unsupported integration + +The MVP assumes ordinary documented operating-system and Git capabilities: + +- a local macOS process may bind an ephemeral loopback listener; +- the system Git executable supplies Repository, Worktree, refs, hooks, signing, + and configured credential behavior; +- documented macOS mechanisms may open Terminal/Finder/default applications for + validated local targets; +- the standalone browser surface can load the same built application and protocol. + +The MVP does **not** assume an official Codex Desktop plugin or extension interface +for adding a sidebar destination, embedding a page, reading Current Project/task +context, or navigating to a task/file. Those behaviors belong exclusively to the +unsupported `CodexCdpHostAdapter`, use compatibility probes against explicitly +recorded Codex builds, and are never required for Repository correctness. + +The adapter must not modify Codex application bundles, files, or private JavaScript +state. If its probe fails, it must leave the native UI untouched and return a +typed fallback result that launches or points to the standalone surface. + +## Module seams and interfaces + +The following TypeScript is conceptual public shape. Runtime schemas at the +protocol seam remain authoritative for untrusted input. + +### Host Adapter + +The Host Adapter is a real seam because two adapters vary: standalone and Codex +CDP/DOM. The interface hides discovery, mounting, remount, cleanup, theme/context +transport, host navigation, and compatibility behavior. + +```ts +interface HostAdapter { + attach(request: HostAttachRequest): Promise; +} + +type HostAttachResult = + | { kind: 'attached'; connection: HostConnection } + | { kind: 'standalone-required'; reason: SanitizedDiagnostic }; + +interface HostConnection { + currentContext(): HostContext; + contexts(): AsyncIterable; + perform(action: HostAction): Promise; + close(): Promise; +} +``` + +The interface contains named product actions, never arbitrary URLs, JavaScript, +DOM selectors, filesystem paths, or CDP commands. A connection has one generation; +messages from an old renderer/frame generation are rejected. + +### Repository Engine + +The Repository Engine is the deepest module. Removing it would spread canonical +identity, Git process recipes, parsing, freshness validation, coordination, +outcomes, and redaction across the server and UI. + +```ts +interface RepositoryEngine { + open(anchor: ProjectAnchor): Promise; +} + +interface RepositorySession { + snapshot(): Promise; + subscribe(): AsyncIterable; + diff(request: DiffRequest): Promise; + searchBranches(request: BranchSearch): Promise; + updateDraft(request: DraftUpdate): Promise; + dispatch(command: ProductCommand): Promise; + recover(operationId: OperationId): Promise; + perform(action: NativeAction): Promise; + close(): Promise; +} +``` + +Callers do not learn executable paths, Git argv, lock paths, repository layout, +watcher details, retry rules, or credential handling. Tests exercise the same +session interface as production callers against real temporary Git repositories. + +### Versioned protocol + +The protocol module is the only browser-to-local seam. It owns runtime schemas, +protocol negotiation, authentication, size limits, structured errors, and mapping +between transport payloads and Repository Session calls. It does not duplicate +Repository Engine policy. + +### Repository Store + +The UI uses one external Repository Store consumed through +`useSyncExternalStore`. The store owns the latest snapshot, connection status, +selected Worktree/file IDs, filters, and operation progress. It never invents Git +state optimistically; an operation receipt may show progress but only a new +snapshot changes Git facts. + +### Internal seams + +The Repository Engine may use private parser, process-runner, filesystem-observer, +clock, and scheduler interfaces for deterministic tests. These are internal seams, +not public packages or protocol concepts. A second production adapter is required +before promoting any internal seam to a public interface. + +## Identity and snapshot model + +### Canonical identity + +Repository identity derives from the canonical common Git directory resolved by +Git, not from the Current Project path. Worktree identity derives from canonical +Git registration plus a generation nonce/evidence record maintained for the open +session. + +Opaque `RepositoryId`, `WorktreeId`, `FileId`, `RefId`, `RemoteId`, and +`OperationId` values are random or keyed identifiers with no client-constructible +path/ref meaning. Server maps bind them to one session and relevant generation. + +Path reuse never revives identity. If a Worktree registration disappears, moves, +is recreated, or loses continuous identity evidence, old Worktree and descendant +IDs expire. + +### Revision axes + +```ts +interface RepositorySnapshot { + repositoryId: RepositoryId; + repositoryRevision: number; + topologyRevision: number; + refsRevision: number; + refresh: RefreshState; + worktrees: readonly WorktreeSnapshot[]; + operations: readonly OperationSummary[]; +} + +declare const worktreeGenerationBrand: unique symbol; +type WorktreeGeneration = string & { + readonly [worktreeGenerationBrand]: true; +}; + +interface WorktreeSnapshot { + worktreeId: WorktreeId; + worktreeRevision: number; + generation: WorktreeGeneration; + head: HeadState; + indexTree: ObjectId | null; + status: WorktreeStatus; +} +``` + +- `repositoryRevision` changes when any externally visible snapshot fact changes. +- `topologyRevision` changes when Repository/Worktree registration or availability + changes. +- `refsRevision` changes when shared Local or Remote-tracking refs, Remotes, or + Upstream configuration changes. +- `worktreeRevision` changes only for facts owned by one Worktree, including its + HEAD/Index/status observation. + +A snapshot response is coherent for its declared revisions. The coordinator may +refresh selected and non-selected Worktrees at different times, but it publishes a +new immutable snapshot atomically and marks any retained observation with its own +freshness. + +### Target bindings + +A `FileId` binds Worktree generation, Worktree revision, path bytes, status kind, +and Diff Baseline. A `RefId` binds full ref name, target object ID, refs revision, +and relevant occupancy. A native target binds its Worktree generation and exact +canonical path rules. + +IDs communicate identity, not authorization. Dispatch still re-resolves the map +entry and verifies its matching axes immediately before execution. + +## Repository and Worktree discovery + +1. Ask system Git to resolve the anchor to the canonical common Git directory. +2. If Git reports a non-repository, return the typed non-repository result without + attempting mutation or walking parent directories independently. +3. Run `git worktree list --porcelain -z` against that Repository as the sole + inventory authority. +4. Parse NUL-delimited records without assuming `.git/worktrees` layout. +5. Canonicalize paths without losing the original display path or unusual bytes. +6. Classify Main, linked, locked reason, prunable/missing, Branch, and detached + Commit facts. +7. Compare with the prior topology map to retain or replace Worktree generations. +8. Publish unavailable diagnostics without repair, prune, or lock removal. + +No directory scan, `.codex` folder, Branch prefix, task title, or provenance record +may add or remove a Worktree. The optional provenance adapter joins stable metadata +onto an already-complete Git inventory. + +## Git execution contract + +### Authority and command construction + +Only a private Git process runner inside the Repository Engine may select the +system Git executable and construct arguments. Each Product Command maps to one +allow-listed recipe with literal argv, an explicit working directory or Git +directory, a sanitized environment policy, bounded stdin/stdout/stderr, a timeout, +and cancellation/reconciliation behavior. + +The runner never invokes a shell. Client input never becomes an executable, +option, refspec, configuration override, environment variable, or absolute path. +Path sets use Git's NUL-delimited path input capabilities and an explicit +end-of-options contract. Commit messages use stdin or a private file descriptor. + +### Read policy + +- Worktree inventory uses porcelain NUL output. +- status uses `--porcelain=v2 -z --branch --untracked-files=all`. +- refs and Branch search use full ref names and object IDs from machine-readable + output; tags and symbolic Remote HEAD aliases are filtered server-side. +- diffs identify explicit baselines and disable external diff/text conversion. +- reads have output limits and return an explicit too-large/unsupported result + rather than truncating content into a plausible false state. +- local Refresh never runs a network-capable Git command. + +### Mutation policy + +- Stage and Unstage accept only resolved File IDs and affect the selected Index. +- Commit preserves hooks and configured signing and does not synthesize identity. +- Branch switching names an exact full ref and uses no force, stash, or carry + behavior. +- Fetch names exact configured Remotes and does not prune by default. +- Pull targets the exact displayed Upstream and enforces fast-forward-only. +- Push targets the current Local Branch's exact Upstream and never includes force, + matching refs, tags, or deletion. +- Publish names a confirmed Remote and same-name target and sets Upstream only + after verified transfer. + +Implementation tests assert exact argv and environment for every recipe, including +paths beginning with `-`, whitespace, Unicode, and newlines. The package supervisor +cannot add alternate recipes. + +### Git-native behavior + +Hooks, signing, credential helpers, SSH, server policy, filesystem permissions, +and Git locks remain active. The Engine classifies their observable failures and +redacts diagnostics; it does not bypass, configure, or remove them. + +## Refresh architecture + +### Triggers + +- Repository Session open +- browser focus or visibility return +- Current Project change +- selected Worktree filesystem or Index invalidation +- common Git directory, refs, configuration, or Worktree registration invalidation +- manual Refresh +- every operation terminal or uncertain outcome +- staggered non-selected Worktree poll +- full Repository discovery fallback poll + +### Selected-first pipeline + +The coordinator resolves topology and shared refs as needed, then prioritizes the +selected Worktree's HEAD, Index, and status. Non-selected Worktrees refresh with +bounded concurrency and staggered polling. Equivalent pending reads are +deduplicated. + +Every refresh run owns a generation. Results can be merged only when their source +generation and prerequisite topology/refs axes remain current. Late old results +are discarded rather than allowed to overwrite a newer snapshot. + +Filesystem events are invalidation hints, not Git truth. Debouncing may combine +events; only Git reads produce a snapshot. Watcher loss or unsupported paths fall +back to polling. + +### Failure and stale data + +The last successful snapshot remains visible after a read failure with explicit +error, freshness, and stale state. A failed read never replaces known status with +Clean, an empty Change Group, no Branch, or no Worktree. + +SSE transmits revision/progress invalidations, not authoritative incremental Git +patches. On invalidation or stream recovery, the UI requests a coherent snapshot. + +## Operation coordination + +### Lanes + +| Lane | Scope | Primary operations | Cardinality | +| ---------------- | ------------------- | ---------------------------- | ------------------ | +| Local mutation | Worktree generation | Stage, Unstage, Commit | One per Worktree | +| Branch switch | Repository | Local/Remote-tracking switch | One per Repository | +| Remote operation | Repository | Fetch, Pull, Push, Publish | One per Repository | + +Admission is atomic and never waits in a user-visible mutation queue. If a required +lane or conflicting state claim is held, dispatch returns Busy with the active +operation summary. + +Lanes are the coarse coordination rule; state claims close cross-lane races. A +Branch switch claims its target Worktree HEAD/Index and Repository occupancy axes. +Pull claims its target Worktree plus Upstream/ref axes. Push/Publish claim the +source Branch OID and destination mapping. Commit claims its Worktree HEAD/Index +and exact Local Branch reference when attached. Admission rejects an overlap whose +effects could change another operation's verified target. + +Independent local mutations in different Worktrees remain concurrent when their +exact refs and state claims do not conflict. Read-only Refresh continues with +bounded concurrency, but mutation postconditions publish only through +reconciliation. + +### Precondition axes + +Every Product Command declares the axes it observed and the Engine must refresh: + +- Repository identity +- Worktree ID and generation +- topology/availability +- Worktree revision and status kind +- HEAD object ID and Local Branch attachment +- Index tree object ID +- file path bytes and Diff Baseline +- refs revision and exact target object ID +- Branch Occupancy +- Remote identity and URL/config generation +- Upstream source and destination full refs +- operation-lane availability + +The Engine checks only relevant axes so an unrelated Worktree mutation does not +invalidate independent evidence. Producers and consumers share the same canonical +comparison functions; no producer may issue an ID for a value its consumer would +reject. + +### Operation lifecycle + +```mermaid +stateDiagram-v2 + [*] --> Submitted + Submitted --> ProtocolRejected: schema, token, origin, or version invalid + ProtocolRejected --> [*] + Submitted --> RejectedPendingReconciliation: stale, Busy, or failed precondition + RejectedPendingReconciliation --> Reconciling: no mutation begins + Submitted --> Running: fresh preconditions and admitted + Running --> Reconciling: exit, cancel, timeout, or interruption + Reconciling --> Rejected: no mutation began and current state is known + Reconciling --> Succeeded: requested effect verified + Reconciling --> FailedKnown: requested effect absent and state known + Reconciling --> PartialSuccess: independent effects differ + Reconciling --> UnknownOutcome: state cannot yet prove result + UnknownOutcome --> Reconciling: recovery request or new evidence + Succeeded --> [*] + Rejected --> [*] + FailedKnown --> [*] + PartialSuccess --> [*] +``` + +Protocol rejection is not a Product Command outcome: malformed or unauthorized +transport input cannot reach the Repository Session. Once a valid Product Command +does reach dispatch, stale, Busy, and other failed preconditions produce a +Rejected outcome only after relevant state axes are freshly observed and +published. No Git mutation begins on that path. + +Each accepted command has a client command ID, a fingerprint of its fully +validated intent, and a server `OperationId`. An exact idempotent retry with the +same ID and fingerprint returns the prior receipt/result and never starts a second +process. Reusing an ID with a different fingerprint is a command-ID collision: the +protocol rejects it without dispatch. Cancellation requests intent but cannot +assert rollback or termination before the process and Git state are observed. + +Every attempted mutation reconciles the axes it could affect. Unknown Outcome +retains recovery metadata and disables blind retry until later reconciliation can +classify the state or route the user to safe manual inspection. + +## Change and diff architecture + +The status parser produces independent observations, not one mutable record per +path. An ordinary entry may yield a Staged Change, a Change, or both. Conflict and +Untracked records preserve their own semantics. Rename observations retain both +old and new path bytes. + +On-demand diff takes a `FileId`, validates its Worktree generation and baseline, +then reads exactly one of: + +- HEAD to Index for Staged Change +- Index to Working Tree for Change +- empty input to Working Tree for Untracked File +- explicit conflict metadata/content for Conflict + +Before text rendering, the Engine detects binary content, decoding failure, bytes, +and line count. It returns a tagged metadata result for binary, undecodable, +over-2-MiB, or over-20,000-line content. Process output limits are stricter than +memory/UI limits so an external process cannot exhaust the server before +classification. + +Diff results contain display content and opaque navigation targets but no +client-authoritative absolute path. UI Previous/Next order derives from the current +snapshot's selected Worktree/group/filter, not from server mutation state. + +## Loopback protocol and security + +### Instance security + +- Bind `127.0.0.1` only and ask the operating system for an ephemeral port. +- Generate at least 256 bits of randomness per launch. +- Put the launch token in an unguessable URL path rather than logs, query strings, + or ordinary error messages. +- Validate token, protocol version, method, content type, body size, runtime schema, + command-ID/fingerprint consistency, and expected origin before calling a product + module. +- Permit the opaque Codex iframe's `Origin: null` only after token and protocol + validation; reject unexpected browser origins and non-loopback peers. +- Expire the token and all opaque IDs when the server instance ends. + +### Endpoint families + +| Family | Purpose | Authority returned/accepted | +| ------------- | ----------------------------------- | --------------------------------------- | +| Session | protocol/capability negotiation | session metadata only | +| Snapshot | coherent Repository state | opaque IDs and revisions | +| Diff | one validated on-demand review | display content/metadata only | +| Branch search | cached exact ref candidates | opaque Ref IDs | +| Draft | get/update/clear one Worktree draft | text with size limit | +| Command | submit typed product mutation | operation receipt | +| Operation | progress/result/recovery | typed outcome and sanitized diagnostics | +| Native action | perform an allow-listed action | typed result | +| Events | revision/progress invalidation | no authoritative Git patches | + +Requests cannot contain executable names, Git argument arrays, refspecs, arbitrary +absolute paths, arbitrary URLs, environment maps, or generic native commands. + +### Host message security + +The Codex adapter mounts an opaque sandboxed iframe without `allow-same-origin`, +popup, or top-navigation privileges. Every iframe/host connection uses a fresh +capability and challenge bound to its frame generation. The adapter accepts only +allow-listed tagged messages, validates their source window and generation, and +rejects stale or replayed messages. + +### Redaction + +All process diagnostics cross one redaction module before logging, protocol +serialization, UI display, or test artifact archival. Redaction covers URL +userinfo, HTTP authorization, tokens, credential helper material, launch secrets, +known test secrets, and common key/value secret shapes. + +Structured product fields prefer sanitized Remote host, exact operation category, +and stable error code over raw command lines. Normal logs never record launch +tokens, complete Remote URLs with credentials, Commit message contents, file +contents, or environment dumps. + +## Native actions and provenance + +The server issues native target IDs from current Repository state. At execution it +re-resolves Worktree generation, canonical path, rename/delete rule, and existence, +then maps a tagged action to one allow-listed macOS or host operation. + +The protocol never accepts an arbitrary path or URL to open. Copy actions return +validated display text; Open/Reveal actions receive exact local targets directly +from the resolver. + +The provenance adapter is an optional read-only join. It may associate exact +canonical Worktree `cwd` values with stable Codex-owned task/lifecycle metadata. +Missing, conflicting, inferred, or name/path-derived evidence yields +`Unclassified`. Removing the adapter leaves discovery, snapshots, Git operations, +and standalone navigation functional. + +## Host lifecycle + +### Codex adapter attach + +1. Connect only to an explicitly selected/owned compatible Codex renderer. +2. Run a read-only compatibility probe. +3. If compatible, add exactly one top-level `Git` entry and mount one opaque frame + when selected. +4. Restore native content when another destination is selected. +5. Detect compatible renderer/DOM replacement and reattach idempotently with a new + frame generation. +6. On close or failure, remove listeners, mounted nodes, CDP sessions, and + capabilities. + +No probe failure may partially mutate the native UI. Runtime diagnostics name the +tested Codex version and fallback without presenting the adapter as an official +extension. + +The packaged supported path prefers a dedicated Codex profile/instance that it +owns, reducing the chance that renderer selection or teardown affects an unrelated +user-owned Codex window. + +### Standalone adapter + +The standalone adapter provides the same `HostConnection` shape with locally +available theme/context/navigation capabilities. Unsupported host actions return a +typed unavailable result; they do not disable Git workflows. + +## Failure containment and shutdown + +- Closing a Repository Session cancels observers, stops admission, requests + process cancellation, performs bounded reconciliation where possible, and + releases only locks/resources owned by Codex Git. +- Server shutdown closes SSE clients and loopback listeners and invalidates all + tokens/IDs. +- Renderer failure does not terminate an in-flight Git process without entering + recovery; the server retains operation state for reconnect during the process + lifetime. +- Package exit tears down child processes and Codex CDP connections. Relaunch + performs fresh discovery rather than trusting persisted snapshots. +- No recovery routine removes Git locks, deletes Worktrees, changes Git config, or + rewrites refs. + +## Testing strategy + +### Protocol contract tests + +- Runtime schema and TypeScript shape agreement +- version negotiation and structured errors +- malformed/oversized bodies, unexpected origin, stale token, and duplicate command +- opaque-target and native-action allow-list enforcement +- SSE invalidation semantics and reconnect +- redaction snapshots containing representative credential material + +### Repository Engine integration tests + +Use real temporary Git repositories and the system Git executable for: + +- Main, linked, detached, locked, missing, prunable, custom-root, and unusual-path + Worktree discovery +- porcelain-v2 status and diff matrices including dual-state paths, rename, + deletion, Conflict, Untracked, binary, encoding, and size limits +- Initial/Local/detached Commit, hooks, signing stubs, identity, and Index locks +- Branch Occupancy and Remote-tracking collision matrices +- local bare Remotes for Fetch/Pull/Push/Publish, divergence, rejection, and Partial + Success +- exact Working Tree/Index byte preservation assertions +- external mutation, delayed read, interruption, timeout, and disappearing-target + races + +Tests verify product results and exact allowed Git argv/environment. They do not +mock Git semantics at the public Repository Session interface. + +### Deterministic coordination tests + +Inject private scheduler, clock, process, and observer adapters to prove: + +- bounded/deduplicated reads and selected-first refresh +- late-generation result rejection +- independent Worktree concurrency +- Busy admission for conflicting lane/state claims +- cancellation and every reconciliation outcome +- no retry during Unknown Outcome + +### UI and accessibility tests + +- one, many, stale, and unavailable Worktree snapshots +- stable sorting, search, selection retention/invalidation, and adaptive layout +- truthful group/diff rendering and navigation +- target-specific accessible names, keyboard operation, visible focus, live status, + non-color state, and focus recovery +- no optimistic Git state after command submission + +### Host Adapter tests + +Both adapters run the same contract suite. Codex DOM fixtures cover probe, mount, +native navigation, renderer replacement, duplicate prevention, stale generation, +challenge validation, cleanup, and fail-closed fallback. Manual smoke evidence +records exact supported Codex Desktop versions. + +### Release tests + +The release gate maps AC-01 through AC-24 to evidence and records reference +hardware/software. It includes the 25-Worktree/2,000-Changed-File/5,000-ref fixture, +timing thresholds, assistive technology, multi-process races, security threats, +listener/process teardown, and standalone/Codex parity. The signed package reruns +the same gate rather than using a separate behavior suite. + +## Implementation phases and dependencies + +```mermaid +flowchart TD + I2["#2 Product and architecture docs"] --> I3["#3 Workspace bootstrap"] + I3 --> I4["#4 Host Adapters"] + I3 --> I5["#5 Protocol and security"] + I3 --> I6["#6 Worktree discovery"] + I5 --> I6 + I5 --> I7["#7 Refresh and coordination"] + I6 --> I7 + I4 --> I8["#8 Overview UI"] + I5 --> I8 + I6 --> I8 + I7 --> I8 + I6 --> I9["#9 Change review"] + I7 --> I9 + I8 --> I9 + I7 --> I10["#10 Stage and Unstage"] + I9 --> I10 + I7 --> I11["#11 Commit"] + I10 --> I11 + I6 --> I12["#12 Branch switching"] + I7 --> I12 + I8 --> I12 + I6 --> I13["#13 Fetch"] + I7 --> I13 + I8 --> I13 + I7 --> I14["#14 Pull, Push, Publish"] + I13 --> I14 + I4 --> I15["#15 Navigation and provenance"] + I6 --> I15 + I8 --> I15 + I9 --> I15 + I4 --> I16["#16 Release gate"] + I5 --> I16 + I6 --> I16 + I7 --> I16 + I8 --> I16 + I9 --> I16 + I10 --> I16 + I11 --> I16 + I12 --> I16 + I13 --> I16 + I14 --> I16 + I15 --> I16 + I4 --> I17["#17 macOS package"] + I5 --> I17 + I16 --> I17 +``` + +### Phase 0 — Decisions and workspace + +- #2 records product, domain, architecture, and ADR authority. +- #3 completes the runnable workspace without Git product behavior. + +### Phase 1 — Risk boundaries + +After #3, #4 proves both Host Adapters while #5 builds the protocol/security seam. +Neither contains Git product behavior. + +### Phase 2 — Read-only vertical slice + +#6 establishes Repository/Worktree identity; #7 builds coherent refresh and +coordination; #8 presents snapshots; #9 adds truthful status/diff review. + +### Phase 3 — Mutation lanes + +- Local Index/Commit: #10 then #11 +- Branch switching: #12 +- Remote operations: #13 then #14 +- Host navigation/provenance: #15 + +Each issue lands only after its consumed dependencies and remains independently +testable and revertible. + +### Phase 4 — Release and package + +#16 proves all AC-01 through AC-24 in source mode. #17 adds a thin signed/notarized +macOS supervisor and repeats the gate against the package without duplicating the +Repository Engine. + +## Architectural definition of done + +- UI modules contain no Node filesystem/process imports or Git argument assembly. +- All untrusted transport values pass runtime validation before product modules. +- Every mutation recipe has exact target, precondition, lane/state-claim, argv, + redaction, outcome, and reconciliation tests. +- Worktree discovery contains no provenance or path convention dependency. +- Removing the Codex adapter leaves a complete standalone Git product path. +- Removing the package supervisor leaves a complete source-mode runtime. +- Every AC-01 through AC-24 row maps to automated or explicit manual release + evidence. diff --git a/docs/product/mvp-prd.md b/docs/product/mvp-prd.md new file mode 100644 index 0000000..10a4a2d --- /dev/null +++ b/docs/product/mvp-prd.md @@ -0,0 +1,645 @@ +# Codex Git macOS MVP product requirements + +## Status and authority + +This document defines the release-blocking macOS MVP tracked by +[issue #1](https://github.com/codeacme17/codex-git/issues/1). The canonical +domain terms are defined in [`CONTEXT.md`](../../CONTEXT.md). The +[technical architecture](../architecture/mvp-technical-architecture.md) defines +how the product preserves these requirements; it does not add product behavior. + +Every requirement in this document applies equally to a Repository with one +Worktree and to a Repository containing manual, Permanent, Codex-managed, +Scheduled, detached, custom-root, or Unclassified Worktrees. + +## Product objective + +Deliver a top-level `Git` page for the Current Project that shows the Repository +and every registered Worktree, then supports the ordinary review, Stage, Commit, +Branch switch, Fetch, fast-forward Pull, normal Push, and Publish Branch loop. + +The product must make the selected Worktree and exact Git target obvious, remain +truthful while external Git processes and Codex tasks change the Repository, and +refuse mutations when fresh evidence cannot prove that the requested target and +preconditions still hold. + +## Product principles + +1. **All registered Worktrees are first-class.** Provenance, location, Branch + naming, and creator never filter the Git inventory or capabilities. +2. **The selected Worktree is the unit of local work.** Its Working Tree, Index, + HEAD, Commit Draft, Changed Files, and local mutations are never blended with + another Worktree. +3. **Observed state is not authority.** Displayed state may become stale; every + mutation requires fresh server-side target and precondition checks. +4. **Outcomes describe evidence.** The product reports what reconciliation can + prove, including Partial Success and Unknown Outcome, without implying rollback + or success. +5. **Ordinary Git stays ordinary.** Existing Git hooks, signing, credentials, + configuration, locks, and safety rules remain in force. +6. **Host integration is optional.** Codex-hosted and standalone surfaces expose + the same Git behavior; loss of unsupported host compatibility cannot remove the + standalone path. + +## Users + +### Single-Worktree developer + +A developer using the Main Worktree expects the page to open directly into useful +Repository and Worktree state without configuring a multi-Worktree workflow. + +### Multi-Worktree developer + +A developer coordinating multiple Worktrees expects each registered Worktree to +appear exactly once, keep an independent Index and Commit Draft, and identify +Branch Occupancy and mutation targets unambiguously. + +### Codex task coordinator + +A developer may benefit from a proven association between a Worktree and a Codex +task, but missing or conflicting Codex metadata must not hide or disable Git +behavior. + +## MVP scope + +- One top-level Codex sidebar `Git` entry when the supported host adapter is + compatible. +- One standalone surface with the same Git product behavior. +- One adaptive, full-page Repository/Worktree master-detail experience. +- The Current Project's local macOS Repository only. +- The Main Worktree and every valid Worktree registered with that Repository. +- Optional provenance labels only when stable Codex-owned metadata proves them. +- Repository and Worktree summaries, truthful Change Groups, and on-demand file + diffs. +- File-level and selected-group Stage and Unstage. +- Worktree-scoped Commit Drafts and Commit. +- Switching to existing Local Branches and the narrow same-name local tracking + Branch case for a cached Remote-tracking Branch. +- Explicit Fetch, fast-forward-only Pull, normal Push, and Publish Branch. +- Automatic and manual local Refresh, stale-state rejection, operation lanes, + typed outcomes, post-operation reconciliation, and safe supporting navigation. +- A release fixture with 25 active Worktrees, 2,000 Changed Files, and 5,000 Local + and Remote-tracking references. + +## Non-goals + +- Windows, Linux, remote Git execution environments, or multiple simultaneous + Current Projects. +- Worktree or general Branch create, delete, rename, move, repair, or prune + workflows. +- Hunk or line staging; discard; stash; reset; clean; merge; rebase; cherry-pick; + conflict resolution; graph; blame; history; or generalized undo. +- Force push, tags, Remote Branch deletion, arbitrary refspecs, pull requests, + checks, GitHub APIs, or provider-specific repository features. +- Credential setup, submodule workflows, or Git LFS management. +- Modifying the Codex task list, Codex application files, or undocumented private + Codex application state. +- Guaranteed syntax highlighting, a merge editor, auto-update, telemetry, cloud + accounts, or a remote backend. + +## Information architecture + +### Repository header + +The header shows the Repository name and path, active and unavailable Worktree +counts, local Refresh freshness, Fetch freshness, active operation status, a +manual Refresh action, and explicit Fetch entry points. + +Local Refresh and Fetch are visually and semantically distinct. Refresh never +contacts a Remote. Ahead/behind information is labeled as cached and tied to the +latest successful Fetch. + +### Worktree navigator + +The navigator is stably ordered with the Main Worktree first. Each row identifies +the Worktree by a disambiguated name/path, Local Branch or Detached HEAD Commit, +Clean/change/conflict counts, cached ahead/behind or Unpublished state, and any +unavailable or transitioning status. + +Search matches Worktree name, path, Branch, and proven associated Codex title. +Status changes do not reorder rows. With one Worktree, the navigator collapses +automatically without hiding Repository or Worktree actions. + +### Worktree detail + +The selected Worktree detail shows its exact identity and path, Local Branch or +Detached HEAD, Upstream and freshness, applicable Git and navigation actions, its +Commit Draft, ordered Change Groups, and the selected file diff. + +Selection survives a harmless Refresh while its opaque identity remains valid. +Worktree generation or Branch changes clear stale file and diff selections. + +### Change Groups and diff + +Non-empty groups appear in this order: + +1. Conflicts +2. Staged Changes +3. Changes +4. Untracked + +A path with both staged and unstaged content appears in two groups because it has +two independent Diff Baselines. Diff review defaults to side-by-side, offers a +Unified toggle, and supports Previous/Next navigation with an `N of M` position +within the current Worktree group and filter. + +Binary, undecodable, oversized, or excessively long content receives truthful +metadata and safe open actions rather than a fake text diff. The degradation +threshold is more than 2 MiB or more than 20,000 lines. + +## Functional requirements + +### FR1 — Repository and Worktree discovery + +- Resolve the Current Project to one canonical Repository or a safe + non-repository result. +- Treat Git's registered Worktree inventory as authoritative. +- Include the Main Worktree and every valid Linked Worktree exactly once, + independent of location, Branch name, creator, provenance, or Detached HEAD. +- Distinguish an available Worktree, a Git-locked Worktree, and a missing or + prunable registration without mutating or repairing any of them. +- Treat a removed, restored, recreated, or moved Worktree as a new generation + when its continuous identity cannot be proven. + +### FR2 — Repository and Worktree overview + +- Present the adaptive Repository header, stable Worktree navigator, and selected + Worktree detail defined above. +- Keep every Worktree's status, Index, Commit Draft, operation state, and actions + independent. +- Communicate Clean, changed, conflicted, unavailable, stale, and transitioning + state with text or icons as well as color. +- Preserve only selections that remain valid in the latest authoritative + snapshot. +- Show optional provenance only when stable metadata explicitly proves it; + otherwise show `Unclassified`. + +### FR3 — Change classification and diff review + +- Classify Conflict, Staged Change, Change, and Untracked File observations + without collapsing distinct baselines for the same path. +- Review Staged Changes from HEAD to Index, Changes from Index to Working Tree, + and Untracked Files from empty content to the file. +- Represent Conflict content truthfully without implying an MVP conflict editor. +- Preserve rename old/new paths and keep deletions reviewable. +- Bind every Changed File observation to one Worktree generation, revision, path, + and Diff Baseline. +- Disable external diff drivers and text conversion, bound output, and degrade + safely for binary, undecodable, oversized, or excessively long content. + +### FR4 — Stage and Unstage + +- Provide file actions, `Stage all` for Changes and Untracked Files, and + `Unstage all` for Staged Changes. +- Reject Stage for Conflict entries. +- Change only the selected Worktree's Index and never infer a path from client + text. +- Revalidate file identity, current status, and the expected Diff Baseline before + execution. +- Make Unstage safe before the Initial Commit and preserve Working Tree bytes. +- Report bulk results per path as Succeeded, Failed Known, or Partial Success; + never claim transactional rollback. + +### FR5 — Commit + +- Maintain one multiline Commit Draft per Repository and Worktree for the current + backend session. +- Preserve a draft across navigation, Refresh, Branch switch, and failed or + uncertain Commit; clear it only after verified success or an explicit user + clear action. +- Enable Commit only when staged content exists and show the target Worktree path, + Branch or Detached HEAD, and staged-file count. +- Support an Initial Commit and require prominent confirmation for a Detached HEAD + Commit. +- Block ordinary Commit during Conflict, an In-progress Git Operation, missing + identity, an unresolved Index lock, or an empty Index. +- Preserve configured hooks and signing, pass the Commit message without shell + interpolation, and reconcile HEAD and Index after every attempt. +- Include exactly the selected Worktree's staged content and leave its unstaged + content unchanged. + +### FR6 — Branch discovery and switching + +- Search cached Local Branches separately from cached Remote-tracking Branches; + exclude tags and Remote symbolic HEAD aliases. +- Show Remote-qualified names and use exact, opaque Branch targets. +- Compute Branch Occupancy across every registered Worktree and identify the + occupying Worktree. +- Allow switching only when the selected Worktree is Clean, has no Conflict, and + has no In-progress Git Operation. +- Allow a Clean Detached HEAD to switch, warning before leaving a Commit not + reachable from another named reference. +- Disable an occupied Local Branch and offer navigation to its Worktree. +- For a Remote-tracking Branch, permit only creation of a same-name Local tracking + Branch after target, name, collision, Upstream, and occupancy checks all pass. +- Never Fetch implicitly or carry, stash, discard, or overwrite local changes. + +### FR7 — Remotes, Fetch, Pull, Push, and Publish + +- Discover configured Remotes and show each name with a sanitized host. +- Resolve Local Branch Upstreams and cached ahead/behind or Unpublished state with + the last successful Fetch time. +- Provide explicit `Fetch ` and `Fetch all`; Fetch all reports each Remote + independently and preserves successful updates. +- Do not prune by default, modify Working Tree files during Fetch, or contact a + Remote during local Refresh or Branch search. +- Pull only from the displayed Upstream, only into a Clean Worktree, and only with + explicit fast-forward-only integration. Ahead is a no-op; divergence is blocked. +- Push only the current Local Branch to its exact configured Upstream. Uncommitted + content may remain present but is explicitly excluded from the Push. +- Block a known behind or diverged Push and never retry with force. +- Publish only an Unpublished Branch to a confirmed Remote and same-name target; + set Upstream only after verified success. +- Use existing credential helpers and SSH while distinguishing offline, + authentication, permission, policy, non-fast-forward, Partial Success, and + Unknown Outcome results. + +### FR8 — Refresh, coordination, and recovery + +- Produce coherent versioned Repository and Worktree snapshots and preserve the + last successful snapshot with explicit stale/error state when Refresh fails. +- Refresh on open, focus, Current Project change, relevant filesystem or Git + metadata invalidation, manual request, and every operation outcome. +- Observe the selected Worktree first; debounce, bound, and deduplicate reads; use + staggered non-selected polling and full-discovery fallback polling. +- Discard late results from superseded Refresh generations. +- Permit independent local mutations in different Worktrees while serializing + each Worktree's local mutations, all Repository Branch switches, and all + Repository remote operations in their respective lanes. +- Return Busy rather than silently queueing a conflicting mutation. +- Track operation identity, cancellation, duplicate submission, typed outcomes, + and reconciliation. Disable retry while an outcome remains unknown. +- Never remove external Git locks or contact a Remote as part of local Refresh. + +### FR9 — Exact-target navigation and host context + +- Provide Worktree actions to Open in Terminal, Reveal in Finder, Copy Absolute + Path, Copy Branch/SHA, and open an exact Codex task/project where proven and + available. +- Provide Changed File actions to Open File in Codex, copy its relative or + absolute path, Reveal in Finder, and Open in Default App. +- Resolve targets from server-issued identity, revalidate existence and generation + immediately before launch, and allow only named product actions. +- Do not open deleted files; target the new path for a rename; permit external + opening of a Conflict without implying conflict editing. +- Cancel a missing or moved target with an explanation and safe copy/Refresh + fallback. +- Treat Codex Current Project, theme, task context, navigation, and mounting as + adapter capabilities rather than Git correctness dependencies. + +## Capability matrix + +`Yes` means the capability may be offered after all target-specific fresh +preconditions pass. `No` means the MVP must not offer it in that state. + +| Worktree state | Review | Stage/Unstage | Commit | Switch Branch | Pull | Push | Publish | +| ------------------------- | ------------------ | ------------------------ | ----------------------- | ------------- | ----------- | ----------------- | -------------- | +| Clean Local Branch | Yes | No content | No staged content | Yes | If Upstream | If Upstream | If Unpublished | +| Changed, no Conflict | Yes | Yes | If staged | No | No | If exact Upstream | If Unpublished | +| Conflict | Yes | Unstage only where valid | No | No | No | No | No | +| Detached HEAD, Clean | Yes | No content | No staged content | Yes | No | No | No | +| Detached HEAD, Changed | Yes | Yes | If staged and confirmed | No | No | No | No | +| Initial Repository State | Yes | Yes | If staged | No | No | No | No | +| In-progress Git Operation | Yes where readable | No | No | No | No | No | No | +| Unavailable | Diagnostics only | No | No | No | No | No | No | + +Additional rules override the table: + +- Stage never accepts a Conflict entry. +- Pull requires a Clean Worktree and a fast-forward result. +- A known behind/diverged Push is blocked even when an exact Upstream exists. +- An occupied Local Branch cannot be selected in another Worktree. +- Any stale, missing, ambiguous, or mismatched target disables mutation. + +## Safety contract + +### Target integrity + +- Client-visible paths, Branch names, Remote names, and displayed snapshots are + descriptive, not authority. +- Every mutation and native action identifies server-issued opaque targets. +- The local authority resolves the target and validates Worktree generation, + relevant revisions, Git state, and operation-lane availability immediately + before execution. +- A Current Project, path, Worktree, Branch, Upstream, or file mismatch produces a + Rejected outcome and refreshed state. + +### Git process safety + +- The browser does not execute a shell, construct Git arguments, choose executable + names, or submit arbitrary paths, refs, refspecs, URLs, or native actions. +- Git paths are passed literally with NUL-delimited input when supported. Commit + messages use stdin or a file descriptor. +- No MVP command force-pushes, rewrites history, deletes a ref, removes a lock, + auto-stashes, prunes by default, or bypasses hooks/signing. +- Output, execution time, request bodies, and rendered diff size are bounded. +- Secrets, credential material, URL userinfo, tokens, and authorization data are + redacted from UI, logs, errors, and diagnostic artifacts. + +### Mutation lifecycle + +1. Accept a typed intent against opaque targets and observed revisions. +2. Reject malformed, unauthorized, stale, Busy, or command-ID collision requests. +3. Resolve exact Git targets and re-read every relevant precondition. +4. Execute in the narrowest applicable operation lane. +5. On success, failure, cancellation, interruption, or timeout, reconcile the + affected HEAD, Index, refs, Upstream, status, and topology. +6. Report Succeeded, Rejected, Failed Known, Partial Success, or Unknown Outcome + from evidence. Never synthesize success from process exit alone. + +An idempotent retry repeats the same client command ID and the same validated +intent; it returns the existing receipt or result without invoking another product +operation. Reusing that ID with any different intent, target, revision, or payload +is a command-ID collision and is rejected without execution. + +## Error and recovery behavior + +- A failed local Refresh keeps the last successful data visible and marked stale; + it never renders a false Clean or empty state. +- Rejected operations explain the precondition that changed and present refreshed + state when available. +- Failed Known outcomes preserve unaffected user work and include sanitized, + actionable diagnostics. +- Partial Success identifies every independently attempted target and its result. +- Unknown Outcome disables blind retry until reconciliation proves the state or + routes the user to safe manual inspection. +- Disappearing Worktrees and navigation targets fail closed without mutation. +- External locks remain owned by the external process; the product waits, reports, + or routes to Terminal guidance but never removes them. + +## Non-functional requirements + +### Performance and capacity + +On the documented supported reference machine and release fixture: + +- The application shell appears within 1 second. +- The selected ordinary Worktree state appears within 2 seconds. +- A full supported Repository snapshot completes within 5 seconds. +- A visible external change in the selected Worktree appears within 2 seconds. +- Loaded UI interactions respond within 100 milliseconds. +- Twenty-five active Worktrees, 2,000 Changed Files, and 5,000 Local and + Remote-tracking refs remain usable. +- A binary, undecodable, over-2-MiB, or over-20,000-line file degrades without + freezing or crashing the page. + +Measurements record hardware, macOS, Git, Node.js, and Codex versions. + +### Accessibility + +- All workflows are keyboard operable with visible focus. +- Accessible names include the exact target when repeated controls would otherwise + be ambiguous. +- Status changes and operation progress use appropriate live announcements without + stealing focus. +- State never relies on color alone. +- Harmless Refresh preserves logical focus; removal or invalidation moves focus to + the nearest safe context and explains the change. +- The supported release gate includes assistive-technology checks on macOS. + +### Compatibility and availability + +- Standalone and supported Codex-hosted surfaces expose the same Git behavior. +- The Codex compatibility matrix names exact tested builds. +- An incompatible or changed Codex host fails closed and leaves native UI + unmodified before directing the user to standalone mode. +- Server restart, renderer replacement, Worktree disappearance, network outage, + and interrupted processes have explicit recovery paths. + +### Security and privacy + +- The local protocol binds only to loopback on an ephemeral port and requires a + per-launch secret plus protocol and origin validation. +- The Codex-hosted iframe remains opaque and lacks same-origin, popup, or + top-navigation authority. +- Only allow-listed native actions against validated opaque targets cross the host + seam. +- No cloud backend, account, telemetry, or credential collection is part of the + MVP. +- Installation documents the unsupported CDP/DOM integration and trusted local + process risk. + +## Release-blocking acceptance scenarios + +Each scenario must have automated evidence where feasible and an explicit manual +record only where macOS or host behavior requires it. + +### AC-01 — Resolve the Current Project + +- An ordinary non-repository directory produces a safe non-repository state and + enables no Git mutation. +- A Current Project inside the Main or a Linked Worktree resolves to the same + Repository and selects the exact Worktree when it remains registered. +- A one-Worktree Repository opens directly into useful Branch, Upstream, status, + and action state. + +### AC-02 — Present the Repository and stable Worktree navigator + +- Repository identity, path, counts, Refresh freshness, Fetch freshness, and + operation status are visible and distinguish local from Remote state. +- Main is first; remaining Worktrees stay in stable order as status changes. +- Search covers name, path, Branch, and proven Codex title without combining + Worktree state. + +### AC-03 — Include every registered Worktree exactly once + +- Main, manual, Codex-style, Scheduled-style, Permanent-style, detached, + custom-root, and Unclassified registered Worktrees all appear exactly once. +- Path conventions, Branch prefixes, task titles, and missing Codex metadata never + filter inclusion or Git capabilities. +- Provenance appears only from stable evidence; conflicting evidence is + `Unclassified`. + +### AC-04 — Degrade unavailable registrations safely + +- Git-locked, missing, and prunable registrations remain distinguishable. +- An unavailable Worktree exposes diagnostics and safe navigation/copy fallbacks + but no mutation. +- Discovery never prunes, repairs, unlocks, or deletes a registration. + +### AC-05 — Classify changes truthfully + +- Non-empty groups appear as Conflicts, Staged Changes, Changes, and Untracked. +- A path with staged and unstaged content appears twice with independent baselines + and correct content. +- Renames, deletions, unusual path bytes, and Worktree-local identity remain + accurate. + +### AC-06 — Review every supported diff kind safely + +- HEAD-to-Index, Index-to-Working-Tree, and empty-to-Untracked diffs show the + intended content. +- Conflict entries are truthful without implying resolution. +- Binary, undecodable, oversized, and over-20,000-line files return metadata and + safe actions without fake text or UI failure. +- Previous/Next remains scoped to the selected Worktree, group, and filter. + +### AC-07 — Stage and Unstage only the selected target + +- File and group operations change only the selected Worktree's Index, even when + another Worktree has the same relative path. +- Conflict entries cannot be staged. +- Spaces, Unicode, leading dashes, newlines, renames, deletions, and Untracked + Files are passed literally and handled safely. + +### AC-08 — Reject stale Index and file evidence + +- External file, status, baseline, or Index changes between review and submission + reject the stale operation and return current state. +- Unstage is Initial-Commit-safe and leaves Working Tree bytes unchanged. +- Bulk results identify each path and represent mixed outcomes as Partial Success + without rollback claims. + +### AC-09 — Commit staged content in Local, Initial, and detached states + +- A Local Branch Commit contains exactly staged content and retains unstaged + content. +- Initial Commit succeeds when identity and staged content exist. +- Detached HEAD Commit requires prominent confirmation and reports the resulting + Commit without claiming Branch reachability. + +### AC-10 — Recover Commit outcomes without losing the draft + +- Missing identity, hook rejection, signing failure, external Index lock, stale + HEAD/Index, timeout, and ambiguous process exit are distinct. +- Commit Draft survives every non-verified-success outcome. +- Verified success reports the short SHA and summary, clears only that Worktree's + draft, and refreshes HEAD, Index, and status. +- Unknown Outcome prevents duplicate retry until reconciled. + +### AC-11 — Switch only a Clean Worktree + +- Existing Local Branch and Clean Detached HEAD switches succeed when unoccupied. +- Changed, conflicted, and In-progress Worktrees are blocked without stash, + discard, carry, or force behavior. +- Leaving an unreachable Detached HEAD Commit requires a warning. + +### AC-12 — Enforce Branch Occupancy Repository-wide + +- A Local Branch checked out in another registered Worktree is disabled and names + that exact Worktree. +- Navigation reaches the occupying Worktree. +- Simultaneous switches cannot race occupancy and conflicting requests are Busy, + not queued. + +### AC-13 — Limit Remote-tracking Branch selection + +- Local and Remote-tracking results remain separate and Remote-qualified; tags and + symbolic Remote HEAD aliases do not appear. +- Selecting a Remote-tracking Branch creates only the same-name Local tracking + Branch after collision, Upstream, target, and occupancy checks pass. +- Branch discovery never Fetches or rewrites an existing Local Branch or Upstream. + +### AC-14 — Fetch without changing Worktree content + +- Fetch updates objects and Remote-tracking refs without changing any Working Tree + or Index bytes. +- Fetch all attempts each configured Remote once and reports per-Remote Partial + Success while preserving successful updates. +- Cached ahead/behind and the last successful Fetch time update truthfully; no + prune occurs by default. + +### AC-15 — Pull only by fast-forward + +- A Clean behind Branch fast-forwards from its exact displayed Upstream. +- An ahead Branch is a no-op. +- A diverged, dirty, conflicted, or In-progress Worktree changes no files or refs + and receives safe guidance without Merge, Rebase, or auto-stash. + +### AC-16 — Push only committed history to the exact Upstream + +- Push targets only the current Local Branch's exact configured Upstream. +- Uncommitted content remains local and is explicitly described as excluded. +- Known behind/diverged and server non-fast-forward results are blocked or rejected + without force, matching refs, tags, deletion, or automatic retry. + +### AC-17 — Publish an Unpublished Branch explicitly + +- Confirmation names the exact Remote and same-name target Branch. +- Upstream is configured only after verified Push success. +- Existing target, permission, policy, network, and ambiguous outcome cases do not + silently overwrite configuration or escalate to force. + +### AC-18 — Distinguish Remote and credential failures + +- Offline, authentication, permission, invalid Remote, protected-Branch/policy, + and non-fast-forward failures remain distinct. +- Existing Git credential helpers and SSH are used without collecting credentials. +- URL userinfo, tokens, secrets, and authorization material never appear in UI, + logs, errors, or archived evidence. + +### AC-19 — Coordinate independent Worktree local mutations + +- Different Worktrees may Stage, Unstage, or Commit concurrently without crossing + Index, HEAD, draft, or selected-target state. +- Two local mutations in one Worktree do not overlap; the second is Busy and not + queued. +- Existing external locks are reported and never removed. + +### AC-20 — Coordinate Repository-wide Branch and Remote operations + +- All Branch switches share one Repository lane; all Fetch/Pull/Push/Publish + operations share another Repository lane. +- Conflicting operations return Busy without silent queueing. +- An unrelated Worktree local mutation does not invalidate independent evidence + unnecessarily, while shared ref and occupancy changes invalidate every affected + target. + +### AC-21 — Reconcile every attempted mutation + +- Success, rejection, known failure, Partial Success, cancellation, interruption, + and timeout all trigger fresh observation of every affected state axis. +- Late reads cannot overwrite a newer snapshot. +- The displayed operation result agrees with reconciled HEAD, Index, refs, + Upstream, status, and topology; ambiguity is reported as Unknown Outcome. + +### AC-22 — Reject stale topology, identity, and navigation targets + +- Removing, moving, restoring, or recreating a Worktree invalidates old Worktree, + file, Branch occupancy, and navigation targets. +- External HEAD, ref, Upstream, occupancy, and registration changes reject stale + mutations before execution. +- A restored path is never assumed to be the previous Worktree generation. + +### AC-23 — Navigate to exact targets and preserve provenance optionality + +- Worktree and Changed File actions never open a same-named target in another + Worktree. +- Deleted files cannot Open File; renames target the new path; missing or moved + targets cancel with explanation and safe fallback. +- Codex task/project actions appear only when exact stable metadata supports them; + loss of that metadata changes no Git inclusion or capability. + +### AC-24 — Pass the supported release envelope + +- The 25-Worktree, 2,000-Changed-File, and 5,000-ref fixture meets all documented + timing targets without UI freeze. +- Keyboard, visible focus, target-specific names, live status, focus retention, + assistive technology, and non-color requirements pass. +- Standalone and named compatible Codex builds expose equivalent Git behavior; + compatibility failure leaves native Codex state intact and falls back safely. +- Loopback, token/origin, iframe, path/ref injection, process, race, and redaction + threat tests pass. + +## Traceability + +| Requirement | Acceptance scenarios | Delivery issues | +| ------------ | -------------------------- | --------------- | +| FR1 | AC-01, AC-03, AC-04, AC-22 | #6 | +| FR2 | AC-01–04, AC-22, AC-24 | #8, #15 | +| FR3 | AC-05, AC-06 | #9 | +| FR4 | AC-07, AC-08, AC-19, AC-21 | #10 | +| FR5 | AC-09, AC-10, AC-19, AC-21 | #11 | +| FR6 | AC-11–13, AC-20–22 | #12 | +| FR7 | AC-14–18, AC-20, AC-21 | #13, #14 | +| FR8 | AC-08, AC-10, AC-19–22 | #7 | +| FR9 | AC-03, AC-22, AC-23 | #4, #15 | +| Release gate | AC-01–24 | #16, #17 | + +## Definition of done + +The MVP is complete only when delivery issues #2 through #17 are closed, every +AC-01 through AC-24 row has passing source-mode evidence, and the same gate passes +against the signed/notarized macOS package. The standalone surface must remain +functional without the Codex Host Adapter, and installation must clearly disclose +the local CDP trust boundary and unsupported host integration.