Guides / json-api · Edit on GitHub

rgctl JSON API

Programmatic reference for parsing rgctl output. Every structured CLI command emits one JSON document on stdout when invoked with -f json / --format json.

Canonical JSON reference (includes field catalogs formerly in cli-output-schemas.md).

Source of truth (Rust types): crates/rgctl-service JSON modules (CLI src/cli/*_output.rs re-exports).


Table of contents

  1. Invocation
  2. Schema versioning
  3. Command index
  4. discover
  5. Structured query verbs
  6. blast-radius
  7. metrics
  8. check
  9. slice
  10. inspect
  11. export (file formats)
  12. On-disk JSON after discover
  13. Exit codes
  14. Parsing recipes
  15. semantic
  16. communities
  17. cpg
  18. install

1. Invocation

Global flags

FlagEffect
-f jsonEmit structured JSON on stdout
-r PATHRepository root (default: cwd)
-o FILEWrite stdout payload to a file instead of the terminal
export REPO=/path/to/coolstore
rgctl -r "$REPO" -f json find --type function --limit 5 | jq .
rgctl -r "$REPO" -f json blast-radius ShoppingCartService -o /tmp/blast.json

Stdout vs stderr

Modestdoutstderr
-f json discoverSingle JSON telemetry objectQuiet (errors only) unless -v
-f json (other commands)Single JSON resultErrors / warnings
Default textHuman-readable tablesProgress / info logs

Rule: parse stdout only for JSON. Do not scrape stderr.

Prerequisites

All query commands require a prior successful discover (creates .rgctl/graph.snapshot.bin and related caches). See user-guide.md.


2. Schema versioning

Every JSON payload includes a top-level schema_version integer. Check it before parsing nested fields.

const doc = JSON.parse(stdout);
if (doc.schema_version !== 2) {
  throw new Error(`unsupported blast-radius schema ${doc.schema_version}`);
}
CommandCurrent schema_versionBreaking changes
discover2v2 introduced structured metrics block
blast-radius2v2 added target.language, target.canonical_fqn, metrics.caller_depth_limit
find / callers / callees / relations / inventory / status2 (find/neighbors/relations/inventory); 2 (status)status v2 adds index_current / dirty counts
update1Incremental structural patch
metrics1—
check1—
slice1—
inspect1—
semantic index2CLI index telemetry
semantic query3hits + optional expansion / fusion fields
semantic distill1RBVK matrix write (hash/code-daemon teacher)
communities1list / label
clones1 / 2exact Type-1 / bloom (v1); fragment hammocks (v2) + optional seed
cpg (status / mutations / flows / …)1per-subcommand shapes
install2agent pack write report (list-agents JSON is separate; see §18)

Omitted vs null: optional fields are absent when unset (not null), unless noted otherwise. Empty collections are usually [], not omitted.

Graph topology reuse: slice (text view) and inspect (cfg/pdg) share node/edge shapes documented in the field catalogs below (§ Field catalogs / inspect & slice).


3. Command index

Command-f jsonPrimary keysTypical use
discover✅metricsCI ingestion gates, timing
find / callers / callees / relations / inventory✅entities, edges, countsSymbol lookup, call graph, inventories
status✅status, nodes, edges, index_currentSession snapshot + source staleness
update✅files_affected, node/edge deltasIncremental structural graph patch
blast-radius✅target, metrics, topologyChange-impact automation
metrics✅pagerank, betweenness, communitiesHotspot ranking
check✅passed, violationsCI policy gate
slice✅lines / nodes / edges / taintLine-level analysis
inspect✅layer, nodes, edgesCFG/PDG/dominance dumps
semantic✅hits / functions_indexedOpt-in NL / keyword search
communities✅communities, modularityNamed community labels
clones✅groups, graph_digestExact Type-1 clone groups (code_hash); not semantic search
cpg✅varies by subcommandHybrid CPG façade (slice / inspect are canonical for flow/CFG)
vuln / deps✅variesOSV triage / analyze / deps check (security … aliases same handlers)
security✅same as vuln/deps/taintSoft namespace; no separate JSON schema
install✅writesInstall bundled agent pack (skills + optional commands)
export❌ (file)—Full-graph serialization
serve❌—HTTP dashboard + semantic API (foreground)

Help groups commands as Lifecycle · Query · Analysis · Security · Policy · Meta (rgctl --help command map).


4. discover

rgctl -f json discover PATH [-l LANGS] [-e PATTERNS] [--with-cfg] [--with-taint] [--with-kantra] [--kantra-target NAME] [--full]

Kantra flags (--with-kantra, --kantra-rules, --kantra-catalog, --kantra-target, --kantra-index-only) do not change stdout JSON shape; they add .rgctl/kantra_findings.json and KantraRule nodes in graph.snapshot.bin. See kantra_findings.json.

TypeScript shape

interface DiscoverResponse {
  schema_version: 2;
  command: "discover";
  metrics: {
    files_discovered: number;
    files_indexed: number;
    files_skipped: number;
    nodes_generated: number;
    edges_generated: number;
    duration_ms: number;
  };
  full?: true;
  plan?: { id: "basic_discover" | "deep_pass" | "semantic_index"; status: string }[];
}

With --full, stdout is still one JSON object (full: true + plan). Live stage updates go to .rgctl/pipeline_status.json (schema_version 1, command: "pipeline_status"). GET /api/status on HTTP serve returns the same document.

Example

{
  "schema_version": 2,
  "command": "discover",
  "metrics": {
    "files_discovered": 120,
    "files_indexed": 118,
    "files_skipped": 2,
    "nodes_generated": 1842,
    "edges_generated": 4103,
    "duration_ms": 12500
  }
}

jq

rgctl -f json discover . | jq '.metrics | {nodes: .nodes_generated, ms: .duration_ms}'

5. Structured query verbs

Agent-facing graph exploration: find, callers, callees, relations, inventory, and status. Guide: structured-query.md.

Source: crates/rgctl-graph/src/structured_query.rs, src/cli/structured_query.rs, src/cli/session_status.rs

find

rgctl -f json find [PATTERN] [--type TYPE] [--file GLOB] [--scope PREFIX] [--exact] [--limit N] [--count-only]
interface FindResult {
  schema_version: 2;
  returned: number;
  total: number;
  entities?: EntityRow[];
}

interface EntityRow {
  id: string;
  name: string;
  type: string;              // lowercase CLI form, e.g. "function"
  qualified_name?: string;
  file?: string;
  line?: number;
  attributes?: string;       // with --show-attributes + --annotation
}

Ambiguous symbols return error: "ambiguous_symbol" with candidates[] (stdout JSON, exit non-zero).

callers / callees

rgctl -f json callers SYMBOL [--depth N] [--file GLOB] [--limit N]
rgctl -f json callees SYMBOL [--depth N]
interface CallNeighborsResult {
  schema_version: 2;
  target: EntityRow;
  callers?: EntityRow[];     // `callers` subcommand
  callees?: EntityRow[];     // `callees` subcommand
  hops: number[];
  returned: number;
  total: number;
  depth: number;
  direction: "callers" | "callees";
}

relations

rgctl -f json relations [SYMBOL] --edge EDGE [--direction out|in|both] [--from-type T] [--to-type T] [--depth N]
interface RelationsResult {
  schema_version: 2;
  target?: EntityRow;
  edges: EdgeRow[];
  returned: number;
  total: number;
}

interface EdgeRow {
  source: EntityRow;
  edge: string;
  direction: string;
  target: EntityRow;
  hops?: number;
  occurrences: number;
}

inventory

rgctl -f json inventory --by type|edge|lang|file|community|import-prefix [--file GLOB] [--scope PREFIX]
interface InventoryResult {
  schema_version: 2;
  by: string;
  counts: { key: string; count: number; occurrences?: number }[];
}

status

rgctl -f json status
interface SessionStatus {
  schema_version: 2;
  command: "status";
  status: "ok" | "missing";
  repo: string;
  snapshot?: string;
  digest?: string;
  nodes?: number;
  edges?: number;
  kantra_findings: boolean;
  kantra_findings_path?: string;
  /** Present when status is `ok`: sources match file_hashes.json */
  index_current?: boolean;
  dirty_files?: number;
  files_added?: number;
  files_changed?: number;
  files_deleted?: number;
  dirty_sample?: string[];
  /** Live `serve --watch` elected via `.rgctl/watch.lock` */
  watcher_alive?: boolean;
  watcher_pid?: number;
  /** Pending lines in `.rgctl/update_queue.jsonl` */
  update_queue_pending?: number;
  message?: string;
}

When index_current is false, message hints at rgctl update or serve --watch. Status does not rewrite the snapshot.

update

rgctl -f json update
rgctl -f json update --files src/Foo.java
rgctl -f json update --since HEAD~1
rgctl -f json update --no-wait          # enqueue only when serve --watch is live
rgctl -f json update --wait-timeout 30
interface UpdateResultJson {
  schema_version: 1;
  command: "update";
  files_added: number;
  files_changed: number;
  files_deleted: number;
  files_affected: number;
  nodes_added: number;
  nodes_removed: number;
  edges_added: number;
  edges_removed: number;
  duration_ms: number;
  message?: string; // e.g. "already current"
  /** Present when completed via the watch queue */
  source?: "watch_queue";
  warnings?: string[];
}

/** `--no-wait` while a live watcher holds the lock */
interface UpdateQueuedJson {
  schema_version: 1;
  command: "update";
  queued: true;
  request_id: string;
  message?: string;
}

When serve --watch is live, update enqueues to .rgctl/update_queue.jsonl and waits for a result (default). See Watch mode.

jq examples

rgctl -f json find --type function --count-only | jq '.total'
rgctl -f json find clearCart --type function --exact | jq '.entities[].file'
rgctl -f json callers clearCart | jq '.callers[].name'
rgctl -f json relations clearCart --edge calls --direction in | jq '.edges[].source.name'
rgctl -f json inventory --by community | jq '.counts[:5]'
rgctl -f json communities list | jq '.communities[:5]'
rgctl -f json find --type kantrarule --limit 5 | jq '.entities[].name'

6. blast-radius

rgctl -f json blast-radius SYMBOL [--depth N] [--policy-file PATH] [--with-slices]

TypeScript shape

interface BlastRadiusResponse {
  schema_version: 2;
  target: {
    id: string;              // UUID
    symbol: string;
    class_context: string | null;
    file_path: string;
    language: string;        // "java" | "rust" | "python" | "unknown"
    signature?: string;      // omitted when unknown
    canonical_fqn: string;   // prefer for routing: "Class::method"
  };
  metrics: {
    score: number;           // 0–100
    direct_callers_count: number;
    impact_zone_size: number;
    caller_depth_limit?: number;  // present when --depth N
  };
  topology: {
    scc_component_id: number | null;
    direct_callers: SymbolContext[];
    impact_zone: SymbolContext[];
  };
  gatekeeping: {
    policy_status: "SKIPPED" | "PASS" | "VIOLATED";
    violations: PolicyViolation[];
    handoffs: SliceHandoff[];  // [] unless --with-slices
  };
}

interface SymbolContext {
  id: string;       // UUID — stable join key
  fqn: string;      // display name (language-native)
  file_path: string;
}

interface SliceHandoff {
  callee: string;
  param: string;
  index: number;
}

Policy violations (gatekeeping.violations)

Tagged union — discriminant field is kind:

kindFields
domain_isolationsource_domain, reached_domain, node
scale_failurecount, max
cascade_hazardnode, betweenness, threshold
sanitization_bypasssink_line, path_trace, sanitizer_node

jq

# Impact score and caller UUIDs
rgctl -f json blast-radius ShoppingCartService \
  | jq '{score: .metrics.score, callers: [.topology.direct_callers[].id]}'

# Depth-capped impact zone
rgctl -f json blast-radius CartEndpoint --depth 3 \
  | jq '.metrics.caller_depth_limit, .topology.impact_zone | length'

# Policy gate
rgctl -f json blast-radius OrderService --policy-file policy.json \
  | jq '.gatekeeping.policy_status, .gatekeeping.violations'

Routing rule: use target.canonical_fqn and topology.*.id (UUID). Treat topology.*.fqn as display text only.

Migration from legacy flat JSON

Older rgctl emitted a flat object (symbol, score, direct_callers[], impact_zone[] at the root). Current output is nested with schema_version: 2. See the blast-radius field catalog in this document for the full table and jq path mapping.

# Was: jq '.score'  →  Now:
jq '.metrics.score'

# Was: jq '.direct_callers[]'  →  Now:
jq '.topology.direct_callers[].fqn'

# Prefer for automation (v2):
jq '.target.canonical_fqn'

7. metrics

rgctl -f json metrics [--pagerank] [--betweenness] [--communities] [--iterations N]

Default (no section flags) includes all three sections. Requesting a single flag omits the others entirely.

TypeScript shape

interface MetricsResponse {
  schema_version: 1;
  pagerank?: {
    top: { node: string; pagerank: number }[];  // max 20
    converged: boolean;
    iterations: number;
    max_delta: number;
  };
  betweenness?: { node: string; score: number }[];  // max 20, top-level array
  communities?: {
    count: number;
    modularity: number;
    assignments: number;
  };
}

jq

rgctl -f json metrics --pagerank | jq '.pagerank.top[:5]'
rgctl -f json metrics | jq '.communities.modularity'

8. check

rgctl -f json check --policy-file policy.json
rgctl -f json check --policy-file policy.json \
  --base-ref origin/main --head-ref HEAD --strict

Evaluates policy rules against git-scoped functions. Default scope: git diff --name-only HEAD (working tree vs last commit). With --base-ref and --head-ref: commit-to-commit diff. Without --strict, empty scope falls back to all functions.

TypeScript shape

interface CheckResponse {
  schema_version: 1;
  policy: string;           // path passed to --policy-file
  passed: boolean;
  violations: {
    symbol: string;
    error?: string;         // engine error (mutually exclusive with violation)
    violation?: string;     // human-readable policy text
  }[];
}

jq

rgctl -f json check --policy-file policy.json | jq '{passed, count: (.violations | length)}'

8b. review check / pr-check

rgctl -f json review check --policy-file rgctl-tests/rgctl-pr-policy.json \
  --base-ref origin/main --head-ref HEAD --strict
# Compatibility alias (identical JSON + exit codes):
rgctl -f json pr-check --policy-file rgctl-tests/rgctl-pr-policy.json \
  --base-ref origin/main --head-ref HEAD --strict

Temporal PR gate: compares base + head graph snapshots, git-scoped entities, classifies violations as new | existing | resolved | regression. Default head synthesis builds a delta head from the base artifact; use --full-snapshots for pre-built dual artifacts. Flags: --bisect, --synthetic-head worktree, --cascade-depth. Preferred command name is review check; pr-check remains a synonym. Guide: pr-review.md.


8c. review paths

rgctl -f json review paths --base-ref origin/main --head-ref HEAD --full-snapshots
# Optional: --upstream-depth 2 --downstream-depth 1 --symbol submitOrder --fanout 10 --max-symbols 50

Structural before/after call-path report (not a policy gate). Shares base/head artifact prep with review check. Truncation sets flags in JSON but still exits 0 when the report is produced. Ambiguous --symbol returns ambiguous candidates and exits 2. Guide: pr-review.md.

TypeScript shape

interface ReviewPathsResponse {
  schema_version: 1;
  command: "review paths";
  change_summary: {
    changed_symbols: number;
    call_edges: {
      added: number;
      removed: number;
      retargeted: number;
      unchanged: number;
    };
    files_in_scope: number;
    unscored_files: number;
  };
  truncation: { symbols: boolean; fanout: boolean; depth: boolean };
  symbols: {
    stable_key: string;
    name: string;
    kind: string;
    base?: { file?: string; start_line: number; end_line: number };
    head?: { file?: string; start_line: number; end_line: number };
    path_before: string[];
    path_after: string[];
    path_delta: PathDelta[];
    truncation: { symbols: boolean; fanout: boolean; depth: boolean };
  }[];
  unscored_files: { path: string; reason: string }[];
  ambiguous: { name: string; file?: string; stable_key: string }[];
}

type PathDelta =
  | { kind: "added"; from: string; to: string; call_site_line?: number }
  | { kind: "removed"; from: string; to: string; call_site_line?: number }
  | { kind: "retargeted"; from: string; to_before: string; to_after: string };

TypeScript shape

interface PrCheckResponse {
  schema_version: "2";
  passed: boolean;
  violations: {
    symbol: string;
    classification: "new" | "existing" | "resolved" | "regression";
    violation: PolicyViolation;  // tagged union — see check / blast-radius
    stable_key: number;
    introduced_in_commit?: string;  // with --bisect
    severity?: "warn" | "fail";   // calendar grace / sunset
  }[];
  violations_summary: {
    new: number;
    existing: number;
    resolved: number;
    regression: number;
  };
  graph_diff: {
    nodes_added: number;
    nodes_removed: number;
    nodes_changed: number;
    edges_added: number;
    edges_removed: number;
  };
  scope: { files: number; entities: number };
}

Exit 1 when passed is false. With scope.new_violations_only, only new (and regression when scope.fail_on_regression is true) block the gate. resolved violations are reported as debt paid down and do not fail.

Appends outcomes to .rgctl/violation_ledger.jsonl keyed by (stable_key, rule_id).


9. slice

rgctl -f json slice FILE --line N --variable VAR [--function NAME] \
  [--view text|cfg|pdg] [--direction backward|forward] [--taint]

Response shape depends on --view and --taint.

--view text (default)

interface SliceTextResponse {
  schema_version: 1;
  file: string;
  criterion: { line: number; variable: string };
  direction: "backward" | "forward";
  reduction_percent: number;
  lines: number[];              // source lines in the slice
  nodes: PdgNode[];             // PDG subgraph
  edges: PdgEdge[];
}

--view cfg

interface SliceCfgResponse {
  schema_version: 1;
  file: string;
  function: string;
  view: "cfg";
  nodes: CfgBlockNode[];
  edges: CfgEdgeNode[];
}

--view pdg

Same topology as inspect PDG (view: "pdg").

--taint

Flat summary (no graph topology):

interface SliceTaintResponse {
  schema_version: 1;
  file: string;
  function: string;
  line: number;
  variable: string;
  taint: true;
  flows: number;
  vulnerable: number;
}

Shared graph primitives

interface PdgNode {
  id: string;       // "node_0", …
  line: number;
  label: string;
  kind: string;
  defined?: string[];
  used?: string[];
}

interface PdgEdge {
  source: string;
  target: string;
  kind: "data" | "control" | string;
  variable?: string;  // data deps only
}

interface CfgBlockNode {
  id: string;       // "block_0", …
  block_index: number;
  start_line: number;
  end_line: number;
  statements: { line: number; kind: string; text: string }[];
}

interface CfgEdgeNode {
  source: string;
  target: string;
  kind: string;   // "next", "iftrue", "iffalse", …
}

jq

# Lines touched by backward slice
rgctl -f json slice src/.../Foo.java --line 42 --variable x --function Foo \
  | jq '.lines'

# Taint counts only
rgctl -f json slice src/.../Foo.java --line 10 --variable input --function Foo --taint \
  | jq '{flows, vulnerable}'

10. inspect

rgctl -f json inspect SYMBOL cfg|pdg|dom [layer options]

Requires discover --with-cfg for richest PDG/CFG data from the analysis archive.

CFG layer

interface InspectCfgResponse {
  schema_version: 1;
  symbol: string;
  layer: "cfg";
  pruned: boolean;
  nodes: CfgBlockNode[];
  edges: CfgEdgeNode[];
}

PDG layer

interface InspectPdgResponse {
  schema_version: 1;
  symbol: string;
  layer: "pdg";
  nodes: PdgNode[];
  edges: PdgEdge[];
  data_deps: number;
  control_deps: number;
}

Dominance layer

interface InspectDomResponse {
  schema_version: 1;
  symbol: string;
  layer: "dom";
  nodes: { block_index: number; start_line: number; end_line: number }[];
  idom: { block: number; immediate_dominator: number }[];
  frontiers?: { block: number; frontier_blocks: number[] }[];  // with --frontiers
}

Block references use integer block_index (sorted by start_line), not string ids.

jq

rgctl -f json inspect ShoppingCartService pdg --edge-layer data \
  | jq '{data: .data_deps, nodes: [.nodes[] | {line, label}]}'

Diagram formats: -f mermaid and -f graphviz emit diagram text (not JSON) for cfg/dom layers.


11. export (file formats)

export writes to --export-output; stdout is a one-line summary (unless global -o redirects).

rgctl export --export-format json --export-output graph.json --query all
rgctl export --export-format mermaid --export-output clearCart.mmd --query 'name:clearCart'
--export-formatFile content
jsonGraph snapshot JSON (filtered when --query ≠ all)
graphmlGraphML XML
graphvizDOT
mermaidMermaid flowchart
obsidianObsidian vault directory (one markdown note per doc heading; needs markdown discover + content_store.bin)
okfOpen Knowledge Foundation JSON entity bundle (doc headings + bodies)

obsidian and okf export doc heading modules from the graph; use --query all. Output for obsidian is a directory (--export-output "$REPO/vault"), not a single file.

--query uses filter syntax (all, name:Foo, type:Function, functions) — not find patterns. The summary line reports the filtered node/edge counts (or note count for Obsidian).


12. On-disk JSON after discover

These files are written under .rgctl/ (and copied into .rgctl/dashboard/ for the UI). They are not emitted on stdout but are stable inputs for custom tooling.

Pathschema_versionPurpose
dashboard/manifest.json1Bundle metadata, phase flags, metric summary
dashboard/metagraph.json2Package-level graph for LOD UI
dashboard/cfg_index.json1CFG function catalog
dashboard/slice_index.json1Slice/PDG function catalog
dashboard/dataflow_index.json1Dataflow function catalog
dashboard/taint_index.json1Taint summary (discover --with-cfg)
dashboard/taint/{uuid}.json1Per-function taint flows
dashboard/slice/{uuid}.json1Per-function source + PDG bundle
dashboard/cfg/{uuid}.json1Per-function CFG preview
kantra_findings.json2Kantra violations + enrichment + skipped rules (discover --with-kantra)
file_hashes.json—Incremental discover state
content_store.bin—Blake3-keyed blob store for truncated markdown bodies / large files (body_ref, blob_ref)

manifest.json (excerpt)

{
  "schema_version": 1,
  "phases": { "0": "complete", "4": "complete", "8": "pending" },
  "graph": {
    "payload_path": "graph_payload.bin",
    "payload_format": "columnar_v2",
    "node_count": 1842,
    "edge_count": 4103
  },
  "analysis": {
    "cfg_available": true,
    "taint_available": true,
    "taint_flow_count": 12,
    "taint_vulnerable_count": 3
  },
  "metrics": {
    "function_count": 412,
    "avg_complexity": 1.2
  }
}

taint_index.json

{
  "schema_version": 1,
  "available": true,
  "detail_dir": "taint",
  "function_count": 8,
  "total_flows": 12,
  "vulnerable_flows": 3,
  "functions": [
    {
      "function_id": "uuid",
      "name": "ShoppingCartService",
      "file_path": "src/main/java/.../ShoppingCartService.java",
      "flow_count": 2,
      "vulnerable_count": 1
    }
  ]
}

taint/{uuid}.json flow entry

{
  "id": 0,
  "variable": "userInput",
  "source_type": "HttpParameter",
  "sink_type": "SqlQuery",
  "severity": 10,
  "vulnerable": true,
  "sanitizers": [],
  "source_line": 42,
  "sink_line": 88,
  "source_text": "...",
  "sink_text": "...",
  "path_lines": [42, 55, 88],
  "path_statements": ["...", "...", "..."]
}

kantra_findings.json

Written by discover --with-kantra (eval + enrich stages). Rule nodes are indexed into graph.snapshot.bin (KantraRuleset, KantraRule); VIOLATES edges link rules to resolved code nodes after enrich.

Incremental filecontent results are cached under .rgctl/kantra_cache/ (content-hash keyed; invalidated on ruleset change).

{
  "schema_version": 2,
  "command": "kantra_findings",
  "catalog_id": "stable-java@022bbd34b34eca53d04b6cb2b97b27e47fef479b",
  "ruleset": "embedded-stable-java",
  "target_filter": "quarkus",
  "evaluated_rules": 2656,
  "violations": [
    {
      "rule_id": "springboot-00001",
      "category": "mandatory",
      "file": "src/main/java/com/example/Foo.java",
      "line": 12,
      "message": "…",
      "matched_by": "java.referenced",
      "symbol": "org.springframework.stereotype.Service",
      "enrichment": {
        "node_id": "550e8400-e29b-41d4-a716-446655440000",
        "community_id": 12,
        "pagerank": 0.0042,
        "blast_radius_score": 18.5,
        "impact_zone_size": 47
      }
    }
  ],
  "skipped_rules": [
    {
      "rule_id": "some-xml-rule",
      "reason": "unsupported: builtin.xml"
    }
  ],
  "cache_hits": 120,
  "cache_misses": 3
}
FieldTypeNotes
schema_version2Artifact version
command"kantra_findings"Discriminator
catalog_idstring?Embedded: stable-java@<rulesets-git-sha>; fixture: fixture@<hash>; override paths use dir@… / tree id
rulesetstringDisplay name from catalog
target_filterstring?Set when --kantra-target is active
evaluated_rulesnumberRules in catalog before per-rule skip
violationsarrayMatches with rule_id, file, line, matched_by (filecontent, java.referenced, …)
violations[].symbolstring?Import or symbol name when resolved by referenced rules
violations[].enrichmentobject?Graph linkage + metrics (after blast engine build)
violations[].enrichment.node_idstring?UUID of matched graph node (used for VIOLATES edges)
violations[].enrichment.community_idnumber?Louvain community
violations[].enrichment.pageranknumber?PageRank centrality
violations[].enrichment.blast_radius_scorenumber?Blast-radius score for the node
violations[].enrichment.impact_zone_sizenumber?Transitive impact zone size
skipped_rulesarrayUnsupported providers, invalid regex, or eval errors (rule_id, reason)
cache_hitsnumber?Per-file cache hits (builtin.filecontent warmup); omitted when zero
cache_missesnumber?Stale cache entries re-evaluated; omitted when zero

Query violation edges: rgctl -f json relations --edge violates --from-type kantrarule --limit 20.

Fixture override (--kantra-rules) omits full Konveyor catalog_id unless the ruleset was compiled from the submodule.

Binary artifacts (graph.snapshot.bin, graph_payload.bin, blast_engine.snapshot.bin) use internal columnar formats — use CLI JSON or export --export-format json for portable graph access.


13. Exit codes

Command01
discoverSuccessFailure
find / callers / relations / …SuccessQuery/IO error
blast-radiusSuccess, or policy skipped--policy-file + policy_status == "VIOLATED" (JSON still on stdout)
checkpassed == truepassed == false
slice / inspect / metrics / exportSuccessError
installSkill files written or unchangedMissing --skill, dest differs without --force (skipped_exists; JSON still on stdout), or I/O error

CI pattern: capture stdout first, then check $?.

out=$(rgctl -f json blast-radius Foo --policy-file policy.json) || ec=$?
echo "$out" | jq .
exit "${ec:-0}"

14. Parsing recipes

Python

import json, subprocess

def rgctl_json(repo: str, *args: str) -> dict:
    cmd = ["rgctl", "-r", repo, "-f", "json", *args]
    out = subprocess.check_output(cmd, text=True)
    return json.loads(out)

doc = rgctl_json("/path/to/coolstore", "blast-radius", "CartEndpoint")
assert doc["schema_version"] == 2
for caller in doc["topology"]["direct_callers"]:
    print(caller["id"], caller["fqn"])

Node.js

import { execFileSync } from "node:child_process";

function rgctlJson(repo, ...args) {
  const out = execFileSync("rgctl", ["-r", repo, "-f", "json", ...args], {
    encoding: "utf8",
  });
  return JSON.parse(out);
}

const inv = rgctlJson(process.env.REPO, "find", "--type", "function", "--count-only");
const total = inv.total;

CI ingestion gate

metrics=$(rgctl -f json discover .)
nodes=$(echo "$metrics" | jq '.metrics.nodes_generated')
test "$nodes" -gt 100

Chaining discover → query

rgctl -f json discover . | tee discover.json
rgctl -f json find --type function --count-only | jq '.total'

15. semantic

Opt-in embedding index + query. Types: src/cli/semantic_output.rs.

semantic index

rgctl -r "$REPO" -f json semantic index
rgctl -r "$REPO" -f json semantic index --scope docs --embedder hash
# extras: --embedder code-daemon|hash   --embed-bodies

--scope on index: function (default), docs (:Module kind=heading + kind=code_block), or all. Doc embeddings read body_text or content_store.bin via body_ref. Function vectors use declaration metadata unless --embed-bodies.

type SemanticIndexJsonResponse = {
  schema_version: 2;
  model_id: string;            // e.g. vocab-accumulate-v1; +bodies when --embed-bodies
  dimensions: number;          // default 256
  functions_indexed: number;   // entry count (doc sections when --scope docs; field name is legacy)
  path: string;                // .rgctl/semantic_index.bin
  graph_digest?: string;
  build_stats?: {
    total: number;
    reused: number;
    embedded: number;
    removed: number;
  };
};

Text mode prints Indexed N functions — same count as functions_indexed (not always functions when --scope docs).

rgctl -r "$REPO" -f json semantic index | jq '{model_id, dimensions, functions_indexed}'

semantic distill

Teacher-embed vocab_tokens.txt into an RBVK blob. Copy the file to crates/rgctl-analysis/assets/vocab_matrix.bin and rebuild to compile vocab-accumulate-v2. Teacher cannot be vocab (that would distill the table from itself). Default teacher is code-daemon; --embedder hash is for tests / offline CI.

rgctl -r "$REPO" -f json semantic distill --matrix vocab_matrix.bin --embedder hash
rgctl -r "$REPO" semantic distill --matrix crates/rgctl-analysis/assets/vocab_matrix.bin --embedder code-daemon
type SemanticDistillJsonResponse = {
  schema_version: 1;
  path: string;
  tokens: number;
  dimensions: number;
  teacher_model_id: string;
  compiled_model_id: string;  // vocab-accumulate-v2
};

semantic query

rgctl -r "$REPO" -f json semantic query "checkout flow" --limit 10
rgctl -r "$REPO" -f json semantic query "checkout flow" --scope docs --limit 10

No --embedder on query — uses the model saved in semantic_index.bin. --scope docs on query does not filter hits (except --scope community); build the index with matching --scope first.

type SemanticHitJson = {
  node_id: string;
  name: string;
  qualified_name?: string;
  file_path?: string;
  distance: number;            // Hamming
  score: number;
  fused_score?: number;
  ranking?: string;            // e.g. "fusion"
};

type SemanticQueryJsonResponse = {
  schema_version: 3;
  query: string;
  model_id: string;
  dimensions: number;
  hits: SemanticHitJson[];
  expansion?: object;          // optional query expansion payload
};
rgctl -r "$REPO" -f json semantic query "OrderService" --limit 5 \
  | jq '.hits[:5] | map({name, score, file_path})'
rgctl -r "$REPO" -f json semantic query "cart" --scope community --limit 5 \
  | jq '.hits[].name'

16. communities

List / refresh heuristic labels over label-propagation clusters. Types: src/cli/communities.rs.

rgctl -r "$REPO" -f json communities list
rgctl -r "$REPO" -f json communities label --write
type CommunitiesJsonResponse = {
  schema_version: 1;
  modularity: number;
  written: boolean;            // true after `label --write`
  communities: Array<{
    id: number;
    label: string;
    member_count: number;
  }>;
};
rgctl -r "$REPO" -f json communities list | jq '.communities[:10]'
rgctl -r "$REPO" -f json communities list | jq '{modularity, n: (.communities|length)}'

See also inventory --by community (User Guide §6).


16b. clones

Clone groups via Function code_hash (exact Type-1), token_bloom Jaccard (bloom candidates), or CFG SESE hammock Weisfeiler-Lehman canonical hashing (fragment sub-function clones). Query-time / sidecars .rgctl/clones.json / .rgctl/clones.bloom.json / .rgctl/clones.fragment.json — not topology edges. Distinct from semantic query. Types: crates/rgctl-analysis/src/clones.rs, crates/rgctl-analysis/src/fragment_clones.rs, CLI src/cli/clones.rs. Design: clone-detection-design.md. Guide: clone-detection.md.

rgctl -r "$REPO" -f json clones --mode exact [--min-loc N] [--exclude GLOB] [--lang ID]
rgctl -r "$REPO" -f json clones --mode bloom [--threshold 0.85] [--min-loc N] [--exclude GLOB]
rgctl -r "$REPO" -f json clones --mode fragment [--seed SYMBOL|FILE:LINES] [--lines START-END] [--min-statements N] [--max-statements N] [--threshold F]
rgctl -r "$REPO" -f json clones SYMBOL --file PATH [--class C] [--line N]

Schema v1: Whole-function clones (exact | bloom)

type CloneReport = {
  schema_version: 1;
  mode: "exact" | "bloom";
  graph_digest: string;
  filters: {
    min_loc?: number;       // default 5
    exclude: string[];      // path-component or path-substring needles
    language?: string;
  };
  threshold?: number;       // bloom min Jaccard (default 0.85)
  candidates?: boolean;     // true for bloom — not Type-1
  group_count: number;
  groups: Array<{
    mode: "exact" | "bloom";
    hash?: string;          // exact only
    size: number;
    members: Array<{
      id: string;
      name: string;
      file?: string;
      start_line?: number;
      end_line?: number;
      loc?: number;
    }>;
    confidence?: number;    // 1.0 exact; Jaccard for bloom
    score?: number;         // bloom: min pairwise Jaccard
  }>;
  seed?: {                  // present for symbol-scoped clones
    id: string;
    name: string;
    file?: string;
    start_line?: number;
    end_line?: number;
    loc?: number;
  };
};

Schema v2: Sub-function fragment clones (fragment)

type FragmentCloneReport = {
  schema_version: 2;
  mode: "fragment";
  graph_digest: string;
  filters: {
    min_statements: number; // default 3
    max_statements: number; // default 15
    threshold: number;      // default 1.0 (exact structural)
    exclude: string[];
  };
  seed?: {
    file: string;
    start_line: number;
    end_line: number;
    enclosing_function?: string;
    structural_hash: string;
  };
  group_count: number;
  groups: Array<{
    structural_hash: string;
    size: number;
    score: number;
    members: Array<{
      id: string;
      name: string;
      file: string;
      start_line: number;
      end_line: number;
      enclosing_function: string;
      statement_count: number;
    }>;
  }>;
};

Ambiguous symbols emit the structured-query envelope (error: "ambiguous_symbol", schema_version 2) — disambiguate with --file / --class / --line.

# Exact Type-1 clones
rgctl -r "$REPO" -f json clones --exclude test \
  | jq '{group_count, top: [.groups[:3][] | {size, hash: .hash[0:12], names: [.members[].name]}]}'

# Bloom candidates
rgctl -r "$REPO" -f json clones --mode bloom --threshold 0.9 --exclude test \
  | jq '{candidates, threshold, top: [.groups[:3][] | {size, score, names: [.members[].name]}]}'

# Fragment clone query scoped to a seed loop/region
rgctl -r "$REPO" -f json clones --mode fragment --seed process_orders --lines 3-8 \
  | jq '{group_count, seed, groups: [.groups[] | {hash: .structural_hash, members: [.members[] | {fn: .enclosing_function, lines: "\(.start_line)-\(.end_line)"}]}]}'

17. cpg

Hybrid CPG façade (needs discover --with-cfg). Types: crates/rgctl-analysis/src/cpg.rs + src/cli/cpg.rs. All JSON payloads use schema_version: 1.

cpg status

rgctl -r "$REPO" -f json cpg status
type CpgStatus = {
  schema_version: 1;
  archive_path: string;
  archive_present: boolean;
  function_count: number;
  graph_digest?: string;
  field_write_index_present: boolean;
  field_write_count: number;
  ast_skeleton_present: boolean;
  ast_skeleton_count: number;
};

cpg mutations

rgctl -r "$REPO" -f json cpg mutations --type ShoppingCart --exclude-ctors
type CpgMutationsResult = {
  schema_version: 1;
  type_name: string;
  exclude_ctors: boolean;
  member?: string;
  include_unresolved: boolean;
  mutations: Array<{
    file: string;
    line: number;
    code: string;
    member: string;
    function: string;
    is_constructor: boolean;
    receiver_local?: string;
    receiver_type?: string;
    kind: string;
  }>;
};

Other subcommands

SubcommandPrimary keys
cpg function <Symbol>id, name, has_l_proc, …
cpg calls <Symbol>edges[] (direction, name, id)
cpg flows …steps[] (data dependence walk)
cpg exportwrites a file (--format / --output); not stdout JSON
rgctl -r "$REPO" -f json cpg status | jq '{archive_present, function_count, field_write_count}'
rgctl -r "$REPO" -f json cpg mutations --type ShoppingCart --exclude-ctors \
  | jq '.mutations | length'
rgctl -r "$REPO" -f json cpg calls priceShoppingCart | jq '.edges[:10]'

18. vuln triage / deps check / vuln analyze / taint

OSV supply-chain + reachability (does not run on discover). Parse uses the osv schema crate; OpenVEX via openvex. Types: rgctl-security. Analyze schema_version is 1.

rgctl -f json vuln triage --osv ./advisory.json
rgctl -r "$REPO" -f json deps check --osv ./advisory.json --include-jars lib
rgctl -r "$REPO" -f json find --package 'com.fasterxml.jackson.core:jackson-databind' -t import
rgctl -r "$REPO" -f json callers ObjectMapper --methods readValue,writeValueAsString
rgctl -r "$REPO" -f json blast-radius <Symbol> --classify-boundary
rgctl -r "$REPO" -f json taint --sink ObjectMapper.readValue --source external   # needs discover --with-cfg
rgctl -r "$REPO" -f json vuln analyze --osv ./advisory.json --include-jars lib

deps check verdict: not_affected | affected_candidate. Analyze exploitability: not_affected | not_exploitable | exploitable | under_investigation. Bundled JAR / node_modules scans are opt-in. Sink-first requires CFG artifacts.

Human guide: Vulnerability and OSV Supply-Chain Analysis · Agent pack · skill workflow vuln.


19. install

Install the embedded agent pack (single skill rgctl, optional policy). Does not require a prior discover and does not run discover. Types: src/cli/install_output.rs. schema_version is 3.

Human-readable install reference: Agent pack walkthrough.

rgctl -r "$REPO" -f json install --skill [] [--with-policy] \
  [--tools cursor,claude,codex,agents|all] [-g] [--force]
rgctl -f json install --list-agents

Omitting --tools installs the default adapters: cursor, claude, codex, agents, antigravity. Use --tools all for every registry entry. Unknown ids in --tools are reported on stderr; if none are valid, exit 1. --global is rejected for agents with supports_global: false in the registry.

install --list-agents

Separate JSON payload (not install schema v2):

type ListAgentsResponse = {
  schema_version: 1;
  command: "list-agents";
  list_agents: true;
  agents: Array<{ id: string; skills_path: string; supports_global: boolean; /* … */ }>;
  workflows: Array<{ id: string; title: string }>;
  rgctl_version: string;
};
type InstallWriteStatus = "created" | "unchanged" | "overwritten" | "skipped_exists";
type InstallWriteKind = "skill" | "policy" | "meta";

type InstallResponse = {
  schema_version: 3;
  command: "install";
  skill: "rgctl";
  repo: string; // absolute install prefix (repo or home for -g)
  scope: "local" | "global";
  agents: string[];
  with_policy: boolean;
  force: boolean;
  writes: Array<{
    agent: string;
    workflow?: string;
    kind: InstallWriteKind;
    path: string;
    status: InstallWriteStatus;
    host?: "claude" | "codex" | "cursor"; // legacy compat when set
  }>;
};

Pass --skill and/or --with-policy. --host is deprecated (use --tools). Workflow markdown is skills/rgctl/references/workflows.md, copied into the pack at rgctl build time. If any write is skipped_exists, JSON is still printed and the process exits 1.

rgctl -r "$REPO" -f json install --skill | jq '.writes[] | {agent, workflow, kind, status}'

Verification

Schema fixtures are tested in CI:

cargo test --test cli_output --test subprocess_golden_path --test all_commands_sanity

See cli-io-sanity-qe.md for the full coverage matrix.


Related

  • user-guide.md — install, ecommerce-java walkthrough (CoolStore dual API), CLI examples
  • Field catalogs — exhaustive tables later in this document (formerly cli-output-schemas.md)
  • HTTP Server and Dashboard — rgctl serve, dashboard, semantic HTTP API
  • cli-io-sanity-qe.md — subprocess JSON contract and release perf gates

Field catalogs (from former cli-output-schemas)

Conventions matrix

Conventionblast-radiusdiscoverstructured querymetricschecksliceinspectsemanticcommunitiescpginstall
schema_version✅ v2✅ v2✅ v2 (v1 status)✅ v1✅ v1✅ v1✅ v1✅ v2/v3✅ v1✅ v1✅ v1
Typed *_output.rs / analysis types✅✅✅ (graph crate)✅✅✅✅✅✅✅✅
Explicit empty arrays✅—✅✅✅✅✅✅✅✅✅
Omitted optional keys———✅✅✅✅✅—✅—
Composable graph topology✅————✅✅————
Stable node UUIDs (no nil)✅——————————

Tests: See cli-io-sanity-qe.md for the full coverage matrix, harness design, and extension guide.

