Skip to content

feat(profiling): hook the enrich phase into the profiler - #477

Open
Disentinel wants to merge 2 commits into
feat/pipeline-profile-graphfrom
feat/enrich-profiling
Open

feat(profiling): hook the enrich phase into the profiler#477
Disentinel wants to merge 2 commits into
feat/pipeline-profile-graphfrom
feat/enrich-profiling

Conversation

@Disentinel

Copy link
Copy Markdown
Owner

What

Hooks the enrich phase into the analysis profiler — the second profiler blind spot after the derive packs (closed in the profile-subgraph PR this is stacked on).

Enrichment ran in two invisible places:

  • Rust plugins (type-inference ~64s, shape-tracker ~34s) — run inside the orchestrator but were silently folded into resolve_ms (~98s mislabeled as resolution).
  • TS enrichers (mcp-tool, contract, speced-contract, behavior, package-api, library-callback) — run in the CLI after the orchestrator exits, so they never touched the JSONL.

Changes

  • Rust (grafema-orchestrator): de-conflate the user-plugins from resolve_ms into a dedicated enrich_ms; emit one enrich_plugin_complete profiler event per plugin; add enrich to phase_summary + the phase METRIC nodes; map enrich_plugin_completephase=enrich profile:stage in build_profile_stages (lands in the subgraph in-process, before the commit).
  • TS (@grafema/cli): new profileAppend.ts util that appends to analysis-profile.jsonl in the orchestrator's schema; an enrichStep wrapper times each enricher — including ones that skip on an RPC timeout (via finally) — and emits enrich_step_complete.
  • build_profile_stages also maps enrich_step_complete (ready for when the subgraph is rebuilt from the full JSONL after enrich — the orchestrator currently commits the subgraph from its in-memory stream, which predates the TS phase).
  • Docs: _ai/profile-subgraph.md + CHANGELOG.md.

Verification (real self-analyze on the grafema monorepo)

phase_summary: resolve_ms=102024  enrich_ms=97783  depends_on_ms=157729  total_ms=414788
enrich_plugin_complete  type-inference   63858ms
enrich_plugin_complete  shape-tracker    33925ms
enrich_step_complete    library-callbacks  2ms
Profile subgraph committed … stages=47   (was 45 → +2 enrich plugins)

resolve_ms dropped from a single conflated ~200s to a true 102s resolve + 98s enrich.

Note

This run surfaced a pre-existing slow/hanging mcp-tool-defs enricher (a queryNodesStream that no longer fails fast) — exactly the kind of bottleneck this instrumentation is meant to make visible. Orthogonal to this PR.

Base

Stacked on feat/pipeline-profile-graph (the profile-subgraph PR). Merge that first.

🤖 Generated with Claude Code

Vadim Reshetnikov and others added 2 commits June 19, 2026 17:01
The enrich phase was a profiler blind spot. The Rust enrichment plugins
(type-inference ~64s, shape-tracker ~34s) ran inside the orchestrator but
were silently folded into resolve_ms — ~98s mislabeled as resolution. The
TS enrichers (mcp-tool, contract, behavior, package-api, …) run in the CLI
after the orchestrator exits and never touched the JSONL at all.

Now:
- Rust plugins are de-conflated from resolve_ms into a dedicated enrich_ms,
  emit one `enrich_plugin_complete` event each, and land as `phase=enrich`
  profile:stage nodes in the subgraph (in-process, before the commit).
- TS enrichers append `enrich_step_complete` events to the same
  analysis-profile.jsonl via a small profileAppend util, timed across
  success AND RPC-timeout skips (finally), so the tail is visible in the
  JSONL profiler and the route view.
- build_profile_stages maps both event types to phase=enrich; the
  enrich_step arm is ready for when the subgraph is rebuilt from the
  full JSONL after enrich.

Verified by a real self-analyze: phase_summary now reports
resolve_ms=102024 + enrich_ms=97783 (was one conflated 200s resolve);
enrich_plugin_complete events for type-inference/shape-tracker; the
subgraph gains the 2 enrich stages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PRECEDES encodes the ACTUAL (sequential) execution order; this adds REQUIRES
— the subset that is a true data dependency. A PRECEDES edge with no REQUIRES
backing it means a stage ran sequentially but did not need to: that is
parallelism headroom.

- REQUIRES edges between profile:stage nodes. Derive model (conservative,
  so headroom is a floor): language verticals chain internally; shared sinks
  (depends/method_calls/shape_verifier) require the last pack of every
  vertical; shape_verifier also requires method_calls; resolvers and TS
  enrich steps have no REQUIRES (fully parallel); shape-tracker requires
  type-inference.
- Per-phase critical_path_ms + parallelism_headroom_ms METRICs; a run-level
  parallelism_headroom_ms on profile:run.
- "What could run in parallel" = total_work - critical_path is now a graph
  query. On the grafema monorepo: resolve ~48s headroom (4 independent
  language resolvers run sequentially), derive ~57s (independent verticals).

Pure-function change; 4 unit tests (incl. headroom) green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant