Appendix B - 17. Model query surface (`repo-query`)
The Structure of Model query surface (repo-query) — its shape at a glance:
One CLI sits over the model set. A models meta-command self-describes the surface; per-model subcommands each emit JSON, so an agent consumes structure, not prose.
flowchart LR Agent[Agent / orchestrator] --> CLI[query CLI] CLI --> Meta[models meta-command] CLI --> Sub[per-model subcommands] Sub --> JSON[/--json output/] Sub --> M[(Models)]
Accessible description: an agent calls one query CLI that exposes a self-describing meta-command and per-model subcommands. The subcommands read the models and emit JSON, giving the agent one structured answer to act on.
Projected from the catalogue entry system-models / Model query surface (repo-query).
On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns
Intent
Intent — One canonical, self-describing query API over all the models — a repo-query CLI with deterministic --json subcommands — so an agent reads the system's compressed truth through a tool rather than parsing raw files, and the tool itself documents how the models load.
Motivation
The models are the agent's compressed map of the codebase, but only if the agent can read them cheaply and correctly. Left to cat/grep the model files, an agent (or a tool) re-implements loading + traversal, gets the dialect subtly wrong, and produces brittle one-offs. The failure is ad-hoc, error-prone model access — which defeats a queryable map's purpose.
Applicability
- The models exist and load cleanly (the substance being queried).
- A stable subcommand +
--jsoncontract so agents can act on the output. - The loader pattern documented (here, by co-locating the tool with the models).
Structure
The Structure diagram appears at the top of this page.
Sample Code
The CLI dispatches a subcommand per model and emits JSON. A models meta-command lists the surface, so the tool documents itself and an agent consumes structure instead of re-deriving the loader each time.
import json, sys
# subcommand -> loader for its model (each returns plain records the CLI serializes).
SUBCOMMANDS = {
"component": lambda: load_components(),
"service-flow": lambda: load_services(),
}
def dispatch(argv: list[str]) -> int:
if not argv or argv[0] == "models":
print(json.dumps({"subcommands": sorted(SUBCOMMANDS)})) # self-describing
return 0
cmd = argv[0]
if cmd not in SUBCOMMANDS:
print(f"unknown subcommand: {cmd}", file=sys.stderr)
return 2
print(json.dumps(SUBCOMMANDS[cmd](), default=vars)) # deterministic --json contract
return 0
if __name__ == "__main__":
sys.exit(dispatch(sys.argv[1:]))
Consequences
- Not the sole path — Python tools still import models directly;
repo-queryis the canonical read/query path for agents + orchestration, not a lint-banned monopoly. So it's a Soft convenience, not a Hard gate. - Subcommand surface to maintain as models are added.
Example use within DocAble
repo-query—models/component/service-flow/frontend-flow/design/callers/xrefs/epic/task/web-api/configsubcommands (--json).- The
repo-queryorchestrator skill.
Related Patterns
- Bridge — this is the agent-facing face of the models: how a bounded agent reads the unbounded codebase's compressed truth.
- Consumer — reads every model here; e.g. component-zone, service-flow.
- See also — meta-model-consumption: the discipline of reading the model (not a hardcoded copy) —
repo-queryis the canonical way to do it.