LayerCargo targetPathCovers
1 — Unit schemacli_outputtests/cli_output/*.rsTyped *_output.rs fixtures, serde shapes
2 — Golden pathsubprocess_golden_pathtests/cli_output/subprocess_golden_path.rsDiscover + blast-radius pipelines, exit 1
3 — Full sanityall_commands_sanitytests/cli_output/all_commands_sanity.rsAll JSON commands, sandbox -d, platform rules
Fixture—tests/fixtures/tiny_polyglot_repo/Java + Rust polyglot subprocess input
cargo test --test cli_output --test subprocess_golden_path --test all_commands_sanity

Source: src/cli/blast_radius_output.rs — BLAST_RADIUS_SCHEMA_VERSION is 2.


1. blast-radius — schema v2

Command:

rgctl -f json blast-radius <SYMBOL> [--depth N] [--policy-file PATH] [--with-slices] [--class CLASS] [--file PATH]

Flags:

FlagDescription
--depth NCap topology.impact_zone to upstream callers within N incoming call hops (hop 1 = direct callers). Omits metrics.caller_depth_limit when unset (full closure). Score is recomputed when capped.
--policy-fileRun policy guardrails on the (possibly depth-filtered) impact zone
--with-slicesPopulate gatekeeping.handoffs (requires full graph path)
--class / --fileDisambiguate overloads

Optional warm path: foreground rgctl serve is POST /api/query on 127.0.0.1:8080. Daemon HTTP uses /{reponame}/api/query. See HTTP Server and Dashboard.

Source: src/cli/blast_radius_output.rs
Cache enrichment: crates/rgctl-analysis/src/macro_call_index.rs, macro_call_lookup.rs

Top-level

{
  "schema_version": 2,
  "target": { },
  "metrics": { },
  "topology": { },
  "gatekeeping": { }
}

target — identification metadata (v2)

FieldTypeDescription
idUUID stringResolved graph node id
symbolstringBare function/method name
class_contextstring | nullContaining class or namespace when known
file_pathstringProject-relative source path (empty if unknown)
languagestring"java", "rust", "python", or "unknown"
signaturestring | omittedMethod signature when known (overload disambiguation)
canonical_fqnstringUniform Class::method (e.g. OrderService::process)
  • language comes from graph properties.language (set by language plugins at extract time) or file-extension fallback.
  • signature comes from graph Node.signature (tree-sitter during discover).
  • canonical_fqn normalizes Java dot notation to double-colon form; Rust module::fn passes through.

metrics — quantitative impact

FieldTypeDescription
scorenumberImpact score 0–100
direct_callers_countintegerImmediate caller count
impact_zone_sizeintegerTransitive caller count (functions only); reflects --depth cap when set
caller_depth_limitinteger | omittedPresent when --depth N was passed; echoes the hop cap applied to impact_zone

topology — graph layout

FieldTypeDescription
scc_component_idinteger | nullSCC index from engine; null on macro-index blast lookup cache hit
direct_callersSymbolContext[]Immediate callers
impact_zoneSymbolContext[]Transitive upstream callers (filtered by --depth when set)

SymbolContext:

{
  "id": "UUID",
  "fqn": "string",
  "file_path": "string"
}
  • fqn is language-native display text from graph qualified_name or bare name.
  • Route on target.canonical_fqn + UUIDs, not parsed topology.fqn.
  • Nil UUID policy: entries without a resolvable graph UUID are omitted from topology (never 00000000-…).
  • After discover, the blast lookup cache (macro_call_index.db / .bin) stores direct_caller_ids, impact_zone_ids, and target metadata for composable chaining.

gatekeeping — policy and slice tracing

FieldTypeDescription
policy_statusstring"SKIPPED" (default), "PASS", or "VIOLATED"
violationsarrayStructured policy violations (always present; [] when none)
handoffsarraySlice seeds (always present; [] without --with-slices)

SliceHandoff: { "callee": string, "param": string, "index": number }

PolicyViolation (internally tagged; discriminant is kind):

kindFields
domain_isolationsource_domain, reached_domain, node
scale_failurecount, max
cascade_hazardnode, betweenness, threshold
sanitization_bypasssink_line, path_trace, sanitizer_node

Schema history (migration)

Legacy flat JSON (removed) — do not parse:

{
  "symbol": "CartService",
  "score": 42.3,
  "direct_callers": ["authenticate"],
  "impact_zone": ["authenticate", "main"],
  "handoffs": []
}

v1 (nested) — replaced flat root keys with target / metrics / topology / gatekeeping. schema_version: 1.

v2 (current) — adds target.language, target.signature, target.canonical_fqn, and optional metrics.caller_depth_limit when --depth N is set. schema_version: 2.

jq path (v2)Replaces legacy
.metrics.score.score
.topology.direct_callers[].fqn.direct_callers[] (bare names)
.topology.impact_zone[].fqn.impact_zone[]
.target.id(new)
.target.canonical_fqn(new — prefer for routing)
.gatekeeping.handoffs.handoffs (always present in v1+)

FQN policy: route on target.canonical_fqn (Class::method) and topology.*.id (UUID). Treat topology.*.fqn as language-native display text only.

Cache: target metadata is written at discover into macro_call_index.db / .bin. Re-run discover after upgrading rgctl to populate v2 fields on cache hits.

Example (Java, cache path)

{
  "schema_version": 2,
  "target": {
    "id": "424d403b-1b2c-4a3d-8e9f-0c1b2a3f4e5d",
    "symbol": "process",
    "class_context": "OrderService",
    "file_path": "java/com/example/OrderService.java",
    "language": "java",
    "signature": "public void process(String orderId) {",
    "canonical_fqn": "OrderService::process"
  },
  "metrics": {
    "score": 25.05,
    "direct_callers_count": 1,
    "impact_zone_size": 3
  },
  "topology": {
    "scc_component_id": null,
    "direct_callers": [
      {
        "id": "8b2c4a3d-0c1b-4e5d-8e9f-424d403b1b2c",
        "fqn": "com.example.OrderController.checkout",
        "file_path": "java/com/example/OrderController.java"
      }
    ],
    "impact_zone": []
  },
  "gatekeeping": {
    "policy_status": "SKIPPED",
    "violations": [],
    "handoffs": []
  }
}

Exit codes

  • 0 — success
  • 1 — policy_status == "VIOLATED" when --policy-file is set (JSON still emitted to stdout first)

1b. serve — HTTP dashboard

Foreground HTTP (one repo):

rgctl serve -r REPO [--open]

Binds http://127.0.0.1:8080/ — dashboard at /, semantic search at POST /api/semantic/query. See HTTP Server and Dashboard.

Requires: prior discover producing graph.snapshot.bin under {repo}/.rgctl/.


2. discover — schema v2 (stdout JSON)

Command:

rgctl -f json discover PATH [--languages LANGS] [--exclude PATTERNS] [--with-security] [--with-cfg] [--with-taint] [--write-json-graph]

Source: src/cli/discover_output.rs, src/cli/discover_impl.rs

With -f json, discover suppresses progress bars and human status lines on stderr (logging quiet unless -v). Stdout receives a single telemetry object after ingestion completes. Artifacts under .rgctl/ are still written.

{
  "schema_version": 2,
  "command": "discover",
  "metrics": {
    "files_discovered": 10921,
    "files_indexed": 10784,
    "files_skipped": 137,
    "nodes_generated": 231410,
    "edges_generated": 562067,
    "duration_ms": 18200
  }
}
FieldSource
files_discoveredPipelineStats.files_discovered
files_indexedPipelineStats.files_processed
files_skippedPipelineStats.files_failed
nodes_generatedPipelineStats.nodes_created
edges_generatedPipelineStats.edges_created
duration_msFull discover wall-clock (includes analysis + persist)

Without -f json, discover remains human-readable text progress (unchanged).

Artifacts on disk

PathWhenFormat
.rgctl/graph.snapshot.binAlways (default canonical graph)Binary graph snapshot
.rgctl/blast_engine.snapshot.binAlwaysBinary blast engine snapshot
.rgctl/macro_call_index.dbAlwaysSQLite blast-radius lookup cache only (+ UUID + v2 target columns)
.rgctl/macro_call_index.binAlwaysBincode companion index (same data family as .db)
.rgctl/analysis_results.binAlwaysColumnar analysis tables
.rgctl/dashboard/When export succeedsStatic dashboard bundle (index.html, manifest.json, …)
.rgctl/graph.db / .rgctl/graph.json--write-json-graph onlyLegacy full graph JSON
.rgctl/analysis/cfg_pdg.archive.bin--with-cfg or --with-taintCFG + PDG for --with-slices
.rgctl/analysis/*.json--with-cfg or --with-taintPer-function analysis storage (taint, CFG, PDG)
.rgctl/dashboard/taint_index.json--with-cfg or --with-taintDashboard taint catalog (see json-api.md §12)

3. Structured query — schema v2 (field catalog)

Commands: find, callers, callees, relations, inventory (schema_version 2); status (schema_version 1).

Source: crates/rgctl-graph/src/structured_query.rs, src/cli/structured_query.rs, src/cli/session_status.rs

See §5 Structured query verbs for TypeScript shapes and jq recipes. EntityRow.id is the stable graph UUID (use with blast-radius and export filters).


4. metrics — schema v1

Command:

rgctl -f json metrics [--pagerank] [--betweenness] [--communities] [--iterations N]

Source: src/cli/metrics_output.rs, src/cli/metrics.rs

Default (no section flags) computes all three sections.

{
  "schema_version": 1,
  "pagerank": {
    "top": [
      { "node": "UUID string", "pagerank": 0.0 }
    ],
    "converged": true,
    "iterations": 20,
    "max_delta": 0.0
  },
  "betweenness": [
    { "node": "UUID string", "score": 0.0 }
  ],
  "communities": {
    "count": 0,
    "modularity": 0.0,
    "assignments": 0
  }
}
SectionWhen presentNotes
pagerank--pagerank or default (all)top capped at 20 nodes
betweenness--betweenness or defaultTop-level array, top 20
communities--communities or defaultassignments = number of labeled nodes

Omitted keys: sections not requested are absent (not null, not []). Serialization uses Option + #[serde(skip_serializing_if = "Option::is_none")] via MetricsJsonResponse.


5. check — schema v1

Command:

rgctl -f json check --policy-file PATH

Source: src/cli/check_output.rs

{
  "schema_version": 1,
  "policy": "path/to/policy.json",
  "violations": [
    {
      "symbol": "string",
      "error": "string",
      "violation": "string"
    }
  ],
  "passed": true
}
FieldTypeDescription
policystringPath passed to --policy-file
violationsarrayAlways present; empty when passing
passedbooleantrue iff violations is empty

Violation entry (one of error or violation; the other is omitted):

{ "symbol": "foo", "error": "engine or policy error text" }
{ "symbol": "foo", "violation": "cascade hazard: node … betweenness …" }

Exit codes

  • 0 — passed == true
  • 1 — passed == false

6. slice — schema v1

Command:

rgctl -f json slice FILE --line N --variable VAR [--view cfg|pdg|text] [--direction backward|forward] [--taint]

Source: src/cli/slice_output.rs

CFG view (--view cfg)

{
  "schema_version": 1,
  "file": "string",
  "function": "string",
  "view": "cfg",
  "nodes": [
    {
      "id": "block_0",
      "block_index": 0,
      "start_line": 1,
      "end_line": 5,
      "statements": [
        { "line": 1, "kind": "Expression", "text": "let x = 1;" }
      ]
    }
  ],
  "edges": [
    { "source": "block_0", "target": "block_1", "kind": "next" }
  ]
}

PDG view (--view pdg)

{
  "schema_version": 1,
  "file": "string",
  "function": "string",
  "view": "pdg",
  "nodes": [
    { "id": "node_0", "line": 42, "label": "let tmp = ctx;", "kind": "Expression" }
  ],
  "edges": [
    { "source": "node_1", "target": "node_0", "kind": "data", "variable": "ctx" }
  ]
}

Text slice view (default --view text)

Includes line list and PDG subgraph topology for the slice:

{
  "schema_version": 1,
  "file": "string",
  "criterion": { "line": 42, "variable": "ctx" },
  "direction": "backward",
  "reduction_percent": 65.0,
  "lines": [40, 42],
  "nodes": [ { "id": "node_0", "line": 42, "label": "...", "kind": "..." } ],
  "edges": [ { "source": "node_1", "target": "node_0", "kind": "data", "variable": "ctx" } ]
}

Taint mode (--taint)

{
  "schema_version": 1,
  "file": "string",
  "function": "string",
  "line": 0,
  "variable": "string",
  "taint": true,
  "flows": 0,
  "vulnerable": 0
}

7. inspect — schema v1

Command:

rgctl -f json inspect SYMBOL --layer cfg|pdg|dom [layer options]

Source: src/cli/inspect_output.rs

CFG layer

{
  "schema_version": 1,
  "symbol": "string",
  "layer": "cfg",
  "pruned": false,
  "nodes": [ { "id": "block_0", "block_index": 0, "start_line": 1, "end_line": 5, "statements": [] } ],
  "edges": [ { "source": "block_0", "target": "block_1", "kind": "next" } ]
}

PDG layer

{
  "schema_version": 1,
  "symbol": "string",
  "layer": "pdg",
  "nodes": [
    { "id": "node_0", "line": 1, "label": "...", "kind": "...", "defined": ["x"], "used": ["y"] }
  ],
  "edges": [ { "source": "node_0", "target": "node_1", "kind": "control" } ],
  "data_deps": 0,
  "control_deps": 0
}

defined / used appear when --def-use is set.

Dominance layer

{
  "schema_version": 1,
  "symbol": "string",
  "layer": "dom",
  "nodes": [ { "block_index": 0, "start_line": 10, "end_line": 15 } ],
  "idom": [ { "block": 1, "immediate_dominator": 0 } ],
  "frontiers": [ { "block": 0, "frontier_blocks": [2, 3] } ]
}

Block references use stable block_index integers (sorted by start_line), not debug strings.

Other formats: --format mermaid and --format graphviz emit diagram text for CFG/dom layers (not JSON).


8. export — file output (not stdout JSON)

Command:

rgctl export --export-format json --export-output graph.json [--query "…"]

Writes to -o; stdout is a one-line summary unless output is redirected via global -o.

--formatFile content
jsonCodeGraph::export_json() (same family as graph.db)
graphmlGraphML XML
graphvizDOT
mermaidMermaid flowchart

9. semantic

See json-api.md §15 for TypeScript shapes and jq recipes.

Subcommandschema_versionSource
semantic index2SEMANTIC_INDEX_CLI_SCHEMA_VERSION
semantic query3SEMANTIC_QUERY_CLI_SCHEMA_VERSION
semantic distill1teacher → RBVK write
Field (index)TypeNotes
model_idstringEmbedder / model id
dimensionsnumberDefault 256
functions_indexednumberEntries written
pathstringIndex file path
build_statsobject?Incremental counters
Field (query hit)TypeNotes
node_idstringGraph node UUID
namestringFunction name
distancenumberHamming distance
scorenumberSimilarity or fused score
fused_scorenumber?Present when fusion ranking applied

10. communities

See json-api.md §16.

FieldTypeNotes
schema_versionnumber1
modularitynumberNewman Q
writtenboolTrue after label --write
communities[].idnumberCommunity id
communities[].labelstringHeuristic label
communities[].member_countnumberMembers

11. cpg

See json-api.md §17. Requires discover --with-cfg.

SubcommandPrimary fields
statusarchive_present, function_count, field_write_*, ast_skeleton_*
functionid, name, has_l_proc, is_constructor
callsedges[]
mutationsmutations[] (file, line, member, function, …)
flowssteps[]
exportfile output (--format / --output), not stdout JSON

All -f json CPG payloads use schema_version: 1.


12. install

See json-api.md §18. Source: src/cli/install_output.rs. Does not require discover.

FieldTypeNotes
schema_versionnumber1
commandstringAlways "install"
skillstringAlways "rgctl"
repostringAbsolute repository root
forceboolWhether --force was set
writes[].hoststring"claude", "codex", or "cursor"
writes[].pathstringAbsolute dest path
writes[].statusstringcreated / unchanged / overwritten / skipped_exists

Verification

# Typed schema sanity (unit fixtures per command)
cargo test --test cli_output

# Subprocess golden path (discover + blast-radius)
cargo test --test subprocess_golden_path

# Full platform I/O audit (all structured commands, sandbox -d)
cargo test --test all_commands_sanity

# Combined CI gate
cargo test --test cli_output --test subprocess_golden_path --test all_commands_sanity

Remaining gaps

  • HTML dashboard: still uses discover-time node properties, not CLI JSON shapes
  • Rust plugin: does not set properties.language on graph nodes yet (v2 falls back to .rs extension)
  • Re-run discover on repos indexed before P2 to populate blast lookup cache UUID + v2 target columns