Flownix
Разделы
На этой странице

flownix-basics

Read FIRST before any Flownix / AI-Flow work over MCP, and before any other flownix-* skill. Use when the request mentions Flownix, AI-Flow, a node slug (AF-TASK-12, ACME-FEAT-3), a ticket/задачу/тикет/эпик/фичу in the tracker, the .flownix

Flownix Basics

Flownix is a hierarchical product-planning system, reached only through the Flownix MCP server. Every other Flownix skill builds on this one.

The hierarchy

Every unit of work is a node, in a strict tree:

project → epic → feature → plan → task

  • project — the product/initiative (create_project).
  • epic — a large body of work.
  • feature — a shippable capability.
  • plan — an implementation plan for a feature.
  • task — a concrete executable unit (the leaf where real work happens).

get_tree shows the structure, list_nodes lists by kind/parent, get_node loads one node with content, comments, tags and dependencies.

Status lifecycle

Drive it explicitly with update_node_status:

backlog → planning → in_progress → testing → regression → done ↘ blocked ↗

  • backlog not started · planning being decomposed · in_progress active · testing implemented, under verification · regression verification found issues · done complete and verified · blocked cannot proceed (always explain in a comment).

update_node_progress sets 0..100 for finer tracking on long work.

The ticket is your memory

You are stateless between sessions. The node IS your durable memory.

  1. Read before actingget_node for content and prior comments. Never assume.
  2. Write down what you didadd_comment after any meaningful step.
  3. Keep status/progress honest — the board must reflect reality.

Comment prefixes, so logs stay scannable: [plan] · [progress] · [decision] · [blocker] · [review] · [done]

System components — binding is mandatory

Besides the work tree, a project describes the system being built: its services, apps, libraries and infrastructure. Each is a component with its own slug (AF-COMP-3).

A component is not a unit of planning: it executes nothing, never moves to done, and is outside the epic→feature→plan→task tree. It groups work and reflects its state. Its own lifecycle is lifecycle (planned/active/deprecated), never status.

Not a tag: a tag has no description, no tree, no repository path, no progress and no page.

FieldMeaning
titlethe name it has in the repository: core-service
descriptionpurpose, boundaries, entry points
component_kindservice | app | library | infra | integration
repo_pathservices/core-service
lifecycleplanned | active | deprecated
parentparent component; components form a tree (monorepo → group → service)

The rule

Every node and every doc you create or close must be linked to at least one component.

  • Links are many-to-many — one node may touch several components, and an architectural spec describing gateway and core-service belongs to both. Link all of them, not the first.
  • Docs and specs are bound too, not only tasks: a doc node without a component is unreachable from the part of the system it documents.
  • No suitable component? Create it with create_component (title = the real name in the repo, repo_path, component_kind, parent when it nests) and link to that. Never leave work unlinked because the inventory is incomplete — an unlinked node is invisible in the system view and its progress is counted nowhere.
  • Never invent a component that does not exist in the repository. If you cannot point at the code it stands for, you are inventing structure — check the repo first.
  • The exception is a genuinely cross-cutting node that belongs to no part of the system (a release checklist, a process ticket). Say so in a [decision] comment rather than leaving it silently unlinked.

Infrastructure and external services are components too, not tags: postgres, qdrant, the cluster, the pipeline (infra), the LLM provider, object storage (integration). Migrations, reindexing, deploy and quota work belongs to them, and that is what makes "everything we did to the database" a question with an answer. The languages and libraries a component is written in stay a tag plus a line in its description.

For a first pass over a project that has none — code and stack alike — use map-components.

Tools

ToolPurpose
list_componentsComponents of a project, flat with parent_id, each with its progress
get_componentOne component with children, linked tasks, docs and reviews
create_component / update_componentCreate and edit (project_id, title, …)
set_node_componentsReplace the set of components a node is linked to
set_review_componentsThe same for a review
shell
list_components({ project_id })                     // always check first
create_component({ project_id, title: "core-service", component_kind: "service",
                   repo_path: "services/core-service" })
create_node({ ..., components: ["AF-COMP-3", "AF-COMP-7"] })   // link at creation
set_node_components({ node: "AF-TASK-12", components: ["AF-COMP-3"] })  // replaces the set
list_nodes({ project_id, component: "AF-COMP-3" })  // direct links only
list_docs({ project_id, component: "none" })        // what is still unsorted
rag_query({ project_id, query: "…", component: "AF-COMP-3" })

Two things that are easy to get wrong:

  • Lists filter by DIRECT links; progress counts recursively down the component tree — work on gateway shows up in services and in the monorepo above it. Deliberate: a list answers "what did I link here", progress answers for a part of the system as a whole.
  • set_node_components replaces the set, it does not add. Pass the existing links too unless you mean to drop them.

Progress is computed, never set. list_components/get_component return percent with total, done, a status breakdown and counts of linked docs and reviews.

MCP tool reference

Most tools accept node_id or slug. A slug alone is enough — it carries the project key (MF-TASK-274 → project MF) and is unique across the org, so project_id is optional wherever a slug is accepted. Prefer slugs.

ToolPurpose
list_organizations / create_organizationList orgs the user belongs to / create one
list_projectsProjects in an organization (org_id)
create_projectCreate a project (org_id, name, key, spec Markdown)
get_treeFull hierarchy of a project (project_id)
list_nodesList nodes (project_id, optional kind, parent_id/parent_slug, component, ticket_ref — exact match on an external tracker ticket)
get_nodeOne node + content, comments, tags, deps, child_ids; returns commit_hash, ai_model, agent_name, author_email, timestamps
get_node_by_slugSame, slug-only (slug, optional project_id)
create_nodeCreate epic/feature/plan/task (project_id, parent_id/parent_slug, kind, title, content, optional tags, components, ai_model, agent_name, ticket_ref)
update_node_contentUpdate title/content (optional commit_hash, ticket_ref)
update_node_status / update_node_progressSet status / progress 0..100
add_commentAppend an agent comment (Markdown body)
add_tagTag a node
add_dependency / remove_dependencyEdge read as "from_node depends on to_node": to_node is the blocker
get_dependenciesdepends_on (blockers) and blocks (waiters) for one node
set_references / get_referencesSet (overwriting) / read cross-references (ref_ids, ref_slugs; a ref may live in another project)
add_relation / remove_relation / get_relationsTyped bidirectional relations (relation_type)
list_components / get_component / create_component / update_componentSystem components — see above
set_node_components / set_review_componentsReplace a node's / review's component set
get_project_policy / set_project_policyRead / set spec_mode (off/strict), require_proposal_approval and spec_first; org members only, never silently
validate_specCheck a spec against the OpenSpec format — by doc_slug or by raw body; changes nothing
propose_doc_deltaPropose the complete next body of a doc, linked to a source task/plan
update_doc_delta / submit_doc_deltaEdit a draft (or rejected) delta in place with the full body / send a draft to review — the only draft → pending transition
get_doc_deltaOne delta in full, with its round history. The list call does not return rounds
list_doc_deltas / apply_doc_deltaList revisions (each carries review_status) / apply one with optimistic version checking
approve_doc_delta / reject_doc_deltaHumans only. A call from an agent session is refused — see The approval gate
discard_doc_delta / delete_doc_deltaRecord a rejected revision / remove a garbage row (applied is refused)
declare_no_spec_impactRecord why a task/plan changed no documented behaviour
create_doc_node / update_doc_content / list_docsDoc nodes
archive_doc / restore_doc / list_archived_docsArchive cascades down the subtree; restore returns exactly one doc
rag_query / rag_context / rag_statusSemantic search, assembled context block, index freshness. Read-only
list_action_eventsProject audit trail: who did what, when, through which channel. Read-only

Deltas: discard records a decision, delete removes a mistake

discard_doc_delta = "we considered this and decided against it" — the row stays, marked discarded, and the history shows the proposal was handled. This is the normal way to say no. delete_doc_delta = "this should never have existed": a duplicate, an accidental proposal. An applied delta cannot be removed at all — it is spec history; propose a new one on top.

Documents: archive, not delete

delete_node on a doc is irreversible and takes its revisions with it. Use archive_doc: the doc leaves the tree, list_docs and rag_query, but revisions, comments and references stay. Archiving cascades down the subtree, restoring does not — read the archive response, it names every slug that went along. A doc missing from list_docs is not proof it was deleted: check list_archived_docs first.

Dependencies: mind the direction

Always read as "from_node depends on to_node"to_node is the blocker.

shell
// "Register the skill" can only be done after the skill file exists:
add_dependency(from_slug: "AF-TASK-200", to_slug: "AF-TASK-199")
//              ^ waits                   ^ blocker, done first
get_dependencies(slug: "AF-TASK-200")  // → depends_on: [AF-TASK-199], blocks: []

The common mistake is calling it in the order the work happens and getting the edge backwards. Say the sentence out loud: the node you name first is the one that waits. get_node(...).dependency_ids likewise lists blockers. Dependencies are the ordering graph; typed relations (add_relation) express semantics — don't duplicate one as the other.

Slugs and navigation

shell
get_node(node_id: "abc123")                       // all three are equivalent
get_node(slug: "ACME-TASK-1", project_id: "…")
get_node(slug: "ACME-TASK-1")
create_node(parent_slug: "ACME-PLAN-1", kind: "task", title: "…", components: ["AF-COMP-3"])
set_references(slug: "MF-FEAT-68", ref_slugs: ["MF-DOC-7", "AF-TASK-217"])

An unresolvable slug returns NotFound naming it — fix that slug rather than hunting for IDs. get_node returns child_ids, so you can walk the tree without get_tree.

RAG — semantic search over the project

Every project has a semantic index built automatically from node content and comments:

  • rag_query — ranked hits by meaning (slug, kind, title, content, status, score); narrow with kind_filter or component.
  • rag_context — top chunks pre-assembled into one Markdown block for prompt injection.
  • rag_statustotal_chunks, last_indexed_at and the sync breakdown (pending/in_progress/synced/failed/skipped).

Reach for RAG whenever you need to find something. Before planning a feature, before creating any node, when picking up a task whose context you don't hold, when the user names work by description rather than slug, and whenever you are about to guess. Two or three queries with different phrasings routinely prevent a duplicate ticket.

Recall, not truth — hits are leads, possibly seconds stale. Confirm with get_node. Sync is automatic; there are no indexing tools. A node you just wrote may not be searchable yet, so never verify your own write with rag_query — use get_node. Empty results mean "not found right now": check rag_status before concluding something doesn't exist. On rag-service unavailable, degrade to structural tools and say so.

Query patterns and per-tool arguments: flownix-rag-search.

The action log

Every significant mutation is recorded project-wide, independent of any node's comments. list_action_events(project_id, limit: 10) — or scoped with entity_id — tells you what happened recently and who did it. Useful when picking up work another agent left mid-flight. Comments are the why; the action log is the what/when/how.

Presence

While connected over MCP you appear in a live presence panel — nothing is required of you: the session registers on connect, every call keeps it alive, and the task is inferred from the node_id/slug you pass. One optional call makes the row informative:

shell
session_announce({ agent_name: "Claude Code", ai_model: "Claude Opus 5",
                   host: "…", cwd: "…", git_branch: "…" })

session_report (tokens/cost, normally from a statusLine script) and session_end also exist. Reads carrying no node (rag_query, get_tree) keep the session alive but don't change which task you're shown on — searching is not switching.

Harness — multi-agent workflow orchestration

A separate subsystem of reusable multi-agent workflow templates (council, review board, debate) that you define, publish and run. Its MCP tools use dotted names (harness.create, work.claim, council.submit_proposal) — distinct from the underscore-named node tools. workspace_id on harness tools is the Flownix project_id; there is no separate workspace entity and no FK validating it, so getting it right is on you.

Concepts

  • Harness definition (harness_id) — the named template.
  • Version (harness_version_id) — a draft or published snapshot: graph, roles, policies, schemas. Sequential; one published at a time.
  • Role — objective, system prompt, responsibilities, prohibitions, instance bounds. Referenced by key from workflow nodes.
  • Workflow node — one step; typeagent_task, parallel_group, review, debate, vote, judge, human_gate, transform, create_flow_nodes, condition, loop, end.
  • Policy — category (tools/data_access/voting/budget/execution/approval/ retention/privacy) + effect allow/deny/require_approval; deny wins.
  • Run (run_id) — one execution; starting it creates work items for agent-needing nodes.
  • Work item — a claimable unit with a lease. source_node_ids/result_node_ids are plain JSONB, no join table — if you need the run↔node link discoverable, record it yourself (add_comment with the run_id, or set_references).
  • Deliberation — proposals, claims with evidence, critiques (info/minor/major/blocking), revisions, then a ballot or a judgement.

