Skip to content

Tracker-sync: project Flow spec dependencies as blocked-by relations (Linear + GitHub) — fn-64#176

Merged
gmickel merged 16 commits into
mainfrom
fn-64-tracker-sync-project-flow-spec
Jun 17, 2026
Merged

Tracker-sync: project Flow spec dependencies as blocked-by relations (Linear + GitHub) — fn-64#176
gmickel merged 16 commits into
mainfrom
fn-64-tracker-sync-project-flow-spec

Conversation

@gmickel

@gmickel gmickel commented Jun 17, 2026

Copy link
Copy Markdown
Owner

Spec: fn-64-tracker-sync-project-flow-spec · Tracker: FLOW-14 · Plan-review: SHIP · Completion-review: SHIP

What & why

Tracker-sync projects a spec's body, status, and comments to the board — but dependency edges stayed local-only: depends_on_epics was invisible on Linear/GitHub, so the board showed independent issues even when Flow knew one blocked another (this bit SapienXT — relations had to be hand-added as a stopgap).

This adds dependency projection: a depends_on_epics edge between two linked specs becomes a blocked-by relation between their issues — on both Linear and GitHub (scope extended from the original Linear-only issue per @gmickel) — idempotently, provenance-tracked, and never clobbering a relation a human added by hand.

One transport-blind hook (projectDepRelations, modelled on readiness projection) drives both adapters; only fidelity differs.

R-ID coverage

R-ID What Task
R1 Linear blocked-by relations (MCP save_issue blockedBy + GraphQL issueRelationCreate type: blocks) fn-64.3
R2 GitHub native REST blocked_by deps (GA Aug 2025) + fenced <!-- flow:deps --> body-block fallback fn-64.4
R3 Idempotent re-sync (read-before-write; Linear relations+inverseRelations canonicalized) fn-64.3/.4
R4 Missing dep-link → named warning, sync continues fn-64.1/.5
R5 done dependency stays visible but never re-gates ready=true fn-64.5
R6 Never removes a relation not in our ledger / fenced marker fn-64.5
R7 Provenance-tracked depRelations ledger; list-dep-relations; projected keyed off the directed tracker edge fn-64.1
R8 Transport-blind hook (setIssueRelation/listIssueRelations); self-edge skip; cycles = direct edges, no traversal fn-64.2/.5
R9 flowctl unit coverage: add, idempotent rerun, missing-link, completed-blocker, bare-N id, self-edge, relink fn-64.1
R10 GitHub fenced-block ↔ body-merge ownership: trackerBodyForMerge strips before hash/merge-base; collision→queued/defer fn-64.5
R11 Docs (tracker-sync.md, flowctl.md, adapter refs, body-merge), GLOSSARY, CHANGELOG, 2.1.0 bump, Codex mirror, flow-next.dev fn-64.6

Critical changes — where to look

  • plugins/flow-next/scripts/flowctl.py (+~300) — depRelations ledger in default_spec_tracker_state(), opaque hashed edge keys (never inlines a raw issue key — auto-linkify pitfall), sync list-dep-relations / set-dep-relation / clear-dep-relation, bare-N tracker-identifier widening. dep_status is the local dep-spec status (not a remote fetch); projected keys off the directed tracker edge so a relinked issue reads un-projected.
  • skills/flow-next-tracker-sync/references/{adapter-interface,linear-mcp,linear-graphql,linear-ladder,github,body-merge}.md — the relation transport contract + both adapter rungs (docs-as-implementation: the adapter is the procedure the host agent runs). body-merge.md gains the trackerBodyForMerge strip so the GitHub fenced block never reads as tracker divergence.
  • skills/flow-next-tracker-sync/{steps.md,SKILL.md}projectDepRelations on push + reconcile: warnings, completed-blocker, never-clobber, collision-before-per-side (queued/defer), self/cycle.
  • plugins/flow-next/tests/test_tracker_sync_state.py (+~240) — pure-stdlib unittest; covers the ledger, idempotency, missing-link, completed-blocker, bare-N, self-edge, and the relink regression (projected→false after a dependency relinks).
  • Codex mirror regenerated (plugins/flow-next/codex/…); docs + GLOSSARY (3 terms) + CHANGELOG; plugin 2.0.0 → 2.1.0. flow-next.dev updated in its own repo (gmickel/flow-next.dev@c9d407b).

