本文へ移動
cccskills
無料GitHub で公開

materialize-only-what-queries-need

Decide what to PERSIST in your data structures (graph nodes, DB tables, cache layouts) based on the QUERIES you must answer, not the DEFINITIONS that justify the data. Definitions tell you logical identity; storage strategy is independent. Materializing every definitional component leads to O(N×M) blow-ups that masquerade as memory leaks. Use when: (1) designing a new graph-node type, edge type, DB table, or cache layout; (2) about to name a `subgraph` / `comprises` / `members` / `contains` collection on a parent entity; (3) memory growth proportional to (entities × per-entity-elements); (4) "memory leak" debugging that survives multiple targeted patches — the leak may be storage shape, not retention; (5) pre-implementation sketch includes "for each X, store all reached nodes"; (6) research/spec doc says "X = ⟨A, B, C⟩" and you're tempted to make it three storage edges per X.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md8.0 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Materialize only what queries need

Why this skill

Definitions are about logical identity. Storage is about physical access cost. These are independent. When the spec says

Behavior = ⟨ExecutionSubgraph, Effects, ExitPoints⟩

it tells you what characterizes a Behavior — not how to represent it on disk. Reading the tuple as "store these three pieces" is a category mistake. The result is a representation that contains data no query asks for, at the cost of OOM-grade memory pressure on real-scale inputs.

This is the meta-pattern behind a class of "performance bugs" that resist patching: each patch makes the implementation slightly faster, but the fundamental shape of the persisted data is wrong by orders of magnitude.

In-action triggers (catch the wrong default before writing code)

TriggerReflectQuick check
Naming a collection subgraph / members / contains / comprises on a parent entityList queries that need membershipIf none — drop the collection, store an aggregate
Spec says X = ⟨A, B, C⟩ and you're modeling A, B, C as fields/edges of XDistinguish definition from storageDefinition can stay; storage ≠ definition
Estimated row/edge count = entities × average-children = millionsCost-model the queries you'd serveMost queries are answerable from a 64-byte hash
"Materialize once, query fast" intuitionOnly true if storage_cost ≪ Σ query_cost. Usually false for forward closures over large graphsEstimate aggregate query frequency × on-demand cost vs storage cost
2nd patch at "memory issue" hasn't fixed itClimb from process to premise: am I storing the right thing at all?Apply this skill; the leak is shape, not retention

On-action diagnostic: am I about to over-materialize?

For every persistent collection / edge type / table you're about to create, walk this list. Skip → don't materialize.

  1. Name a concrete query — user-facing or internal — that needs THIS data in THIS shape. If you can't, stop.
  2. Aggregate? Could a hash, count, max/p90, or Set<dimension> answer instead?
  3. On-demand viable? Can the query be computed from existing edges in ≤ 1 s on the realistic-scale graph? If yes, don't precompute.
  4. Direction inverted? Many "X contains Y" queries are answered better by "Y is reachable from X-entry" — runtime backward walk, not stored forward edges.
  5. Frequency? A query that runs once per CI gate doesn't justify precompute. A query in a hot path that scales with graph size might.

If ALL queries answer "aggregate suffices" or "on-demand is fine" — drop the materialized collection.

Diagnostic when memory issues resist patching

Symptoms that point at over-materialization (vs. real leak):

  • Memory grows roughly linearly with the number of parent entities being iterated, not with global state size.
  • Targeted patches (chunked flush, explicit GC, freeing intermediate vars) reduce the slope but don't eliminate it.
  • "Synthetic small fixture passes; real-scale OOMs" — synthetic tests don't show the explosion because per-parent payload is small.
  • Time-to-OOM is roughly proportional to heap_cap. Doubling the cap doubles the time before OOM, instead of completing.

When 2-3 of these are true, the issue is storage shape, not allocation discipline.

Refactor recipe

When you've over-materialized:

  1. Re-derive query list for the artifact under suspicion.
  2. For each query, pick minimum storage:
    • Aggregate (hash, count, p90) → field on parent entity
    • On-demand traversal → no storage; document the query function instead
    • Repeated forward joins on stored data → maybe materialize, with explicit cost model in the spec
  3. Drop the unneeded edges/rows. Existing analyzers/enrichers stop emitting them.
  4. Add a Datalog rule or query function for queries previously served by the materialized form.
  5. Update the spec / research doc: change X = ⟨A, B, C⟩ to clarify "A, B, C characterize X; storage = {hash, summary, ...}".

Anti-patterns

  • "The research doc says it's a tuple, so I store all three components" — category error. Spec gives the concept; storage is independent.
  • "Materialize once, query fast" without a cost model — usually false for forward-closure-over-large-graph cases.
  • Synthetic-only verification — "200 small entities pass, ship it." Always verify on realistic-scale fixture early. The shape bug only shows up when per-parent payload is large.
  • Patching the leak instead of the shape — chunked flush, explicit GC, WeakRef, etc. each look reasonable in isolation but accumulate without fixing the issue. After 2 failed patches, climb to premise.
  • "It's fine for typical case" as exit excuse — if the failing case is a moderate codebase, real worst case is bigger, not smaller. Don't rationalize away the failure.

Concrete failure (Grafema, 2026-04-26)

Spec (research doc):

Behavior  = ⟨ ExecutionSubgraph, Effects, ExitPoints ⟩
ExecutionSubgraph = reachable(EntryPoint, CALLS ∪ READS_FROM) ∩ ...