Build → publish → run

  1. harness.create (workspace_id, name, type)
  2. harness.create_version (draft)
  3. harness.add_role × N, harness.add_workflow_node × N, harness.connect_workflow_nodes
  4. harness.set_policy (optional)
  5. harness.validate — missing roles, disconnected nodes, cycles, policy conflicts
  6. harness.publish
  7. harness.start_run — pass source_node_ids to link it to the task/feature it operates on

harness.clone deep-copies a definition as a new draft. harness.export produces a ZIP (harness.yaml, roles.md, rules.yaml, workflow.json, prompts, README) — URL expires in 1 hour.

To build one: harness-blueprint. Once a run is live: flownix-harness-participant (you hold a role) or flownix-harness-moderator (you hold judge).

Harness tools

ToolPurpose
harness.create / harness.getCreate a definition / read it with version details
harness.create_versionOpen a new draft version
harness.add_role / harness.update_roleAdd or edit an agent role
harness.add_workflow_node / harness.connect_workflow_nodesBuild the graph
harness.set_policyAdd a policy
harness.validate / harness.publishCheck a draft / make it active
harness.clone / harness.exportDeep-copy / export as ZIP
harness.start_runStart a run of a published version
work.list_available / work.claim / work.heartbeatFind, lease, extend
work.complete / work.failFinish with result artifacts / fail with an error
council.submit_proposal / council.revise_proposalSubmit / supersede a proposal
council.submit_claim / council.attach_evidenceAttach a claim / evidence
council.submit_critique / council.respond_to_critiqueCritique / accept-reject-revise
council.cast_vote / council.abstainVote on a ballot
council.submit_judgementIssue the authoritative decision as judge

The project config file

Every skill reads project context from .flownix in the working directory, created by flownix-init:

shell
spec_mode: "off"
org:     { id: "<org_id>", name: "<org_name>" }
project: { id: "<project_id>", name: "<name>", key: "<KEY>", spec: "<markdown>" }

Read it before any MCP call. The product was renamed, so both names are accepted:

shell
cat .flownix 2>/dev/null || cat .ai-flow
  1. .flownix exists → use it (if .ai-flow is also there, say once it is now redundant).
  2. Only .ai-flow → use it, and say once: "the config is now .flownix; rename when convenient — the old name keeps working."
  3. Neither → run flownix-init.

Once per session, not per call. Parse the YAML for project_id, org_id, project.key — never ask the user for IDs. If parsing fails or the project_id no longer resolves, re-run flownix-init; new files are always written as .flownix.

Spec mode and doc deltas

off keeps the ordinary workflow. In strict, a task or plan reaches done only after an applied doc delta sourced from that node, or an explicit declare_no_spec_impact with a real reason. A delta is the complete resulting document, never a patch or excerpt: reread the doc, preserve everything still true, make the change, propose and apply that full body. That is what makes the server's revision history and diffs reliable.

The approval gate

Every proposed delta carries a review_statusdraft, pending, approved or rejected — separate from its state (proposed/applied/discarded). state says whether the change reached the spec; review_status says whether a human looked at it. A delta is routinely proposed and already approved: that pair is what "you may start now" looks like.

When the project has require_proposal_approval on (get_project_policy tells you, alongside spec_mode), the server refuses two things until the covering delta is approved:

  • moving the covered task or plan into in_progress, and
  • apply_doc_delta.

Both refusals come back as FailedPrecondition prefixed proposal_not_approved. The gate looks up the tree: a delta proposed from a feature or plan blocks the tasks underneath it. It does not require that a proposal exist — it only refuses to let one past review.

draft is the odd one out: it means nobody has been asked yet. It appears only in projects with spec_first on, it stands in no queue and holds no gate, and it becomes a question to a human only when submit_doc_delta makes it pending. See Spec-first below.

