Skip to content

Split investigationEvidence into a directory, one adapter per tool family - #1731

Merged
nadaverell merged 2 commits into
mainfrom
refactor/investigation-evidence-directory
Sep 13, 2026
Merged

nadaverell merged 2 commits into
mainfrom
refactor/investigation-evidence-directory

Conversation

@nadaverell

@nadaverell nadaverell commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1730.

investigationEvidence.ts had grown to 5,572 lines with 46 exports — the largest file in web/src/components/diagnose/. Every tool adapter, the observation model, the projection builder and the shared types lived in it, so each new tool landed in the same file and every change competed for the same merge surface.

What changed

The module is now a directory, one file per concern and one adapter per tool family:

types.ts wire and projection types
identity.ts evidence refs, source ids, DOM ids
parse.ts payload coercion shared across adapters
observations.ts ProjectionBuilder, relevance, limitations, observation adders
adapters/ changes diagnose events helm issues logs permissions prometheus resource topology
builder.ts the ADAPTERS registry and projectInvestigationEvidence
index.ts the public surface

Adding a tool is now a new file under adapters/ plus a registry line.

No importer changed and no shim was left behind. All 16 call sites already import "./investigationEvidence", which now resolves to the directory's index.ts. That file re-exports exactly the 46 names the single file exported — verified as an exact set comparison, nothing added, nothing dropped. Symbols that only a sibling needs are exported at file level but deliberately kept out of index.ts, so the module's public surface is unchanged rather than widened by the split.

The dependency graph is acyclic by construction: types → parse/identity → observations → adapters → builder → index.

Verification

This is a pure move, and it is checked as one rather than asserted:

  • The moved code is token-for-token identical to the original — 39,252 tokens, 1,892 distinct, zero difference in either direction, comparing the original body against all new files with import/export lines normalised away. The only textual edits are the added imports/exports and Prettier re-wrapping eleven signatures that the export keyword pushed past the print width.
  • The test file is untouched. investigationEvidence.test.ts (6,617 lines) is not modified by this PR — that is what makes the suite an independent check rather than a circular one.
  • npm test — 1,135 passed (90 files), identical to the baseline measured on the same commit with these changes stashed.
  • tsc --noEmit clean, eslint clean, prettier --check clean.

Notes

Splitting investigationEvidence.test.ts along the same seams is deliberately deferred to a follow-up. Doing it here would remove the property that makes this PR verifiable: the evidence that the move preserves behaviour is precisely that the tests did not change and still pass.

ðŸĪ– Generated with Claude Code

https://claude.ai/code/session_0125QDX24TNuod7FKLwTDhvW


Note

Low Risk
Structural refactor with unchanged public API and unchanged tests; no intended runtime or diagnosis behavior change.

Overview
Replaces the 5,572-line monolithic investigationEvidence.ts with an investigationEvidence/ package so tool-specific logic and shared projection code live in separate files instead of one merge hotspot.

The layout separates types, identity/DOM helpers, payload parsing, observations (ProjectionBuilder and shared adders), per-tool adapters under adapters/, and builder (ADAPTERS registry plus projectInvestigationEvidence). index.ts re-exports the same 46 public symbols as before, so existing imports of "./investigationEvidence" stay valid with no call-site or shim changes.

This is intended as a behavior-preserving move (logic moved verbatim; tests unchanged). New investigation tools can be added as an adapter file plus one registry entry rather than extending the giant single file.

Reviewed by Cursor Bugbot for commit 95d5741. Bugbot is set up for automated code reviews on this repo. Configure here.

One file per concern, one adapter per tool family. Adding a tool now means a
new file under adapters/ and a line in the ADAPTERS registry, instead of
another few hundred lines in a file every other adapter is also editing.

  types.ts        the wire and projection types
  identity.ts     evidence refs, source ids, DOM ids
  parse.ts        payload coercion shared across adapters
  observations.ts ProjectionBuilder, relevance, limitations, observations
  adapters/       changes diagnose events helm issues logs
                  permissions prometheus resource topology
  builder.ts      the adapter registry and projectInvestigationEvidence
  index.ts        the public surface

index.ts re-exports exactly the 46 names the single file exported, and every
call site already imports "./investigationEvidence", which now resolves to the
directory — so no importer changes and no compatibility shim is left behind.
Symbols a sibling needs are exported at file level but deliberately kept out of
index.ts; the module's public surface is unchanged.

