Skip to content

[Rust port] relayburn-analyze: ghost-surface detector #273

@willwashburn

Description

@willwashburn

Parent: #244
Depends on: #242, #266 (foundation), #268 (findings)

Context

"Ghost surface" detects skill / command names a session referenced but never invoked — i.e. the LLM saw a list of available tools/skills/commands but ignored most of them, paying tokens for unused surface area. Per-harness mining adapters (Claude / Codex / OpenCode) extract candidate names from session text, the detector cross-references with observed call patterns, and findings get emitted with savings estimates.

This is a single ~930 LoC chunk because the inputs file (ghost-surface-inputs.ts) is tightly coupled to the detector — it builds the per-source observed-name maps the detector consumes.

Scope

ghost_surface_inputs.rs (port of ghost-surface-inputs.ts, ~137 LoC)

  • Port buildObservedNamesBySource(turns), buildSessionCountBySource(turns), pickRepresentativeCacheReadRate(turns), buildGhostSurfaceInputs(turns).
  • Pure aggregations over EnrichedTurn[] — straightforward.

ghost_surface.rs (port of ghost-surface.ts, ~793 LoC)

  • Port GhostFindingKind (string-literal union — closed enum), GhostSurfaceFinding, GhostCandidate, GhostSurfaceInputs, GhostSurfaceAdapter, DetectGhostSurfaceOptions, GhostSurfaceFindingOptions types.
  • Port mineClaudeCommandNames(...), mineCodexSlashInvocations(...) — text-mining helpers that grep for /foo slash-command tokens in session bodies.
  • Port the three default adapters: claudeGhostAdapter, codexGhostAdapter, opencodeGhostAdapter. Each is a GhostSurfaceAdapter impl that knows how to mine its harness's session shape. Use a static &'static [&'static dyn GhostSurfaceAdapter] or an enum dispatch — Rust-idiomatic, no need to mirror the JS const adapter: GhostSurfaceAdapter = {...} literal shape.
  • Port DEFAULT_GHOST_ADAPTERS constant.
  • Port detectGhostSurface(inputs, options) — the main entry point.
  • Port ghostSurfaceToFinding(candidate, options) and ghostFindingsToWasteFindings(findings) adapters.

Conformance gate

  • packages/analyze/src/ghost-surface.test.ts (564 lines)

The fixture corpus exercises all three adapters across mixed-source sessions. Output ordering must be deterministic — sort findings by (severity desc, savings desc, kind, name).

Acceptance

  • cargo test -p relayburn-analyze green for ghost-surface.
  • Public surface: claudeGhostAdapter, codexGhostAdapter, opencodeGhostAdapter, DEFAULT_GHOST_ADAPTERS, detectGhostSurface, ghostSurfaceToFinding, ghostFindingsToWasteFindings, buildGhostSurfaceInputs, buildObservedNamesBySource, buildSessionCountBySource, pickRepresentativeCacheReadRate, plus all listed types.
  • Adapter trait shape allows downstream sub-issues (e.g. a future Cursor adapter) to plug in without modifying core detector logic.
  • Finding ordering matches TS on the fixture corpus.

Files

  • packages/analyze/src/ghost-surface.ts + ghost-surface.test.ts
  • packages/analyze/src/ghost-surface-inputs.ts

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions