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.

Quellinformationen

Repository
microsoft/amplifier-bundle-context-intelligence
Letzte Quellaktivität
10. September 2026 um 23:19
Erkannte Sprache von SKILL.md
Englisch
Sterne
5
Forks
7

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
2 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
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
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen