Co-authored-by: nova28 <17953305+nova28@users.noreply.github.com> Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
6.2 KiB
id, title, status, wave, depends_on, plan, requirements, acceptance_criteria, system_design
| id | title | status | wave | depends_on | plan | requirements | acceptance_criteria | system_design | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-resolution-primitives | Push-target and expected-branch primitives | completed | 1 | plan.md |
|
|
|
Task 01: Push-target and expected-branch primitives
Summary
Add the vocabulary and pure helpers the rest of the package builds on: a
PushOptions request struct, the eleven error-code constants, the five result
fields, remote enumeration, push-target resolution, strict expected-branch
validation, and a symbolic-ref current-branch read. Push and preflight take the
new options and ignore them, so this work order changes no observable behavior.
In scope
PushOptions{Force, SetUpstream bool; Remote, ExpectedBranch string}ininternal/agentctl/server/process, threaded throughGitOperator.PushandGitOperator.PushPreflightas a pass-through with behavior unchanged.- The eleven error-code constants from the system design's table.
PushedRemote,PushedBranch,ExpectedBranch,CurrentBranch, andBaselinePublishedonprocess.GitOperationResult, allomitempty.configuredRemotes, which reads each remote's effective push URL set and distinguishes a genuine read failure from an absent remote.resolvePushTarget, returning a remote name or one of the four resolution refusals, and never placing a caller-supplied URL on a Git command line.currentBranch, readinggit symbolic-ref HEADso a detachedHEADis an empty branch rather than the literalHEAD.securityutil.IsValidExpectedBranchName, the strict allowlist withoutIsValidBaseBranchRef'sorigin/prefix strip.
Out of scope
- Any change to how
PushorPushPreflightbehave. The options are accepted and ignored here; Tasks 02 and 03 own the behavior. - The runtime client and backend handler result structs, which Task 04 owns.
- Widening
runGitCommand's argument allowlist.symbolic-ref HEADandconfig --get-allare already admitted; if an implementation needs a flag that is not, change the approach rather than the allowlist.
Acceptance
resolvePushTargetreturns the resolved remote name for the name form and the single-entry URL form,push_remote_fanoutfor a multi-push-URL remote carrying the value,push_remote_url_unmatchedwhen nothing carries it,push_remote_not_foundfor an unknown name, andpush_remote_config_unreadablewhen the remote configuration cannot be read.currentBranchreturns the branch for an attachedHEADand an empty string with no error for a detached one, distinguished from a real read failure.go build ./...and the existing process and securityutil suites pass withPushandPushPreflightbehavior unchanged.
Verification
cd apps/backend && go test -tags fts5 ./internal/agentctl/server/process/... ./internal/common/securityutil/...
cd apps/backend && make lint
Files likely touched
apps/backend/internal/agentctl/server/process/git_push_target.go(new)apps/backend/internal/agentctl/server/process/git_push_target_test.go(new)apps/backend/internal/agentctl/server/process/git.goapps/backend/internal/common/securityutil/git.goapps/backend/internal/common/securityutil/git_test.goapps/backend/internal/agentctl/server/api/git.go(call-site signature only)
Dependencies
None.
Risks
IsValidDefaultBranchNamelooks like the right validator but strips anorigin/prefix, so reusing it would letorigin/mainvalidate asmain. The new validator must not route throughIsValidBaseBranchRef.- Changing
Push's signature touches its existing call site ininternal/agentctl/server/api/git.go. Keep that edit mechanical; Task 04 owns the request-field work there. - A remote name that fails
IsValidBranchNamecannot be safely interpolated intoremote.<name>.url. Skip such remotes during URL matching rather than failing the whole enumeration.
Parallelism
sequential
Inputs
- Requirements REQ-WORKSPACES-CONFIGURED-PUSH-TARGET-001 and -002, and the system design's Push-target resolution and Data and contracts sections.
internal/agentctl/server/process/git.gorunGitCommandargument allowlist andgetCurrentBranch.internal/common/securityutil/git.goexisting validators.setupEmptyRemoteTaskRepoingit_empty_remote_test.goas the fixture pattern for real-repository tests.
Results
Implemented. PushOptions, the eleven error-code constants, the five result
fields, configuredRemotes, resolvePushTarget, and currentBranch live in
apps/backend/internal/agentctl/server/process/git_push_target.go;
securityutil.IsValidExpectedBranchName lives alongside the existing
validators. Push and PushPreflight take PushOptions as a pass-through, so
this work order changed no observable behavior.
IsValidBaseBranchRef's origin/ strip was confirmed to matter: it and the new
validator disagree on inputs such as origin/-dash, so the new validator checks
the value that is actually compared against HEAD and used in the refspec.
currentBranch distinguishes a detached HEAD from an unreadable checkout by
falling back to rev-parse --verify HEAD only when symbolic-ref fails, so a
detached checkout reports an empty branch and a broken one still errors.
Verified with go test -tags fts5 ./internal/agentctl/server/process/... ./internal/common/securityutil/... and make -C apps/backend lint.