Decision context

  • Both adapters, one hook — keeping the skill transport-blind (R8) means dependency logic lives once; both trackers inherit it like body/status/comments already do.
  • Provenance over diff-reconcile — neither platform records relation authorship, so we only ever touch edges we can prove we created (ledger / fenced marker). Wrongly deleting a human's relation is high-cost and silent; the collision rule defers rather than recreate a human-removed edge.
  • Bug captured during review: [[gh-api-f-stringifies-numeric-body-2026-06-17]] — GitHub native blocked_by POST needs gh api -F (numeric issue_id), not -f (string) → 422. Caught by the fn-64.4 rp review; fixed + memory'd.

Verification

  • python3 -m unittest discover -s plugins/flow-next/tests1088 tests OK (2 skipped).
  • Live: flowctl sync list-dep-relations fn-64 resolves fn-52→FLOW-3 / fn-57→FLOW-10 / fn-58→FLOW-7, dep_status=done, projected=false.
  • Live gh api probe confirmed the GitHub native blocked_by endpoint shape against gmickel/flow-next.
  • Plan-review (Codex GPT-5.5): SHIP after 2 fix rounds. Completion-review (Codex): SHIP after closing the relink-projected correctness gap + flow-next.dev.
  • Codex mirror validators green; scripts/bump.sh minor applied across all five manifests.

🤖 Generated with Claude Code via /flow-next:make-pr

gmickel added 14 commits June 16, 2026 17:02
…ation/listIssueRelations + relation struct

- Add the two relation methods to the transport table (now 8 methods), tagged fn-64.3/fn-64.4
- New `relation` struct {from, to, type, source} with source provenance semantics
- Direction convention stated once: blocked-by = current issue blocked by dep issue
- Read-before-write idempotency (mandatory) + never-delete-non-ours provenance (ledger / fenced marker)
- Cross-link to fn-64.1 depRelations ledger, fn-64.4 body-merge fenced-block exclusion, completed-blocker rule

Task: fn-64-tracker-sync-project-flow-spec.2
…e blockedBy + GraphQL issueRelationCreate, read-before-write dedup

- linear-mcp.md: setIssueRelation via save_issue blockedBy (append-only, live re-verified 2026-06-17) + listIssueRelations via get_issue includeRelations:true; read-before-write rationale
- linear-graphql.md: issueRelationCreate(type:blocks) with correct operand inversion (issueId:B blocker, relatedIssueId:A blocked); dedup queries BOTH relations+inverseRelations (explicit first:) canonicalized to one direction
- linear-ladder.md: relation pair routed through MCP/GraphQL/no-op ladder; per-capability MCP-schema-drift fallback (MCP absent -> GraphQL if LINEAR_API_KEY else noop); parity table + no-op rung extended to eight methods
- never-delete-non-ours: setIssueRelation only ever adds; depRelations ledger is provenance authority

Task: fn-64-tracker-sync-project-flow-spec.3
…locked_by deps + fenced #N body-block fallback

- Native path: GET/POST /repos/{o}/{r}/issues/{n}/dependencies/blocked_by via gh api
  (API 2026-03-10, GA Aug 2025); issue_id resolved to numeric DB id; 50/type cap;
  blocked_by-only-writable; DELETE optional/no-delete-default (R6)
- Feature-detect GET probe ([] = available-empty, 404/410 = fallback)
- Fallback: provenance-fenced <!-- flow:deps --> #N body block, rewritten only
  inside markers, idempotent (R3); #N/owner-repo#N/bare-N parse on read
