Skip to content

fix(smithy): sf task merge honors workspace merge.targetBranch config (#63)#111

Open
komoreka wants to merge 1 commit into
stoneforge-ai:masterfrom
komoreka:fix/merge-target-branch-config-fallback
Open

fix(smithy): sf task merge honors workspace merge.targetBranch config (#63)#111
komoreka wants to merge 1 commit into
stoneforge-ai:masterfrom
komoreka:fix/merge-target-branch-config-fallback

Conversation

@komoreka
Copy link
Copy Markdown
Contributor

@komoreka komoreka commented May 7, 2026

Summary

Closes #63.

merge.targetBranch in .stoneforge/config.yaml was previously consumed only by prompt-builders (sessions, steward scheduler, dispatch daemon) which feed it into WorkflowPresetContext for agent system prompts. The actual git tooling in sf task CLI commands and the MR-creation path never loaded it. So setting merge.target_branch: dev told agents about the target in their prompts, but sf task merge, sf task complete, sf task sync, and sf task update --status merged all still defaulted to git origin/HEAD (typically main) when no per-task targetBranch was set.

This PR wires the config value into all merge-related code paths. Resolution chain across the affected commands is now:

task.targetBranch → config.merge.targetBranch → git origin/HEAD → main

This unblocks the steward case from #63: stewards cannot own a targetBranch field per the agent-type contract (the API rejects PATCH attempts), so a workspace-level config has always been the only path available to them. Now it actually flows through to git.

What changed

  • packages/smithy/src/cli/commands/task.ts — exports loadConfigTargetBranch() helper; threads it into the mergeTaskHandler (mergeBranch call + post-merge verify), the sync handler, and the update --status merged verify path.
  • packages/smithy/src/services/task-assignment-service.ts — same fallback in the MR-creation path used by sf task complete.
  • packages/smithy/src/cli/commands/task-target-branch.bun.test.ts (new) — 6 unit tests for the helper covering: value present, missing config file, missing merge block, null value, empty string, slash-bearing branch names.

Test plan

  • bun test src/cli/commands/task-target-branch.bun.test.ts — 6/6 pass
  • bun test src (full smithy suite) — 1574 pass, 0 fail, 19 skip
  • Full workspace pre-PR validation:
    • turbo run typecheck — 16/16 pass
    • pnpm build — 13/13 pass
    • bun test on core (2737/0/22), storage (137/0/0), quarry (4260/0/1), smithy (1574/0/19) — total 8708 passing, 0 failing
  • Live probe against a real workspace's .stoneforge/config.yaml — confirmed the helper resolves null, plain values (dev), and slash-bearing values (release/v2) correctly when called from inside the workspace.

Out of scope (deferred)

  • The deeper director→task propagation discussed in bug: sf task merge defaults to 'main' for stewards — targetBranch is director-only, no fallback #63's review — currently dispatch-daemon.ts:2890-2896 propagates the director's targetBranch onto a task only during assignTaskToWorker, which means tasks not yet worker-dispatched (or where resolveOwningDirector fails) never get it. Worth a follow-up issue/PR if desired; happy to file separately. This PR closes the user-visible symptom by making the config workaround actually work end-to-end.

The `merge.targetBranch` value in `.stoneforge/config.yaml` was previously
only consumed by prompt-builders (sessions, steward scheduler, dispatch
daemon) to populate WorkflowPresetContext for agent system prompts. The
actual git tooling in the `sf task` CLI commands never loaded it, so
setting `merge.target_branch: dev` told agents about the target in their
prompts but the merge operations themselves still defaulted to git
origin/HEAD (typically `main`).

Resolution chain across all merge-related paths is now:
  task.targetBranch → config.merge.targetBranch → git origin/HEAD → main

Wired in:
- packages/smithy/src/cli/commands/task.ts (merge handler, sync handler,
  update --status merged verify path)
- packages/smithy/src/services/task-assignment-service.ts (MR-creation
  path used by sf task complete)

A new exported `loadConfigTargetBranch()` helper centralises the config
read with safe handling of missing config, missing merge block, null
values, and empty strings. Six unit tests in task-target-branch.bun.test.ts
cover those cases.

Stewards (which cannot own a targetBranch field per the agent type
contract) finally have a config-only path to set a project-wide target.

Refs stoneforge-ai#63.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: sf task merge defaults to 'main' for stewards — targetBranch is director-only, no fallback

1 participant