Command-line tool that runs graph validations against an ipm
graph. Use it to catch structural problems in your .ipmt
files, in markdown docs that embed ipmt blocks, or in graphs
produced by other tools (graphJSON).
For the full catalog of rules with severities and rationale, see ipm-validator-rules.md. For heuristic modelling-tip checks that are not part of this strict tool, see ipm-modeling-tips.md.
13 graph-validation rules (mix of error, warning, info). The full list is in ipm-validator-rules.md; a quick summary:
- Structural invariants (errors): self-loop, duplicate edge per pair, SST type-pair conformance.
- Implicit-relation redundancy (warning): parent thing's part-of-event edge when a sub-thing already participates.
- Acyclicity (errors): PartOf is a DAG, LeadsTo is a DAG, happens-before (the composition of the two) is acyclic.
- Temporal / shape (warnings): sibling sub-events of the same parent should declare a leads-to ordering; a leads-to edge should not dip into a container that the predecessor is unrelated to; leads-to connects WHOLE (outermost) events, not a part-of sub-part (IPMV2.9).
- Hygiene (info): stranded thing, stranded event.
- Undecided kinds (IPMV1.5): Unresolved (grey) node kinds are
flagged as a warning by default, promoted to an error under
--strict-undecided(the publish gate).
Every rule is a deterministic graph predicate. False positives are not expected; if you hit one, please file an issue.
ipm-validate is part of this repository. Build or run with
the usual Go workflow:
go build -o ./bin/ipm-validate ./cmd/ipm-validate
# or run directly:
go run ./cmd/ipm-validate --helpipm-validate [--in <file>] [--json] [--in-json|--in-md] [--list-checks] [--strict-undecided]
Reads from stdin when --in is omitted.
The tool auto-detects the input mode from the file extension, or you can force it with a flag.
| Mode | Detected by | What it does |
|---|---|---|
| ipmt (default) | no flag, no recognised extension | Parse with pkg/ipm/parser, run checks. |
| markdown | --in-md or .md extension |
Extract every ```ipmt fenced block, parse and validate each independently, adjust finding line numbers so they point at the markdown file. |
| graphJSON | --in-json or .json extension |
json.Unmarshal into an IpmGraph (e.g. output from cmd/ipmt-parse), run checks. |
By default, findings print to stderr in compiler format:
demo.md:42:17: warning: IPMV2.7: sibling sub-events "edit-loop" and "compile-loop" of "workflow-001" have no declared leads-to order
suggest: add "edit-loop" --> "compile-loop" (or reverse), or mark them parallel with "edit-loop" --- "compile-loop"
With --json, findings emit as a JSON array suitable for
editor integration:
ipm-validate --in foo.ipmt --json | jq '.[] | select(.severity == "error")'| Code | Meaning |
|---|---|
| 0 | No findings. |
| 1 | Warnings or info only. |
| 2 | Parse or read error (could not run the checks). |
| 3 | At least one error-severity finding. |
CI integration: treat exit codes 0 and 1 as "tool ran
successfully" and inspect the JSON for errors, OR use the exit
code directly to fail builds only on errors (!= 0 && != 1).
| Flag | Default | Meaning |
|---|---|---|
--in <file> |
stdin | Input file path. |
--in-json |
false | Force graphJSON input mode (overrides extension). |
--in-md |
false | Force markdown input mode (overrides extension). |
--json |
false | Emit findings as JSON instead of compiler-style text. |
--list-checks |
false | Print one line per registered check (code + description) and exit. |
--strict-undecided |
false | Treat Unresolved (grey) node kinds as errors (publish gate). An unresolved or defaults flag (fence meta or # ipmt: pragma) opts that block/file out of the promotion. |
ipm-validate --in examples/murder-simple.ipmtfind examples docs -name '*.ipmt' -print0 \
| xargs -0 -n1 ipm-validate --inipm-validate --in docs/example.mdThe tool extracts each ```ipmt block, parses it
independently (so name re-use across blocks is fine), and
reports any findings with line numbers pointing at the
markdown file's line — the same way a typical linter
integrates with editor quickfix lists.
ipmt-parse --in foo.ipmt --pretty=false \
| ipm-validate --in-jsonUseful when a separate tool produced JSON and you want to verify it before consuming it.
ipm-validate --in graph.json
ipm-validate --in graph.json --json | jq '. | length'ipm-validate --list-checksOutput is code<TAB>description, one per line. Machine-readable
and stable.
if ! ipm-validate --in foo.ipmt > /dev/null; then
case $? in
1) echo "ipmt has warnings (non-blocking)" ;;
2) echo "ipmt failed to parse" ;;
3) echo "ipmt has hard errors" ;;
esac
fiBy design, ipm-validate does not run:
- Heuristic / fuzzy checks — see ipm-modeling-tips.md. Those are catalogued separately so this tool can stay false-positive-free.
- Cross-block / cross-file checks — name collisions
across multiple ipmt blocks or files belong in the
ipm-collectiontool, not here. - Parser-only syntactic rules — identifier shape, tooltip quoting, three-dash near-to, etc. The parser enforces these at parse time; no point in re-running them.
- Layout validation — positional / grid-alignment checks on rendered SVG layouts are out of scope; this tool validates graph semantics only.
The validator is also available as a Go package for programmatic use:
import (
"github.com/infinite-pm/ipm-tools/pkg/ipm/parser"
"github.com/infinite-pm/ipm-tools/pkg/ipm/validate"
)
g, _ := parser.ParseIPMTBytes(src, "in-memory")
findings := validate.Run(g)
if validate.HasErrors(findings) {
// ...
}Each individual check is exposed as a type implementing
validate.Check (Code() / Description() / Run(*IpmGraph)).
validate.AllChecks() returns the canonical list.
- ipm-validator-rules.md — the rule catalog with severities and rationale.
- ipm-modeling-tips.md — heuristic modelling checks that are NOT in this tool by design.
- ipmt-spec.md — the ipmt language spec.
- ipmt-parser.md — what the parser enforces (and the validator does not need to re-enforce).