You cannot approve anything. approve_doc_delta and reject_doc_delta refuse every call from an agent session with PermissionDenied; the tools are listed so you can name the step you are waiting on. When a delta is pending, stop and tell the user which delta needs approval, then wait. Do not re-propose it, do not apply it, do not move the task into in_progress, do not delete it and start over — a rejected delta can still be approved later. When it comes back rejected, read review_note, rebuild the spec on that feedback, and propose a new delta.

The full procedure for authoring a change proposal is its own skill: flownix-write-spec.

Spec-first: the document before the tasks

A third policy flag, spec_first, reverses the order of work: the feature's document is written and accepted before any task is created under it. With it on, two things change.

  • A delta is born review_status: draft — a working copy that asks nobody anything — and reaches a human only through submit_doc_delta.
  • Creating (or moving) a task or plan under a feature is refused with FailedPrecondition prefixed spec_first_not_approved until that feature has an approved delta, or has declared no spec impact. Unlike the entry gate above, here the absence of a proposal is itself the violation.

spec_first only works together with spec_mode: strict and require_proposal_approval; the server refuses to enable it without them, and refuses to disable either while it is on. It is off by default, and with it off nothing above applies.

The procedure is its own skill — spec-first-change. It is not described here on purpose: this file is the map, and a second full account of the flow would be the copy that drifts.

The spec format

A spec describes observable behaviour, not a plan of work, and it is written in the OpenSpec format — the only accepted way to record behaviour in this system. Not a preference, not a default you may fall back from: freeform Markdown in a doc node is a defect, validate_spec reports it, and in spec_mode=strict any delta that adds a new violation is refused.

shell
# <Document title>

## Purpose
One or two sentences: what this part of the system is for.

## Requirements

### Requirement: <name>
The system SHALL <one observable behaviour>.

#### Scenario: <the case this covers>
- GIVEN <starting condition>
- WHEN <what happens>
- THEN <the observable result>

One requirement, one behaviour, one SHALL; every requirement needs at least one scenario; the how stays in the plan. The rules, validate_spec, propose_doc_delta and the approval gate are owned by one skill — flownix-write-spec. They are not restated in each skill on purpose: the same block used to sit in five of them, and five copies drift.

Golden rules

  • Read .flownix first (falling back to .ai-flow); discover IDs, never invent them.
  • Link every node and doc to at least one component; create the component if it is missing. See System components above.
  • One task = one executable unit. Too big → it is a plan/feature; split it.
  • Search before you create and before you guessrag_query/rag_context is the default way to find anything; confirm hits with get_node; if the index is cold (rag_status), say so instead of concluding nothing exists.
  • Set ai_model and agent_name on create_node (e.g. "Claude Opus 5", "Claude Code").
  • Set commit_hash via update_node_content after committing the work.
  • Write specs as ## Purpose / ### Requirement: / #### Scenario: — this format is mandatory, not preferred. Run validate_spec before proposing a delta. See The spec format above, and flownix-write-spec for the whole procedure.
  • Never start work on a task whose covering delta is not approved when the project has require_proposal_approval on. Stop and ask the user; you cannot approve it yourself. See The approval gate.
  • Never create tasks under a feature before its document is accepted when the project has spec_first on — the server refuses, and the refusal is not a bug to route around. See Spec-first and spec-first-change.
  • Set ticket_ref whenever an external ticket is mentioned (Jira/YouTrack/…), on create_node or update_node_content. Optional in the schema, mandatory in practice — but never invent one. list_nodes(ticket_ref: …) finds everything tied to a ticket.
  • After finishing a feature or epic, write the doc and link it back in the same stepcreate_doc_node, then set_references(slug: "<FEAT slug>", ref_slugs: ["<DOC slug>"]). Nobody finds a doc by browsing the doc list; they arrive from the feature. Creating the doc and forgetting the reference is the common failure.
  • Cross-link with set_references, not with text in content — references render as clickable links on both nodes. Each call replaces the set, so pass the full list.
  • Write plans, tickets and docs in the language of the user's prompt. Russian prompt → Russian ticket.
  • Harness tools are dotted and take workspace_id (= project_id) — don't mix namespaces.