Initial plan emitted BEHAVIOR -COMPRISES-> code_node edges per touched node. For 149 features × ~thousands of touched nodes per feature (real codebase), that's millions of edges materialized in RFDB.

Real user queries:

  • Auto-doc → CONTRACT (params/returns) + effect summary. Not subgraph.
  • Dedup → hash(sorted(transitive_call_targets)). Not subgraph.
  • Cognitive metrics → counts + depth p90. Not membership.
  • "What features does this code serve?" → backward callers traversal at query time. Not forward subgraph.

Three patch rounds at "memory leak" (chunked flush → trim retained → shared index cache) each cut growth rate but never eliminated it. Round 4 (premise reflection triggered by user pushback) revealed: no query needs the materialized edges. Drop them — peak memory falls ~1000×.

Connection to companion skills

  • reflection-in-and-on-action: catches the trigger ("2nd patch attempt"). This skill provides the resolution at premise level.
  • profile-before-rewrite: don't optimize the wrong thing. This skill is upstream — pick the right shape, then profile. If you've already profiled and the issue is "we're storing X bytes per entity", this skill says "consider not storing X at all".
  • validate-assumptions-before-implementing: same family. Validate the premise (do we even need this data?) before validating the implementation (does the leak fix work?).

When NOT to apply

  • Spec's tuple components are all small and bounded (e.g., a few fields).
  • The query that needs full materialization runs frequently and is in a hot path.
  • You've already verified at realistic scale that materialization is safe and the spec query mix justifies it.

Skill is about catching the default mistake of materializing because the spec describes the concept that way. Some materializations are correct; the discipline is to derive them from queries, not from definitions.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Fix Elixir/Erlang AST processing bugs in Grafema beam-analyzer. Use when: (1) Elixir parser returns MODULE node but 0 functions/calls — body nesting issue, (2) Erlang parser crashes with "cannot convert list to string" on OTP 26+ — location format changed from integer to keyword list, (3) pipe operator |> creates spurious CALL nodes instead of desugared function calls — clause ordering bug, (4) multi-module .ex files return only the first module — missing __block__ handler, (5) installing Erlang/Elixir on macOS with outdated Xcode/Clang.

日本語の概要は準備中です。原文の説明を表示しています。

Disentinel/grafema362026年8月24日 更新

Fires after fixing any non-trivial bug or regression. Asks: could the graph have caught this as a guarantee? Pairs with reflection-in-and-on-action — picks up after "earliest catchable signal" and asks the next question: was that signal expressible in graph? Triggers: (1) after any non-trivial bug fix is verified working, (2) after a regression report (something used to work, broke), (3) during step 6 (knowledge extraction) of the workflow, (4) when reflection-on-action surfaces a "would have been catchable" signal. Outcome is a triage decision (graph-reachable? rule expressible? rule sound?) and either a draft Linear issue + guarantee proposal, or a recorded "graph capability gap" note. Never auto-creates guarantees.

日本語の概要は準備中です。原文の説明を表示しています。

Disentinel/grafema362026年8月24日 更新

Fix Playwright automation failures against code-server (VS Code in browser). Use when: (1) trust dialog blocks all clicks — "monaco-dialog-modal-block intercepts pointer events", (2) button:has-text() finds wrong buttons behind modal dialog, (3) keyboard shortcuts don't work — Meta vs Control inconsistency, (4) VS Code extension activity bar icon not found by aria-label, (5) panels show placeholder text despite extension being "Connected". Covers trust dialog dismissal, keyboard shortcut hybrid mode, extension panel selectors, and database connection verification for Grafema extension.

日本語の概要は準備中です。原文の説明を表示しています。

Disentinel/grafema362026年8月24日 更新

Systematic methodology for achieving 100% backward dataflow reachability in a new language. Create gauntlet fixture, write trace, diagnose gaps, fix analyzer/algorithm, iterate to 100%. Language-agnostic process. Use when: (1) adding a new language to Grafema, (2) auditing dataflow coverage for existing language, (3) user says "/dataflow-gauntlet".

日本語の概要は準備中です。原文の説明を表示しています。

Disentinel/grafema362026年8月24日 更新

Fix docker exec hanging when starting background processes (servers, daemons) inside containers. Use when: (1) docker exec never returns despite using & or nohup, (2) background server started in container causes docker exec to hang indefinitely, (3) env_startup_command in SWE-bench or similar frameworks times out, (4) setsid/disown needed for proper process detachment in Docker. Root cause: docker exec tracks ALL processes in the exec session, not just the top-level PID.

日本語の概要は準備中です。原文の説明を表示しています。

Disentinel/grafema362026年8月24日 更新

Fix Node.js CLI tools crashing inside Docker containers when host-installed node_modules require a newer Node version than the container provides. Use when: (1) "SyntaxError: Invalid regular expression flags" with /v flag in string-width or similar packages, (2) node_modules installed on host with Node 20+ but container has Node 18, (3) `npm install` with file: protocol creates symlinks that break inside Docker, (4) pnpm workspace packages become broken symlinks in containers. Covers version detection, Node binary mounting, and npm install strategies for cross-version compatibility.

日本語の概要は準備中です。原文の説明を表示しています。

Disentinel/grafema362026年8月24日 更新

Disentinel のスキルをすべて見る

このスキルの問題を報告する