Reference
Rule reference
Every rule grooph checks, by its stable code: what it says, what grooph validate prints when it fires, a document that fires it, and one that does not. E_… is an error; W_… is a warning, which is shown and does not stop anything. The advice about loops and stops is a warning whatever its code begins with, and is given when asked for (--loops).
Generated by scripts/rule-reference.mjs from the rule tables and fixtures/. Do not edit it; CI fails when it is stale.
Graph documents#
Checked by grooph validate <file>; --complete adds five rules for a finished document: E_UNFILLED_SLOT, E_NO_GOAL, and E_IS_TEMPLATE, which any finished diagram passes, and E_NO_TARGET and E_PERSON_STEP_NOT_COMPILED, which only a package for a harness needs (experimental) and a plan keeps. A filled-in diagram is worth checking with --complete, because it is what reports a {{slot}} left unfilled. --for-export is the flag's old name, and still works. --loops gives the advice about loops and stops: six rules given as warnings, under their own codes, and never as errors. The rules are defined in graph-ir.md §3.
E_SCHEMA#
Does not match the schema. The document fails the JSON Schema. The message names the path.
Fails: fixtures/invalid/E_SCHEMA/wrong-version-and-no-nodes.grooph.json prints
error E_SCHEMA /nodes: missing required property "nodes"
error E_SCHEMA /grooph: expected 0, got 1
error E_SCHEMA /id: expected kebab-case id matching ^[a-z][a-z0-9-]*$, got "Wrong Schema"
error E_SCHEMA /version: expected integer, got string
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_DUPLICATE_ID#
An id used twice. An id appears more than once, the graph's own id included.
Fails: fixtures/invalid/E_DUPLICATE_ID/policy-reuses-node-id.grooph.json prints
error E_DUPLICATE_ID id "worker" is used 2 times: nodes[0], policys[0] [at: worker]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_DANGLING_REF#
A reference to nothing. An id the document does not have is named by an edge's from or to; a loop's members, back edges, answerKeyFrom, or a stop's then; a group's members; or a policy's scope.
Fails: fixtures/invalid/E_DANGLING_REF/edge-to-unknown-node.grooph.json prints
error E_DANGLING_REF edge "e-handoff" ends at unknown node "proofreader" [at: e-handoff]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_LOOP_BACK_EDGE#
A back edge that does not loop. A loop's back list is empty, or a back edge does not have both ends among members, or no path inside members leads from its to back to its from.
Fails: fixtures/invalid/E_LOOP_BACK_EDGE/back-edge-outside-members.grooph.json prints
error E_LOOP_BACK_EDGE loop "fix-cycle": back edge "e-check-fail" runs suite → builder, which is not inside the loop's members [at: fix-cycle, e-check-fail]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_GROUP_CYCLE#
A group inside itself. A group holds itself, directly or through other groups. One issue for each ring.
Fails: fixtures/invalid/E_GROUP_CYCLE/group-holds-itself.grooph.json prints
error E_GROUP_CYCLE group "fix" holds itself: fix → notes → fix; groups form a tree [at: fix, notes]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_SECOND_LEAD#
Two leads. More than one agent node has the role lead. A graph is one session, and the lead is that session.
Fails: fixtures/invalid/E_SECOND_LEAD/two-leads.grooph.json prints
error E_SECOND_LEAD the graph has 2 lead nodes ("lead", "placed-lead"); a graph is one session and has at most one lead [at: lead, placed-lead]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_PERSON_LEAD#
A person as the lead. The node with the role lead is a person's step. The lead is the harness's own session.
Fails: fixtures/invalid/E_PERSON_LEAD/the-lead-is-a-person.grooph.json prints
error E_PERSON_LEAD node "lead" is the lead and is marked as a person's step; the lead is the harness's own session: give the step another role, or leave it an agent's [at: lead]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_NO_TARGET#
No target harness. A package is asked for, and target.harness is missing, blank, or a harness grooph has no compiler for. The lab's compiler also reports it when asked for a harness the document does not name; change the document first with the setTarget op.
Fails: fixtures/invalid/E_NO_TARGET/no-target-harness.grooph.json prints
error E_NO_TARGET export needs a target harness; set target.harness [at: no-target-harness]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_NO_GOAL#
No goal. A package is asked for, and goal is missing or blank.
Fails: fixtures/invalid/E_NO_GOAL/no-goal.grooph.json prints
error E_NO_GOAL export needs a goal; the lead brief is built from it [at: no-goal]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_IS_TEMPLATE#
Still a template. A package is asked for on a document that still has a template block. Fill it in first (templates.md §2).
Fails: fixtures/invalid/E_IS_TEMPLATE/template-exported.grooph.json prints
error E_IS_TEMPLATE "fix-it" is a template; instantiate it first (grooph template use fix-it --name <graph name>) [at: fix-it]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_UNFILLED_SLOT#
An unfilled slot. A package is asked for, and a {{slot}} remains in a string field of a document that is not a template. at names the objects holding it.
Fails: fixtures/invalid/E_UNFILLED_SLOT/slot-left-unfilled.grooph.json prints
error E_UNFILLED_SLOT slot {{test-command}} is still unfilled in "make-it-green", "tests"; replace it with a real value before export [at: make-it-green, tests]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_PERSON_STEP_NOT_COMPILED#
A person's step in a package. A package is asked for on a document, not a template, with a person's step. No package is written; grooph plan writes the plan out instead. One issue names every such step.
Fails: fixtures/invalid/E_PERSON_STEP_NOT_COMPILED/a-persons-step-in-a-graph-for-a-harness.grooph.json prints
error E_PERSON_STEP_NOT_COMPILED "write" is a person's step, and grooph cannot yet hand a step to a person inside a harness, so no package is written; the plan exports as it is (PLAN.md, the picture, the file); make it an agent's if the graph is to run [at: write]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_CRITIC_NOT_ISOLATED#
A critic shares the builder's context. A critic-isolation policy covers an edge into a critic-family agent, and the edge is shared, or it comes from a writer with no evidence list. A critic that is a person's step is exempt.
Fails: fixtures/invalid/E_CRITIC_NOT_ISOLATED/a-person-writes-and-an-agent-judges.grooph.json prints
error E_CRITIC_NOT_ISOLATED edge "e-write-review" runs from writer "write" to critic "review" with no evidence list, but policy "p-critic-isolation" requires critic isolation; list what the critic may inspect [at: e-write-review, write, review]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_OWNERSHIP_CONFLICT#
Two writers, one artifact, no merge. Two writer-family nodes list the same artifact in owns, and no merge node lists it in merges.
Fails: fixtures/invalid/E_OWNERSHIP_CONFLICT/two-writers-one-artifact.grooph.json prints
error E_OWNERSHIP_CONFLICT artifact "src" is owned by writers "planner", "builder" and no merge node lists it in merges; give it one owner, or add a merge node that merges it [at: planner, builder]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_IRREVERSIBLE_NO_GATE#
An irreversible step without a gate. An agent's step with a non-empty irreversible can be reached without a human decision: nothing leads to it; or it is an entry node that only a back edge or its own loop's stop leads to; or an inbound edge neither has approval: true nor starts at a human gate; or a stop other than human continues at it. A person's step that does the irreversible thing itself needs no gate.
Fails: fixtures/invalid/E_IRREVERSIBLE_NO_GATE/a-persons-step-is-no-gate.grooph.json prints
error E_IRREVERSIBLE_NO_GATE node "publish" performs irreversible actions (publish) and can be reached without a human decision through "e-write-publish"; every way in must pass a human: set approval: true on those edges, or start them at a human-gate node [at: publish, e-write-publish]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
W_HOMOGENEOUS_CRITICS#
A critic on its writer's model. A critic-family agent is on the same model as each of its nearest writers (the writer-family agents with a path to it along edges that are not back edges, through no other writer). Same model means the same pin for the named harness, or, where neither is pinned for it, the same tier; with no harness named, the tier and every pin. A critic no writer reaches is not flagged, and people's steps are not compared.
Fails: fixtures/invalid/W_HOMOGENEOUS_CRITICS/critic-same-model-as-builder.grooph.json prints
warning W_HOMOGENEOUS_CRITICS critic "critic" judges "builder" on the same model (tier strong); a critic on a different tier or pin may catch different mistakes [at: builder, critic]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
W_FANOUT_ON_COUPLED#
Fan-out into coupled work. A node marked coupled, or a member of a coupled group, receives an edge with concurrency.max above 1; or two coupled agents share an owns entry.
Fails: fixtures/invalid/W_FANOUT_ON_COUPLED/fanout-into-coupled-node.grooph.json prints
warning W_FANOUT_ON_COUPLED edge "e-plan-migrate" fans out up to 3 at a time into "migrator", which is marked coupled; coupled work should run one at a time [at: e-plan-migrate, migrator]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
W_UNREACHABLE_NODE#
An unreachable node. No entry node reaches the node, along edges or by a loop's stop that continues there. It usually comes with E_CYCLE_NO_STOP or E_DANGLING_REF, and names the stranded nodes.
Fails: fixtures/invalid/W_UNREACHABLE_NODE/cycle-with-no-way-in.grooph.json prints
warning W_UNREACHABLE_NODE node "reviewer-a" is not reachable from any entry node ("writer"); connect it or remove it [at: reviewer-a]
warning W_UNREACHABLE_NODE node "reviewer-b" is not reachable from any entry node ("writer"); connect it or remove it [at: reviewer-b]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
W_NO_TERMINAL#
No reachable stop node. No stop node is reachable from an entry node. Not raised for an empty graph or a fragment template.
Fails: fixtures/invalid/W_NO_TERMINAL/no-stop-node.grooph.json prints
warning W_NO_TERMINAL the graph has no stop node, so the run ends only when no edge is left to take; add one where the work is done [at: no-terminal]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
W_OUTPUT_NOT_WRITABLE#
Outputs it cannot write. An agent other than the lead declares outputs but is allowed neither edit-files nor write-outputs. A person's step is not flagged.
Fails: fixtures/invalid/W_OUTPUT_NOT_WRITABLE/critic-cannot-write-its-review.grooph.json prints
warning W_OUTPUT_NOT_WRITABLE agent "critic" must leave outputs behind but is allowed neither edit-files nor write-outputs, so it cannot write them itself; allow write-outputs [at: critic]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
W_PERSON_FIELDS_NOT_READ#
Agent fields on a person's step. A person's step sets model, effort, skills, allow, or deny, which are kept and not read.
Fails: fixtures/invalid/W_PERSON_FIELDS_NOT_READ/a-model-on-a-persons-step.grooph.json prints
warning W_PERSON_FIELDS_NOT_READ step "write" is a person's and sets model, allow: those are an agent's, and are not read; remove them [at: write]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
W_GROUP_OVERLAP#
A member of two groups. A node or group is a member of two groups, and neither holds the other. Views draw it in the first. Listing a member again in a group that holds it through an inner group is not an overlap.
Fails: fixtures/invalid/W_GROUP_OVERLAP/node-in-two-groups.grooph.json prints
warning W_GROUP_OVERLAP node "writer" is a member of groups "fix" and "notes", and neither holds the other; views draw it in "fix" [at: writer, fix, notes]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
W_UNKNOWN_KEY#
An unknown key. The document has a key the schema does not know. It is kept and not read; the warning shows a typo.
Fails: fixtures/invalid/W_UNKNOWN_KEY/typo-in-optional-field.grooph.json prints
warning W_UNKNOWN_KEY unknown key "efort" at /nodes/0/efort; did you mean "effort"?; it is kept, but grooph does not read it [at: writer]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
W_DOC_TOO_LARGE#
A document too large. The canonical form without layout is over 24,000 characters (about six thousand tokens), the size meant to be rewritten in one pass.
Fails: fixtures/invalid/W_DOC_TOO_LARGE/scout-fanout-too-large.grooph.json prints
warning W_DOC_TOO_LARGE canonical document without layout is 27299 characters, over the 24000-character budget for rewriting in one pass; split it or shorten the briefs [at: docs-audit-fanout]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_CYCLE_NO_STOP#
A cycle with no stop. Take away the back edges of every loop that has a stop; any cycle left is uncovered. The message lists its nodes.
Fails: fixtures/invalid/E_CYCLE_NO_STOP/loop-without-stop.grooph.json prints
warning E_CYCLE_NO_STOP cycle with no stop: builder → critic. Cover it with a loop that has at least one stop. [at: builder, critic]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_JUDGMENT_LOOP_NO_BAR#
A judgment loop with no bar. A loop whose mode is judgment, set or inferred, has no bar, or its bar inspects nothing. An answer-key entry counts only when answerKeyFrom names a node in the graph.
Fails: fixtures/invalid/E_JUDGMENT_LOOP_NO_BAR/taste-loop-without-bar.grooph.json prints
warning E_JUDGMENT_LOOP_NO_BAR judgment loop "polish-cycle" has no bar; name what stopping looks like and what the critic inspects [at: polish-cycle]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
E_STOP_NOT_INSPECTABLE#
The only stop is an adjective. Every stop of a loop is bar-passed, and the bar is missing or inspects nothing.
Fails: fixtures/invalid/E_STOP_NOT_INSPECTABLE/only-stop-is-an-adjective.grooph.json prints
warning E_STOP_NOT_INSPECTABLE loop "polish-cycle" stops only when its bar passes, but the bar names nothing a critic can inspect. An adjective is not a bar. [at: polish-cycle]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
W_LONG_LOOP_NO_BUDGET#
A long loop with no budget. A loop has no budget stop and no max-iterations stop of 5 or fewer.
Fails: fixtures/invalid/W_LONG_LOOP_NO_BUDGET/twelve-rounds-no-budget.grooph.json prints
warning W_LONG_LOOP_NO_BUDGET loop "fix-cycle" has no budget stop and its max-iterations stop allows 12 rounds; add a budget stop, or cap it at 5 rounds or fewer [at: fix-cycle]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
W_ASPIRATION_AS_ACCEPTANCE#
The aspiration used as acceptance. A bar's aspiration is the same text as its acceptance, ignoring case and spacing, or acceptance is blank while aspiration is set.
Fails: fixtures/invalid/W_ASPIRATION_AS_ACCEPTANCE/aspiration-equals-acceptance.grooph.json prints
warning W_ASPIRATION_AS_ACCEPTANCE loop "headline-cycle" uses its aspiration as its acceptance; the acceptance must be reachable, the aspiration is only direction [at: headline-cycle]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
W_ONLY_MAX_ITERATIONS#
Only a round cap. Every stop of a loop is max-iterations.
Fails: fixtures/invalid/W_ONLY_MAX_ITERATIONS/only-max-iterations.grooph.json prints
warning W_ONLY_MAX_ITERATIONS loop "fix-cycle" stops only on max-iterations, which is a safety backstop, not a definition of done; add a bar-passed, budget or human stop [at: fix-cycle]
Passes: fixtures/valid/fix-until-green.grooph.json validates with no issues.
Operation maps#
Checked by grooph validate <file> on a *.grooph-map.json. The rules are defined in operation-map.md §3.
E_SCHEMA#
The document is not valid JSON or fails the map schema. The message names the path.
Fails: fixtures/maps/invalid/E_SCHEMA/session-without-harness.grooph-map.json prints
error E_SCHEMA /sessions/1/harness: missing required property "harness" [at: builder]
Passes: fixtures/maps/valid/two-sessions.grooph-map.json validates with no issues.
E_DUPLICATE_ID#
An id is used more than once across the map, its lanes, its people, its sessions, and its handoffs.
Fails: fixtures/maps/invalid/E_DUPLICATE_ID/handoff-named-as-a-session.grooph-map.json prints
error E_DUPLICATE_ID id "builder" is used 2 times: sessions[1], handoffs[0] [at: builder]
Passes: fixtures/maps/valid/two-sessions.grooph-map.json validates with no issues.
E_DANGLING_REF#
A session names a lane that does not exist, or a handoff starts or ends at an id that is neither a session nor a person.
Fails: fixtures/maps/invalid/E_DANGLING_REF/unknown-lane-and-session.grooph-map.json prints
error E_DANGLING_REF session "builder" is in unknown lane "cloud" [at: builder]
error E_DANGLING_REF handoff "h-result" ends at unknown session "planer"; did you mean "planner"? [at: h-result]
Passes: fixtures/maps/valid/two-sessions.grooph-map.json validates with no issues.
E_HANDOFF_NO_CARRIER#
A handoff has no carrier, or its carrier does not say what carries it: a branch or pull-request with no repo, a review-page with no where, an other with no name, or a person with no who (unless the handoff starts at a person, who is then the carrier).
Fails: fixtures/maps/invalid/E_HANDOFF_NO_CARRIER/branch-without-a-repo.grooph-map.json prints
error E_HANDOFF_NO_CARRIER handoff "h-result" ("builder" → "planner") is carried by a branch that is not named; set carrier.repo [at: h-result]
Passes: fixtures/maps/valid/two-sessions.grooph-map.json validates with no issues.
W_CARRIER_CANNOT_CROSS#
A carrier that stays inside one harness or one account joins two sessions that are not in the same one: a session-message or scheduled-message between sessions whose lanes have different accounts or whose harnesses differ, or a review-page between sessions whose lanes have different accounts (a published page belongs to the account that published it). Such a handoff is usually carried by a person.
Fails: fixtures/maps/invalid/W_CARRIER_CANNOT_CROSS/review-page-across-accounts.grooph-map.json prints
warning W_CARRIER_CANNOT_CROSS handoff "h-plan" ("planner" → "builder") crosses accounts "one account" and "another account" by review page; a published page belongs to the account that published it, so something else carries this: a branch, a pull request, or a person [at: h-plan, planner, builder]
Passes: fixtures/maps/valid/two-sessions.grooph-map.json validates with no issues.
W_NOTIFY_NOT_PERSON#
A notification is sent to a session. A notification reaches a person; a session is reached by a message, a branch, or a pull request.
Fails: fixtures/maps/invalid/W_NOTIFY_NOT_PERSON/notification-to-a-session.grooph-map.json prints
warning W_NOTIFY_NOT_PERSON handoff "h-back" sends a notification to session "lead"; a notification reaches a person, and a session is reached by a message, a branch or a pull request. Name the person it reaches, or the carrier that reaches the session [at: h-back, lead]
Passes: fixtures/maps/valid/two-sessions.grooph-map.json validates with no issues.
W_SESSION_ISLAND#
A session or a person has no handoff in or out. A person whose only handoffs are to themselves is one too.
Fails: fixtures/maps/invalid/W_SESSION_ISLAND/a-session-nothing-touches.grooph-map.json prints
warning W_SESSION_ISLAND session "watcher" has no handoff in or out: nothing reaches it and it reaches nothing [at: watcher]
Passes: fixtures/maps/valid/two-sessions.grooph-map.json validates with no issues.
W_NO_RETURN#
A session receives a handoff and hands nothing on.
Fails: fixtures/maps/invalid/W_NO_RETURN/work-in-nothing-out.grooph-map.json prints
warning W_NO_RETURN session "builder" is handed work and hands nothing on: add the handoff its results leave by, and name the carrier [at: builder]
Passes: fixtures/maps/valid/two-sessions.grooph-map.json validates with no issues.
W_GRAPH_UNRESOLVED#
A session's graph pointer was looked up and is not a graph document that can be read. Only grooph validate looks, and only for a path ending in .json, beside the map; a URL or an id is not checked.
Fails: fixtures/maps/invalid/W_GRAPH_UNRESOLVED/graph-pointer-to-nothing.grooph-map.json prints
warning W_GRAPH_UNRESOLVED session "builder" points at graph "missing/nowhere.grooph.json", which is not a graph document that could be read [at: builder]
Passes: fixtures/maps/valid/two-sessions.grooph-map.json validates with no issues.
W_UNKNOWN_KEY#
A key the schema does not know. It is kept, as in a graph.
Fails: fixtures/maps/invalid/W_UNKNOWN_KEY/misspelt-lifetime.grooph-map.json prints
warning W_UNKNOWN_KEY /sessions/0/lifetme: unknown key "lifetme"; did you mean "lifetime"? [at: planner]
Passes: fixtures/maps/valid/two-sessions.grooph-map.json validates with no issues.
W_DOC_TOO_LARGE#
The canonical form is over 24,000 characters, the same limit as a graph's. Draw a family as one session with a count, or split the map.
Fails: fixtures/maps/invalid/W_DOC_TOO_LARGE/a-novel-of-roles.grooph-map.json prints
warning W_DOC_TOO_LARGE the map is 27,988 characters in canonical form, over the 24,000 a model rewrites in one pass; draw families as one session with a count, or split the map [at: two-sessions]
Passes: fixtures/maps/valid/two-sessions.grooph-map.json validates with no issues.