Pure move: no behaviour change. The token stream of the moved code is identical
to the original (39252 tokens, 1892 distinct, zero difference), the only edits
being added import/export lines and Prettier re-wrapping eleven signatures that
the export keyword pushed past the print width.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0125QDX24TNuod7FKLwTDhvW
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Modularize investigation evidence by tool family

âœĻ Enhancement 🕐 40+ Minutes

Grey Divider

AI Description

â€Ē Splits the 5,572-line evidence module into focused types, parsing, identity, and projection
 layers.
â€Ē Isolates each tool family adapter behind a centralized registry.
â€Ē Preserves all 46 public exports and existing importer resolution without behavior changes.
Diagram

graph TD
  C["External Consumers"] --> I["Public Index"] --> B["Projection Builder"] --> A["Tool Adapters"] --> O["Observation Model"] --> H["Identity Helpers"] --> P["Payload Parsing"] --> T["Shared Types"]
  I --> H
  I --> T
  B --> O
  A --> H
  A --> P
  A --> T
  O --> P
  O --> T
  H --> T
Loading
High-Level Assessment

The directory split with a stable barrel export and explicit adapter registry is the appropriate approach. It reduces merge contention and isolates tool-family concerns without changing importers or widening the public API; retaining the monolith would preserve the existing maintenance problem, while a dynamic plugin system would add unnecessary runtime complexity for a fixed adapter set.

Files changed (16) +5840 / -0

Refactor (16) +5840 / -0
changes.tsExtract recent-change adapter +60/-0

Extract recent-change adapter

â€Ē Moves recent-change payload validation, coverage limitations, relevance, and observation creation into the changes tool-family adapter.

web/src/components/diagnose/investigationEvidence/adapters/changes.ts

diagnose.tsExtract diagnosis bundle adapter +935/-0

Extract diagnosis bundle adapter

â€Ē Moves diagnosis projection for resources, issues, startup blockers, crashes, logs, events, changes, network evidence, DNS, and workload metrics into a dedicated adapter.

web/src/components/diagnose/investigationEvidence/adapters/diagnose.ts

events.tsExtract Kubernetes events adapter +96/-0

Extract Kubernetes events adapter

â€Ē Moves event parsing, permission-aware scope handling, coverage limitations, and empty-result receipts into the events adapter.

web/src/components/diagnose/investigationEvidence/adapters/events.ts

helm.tsExtract Helm release adapter +208/-0

Extract Helm release adapter

â€Ē Moves Helm release, owned-resource, operation, relevance, and comparison-limitation projection into a dedicated adapter.

web/src/components/diagnose/investigationEvidence/adapters/helm.ts

issues.tsExtract issue scanning adapter +172/-0

Extract issue scanning adapter

â€Ē Moves issue validation, change correlation, DNS context, visibility limitations, and authoritative empty-result handling into the issues adapter.

web/src/components/diagnose/investigationEvidence/adapters/issues.ts

logs.tsExtract pod and workload log adapters +183/-0

Extract pod and workload log adapters

â€Ē Moves pod and workload log parsing, scope attribution, relevance classification, empty-stream handling, and log observations into one tool-family module.

web/src/components/diagnose/investigationEvidence/adapters/logs.ts

permissions.tsExtract permissions adapter +292/-0

Extract permissions adapter

â€Ē Moves access-check and subject-permission validation, target service-account attribution, binding projection, and coverage limitations into the permissions adapter.

web/src/components/diagnose/investigationEvidence/adapters/permissions.ts

prometheus.tsExtract Prometheus adapters +769/-0

Extract Prometheus adapters

â€Ē Moves alert-rule and PromQL result projection into the Prometheus adapter. This includes selector-based target relevance, established-pod attribution, metrics labels, and query coverage handling.

web/src/components/diagnose/investigationEvidence/adapters/prometheus.ts

resource.tsExtract resource adapters +257/-0

Extract resource adapters

â€Ē Moves resource detail and inventory projection, bundled events and changes, relevance evaluation, and producer limitation handling into the resource adapter.

web/src/components/diagnose/investigationEvidence/adapters/resource.ts

topology.tsExtract topology adapters +207/-0

Extract topology adapters

â€Ē Moves neighborhood and topology payload validation, partiality reporting, relationship observations, and topology summaries into a dedicated adapter.

web/src/components/diagnose/investigationEvidence/adapters/topology.ts

builder.tsCentralize adapter registration and projection orchestration +415/-0

Centralize adapter registration and projection orchestration

â€Ē Defines the adapter registry and retains the main projection workflow, including deferred membership-dependent adapters, source coverage, evidence ordering, and verification retirement.

web/src/components/diagnose/investigationEvidence/builder.ts

identity.tsExtract evidence identity and linking helpers +261/-0

Extract evidence identity and linking helpers

â€Ē Moves resource subject resolution, source and DOM identifiers, evidence-reference validation, root-cause link resolution, and stable hashing into one module.

web/src/components/diagnose/investigationEvidence/identity.ts

index.tsPreserve the investigation evidence public API +55/-0

Preserve the investigation evidence public API

â€Ē Introduces the directory entry point and explicitly re-exports the same 46 public values and types as the former single-file module.

web/src/components/diagnose/investigationEvidence/index.ts

observations.tsExtract projection and observation model +951/-0

Extract projection and observation model

â€Ē Moves ProjectionBuilder, relevance and coverage logic, semantic snapshots, limitation handling, and shared observation adders into the observation layer.

web/src/components/diagnose/investigationEvidence/observations.ts

parse.tsExtract shared payload parsers +467/-0

Extract shared payload parsers

â€Ē Moves JSON coercion and validation helpers for resources, issues, events, logs, topology, network evidence, and metrics into a shared parsing module.

web/src/components/diagnose/investigationEvidence/parse.ts

types.tsExtract evidence wire and projection contracts +512/-0

Extract evidence wire and projection contracts

â€Ē Moves the complete investigation evidence type model, including sources, observations, tool-specific data, coverage, and root-cause link contracts, into a dedicated types module.

web/src/components/diagnose/investigationEvidence/types.ts

@qodo-code-review

qodo-code-review Bot commented Sep 12, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. The module index embeds refactor history ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
index.ts describes the export set as matching the former single file and says the directory split
is invisible. The comment appears above the new barrel exports, so later readers receive PR-specific
migration context rather than a durable invariant alone.
Code

web/src/components/diagnose/investigationEvidence/index.ts[R1-4]

+// The module's public surface — the same 46 names the single file exported.
+// Call sites import "./investigationEvidence", which resolves here, so the
+// split is invisible outside this directory. Anything not listed is internal
+// to the module: exported only so a sibling file can reach it.
Relevance

●●● Strong

Recent precedent accepted removing refactor-history references from comments under this repository
rule.

PR-#1695

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Compliance rule 3036538 prohibits code comments that reference explicit diff or change history.
Lines 1-4 compare the new barrel with the former single file and explain the effect of the split,
directly recording refactor history in source code.

Rule 3036538: Disallow references to tickets or PR history in code comments
web/src/components/diagnose/investigationEvidence/index.ts[1-4]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new module index comment records details about the prior single-file implementation and this refactor's directory split.

## Fix Focus Areas
- web/src/components/diagnose/investigationEvidence/index.ts[1-4]

## Recommended Fix
Remove the change-history language. If documentation is still useful, retain only the durable invariant that this file defines the module's public exports and that unlisted symbols are internal.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 44 rules
✅ Cross-repo context — repo relationships
  Explored: repo: skyhook-dev/radar-hub-web (sha: 2b5189f1)
Review mode: ⚖ïļ Balanced: This is a broad multi-file refactor, but it is explicitly a token-preserving split with unchanged imports, public exports, and passing typecheck/tests, so it does not clearly warrant redundant review passes.

Grey Divider

Tip of the day
ðŸ’Ą Did you know, you can start a comment with 'qodo' or '@qodo' to chat about any finding

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread web/src/components/diagnose/investigationEvidence/index.ts Outdated
@nadaverell

Copy link
Copy Markdown
Contributor Author

Visual regression check

Ran the rendered surface against a real saved investigation, not just unit tests — the refactor reshapes the ESM module graph, which type-checking and Vitest's dev transform don't fully cover.

Method. Built both 0e3dc487 (main) and 9ade124f (this branch) with make build, ran each against the same fixture ai-runs.db, and opened the same saved run — one chosen for coverage: 13 distinct tools, 22 evidence events, exercising 10 of the 14 adapters (diagnose, get_resource, list_resources, get_events, get_changes, get_pod_logs, get_workload_logs, get_neighborhood, query_prometheus, issues).

Result: the rendered Findings pane is byte-identical — 12,505 bytes on both builds.

That covers the Assessment block, the "Evidence coverage is incomplete" limitation banner with both sub-limitations, log cards with selected excerpts and Previous observations, the ConfigMap card with all 13 keys, StatefulSet and Deployment cards with conditions and HPA scaling, three diagnose metric charts, the query_prometheus restarts chart with 4 series and its selector footer, the "28 results about other resources are not shown" line, and all four zero-result receipts.

Also checked:

  • vite build — the production Rollup bundle succeeds. Worth confirming separately because adapters/prometheus.ts imports from investigationMetrics, which imports back; that cycle is type-only, so it is erased at compile time and never exists at runtime.
  • Browser console — 0 errors, 0 warnings on load.

The index comment described the export set by comparing it to the file the
directory replaced, which records a migration rather than an invariant and puts
a count in a comment that any new export invalidates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0125QDX24TNuod7FKLwTDhvW
@nadaverell
nadaverell merged commit c1e9e27 into main Sep 13, 2026
9 checks passed
@nadaverell
nadaverell deleted the refactor/investigation-evidence-directory branch September 13, 2026 10:45
nadaverell added a commit that referenced this pull request Sep 13, 2026
Closes #1732.

`investigationEvidence.test.ts` was **6,617 lines** — the largest file
in `web/src/components/diagnose/` after #1731 split the source it covers
into a directory. It was deliberately left alone by that PR, because the
evidence the move preserved behaviour *was* that the tests did not
change and still passed. That reason expires now.

## What changed

The suite follows the same seams as the module, each file beside the
code it pins:

| | |
|---|---|
| `identity.test.ts` | source ids, subject refs, provenance, root-cause
resolution |
| `projection.test.ts` | the shared model — relevance, proof scope,
merging, retirement, receipts |
| `adapters/*.test.ts` | `diagnose` `helm` `logs` `permissions`
`prometheus` `resource` |
| `evidenceFixtures.ts` | fixtures more than one suite needs |

**The adapter suites split by producer; the model suites deliberately do
not.** Relevance, proof scope and retirement are one behaviour observed
through whichever tool happened to report it — filing those under an
adapter would scatter a single contract across six files and make it
harder to find, not easier. That is why `projection.test.ts` stays the
largest of them.

`evidenceFixtures.ts` follows the existing `reachFixtures.ts`
convention. It holds only what more than one suite uses: the target, the
tool-result builder, the projection helper, and the workload-log payload
shared by the logs and `query_prometheus` suites.

## Verification

- **Token-for-token identical to the original** — 46,137 tokens, zero
difference in either direction, comparing the original file against all
new files with import lines normalised away. No assertion was added,
changed, or dropped.
- **Same test count**: `1147` before and after, measured on this same
commit of `main` rather than quoted from memory. Test files 92 → 99.
- `tsc --noEmit` clean, `eslint` clean, `prettier --check` clean.

## Note

`src/api/client.authRedirect.test.ts` fails intermittently in full-suite
runs on a loaded machine — on `main` as well as here, and in a directory
this PR does not touch. It is #1735, not a regression from this change.

ðŸĪ– Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_0125QDX24TNuod7FKLwTDhvW

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Test-only refactor with no production code changes; behavior is
preserved by moving existing assertions unchanged.
> 
> **Overview**
> **Removes** the 6,617-line monolithic `investigationEvidence.test.ts`
and **re-homes** the same Vitest coverage under
`investigationEvidence/`, aligned with how the implementation is already
split after #1731.
> 
> New layout: **`identity.test.ts`** (source IDs, subject refs,
provenance, root-cause linking), **`projection.test.ts`** (shared
projection rules—relevance, merging, retirement, receipts),
**`adapters/*.test.ts`** per producer (`diagnose`, `helm`, `logs`,
`permissions`, `prometheus`, `resource`), and **`evidenceFixtures.ts`**
for cross-suite helpers (`target`, `tool`, `project`, shared payloads).
Per the PR, assertions are unchanged—this is organizational only; test
count stays at 1147.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
811ba37. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Opus 5 (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.

Split investigationEvidence.ts into a directory, one adapter per tool family

1 participant