package projectsync // CheckoutPlan is the policy object describing where a single workspace slot // should be checked out for a project sync target. It is derived purely from a // normalized Config and a reserved WorkspaceSlot; building it never runs a git // process. Actual clone/fetch/checkout execution is out of this package's scope. type CheckoutPlan struct { // RemoteURL and SourceBranch always come from the project sync Config, never // from request input, so every slot of the same project checks out the same // remote and source-of-truth branch. RemoteURL string SourceBranch string // ProjectWorkspaceRoot is the per-project workspace root and SlotPath is this // slot's independent checkout directory under that root. ProjectWorkspaceRoot string SlotPath string SlotIndex SlotIndex } // BuildCheckoutPlan derives a CheckoutPlan from a project sync Config and a // reserved slot. The git remote and source branch are read from the Config so // they can never be spoofed by request input, and each slot resolves to its own // zero-padded checkout path (000 for the default single-operation slot, 001/002 // for parallel slots). It returns ErrInvalidConfig when the Config or slot index // is invalid. func BuildCheckoutPlan(config Config, slot WorkspaceSlot) (CheckoutPlan, error) { normalized, err := config.Normalize() if err != nil { return CheckoutPlan{}, err } if !slot.Index.Valid() { return CheckoutPlan{}, ErrInvalidConfig } root, err := ProjectWorkspaceRoot(normalized.WorkspaceBasePath, normalized.RepoDirName) if err != nil { return CheckoutPlan{}, err } slotPath, err := SlotWorkspacePath(normalized.WorkspaceBasePath, normalized.RepoDirName, slot.Index) if err != nil { return CheckoutPlan{}, err } return CheckoutPlan{ RemoteURL: normalized.GitRemoteURL, SourceBranch: normalized.SourceBranch, ProjectWorkspaceRoot: root, SlotPath: slotPath, SlotIndex: slot.Index, }, nil }