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

map-components

Use when a Flownix project has no system components yet, or has them and the accumulated work is still unlinked — triggers on "map the components", "what does this system consist of", "link the tasks to services", "what stack do we use", "w

Map the System into Components

Turn a project that has been tracking work as a flat list into one that knows the shape of the system it is building — its code, and what that code runs on. Prerequisite: read flownix-basics, section System components.

The rule this skill is built around

Never link silently. A pass over an existing project by heuristics WILL contain mistakes. A missed link is visible — the component's section is empty, and the "No component" filter lists it. A wrong link is visible to nobody: it quietly moves a task under a service it has nothing to do with, and the progress of that service is wrong from then on.

So: propose, explain the ground for each proposal, and apply only what a human confirmed.

Workflow

0. Load project context

shell
cat .flownix 2>/dev/null || cat .ai-flow

Use project_id in every call below. If neither file exists, run flownix-init first.

1. Inventory the code

Read the structure, not the names. Walk:

  • services/*, apps/*, packages/*, cmd/*
  • top-level applications: web, admin, landing, mobile
  • shared and infrastructural directories: pkg, lib, migrations, deploy, k8s, infra
  • build and orchestration manifests: docker-compose.yml, go.work, package.json workspaces, Cargo.toml, pyproject.toml, Makefile targets, CI workflow files

Cross-check the directories against the manifests. A directory that no manifest builds, deploys or references is probably not a component — it is dead code or a scratch folder, and naming it a component invites people to file work against it.

Invent nothing. A component that does not exist in the repository is worse than one you missed: the missed one nobody links to, the invented one collects work.

2. Inventory the stack and the infrastructure

Half the work in a real project is not "a task about gateway" — it is a migration, an index rebuild, a broken pipeline, a version bump. That work has no owner among the code components, and it is exactly the work people later search for: "what have we done to the database", "everything that touched the deploy". So the inventory has a second half.

Read the declarations, not your memory of the ecosystem:

WhereWhat it declares
docker-compose.yml, *.dev.ymlthe datastores and brokers the system actually runs against
migrations/, schema.sql, ORM configthe database, and the fact that schema work is a stream of its own
k3s/, k8s/, helm/, terraform/, Dockerfile*how it is deployed, and by what
.github/workflows/, .gitlab-ci.yml, Makefilethe pipeline — the thing that breaks on Fridays
go.mod, package.json, pyproject.toml, Cargo.tomlframeworks and clients: the driver names the datastore
.env.example, config structs, secrets manifestsexternal services: object storage, LLM providers, payments, mail

Evidence, not ecosystem knowledge. A Redis client in go.mod and a redis service in compose is evidence. "Projects like this usually cache in Redis" is not — an invented infra component collects work that then describes something the system does not have.

Two layers come out of this, and they are not interchangeable:

The stack of a component — the languages, frameworks and libraries it is written in. This is not a component: it is tags plus one line in the description (step 3). A component per library gives you a hundred leaves nobody links to.

A shared dependency — a datastore, broker, platform or external service that several components use, and against which work is filed on its own. This is a component:

  • component_kind: "infra" — what you run: postgres, qdrant, redis, kafka, k3s / helm, ci.
  • component_kind: "integration" — what you call: openai, s3, stripe, smtp.

The threshold between the two layers, when a case is unclear: has work ever been filed against this alone? A migration, an upgrade, an outage, a quota — yes, it earns a component. If every mention of it is really a task about the service that uses it — it is a tag.

3. Create the components

For each part of the system:

shell
list_components({ project_id })            // ALWAYS first — see idempotency below
create_component({
  project_id,
  title: "core-service",                   // the name it has in the repository
  repo_path: "services/core-service",
  component_kind: "service",               // service | app | library | infra | integration
  description: "Purpose. Boundaries. Entry points.\nСтек: Go 1.23, gRPC, PostgreSQL (AF-COMP-9).",
  parent: "AF-COMP-1",                     // omit for a top-level component
  tags: ["go", "grpc", "postgres"],
})

The tree mirrors the repository: monorepo → group (services) → service. Two or three levels is what the UI is built for. Infra and integration components sit at the top level next to the code groups — they are not part of any one service, that is the whole point; or under one group (infra, platform) when there are more than three of them.

An infra component gets a repo_path too — the strongest evidence for it, the place where its work actually lands: migrations for the database, k3s for the cluster, .github/workflows for the pipeline. An integration to an external service usually has none; name the config or client package instead, and leave repo_path empty rather than inventing a path.

Write the stack into the description, and tag it. Descriptions are what rag_query searches (step 4) and what a future agent reads to decide whether its task belongs here, so a Стек: line naming the language, framework and the datastores it talks to — with the slugs of the infra components — is what makes "which services go to Postgres" answerable at all. Tags carry the same words in normalized form: one spelling per technology across the whole project (postgres, never also postgresql and pg), lowercase. Two spellings split the answer in half and nothing warns you.

The skill must be safe to re-run. Start with list_components and skip anything whose repo_path or title already exists. Duplicated components split a service's work in half and nothing warns you. On a re-run over a project that already has code components but no stack layer, do not recreate anything: add the missing infra components, and bring the existing descriptions up to date with update_component (it changes only the fields you pass) — tags is not among them, so use add_tag for those.

For each component, collect candidates from several independent angles — one angle alone finds only what it is shaped to find:

shell
rag_query({ project_id, query: "<component name> <purpose> <repo path>", k: 20 })
list_nodes({ project_id, kind: "task" })   // titles, tags, ticket_ref
list_docs({ project_id })
list_reviews({ project_id })

Grounds worth recording, roughly in descending order of trust:

GroundExample
the path appears in the node's body or commitservices/gateway/... in the task text
the branch of a review touches the component's pathfix/gateway-timeout
tag matches the component#ragrag-service
the component name appears in the title«Обработчики gateway…»
semantic hit on the description alonea task about routing → gateway

For the infra and integration components the same trust order holds, on their own evidence:

GroundComponent
the node touches migrations/, or the body carries DDL / an index / a query planthe database
the node touches k3s/, helm/, Dockerfile, or the body carries a deploy failurethe platform
the node touches .github/workflows/, or is about a red pipelinethe CI
the body names the client, the collection, the topic, the bucket, the modelthat datastore / broker / storage / provider
semantic hit alone: «переиндексация», «эмбеддинги», «квоты»uncertain — propose it as such

Infra links are additional, not alternative. A task that added a migration to core-service belongs to core-service and to postgres; links are many-to-many, and dropping either one loses an answer someone will later ask for. Docs follow the same rule: a schema description, an ADR about the queue, a deploy runbook get the infra component, not only the service they were written for — a spec reachable only from a service is invisible from the thing it actually documents.

5. Propose in batches

Group proposals by component, and inside each group split them:

  • Confident — path, branch or explicit name. Confirmed in one answer for the whole group.
  • Uncertain — a semantic hit alone, or a tag that fits several components. Listed separately, one line each, so the human can reject individually.

Mixing the two costs the confident ones their speed and the uncertain ones their scrutiny.

Apply only what was confirmed:

shell
set_node_components({ node: "AF-TASK-341", components: ["AF-COMP-3", "AF-COMP-9"] })
set_review_components({ review_id: "...", components: ["AF-COMP-3"] })

set_node_components replaces the whole set. When a node already has links, include them in the list unless the human asked to drop them — otherwise the confirmation silently unlinks whatever was there before. This is what makes the infra pass dangerous when it runs second: adding postgres to a task means sending ["AF-COMP-3", "AF-COMP-9"], not ["AF-COMP-9"].

6. Report

State plainly:

  • how many nodes, docs and reviews were linked, and to what;
  • the stack layer separately: which infra and integration components were created, on what evidence, and how much work each one collected — a datastore that ended up with two links in a project full of migrations means the grounds missed, not that the work does not exist;
  • what was proposed and rejected;
  • what is left with no component, and why — no obvious owner, spans everything, or predates the part of the system it touches.

The leftovers are the useful half of the report: they are found later with the "No component" filter (component: "none" in list_nodes / list_docs / list_reviews), and this report is what explains them.

Acceptance

  • Every created component matches a real part of the repository, with repo_path filled — and every infra/integration component names the manifest that declares it.
  • Every code component's description carries its stack line, and its tags use the project's one spelling per technology.
  • A second run creates nothing new.
  • Every applied link was confirmed by a human and had a stated ground.
  • Adding an infra link never dropped the code link that was already there.
  • The report names what stayed unlinked.