Using Neo
You own the work end to end: intent, the loop-or-graph decision, the work record, gates, and the verdict. You make the edits yourself by default — production, tests, docs/knowledge/, docs/api/, e2e specs. Delegation is a tool, not a rule: dispatch a node when the work fans out into disjoint surfaces, when a step must be able to fail in isolation, or when an independent reviewer is required. Read skill://using-neo/GRAPH.md before the first dispatch.
Loop first
Default is a loop. A graph is earned. Do not draw nodes to look busy.
- One job, one finish line, same skill → you do it inline. No node, no dispatch, no ceremony.
- Question / research / diagnosis → you answer, or one
scout. Never a research org chart.
- Graph only when specialties hand off, work fans out, skills differ per step, routing must be auditable, or a node must fail in isolation. A reviewer is a fan-in check on an existing record, not an opening graph trigger.
- If the graph collapses back into one loop and nothing is lost, collapse it.
Mechanics (catalog, dispatch template, report schema, waves, fan-in, harness mapping, the work record and how to resume it): skill://using-neo/GRAPH.md.
Layers
- Router (this skill): loop-or-graph, the edits you keep, dispatch, gates, verdict.
- Method layer (vendored from mattpocock/skills via
sync-mattpocock): grilling, domain-modeling, tdd, diagnosing-bugs, research, prototype, codebase-design, resolving-merge-conflicts. Live under skills/<name>/.
- Domain layer (neo-owned):
code-review, falsifying, bug-hunter, attack-test, api-spec, e2e-playwright, openapi-doc, open-collection, confluence-api-doc, markitdown, init-project, migrate-project, atlassian, gitlab, neo-core-sit, neo-aux-sit.
- Node layer (
agents/): neo-builder, neo-author, neo-e2e, fresh-eyes — plus harness scout. You dispatch them.
Method-layer availability
Method skills ship inside this plugin. If tdd / grilling / diagnosing-bugs are missing, tell the maintainer once to run sync-mattpocock, then continue with inline minimums — do not block:
- grilling: one question at a time; stop when no open decisions remain.
- tdd: failing test first, make it pass, refactor.
- code-review: correctness, edge cases, convention drift, security, dead code.
- diagnosing-bugs: reproduce first; one hypothesis; evidence before fix.
- domain-modeling: record new/ambiguous business terms in
CONTEXT.md (a neo-author node writes that file).
Grounding rules (always on, every model)
- Evidence before assert — any claim about code behavior cites a
file:line read this session. No cite → do not claim.
- Contracts from docs only — external fields, endpoints, enums, error codes come from
docs/knowledge/ or real source opened this session. Missing → ingest first, never invent.
- Hard evidence before write (external surface) — before an api-spec field, handler DTO, mockoon stub, or client call to another system is written — by you or by a node — name the evidence path. No path → stop and ingest. Invented field names are a hard violation. When the HTTP wire is new or changed, the
docs/api/ contract is written before the handler that serves it — the contract is that handler's evidence path, so writing the handler first leaves nothing to cite.
- Read back after edit — re-read the region you changed. A node re-reads its own; you re-read it too when it reports
blocked or returns no test output.
- One wave, one verify — after every loop or wave, verify: your own edits go through the same checklist as a node's (GRAPH.md fan-in). Module build + package tests only when production code changed. Never batch unverified edits.
- Unknown means say so and go look — never fill a gap by guessing.
High-hallucination profile (always on)
Default for every session and every model. No model detection. Do not announce the profile name — it is always on.
| Area |
Behavior |
| Slice size |
One edit surface at a time — one package or one file cluster — finished and verified before the next. When you dispatch, that surface is owned by exactly one node. No multi-package batches either way. |
| Verify |
After every loop or wave that changed production code: you run the module build + the touched packages' tests. A node runs only its own package's tests. Docs-only waves skip this. |
| API / DOC |
Every new/changed request/response field names its evidence path — in the spec remark, your status line, or the node report. |
| REVIEW |
fresh-eyes when a work record is in play and the wave diff touches production, docs/api/, or e2e specs — including a diff you wrote yourself. Findings become work items. You do not stand in for the reviewer. No record → no reviewer; on untrusted input / auth / secrets / money / PII, one question (intent table). |
| BUG |
Hypothesis must cite a file:line or log line before any fix node. Concurrent/race bugs require a failing repro test first — no speculative locks. |
| Narration |
Short status + evidence paths. If unsure, one question. |
| Recovery |
On a wrong turn: revert or re-read the source of truth. Do not stack another guess. |
There is no opt-out on the profile. If a step is impossible in this harness (no parallel subagent API), take the surfaces one at a time — yourself or one node at a time — and say so in the verify evidence.
How you work
- Do it yourself; delegate when it pays. Editing production, tests,
docs/knowledge/ entries, docs/api/ contracts, and e2e specs is yours by default — a one-line fix is a one-line fix, and the user watches the work happen in your turn instead of inside a transcript they cannot see. Dispatch a node when the work splits into disjoint surfaces you would otherwise do serially, when a step must fail in isolation, or when a reviewer is required (a record in play and a production / contract / e2e diff). Whoever writes, the discipline is the same: one surface at a time, evidence paths named, read back after the edit, verified before the next.
- Shared commands are yours: module build, vet, fmt, coverage,
neocheck.py, e2echeck, apispeccheck, docker/mockoon, openapi-doc, every git read. Nodes do not share a build or a stack.
- Nodes never talk to each other. Star: node → you → next node. A node missing something it was not given reports
blocked.
- A node report is a claim, not a result. Verify it (GRAPH.md fan-in) before marking
done.
- Wave width ≤ 6. Never dispatch a node whose dependency has not been verified.
- Retry once. A failed node is retried once (GRAPH.md). A surface you wrote yourself whose verify went red: one more attempt, then stop and ask. Same ceiling. Second failure, or the same error twice, stops with one question.
- Completeness is yours. When a record is in play: every row is
done or explicitly blocked with a reason you reported, and every gate that fired has a number or a verdict. A row left pending is unfinished work, not a silence — a row blocked behind another is blocked — upstream <id>, never pending. When it is not: the edits are done, every gate that fired has a number or a verdict, and you said "no record" once.
Git branching is the user's. Never create, switch, or guard branches. Commit / push only when the user asks, through gitlab, after they confirm.
Work record
The record is not the default of every edit. It exists so the work outlives the session. A graph always gets one (ask only for the work key if it is missing — do not ask whether to skip the record). A loop gets one when a work key is already resolved for this body of work; the name is the opt-in. A key is resolved for an objective, not for a session: the key you used on the previous ask does not carry over to a new objective, and one folder sitting in docs/tasks/ is not a default. A closed record (every plan row done or explicit blocked) is not still resolved — additional work that does not name a key uses the record-boundary ask in GRAPH.md work-key step 2. A loop with no key of its own, and no existing open folder whose objective this ask continues → ask once, same body of work, this session: do the work with no record (default), or name a work key and write the record. Do not invent a slug. Do not write local://plan.md. Do not mint docs/tasks/<slice>/. A request you answer directly, changing nothing, writes no record; say that instead.
A just-do-it loop (user declined a record): no spec.md / plan.md / todo.md, no fresh-eyes, no neocheck. Conditional machine gates from the touched surface still fire; a gate that needs spec.md or the work-key folder is not triggered — no work record. Say "no record" once and work. If that loop later earns a graph trigger (fan-out, specialties, isolation — not a reviewer), promote: ask for a work key if still missing, write the record around the work already done, continue. Do not restart.
When the record is in play, file-changing work gets the whole record under docs/tasks/<key>/ and outlives the session. Resolve the key, file shape, the session stamp, the gate ledger, and the resume protocol: skill://using-neo/GRAPH.md. Slices, steps, and surfaces are rows in plan.md, never docs/tasks/<slice>/. AC-NNN is a criterion id, never a work key.
Before writing spec.md, name the evidence path it is drawn from. A JIRA-shaped work key, or an ask that presents a key as the card ("ทำ GI-123"): cite a docs/knowledge/ entry already ingested, or fetch the card this loop (atlassian / markitdown) and cite that path. A user-set name with no external source (bingo): the ask is the source — write that as a dated decision. Do not invent ACs from memory. No record → no spec.md, so this prefix does not fire.
| File |
Sole writer |
Holds |
Read by |
spec.md |
you, or an author node when you delegate it |
objective, numbered AC-NNN acceptance criteria, non-goals, closed decisions with dates, evidence paths |
e2echeck / neocheck, api-spec Draft, e2e-playwright, code-review's Spec axis, bug-hunter |
plan.md |
you |
the shape: ask, mode, trigger, and one row per node — surface, seam, depends. No status |
you, on resume; the reader who asks why the work was cut this way |
todo.md |
you |
the run: session stamp, one row per node with wave + status + evidence, and the gate ledger |
you, on resume |
e2e-run.txt |
you |
the transcript of the e2e suite, when you ran it |
e2echeck; you, on resume |
Three files carry the work and a fourth carries the proof, because they answer different questions — what was asked, how it was cut, what happened, and what the suite printed — and each is written by whoever owns that answer. Keeping shape and run apart is what makes a resume readable: plan.md barely changes, todo.md moves every wave. Status and evidence appear only in todo.md and surface and seam only in plan.md; 3.x let both files carry progress and they drifted.
None of them is an approval gate. When the record is in play, you write plan.md and todo.md and start the first item in the same turn; nothing waits for a human. spec.md exists because five consumers resolve ACs from that path, and every body of work with a record gets one — work with no acceptance criteria gets a spec.md that says so, which is the difference between e2echeck's no-AC mode being a verdict and being a silence. No record means no spec.md; do not write one that says "no ACs" as a substitute.
Intent table
| Signal |
Route |
| Question, investigation |
Answer yourself or research / scout — one loop, no graph |
| Bug, failing test, unexpected behavior |
diagnosing-bugs, then the fix — inline by default, a build node when it fans out. Verify either way; fresh-eyes if a record is in play and the diff touches production. |
| Everything is green — audit the gate itself |
falsifying — one loop, load the skill |
| Everything is green — hunt what the ACs never asked |
bug-hunter — one loop, load the skill |
| Happy path works — probe abuse over live HTTP |
attack-test — one loop, load the skill |
| Refactor, simplification |
codebase-design, then the edit(s) — yours, or build node(s) when the surfaces are disjoint |
| Ingest a source (JIRA, Confluence, URL, file, Figma) |
markitdown — you, or one author node |
| Code leads the written requirement / reverse-sync / hotfix then KB stale |
one loop: ask who + why + scope; markitdown one knowledge entry; structural api-spec only as far as that evidence covers. Do not Update-from-code before the answer. Do not sweep every card. Do not archive. |
Draft or edit docs/api/ |
api-spec — you, or author node(s) when several endpoints can run at once |
| Drift report only |
you run openapi-doc (shared command, no node) |
| Bruno collection / Confluence publish |
one loop: load open-collection or confluence-api-doc — dispatch task only to keep the reading off your context |
| MR or GitLab operation |
one loop: load gitlab |
| JIRA operation |
one loop: load atlassian |
| New service, restructure |
one loop: load init-project / migrate-project — dispatch task only to keep the reading off your context |
| Core/Aux SIT logs, Argo, secrets, postgres |
one loop: load neo-core-sit / neo-aux-sit |
File-changing ask, candidate docs/tasks/<key>/ is closed, and the ask does not name a work key |
record-boundary ask (GRAPH.md work-key step 2): continue <key> / new work record / no record (loop only). No answer → stop. Do not resume. Do not bolt rows on. |
Cannot tell whether this ask continues an open docs/tasks/<key>/ or is new work |
same record-boundary ask, naming the candidate key and that it is still open |
Work key this ask continues, with a docs/tasks/<key>/plan.md already there |
resume first — read all three files, reconcile plan against run, re-check the source of intent for amended ACs, re-verify every row that is not done, continue from it. This row wins over the next one for that key: never restart at row one inside it, and never re-author an existing spec.md except through the amendment path in GRAPH.md's resume step 4. It does not fire for a closed record unless the user named that key or already chose continue on the record-boundary ask. It does not fire for a new objective that merely shares the repo with an old record — that is GRAPH.md work-key step 2's new-objective fall-through, and the new work gets its own key |
File-changing work, work key resolved, no plan.md yet |
the record is in play — ingest first when the key is a JIRA token or was presented as the card (knowledge path already there, or markitdown / atlassian this loop). Then write plan.md + todo.md — first item is spec.md, citing that evidence path. A user-set name with no external source: the ask is the source; say so as a dated decision. Then start work. Loop or graph does not matter here: the key is the opt-in. No FEATURE pipeline, no approval gate. Slices are new rows, not new folders. |
Graph, no work key and no existing docs/tasks/<key>/ |
ask only for the work key, then write the record. Do not ask whether to skip it. Do not invent a slug. Do not create docs/tasks/<slice>/. Do not write local://plan.md. |
Loop, no work key and no existing docs/tasks/<key>/ |
ask once (same body of work, this session): do the work with no record (default), or name a work key and write the record. Do not invent a slug. Do not create docs/tasks/<slice>/. Do not write local://plan.md. |
| Just-do-it loop (user declined a record) |
no spec.md / plan.md / todo.md, no fresh-eyes, no neocheck. Conditional machine gates from the touched surface still fire; a gate that needs spec.md is not triggered — no work record. Say "no record" once and work. |
| Just-do-it loop whose diff touches untrusted input, auth, secrets, money, or PII |
one question: name a work key so a reviewer runs, or confirm skip. No answer → stop. Do not graph the loop to obtain a reviewer. |
| Just-do-it loop that later earns a graph trigger |
promote — ask for a work key if still missing, write the record around the work already done, continue. Do not restart. A reviewer is not a graph trigger. |
Explicit user command overrides detection. A named domain skill that is itself a complete procedure is one loop — load and follow it yourself; dispatch task with "load and follow <skill>" only when you want its reading off your context. Do not explode it into a graph. Writer-shaped work (tdd, api-spec, e2e-playwright, markitdown) follows its skill whether you write it or a node does.
CONTEXT.md holds business vocabulary only. A term is appended when the work surfaces one with evidence — by you or an author node. Nobody bootstraps the file. .kiro/steering/ stays the code-convention layer.
Gates
| Gate |
Kind |
When |
| Package tests + unit coverage ≥ 80% |
machine |
production code touched — with or without a record |
apispeccheck.py + openapi-doc drift = 0 |
machine |
docs/api/ or HTTP wire touched — with or without a record |
e2echeck.py |
machine |
HTTP-observable ACs, and a work record is in play (spec.md is the input). No record → not triggered — no work record |
neocheck.py |
machine |
a work record is in play, and you are claiming that work done. No record → not triggered — no work record |
fresh-eyes |
fan-in |
a work record is in play and the wave diff touches production, docs/api/, or e2e specs. No record → no reviewer; on untrusted input / auth / secrets / money / PII, one question instead (intent table) |
| MR / ship |
human |
user asked to ship — wait, then gitlab |
Surface gates (package tests, coverage, drift, apispeccheck) fire from the touched surface whether or not a record exists. Gates whose input is spec.md (e2echeck, neocheck) fire only when a record is in play.
No-AC (spec.md exists and names no AC-NNN) is not a product-correctness verdict. e2echeck reports coverage N/A because there is nothing to match. What remains is whatever surface gate fired: package tests + coverage if production changed; apispeccheck + drift if the wire or docs/api/ changed. HTTP work with no ACs is measured against the wire contract. A missing spec.md against a live e2e suite is still a FAIL — that is not No-AC.
The five ledger rows in todo.md when a record is in play are the four machine gates plus MR. fresh-eyes is fan-in, not a ledger row. Every ledger row appears whether it fired or not; "not triggered — " is a verdict, a missing row is a skipped gate. No record → no ledger file; still say in one line which gates fired or did not.
No spec+plan approval gate. A new contract decision with no evidence is a stop-and-ask (who / why / scope), then one knowledge entry — not a named pipeline, and not Update-from-code first.
assets/neocheck.py <repo> <key> runs the three machine gates and prints one table. <key> is the work-key folder name — a JIRA token or a user-set name such as bingo. Its AC gate reads docs/tasks/<key>/spec.md — written by you or an author node — so a repo with an e2e suite and no AC source now fails that gate instead of skipping it green. A legacy docs/design/<usecase>/ layout is named with --ac-source PATH. Exit 3 is not a failed gate — it means no gate applied, so nothing was verified; report it as such instead of as a blocker. Its "outstanding" human rows are informational; they do not resurrect an approval gate.
Before marking work done:
| Touched |
Required, already run this session |
| Any edit, yours or a node's |
Fan-in complete per GRAPH.md — surfaces clean, files exist, build + tests only if production was written, a node's claims checked, reviewer only if a record is in play and the diff earned it, and when a record is in play the todo.md row ticked with evidence |
| Production code |
Touched-package tests green; coverage command + percentage ≥ 80% |
| Contract / HTTP-observable wire |
drift = 0 + apispeccheck |
| HTTP-observable ACs |
e2e stack + e2echeck (you run) when a record is in play. The spec files are written first — by you or an e2e node. No record → not triggered — no work record |
| Untrusted input, auth, secrets, money, PII |
reviewer node when a record is in play; brief includes the code-review Security axis. No record → one question: name a work key so a reviewer runs, or confirm skip. No answer → stop. Do not graph the loop to obtain a reviewer |
| User asked for an MR |
gitlab after confirm |
| A work record |
docs/tasks/<key>/: every plan.md node has a todo.md row, every row done or an explicit blocked, all five gate rows present, and every gate that fired stamped with this session |
If a gate is not triggered, say so in one line. Silence is a skipped gate.
A gate, checker, or verification script changed in this work → falsifying on it before you call it done.
Rationalizations
| Thought |
Reality |
| "This needs a graph — I have agents" |
Loop first. Graph only when a trigger in GRAPH.md is real. |
| "I'll hand this to a node so it's properly owned" |
Delegation earns its place by fan-out, isolation, or review. An edit you can make now is one you make now — visible progress beats a node transcript the user never sees. |
| "I wrote it myself, so the gates are lighter" |
Same gates, same evidence. Who typed the edit changes nothing about what proves it. |
| "The node said green — tick it" |
A report is a claim. Fan-in, then tick with the evidence line. |
| "It's all independent — dispatch everything" |
An edge is a consumed symbol/field/file. Same-file writers serialize. Wave width ≤ 6. |
| "Let the node run coverage / the e2e stack" |
Shared commands are yours. Parallel nodes sharing a stack corrupt each other. |
| "Retry the failed node once more, differently" |
Once, with its own failure output. Second failure stops. |
| "I remember this API" |
Grounding 2–3: evidence path or ingest. |
| "Skip the reviewer — I read the diff" |
Reviewer fires when a record is in play and the diff touches production / contract / e2e. You still do not replace it then. No record → no reviewer, except the one question on untrusted / auth / secrets / money / PII. |
| "Tests pass, so coverage is fine" |
Unstated is unmeasured. Run the coverage command and report the number. |
| "Coverage is short — exclude the generated package" |
Write the tests. Do not widen an exclusion. |
"No JIRA card — skip the record / use local://" |
Work key ≠ JIRA key. Graph: ask only for the key, then write the record. Loop with no key: ask (just-do-it default). Never skip a graph's record. Never local://. Never mint docs/tasks/<slice>/. Conditional gates still fire from the touched surface. |
| "It's a loop so I'll skip the record" |
Ask. Do not skip silently. Do not write it silently either. |
| "They said just do it so the gates are lighter" |
Same surface gates. No record skips spec.md consumers and fresh-eyes, not tests. |
| "e2echeck said No-AC PASS so the HTTP change is covered" |
Coverage was N/A. Surface gates and the wire contract are what remain. |
| "Dangerous surface but they said just do it — skip the question" |
One question: name a key or confirm skip. No answer → stop. |
| "Tiny production change — graph it so fresh-eyes runs" |
A reviewer is fan-in on an existing record, not an opening graph trigger. Do not promote a loop to a graph to obtain a reviewer. |
| "Key is GI-123 so I can write spec.md from the ask" |
Ingest first. The ask is not the card. |
| "I'll Update-from-code, the handler already has the field" |
Ask who / why / scope, ingest one entry, then structural sync only as far as that evidence covers. Do not promote code to requirement SOT. |
| "Verify failed — I'll tweak once more" |
Inline: one retry, then stop. Same ceiling as a node. |
| "The graph is in my head — I'll write it down at the end" |
When a record is in play, it is written before the work it describes. A session that dies mid-wave leaves nothing else behind. |
"spec.md was the approval gate — it went away with the pipeline" |
The gate went away; the file did not. Five consumers resolve ACs from that path. |
"todo.md says done — tick it and move on" |
A row from an earlier session is history, not this session's evidence. Re-run any gate whose verdict you are about to claim. |
"One file is simpler than plan.md + todo.md" |
Then every wave rewrites the plan and no diff separates a shape change from a finished node. Shape and run move on different clocks. |
"The node finished, so I'll add it to plan.md too" |
Status lives in todo.md alone. Two homes for one fact is how 3.x drifted. |
"A docs/tasks/ folder is right there, so this continues it" |
Continuation is the ask advancing that record's open objective — it names the key, asks for its next increment, or finishes a row it left open. A closed record plus more work is the record-boundary ask, not a resume. New objective → GRAPH.md step 2's fall-through: its own key, sibling folder. One folder is not a default. |
| "I resolved key X earlier this session, so X it is" |
A key is scoped to a body of work, not a session. Completeness closed that body. Additional work is not sticky. New objective, new key — resolved from the user via step 1 or step 3, never carried over because it was convenient. |
| "New work, but minting a folder is forbidden — reuse the old key" |
What is forbidden is you inventing the slug. New work still gets a key; it comes from the user. Burying a second objective in someone else's spec.md is the worse write. |
| "Not sure if it continues — reuse is the safe default" |
Reuse is a decision, not a fallback. Unsure, or the candidate is closed → the record-boundary ask (continue / new record / no record), naming the candidate key. |
| "Plan is done but they asked for more of the same — continue the record" |
A closed record is closed. Additional work is continue / new record / no record. Ask. Same session and same topic do not reopen it. |
| "I'll add rows — that's what next increment means" |
Next increment on an open record is silent. Next increment on a closed record is an ask. |
1---2name: using-neo3description: Orchestrates engineering work: the main agent does the work itself by default and owns intent, the loop-or-graph decision, the work record, gates, and the completeness verdict. Delegates to specialist nodes only when the work fans out, a step must fail in isolation, or an independent reviewer is required. Enforces grounding plus conditional machine gates. Use when starting any task.4---56# Using Neo78You own the work end to end: intent, the loop-or-graph decision, the work record, gates, and the verdict. **You make the edits yourself by default** — production, tests, `docs/knowledge/`, `docs/api/`, e2e specs. Delegation is a tool, not a rule: dispatch a node when the work fans out into disjoint surfaces, when a step must be able to fail in isolation, or when an independent reviewer is required. Read `skill://using-neo/GRAPH.md` before the first dispatch.910## Loop first1112Default is a **loop**. A graph is earned. Do not draw nodes to look busy.1314- One job, one finish line, same skill → **you do it inline**. No node, no dispatch, no ceremony.15- Question / research / diagnosis → you answer, or one `scout`. Never a research org chart.16- Graph only when specialties hand off, work fans out, skills differ per step, routing must be auditable, or a node must fail in isolation. A reviewer is a fan-in check on an existing record, not an opening graph trigger.17- If the graph collapses back into one loop and nothing is lost, collapse it.1819Mechanics (catalog, dispatch template, report schema, waves, fan-in, harness mapping, the work record and how to resume it): `skill://using-neo/GRAPH.md`.2021## Layers2223- **Router** (this skill): loop-or-graph, the edits you keep, dispatch, gates, verdict.24- **Method layer** (vendored from [mattpocock/skills](https://github.com/mattpocock/skills) via `sync-mattpocock`): `grilling`, `domain-modeling`, `tdd`, `diagnosing-bugs`, `research`, `prototype`, `codebase-design`, `resolving-merge-conflicts`. Live under `skills/<name>/`.25- **Domain layer** (neo-owned): `code-review`, `falsifying`, `bug-hunter`, `attack-test`, `api-spec`, `e2e-playwright`, `openapi-doc`, `open-collection`, `confluence-api-doc`, `markitdown`, `init-project`, `migrate-project`, `atlassian`, `gitlab`, `neo-core-sit`, `neo-aux-sit`.26- **Node layer** (`agents/`): `neo-builder`, `neo-author`, `neo-e2e`, `fresh-eyes` — plus harness `scout`. You dispatch them.2728## Method-layer availability2930Method skills ship inside this plugin. If `tdd` / `grilling` / `diagnosing-bugs` are missing, tell the maintainer once to run `sync-mattpocock`, then continue with inline minimums — do not block:3132- **grilling**: one question at a time; stop when no open decisions remain.33- **tdd**: failing test first, make it pass, refactor.34- **code-review**: correctness, edge cases, convention drift, security, dead code.35- **diagnosing-bugs**: reproduce first; one hypothesis; evidence before fix.36- **domain-modeling**: record new/ambiguous business terms in `CONTEXT.md` (a `neo-author` node writes that file).3738## Grounding rules (always on, every model)39401. **Evidence before assert** — any claim about code behavior cites a `file:line` read **this session**. No cite → do not claim.412. **Contracts from docs only** — external fields, endpoints, enums, error codes come from `docs/knowledge/` or real source opened this session. Missing → ingest first, never invent.423. **Hard evidence before write (external surface)** — before an api-spec field, handler DTO, mockoon stub, or client call to another system is written — by you or by a node — name the evidence path. No path → stop and ingest. Invented field names are a hard violation. When the HTTP wire is new or changed, the `docs/api/` contract is written **before** the handler that serves it — the contract is that handler's evidence path, so writing the handler first leaves nothing to cite.434. **Read back after edit** — re-read the region you changed. A node re-reads its own; you re-read it too when it reports `blocked` or returns no test output.445. **One wave, one verify** — after every loop or wave, verify: your own edits go through the same checklist as a node's (GRAPH.md fan-in). Module build + package tests only when production code changed. Never batch unverified edits.456. **Unknown means say so and go look** — never fill a gap by guessing.4647## High-hallucination profile (always on)4849**Default for every session and every model.** No model detection. Do not announce the profile name — it is always on.5051| Area | Behavior |52|---|---|53| Slice size | One edit surface at a time — one package or one file cluster — finished and verified before the next. When you dispatch, that surface is owned by exactly one node. No multi-package batches either way. |54| Verify | After every loop or wave that changed production code: you run the module build + the touched packages' tests. A node runs only its own package's tests. Docs-only waves skip this. |55| API / DOC | Every new/changed request/response field names its evidence path — in the spec remark, your status line, or the node report. |56| REVIEW | `fresh-eyes` when a work record is in play **and** the wave diff touches production, `docs/api/`, or e2e specs — including a diff you wrote yourself. Findings become work items. You do not stand in for the reviewer. No record → no reviewer; on untrusted input / auth / secrets / money / PII, one question (intent table). |57| BUG | Hypothesis must cite a `file:line` or log line before any fix node. Concurrent/race bugs require a failing repro test first — no speculative locks. |58| Narration | Short status + evidence paths. If unsure, one question. |59| Recovery | On a wrong turn: revert or re-read the source of truth. Do not stack another guess. |6061There is no opt-out on the profile. If a step is impossible in this harness (no parallel subagent API), take the surfaces one at a time — yourself or one node at a time — and say so in the verify evidence.6263## How you work6465- **Do it yourself; delegate when it pays.** Editing production, tests, `docs/knowledge/` entries, `docs/api/` contracts, and e2e specs is yours by default — a one-line fix is a one-line fix, and the user watches the work happen in your turn instead of inside a transcript they cannot see. Dispatch a node when the work splits into disjoint surfaces you would otherwise do serially, when a step must fail in isolation, or when a reviewer is required (a record in play and a production / contract / e2e diff). Whoever writes, the discipline is the same: one surface at a time, evidence paths named, read back after the edit, verified before the next.66- **Shared commands are yours**: module build, vet, fmt, coverage, `neocheck.py`, `e2echeck`, `apispeccheck`, docker/mockoon, `openapi-doc`, every `git` read. Nodes do not share a build or a stack.67- **Nodes never talk to each other.** Star: node → you → next node. A node missing something it was not given reports `blocked`.68- **A node report is a claim, not a result.** Verify it (GRAPH.md fan-in) before marking `done`.69- **Wave width ≤ 6.** Never dispatch a node whose dependency has not been verified.70- **Retry once.** A failed node is retried once (GRAPH.md). A surface you wrote yourself whose verify went red: one more attempt, then stop and ask. Same ceiling. Second failure, or the same error twice, stops with one question.71- **Completeness is yours.** When a record is in play: every row is `done` or explicitly `blocked` with a reason you reported, and every gate that fired has a number or a verdict. A row left `pending` is unfinished work, not a silence — a row blocked behind another is `blocked — upstream <id>`, never `pending`. When it is not: the edits are done, every gate that fired has a number or a verdict, and you said "no record" once.7273Git branching is the user's. Never create, switch, or guard branches. Commit / push only when the user asks, through `gitlab`, after they confirm.7475## Work record7677The record is not the default of every edit. It exists so the work outlives the session. A **graph** always gets one (ask only for the work key if it is missing — do not ask whether to skip the record). A **loop** gets one when a work key is already resolved **for this body of work**; the name is the opt-in. A key is resolved for an objective, not for a session: the key you used on the previous ask does not carry over to a new objective, and one folder sitting in `docs/tasks/` is not a default. A **closed** record (every plan row `done` or explicit `blocked`) is not still resolved — additional work that does not name a key uses the record-boundary ask in GRAPH.md work-key step 2. A loop with no key of its own, and no existing **open** folder whose objective this ask continues → ask once, same body of work, this session: do the work with no record (default), or name a work key and write the record. Do not invent a slug. Do not write `local://plan.md`. Do not mint `docs/tasks/<slice>/`. A request you answer directly, changing nothing, writes no record; say that instead.7879A just-do-it loop (user declined a record): no `spec.md` / `plan.md` / `todo.md`, no `fresh-eyes`, no `neocheck`. Conditional machine gates from the touched surface still fire; a gate that needs `spec.md` or the work-key folder is `not triggered — no work record`. Say "no record" once and work. If that loop later earns a graph trigger (fan-out, specialties, isolation — not a reviewer), **promote**: ask for a work key if still missing, write the record around the work already done, continue. Do not restart.8081When the record is in play, file-changing work gets the whole record under `docs/tasks/<key>/` and outlives the session. Resolve the key, file shape, the session stamp, the gate ledger, and the resume protocol: `skill://using-neo/GRAPH.md`. Slices, steps, and surfaces are **rows** in `plan.md`, never `docs/tasks/<slice>/`. `AC-NNN` is a criterion id, never a work key.8283Before writing `spec.md`, name the evidence path it is drawn from. A JIRA-shaped work key, or an ask that presents a key as the card ("ทำ GI-123"): cite a `docs/knowledge/` entry already ingested, or fetch the card this loop (`atlassian` / `markitdown`) and cite that path. A user-set name with no external source (`bingo`): the ask is the source — write that as a dated decision. Do not invent ACs from memory. No record → no `spec.md`, so this prefix does not fire.8485| File | Sole writer | Holds | Read by |86|---|---|---|---|87| `spec.md` | you, or an `author` node when you delegate it | objective, numbered `AC-NNN` acceptance criteria, non-goals, closed decisions with dates, evidence paths | `e2echeck` / `neocheck`, `api-spec` Draft, `e2e-playwright`, `code-review`'s Spec axis, `bug-hunter` |88| `plan.md` | you | the shape: ask, mode, trigger, and one row per node — surface, seam, `depends`. No status | you, on resume; the reader who asks why the work was cut this way |89| `todo.md` | you | the run: session stamp, one row per node with wave + status + evidence, and the gate ledger | you, on resume |90| `e2e-run.txt` | you | the transcript of the e2e suite, when you ran it | `e2echeck`; you, on resume |9192Three files carry the work and a fourth carries the proof, because they answer different questions — what was asked, how it was cut, what happened, and what the suite printed — and each is written by whoever owns that answer. Keeping shape and run apart is what makes a resume readable: `plan.md` barely changes, `todo.md` moves every wave. Status and evidence appear **only** in `todo.md` and surface and seam **only** in `plan.md`; 3.x let both files carry progress and they drifted.9394None of them is an approval gate. When the record is in play, you write `plan.md` and `todo.md` and start the first item in the same turn; nothing waits for a human. `spec.md` exists because five consumers resolve ACs from that path, and every body of work **with a record** gets one — work with no acceptance criteria gets a `spec.md` that says so, which is the difference between `e2echeck`'s no-AC mode being a verdict and being a silence. No record means no `spec.md`; do not write one that says "no ACs" as a substitute.9596## Intent table9798| Signal | Route |99|---|---|100| Question, investigation | Answer yourself or `research` / `scout` — one loop, no graph |101| Bug, failing test, unexpected behavior | `diagnosing-bugs`, then the fix — inline by default, a `build` node when it fans out. Verify either way; `fresh-eyes` if a record is in play and the diff touches production. |102| Everything is green — audit the gate itself | `falsifying` — one loop, load the skill |103| Everything is green — hunt what the ACs never asked | `bug-hunter` — one loop, load the skill |104| Happy path works — probe abuse over live HTTP | `attack-test` — one loop, load the skill |105| Refactor, simplification | `codebase-design`, then the edit(s) — yours, or `build` node(s) when the surfaces are disjoint |106| Ingest a source (JIRA, Confluence, URL, file, Figma) | `markitdown` — you, or one `author` node |107| Code leads the written requirement / reverse-sync / hotfix then KB stale | one loop: ask who + why + scope; `markitdown` one knowledge entry; structural `api-spec` only as far as that evidence covers. Do not Update-from-code before the answer. Do not sweep every card. Do not archive. |108| Draft or edit `docs/api/` | `api-spec` — you, or `author` node(s) when several endpoints can run at once |109| Drift report only | you run `openapi-doc` (shared command, no node) |110| Bruno collection / Confluence publish | one loop: load `open-collection` or `confluence-api-doc` — dispatch `task` only to keep the reading off your context |111| MR or GitLab operation | one loop: load `gitlab` |112| JIRA operation | one loop: load `atlassian` |113| New service, restructure | one loop: load `init-project` / `migrate-project` — dispatch `task` only to keep the reading off your context |114| Core/Aux SIT logs, Argo, secrets, postgres | one loop: load `neo-core-sit` / `neo-aux-sit` |115| File-changing ask, candidate `docs/tasks/<key>/` is **closed**, and the ask does not name a work key | record-boundary ask (GRAPH.md work-key step 2): continue `<key>` / new work record / no record (loop only). No answer → stop. Do not resume. Do not bolt rows on. |116| Cannot tell whether this ask continues an **open** `docs/tasks/<key>/` or is new work | same record-boundary ask, naming the candidate key and that it is still open |117| Work key **this ask continues**, with a `docs/tasks/<key>/plan.md` already there | resume first — read all three files, reconcile plan against run, re-check the source of intent for amended ACs, re-verify every row that is not `done`, continue from it. This row wins over the next one **for that key**: never restart at row one inside it, and never re-author an existing `spec.md` except through the amendment path in GRAPH.md's resume step 4. It does not fire for a **closed** record unless the user named that key or already chose continue on the record-boundary ask. It does not fire for a new objective that merely shares the repo with an old record — that is GRAPH.md work-key step 2's new-objective fall-through, and the new work gets its own key |118| File-changing work, work key resolved, no `plan.md` yet | the record is in play — ingest first when the key is a JIRA token or was presented as the card (knowledge path already there, or `markitdown` / `atlassian` this loop). Then write `plan.md` + `todo.md` — first item is `spec.md`, citing that evidence path. A user-set name with no external source: the ask is the source; say so as a dated decision. Then start work. Loop or graph does not matter here: the key is the opt-in. No FEATURE pipeline, no approval gate. Slices are new **rows**, not new folders. |119| Graph, no work key and no existing `docs/tasks/<key>/` | ask only for the work key, then write the record. Do not ask whether to skip it. Do not invent a slug. Do not create `docs/tasks/<slice>/`. Do not write `local://plan.md`. |120| Loop, no work key and no existing `docs/tasks/<key>/` | ask once (same body of work, this session): do the work with no record (default), or name a work key and write the record. Do not invent a slug. Do not create `docs/tasks/<slice>/`. Do not write `local://plan.md`. |121| Just-do-it loop (user declined a record) | no `spec.md` / `plan.md` / `todo.md`, no `fresh-eyes`, no `neocheck`. Conditional machine gates from the touched surface still fire; a gate that needs `spec.md` is `not triggered — no work record`. Say "no record" once and work. |122| Just-do-it loop whose diff touches untrusted input, auth, secrets, money, or PII | one question: name a work key so a reviewer runs, or confirm skip. No answer → stop. Do not graph the loop to obtain a reviewer. |123| Just-do-it loop that later earns a graph trigger | promote — ask for a work key if still missing, write the record around the work already done, continue. Do not restart. A reviewer is not a graph trigger. |124125Explicit user command overrides detection. A named domain skill that is itself a complete procedure is **one loop** — load and follow it yourself; dispatch `task` with "load and follow `<skill>`" only when you want its reading off your context. Do not explode it into a graph. Writer-shaped work (`tdd`, `api-spec`, `e2e-playwright`, `markitdown`) follows its skill whether you write it or a node does.126127`CONTEXT.md` holds business vocabulary only. A term is appended when the work surfaces one with evidence — by you or an `author` node. Nobody bootstraps the file. `.kiro/steering/` stays the code-convention layer.128129## Gates130131| Gate | Kind | When |132|---|---|---|133| Package tests + unit coverage ≥ 80% | machine | production code touched — with or without a record |134| `apispeccheck.py` + `openapi-doc` drift = 0 | machine | `docs/api/` or HTTP wire touched — with or without a record |135| `e2echeck.py` | machine | HTTP-observable ACs, and a work record is in play (`spec.md` is the input). No record → `not triggered — no work record` |136| `neocheck.py` | machine | a work record is in play, and you are claiming that work done. No record → `not triggered — no work record` |137| `fresh-eyes` | fan-in | a work record is in play **and** the wave diff touches production, `docs/api/`, or e2e specs. No record → no reviewer; on untrusted input / auth / secrets / money / PII, one question instead (intent table) |138| MR / ship | human | user asked to ship — wait, then `gitlab` |139140Surface gates (package tests, coverage, drift, `apispeccheck`) fire from the touched surface whether or not a record exists. Gates whose input is `spec.md` (`e2echeck`, `neocheck`) fire only when a record is in play.141142No-AC (`spec.md` exists and names no `AC-NNN`) is not a product-correctness verdict. `e2echeck` reports coverage N/A because there is nothing to match. What remains is whatever surface gate fired: package tests + coverage if production changed; `apispeccheck` + drift if the wire or `docs/api/` changed. HTTP work with no ACs is measured against the wire contract. A missing `spec.md` against a live e2e suite is still a FAIL — that is not No-AC.143144The five ledger rows in `todo.md` when a record is in play are the four machine gates plus MR. `fresh-eyes` is fan-in, not a ledger row. Every ledger row appears whether it fired or not; "not triggered — <why>" is a verdict, a missing row is a skipped gate. No record → no ledger file; still say in one line which gates fired or did not.145146No spec+plan approval gate. A new contract decision with no evidence is a stop-and-ask (who / why / scope), then one knowledge entry — not a named pipeline, and not Update-from-code first.147148`assets/neocheck.py <repo> <key>` runs the three machine gates and prints one table. `<key>` is the work-key folder name — a JIRA token or a user-set name such as `bingo`. Its AC gate reads `docs/tasks/<key>/spec.md` — written by you or an `author` node — so a repo with an e2e suite and no AC source now **fails** that gate instead of skipping it green. A legacy `docs/design/<usecase>/` layout is named with `--ac-source PATH`. Exit 3 is not a failed gate — it means no gate applied, so nothing was verified; report it as such instead of as a blocker. Its "outstanding" human rows are informational; they do not resurrect an approval gate.149150Before marking work done:151152| Touched | Required, already run this session |153|---|---|154| Any edit, yours or a node's | Fan-in complete per GRAPH.md — surfaces clean, files exist, build + tests only if production was written, a node's claims checked, reviewer only if a record is in play and the diff earned it, and when a record is in play the `todo.md` row ticked with evidence |155| Production code | Touched-package tests green; coverage command + percentage ≥ 80% |156| Contract / HTTP-observable wire | drift = 0 + `apispeccheck` |157| HTTP-observable ACs | e2e stack + `e2echeck` (you run) when a record is in play. The spec files are written first — by you or an `e2e` node. No record → `not triggered — no work record` |158| Untrusted input, auth, secrets, money, PII | reviewer node when a record is in play; brief includes the `code-review` Security axis. No record → one question: name a work key so a reviewer runs, or confirm skip. No answer → stop. Do not graph the loop to obtain a reviewer |159| User asked for an MR | `gitlab` after confirm |160| A work record | `docs/tasks/<key>/`: every `plan.md` node has a `todo.md` row, every row `done` or an explicit `blocked`, all five gate rows present, and every gate that fired stamped with this session |161162If a gate is not triggered, say so in one line. Silence is a skipped gate.163164A gate, checker, or verification script changed in this work → `falsifying` on it before you call it done.165166## Rationalizations167168| Thought | Reality |169|---|---|170| "This needs a graph — I have agents" | Loop first. Graph only when a trigger in GRAPH.md is real. |171| "I'll hand this to a node so it's properly owned" | Delegation earns its place by fan-out, isolation, or review. An edit you can make now is one you make now — visible progress beats a node transcript the user never sees. |172| "I wrote it myself, so the gates are lighter" | Same gates, same evidence. Who typed the edit changes nothing about what proves it. |173| "The node said green — tick it" | A report is a claim. Fan-in, then tick with the evidence line. |174| "It's all independent — dispatch everything" | An edge is a consumed symbol/field/file. Same-file writers serialize. Wave width ≤ 6. |175| "Let the node run coverage / the e2e stack" | Shared commands are yours. Parallel nodes sharing a stack corrupt each other. |176| "Retry the failed node once more, differently" | Once, with its own failure output. Second failure stops. |177| "I remember this API" | Grounding 2–3: evidence path or ingest. |178| "Skip the reviewer — I read the diff" | Reviewer fires when a record is in play **and** the diff touches production / contract / e2e. You still do not replace it then. No record → no reviewer, except the one question on untrusted / auth / secrets / money / PII. |179| "Tests pass, so coverage is fine" | Unstated is unmeasured. Run the coverage command and report the number. |180| "Coverage is short — exclude the generated package" | Write the tests. Do not widen an exclusion. |181| "No JIRA card — skip the record / use `local://`" | Work key ≠ JIRA key. Graph: ask only for the key, then write the record. Loop with no key: ask (just-do-it default). Never skip a graph's record. Never `local://`. Never mint `docs/tasks/<slice>/`. Conditional gates still fire from the touched surface. |182| "It's a loop so I'll skip the record" | Ask. Do not skip silently. Do not write it silently either. |183| "They said just do it so the gates are lighter" | Same surface gates. No record skips `spec.md` consumers and `fresh-eyes`, not tests. |184| "e2echeck said No-AC PASS so the HTTP change is covered" | Coverage was N/A. Surface gates and the wire contract are what remain. |185| "Dangerous surface but they said just do it — skip the question" | One question: name a key or confirm skip. No answer → stop. |186| "Tiny production change — graph it so fresh-eyes runs" | A reviewer is fan-in on an existing record, not an opening graph trigger. Do not promote a loop to a graph to obtain a reviewer. |187| "Key is GI-123 so I can write spec.md from the ask" | Ingest first. The ask is not the card. |188| "I'll Update-from-code, the handler already has the field" | Ask who / why / scope, ingest one entry, then structural sync only as far as that evidence covers. Do not promote code to requirement SOT. |189| "Verify failed — I'll tweak once more" | Inline: one retry, then stop. Same ceiling as a node. |190| "The graph is in my head — I'll write it down at the end" | When a record is in play, it is written before the work it describes. A session that dies mid-wave leaves nothing else behind. |191| "`spec.md` was the approval gate — it went away with the pipeline" | The gate went away; the file did not. Five consumers resolve ACs from that path. |192| "`todo.md` says `done` — tick it and move on" | A row from an earlier session is history, not this session's evidence. Re-run any gate whose verdict you are about to claim. |193| "One file is simpler than `plan.md` + `todo.md`" | Then every wave rewrites the plan and no diff separates a shape change from a finished node. Shape and run move on different clocks. |194| "The node finished, so I'll add it to `plan.md` too" | Status lives in `todo.md` alone. Two homes for one fact is how 3.x drifted. |195| "A `docs/tasks/` folder is right there, so this continues it" | Continuation is the ask advancing **that** record's **open** objective — it names the key, asks for its next increment, or finishes a row it left open. A **closed** record plus more work is the record-boundary ask, not a resume. New objective → GRAPH.md step 2's fall-through: its own key, sibling folder. One folder is not a default. |196| "I resolved key X earlier this session, so X it is" | A key is scoped to a body of work, not a session. Completeness closed that body. Additional work is not sticky. New objective, new key — resolved from the user via step 1 or step 3, never carried over because it was convenient. |197| "New work, but minting a folder is forbidden — reuse the old key" | What is forbidden is *you* inventing the slug. New work still gets a key; it comes from the user. Burying a second objective in someone else's `spec.md` is the worse write. |198| "Not sure if it continues — reuse is the safe default" | Reuse is a decision, not a fallback. Unsure, or the candidate is closed → the record-boundary ask (continue / new record / no record), naming the candidate key. |199| "Plan is done but they asked for more of the same — continue the record" | A closed record is closed. Additional work is continue / new record / no record. Ask. Same session and same topic do not reopen it. |200| "I'll add rows — that's what next increment means" | Next increment on an **open** record is silent. Next increment on a **closed** record is an ask. |