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

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.
  • bug — a defect fix. It stands at feature level: its parent is a project, an epic or a group (never a task or a feature), and it holds only plans, tasks and groups. Its slug is <KEY>-BUG-N (AF-BUG-1). Use it instead of a feature when the work restores behaviour the spec already describes (see plan-feature). Filter with list_nodes / rag_query kind=bug.
  • group — a free-form container for grouping anything with anything. It is not a level of the hierarchy: it can stand under any node (or at the project root) at any depth and can hold any nodes, including other groups. Its slug is <KEY>-GRP-N (AF-GRP-1). It carries no spec or status rules of its own: a task under a group is not gated by a feature's spec. Create one with create_node kind=group, then move_node the nodes that belong in it. Filter with list_nodes / rag_query kind=group.
  • release — a release marker. It stands only at the project root (parent = the project). Slug <KEY>-REL-N. Create it with create_node kind=release.
  • plan — an implementation plan for a feature or a bug.
  • 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.

Project profile and process settings — read them before working

get_project_policy returns more than the spec policy. Read these fields once per session and follow their *_requirement texts — the server enforces what it can, but finding the rule out from a refusal wastes the user's turn.

  • profile — simple or full. A simple project is just the node tree and tasks: skip component binding, doc nodes, spec deltas and harness, and do not propose strict mode. The server does not refuse those calls, but the web UI of a simple project hides their results. If the user asks for specs, components or reviews, offer to switch the project to full (set_project_policy profile=full, with their consent). Strict spec mode requires full; combining them is refused with profile_conflict.
  • execution_order — parallel (default) or sequential. In sequential, a task cannot enter in_progress while the previous task of the same parent (tree order) is not done — refused with workflow_order. Take tasks in order; if the previous one is waiting for review, stop and tell the user.
  • completion_flow — direct (default) or review. In review, finish a task in testing and tell the user it awaits their check: only a human moves it to done. An agent's done is refused with workflow_review_required — including closing a feature or plan whose tasks are not yet accepted, because closing a parent closes its children.

The ticket is your memory

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

  1. Read before acting — get_node for content and prior comments. Never assume.
  2. Write down what you did — add_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]

Working efficiently — tokens, planning, delegation

Every skill in this set follows these rules. Tokens are the budget; spend them on decisions, not on re-reading.

Spend fewer tokens

  • Read once, narrow, by slug. One get_node per node per session; keep what you learned in your working notes instead of re-fetching. Prefer rag_context (one assembled block) over five get_node calls to "get oriented"; use get_node to confirm a hit you will act on.
  • Never list wide. list_nodes / get_tree default to 200 rows: pass kind, status, parent_slug, component, limit/offset. Ask for the slice you need, not the project.
  • Batch independent calls in one turn (several get_node, rag_query, set_node_components). Sequence only what depends on an earlier result.
  • Write short, write once. A comment is a dense record: marker, what changed, result, next step — not a transcript. Files, diffs, logs and reports go to the Artifactory and are cited by slug (AF-ARTF-12), never pasted into a comment or a handoff.
  • Hand over references, not content. When you delegate, pass slugs, file paths and the exact question — the receiver reads what it needs. Pasting the ticket, the spec and half a file into a prompt pays for those tokens twice.
  • Don't verify with prose. A failing/passing test, validate_spec or harness.validate is cheaper and more reliable than a model reading its own diff.
  • Keep the main context clean. Exploration that returns "many files, one conclusion" (broad searches, log digging, library research) goes to a sub-agent that returns a short answer; compact or restart the session between unrelated nodes — the ticket is the memory, so nothing is lost.

Plan so the work is cheap to execute

  • Size a task to one executor's context. A task is a unit one agent finishes with the task text, a handful of files and its tests — roughly: ≤ 5–6 files to edit, one component, one acceptance check per behaviour. Bigger → split before creating it. A task that needs the whole system in mind is a plan.
  • Make every task self-contained: goal, files/entry points, acceptance criteria, how to verify, what is out of scope. An executor that must ask or explore to understand the task costs more than the minute spent writing it.
  • Draw dependencies to expose parallelism, not to document order. Independent tasks (different components or files) can run side by side; couple tasks only for a real blocker.
  • MUST tag the weight: every task gets exactly one of tier:light / tier:standard / tier:deep in tags when it is created, assessed with the four signals below — no other tier names. The executor re-checks it before dispatch, but starts from your call.
  • Plan once, at the right altitude: spec (what) → plan (how, risks, order) → tasks. Do not re-plan inside an executing task; a plan that turns out wrong is a [decision] comment and a corrected node, not a second plan beside it.

