feat: GitLab sync — dedup fixes, type filtering, epic→milestone mapping, work item hierarchy#2889
feat: GitLab sync — dedup fixes, type filtering, epic→milestone mapping, work item hierarchy#2889ktoulgaridis wants to merge 10 commits into
Conversation
…URLs GetIssueByExternalRefInTx only queried the issues table, missing pushed wisps that have external_ref set in the wisps table. Also, GitLab CE can return work_items URLs instead of issues URLs, causing IsExternalRef and ExtractIdentifier to fail on valid refs. - Check wisps table as fallback in GetIssueByExternalRefInTx - Expand issueIIDPattern to match both /issues/ and /work_items/ paths - Set source_system on push-create for additional dedup resilience - Add test cases for work_items URL matching Executed-By: mayor
Wire up --type, --exclude-type, and --no-ephemeral flags for bd gitlab sync. ExcludeEphemeral defaults to true so wisps/agent beads are never pushed to GitLab unless explicitly opted in with --no-ephemeral=false. This enables the sync hierarchy design: epics/stories/tasks sync to GitLab while wisps and internal coordination beads stay local. Executed-By: mayor
Internal coordination types (molecule, message, event) should never be pushed to GitLab. Add them as default ExcludeTypes when the user hasn't provided an explicit --type whitelist or --exclude-type override. Executed-By: mayor
The source_system field doesn't exist in the storage schema, causing UpdateIssue to reject the entire update map — including the critical external_ref field. This left pushed issues without local external_refs, causing duplicate creation on subsequent syncs. Verified fix: push creates GitLab issue, saves external_ref locally, subsequent pull recognizes the issue via external_ref (including work_items URL format) and skips it. Executed-By: mayor
Push status::open for open issues and status::done for closed issues (previously only in_progress/blocked/deferred were mapped). Pull now handles status::review (→ in_progress) and status::done (→ closed). This enables GitLab boards with columns: Open, In Progress, Review, Deferred, Done — all driven by scoped status:: labels. Executed-By: mayor
Pull-side mapping now recognizes status::review (maps to in_progress) and status::done (maps to closed) labels from GitLab. Push-side remains non-opinionated: only sets status labels for in_progress, blocked, and deferred — open and closed are handled by GitLab's native issue state. This lets users configure custom board columns without beads imposing a specific workflow. Tests added for new pull-side mappings. Executed-By: mayor
Executed-By: mayor
|
@steveyegge a note on the design choices here. We initially went down a path of trying to support arbitrary GitLab board workflows (custom status labels like Instead, we opted for a 1-to-1 mapping of beads statuses to GitLab:
The push side stays minimal — it only sets labels for the states that GitLab doesn't represent natively. The pull side recognizes This means beads drives the workflow, and GitLab is just the viewport. Users who want custom board columns (like "Review" or "QA") can add them at the project level without beads needing to know about them. The formatting commit at the end is a pre-existing Let me know if you envision this working differently in any way, but I have been toying with this idea of exposing the highest levels of beads to human eyes, for the illusion of control. |
❌ 6 Tests Failed:
View the top 3 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
GitLab CE has no Epic work item type, so epics were being pushed as regular issues — polluting the board. Now: - type=epic beads create/update GitLab milestones - Non-epic issues with a parent-child dep to an epic get the milestone assigned automatically via milestone_id - Milestone external refs use /-/milestones/<id> URL pattern - IsExternalRef/ExtractIdentifier handle both issue and milestone URLs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Executed-By: mayor
When a bead of type=task has a parent-child dependency to a story or feature, it is now created as a GitLab Task work item (not a regular Issue) with the parent set via the hierarchy widget. This enables the full 3-level hierarchy in GitLab CE: Epic (milestone) → Story/Feature (issue) → Task (child work item) Uses GraphQL API for work item creation since the REST API only creates Issue-type work items. Requires gitlab.project_path config to be set. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Executed-By: mayor
New commits: GitLab work item hierarchy supportTwo new commits on this branch: 1. 2. The GraphQL path requires a new config key Tested against GitLab CE 18.10 — milestones, issues, and task work items all created correctly with proper hierarchy links. |
findParentEpicMilestone now walks up the parent-child chain (up to 5 levels) to find an ancestor epic, not just the direct parent. This fixes milestone assignment for tasks whose parent is a story (task → story → epic). Also fixes milestone ID lookup: the URL contains the IID (project- scoped) but milestone_id needs the API ID (global). Now fetches milestones from the API and matches by IID. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Executed-By: mayor
…ng, work item hierarchy (#2889) - Fix pull dedup: check wisps table and match work_items URLs - Add type-based filtering to GitLab sync CLI - Default-exclude molecule/message/event types from push - Fix source_system update that breaks external_ref persistence - Map all beads statuses to GitLab scoped labels for board columns - Handle status::review and status::done labels on pull - Map epic beads to GitLab milestones instead of issues - Create task beads as GitLab Task work items with parent hierarchy - Walk parent chain to find epic milestone, use API ID not IID Co-authored-by: ktoulgaridis <ktoulg@gmail.com> Executed-By: beads/crew/emma
|
Merged via squash-merge after rebase conflict resolution. Thank you! |
Summary
Pull-side fixes:
GetIssueByExternalRefInTxchecks bothissuesandwispstables/issues/and/work_items/paths (GitLab CE returns work_items URLs)Push-side fixes:
molecule,message, andeventtypes from GitLab push (internal coordination beads were leaking as GitLab issues)--typeand--exclude-typeCLI flags for explicit controlEpic → Milestone mapping (new):
type=epicbeads create/update GitLab milestones instead of issues (GitLab CE has no Epic work item type)milestone_idsetStory → Task hierarchy (new):
type=taskbeads with a parent-child dep to a story/feature create GitLab Task work items (not regular Issues) via GraphQLEpic (milestone) → Story/Feature (issue) → Task (child work item)gitlab.project_pathconfig for GraphQL API callsStatus label mapping:
in_progress,blocked,deferred,review,done) mapped to GitLab scoped labels for board columnsConfig
New optional config key:
gitlab.project_path— GitLab project path (e.g.,socwave/socwave). Required for Task work item creation via GraphQL.Test plan
mol-refinery-patroland other internal types excluded from pushgo test ./internal/gitlab/...passes