plugins/flow-next/codex/skills/flow-next-tracker-sync/SKILL.md
Project a flow-next spec to a tracker issue (Linear first, GitHub next) and reconcile body/status/comments two-way — projection, not coordination. The spec stays the source of truth; the tracker is a co-editable mirror. Use to configure the bridge (discovery ceremony), link a spec to an issue (flow-first push or tracker-first "grab issue X and spec it"), push/pull/reconcile, or unlink. Triggers on /flow-next:tracker-sync, "sync to linear", "push this spec to the tracker", "grab issue X and spec it", "link this spec to the issue", "reconcile with the tracker". NOT /flow-next:sync (that is plan-sync, a different skill).
npx skillsauth add gmickel/gmickel-claude-marketplace flow-next-tracker-syncInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The .flow/specs/<id>.md spec is the source of truth and the quality layer; the tracker (Linear first, GitHub next) is a co-editable mirror for teams that must live in it. This skill is projection, not coordination — the tracker mirrors the spec (body, status, comments all sync two-way) but never drives flow state or spawns agents (see the decision record at .flow/memory/.../tracker-sync-is-projection-not-*).
This skill is the spine: the discovery ceremony, the spec↔issue grain, the identity/naming alias, and a transport-blind push/pull/reconcile orchestration skeleton. It does NOT contain transport code or merge logic — those plug in via the interface defined here:
fetchIssue / writeIssue / listComments / postComment / readStatus / setStatus) are implemented by the Linear adapter (fn-52.3) and GitHub adapter (fn-52.7). This skill calls them through the normalized interface; it never sees a wire shape. The Linear adapter is a detect-best-available transport ladder (MCP → GraphQL → no-op, mirroring fn-51's driver ladder) — see references/linear-ladder.md; the GitHub adapter is the headless-robust gh transport (single rung + no-op, reduced-fidelity status) — see references/github.md.issue / comment / status) the adapters exchange. The agentic 3-way body merge + format translation + scoped conflict is in references/body-merge.md (fn-52.4); the per-field status who-wins is references/status-sync.md and comments/evidence append + dedup is references/comments-sync.md (fn-52.5). The interface is defined in references/adapter-interface.md.Read steps.md for the full phase-by-phase execution. Read references/adapter-interface.md for the transport interface + normalized payload contract, references/body-merge.md for the agentic 3-way body merge / format translation / scoped conflict, references/status-sync.md for the per-field status who-wins + deadlock fallback, references/comments-sync.md for comments/evidence two-way append + dedup, references/identity.md for the hybrid id model (tracker-first canonical vs flow-first alias), references/linear-ladder.md (→ linear-mcp.md, linear-graphql.md) for the Linear transport ladder, and references/github.md for the GitHub adapter (gh transport, reduced-fidelity status).
Sync engine shape (discovery ceremony, per-item
lastSyncedAt, surface-diffs-never-overwrite) adapted from Ray Fernando'srunning-bug-review-boardissue-trackers.md(Apache-2.0) — see CHANGELOG.
CRITICAL: flowctl is BUNDLED — NOT installed globally. which flowctl will fail (expected). Define once; subsequent blocks (here and in steps.md) use $FLOWCTL:
FLOWCTL="$HOME/.codex/scripts/flowctl"
[ -x "$FLOWCTL" ] || FLOWCTL=".flow/bin/flowctl"
Non-blocking, same pattern as /flow-next:plan — one-line nag when the local setup lags the plugin:
SETUP_VER=$(jq -r '.setup_version // empty' .flow/meta.json 2>/dev/null)
PLUGIN_JSON="${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-$HOME/.codex}}/.codex-plugin/plugin.json"
PLUGIN_VER=$(jq -r '.version' "$PLUGIN_JSON" 2>/dev/null || echo "unknown")
if [[ -n "$SETUP_VER" && "$PLUGIN_VER" != "unknown" && "$SETUP_VER" != "$PLUGIN_VER" ]]; then
echo "Plugin updated to v${PLUGIN_VER}. Run /flow-next:setup to refresh local scripts (current: v${SETUP_VER})." >&2
fi
Continue regardless (never blocks; silent when setup was never run or versions match).
Inline skill (no context: fork) — plain-text numbered prompt must stay reachable across phases. Subagents can't call plain-text numbered prompts (Claude Code issues #12890, #34592). The discovery ceremony (Phase 1) and genuine-conflict surfacing (handled in fn-52.4/.5) both require user choice in interactive mode.
The canonical flow-next split. flowctl (fn-52.1, fn-52.10) provides atomic, deterministic helpers; this skill, running on the host agent, does the API calls / reconciliation / asking:
| flowctl owns (deterministic) | the skill owns (host-agent judgment) |
|---|---|
| sync active — is the bridge active (value-checked)? | discovery ceremony: probe signals, surface, ASK, confirm |
| sync list-unsynced / list-stale — enumerate | decide which specs to push/pull this run |
| sync set-tracker-id / set-last-synced / set-merge-base — atomic state write | call the transport (fetchIssue / writeIssue / …) |
| sync clear — unlink, wipe state atomically | semantic 3-way body merge (fn-52.4), status who-wins + comment dedup (fn-52.5) |
| sync receipt / sync defer — proof-of-work + queue | translate flow-structured ↔ tracker free-form |
| sync check-collisions — flag shared tracker ids | decide create-vs-link on ambiguity; ASK the user |
| spec create --tracker-first / config set — id + config write | choose the hybrid id origin (tracker-first vs flow-first) |
Never reimplement a flowctl helper inline; never push a merge/judgment decision into flowctl.
The bridge is off until explicitly enabled. The ceremony probes four signals, surfaces present AND absent, ASKS, and writes config only on confirmation — with provenance. No-signal ⇒ nothing written; enabled stays false. Never assume. But once the user confirms, enabling is opt-OUT, not opt-in: the ceremony activates the whole pipeline (every perEvent event) by default — hooking up the bridge means you want it to sync. The user excludes events at ceremony time or turns any off later (flowctl config set tracker.perEvent.<event> off). The get_default_config() schema default stays off, so a bare enabled=true set WITHOUT the ceremony activates no lifecycle-event sync (every perEvent event stays dormant) — only the ceremony's explicit writes activate them. (The lone exception: make-pr's PR↔issue link is unconditional whenever the bridge is active — no per-event gate, by design.)
Probe these four signals (detection lives in the skill, not flowctl — same shape as fn-51's driver-ladder detection):
| Signal | Probe | Means |
|---|---|---|
| Linear MCP registered | the host's MCP/tool list contains a Linear server (e.g. *Linear* tools like save_issue) | interactive Linear transport available (OAuth handled) |
| LINEAR_API_KEY | [ -n "$LINEAR_API_KEY" ] | headless Linear GraphQL transport available |
| GitHub auth | gh auth status exits 0 | headless GitHub transport available |
| Jira host | a *.atlassian.net host configured/visible | Jira present (out of scope here — surface but don't offer) |
Resolution model is env > config > ASK, mirroring cmd_review_backend (flowctl.py:4859): if the transport/tracker is already decided by env or config, don't re-ask. Steps in steps.md Phase 1.
On confirmation only, write via flowctl config set (dot-paths are safe — config keys are nested):
$FLOWCTL config set tracker.enabled true
$FLOWCTL config set tracker.type linear # or github
$FLOWCTL config set tracker.provenance "discovery ceremony 2026-06-03; confirmed by <who>; signals: MCP+API_KEY"
# DEFAULT-ON (opt-out): activate the whole pipeline — skip only what the user excluded.
$FLOWCTL config set tracker.perEvent.capture reconcile
$FLOWCTL config set tracker.perEvent.interview reconcile
$FLOWCTL config set tracker.perEvent.plan reconcile
$FLOWCTL config set tracker.perEvent.work.firstClaim push
$FLOWCTL config set tracker.perEvent.work.done comment
$FLOWCTL config set tracker.perEvent.makePr comment
$FLOWCTL config set tracker.perEvent.resolvePr comment
$FLOWCTL config set tracker.perEvent.completionReview reconcile
Confirm the result with flowctl sync active --json (must report active: true once enabled/type are set). Negative path: user declines ⇒ write nothing; sync active stays active: false.
Two entry flows, both attach sync state on link (never impose where the user must start):
fn-NN spec already exists (capture/interview/plan authored it). Push creates the tracker issue, then sync set-tracker-id attaches the issue UUID + --identifier WOR-17 + --url. Keep the fn-NN id; store the tracker key as a resolvable alias.flowctl spec create --tracker-first --tracker-identifier WOR-17), seed the merge base from the current issue body, first pass is pull-only. See steps.md Phase 2 (link) and references/identity.md.sync set-tracker-id); sync check-collisions flags any UUID shared by two specs.renderTaskChecklist hook on the body-sync path (off by default) so .4 can opt it in without reshaping the spine.The link/create ceremony assigns the canonical id through fn-52.10's generator. Never rename an existing spec. Full rules in references/identity.md; the headline:
wor-17-slug, canonical tasks wor-17-slug.M. The bare forms wor-17 / wor-17.M are aliases, resolved by fn-52.10's widened resolver (flowctl show wor-17, work wor-17, … all resolve). Branch follows the canonical id. Use flowctl spec create --tracker-first --tracker-identifier WOR-17.fn-NN-slug. Store the tracker key in the single tracker.identifier field (R4, display form WOR-17) as a resolvable alias via sync set-tracker-id --identifier WOR-17, and write the back-reference into the issue (flow:<id> label / [<id>] title-prefix).identifier in sync listings (see Phase 6 in steps.md).Three sync operations across three layers, all transport-blind. The skeleton routes; the named hooks plug in later:
push flow → tracker (writeIssue/setStatus/postComment from the normalized spec view)
pull tracker → flow (fetchIssue/readStatus/listComments → normalized → fold into spec)
reconcile two-way (3-way body merge + status who-wins + comment append)
fetchIssue, writeIssue, listComments, postComment, readStatus, setStatus. Each maps its wire shape to/from the normalized structs. Defined in references/adapter-interface.md.issue / comment / status structs — never a transport detail. The 3-way body merge + format translation + scoped conflict is references/body-merge.md (fn-52.4); status who-wins is references/status-sync.md and comments/evidence append + dedup is references/comments-sync.md (fn-52.5).sync clear and posts a one-line detached comment to the issue (postComment). Skeleton in steps.md Phase 5.Every run emits a receipt (sync receipt --status …) and genuine conflicts queue (sync defer …) — never block (R11/R12). The transport choice (mcp / graphql / gh / none) is recorded on the receipt; when no transport is reachable, the run is a noop + receipt note (never a crash). Lifecycle runs are event-tagged (fn-57): the calling skill passes event: <perEvent-key> in the invocation, and every receipt that run carries --event — the tag flowctl sync check audits at end-of-skill. Manual runs carry no event tag (see steps.md Phase 0).
/flow-next:tracker-sync is DISTINCT from /flow-next:sync (= plan-sync, flow-next-sync skill). Never conflate them. The two are documented side-by-side (doc note lands in fn-52.8).data-ai
Render a cognitive-aid PR body from flow-next state and open via gh. Triggers on /flow-next:make-pr with optional spec id and flags (--draft, --ready, --no-mermaid, --base <ref>, --memory, --dry-run). Auto-detects spec from current branch when no id given. NOT Ralph-blocked — autonomous loops can surface a draft PR for human review.
data-ai
Render a cognitive-aid PR body from flow-next state and open via gh. Triggers on /flow-next:make-pr with optional spec id and flags (--draft, --ready, --no-mermaid, --base <ref>, --memory, --dry-run). Auto-detects spec from current branch when no id given. NOT Ralph-blocked — autonomous loops can surface a draft PR for human review.
testing
Synthesize the current conversation context into a flow-next spec at `.flow/specs/<spec-id>.md` via `flowctl spec create + spec set-plan` — agent-native, source-tagged, with mandatory read-back before write. Triggers on /flow-next:capture, "capture spec", "lock down what we discussed", "make a spec from this conversation", "convert conversation to spec". Optional `mode:autofix` token runs without questions and requires `--yes` to commit. Optional `--rewrite <spec-id>` overwrites an existing spec; `--from-compacted-ok` overrides the compaction-detection refusal; `--override-strategy` proceeds despite a contradiction with an active STRATEGY.md track (and prompts to record the override as a decision).
testing
Synthesize the current conversation context into a flow-next spec at `.flow/specs/<spec-id>.md` via `flowctl spec create + spec set-plan` — agent-native, source-tagged, with mandatory read-back before write. Triggers on /flow-next:capture, "capture spec", "lock down what we discussed", "make a spec from this conversation", "convert conversation to spec". Optional `mode:autofix` token runs without questions and requires `--yes` to commit. Optional `--rewrite <spec-id>` overwrites an existing spec; `--from-compacted-ok` overrides the compaction-detection refusal; `--override-strategy` proceeds despite a contradiction with an active STRATEGY.md track (and prompts to record the override as a decision).