Add cross-harness ghost user-installed surface detector (#166)#179
Merged
willwashburn merged 2 commits intomainfrom Apr 28, 2026
Merged
Add cross-harness ghost user-installed surface detector (#166)#179willwashburn merged 2 commits intomainfrom
willwashburn merged 2 commits intomainfrom
Conversation
Detects user-installed surface files (agents/skills/commands/prompts/rules /memories) that ride in every session's system prompt but are never invoked in the observed window — across Claude, Codex, and OpenCode via per-harness GhostSurfaceAdapters. Surfaced through `burn waste --patterns ghost-surface` with a `mv <path> <archive-dir>/` command-style WasteAction. OpenCode declared-catalog skills emit at $0 to avoid double-counting against the existing #54 catalog-bloat detector. Adapter inputs reserve a userTurnTextBySession field for the #172 slash-command follow-up so it can land without a breaking change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9 tasks
Merge resolves the packages/cli/CHANGELOG.md conflict against #148's OpenCode compaction-event entry by keeping both [Unreleased] bullets. Devin review fixes (PR #179): 1. Severity / usdPerSession used cumulative cost instead of per-session cost. `GhostSurfaceFinding` now carries a `costPerSession` field (sizeTokens × dollarPerToken); the unified WasteFinding envelope reads that for `estimatedSavings.usdPerSession` and severity classification. `cost` stays cumulative so the `burn waste` ghost-surface table column keeps its meaning. A 100k-session × small-per-session ghost no longer ranks above a single high-cost retry loop. 2. Suggested-fix `mv` action interpolated paths without shell quoting (broken on spaces) and tried to `mv` synthetic OpenCode JSON-pointer paths like `opencode.json#/commands/foo` (not real files). Real-path ghosts now use POSIX single-quoted paths; synthetic OpenCode entries emit a `paste`-style instruction pointing at the config fragment to remove instead of an `mv` command. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
detectGhostSurface(...)orchestrator + per-harnessGhostSurfaceAdapters (claudeGhostAdapter,codexGhostAdapter,opencodeGhostAdapter) in@relayburn/analyze. Adapters enumerate user-installed surface files (Claude~/.claude/{agents,skills,commands}/, Codex~/.codex/{prompts,skills,rules,memories}/, OpenCodeopencode.jsondeclared skills + custom commands + project skills folder) and the orchestrator cross-references basenames against an observed-names set mined from the turn stream to flag ghosts that ride in every system prompt but are never invoked.burn waste --patterns ghost-surfacewith a labeled section, JSON output (ghostSurfacearray), and unified-finding rendering (--findings). Suggested fix is acommand-styleWasteAction:mkdir -p <archive-dir> && mv <path> <archive-dir>/.opencode.json) but emitted withcost: 0and acatalog (#54)note to avoid double-counting against the existingSystemPromptTaxdetector.GhostSurfaceInputscontract reserves auserTurnTextBySessionfield for the Ghost surface: detect Codex/Claude slash-command invocations from session content #172 slash-command follow-up so it can land without a breaking change.claudeGhostAdapterandcodexGhostAdaptercarry an inline doc note that slash-command-style invocations are not currently detected from tool calls.Acceptance criteria
detectGhostSurface) runs per-harness adapters viaDEFAULT_GHOST_ADAPTERS.prompts/openspec-archive.mdexample called out in the issue. Smoke-tested against the user's real ledger and it surfaced~/.codex/prompts/openspec-archive.mdas predicted.cost: 0withcountedByCatalogBloat: true; project-folder skills and custom commands cost normally.burn wastealongside existing finding categories — new--patterns ghost-surfacevalue, dedicated text/JSON sections, folded into--findingsenvelope.claudeGhostAdapterandcodexGhostAdaptercarry inline notes that slash-command-style invocations are a Ghost surface: detect Codex/Claude slash-command invocations from session content #172 follow-up.pnpm run build); 727 tests pass (pnpm run test); no lint errors (no lint step in this repo).Test plan
pnpm run buildpnpm run test(727 passed, 0 failed)node packages/cli/dist/cli.js waste --patterns ghost-surfaceand--findingsagainst the live ledger — output renders the newGhost user-installed surfacetable and surfaces real ghosts including the issue's canonical example.Closes #166