Skip to content

Deepen Launch directory Base branch preparation around one origin sample #499

Closed

Description

@JacobStephens2

What to build

Deepen the Launch directory module so Base branch validation and an optional later checkout update share one immutable origin sample. A Run must refuse genuinely unpushed Base branch work and, with launch pulling enabled, fast-forward the Launch directory toward the commit it validated even when a tag, local branch, or sibling fetch changes what a short origin name would resolve to.

Preserve when checkout updates happen: Run preparation follows Issue branch Selection and precedes its Claim; an Architect run updates only after its skip gates, Harness check and start record; a Pickup run updates only through the Run it dispatches. Worktree acquisition continues to fetch independently.

Acceptance criteria

  • Base branch preparation returns a validated, Launch-directory-bound value with a small interface: read its name and request its optional checkout update. Its origin commit and construction stay private. Remove the public string-based pull operation and its prepare-before-pull ordering requirement.
  • Preserve Base branch precedence, the distinct detached-HEAD diagnostics for a Run and a Pass, missing-origin diagnostics, and local-ahead rejection before Claim or Worktree creation. Compare the sampled origin commit with a sampled, fully qualified local branch commit.
  • Real Git tests cover shadowing local branches and tags for both the Base branch name and its short origin name: genuinely ahead local work is rejected; an equal or behind local Base branch is accepted; a pull reaches the selected origin commit and leaves every shadow ref unchanged.
  • After preparation, advancing, rewinding, replacing or deleting the shared origin tracking ref cannot change validation's meaning or the pull target. Tests assert the actual checkout commit against the expected sampled origin commit.
  • Preparation leaves local branches, index and checkout files untouched. An up-to-date checkout is a quiet no-op; an initially different checkout, detached checkout, or checkout switched to another branch before the deferred update is left untouched.
  • Updates remain fast-forward only, with no reset or force. Dirty checkout or Git failures preserve local work and produce the existing warning behavior; ordinary Command interruption remains recorded and stops ordinary Git work.
  • Run and Spec startup use one preparation operation through their existing execution seam, including the requested pull. Preserve Continuation's open pull request Base branch choice and pulling before Claim.
  • The Pass adapter retains the validated Base branch value. Architect pulling remains after successful gates, Harness validation and the start record; skipped or rejected passes never update the checkout. Pickup still performs no direct pull. Later dispatch and Worktree acquisition may take their own origin samples.
  • Update the domain glossary's Launch directory entry to state that validation and optional pulling share one sampled origin commit, while Worktree acquisition samples independently. This documentation change is part of this Ticket.
  • Replace obsolete split validation/pull tests with tests through the prepared Base branch interface; retain the existing command-level checks for preflight rejection, Continuation precedence, warning-only updates and skipped Architect runs.
  • Formatting, linting and the repository's required test suite pass.

Blocked by

None (can start immediately).

Decisions

The design tree was settled in three rounds: first constraints and ownership, then the interface and caller integration, then sampling, checkout behavior and verification. The frontier is empty; no human decision is deferred.

Round 1 — constraints and ownership

  1. Choose this deepening as Strong. The existing module has reproducible behavior failures and exposes validation-to-pull ordering to Run and Pass callers. Owning the observation removes that knowledge from both callers, passes the deletion test, and has a complete design consistent with ADR 0002 and ADR 0009. No open issue proposes this deepening; diagnostic-advice issues touch the area without covering it.
  2. Keep selection policy at the existing callers. Issue branch Selection still lets a Continuation's open pull request choose the Base branch; otherwise the given branch wins over the Launch directory's checked-out branch. The Launch directory module validates the resulting name. This preserves the domain's existing precedence.
  3. Keep validation read-only and checkout updating deferred where necessary. Fetching may update remote-tracking refs, but preparation must not update local branches, index or checkout files. A Pass validates before its lock and gates; an Architect run pulls after its gates, Harness check and start. Combining these into an unconditional early pull would change skipped Pass behavior.
  4. Keep the change within one Ticket. One value, its two production caller adaptations, existing real Git fixtures and a glossary clarification fit one fresh context window. There is no prefactoring dependency, Spec or blocking edge.

Round 2 — interface and caller integration

  1. Use an immutable validated Base branch value, named BaseBranch. Only the Launch directory module constructs it after successful validation. It owns an ordinary Git view bound to that Launch directory, the branch name, the sampled origin commit and whether that Base branch was originally checked out. Its interface exposes only the name and a warning-only pull operation. Private facts prevent arbitrary strings or another directory from being substituted after validation; an owned Git view avoids a self-referential Launch value.
  2. Replace Run's separate preparation and pull calls with one coarse operation at its existing execution seam. Its production adapter constructs the value, requests its pull only when asked, and returns the branch name for the later Run or Spec run. Its scripted adapter records the preparation request and requested pull policy without reproducing Git rules. This removes sequencing knowledge from Run orchestration while preserving Claim timing.
  3. Retain the value in a Pass's Launch result and production adapter. Pass orchestration reads its name for prompts and dispatch; its pull operation takes no arbitrary branch argument. Architect chooses when to request that operation, as today. Pickup reads the name and dispatches without pulling. This preserves delayed execution without mutable active-preparation state.
  4. Reject directory-owned mutable preparation state and callback-driven gates. Mutable state preserves a runtime prepare-before-pull rule and can pair an old returned name with a new observation. Scoped callbacks hide the sample but move Architect's gates into Launch opening and add generic control-flow machinery. The immutable value provides better depth and locality with smaller changes to existing seams.
  5. Use the existing Base branch concept. BaseBranch is a validated value of that concept, not a new domain term. Clarify the Launch directory glossary entry as this Ticket's work. No ADR is reopened or needed: the representation is reversible and the existing Continuation and Pass decisions remain in force.

Round 3 — sampled facts and checkout behavior

  1. Sample commit IDs once, using fully qualified refs. Keep origin existence checking and the existing fetch. Immediately afterward, resolve the exact remote-tracking Base branch ref as a commit; independently resolve the exact local Base branch ref if it exists. Count local-ahead commits using those two IDs. This prevents shadow names and stops later shared-ref changes from altering the comparison.
  2. Pull the stored origin commit without fetching or resolving its tracking ref again. Use that ID for ancestry checking and the fast-forward. A sibling fetch before the sample can affect which commit is observed, but all work after the sample uses that one observation. This follows the existing Worktree sampling model; it does not promise the latest origin head at checkout time. Private temporary refs, new locks and object-retention machinery are unnecessary for this bounded change.
  3. Recheck checkout eligibility at deferred pull time. Pull only if the Base branch was checked out when prepared and the current symbolic HEAD still names that exact local branch. A changed or detached checkout gets no update. If the sampled commit is already an ancestor of current HEAD, leave it alone; otherwise use the existing fast-forward-only update. These checks protect unrelated checkouts and local commits without introducing reset behavior.
  4. Preserve warnings and interruption. Pull remains best effort: errors retain the existing progress and manual-update advice rather than failing a Run on their own. Git uses the ordinary immutable interruption policy; the existing caller's interruption check remains authoritative before Claim or review. Dropping the prepared value never pulls and requires no remote mutation or private-ref cleanup.
  5. Keep each later acquisition independent. Pass dispatch, including a Pickup's selected Continuation, and Worktree creation still prepare or fetch their own Base branch facts. A prepared Launch-directory sample is not a frozen Base branch for an entire Command. This prevents the refactor from changing Delivery, CI comparison, Repair or Failed run policy.

Testing decisions

  1. The interface is the test surface. Test preparation and the value's pull with the existing real Git fixture and a local bare origin. The production Git adapter and isolated local-origin test adapter justify this existing seam; add no new Git port or mock. Assert acceptance, rejection, branch tips and retained checkout contents, not private fields or exact Git call ordering.
  2. Exercise timing deterministically. Mutate only the fixture's shared tracking ref between preparation and pull to model a sibling fetch. Cover advance, rewind, unrelated replacement and deletion; change the checked-out branch before a deferred pull. These scenarios verify locality of the observation without sleeps or concurrent tests.
  3. Retain the highest existing command checks. Keep preflight and launch-pull command cases and Run/Architect execution-seam cases for selection, Claim timing, warnings and skipped passes. Replace tests of obsolete split operations instead of adding a second parallel suite of implementation assertions. Run normal formatting, linting and required tests once the implementation is complete.
  4. The review verified the friction on main at 8f8eb0c, equal to origin/main. Temporary probes through the actual Launch directory interface all failed as predicted: a shadowing tag admitted unpushed Base work; a shadowing local branch suppressed a pull; rewinding the shared tracking ref after validation changed the pull target. The existing 11 Launch directory tests passed alongside those three failures. Independent local Git fixtures also demonstrated false local-ahead rejection and updating to a shadow commit. The probes were removed; their behavior is specified above for the implementation's regression tests.

Architecture review

Strength: Strong · Dependencies: local-substitutable

Files: src/launch.rs, src/run.rs, src/pass.rs, src/architect.rs, src/pickup.rs, tests/preflight.rs, tests/launch_pull.rs, tests/architect_run.rs, tests/pickup_run.rs, CONTEXT.md

Before

flowchart LR
  Run["Run"] --> Validate
  Architect["Architect run"] --> Validate
  Pickup["Pickup run"] --> Validate
  subgraph Launch["Launch directory"]
    Validate["Base branch validation"]
    Pull["Checkout update"]
  end
  Validate --> Origin["Git origin refs"]
  Pull --> Origin
  Run -. validated name .-> Pull
  Architect -. validated name .-> Pull
  classDef leak stroke:#dc2626,stroke-width:2px;
  class Validate,Pull leak
Loading

After

flowchart LR
  Run["Run"] --> Launch
  Architect["Architect run"] --> Launch
  Pickup["Pickup run"] --> Launch
  subgraph Launch["Launch directory"]
    Validate["Base branch validation"] --> Prepared["Base branch"]
    Prepared --> Pull["Checkout update"]
  end
  Validate --> Origin["Git origin refs"]
  classDef deep stroke-width:3px;
  class Launch deep
Loading

Problem: The Launch directory interface leaks validation-to-pull ordering, while its shallow split operations resolve mutable origin names independently.

Solution: Deepen the module around a validated Base branch value whose implementation owns one sampled commit across the checkout-update seam.

Deletion test: Removing this ownership puts sample retention, ref qualification and update ordering back in Run and Pass callers.

Covered by: None; all 49 open issues were checked.

Wins

  • Depth: validated sample behind small interface
  • Locality: validation and pull share facts
  • Leverage: Run and Pass reuse preparation
  • Tests cross one existing seam
  • Git adapter stays behind the interface

Activity

  1. added
    needs-triageMaintainer needs to evaluate this issue
    ready-for-agentFully specified, ready for an AFK agent
    architect-planAn Architect plan: the Spec or Ticket an Architecture review published
    in-progressA Claim: thirdshift has taken this issue
    and removed
    needs-triageMaintainer needs to evaluate this issue
    ready-for-agentFully specified, ready for an AFK agent
    on Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    architect-planAn Architect plan: the Spec or Ticket an Architecture review published

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions