---
name: flydocs-workflow
description: |
  Unified FlyDocs skill — lifecycle workflow, issue operations, project
  management, knowledge graph, and session context. Tier-aware: auto-detects
  cloud (relay API) or local (filesystem) from config.
triggers:
  - create issue
  - capture issue
  - log a bug
  - add to backlog
  - transition
  - move to
  - mark as
  - set status
  - assign issue
  - close issue
  - start session
  - wrap session
  - project update
  - status update
  - add comment
  - set priority
  - set estimate
---

# FlyDocs Workflow

Index for the FlyDocs development lifecycle, issue operations, project
management, knowledge graph, and session context.

This file tells you what to do next and where to look — it is not the
procedure. Read the stage file for the work in hand, and
`reference/script-catalog.md` for operation IDs and their arguments. Slash
commands (`/capture`, `/implement`, …) load stage files automatically; read them
manually only for natural-language requests that bypass slash commands.

## Lifecycle

```
Capture → Refine → Activate → Implement → Review → Validate → Close
                                  ↕
                               Blocked
```

## Stage Index

| Stage     | File                | Intent                   | Agent          |
| --------- | ------------------- | ------------------------ | -------------- |
| Capture   | stages/capture.md   | Create issue from input  | PM             |
| Refine    | stages/refine.md    | Triage + spec completion | PM             |
| Activate  | stages/activate.md  | Assign + start work      | PM             |
| Implement | stages/implement.md | Build + test + simplify  | Implementation |
| Review    | stages/review.md    | Code quality validation  | Review         |
| Validate  | stages/validate.md  | User acceptance testing  | QE             |
| Close     | stages/close.md     | Archive completed work   | PM             |

Session start, wrap, and stale detection: `session.md`.

## Surfaces: MCP for the Hot Path, `flydocs run` for the Long Tail

The ten operations the lifecycle runs on are MCP tools on the `flydocs` server.
Call them directly. Clients namespace them (`mcp__flydocs__issue_get`); this
skill uses the wire names.

Everything else — audits, milestones, labels, workspace and graph operations —
is the long tail, and runs through `flydocs run <operation-id>`.

Execute, either way. Never describe an operation instead of performing it.

### Invocation Matrix

Each stage file opens its procedure blocks with a **Surface** line naming which
of these carries the step.

| Need                               | Surface | Call                         | Script fallback                                |
| ---------------------------------- | ------- | ---------------------------- | ---------------------------------------------- |
| Read one issue                     | MCP     | `issue_get`                  | `issues.py get REF`                            |
| List issues in focus               | MCP     | `issue_list`                 | `issues.py list --focused`                     |
| Capture an issue                   | MCP     | `issue_create`               | `issues.py create`                             |
| Assign + start work (one call)     | MCP     | `issue_activate`             | `issues.py assign` then `issues.py transition` |
| Move status (comment required)     | MCP     | `issue_transition`           | `issues.py transition REF STATUS COMMENT`      |
| Post a comment                     | MCP     | `issue_comment`              | `issues.py comment REF BODY`                   |
| Tick / untick / defer / note an AC | MCP     | `issue_acceptance_update`    | `issues.py acceptance REF --check N`           |
| Load session continuity            | MCP     | `session_start`              | `session.py start-context`                     |
| End the session (posts the update) | MCP     | `session_wrap`               | `session.py wrap --health H --body-file F`     |
| Mid-session project update         | MCP     | `project_update`             | `session.py project-update --health H`         |
| What shipped around an issue       | MCP     | `change_context`             | none — MCP-only (provenanced issue→PR join)    |
| Everything else                    | runner  | `flydocs run <operation-id>` | the matching dispatcher script                 |

**The scripts still work.** Every rewritten instruction names the script that
does the same job — for one release cycle, and for harnesses with no MCP server
configured. Where both are available, use the tool.

### The Long Tail: `flydocs run <operation-id>`

Sixty-six operations, addressed by a stable ID in `domain.verb` form:
`issue.audit`, `milestone.create`, `workspace.validate`, `graph.query`. The ID
is the contract. It does not name a file, a directory, or an interpreter, so
none of those can break an instruction you have memorised.

```bash
flydocs run issue.audit --limit 20
flydocs run graph.query --node FLY-123 --rel BLOCKS --direction both
flydocs run --list                      # every ID, grouped by domain
```

`--list` is the discovery path and works from anywhere, including a multi-repo
root. For what a single operation accepts, read `reference/script-catalog.md` —
or get it wrong once: a refused invocation prints the accepted shape.

Three properties worth knowing before you use it:

- **No `cd`.** The runner finds the repo that owns `.flydocs/config.json` from
  the working directory, or takes `--repo <name>` in a multi-repo workspace.
  Run it from the workspace root, from a child repo, from any depth inside one.
- **Declared arguments only.** Every flag an operation accepts is registered.
  An unknown flag stops the invocation and prints what was accepted — it is
  never forwarded on the chance the script might understand it.
- **No shell.** Arguments are passed as an argv array. Quoting, `$(…)`, `&&`
  and `;` have no meaning anywhere in this path, so a value containing them is
  a value, not an injection.

`--dry-run` before the ID resolves the entrypoint and prints the argv without
running anything. Use it when you are unsure an invocation is well-formed.

The dispatcher scripts underneath still work when invoked directly; the runner
is a convention over them, not a replacement. `reference/script-catalog.md`
carries both spellings for every operation.

### Structured Arguments: No Temp Files, No Escaping

Tool arguments are structured data. A comment body, a filled wrap template, an
issue description — each is one string argument, and multi-line markdown goes
in as-is. Nothing needs a heredoc, a quoting pass, or a file on disk.

Before — the shell shape, with the body staged on disk (and staged in the wrong
place: `/tmp` is outside the workspace, so nobody can look at what you sent):

```bash
cat > /tmp/ready.md <<'EOF'
**Ready for Review** —

**What changed:** hot-path stage docs now call the tools
**Criteria:** 7/7 complete
EOF
python3 .claude/skills/flydocs-workflow/scripts/issues.py comment FLY-123 < /tmp/ready.md
```

After — `issue_comment`, called with:

```yaml
ref: FLY-123
body: |
  **Ready for Review** —

  **What changed:** hot-path stage docs now call the tools
  **Criteria:** 7/7 complete
```

The `body` is a single string parameter. Write the markdown into it directly.

### Long Inputs on the Runner: `.flydocs/scratch/`

Tools take the markdown inline; `flydocs run` is a command line, and a command
line is the wrong place for a filled wrap template or a rewritten issue
description. Heredocs, escaped newlines and nested quotes in an operation
argument are how a body arrives on the tracker mangled, or how a stray `"`
truncates it silently.

So: **write the body to a file under `.flydocs/scratch/`, pass the path, delete
the file.**

```bash
mkdir -p .flydocs/scratch
cat > .flydocs/scratch/fly-123-description.md <<'EOF'
## Problem

The runner rejects an undeclared flag before it spawns anything.

## Acceptance Criteria

- [ ] Unknown flags exit non-zero
- [ ] The error names what was accepted
EOF

flydocs run issue.description FLY-123 --file .flydocs/scratch/fly-123-description.md

rm .flydocs/scratch/fly-123-description.md
```

The heredoc writes the _file_. It is never the operation argument — the
argument is a path, which has no quoting problem to have.

Why this directory and not a system scratch path:

- **Workspace-local.** It sits beside the config the operation runs against, so
  a relative path works from wherever the runner put you.
- **Gitignored.** `.flydocs/scratch/` is in the managed ignore list. Drafts of
  issue content do not belong in a commit.
- **Inspectable.** When an operation refuses a body, the exact bytes you sent
  are still on disk and still readable by the person you are working with.

`--file` is the spelling to reach for. `issue.create` and `issue.update` also
accept `--description-file`, and `session.wrap` and `session.project-update`
also accept `--body-file`; `--file` is an alias of each, so one habit covers
all four. Clean up after a successful call — a scratch directory that
accumulates is a directory nobody trusts.

## Listing Issues: Focus First

`issue_list` with `focused: true` (script: `issues.py list --focused`) is the
default read — active sprint, else board, else your open assigned work. Focus
narrows attention, it does not restrict access; out-of-focus work stays
reachable on request.

| Use                                   | When                                                     |
| ------------------------------------- | -------------------------------------------------------- |
| `focused: true` / `list --focused`    | Default. What the developer is working on now.           |
| `mine: true, active: true`            | Their wider assigned backlog. Use for counts, not dumps. |
| `project: "<ref>"` / `list --project` | A specific project. Explicit opt-out from focus.         |
| `all: true` / `list --all`            | Everything. Rarely correct; say why you needed it.       |

## Reference

| Topic                     | File                                   |
| ------------------------- | -------------------------------------- |
| Operation catalog         | reference/script-catalog.md            |
| Comment templates         | reference/comment-templates.md         |
| Status transitions        | reference/status-workflow.md           |
| Priority & estimates      | reference/priority-estimates.md        |
| PR & git workflow         | reference/pr-workflow.md               |
| Golden rules              | reference/golden-rules.md              |
| Provider costs            | reference/provider-costs.md            |
| Graph schema              | reference/graph-schema.md              |
| Service descriptor schema | reference/service-descriptor-schema.md |
| Multi-repo workspaces     | reference/multi-repo.md                |
| CLI config (skipPaths, credentials) | reference/cli-config.md      |
| Writing style (copy/docs) | reference/writing-style.md             |
| Output formatting (full)  | reference/output-formatting.md         |
| Claude Code hooks         | reference/claude-hooks.md              |

Issue templates in `templates/issues/` (feature, bug, chore, idea) and PR
templates in `templates/pr/` are pure structure — stage files define how to
fill them.

### For Skill Authors

A skill can add its own operations to the runner: ship an `operations.json`
manifest in the skill directory and its IDs appear in `flydocs run --list`
alongside the core catalog, tagged with the skill they came from. The rules are
the same ones the core catalog lives by — `domain.verb` IDs, every flag
declared, `--repo` reserved — so a contributed operation is reached exactly like
a built-in one and inherits the same standing permission rule.

The manifest schema, the flag kinds the loader accepts, the reserved names and
what a contributed script inherits from the runner are documented in
**Contributing operations from a skill** (`docs/skill-operations.md`). Read it
before writing an `operations.json`: a manifest is validated at load, and an
entry that breaks one of those rules is dropped with a warning on stderr rather
than failing later at the call.

This skill contributes none. Its operations are core, and the note is here so a
skill author looking for the extension point does not go looking in the CLI.

## Golden Rules (Summary)

1. Every status transition gets a comment (see `reference/comment-templates.md`)
2. Assignment required before In Progress — `issue_activate` does both
3. Checkboxes live in issue description — change them with
   `issue_acceptance_update` (script: `issues.py acceptance`), never by
   rewriting the description
4. Use the tools — execute, don't describe
5. Session wrap posts a project update — `session_wrap` posts it itself
6. Product scope filtering — only show projects/issues matching config labels

For full rules with verification gates, see `reference/golden-rules.md`.
