ClickUp-style branch the source of truth for ticket-backed Agent work - #774
Draft
adriandemian wants to merge 55 commits into
Draft
ClickUp-style branch the source of truth for ticket-backed Agent work#774adriandemian wants to merge 55 commits into
adriandemian wants to merge 55 commits into
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Main's #787 workspace-cleanup rewrite made terminal cleanup bail with branch_not_ralphx_owned for any branch not matching the ralphx/<slug>/ naming, skipping worktree removal entirely. Strict ClickUp ticket workspaces use the provider's branch convention (e.g. eng-42_ticket_ada), so after that change merged into this branch the strict startup cleanup and next-cycle prepare paths could no longer release their worktrees. Add a strict-managed cleanup variant that removes the terminal worktree while preserving the canonical ticket branch, and route the strict prepare and startup paths through it. Generic (non-strict) cleanup is unchanged and still preserves non-RalphX branches by skipping removal. Fixes the two Rust lib tests failing on PR #774 (Shard 2/4): - later_conversation_prepares_next_generation_only_after_clean_terminal_release - startup_terminal_workspace_cleanup_removes_worktree_but_preserves_strict_ticket_branch
The two guarded-review race tests polled only 500ms (100x5ms) for the spawned review task to reach disable_pr_auto_merge. That task performs real git subprocess work (target resolution) before the call, so on a loaded CI shard it can exceed the budget, failing Rust Lib Tests (Shard 2/4) with left: 0, right: 1. The mock increments the counter on entry and then holds a 250ms sleep().await, so observation still lands inside that window regardless of total wait; only the budget to reach the call needed widening.
…1517796 # Conflicts: # src-tauri/src/http_server/handlers/agent_workspaces/mod.rs # src-tauri/tests/suite_agent_workspace/agent_workspace_repair_auto_publish.rs
Resolve conflicts from #810 (legacy Claude-only team mode removal): - agent_conversation_start_service/mod.rs: keep strict-ticket PR-naming imports, drop removed TeamService import. - agent_workspace_review_unfinished_git_tests.rs: adopt main's bounded timeout pause-synchronization (pause_started) matching the shared tail. - unified_chat_commands/mod.rs: drop stale team_service param/arg from publish_agent_conversation_workspace_while_guarded to match main's team-mode removal and the existing 4-arg call sites.
Base merge updated update_agent_conversation_workspace_from_base_for_app_state and publish_agent_conversation_workspace_for_app_state to 4-arg signatures, but three test call sites still passed an extra None, breaking the lib test compile (Rust Lib Tests Archive CI failure, E0061).
Resolve conflicts between PR naming enforcement work and origin/main: - startup_pipeline: keep main's inline background lane, keep ticket branch repo arg - unified_chat_commands: publish guard moves into the _with_caller impl; while_guarded wrapper now owns the workspace review lifecycle lock - pr_publish_service: keep the frozen-title path inside main's publish_draft_pr_inner; port main's managed-marker PR body format into the extracted pr_publish_body module - tests: keep both strict-ticket-policy and no-origin publish coverage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements strict ClickUp Git naming enforcement for ticket-backed Agent workspaces. When enabled, every Agent started from a ClickUp task creates or reuses a stable, ticket-shaped branch with frozen commit subjects and PR titles derived from the task name and authenticated user. The feature is opt-in and defaults off; all existing workflows remain unchanged.
User Impact
Once the frontend Settings UI is wired (not yet in this PR), users can enable "Strict Git naming" in Settings > Integrations > ClickUp to:
cu-123_fix-login-redirect_ada-lovelace)Non-ClickUp workspaces, branchless Chat mode, and existing ClickUp workspaces without the setting enabled are unaffected.
Technical Context
Domain Model
TicketCanonicalBranchwithpolicy_kind(LegacyCanonicalBase | StrictGitConvention), immutablestrict_policysnapshot (task title, commit rule, PR title, username), and generationalcyclestate machinecreate_if_absent(atomic first-time binding),compare_and_swap_cycle(CAS on generation + state),get_by_branch_name(ownership resolution by exact branch name)Application Layer
ticket_git_convention: Template interpolation (:taskId:,:taskName:,:username:,:summary:) with placeholder validationticket_git_strict_start: Resolves target base, fetches ClickUp task and authenticated user, atomically loads or creates first-cycle binding, checks ownership and safetyticket_git_cycle_lifecycle: State machine advancing cycles (Preparing → Active → Merged/ClosedUnmerged), integrated with PR merge pollingticket_git_publish_policy: Pre-publish validation of commit subjects and PR title against frozen conventionticket_git_publish_hook: Git hook installed in worktree for early commit-message checkIntegration Points
ensure_strict_clickup_ticket_branch_from_servicesduring workspace provisioningFeature Gate
All strict code paths guarded by:
strict_clickup_ticket_policy_applies()→ checks setting enabled + ClickUp task linkedload_ticket_git_publish_policy()→ returnsNonefor legacy or unbound workspacesRisks / Follow-Ups
Verified scope isolation (orchestrator review completed):
Coverage adjustments:
Not yet implemented:
Existing bindings migration:
View full plan
Goal
Add an opt-in strict ClickUp Git convention that makes a persisted, ClickUp-style branch the source of truth for ticket-backed Agent work. When enabled, RalphX must create or reuse the exact resolved ticket branch, allow only one active owning conversation for that branch, enforce matching commit subjects and PR titles, and safely reuse the same branch for later PR cycles.
Assumptions:
:taskId:_:taskName:_:username:: https://help.clickup.com/hc/en-us/articles/6305771568791-GitHub-integrationRepository Evidence
src-tauri/src/application/agent_conversation_start_service.rsalready fetches the authoritative ClickUp task, derives the preferred custom/raw ID, searches matching open PRs/local branches, and selects linked mode only when a unique candidate exists.src-tauri/src/application/agent_conversation_workspace.rscurrently generates isolated branches asralphx/{project}/agent-{provider}-{ticket}-{conversation}; linked mode checks out the selected branch and already refuses a second active workspace for the same branch.src-tauri/src/application/agent_conversation_start_service/helpers.rs::ensure_linked_branch_workspace_availablealready implements the selected “redirect and block” ownership rule, but returns an unstructured string.src-tauri/src/application/ticket_canonical_branch.rs,TicketCanonicalBranch, andticket_canonical_branchesalready persist a per-ticket branch and partial-push recovery, but the service is not called by production conversation startup and currently createsralphx/ticket/...branches that are never worktree-checked-out.src-tauri/src/application/clickup_integration_service.rs::ClickUpTaskContentsupplies task ID, custom ID, name, creator, and assignees;current_user()supplies the authenticated ClickUp user needed for:username:.src-tauri/src/commands/unified_chat_commands.rscreates automatic workspace commits and drives publication;src-tauri/src/domain/services/pr_publish_service.rs::AgentWorkspacePrPublishercurrently derives PR titles from the describer/conversation and only has special Jira-key normalization.src-tauri/src/application/publish_resilience.rsis the canonical Agent-workspace source-update seam:ensure_publish_branch_freshfetches origin and delegates toupdate_source_from_target, classifying clean updates, conflicts requiring the workspace repair agent, missing branches, and operational failures.src-tauri/src/application/agent_workspace_publish_recovery.rs,agent_workspace_pr_supervision_recovery.rs, andagents/ralphx-agent-workspace-repair/shared/prompt.mdalready provide durableneeds_agentrepair, completion verification, auto-publish retry, stale-run repair, and startup recovery. Commit-hook policy failures are already classified as agent-fixable while hook/environment failures are operational.src-tauri/src/application/services/pr_merge_poller.rsandagent_workspace_external_pr_reconciliation.rsmake GitHub the Agent-workspace merge authority, persist PR terminal state, stop active runs, emit workspace/publication events, reconcile externally-created PRs, and trigger guarded local cleanup.src-tauri/src/application/agent_conversation_workspace.rs::rollover_agent_conversation_workspace_with_setup_modecurrently handles continued conversation after a merged/closed PR by deleting a clean old worktree and creating a new continuation branch.agent_workspace_continuation.rsandchat_resumption.rsseparately classify terminal/missing workspaces for resume. Strict ClickUp reuse must align all of these entry points.src-tauri/src/application/git_artifact_cleanup.rsintentionally separates worktree cleanup from branch deletion and deletes only branch names it can prove RalphX owns. A ClickUp-named strict branch is persistent binding state: RalphX may remove its validated clean worktree but must not make that branch eligible for generic terminal deletion.frontend/src/components/settings/ClickUpIntegrationSettingsPanel.tsxis the existing lazy-loaded ClickUp settings surface;frontend/src/components/agents/AgentsStartComposer.tsxandagentStartErrors.tsown linked-workspace start failures.Decisions
:taskId:_:taskName:_:username::taskId: - :taskName::taskId: - :taskName::taskId:in all three templates. Allow:taskId:,:taskName:, and:username:everywhere; optionally allow:summary:only for commit/PR templates so teams can preserve per-commit detail without weakening the ticket prefix.:username:from the authenticated ClickUp user, not the task assignee or task creator. If a template requires it and ClickUp cannot resolve the current user, fail closed before workspace creation.PendingMerge → Mergingstate machine. Source freshness, repair, push, PR supervision, terminalization, reconciliation, and startup recovery remain owned by their current Agent-workspace services; GitHub remains final merge authority.Data / State
ClickUp settings
Extend
ClickUpIntegrationSettingsandclickup_integration_settingsadditively with:strict_git_naming_enabled(default false)branch_name_templatecommit_subject_templatepr_title_templateUse non-null database defaults matching the ClickUp-style defaults so old rows deserialize safely. Update the memory/SQLite repositories, Tauri response/input types, frontend zod schema, mocks, and tests. Disconnecting ClickUp clears credentials/validation state and disables enforcement, but preserves the user’s template strings for reconnect.
Frontend read models
Do not make the frontend infer policy from a branch prefix. Add an optional, additive
TicketGitConventionSummaryto Agent workspace/start payloads with the minimum display-safe frozen state: provider, task ID/key, task title snapshot, rendered branch, commit subject rule/example, rendered PR title, policy version, and managed/frozen status. This same summary drives the conversation header, workspace line, publish confirmation, replay, and recovery UI.Add a read-only ClickUp convention preview command/query for the start composer. Given project ID plus the selected ClickUp task reference and proposed PR base, it refetches authoritative task/current-user/settings data and returns either a rendered preview or a typed preflight blocker. It must not reserve a branch, create a binding, scan/mutate worktrees, or promise startup success. Cache it by project + task + settings version, cancel/ignore stale results when the reference/project changes, and re-resolve authoritatively during submit.
Represent start/publish policy failures with stable error codes and structured details rather than extending
LINKED_SETUP_FAILURE_MARKERprose parsing. Required details include expected branch, owner conversation ID when busy, related PR URL/number when known, offending commit SHA/subject for commit-policy failure, and a remediation category. Keep a compatibility parser for legacy linked-setup errors during rollout.Ticket binding
Evolve the existing
ticket_canonical_branchesmodel rather than creating a competing mapping. Add immutable strict-policy snapshot fields such as:Add a unique
(project_id, branch_name)lookup and repository method so send/publish/recovery paths can resolve the ticket policy from a workspace branch. Introduce create-if-absent/CAS semantics: concurrent first starts must converge on one stored binding and never overwrite its rendered branch.Legacy
ralphx/ticket/...rows are not silently rewritten. If strict mode encounters one, adopt/rebind only when repository evidence proves there is no active workspace, open PR, divergence, or unpublished work; otherwise fail closed with explicit migration guidance. Existingterminalsemantics for legacy canonical-base rows must not be reused to block the selected strict “reuse same branch after merge” policy; strict bindings need explicit per-cycle state instead of treating the ticket branch as permanently terminal.Runtime ownership and lifecycle
resolve_agent_conversation_workspace_path()and its hashed project/conversation components under the configured worktree root.find_active_by_project_and_branch_namefor the one-active-owner guard.HEAD; publish validation must inspect every introduced commit from the recorded cycle base/effective merge-base toHEAD.pr_merge_pollerand external/startup reconciliation. Terminalization must persist the terminal PR and publication event, stop/settle the current run, and finish guarded worktree cleanup before a strict binding becomes eligible for another cycle.update_source_from_target/the shared freshness path so an ancestor branch advances without a force move. If GitHub deleted the remote head after merge, treat that as normal only after the same terminal/containment proof.is_expected_agent_workspace_branchso ClickUp template branches become disposable; pass/derive an explicit persistent-branch retention policy while still allowing validated clean worktree removal.agent_workspace_continuation,chat_resumption, direct send, external ideation messaging, and Chat-service rollover decisions aligned so one entry point cannot resume a terminal strict workspace while another blocks or creates a continuation suffix.Architecture And Runtime Flow
ticket_git_convention.rs) containing:ticket_canonical_branch.rsinto the strict binding resolver:AgentConversationStartServicebefore conversation/worktree creation whenever strict mode is enabled and a ClickUp reference is present. Reuse the existing ClickUp reference/task lookup andAgentConversationWorkspaceBranchNameHintas the trigger/input seam, but do not send the strict branch throughagent_conversation_ticket_branch_segment()because that deliberately produces the legacyralphx/...-{conversation}name. In strict mode, reject or override user-supplied branch-mode/head-branch selections that do not equal the rendered ticket branch; only the PR target/base branch remains user-selectable. Keep the current hint/candidate search and isolated naming byte-compatible behind the disabled path.workspace.branch_name;workspace.base_refremains the selected/persisted PR target branch. Do not change hashed worktree-path derivation. Exclude Review PR mode from this override so its source-PR isolation rules remain unchanged.unified_chat_commands.rschanges to thin calls into the focused convention service. Acquire the existingtry_acquire_agent_workspace_publish_guard()first, then perform the authoritative convention check before any commit/push/PR side effect: verify the checked-out/frozen branch, compute the current cycle range from the refreshed effective base, and validate every introduced commit subject. Continue through the existing freshness wrapper selected by workspace ownership—direct branches useensure_publish_branch_fresh→update_source_from_target; linked plan branches useensure_plan_publish_branch_fresh→update_plan_from_main_isolated—then the normal push/PR publisher pipeline.commit-msghook/config for strict managed worktrees to reject bad subjects early. Treat the hook as UX only:--no-verifycannot bypass the authoritative pre-publish range validation. Feed typed convention failures directly intoPublishFailureClassrather than relying on new error-string heuristics.needs_agentrepair path for unpushed policy violations. Include the frozen convention and offending commits in the workspace-repair payload; aftercomplete_agent_workspace_repair, extend the existing completion proof (current HEAD, clean tree, no merge/rebase/conflict markers, current base) with branch identity and full cycle-range convention validation before the automatic publish retry. Never let stale repair completion or an old policy snapshot authorize the retry.AgentWorkspacePrPublisher. In strict mode, ignore arbitrary describer/conversation title drift and create/update the PR with the frozen title; keep the generated reviewer-focused body and repository template behavior unchanged.pending/checking/committing/refreshing/describing/pushing/pushed/needs_agent/failed/refreshed), and durable publication events. Add structured convention details/classification without inventing a parallel status machine. No alternate publish path may bypass validation.agent_conversation_continuation_branch_name; strict workspaces reuse the frozen branch only after terminal/content/cleanliness proof and atomic new-cycle persistence.update_publicationwrite: strict workspaces may adopt only a PR whose head is the exact frozen branch and whose base matches the effective workspace base. Validate/fix the title only through the normal publisher when safe; otherwise append a typed blocked publication event and leave prior binding/publication authority intact. Apply the same rule during startup reconciliation.Agent And MCP Surface
UI / UX
1. Settings → Integrations → ClickUp
Keep this inside the existing lazy-loaded ClickUp section; do not add another top-level settings destination. Split the current panel into two visually distinct cards:
When enforcement is off, keep the convention card collapsed to the switch plus default examples. When on, reveal three labeled template inputs (Branch name, Commit subject, Pull request title), supported-token chips, and a compact preview block showing the rendered branch, commit, and PR title for one representative task/user. Use explicit Save/Discard actions and an unsaved-changes state; do not save each keystroke. Validate locally while typing, then validate again on save through the backend renderer. Associate field-level errors with the relevant input for missing
:taskId:, unknown tokens, empty render, invalid Git ref, byte-length overflow, and required-but-unavailable username. Keep the last persisted policy active if save fails.Below the editor, show persistent copy: “Changes apply to new ticket bindings. Existing managed ticket branches keep their frozen naming.” Disconnect preserves templates but visibly disables enforcement. Reconnect does not silently re-enable it. Preserve the current first-paint loading shell and lazy settings import; do not fetch preview dependencies before the Git naming card is expanded/enabled.
2. Agent start composer preflight
The selected ClickUp reference is the trigger. After a project and one ClickUp ticket are selected, render a lightweight inline ClickUp-managed Git callout near the ticket chips/base control:
For Edit, Plan, or Ideation, retain the base picker but label its purpose as the PR base in the managed callout. Hide/disable the isolated-branch toggle and any
ralphx/...head-branch choice because the strict head branch is fixed. A base selection must never be presented as changing the managed head branch. The preview is advisory: submit performs the authoritative refetch/reservation and may return a newer resolved state.Replace the current generic linked-setup retry card for strict failures. In particular, never offer “Retry with isolated branch” for a managed ticket. Render typed inline recovery cards:
The optimistic conversation must be removed on every blocked start, the original draft/ticket reference must remain intact, and navigation occurs only after a successful authoritative start or the explicit “Open conversation” action.
3. Active conversation and workspace identity
Expose the optional convention summary on
AgentConversationWorkspace; never derive “managed” from the branch text. ExtendAgentConversationWorkspaceLineandAgentsWorkspaceStatusPillwith a compact ClickUp-managed indicator while preserving the existing branch/status/PR information. The header tooltip/details show the full branch, PR base, ClickUp task ID/title, frozen commit rule, frozen PR title, and “Naming frozen for this ticket.” The short header remains compact and truncates the branch as it does today.For branchless Chat with a linked ClickUp task, show a non-workspace integration note that the convention will be resolved on Edit/Plan transition. After mode upgrade, refresh from the returned workspace summary rather than keeping the advisory preview. Replayed/recovered conversations must render the same managed identity from persisted backend state, including when the global toggle is now off.
4. Commit & Publish
In
PublishWorkspaceDialog, add a small ClickUp convention summary for managed workspaces: exact branch, commit rule, locked PR title, and selected PR base. Do not add editable title controls. The confirmation text must make clear that RalphX will validate all commits in the cycle before any push/PR side effect.In
AgentsPublishPaneland pipeline notices, map typed policy failures to the existing repair/operational split. For an unpushed local commit mismatch, list each offending short SHA and subject plus the expected format, show that RalphX is repairing or waiting for repair, and retain safe terminal/copy-remediation affordances. The repair agent may reword only proven-unpushed local commits and must pass the existing completion gate plus convention validation before auto-publish retries. If violating history is already remote, rewriting would require force, or the blocker is environmental, show operator action required and never auto-amend/reset/force-push. For branch/PR-title drift, show expected versus actual and whether RalphX can safely correct only the open PR title through the normal publisher; retain the frozen title on retry. Auto-publish and PR-fix failures surface the same durable policy card and do not degrade into a generic failed status.5. Ticket dashboard and repeated entry points
Ticket-dashboard launches and any other Agent-entry composer reuse the same preview component/query/error model rather than duplicating ClickUp naming logic. Conversation/sidebar rows may show the existing branch text, but managed status must come from the workspace summary if a badge is added. Keep the full policy details in the active header/publish surfaces to avoid visual noise.
Interaction, accessibility, and native behavior
aria-describedby; any icon-only copy/open control uses the app tooltip and an accessible name.Progression Scenarios
-next-*.needs_agent, run the existing workspace-repair flow with convention context, verify current-attempt completion plus all cycle commits, then retry publish.Affected Files
src-tauri/src/domain/integrations/clickup_settings.rs,src-tauri/src/infrastructure/{memory,sqlite}/*clickup_integration_settings*, a generated timestamped migration plus migration registration/tests,src-tauri/src/commands/clickup_commands.rs, and focused service/command tests.src-tauri/src/application/ticket_git_convention.rs(and sibling test file) and register it inapplication/mod.rs.src-tauri/src/application/ticket_canonical_branch.rs,src-tauri/crates/ralphx-domain/src/entities/ticket_canonical_branch.rs, repository traits and memory/SQLite implementations/tests, and the existing canonical-branch migration via a new additive migration.src-tauri/src/application/agent_conversation_start_service.rs,clickup_git_association.rs, itshelpers.rsand sibling tests,agent_conversation_workspace.rs,agent_workspace_continuation.rs,chat_resumption.rs,chat_service/mod.rs, linked-plan-branch worktree/publication paths, external ideation messaging, mode-switch paths, and focused sibling/integration tests.src-tauri/src/commands/unified_chat_commands.rs,agent_workspace_auto_publish.rs,src-tauri/src/application/publish_resilience.rs,agent_workspace_publish_recovery.rs,agent_workspace_pr_supervision_recovery.rs, review-to-publish handoff,complete_agent_workspace_repair, its canonical agent payload/prompt, and focused suites; keep policy logic outside giant command/handler modules.src-tauri/src/application/services/pr_merge_poller.rs,agent_workspace_external_pr_reconciliation.rs,pr_startup_recovery.rs,startup_pipeline.rs,git_artifact_cleanup.rs, archive/close-PR paths, repository candidate queries, and coupled tests.src-tauri/src/domain/services/pr_publish_service.rs,src-tauri/src/application/agent_workspace_pr_description.rs, and focused tests.frontend/src/api/clickup.ts,useClickUpIntegration.ts, a focused convention-preview hook/query-key module, API transforms/schemas, mocks, and their tests; add the corresponding read-only Tauri command and backend response DTO.frontend/src/components/settings/ClickUpIntegrationSettingsPanel.tsx, a focused template editor/preview component if the panel would otherwise become oversized,AgentsStartComposer.tsx, the shared composer/ticket-reference surfaces,useStartAgentConversation.ts,agentStartErrors.ts,agentSessionStore.ts, ticket-dashboard launch coverage, and focused Testing Library tests.frontend/src/api/chat.tsworkspace transforms,AgentConversationWorkspaceLine.tsx,AgentsChatHeader.tsx,AgentsPublishWorkspaceDialog.tsx,AgentsPublishPanel.tsx, publish pipeline notices, mocks/fixtures, and focused tests.Constraints
AgentConversationWorkspaceBranchNameHintsanitization, isolatedralphx/...-{conversation}naming, branch-mode defaults, normal continuation suffixes, existing push-status values, and generic terminal cleanup.Avoid
ralphx/...or per-conversation branches for a strict bound ticket; keep the existing generated naming path unchanged for non-strict workspaces.PendingMergestatuses for Agent workspace PRs.Proof Obligations
-next-*or clears publication state early.Testing Strategy
Backend TDD
needs_agent, remote/force-rewrite and environment failures stay operational, and typed classification does not depend on prose matching.Frontend TDD
Validation
python3 scripts/check-layering.py, migration validation, and rustfmt checks on every touched Rust leaf file.HEADand perform the required false-success audit on persistence, race, recovery, publish, and cleanup paths.Risks And Open Questions
Generated by RalphX