Skip to main content

context-intelligence-graph-query

Use when querying the context-intelligence property graph for session history, tool call traces, LLM iteration analysis, execution scale metrics, agent delegation trees, skill loading, and recipe orchestration. Covers all graph layers, cross-layer SOURCED_FROM joins, SST navigation, blob handling, and verified Cypher patterns.

Informations de source

Dépôt
microsoft/amplifier-bundle-context-intelligence
Dernière activité de la source
10 septembre 2026 à 23:19
Langue détectée de SKILL.md
anglais
Étoiles
5
Forks
6

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Explorateur de fichiers
2 fichiers

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
context-intelligence-graph-query
description
Use when querying the context-intelligence property graph for session history, tool call traces, LLM iteration analysis, execution scale metrics, agent delegation trees, skill loading, and recipe orchestration. Covers all graph layers, cross-layer SOURCED_FROM joins, SST navigation, blob handling, and verified Cypher patterns.
license
MIT
visibility
{"priority":5,"summary":"Query the context-intelligence property graph for session history, tool traces, delegation trees, skills, and recipe orchestration via verified Cypher patterns."}
metadata
{"version":"2.6.0","changelog":["2.6.0: Absorbed the former standalone `blob-reading` skill as the level-3 reference `blob-reading.md` beside this file (same content, verbatim), and pointed Section 6 at it. The catalog now carries one always-visible entry for graph work instead of two; nothing was dropped - the full blob contract (when not to resolve, the four-step resolution, blob_read's success/failure shapes and which errors carry a citable `source`, the safe jq patterns, blob-vs-inline detection) moved intact into the reference file.","2.5.0: Elevated the anti-multi-count guidance scattered across Trap 6 (cost) and Trap 8 (tokens) into a single named cross-cutting rule (Section 3: \"Rule — one owner per numeric fact: never multi-count cost or tokens by over-reading events\") — every usage value is emitted once per LLM turn, owned by :LlmResponseEvent, and echoed onto ContentBlockEndEvent / ProviderResponseEvent; aggregating across the wider set sums it 2–3x over. Trimmed Trap 6 and Trap 8 to reference the rule instead of each restating the mechanism; both traps keep only their field-specific specifics (cost_usd is a STRING requiring toFloat; token/cache fields are INTEGER with the correct cache field names). Section 8 queries unchanged.","2.4.0: Added Trap 8 (token & cache usage fields on :LlmResponseEvent are payload INTEGERs — unlike cost_usd's STRING — but still require pinning to :LlmResponseEvent; an unpinned :Event sweep replays the same usage map via ContentBlockEndEvent/ProviderResponseEvent and inflates every total by ~3.4x, measured: cost 3.43x, input 3.31x, output 3.63x, cache 3.39x) with a validated combined cost+token aggregation and a per-model breakdown pattern in Section 8. Documented the correct cache field names (cache_read_input_tokens, cache_creation_input_tokens — complete) versus the sparse/wrong-typed alternatives (cache_read_tokens, cache_creation), and reinforced that Iteration.usage_input/usage_output/usage_cache_write (Trap 7) must never be used for token or cost aggregation — they are last-write-wins corrupted and Iteration carries no cost field at all.","2.3.0: Corrected the working_dir population figure from an inaccurate ~7% (238/3,259 sessions) down to its true measured value: ~0% queryable in-graph (0 of 4,504 / 0 of 9,086 sessions — not a lifted Session property); workspace remains ~100% and the only valid scoping lever. Added Trap 6 (cost_usd totals: a JSON-string payload field, not a lifted property; a bare sum() returns HTTP 500; must pin to :LlmResponseEvent to avoid triple-counting against ContentBlockEndEvent / ProviderResponseEvent) with a validated APOC aggregation pattern in Section 8. Added Trap 7 (Iteration.node_id MERGEs across orchestrator runs, corrupting iteration-scoped counts/aggregates by up to +66%) with caveats on every affected iteration-based query pattern. Added a validated temporal-join pattern (Section 7) for per-prompt/per-run tool-call attribution — more accurate than the structural run→Iteration→ToolCall path and unaffected by the Trap 7 MERGE bug.","2.2.0: Corrected the Section 2 workspace/working_dir claims — workspace is a lifted, reliable (~100%) scoping slug, not the literal working directory; working_dir is ~0% queryable in-graph (0 of 4,504 / 0 of 9,086 sessions — not a lifted Session property) payload-only field with no canonical per-session source, and must never be used to scope a population. Consolidated one visible+correct in-graph APOC payload-parse pattern: dot-access on a JSON-string field (e.g. e.data.working_dir) raises a type error, not a silent null — parse in-graph with apoc.convert.fromJsonMap and return only the scalar needed. De-duplicated the Section 6 copy into a pointer at this pattern.","2.1.0: Reporting guidance — agent-level / self-delegation answers must STATE the resolved self→actor breakdown (root/main vs named) as an explicit standalone finding, not fold it into a blended statistic. Tightened the Section 8 agent-rollup: project Session/FORKED (the labels that exist) and roll up by the Session.agent property; agents are NOT a projectable Agent graph.","2.0.1: Schema accuracy fix: corrected Prompt/SkillLoad/Orchestrator property names, RecipeStep/RecipeRun property placement, RecipeStep→RecipeRun edge (SPAWNED), removed phantom L1 edges, fixed example queries — all validated against the live graph."]}
## When the graph server is not configured If the context-intelligence server is unavailable or not configured for this session, graph-based analysis cannot proceed here. Delegate immediately to `session-navigator` for all session analysis, event lookup, and delegation tree tracing — do not attempt Cypher queries against a server that isn't reachable. --- # Context Intelligence Graph Query This skill equips you to navigate and extract insights from the context-intelligence property graph using the `graph_query` tool. The graph holds a complete record of every Amplifier session — what happened, when, how things connect, and at what scale. **Read the live schema from the server; read the meaning from this skill.** The server can always tell you the current shapes — labels, relationship types, property names. It CANNOT tell you what those shapes mean, where they lie about their meaning, or how to scope, join, and bound a query safely. This skill deliberately does not freeze a schema dictionary (it goes stale and misleads). It keeps the grammar: the scoping levers, the traps, the cross-layer joins, and the verified patterns. --- ## Section 1 — Introspect the Live Schema First Before writing analytical Cypher against an unfamiliar area of the graph, ask the server what it actually contains. These calls are cheap and always current. ```cypher CALL db.labels() // every node label present CALL db.relationshipTypes() // every edge type present CALL db.schema.nodeTypeProperties() // property names + types per label CALL db.schema.visualization() // label/edge connectivity overview ``` To learn the real properties on a specific label (names AND which are populated), sample a few live nodes rather than trusting any documented list: ```cypher MATCH (n:ToolCall) WITH n LIMIT 25 UNWIND keys(n) AS k RETURN k AS property, count(*) AS present_on_n_nodes ORDER BY present_on_n_nodes DESC ``` **The three-layer shape (stable orientation, not a dictionary):** - **Data layer 1 — raw events.** Every kernel event preserved as an `:Event` node (`:ToolPreEvent`, `:LlmResponseEvent`, `:PromptSubmitEvent`, …). Answers *what happened and when*. `node_id` uses a `__` (double-underscore) separator. - **Data layer 2 — semantic entities.** Events assembled into `Session`, `OrchestratorRun`, `Iteration`, `ContentBlock`, `ToolCall`, `Prompt`, and more, connected by typed relationships. Answers *what ran, how, at what scale*. `node_id` uses a `::` (double-colon) separator (a few types — `ToolCall`, concept nodes — use a bare identifier). - **Foundation layer — above the kernel.** Delegation trees (`Delegation`, `Agent`), skill loads (`SkillLoad`), recipe orchestration (`RecipeRun`, `RecipeStep`, `Recipe`). All layers coexist in one graph, bridged by **`SOURCED_FROM`** edges: every layer-2 entity points back to the layer-1 event(s) that produced it (Section 4). Use `db.schema.nodeTypeProperties()` to confirm exact property names before you rely on them — the sections below cover only the meaning the server cannot self-describe. ### Reading results & citing the source (required) `graph_query` returns `{"source": {"name", "url", "origin"}, "rows": [...]}` on success. Read your data from `rows`. ALWAYS report `source.name` (its `origin` is `source`, `destination`, or `env`) in your answer — the user must know which endpoint the outcome came from. On **failure**, `source` is present only when an endpoint was actually chosen: **endpoint-level** errors (`connection_error`, `timeout`, `http_status`, `decode_error`, and input-validation errors like a missing/invalid `query` that occur *after* selection) carry `error.source` — cite it. **Selection/config** errors that occur *before* any endpoint is chosen (`ambiguous_source_selection`, `unknown_source`, `source_misconfigured`, `configuration_error`) have **no** `source` — there is no single endpoint to name, so report that selection failed rather than inventing one. To discover what you can reach, call `graph_query` with `list_sources: true` — it returns the connectable set (`{"connectable_set": [{name, url, origin}, ...]}`) without running a query. To target a specific endpoint (a configured read source OR a hook upload destination), pass `source: "<name>"`. --- ## Section 2 — Scoping (Mandatory, and Not Self-Describing) Scoping is the single most error-prone part of querying this graph, and the server cannot tell you how its client scopes. Get this right first. ### The `workspace` tool argument — NOT a Cypher param **The `graph_query` tool reads a top-level `workspace` argument.** It injects that value as `$workspace` into your Cypher. Do **not** try to set the workspace through `params: {"workspace": ...}` — the tool does not read the workspace from `params`, and a mis-set workspace silently scopes to a tiny `"default"` decoy partition instead of the real one. If your counts look implausibly small (a handful of sessions where you expected hundreds), you are almost certainly scoped to the decoy. ```cypher // Correct: reference $workspace; set the workspace via the tool's top-level // `workspace` argument, not via params. MATCH (s:Session {workspace: $workspace}) RETURN count(s) AS session_count ``` ### The two real scoping levers Both live as first-class properties on nodes — confirm them by sampling: - **`created_by`** — *who* produced the data (present on every node; the data is typically single-user). - **`workspace`** — *where*: a **lifted, dash-slugified label/slug** (e.g. `-mnt-linuxdata-workspaces-...-dashboard-ui`), reliable at ~100% coverage. It is a **label, not the literal working directory**: the true directory lives in the event `data` payload as `working_dir`, and the **same `working_dir`** (`/home/user/project`) can map to **different** `workspace` slugs (`myproject`, `proj`, `multi`). Treat `workspace` as the reliable scoping slug; treat `working_dir` as a **payload field, ~0% queryable in-graph (0 of 4,504 / 0 of 9,086 sessions — not a lifted Session property)** with no canonical per-session source — read it via the APOC parse in Section 8, but do **not** scope by it. The `/cypher` request's own `workspace` scope is a second, orthogonal server-side filter layered on top. **Always anchor scope on the first `MATCH`** so the workspace index is used: ```cypher // CORRECT — workspace on the anchor node MATCH (s:Session {workspace: $workspace, node_id: $session_id}) -[:HAS_EXECUTION]->(run:OrchestratorRun) RETURN run.orchestrator_name, run.started_at // INCORRECT — workspace on a downstream node misses the index MATCH (s:Session {node_id: $session_id}) -[:HAS_EXECUTION]->(run:OrchestratorRun {workspace: $workspace}) RETURN run.orchestrator_name, run.started_at ``` To discover which workspaces exist before scoping, aggregate across all of them (use sparingly — it skips the partition index): ```cypher MATCH (s:Session) WHERE s.workspace <> '' RETURN s.workspace, s.created_by, count(s) AS sessions ORDER BY sessions DESC LIMIT 25 ``` ### Resolving "my" — use `whoami`, don't guess `created_by` scopes by *who produced the data*, but the graph never tells you who "you" are. When a request is scoped to the acting user themselves — "my sessions", "sessions I created", "what have I been working on" — call the `whoami` tool for the relevant server before filtering on `created_by`: ```python whoami() # default endpoint whoami(source="prod") # a specific configured endpoint ``` It returns `{"contributor_id": "<github-id-or-null>", "source": {...}}`. Use `contributor_id` as the value to match against `created_by`: ```cypher MATCH (s:Session {workspace: $workspace, created_by: $contributor_id}) RETURN count(s) AS my_sessions ``` **Null case.** A null `contributor_id` means auth is disabled (or otherwise unknown) on that server — there is no acting-user identity to filter by. Do **not** guess or fall back to some other field. Ask the user how they want to scope the query instead (e.g. by name, by workspace, or "show everyone's"). --- ## Section 3 — Traps: Where the Shapes Lie About Their Meaning These are the highest-value items in this skill. Each is a place where reading the schema literally leads you to a **wrong answer that looks rigorous**. The server will happily hand you the misleading shape; only domain knowledge corrects it. ### Trap 1 — Delegation lineage: use `FORKED` + `Session.parent_id`, not the edge walk The true delegation/fork lineage is carried by the **`FORKED` edge** together with **`Session.parent_id`**. This is the authoritative source for delegation depth and ancestry. Verified live: the fork lineage reaches **max depth 3**. ```cypher // Authoritative lineage depth via the FORKED edge. // Bound the path (never bare `*` — Section 7): *1..20 is generous headroom over the // observed max depth of 3, and the RETURN is aggregate-only (no rows materialized). MATCH path = (root:Session {workspace: $workspace})-[:FORKED*1..20]->(leaf:Session) WHERE root.parent_id IS NULL RETURN max(length(path)) AS max_fork_depth, count(path) AS lineages ``` `Session.parent_id` lets you reconstruct the same chain by property when an edge walk is inconvenient (each child names its parent): ```cypher MATCH (child:Session {workspace: $workspace}) WHERE child.parent_id IS NOT NULL RETURN child.node_id, child.parent_id ORDER BY child.node_id LIMIT 200 // bounded (Section 7); anchor on a node_id to walk one specific chain ``` **Two known undercounts — do NOT trust either for depth:** - **`HAS_SUBSESSION` is a single hop only.** It materializes only the immediate parent→child link; each sub-session is ingested as its own root, so a multi-hop walk `(:Session)-[:HAS_SUBSESSION*]->()` collapses to depth 1. It is a one-level lookup, **not** the delegation tree. - **The `Delegation` property-join (`sub_session_id → parent_session_id`) undercounts beyond hop 1.** It is more complete than the `HAS_SUBSESSION` walk (it finds a second hop), which is exactly why it is dangerous: it *looks* like the answer and reports depth 2, but it still misses the deeper lineage that `FORKED`/`parent_id` reveals. Use it to enumerate individual delegations, not to measure tree depth. ### Trap 2 — `context_depth` / `context_scope` are inheritance modes, NOT tree depth On `Delegation` nodes, `context_depth` and `context_scope` describe **how much parent context a delegation inherited** — an inheritance mode with values like `none`, `recent`, `all`. The name invites reading it as nesting/tree depth. It is not. Self-delegations are almost always `none`; regular delegations spread across `none` / `recent` / `all`. ### Trap 3 — Tool data lives on `ToolCall`, not `ToolExecution` `:ToolCall` carries the tool data you want: `tool_name`, `tool_input`, `started_at`, `ended_at`, `result_success`, `result_error`, `result_output`. `:ToolExecution` is **property-less — a dead end**; do not route tool queries through it. `ToolCall` is the correct, simple source (verified: `tool_name` and `started_at` populated on 100% of `ToolCall` nodes in a real workspace). ```cypher MATCH (t:ToolCall {workspace: $workspace}) RETURN t.tool_name AS tool, count(*) AS n ORDER BY n DESC LIMIT 12 ``` ### Trap 4 — `self` is a self-delegation marker, never a real agent Self-delegation is recorded explicitly as **`Delegation.is_self_delegation = true`** (equivalently the child `Session.agent = 'self'`). Never infer it by string-matching `tool_input` or comparing names. **`'self'` is a marker, not an identity** — it means "this session spawned a fresh sub-context of *itself*," so `self` is never the real actor. Before any per-agent stat (centrality, counts, durations), **resolve it up the fork chain** to the nearest ancestor whose `agent <> 'self'`. That actor is often the **root/main session** (`agent IS NULL` — the user's top-level session, not a named sub-agent); occasionally a named agent that self-delegated. Empirically the chain is a single hop (no `self`→`self`→…). ```cypher // Resolve every self-session to the actor that actually spawned it MATCH (s:Session {workspace: $workspace, agent: 'self'}) OPTIONAL MATCH pth = (anc:Session)-[:FORKED*1..20]->(s) WHERE anc.agent IS NULL OR anc.agent <> 'self' WITH s, anc, pth ORDER BY length(pth) ASC WITH s, head(collect(coalesce(anc.agent, 'root/main session'))) AS actor RETURN coalesce(actor, 'unresolved') AS real_actor, count(*) AS self_delegations ORDER BY self_delegations DESC ``` Self-delegation is itself a **first-class signal** (an actor extending its own context — continuation / context management): count it *as* self-delegation when that is the question, and *resolve* it when you need the real actor. A raw `self` bucket in a per-agent aggregate is a bug — it attributes real work to a non-existent agent. **Report the resolved breakdown as an explicit finding — do not bury it in a blended stat.** The resolve-self join above only pays off if its result reaches the answer as its *own stated fact*. For any agent-level or self-delegation question, **lead with the resolved `self`→actor split as a standalone sentence** — how many, and what percent, of self-delegations resolve to the **root/main session continuing itself** (`agent IS NULL`) versus to **named agents** — *before* any roll-up or combined figure. Computing the number correctly but folding it into a mixed "X% of all delegations are root-launched" statistic hides the very fact the question asked for. This is a general output-shape principle: surface the load-bearing breakdown first, then add supporting context — state it, don't just compute it. ```cypher // The plain self-delegation split (the signal itself) MATCH (d:Delegation {workspace: $workspace}) RETURN d.is_self_delegation AS self_delegation, count(*) AS n ORDER BY n DESC ``` ### Trap 5 — Timestamps are ZONED DATETIME; durations are computed Every `*_at` property (`started_at`, `ended_at`, `occurred_at`, …) and `last_updated` — on nodes and on the edges that carry `occurred_at` (`HAS_EVENT`, `HAS_SUBSESSION`, `FORKED`) — is stored as a native Neo4j **`ZONED DATETIME`**, not a string. - ❌ `WHERE s.started_at > '2026-05-01'` — comparing to a string literal silently returns nothing (no error raised). - ✅ `WHERE s.started_at > datetime('2026-05-01')` — wrap every literal in `datetime()`. - ✅ Rolling windows: `WHERE s.started_at > datetime() - duration('P30D')`. **Durations are not stored — compute them** with `duration.between()`: ```cypher MATCH (t:ToolCall {workspace: $workspace}) WHERE t.started_at IS NOT NULL AND t.ended_at IS NOT NULL WITH t, duration.between(t.started_at, t.ended_at) AS d RETURN t.tool_name AS tool, d.seconds AS secs ORDER BY secs DESC LIMIT 8 ``` ### Rule — one owner per numeric fact: never multi-count cost or tokens by over-reading events This is a cross-cutting rule, not a single trap — Traps 6 and 8 below are both instances of it. Every numeric usage value — dollars (`cost_usd`) and every token field (`input_tokens`, `output_tokens`, `cache_read_input_tokens`, `cache_creation_input_tokens`) — is emitted **once** per LLM turn and is **owned** by the `:LlmResponseEvent` node. The identical `usage` map is then **echoed** onto other events of the same turn: `ContentBlockEndEvent` (streaming replays it ~2.33x
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub