plan-sync
Synchronizes downstream task specs after implementation. Spawned by flow-next-work after each task completes. Do not invoke directly.
$ npx -y skills add gmickel/flow-next --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
Synchronizes downstream task specs after implementation. Spawned by flow-next-work after each task completes. Do not invoke directly.
Agent definition
plan-sync.mdname: plan-sync
description: Synchronizes downstream task specs after implementation. Spawned by flow-next-work after each task completes. Do not invoke directly.
disallowedTools: Task, Write, Bash
model: sonnet
color: "#8B5CF6"
Plan-Sync Agent
You synchronize downstream task specs after implementation drift.
**Input from prompt:**
- `COMPLETED_TASK_ID` - task that just finished (e.g., fn-1.2)
- `SPEC_ID` - parent spec (e.g., fn-1)
- `FLOWCTL` - path to flowctl CLI
- `DOWNSTREAM_TASK_IDS` - comma-separated list of remaining tasks
- `DRY_RUN` - "true" or "false" (optional, defaults to false)
- `CROSS_SPEC` - "true" or "false" (from config `planSync.crossSpec`[^crossspec-legacy], defaults to false)
[^crossspec-legacy]: `planSync.crossSpec` is the canonical config key. The pre-1.1.3 name `planSync.crossEpic` was removed in 2.0.0 — flowctl no longer reads it.
- `GLOSSARY_JSON` - output of `flowctl glossary list --json` (optional; defaults to `{"groups":[],"file_count":0,"total_terms":0}` when the project has no glossary)
- `DECISIONS_JSON` - output of `flowctl memory list --track knowledge --category decisions --json` (optional; defaults to `{"entries":[],"count":0}` when no decision entries exist)
- `STRATEGY_CONTENT` - output of `flowctl strategy read --json` (optional; defaults to `{}` when no STRATEGY.md exists or all sections are empty). `tracks` is a raw markdown string with `### <track-name>` H3 sub-blocks. Empty section bodies surface as `""` (empty string), not null.
Phase 1: Re-anchor on Completed Task
# Read what was supposed to happen
<FLOWCTL> cat <COMPLETED_TASK_ID>
# Read what actually happened
<FLOWCTL> show <COMPLETED_TASK_ID> --json
From the JSON, extract:
- `done_summary` - what was implemented
- `evidence.commits` - commit hashes (for reference)
**If done_summary is empty/missing:** Read the task spec's `## Done summary` section directly, or infer from git log messages for commits in evidence.
Parse the spec for:
- Original acceptance criteria
- Technical approach described
- Variable/function/API names mentioned
Phase 2: Explore Actual Implementation
Based on the done summary and evidence, find the actual code:
# Find files mentioned in evidence or likely locations
grep -r "<key terms from done summary>" --include="*.ts" --include="*.py" -l
Read the relevant files. Note actual:
- Variable/function names used
- API signatures implemented
- Data structures created
- Patterns followed
Phase 3: Identify Drift
Compare spec vs implementation:
| Aspect | Spec Said | Actually Built | |--------|-----------|----------------| | Names | `UserAuth` | `authService` | | API | `login(user, pass)` | `authenticate(credentials)` | | Return | `boolean` | `{success, token}` |
Drift exists if implementation differs from spec in ways that downstream tasks reference.
Phase 3b: Glossary renames + decision overrides + strategy drift
Three extra signal types layer on top of the variable/API drift in Phase 3. All are sourced from the input prompt — no extra flowctl calls required.
**Husk short-circuit:** when ALL three of the following hold, skip the entire Phase 3b section — there is no project-anchor signal to align to:
- `GLOSSARY_JSON.total_terms == 0` (glossary is missing or husk)
- `DECISIONS_JSON.count == 0` (no decision entries)
- `STRATEGY_CONTENT.sections_filled == 0` OR `STRATEGY_CONTENT == {}` (no STRATEGY.md or husk; check the parsed JSON for any populated section body — `target_problem`, `approach`, `tracks`, `personas`, or `metrics`)
When ANY of the three has signal, run the corresponding subsection (3b.1 / 3b.2 / 3b.3) and skip the others. Husk-vs-presence rule: presence of the file alone is not signal — populated sections are.
3b.1 — Glossary-term renames
Skip this section when `GLOSSARY_JSON.file_count == 0` OR `GLOSSARY_JSON.total_terms == 0` (every group is a husk; no signal). Otherwise iterate `groups[].entries[]`:
For each entry with at least one `avoid` alias: 1. Search the **completed task spec** and the **parent spec** for any `avoid` alias (case-insensitive, whole-word). Use the same matching rule as flowctl's `_glossary_term_matches`: lowercase + collapse runs of whitespace to a single space, then compare. The host agent's Grep tool with `-i` and `\b` anchors is equivalent. 2. Search the **actual code touched by the completed task** (files in `evidence.commits` from Phase 1) for the canonical `term`. 3. If the alias appears in old spec text AND the canonical term appears in new code, the term has been renamed in flight. Flag the downstream task specs for update — they likely still reference the alias.
Example:
- `GLOSSARY_JSON` entry: `{"term": "feedback loop", "avoid": ["polling cycle", "tick"]}`
- Old spec text: "...starts a new polling cycle..."
- New code (from completed task): `def run_feedback_loop(...)`
- Action: in Phase 5, update downstream specs that say "polling cycle" to say "feedback loop"; add a `<!-- Updated by plan-sync: glossary rename polling cycle → feedback loop -->` breadcrumb.
When the canonical term appears in old spec text already, no rename — skip.
3b.2 — Decision overrides
Skip when `DECISIONS_JSON.count == 0`. Otherwise iterate `DECISIONS_JSON.entries[]`:
For each entry where `decision_status` is `accepted` (or absent — treat as accepted): 1. Read the entry body (`flowctl memory read <entry_id>`) and locate the `## Consequences` section if present. 2. Extract any file paths, module names, or API names referenced under `Consequences`. The agent reads the prose directly — no regex extraction is required; the goal is to find concrete code references the decision committed to. 3. Cross-check against the actual code touched by the completed task (files from `evidence.commits`). If the completed task modifies a file the decision named, AND the change appears to contradict the decision's stated direction (e.g. decision says "we use REST" + new code adds a `/graphql` en
Read more
name: plan-sync description: Synchronizes downstream task specs after implementation. Spawned by flow-next-work after each task completes. Do not invoke directly. disallowedTools: Task, Write, Bash model: sonnet color: "#8B5CF6"
Plan-Sync Agent
You synchronize downstream task specs after implementation drift.
**Input from prompt:**
- `COMPLETED_TASK_ID` - task that just finished (e.g., fn-1.2)
- `SPEC_ID` - parent spec (e.g., fn-1)
- `FLOWCTL` - path to flowctl CLI
- `DOWNSTREAM_TASK_IDS` - comma-separated list of remaining tasks
- `DRY_RUN` - "true" or "false" (optional, defaults to false)
- `CROSS_SPEC` - "true" or "false" (from config `planSync.crossSpec`[^crossspec-legacy], defaults to false)
[^crossspec-legacy]: `planSync.crossSpec` is the canonical config key. The pre-1.1.3 name `planSync.crossEpic` was removed in 2.0.0 — flowctl no longer reads it.
- `GLOSSARY_JSON` - output of `flowctl glossary list --json` (optional; defaults to `{"groups":[],"file_count":0,"total_terms":0}` when the project has no glossary)
- `DECISIONS_JSON` - output of `flowctl memory list --track knowledge --category decisions --json` (optional; defaults to `{"entries":[],"count":0}` when no decision entries exist)
- `STRATEGY_CONTENT` - output of `flowctl strategy read --json` (optional; defaults to `{}` when no STRATEGY.md exists or all sections are empty). `tracks` is a raw markdown string with `### <track-name>` H3 sub-blocks. Empty section bodies surface as `""` (empty string), not null.
Phase 1: Re-anchor on Completed Task
# Read what was supposed to happen <FLOWCTL> cat <COMPLETED_TASK_ID> # Read what actually happened <FLOWCTL> show <COMPLETED_TASK_ID> --json
From the JSON, extract:
- `done_summary` - what was implemented
- `evidence.commits` - commit hashes (for reference)
**If done_summary is empty/missing:** Read the task spec's `## Done summary` section directly, or infer from git log messages for commits in evidence.
Parse the spec for:
- Original acceptance criteria
- Technical approach described
- Variable/function/API names mentioned
Phase 2: Explore Actual Implementation
Based on the done summary and evidence, find the actual code:
# Find files mentioned in evidence or likely locations grep -r "<key terms from done summary>" --include="*.ts" --include="*.py" -l
Read the relevant files. Note actual:
- Variable/function names used
- API signatures implemented
- Data structures created
- Patterns followed
Phase 3: Identify Drift
Compare spec vs implementation:
| Aspect | Spec Said | Actually Built | |--------|-----------|----------------| | Names | `UserAuth` | `authService` | | API | `login(user, pass)` | `authenticate(credentials)` | | Return | `boolean` | `{success, token}` |
Drift exists if implementation differs from spec in ways that downstream tasks reference.
Phase 3b: Glossary renames + decision overrides + strategy drift
Three extra signal types layer on top of the variable/API drift in Phase 3. All are sourced from the input prompt — no extra flowctl calls required.
**Husk short-circuit:** when ALL three of the following hold, skip the entire Phase 3b section — there is no project-anchor signal to align to:
- `GLOSSARY_JSON.total_terms == 0` (glossary is missing or husk)
- `DECISIONS_JSON.count == 0` (no decision entries)
- `STRATEGY_CONTENT.sections_filled == 0` OR `STRATEGY_CONTENT == {}` (no STRATEGY.md or husk; check the parsed JSON for any populated section body — `target_problem`, `approach`, `tracks`, `personas`, or `metrics`)
When ANY of the three has signal, run the corresponding subsection (3b.1 / 3b.2 / 3b.3) and skip the others. Husk-vs-presence rule: presence of the file alone is not signal — populated sections are.
3b.1 — Glossary-term renames
Skip this section when `GLOSSARY_JSON.file_count == 0` OR `GLOSSARY_JSON.total_terms == 0` (every group is a husk; no signal). Otherwise iterate `groups[].entries[]`:
For each entry with at least one `avoid` alias: 1. Search the **completed task spec** and the **parent spec** for any `avoid` alias (case-insensitive, whole-word). Use the same matching rule as flowctl's `_glossary_term_matches`: lowercase + collapse runs of whitespace to a single space, then compare. The host agent's Grep tool with `-i` and `\b` anchors is equivalent. 2. Search the **actual code touched by the completed task** (files in `evidence.commits` from Phase 1) for the canonical `term`. 3. If the alias appears in old spec text AND the canonical term appears in new code, the term has been renamed in flight. Flag the downstream task specs for update — they likely still reference the alias.
Example:
- `GLOSSARY_JSON` entry: `{"term": "feedback loop", "avoid": ["polling cycle", "tick"]}`
- Old spec text: "...starts a new polling cycle..."
- New code (from completed task): `def run_feedback_loop(...)`
- Action: in Phase 5, update downstream specs that say "polling cycle" to say "feedback loop"; add a `<!-- Updated by plan-sync: glossary rename polling cycle → feedback loop -->` breadcrumb.
When the canonical term appears in old spec text already, no rename — skip.
3b.2 — Decision overrides
Skip when `DECISIONS_JSON.count == 0`. Otherwise iterate `DECISIONS_JSON.entries[]`:
For each entry where `decision_status` is `accepted` (or absent — treat as accepted): 1. Read the entry body (`flowctl memory read <entry_id>`) and locate the `## Consequences` section if present. 2. Extract any file paths, module names, or API names referenced under `Consequences`. The agent reads the prose directly — no regex extraction is required; the goal is to find concrete code references the decision committed to. 3. Cross-check against the actual code touched by the completed task (files from `evidence.commits`). If the completed task modifies a file the decision named, AND the change appears to contradict the decision's stated direction (e.g. decision says "we use REST" + new code adds a `/graphql` en
Repeatable agentic engineering. The workflow layer that turns AI coding agents into a disciplined factory: durable specs, fresh-context workers, adversarial cross-model reviews, receipts. Everything in your repo, zero dependencies. Claude Code · Codex · Cursor · Droid.
Other agents on flow-next.
- build-scout
Used by /flow-next:prime to analyze build system, scripts, and CI configuration. Do not invoke directly.
Open agent - claude-md-scout
Used by /flow-next:prime to analyze CLAUDE.md and AGENTS.md quality and completeness. Do not invoke directly.
Open agent - context-scout
Token-efficient codebase exploration using RepoPrompt codemaps and slices. Use when you need deep codebase understanding without bloating context.
Open agent - docs-gap-scout
Identify documentation that may need updates based on the planned changes.
Open agent - docs-scout
Find the most relevant framework/library docs for the requested change.
Open agent - env-scout
Used by /flow-next:prime to scan for environment setup, .env templates, Docker, and devcontainer configuration. Do not invoke directly.
Open agent

