AI-native semantic compiler and traceability graph for spec-driven delivery. It scans human sources, graph-native contracts, code, tests and provider facts, then compiles bounded implementation context, evidence gates and Git-native change fingerprints.
BA projects have hundreds of interlinked markdown documents. Cross-references between them break silently — renamed IDs, missing links, conflicting numbers, stale content. Manual checking doesn't scale.
graph-ba turns your documents into a queryable graph and lints them automatically. Artifact types and ID patterns are defined in a TOML config — the tool works with any naming convention.
The full knowledge graph keeps provenance and relation semantics useful for agents and humans:
- artifact
origincomes fromtypes.<TYPE>.originand can classify whole artifact classes ashuman,derived,canonical,evidence, etc.; - edge
relation_typedistinguishes rawMENTIONS/INDEXnavigation from declared semantics and proof.
Origins are project-configurable. Relation types are intentionally small and
generic: graph-ba ships the canonical vocabulary (CONTAINS, TRACES_TO,
DEPENDS_ON, IMPLEMENTS, VERIFIES, RENDERS, plus navigation/diagnostic
edges such as MENTIONS, INDEX, TRACE_GAP and CANDIDATE_TRACE). Code uses
IMPLEMENTS; tests and evidence use VERIFIES; provider/file/line provenance
records where the edge came from. Projects should normally
configure a sparse matrix of allowed class-to-class edges instead of inventing
new relation words. Runtime/framework-specific facts should be exported by
adapter packages as graph-native artifact blocks; graph-ba core imports those
blocks without knowing the framework.
graph-ba does not copy truth through separate spec, plan and task documents. The stable-ID graph is the contract; views, plans and worklists are compiled:
human intent / source thesis
-> candidate semantic delta
-> human-approved proposal fingerprint
-> bounded implementation context
-> code + test + runtime proof
-> release verdict
-> accepted graph + drift detection
The database retains five logical layers, while commands project only the
layer required by the current decision: source, contract, proof, change and
navigation. Ordinary prose never becomes a strong trace automatically. An ID
occurrence is MENTIONS. Only a declared attribute/link or an observed proof
boundary creates a strong edge. AI may propose CANDIDATE_TRACE, but gates
ignore it until a change promotes it to a declared relation.
$ graph-ba import
Imported: 356 artifacts, 2059 edges, 16 semantic clusters
$ graph-ba lint
── Incompleteness markers (24) ──
[WARN] BP-08 ...md:80 TODO: manual stop-list ...
── Empty sections (29) ──
[WARN] BP-02 ...md:112 empty section "Exceptions"
── Terminology vs glossary (81) ──
[INFO] BP-09 ...md:22 "Courier" → canonical "Курьер"
Lint: 134 WARN, 81 INFO
$ graph-ba audit
── Issues (47) ──
DANGLING (3), COVERAGE_GAP (8), MISSING_BIDIR (12) ...
── Review Candidates (15) ──
HIGH REQ-99 DANGLING
HIGH F-01 BRIDGE, CYCLE
$ graph-ba review F-01 --semantic
REVIEW: F-01 — Order Management
⚠ [GAP] No links to type: BR (business rules)
── LINKED ARTIFACTS (8) ──
→ REQ-01 — Must manage orders ...
Python 3.11+.
uvx --from git+https://github.com/vgmakeev/graph-ba graph-ba --help
# or
uv tool install git+https://github.com/vgmakeev/graph-bagraph-ba init # create graph-ba.toml template
# edit graph-ba.toml — define your artifact types and scan rules
graph-ba import # scan docs → build graph
graph-ba lint # content quality: TODOs, empty sections, terminology, staleness
graph-ba audit # structure quality: dangling refs, cycles, coverage gaps
graph-ba diff F-01 # semantic/graph Git delta + scoped gaps, no CHG required| Command | What it does |
|---|---|
import |
Scan artifacts and build SQLite DB |
lint [ID] |
Content lint: TODO markers, empty sections, terminology, staleness, code coverage |
audit |
Structural audit: dangling refs, cycles, coverage gaps, bottlenecks |
validate <ID> |
Deterministic per-artifact gate: PASS/FAIL verdict, exit 1 on FAIL |
review <ID> --semantic |
Full text of all linked artifacts for deep validation |
search <query> |
FTS5 full-text search |
node <ID> |
Node details + neighbors |
path <from> <to> |
Shortest path between artifacts |
impact <ID> |
Cascade analysis |
coverage |
Cross-layer coverage matrix |
matrix |
Sparse JSON matrix of typed artifact relationships |
artifact-state |
Fingerprints + computed implemented/verified/changing/stale state |
diff [ID] [--base-ref REF] |
Manifest-free Git review: canonical/graph delta, impact and scoped gaps before/after |
change init/discover/diff/context/compile/check/approve/status |
Git-native semantic change workflow |
gate <ID> |
Explore/dev/review/release readiness gate |
evidence-plan <ID> |
Classify scoped AC and explain required test/evidence kinds |
graph <ID> |
Agent-facing JSON graph slice with nodes, typed edges, content excerpts and findings |
pack <ID> |
Agent pack for a change, screen family, screen or artifact |
code-refs |
Code → artifact links (@trace comments) |
sql <query> |
Raw SQL |
All commands: --json for machine output, --root/--db for paths.
Canonical requirements remain normal stable-ID Markdown. For a small or already-understood change, edit the owning file and review the result directly:
graph-ba diff CONTRACT-ORDERS
graph-ba diff CONTRACT-ORDERS --base-ref origin/dev
graph-ba diff CONTRACT-ORDERS --json > reports/graphba/orders-diff.jsonThe default base is the current branch upstream, then origin/HEAD,
origin/main, main or HEAD. The complete Git file delta remains visible,
while the human summary lists only canonical artifacts reachable from the
selected target. It also compares gate findings and agent worklist before and
after: introduced, resolved and persistent. Without an ID, diff still
returns the repository-wide stable-ID and typed graph delta. Use a CHG-*
manifest only when proposal fingerprint, human approval or rebase-conflict
control is required.
Read commands keep themselves honest: on an empty or stale database they
rebuild the graph automatically before answering (import is cheap). Disable
with --no-auto-import to get a hard error on empty and a stderr warning on
stale instead — a silently clean result on a graph that was never imported is
the worst failure mode for agent workflows.
graph-ba validate F-01 # ✓/✗/⚠ checks + VERDICT: PASS|FAIL
graph-ba --json validate F-01 # {"id", "verdict", "checks": [...]}Fail-level checks: artifact is defined, all outgoing refs resolve, required sections present, expected cross-layer links exist. Warn-level (don't fail): TODO markers and missing test evidence. Bidirectional links are not a default modeling goal; use incoming/outgoing graph queries instead of duplicating edges. Exit code 0 on PASS, 1 on FAIL — usable directly in CI and agent loops.
graph-ba audit --write-baseline baseline.json # snapshot current issues
graph-ba audit --baseline baseline.json # exit 1 only on NEW issuesEvery issue gets a stable fingerprint (DANGLING:REQ-99,
COVERAGE_GAP:FEAT:REQ:F-02, ...). With --baseline, known issues are
tolerated, resolved ones reported, and only new regressions fail the run —
so audit stays useful on corpora with hundreds of legacy issues.
graph-ba matrix \
--source-type TEST \
--target-type REQ \
--relation VERIFIES \
--out reports/graphba/test-req-matrix.jsonThe output is graph-ba.sparse-matrix.v1: typed nodes plus sparse entries
like TEST --VERIFIES--> REQ with file/line/context evidence. Use it as
machine-readable input for agent packs, CI gates and project dashboards.
graph-ba artifact-state AC-ORD-001 \
--snapshot .graphba/state/accepted-fingerprints.json
graph-ba artifact-state \
--write-snapshot .graphba/state/accepted-fingerprints.json \
--out reports/graphba/artifact-state.jsonManual lifecycle stays small: draft, planned, accepted, archived.
Everything else is computed from graph facts:
implemented: observed implementation edge exists;verified: test/evidence edge exists;changing: activeCHG-*contains the artifact;stale: current content/link/observed/evidence fingerprint differs from the accepted snapshot.
Git owns the change lifecycle: branch is the draft, pull request is the proposal, protected review is approval, and merge is acceptance. graph-ba adds one semantic manifest and computes the actual artifact delta from stable IDs; canonical artifacts are edited in place and are never copied into a change directory.
:::artifact type="AC" id="AC-ORD-001" state="planned" origin="canonical" title="Order live updates"
Orders update without reloading the screen.
:::origin may override the type default for a block. Provider adapters must mark
observed aliases as implementation or evidence; sharing a semantic type
with a canonical artifact must not make an observation part of the proposal.
# .graphba/changes/CHG-orders-live-update.yaml
id: CHG-orders-live-update
title: Live updates for orders
intent: Keep the order board current without an operator reload
base_ref: 0123456789abcdef0123456789abcdef01234567
target_ref: main
sources:
- RAC-ORD-014
# Optional discovery hints; the actual scope comes from semantic Git diff.
scope:
- AC-ORD-001The normal agent loop is deliberately short:
graph-ba change init CHG-orders-live-update \
--intent "Keep the order board current without an operator reload" \
--source RAC-ORD-014 --base-ref main \
--worktree ../project-chg-orders-live-update
graph-ba change ready CHG-orders-live-update
# human review of the compiled proposal fingerprint
graph-ba change approve CHG-orders-live-update \
--reviewer "reviewer@example.com" \
--evidence "https://github.com/org/repo/pull/123"
graph-ba change ready CHG-orders-live-updateready refreshes missing observed-provider projections, imports the graph,
compiles semantic delta/impact/evidence/worklist once and prints one explicit
Proposal / Approval / Delivery / Next summary. change check remains the
strict CI gate. compile remains available when only generated files are
needed. discover, diff, context,
graph, path and sql remain drill-down diagnostics; a normal workflow does
not pipe several commands through jq.
change init --worktree PATH is the recommended default. It creates an
isolated change/<change-id> branch without touching a dirty primary checkout,
stores an immutable base_ref commit and remembers the integration
target_ref. Ignored graph-native provider projections and the configured
CodeGraph database are copied into the worktree so it compiles the same local
knowledge graph as the primary checkout. Plain change init switches the current clean checkout to a
change branch. Use --no-branch only when the caller owns the Git lifecycle.
change discover starts with manifest sources and scope; free-text search
only supplements those seeds. change diff preserves the complete Git file
list for delivery review and also separates contract_files,
supporting_files and delivery_files, so unrelated dirty-worktree edits are
visible without being confused with the semantic proposal.
change compile builds separate base, proposed-contract and delivery views,
then writes the typed graph delta and transitive impact paths under
reports/graphba/changes/<change-id>/. Historical base graphs are cached per
repository, commit and graph-ba schema in the user cache directory; repeated
compile, context and release checks do not rescan the same Git tree.
change rebase-check compares stable-ID changes on target_ref since the
recorded base and fails only on overlapping artifacts or proposal-policy
changes. change review renders intent, sources, files, semantic/graph delta,
impact, rebase state, approval and delivery findings as one Markdown or JSON
payload.
Proposal fingerprints bind both canonical artifact deltas and the project
files that define graph/gate meaning (graph-ba.toml, project/class matrices
and evidence policy). Changing those files invalidates approval. Duplicate
canonical definitions fail proposal review unless exactly one owning file has
the migration marker:
<!-- graph-ba: canonical-owner -->change add-artifact and change add-link validate type/ID classification,
relation vocabulary, owner ambiguity and target paths before editing. The same
agent-safe operations are exposed through MCP. For a brownfield source,
change add-link writes a stable LNK-* assertion under .graphba/contract/
instead of rewriting the legacy document:
:::link id="LNK-..." source="RULE-LOAD" relation="TRACES_TO" target="AC-LOAD-001" change="CHG-load"
:::The assertion participates in semantic diff and the approval fingerprint.
The canonical artifact is the accepted set of stable-ID graph-native artifact
blocks in normal project files, not the change manifest or a generated bundle.
The manifest records intent, sources, scope hints and the Git base. A human
approval attestation binds reviewer, external review evidence, review commit,
base commit and canonical proposal fingerprint. Proposal files must be
committed before approval; the approval record must itself be committed before
release accepts it. Later contract/policy edits or a non-ancestor review commit
make it stale. Implementation and test edits do not invalidate approval. A
protected PR/branch remains the external trust boundary. Merge and Git history
accept and archive the change. change accept and change archive remain
compatibility commands for the legacy directory layout.
Agents can use the same service through MCP tool ba_diff or the full change
workflow tools ba_change_init,
ba_change_discover, ba_change_diff, ba_change_context,
ba_change_check, ba_change_rebase_check, ba_change_add_artifact,
ba_change_add_link and ba_change_status. Approval is deliberately CLI-only
so an agent-facing MCP connection cannot attest its own proposal.
Use graph --summary for a compact readiness/worklist view, or --json/--out
for the complete agent payload. --view separates intent instead of using one
unbounded traversal:
gate and graph also accept --findings-only and --worklist-only.
Frequently used commands accept --json after the subcommand as well as the
global form before it, for example graph-ba search "orders" --json.
contract: declared semantic contract;delivery(default): bounded contract plus embedded proof summaries;navigation: bounded source context and weak mentions;full: explicit opt-in connected knowledge graph exploration.
The full payload is graph-ba.graph-slice.v1 JSON:
nodes: scoped artifacts with type, origin, source location, computed flags and optional content excerpts;edges: directed typed relationships inside the scope;relation_catalog: relation meanings so agents do not need hard-coded interpretations;evidence_plan: scoped AC classification, required evidence kinds and missing evidence gaps;findings: the same gate findings for the requested mode.
Weak MENTIONS edges are excluded by default; use --view navigation only
when doing broad investigation rather than acceptance or implementation work.
Use pack when a human-readable markdown bundle is preferable.
pass remains the CI exit condition for the selected strictness. verdict and
readiness are more explicit: PASS, PASS_WITH_GAPS, PASS_WITH_UNKNOWN or
FAIL, and READY, PARTIAL, UNKNOWN or BLOCKED.
.graphba/artifact-class-matrix.json can set policy.enforce=true. Each entry
declares one allowed directed class edge. For every unordered pair of different
classes the project chooses one orientation; reverse views query incoming edges
instead of storing reverse duplicates. Same-class lifecycle and symmetric
relations such as CONFLICTS_WITH are exempt.
Adapter defaults can be overridden with policy.class_directions:
{
"policy": {
"enforce": true,
"class_directions": [
{"source_type": "REACT_COMPONENT", "target_type": "SCR"}
]
},
"entries": [
{"source_type": "JNY", "relation": "CONTAINS", "target_type": "FLOW"},
{"source_type": "FLOW", "relation": "CONTAINS", "target_type": "AC"},
{"source_type": "TEST", "relation": "VERIFIES", "target_type": "AC"}
]
}Undeclared strong edges and opposing class directions become architecture findings. Navigation relations are outside acceptance enforcement.
explore never blocks, dev reports warnings, and review enforces the
required_proofs declared by each project artifact class. release also
requires an accepted fingerprint snapshot and rejects stale scope.
graph-ba does not require projects to migrate to JNY, FLOW, RULE,
STATE or any other built-in-looking vocabulary. Artifact classes declare the
semantic capabilities they already provide. A brownfield project can map its
existing BP to flow, BR_RULE and BD to decision, and its own lifecycle
or signal types to state and event. Readiness gates check those capabilities,
not literal type ids.
Proof obligations are also project policy. required_proofs can require
implementation, verification, both, or neither for a class. New artifact
types are justified only when no existing class owns the missing semantic unit,
review lifecycle and stable identity.
Everything is config-driven via graph-ba.toml. Define your own artifact types, ID patterns, scan rules, and validation expectations. The tool doesn't assume any specific naming convention.
[scan]
dirs = ["docs"]
# Define artifact types with regex patterns
[origins.human]
label = "Human primary source"
description = "Client, stakeholder, refined meeting or human dictation input."
[origins.reviewed_derived]
label = "Reviewed derived artifact"
description = "Agent output reviewed by a human analyst."
# Relation terminology comes from graph-ba's small default enum. Override
# [relations.*] only to clarify wording for a project; express project policy
# as a sparse class matrix outside the relation vocabulary.
[types.REQ]
label = "Requirements"
origin = "canonical" # optional provenance class for all REQ nodes
view_role = "contract"
capabilities = ["acceptance"]
required_proofs = ["implementation", "verification"]
ref = '(?<![A-Za-z])(REQ-\d{2,4})(?!\d)'
classify = 'REQ-\d{2,4}'
[types.BP]
label = "Existing business processes"
origin = "derived"
view_role = "contract"
capabilities = ["flow"]
ref = '(BP-\d{2})'
classify = 'BP-\d{2}'
[types.BD]
label = "Existing business decisions"
origin = "derived"
view_role = "contract"
capabilities = ["decision"]
ref = '(BD-\d{2})'
classify = 'BD-\d{2}'
[behavior_model]
target_capabilities = ["screen"]
required_capabilities = ["flow", "decision", "state", "event"]
[types.RAW]
label = "Raw Criteria"
origin = "human"
ref = '(?<![A-Za-z])(RAW-\d{2,4})(?!\d)'
classify = 'RAW-\d{2,4}'
# Where artifacts are defined (heading or table)
[[definitions]]
type = "REQ"
file = "docs/requirements.md" # supports globs
mode = "table" # or "heading"
pattern = '^\|\s*(REQ-\d{2,4})\s*\|'
# Expected coverage between layers
[[coverage]]
source = "FEAT"
target = "REQ"
label = "FEAT → REQ"
# Validation rules
[review]
required_sections = { "FEAT" = ["Goal", "Scope"] }
# Code traceability (// @trace: F-01, REQ-01)
[code]
dirs = ["src"]
coverage_types = ["FEAT", "REQ"]
# Optional symbol-level enrichment. When enabled, graph-ba resolves each
# @trace file:line to the enclosing function/class in a local CodeGraph index.
# Missing indexes or unmatched symbols gracefully keep the existing file node.
[providers.codegraph]
database = ".codegraph/codegraph.db"
# Optional observed-provider refresh used by `graph-ba change ready`.
# Commands execute directly without a shell. Missing outputs trigger refresh;
# `--refresh-providers` forces it and `--no-refresh-providers` skips it.
[[providers.refresh]]
name = "mini-observed"
command = ["make", "graphba-observed"]
inputs = ["admin/src", "backend", "tests"]
outputs = ["reports/graphba/observed"]
# Test traceability — test files become TEST: nodes; any artifact ID
# in a test file (comments, names, asserts) counts as test evidence.
# `coverage` shows a "Test coverage" block per listed type.
[tests]
dirs = ["tests"]
extensions = ["py", "ts", "tsx", "js", "dart"] # default
coverage_types = ["REQ"]
# UI traceability — machine-readable trace sidecars (e.g. a feature-level
# trace.json mapping data-testid → AC IDs) become UI: nodes; any artifact ID
# in them counts as a UI-to-artifact link. `coverage` shows a
# "UI trace coverage" block per listed type.
[ui]
files = ["app/src/features/*/api/trace.json"] # root-relative globs
coverage_types = ["REQ"]
# Content linting
[lint]
glossary_file = "docs/glossary.md"
meetings_dir = "inputs/meetings_refined"
stale_threshold_days = 30
todo_patterns = ["TODO", "TBD", "FIXME", "???"]
# Semantic clusters (for grouping)
[clusters]
"Order Management" = ["REQ-01", "F-01", "BP-01"]
# ID normalization
[normalize]
char_map = { "М" = "M" }Run graph-ba init for a full template with comments.
uv run pytest tests/ -v # 200+ testsMIT