Ports two changes from the knowledge-base vault this skeleton is extracted from, keeping the shared scripts byte-identical across the pair: - `graph_summary` reports `by_tag` and `tag_count`, sized by `topTags`, and drops the `by_type`/`by_status` breakdowns. An MCP-only agent could not otherwise discover the tag spellings that `graph_search_nodes` matches verbatim. - `generate-graph.py --validate` now enforces a controlled vocabulary for a note's `type`, which is where the drift signal `by_type` used to give now lives. `AGENT_QUERY_GUIDE.md`, both skills and the-knowing-one quote the enforced enum. `tests/test_generate_graph.py` is new; `make test` is 50 tests. Regenerating the graph also drops a stale `GLOBAL_AGENTS` node for a file this skeleton does not carry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
4.5 KiB
4.5 KiB
id, title, type, status, tags, last_reviewed
| id | title | type | status | tags | last_reviewed | |||
|---|---|---|---|---|---|---|---|---|
| meta/agent-query-guide | Agent Query Guide | meta | active |
|
2026-06-16 |
Agent Query Guide
index/graph.json is for navigation. You can query tags or types there. Use it!
Schema:
- Top-level object:
version(number): schema versiongenerated_at(string): ISO-8601 UTC timestampnodes(array): note metadata recordsedges(array): directed wiki-link records
- Node object (
nodes[]):id(string): stable note identifier (usually from frontmatter)title(string): note titlepath(string): vault-relative markdown pathrepo(string): repo scope (tenant-controller,controller-util,global, ...)type(string): note classification from a controlled vocabulary —hub|project|area|guideline|policy|feature|roadmap|decision|note|meta|reference; absent frontmatter meansnote, and--validatefails on anything elsestatus(string): lifecycle state (active,draft,deprecated)tags(string array): topic tagslast_reviewed(string):YYYY-MM-DDoutbound_links(string array): linked note IDs from this noteinbound_count(number): number of edges targeting this note
- Edge object (
edges[]):from(string): source note IDto(string): target note IDkind(string): link mechanism — normallymarkdown_link(legacywiki_linkis rejected by--validateand should not appear in a valid graph)type(string): semantic relationship (see vocabulary below); defaultrelatedanchor_text(string): display / alias text used in the linkannotation(string): free-text "what the source needs from the destination" (from the link'slink-info, falling back to a trailing— gloss); may be emptyweight(number): link weight, currently1.0
Link syntax
Links are standard markdown links whose target is the stable note id, with an
optional JSON brace suffix carrying the relationship and annotation:
[display text](resource/old-deadlines){"type":"supersedes","link-info":"the deadline table this replaces"}
- A bare
[text](id)with no brace is arelatededge. typemust be one of the controlled vocabulary;generate-graph.py --validatefails on any other value:related, supersedes, superseded-by, depends-on, part-of, parent, derived-from, see-also, source.- Legacy
[[wiki links]]are no longer accepted:generate-graph.py --validatefails on any that remain. Locate and convert them withpython3 scripts/list-wiki-links.py.
Examples with jq:
# list repos
jq -r '.nodes[].repo' index/graph.json | sort -u
# list the tag vocabulary, most frequent first (tags are matched verbatim)
jq -r '[.nodes[].tags[]?] | group_by(.) | map({tag: .[0], count: length})
| sort_by(-.count)[] | "\(.count)\t\(.tag)"' index/graph.json
# list top linked notes
jq -r '.nodes | sort_by(-(.inbound_count // 0))[] | "\(.inbound_count // 0)\t\(.id)\t\(.path)"' index/graph.json
# notes in one repo
jq -r '.nodes[] | select(.repo=="tenant-controller") | "\(.id)\t\(.path)"' index/graph.json
# all projects (PARA category 1)
jq -r '.nodes[] | select(.type=="project") | "\(.id)\t\(.path)"' index/graph.json
# outgoing links from one note
NOTE_ID="repo/controller-util/info"
jq -r --arg id "$NOTE_ID" '.nodes[] | select(.id==$id) | .outbound_links[]?' index/graph.json
# incoming links to one note
NOTE_ID="repo/controller-util/info"
jq -r --arg id "$NOTE_ID" '.edges[] | select(.to==$id) | .from' index/graph.json | sort -u
# outbound edges of a note WITH relationship type + annotation
jq -r --arg id "$NOTE_ID" '.edges[] | select(.from==$id) | "\(.type)\t\(.to)\t\(.annotation)"' index/graph.json
# all edges of a given relationship type (e.g. what supersedes what)
jq -r '.edges[] | select(.type=="supersedes") | "\(.from) -> \(.to)"' index/graph.json
# what a note supersedes
jq -r --arg id "$NOTE_ID" '.edges[] | select(.from==$id and .type=="supersedes") | .to' index/graph.json
For jg, use the same filters if your local jg is jq-compatible.
Validation:
# validate: non-zero exit on unresolved links, edge types or note types
# outside their vocabularies, OR any remaining legacy [[wiki links]]
python3 scripts/generate-graph.py --validate
# regenerate graph and validate
python3 scripts/generate-graph.py && python3 scripts/generate-graph.py --validate
# list any remaining legacy [[wiki links]] (rejected by --validate) to convert
python3 scripts/list-wiki-links.py