- Read-before-write dedup on both paths; never-delete-non-ours (ledger native,
  fence fallback); completed-blocker stays visible, no ready=true regress (R5)
- Capability-parity table + relation fidelity note (native vs fenced = identical
  normalized relation[]) extend the R13 transport-blind proof
- All native facts verified vs official REST docs + live gh api probe (2026-06-17)

Satisfies R2, R3.

Task: fn-64-tracker-sync-project-flow-spec.4
…numeric issue_id), not -f (string) — would 422

Review: -f/--raw-field sends issue_id as a JSON string; GitHub requires a numeric DB id. -F/--field types bare integers as JSON numbers.

Task: fn-64-tracker-sync-project-flow-spec.4
… review fix)

Task: fn-64-tracker-sync-project-flow-spec.4
Task: fn-64-tracker-sync-project-flow-spec.4
- steps.md: projectDepRelations on push + reconcile (modelled on projectReadiness);
  edge enumeration via sync list-dep-relations, missing-link warnings (fn-57 grammar),
  read-before-write project + ledger provenance, completed-blocker keep-not-regate (R5),
  strictly-additive never-clobber, collision-before-per-side-rules → defer + queued (R6/R10),
  self-edge/cycle handling no-traversal (R8), transport-unreachable noop, real receipt enum
- body-merge.md: Step 0.5 tracker-body-for-merge transform — strip <!-- flow:deps --> at the
  hash boundary before all baseHashTracker/mergeBaseTracker/fetch comparisons; Fixture E oracle (R10)
- SKILL.md: flowctl-owns/skill-owns table rows + relation interface + dependency-projection
  boundary (additive, no re-gate, no traversal, transport-blind)

Task: fn-64-tracker-sync-project-flow-spec.5
… + 2.1.0 bump + codex mirror

- docs/tracker-sync.md: new Dependency-projection section (direction, per-adapter fidelity, idempotency, provenance ledger, body-merge ownership, completed-blocker, warnings, collision rule)
- docs/flowctl.md: list-dep-relations / set-dep-relation / clear-dep-relation subcommands + bullets
- GLOSSARY.md: dependency projection, provenance ledger, completed-blocker rule
- CHANGELOG.md: 2.1.0 feature entry (fn-64 / FLOW-14)
- scripts/bump.sh minor 2.0.0 -> 2.1.0 across all manifests; README badge
- codex mirror regenerated (sync-codex via bump.sh) — tracker-sync references .2-.5

Task: fn-64-tracker-sync-project-flow-spec.6
Task: fn-64-tracker-sync-project-flow-spec.6
…rected tracker edge, not dep_spec — stale after relink (fn-64 R7/R9)

Completion-review finding: projected derived from dep_spec membership would
report true for an edge whose endpoint was relinked to a different issue.
Now computes the edge key from both current tracker ids + requires both linked.
Adds relink regression test.
@gmickel gmickel marked this pull request as ready for review June 17, 2026 05:50
@gmickel

gmickel commented Jun 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 351393fafd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/flow-next/skills/flow-next-tracker-sync/steps.md Outdated
gmickel added 2 commits June 17, 2026 08:22
…- flow:deps --> block on full-body update (fn-64, PR #176 review)

renderFlowToTracker never emits the dep block, so a raw full-body gh issue edit
wiped it — projectDepRelations then misread the ledgered edge as a remote removal
and queued a false collision (self-deleting projected deps). writeIssue UPDATE now
reads the current body + carries the fenced block forward (write retains, merge
strips per body-merge Step 0.5). Codex mirror regenerated.
@gmickel

gmickel commented Jun 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 8ff0e50f46

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@gmickel gmickel merged commit 2f1faf3 into main Jun 17, 2026
4 checks passed
@gmickel gmickel deleted the fn-64-tracker-sync-project-flow-spec branch June 17, 2026 06:56
gmickel added a commit that referenced this pull request Jun 17, 2026
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.

1 participant