| name | ticket-sync |
| metadata | {"version":"0.24.6"} |
| description | Push work items to the team's configured ticketing system (GitHub Issues, Jira, Azure DevOps, GitLab, Linear...). Use when asked to sync tickets, after closing out a plan, or when items should be visible in the tracker. Push and pull. |
Ticket sync
Sync runs through a typed adapter contract: the dispatcher
(bin/sync_dispatch.py) owns every invariant; a per-system adapter is a
dumb translator. This skill's job is to run it and read the report — not
to re-implement the rules. Contract:
docs/plans/2026-07-18-typed-adapter-contract.md.
1. Read the config
Read the ticketing: block in .work/config.yml. system names the
tracker: github, gitlab, jira, ado, linear, codecatalyst,
other, or none. If it is none or absent, say so and stop — there is
nothing to push to.
The enum is advisory, not a capability list. bin/worklog only ever
branches on none; every other value is a name this skill resolves to
whatever tooling exists (CLI, MCP server, REST). Named values carry known
caveats; other is any tracker not listed — set it, note the real system
in options:, and the skill researches the tooling at runtime.
- linear — no git remote to detect; reach it via the Linear MCP server
or CLI. Markers go in the issue description (markdown survives).
- codecatalyst — AWS's ADO equivalent (issues per project). Closed to
NEW customers since 2025-11-07; existing spaces keep working. Reach via
aws codecatalyst CLI.
- GCP has no native work tracker — GCP-hosted teams pick one of the
above (or
other); there is no gcp value to configure.
2. Scope
Open items plus externally-linked items whose canonical hash differs from
the last pushed one; closed and in-sync items are inert (spec §10.5 — the
dispatcher computes this, see the contract doc).
3. Flow
bin/worklog adapter check — activates and validates the adapter for
the configured system against the contract. Missing adapter → the
dispatcher runs local-only; that is a mode, not an error.
bin/worklog sync — the dispatcher handles scope, hash-skip,
create-vs-update, echo suppression, conflict detection, and
worklog ingest of pulled changes.
- Read the drift report and surface anything that needs a human
(conflicts, unsupported fields, degraded type mappings, auth failures).
4. Invariants — dispatcher-enforced, context only
Each of these is code in bin/sync_dispatch.py now, not procedure for you
to follow — see docs/plans/2026-07-18-typed-adapter-contract.md:
- Canonical hash / skip-unchanged (spec §10.3): an item is pushed only
when its canonical hash differs from
last_pushed_hash — the dispatcher
enforces this.
- Idempotency (spec §10.5): the item ULID travels as a marker in the
ticket body and a retried push finds rather than duplicates — the
dispatcher enforces this.
- Pull mechanics (spec §10.1–10.3, §10.6): remote edits enter the log
via
worklog ingest with deterministic event IDs, echo-suppressed and
conflict-checked — the dispatcher enforces this.
4b. Rich ticket bodies
When creating or updating a ticket, compose its body with
bin/worklog ticket-body <ulid> (issue-description skill): summary,
epic/plan context with the frozen plan's wiki page, milestone, and
traceability edges, rendered from the log + graph. Never hand-edit richness
into the remote ticket only — enrich the source (worklog update --body,
link-pr, sidecar relates_to) and let sync carry it out.
5. Closing
When a scoped item's status is done or cancelled, the sync closes the
remote ticket (adapter close verb) with a short comment naming the
resolution. After that push the item is inert: hashes match, so it is
never rescanned.
6. Per-system tooling
Lives in the adapters, not here. Authoring rules and shipped
implementations: adapters/README.md, adapters/<system>/adapter.
7. Report
Finish with counts: pushed / updated / closed / skipped, plus conflicts
and anything deferred or needing human action.
Conflicts surface in worklog list (stderr), worklog show, and the
status report's Needs-attention section. Resolve with:
bin/worklog resolve <item> --field <f> --take local|remote
Azure DevOps: field-tested caveats (no adapter ships yet — hints for building/driving one)
- Marker must be a TAG, not an HTML comment. ADO silently strips HTML
comments from a work item's Description, so
<!-- worklog:<ulid> -->
vanishes. Store the marker as a work-item tag (worklog:<ulid> — ADO
preserves colons and case exactly) and search by tag. An ADO adapter's
capabilities.marker should be {"style": "tag", "template": "worklog:{ulid}"}.
- Updates merge, never overwrite. When updating an existing work item:
merge tags, and write title/state only when they actually changed. Existing
Description/content in Azure Boards is never replaced — teams adopt worklog
onto boards full of real content.
- Migrating existing tickets (any system): create-vs-update is decided
purely by whether the local item carries
external. Pre-seed it with
worklog link <ulid> --system ado --key <AB#id> for every imported item so
the first sync treats all of them as updates — never duplicates. Pilot one
epic first; the acceptance gate is worklog sync --dry-run reporting
0 creates. Nothing irreversible happens before the first real push.