Delegate to sub-agents — assess first, then pick the cheapest model that fits

When the client can start sub-agents (execute-task over a plan, run-harness work items, parallel research), do not default every one to your own model. Before each dispatch, assess the unit:

Signal012
Files to change1–23–56+ or unknown
Ambiguitycriteria and approach givenapproach open, criteria givencriteria or cause unclear
Blast radiusone local unitshared module / several callerscontract (proto/API), data/migration, auth, concurrency, money
Verificationtests exist and run fasttests must be writtenmanual / environment-dependent

Sum 0–2 → light; 3–5 → standard; 6+ or any signal at 2 on blast radius → deep.

TierModel class (use the family you run on)Typical work
lightsmallest, cheapest model — Claude Haiku class, OpenAI's small tiermechanical edits following an existing pattern, renames, adding a field/test for a given function, doc and comment sync, summarising, grep-style research, formatting
standardmid model — Claude Sonnet classa feature slice inside one component, a bug with a repro, a refactor covered by tests, code review of a diff
deepstrongest model — Claude Opus classplanning and decomposition, architecture and cross-service contracts, migrations, security, concurrency, root-causing unknown failures, the judge/moderator

Rules of dispatch:

  • Start at the lowest tier the assessment allows. A light agent given a precise task beats a deep agent given a vague one, at a fraction of the price.
  • Escalate on evidence, one tier at a time. The same failure twice (failing test, schema violation, wrong result) → rerun the unit one tier up with a short summary of what failed. Never loop at one tier and never jump straight to the top.
  • Hand over a context budget. Give the sub-agent only the node slug, the files to touch and the question; tell it to stay within them and to report back in a few lines (result, files changed, commit hash, open questions). If the unit needs more context than fits comfortably (roughly more than a third of the executor's window), split it.
  • Review the output proportionally. Cheap-tier output is verified by running the tests and checking the diff against the acceptance criteria; send only risky changes (contracts, data, security) to a standard/deep reviewer.
  • Independent units in parallel, dependent ones in order; one sub-agent per unit, never two on one node. Parallel sub-agents on the same files need separate worktrees.
  • Never delegate the decision to a human gate, an approval or a judgement — those stay with the user or the judge role.
  • If you cannot choose the model (the client fixes it), still do the assessment: it tells you how much to split and how hard to verify.
  • Model names change faster than these skills. Tiers are by capability and price; map them to the current models of the vendor in use (the user's own preference wins), and report which model ran which unit in the node's [progress] comment.

Find a skill before improvising

When the work is a specialised domain this set does not cover (a framework, a testing tool, a deployment target, a document format) and no installed skill matches, look for one instead of working from memory — find-skills (Vercel):

  1. Check the leaderboard at https://skills.sh/ for a known skill in the domain.
  2. Otherwise npx skills find <specific keywords> [--owner <owner>] ("react testing", not "testing").
  3. Verify before recommending: prefer ≥ 1K installs (be wary under 100), an official or reputable source (vercel-labs, anthropics, microsoft), and a source repo with real stars (< 100 is a reason for scepticism). Never recommend on a search hit alone.
  4. Present the name, what it does, installs, source and the install command npx skills add <owner/repo@skill>; link https://skills.sh/<owner>/<repo>/<skill>.
  5. Install only after the user says yes (-g -y for user-level, non-interactive). A skill is instructions your agent will follow — an unreviewed one is untrusted input.
  6. Nothing found → say so, do the task directly, and suggest npx skills init <name> if it recurs.

Flownix's own skills come from sync-skills, not from this search.

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/bug/group/release/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
artifact_upload_url / artifact_complete_uploadSave a file that is on disk, any size: signed URL + ready curl lines (<FILE> placeholder), then complete; description is required
artifact_uploadInline content (text/base64, up to 16 MB) for short text not on disk or for agents without a filesystem; description is required
artifact_list / artifact_getBrowse a folder, search by name/description/slug, the trash (trash=true); one artifact with "where used"
artifact_read_text / artifact_download_urlText up to 1 MB into the answer / a 5-minute download link for anything
artifact_update / artifact_move / artifact_delete / artifact_folder_createRename, describe, tag; move; to the trash (confirm when in use); folders
artifact_quotaUsed / limit / percent, whose quota (owner's account or org pool), warning from 80%
list_action_eventsProject audit trail: who did what, when, through which channel. Read-only
submit_feedbackReport a Flownix platform problem to the Flownix team (not to the project) — see Reporting Flownix problems

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_status — total_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.

Artifactory — the project's files

Files that work produces or needs — reports, diffs, logs, benchmarks, diagrams, exports, dumps, context snapshots, mock-ups — live in the project's Artifactory, not in comments and not on somebody's laptop. Every file gets a slug <KEY>-ARTF-<N> (AF-ARTF-12) that never changes: renaming, moving and new versions keep it.

  • Save what you produce with a folder (reports/, created when missing), a name and a description — one or two sentences on what is inside. Search finds files by name and description only; a file without a good description is lost.
  • File on disk → artifact_upload_url, any size. The bytes go straight to storage, not through the model. Inline artifact_upload is only for short text that is not on disk, or for agents without a filesystem and shell (up to 16 MB; the model must print every byte, so it is slow). Example: shasum -a 256 report.html, wc -c < report.html → artifact_upload_url (folder, name, description, sha256, size_bytes) → run commands[0] with the path in place of <FILE> → artifact_complete_upload(upload_id).
  • Refer to it by slug. AF-ARTF-12 in a comment or document becomes a link, and the file shows the task under "where used". set_references accepts artifact slugs too.
  • Read before you download: artifact_read_text returns text up to 1 MB; anything else — artifact_download_url (5 minutes, this one file only).
  • Find files by meaning with rag_query(kind_filter: "artifact"), by name with artifact_list(query: …).
  • Same name = conflict. Re-uploading to a taken name fails unless new_version: true.
  • Deleting goes to the trash for 7 days. A file a task refers to is not deleted without confirm: true — the refusal lists who uses it.
  • Storage has a quota (artifact_quota), shared by all free organizations of one owner. Uploads beyond it fail with plan_limit (limit_key: artifact_storage); reading never does.

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.

Reporting Flownix problems

A Flownix tool misbehaves — errors on correct arguments, returns contradictory data, refuses for no stated reason, lacks a capability the docs promise, or its error ends with the "looks like a Flownix platform failure" hint → submit_feedback, not a task in the project. The user asks you to pass something on to the Flownix developers → submit_feedback with origin: "user". It works in any project regardless of spec_mode/spec_first; no delta, components or approval.

shell
submit_feedback({ feedback_type: "bug", tool_name: "get_tree", title: "get_tree: Internal on a valid project_id",
                  body: "get_tree(project_id=…) → Internal: …; expected the tree" })

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. Every harness also has a slug <KEY>-HRNS-<n> (e.g. AF-HRNS-1): numbered per project like node slugs, never reused, a clone (in the project or from the library) gets its own. harness.get and harness.start_run accept harness_slug + workspace_id instead of harness_id; a slug of another project is NotFound.
  • 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; type ∈ agent_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.list (workspace_id) — check for an existing harness to reuse or harness.clone
  2. harness.create (workspace_id, name, type)
  3. harness.create_version (draft)
  4. harness.add_role × N, harness.add_workflow_node × N, harness.connect_workflow_nodes
  5. harness.set_policy, harness.set_budget, harness.set_context_policy, harness.set_output_schema (optional)
  6. harness.validate — missing roles, disconnected nodes, cycles, policy conflicts
  7. harness.publish
  8. harness.start_run — pass source_node_ids to link it to the task/feature it operates on. To run an existing harness with a prompt and drive it to the end, use the run-harness skill: run-harness AF-HRNS-1 <what to do>.

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.

Template library (harness.library_*)

A public catalog of harness templates published by Flownix users from any organization, modelled on npm. A package has an immutable title and author; each publish by its author adds the next version — an immutable snapshot of a published harness version (roles, steps, transitions, policies, context policy, schemas). The package does not depend on the source harness, but a harness is linked to its package: a clone to the package and version it came from, a source to the package it was published into.

  • Browse: harness.library_list (query, type, sort: recent | rating | clones) and harness.library_get (card with every published version, the selected version's full composition, your my_rating and stats). Any authenticated user sees the catalog.
  • Clone: harness.library_clone({ package_id, workspace_id, number?, name? }) → an independent draft in a project you belong to: version 1 unpublished, you as author, role links to project policies empty, no participants bound. Assign participants, harness.validate, harness.publish.
  • Publish: harness.library_preview({ harness_version_id }) first — the whole content becomes public and is copied into clones as is — then harness.library_publish with summary (1–280) and confirm_public: true; omit package_id for a new package (title 1–120, immutable, defaults to the harness name) or pass your package to add a version.
  • Link: harness.library_link({ harness_id }) → package, matching number, latest_number, update_available, own. A linked copy of your package publishes only as the next version of that package (never a duplicate); a copy of someone else's package cannot be published (library.foreign_copy) — harness.library_unlink first to make it your own. harness.library_update({ harness_id }) puts the latest package version into the harness as a draft (replacing an unpublished latest version); published versions keep serving runs.
  • Own packages: harness.library_my_packages, harness.library_unpublish (a version by number, or the whole package); clones are never affected.
  • Rate: harness.library_rate (1–5, one per user, replaces) / harness.library_unrate; the author cannot rate their own package.
  • Moderation: harness.library_set_hidden — platform administrators only.

Errors carry a library.* code in front of the message, identical in REST and MCP: version_not_published, confirmation_required, title_invalid, summary_invalid, title_immutable, not_author, rating_invalid, self_rating, sort_invalid, foreign_copy, linked_package, not_linked, up_to_date, type_invalid, page_token_invalid, workspace_required, workspace_not_found, permission_denied, admin_required. Unpublished, hidden and unknown packages all answer not found.

To build one: harness-blueprint — it checks the library before building from scratch. Once a run is live: flownix-harness-participant (you hold a role) or flownix-harness-moderator (you hold judge).

Harness tools

ToolPurpose
harness.listHarnesses of a project with slug, status and current published version number
harness.create / harness.getCreate a definition / read it with version details — by harness_id or harness_slug + workspace_id
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.set_context_policyWhat work.get_context returns: include (run_input/role/predecessors/all_steps), exclude output keys, max_tokens
harness.set_output_schemaJSON Schema of the final result, enforced on steps leading into end; role output_schema is enforced on every work.complete (output.schema_violation)
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 — by harness_id or harness_slug + workspace_id
harness.library_list / harness.library_getPublic template catalog (search, type, sort by recent/rating/clones) / package card with versions, composition, your rating and stats
harness.library_cloneClone a package version into your project (workspace_id) as an independent draft
harness.library_preview / harness.library_publishSee exactly what becomes public / publish a published version as a new package or the next version of yours (confirm_public: true)
harness.library_my_packages / harness.library_unpublishYour packages with all versions / unpublish a version (number) or the whole package
harness.library_rate / harness.library_unrateRate a package 1–5 (not your own) / remove your rating
harness.library_set_hiddenHide or restore a package — platform administrators only
harness.get_run / harness.list_runsRead a run with step outputs / find runs by harness, status or Flownix node (slug or UUID)
harness.list_pending_approvalsOpen human_gate approvals of a run or project — read-only, a human answers in the web app
work.list_available / work.claim / work.heartbeatFind, lease, extend
work.get_contextRun input, node, role, input artifacts and predecessor outputs for a work item
council.get_deliberationProposals with claims and evidence, critiques with responses, ballots, decisions
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_status — draft, 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 guess — rag_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 step — create_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.
  • Spend tokens like a budget: read once and by slug, never list wide, batch independent calls, cite files by Artifactory slug, delegate by reference — see Working efficiently.
  • Assess before delegating and pick the cheapest model tier that fits (light / standard / deep); escalate one tier on repeated failure.
  • Harness tools are dotted and take workspace_id (= project_id) — don't mix namespaces.