{
  "assessments": {
    "abstraction_fitness": {
      "score": 45.8,
      "components": [
        "Abstraction Leverage",
        "Indirection Cost",
        "Interface Honesty"
      ],
      "component_scores": {
        "Abstraction Leverage": 65.5,
        "Indirection Cost": 62.1,
        "Interface Honesty": 71.5
      }
    },
    "cross_module_architecture": 56.0,
    "design_coherence": 49.3,
    "error_consistency": 44.4,
    "test_strategy": 46.3
  },
  "dimension_notes": {
    "cross_module_architecture": {
      "evidence": [
        "`internal/quality/enhanced_types.go` centralizes many unrelated boundary contracts (scoring metrics, detector transparency, narrative strategy/tools, debt tracking, config) in one package-level type hub.",
        "`internal/quality/types.go` also acts as a second broad contract hub (findings, scan result, scorecard, language/config, extraction structs), overlapping the same ownership area as `enhanced_types.go` instead of separating by subdomain.",
        "`internal/quality/scoring_test.go` consumes these shared package-level contracts directly, which reinforces coupling to a wide internal surface rather than narrower scorer-specific interfaces/types."
      ],
      "impact_scope": "module",
      "fix_scope": "multi_file_refactor",
      "confidence": "high",
      "unreported_risk": ""
    },
    "abstraction_fitness": {
      "evidence": [
        "Four external scrapers (Astro, Docker, Cloudflare, Nuxt) each reimplement the same transport/change-detection skeleton (`fetchPage`, `DetectChanges`, `generateHash`, per-page `Document` assembly) with only parser/model differences.",
        "`cmd/serve.go` RPC path for `devour_scrape` mutates CLI globals (`scrapeFormat`, `scrapeOutput`, `scrapeAllowEmpty`) to call `scrapeOne` from `cmd/scrape.go`, showing a leaky abstraction where server flow depends on CLI stateful wiring.",
        "`vector.Store` advertises interchangeable backends, but `NewStore` can return `ChromemStore` whose methods are all unimplemented runtime errors, so the abstraction surface overstates usable implementations."
      ],
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor",
      "confidence": "high",
      "unreported_risk": "",
      "sub_axes": {
        "abstraction_leverage": 68.0,
        "indirection_cost": 73.0,
        "interface_honesty": 70.0
      }
    },
    "test_strategy": {
      "evidence": [
        "internal/quality/scoring_test.go is heavily unit-focused and validates scorer internals, but does not validate public contract stability between quality types and README-documented behavior.",
        "README.md publishes CLI and JSON-RPC command contracts, but assigned tests do not exercise those published interfaces as compatibility gates.",
        "pkg/rustdocs/parser_test.go and internal/quality/scoring_test.go include brittle assertions (fixed positional result indexing and strict output text matching) that couple tests to implementation/presentation details."
      ],
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor",
      "confidence": "high",
      "unreported_risk": ""
    },
    "design_coherence": {
      "evidence": [
        "Command passthrough wrappers add non-functional indirection in language command modules: `languages/dart/commands.py` defines `_cmd_*_impl` plus thin `cmd_*` forwarders for each command (`cmd_large`, `cmd_complexity`, `cmd_deps`, `cmd_cycles`, `cmd_orphaned`, `cmd_dupes`).",
        "C# deps module mixes parsing, graph construction, CLI arg resolution, and terminal rendering in one file (`languages/csharp/detectors/deps.py` contains core graph builders and also `cmd_deps`/`cmd_cycles` UI handlers).",
        "Status rendering duplicates score-bar construction logic in two loops in `app/commands/status_parts/render.py` (same threshold/color/filled computation blocks around lines ~191-199 and ~223-231), increasing drift risk."
      ],
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor",
      "confidence": "high",
      "unreported_risk": ""
    },
    "error_consistency": {
      "evidence": [
        "RPC paths mix strict and lax decode behavior: `cmd/serve.go` returns decode errors for `devour_scrape`/`devour_ask` but ignores decode errors for `devour_query` and `devour_sync` (`_ = json.Unmarshal(...)`).",
        "`cmd/serve.go` `devour_status` ignores errors from `LoadSourceState` and `EnsureIndexed` then dereferences `idxStats.Documents`, which can panic on nil when indexing fails.",
        "`internal/server/server.go` exposes raw internal errors to clients (`Message: err.Error()`), while parse/invalid-request errors are normalized strings; HTTP transport always emits 400 for any RPC error, collapsing error classes.",
        "`internal/ai/openai.go` does not check HTTP status before JSON decode, unlike scraper fetchers in `internal/scraper/external/*.go` and `internal/scraper/openapi.go` that explicitly gate on status codes.",
        "`internal/search/engine.go` and `internal/scraper/openapi.go` mix wrapped and passthrough errors in adjacent paths, producing uneven context for operators."
      ],
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor",
      "confidence": "high",
      "unreported_risk": ""
    }
  },
  "findings": [
    {
      "dimension": "cross_module_architecture",
      "identifier": "quality_package_contract_hub_coupling",
      "summary": "Quality contracts are concentrated in broad type hubs, creating a coupling hotspot.",
      "related_files": [
        "internal/quality/enhanced_types.go",
        "internal/quality/types.go",
        "internal/quality/scoring_test.go"
      ],
      "evidence": [
        "`enhanced_types.go` and `types.go` both define large cross-cutting models for multiple concerns (analysis narrative, scoring, detector stats, config, extraction metadata) under one package boundary.",
        "The two files blur subdomain ownership, so edits to one concern can force unrelated consumers in the same package to track evolving shared structs.",
        "Scoring tests rely on shared package contracts rather than a tighter scorer-local contract, indicating broad internal API exposure."
      ],
      "suggestion": "Split `internal/quality` contracts by bounded concern (for example: `qualitycore` findings/scan types, `qualityscore` scorecard/metrics, `qualitynarrative` narrative/report DTOs) and keep scorer-facing types in a narrow subpackage/API. Migrate tests to import only scorer-relevant types to reduce transitive coupling.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "abstraction_fitness",
      "identifier": "duplicated_external_scraper_skeleton",
      "summary": "External docs scrapers duplicate the same orchestration instead of sharing one adapter base",
      "related_files": [
        "internal/scraper/external/astrodocs.go",
        "internal/scraper/external/cloudflaredocs.go",
        "internal/scraper/external/dockerdocs.go",
        "internal/scraper/external/nuxtdocs.go"
      ],
      "evidence": [
        "Each scraper defines near-identical `fetchPage`, `DetectChanges`, and `generateHash` logic.",
        "Each `Scrape` method repeats the same flow: validate URL -> fetch HTML -> parser call -> append main doc + sub-doc loop(s).",
        "Differences are mostly parser/model-specific mapping, but transport/error/hash logic is copy-pasted."
      ],
      "suggestion": "Introduce a shared docs-scraper base (or helper pipeline) for HTTP fetch + hashing + standard error handling, and keep only parser-specific mapping in per-provider adapters.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "abstraction_fitness",
      "identifier": "scrape_api_leaks_cli_state",
      "summary": "Server scrape RPC depends on mutable CLI globals to reuse scrape pipeline",
      "related_files": [
        "cmd/serve.go",
        "cmd/scrape.go",
        "cmd/get.go"
      ],
      "evidence": [
        "`handleServeMethod` temporarily rewrites `scrapeFormat`, `scrapeOutput`, and `scrapeAllowEmpty` before calling `scrapeOne`, then restores them.",
        "`scrapeOne` is not a pure service API; it is coupled to CLI-level shared state and output behavior.",
        "`cmd/get.go` also routes through `runScrape`, reinforcing that scraping orchestration is command-centric rather than a reusable application service."
      ],
      "suggestion": "Extract a stateless scrape service function (explicit request struct + options) used by both CLI commands and RPC handlers; keep CLI flags as translation-only at command boundary.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "architectural_change"
    },
    {
      "dimension": "abstraction_fitness",
      "identifier": "vector_store_interface_overpromises",
      "summary": "Vector store abstraction exposes backends that are selectable but not actually implemented",
      "related_files": [
        "internal/vector/store.go",
        "internal/indexer/indexer.go"
      ],
      "evidence": [
        "`NewStore` can return `ChromemStore` when config type is `chromem`.",
        "All `ChromemStore` interface methods currently return `not implemented` errors.",
        "`Indexer` depends only on `vector.Store`, so backend failure appears at runtime after abstraction selection instead of at wiring/validation time."
      ],
      "suggestion": "Make backend capabilities explicit: either remove/guard `chromem` selection until complete, or return an initialization error type from store construction and enforce backend readiness before indexing starts.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "cross_module_architecture",
      "identifier": "docs_runtime_quality_contract_drift",
      "summary": "Quality dimensions/statuses drift between public docs and runtime model contracts.",
      "related_files": [
        "README.md",
        "internal/quality/enhanced_types.go",
        "internal/quality/types.go"
      ],
      "evidence": [
        "README.md frames score interpretation around a specific dimension set and user workflow.",
        "internal/quality/enhanced_types.go includes extra dimension constants not represented in README contract text.",
        "internal/quality/types.go includes StatusIgnored while README resolution examples only expose fixed/wontfix/false_positive paths."
      ],
      "suggestion": "Define a single versioned public quality contract (dimensions + statuses) as source-of-truth, generate README contract tables from it, and add a CI check that fails when exported enums and published docs diverge.",
      "confidence": "medium",
      "impact_scope": "codebase",
      "fix_scope": "architectural_change"
    },
    {
      "dimension": "test_strategy",
      "identifier": "missing_public_contract_compat_tests",
      "summary": "Published CLI/RPC governance contracts are not protected by compatibility tests.",
      "related_files": [
        "README.md",
        "internal/quality/scoring_test.go",
        "internal/quality/types.go"
      ],
      "evidence": [
        "README.md documents stable command and RPC method surfaces for users.",
        "internal/quality/scoring_test.go covers scoring behavior but not end-to-end contract assertions tied to documented surfaces.",
        "No assigned test verifies that externally documented score/status semantics remain compatible with exported model types."
      ],
      "suggestion": "Add contract tests that parse/validate documented command and RPC surface claims against runtime registration/types, and add schema-level golden tests for serialized quality/status payloads.",
      "confidence": "high",
      "impact_scope": "codebase",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "test_strategy",
      "identifier": "brittle_assertions_on_order_and_formatting",
      "summary": "Tests are fragile due to strict ordering and presentation-string coupling.",
      "related_files": [
        "internal/quality/scoring_test.go",
        "pkg/rustdocs/parser_test.go"
      ],
      "evidence": [
        "pkg/rustdocs/parser_test.go asserts semantic expectations through fixed indexes (results[0], results[1], results[2]) rather than identity-based matching.",
        "internal/quality/scoring_test.go asserts many exact output substrings in FormatScorecard, coupling tests to formatting text that can change without behavioral regressions."
      ],
      "suggestion": "Refactor tests to assert semantic invariants (presence by key/kind, normalized structured fields) and reserve strict golden-output checks for explicitly versioned presentation contracts.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "design_coherence",
      "identifier": "passthrough_command_wrappers_add_low_leverage_layers",
      "summary": "Language command modules include thin forwarding wrappers with no behavioral value.",
      "related_files": [
        "desloppify/desloppify/desloppify/languages/dart/commands.py",
        "desloppify/desloppify/desloppify/languages/csharp/commands.py",
        "desloppify/desloppify/desloppify/languages/framework/commands_base.py"
      ],
      "evidence": [
        "`languages/dart/commands.py` creates `_cmd_*_impl` callables and then defines six `cmd_*` functions that only call the corresponding impl.",
        "`languages/csharp/commands.py` also keeps thin wrappers (`cmd_large`, `cmd_complexity`, `cmd_deps`, `cmd_cycles`) that forward directly to other callables.",
        "The framework already provides factory-returned callables; wrapper layers increase symbol count and call depth without adding policy."
      ],
      "suggestion": "Assign factory outputs directly to exported command names (or build the registry directly from generated callables) and keep wrappers only where they add language-specific behavior, validation, or formatting.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "design_coherence",
      "identifier": "csharp_deps_module_blends_core_and_cli_responsibilities",
      "summary": "C# dependency detector file combines analysis engine logic with CLI presentation.",
      "related_files": [
        "desloppify/desloppify/desloppify/languages/csharp/detectors/deps.py",
        "desloppify/desloppify/desloppify/languages/csharp/commands.py",
        "desloppify/desloppify/desloppify/languages/_shared/scaffold_detect_commands.py"
      ],
      "evidence": [
        "`languages/csharp/detectors/deps.py` includes internal graph/parsing functions (`_parse_project_assets_references`, `_build_dep_graph_roslyn`, `build_dep_graph`) and command handlers (`cmd_deps`, `cmd_cycles`) in the same module.",
        "`languages/csharp/commands.py` forwards to `cmd_deps_direct`/`cmd_cycles_deps`, meaning command routing depends on detector-module UI functions instead of a clean detector API.",
        "Other language paths use shared command scaffolding (`languages/_shared/scaffold_detect_commands.py`) that separates command shell concerns from graph/detection logic."
      ],
      "suggestion": "Split C# deps into `detectors/deps_core.py` (graph/parsing only) and command-facing adapters in `commands.py` (or scaffold factories), so detectors expose data and command modules own JSON/table rendering.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "architectural_change"
    },
    {
      "dimension": "design_coherence",
      "identifier": "status_bar_render_logic_duplicated_in_two_paths",
      "summary": "Status renderer repeats identical score-bar construction for mechanical and subjective rows.",
      "related_files": [
        "desloppify/desloppify/desloppify/app/commands/status_parts/render.py",
        "desloppify/desloppify/desloppify/app/output/scorecard_parts/projection.py"
      ],
      "evidence": [
        "`show_dimension_table` in `status_parts/render.py` computes `filled`, score thresholds, and colored bar in one loop for mechanical dimensions and repeats the same logic in a second loop for subjective entries.",
        "Duplicated threshold constants (`>=98`, `>=93`) and color composition appear in both blocks, creating a two-site maintenance point for one rendering policy."
      ],
      "suggestion": "Extract a shared `render_score_bar(score_val, bar_len)` helper (or move the row formatting policy into scorecard projection/output helpers) and reuse it for both loops to keep display semantics consistent.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "single_edit"
    },
    {
      "dimension": "error_consistency",
      "identifier": "rpc_decode_and_response_contract_drift",
      "summary": "RPC methods use inconsistent decode and error-response contracts",
      "related_files": [
        "cmd/serve.go",
        "internal/server/server.go"
      ],
      "evidence": [
        "`cmd/serve.go` ignores JSON decode errors in `devour_query`/`devour_sync` but returns decode errors in `devour_scrape`/`devour_ask`.",
        "`internal/server/server.go` returns raw `err.Error()` in RPC payloads and maps all RPC errors to HTTP 400 in `writeRPC`."
      ],
      "suggestion": "Define one RPC error policy: always validate/decode params the same way, map known classes to stable RPC codes/messages, and map transport HTTP statuses by error class (client input vs internal failure).",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "error_consistency",
      "identifier": "serve_status_swallows_errors_then_dereferences_nil",
      "summary": "Status handler ignores errors and can panic from nil stats",
      "related_files": [
        "cmd/serve.go",
        "internal/search/engine.go"
      ],
      "evidence": [
        "`cmd/serve.go` uses `state, _ := projectstate.LoadSourceState(...)` and `idxStats, _ := engine.EnsureIndexed(ctx)` in `devour_status`.",
        "The same block unconditionally reads `idxStats.Documents` and `idxStats.LastIndexedAt`, which is unsafe if `EnsureIndexed` returned an error."
      ],
      "suggestion": "Handle both errors explicitly in `devour_status`; return structured partial-status with an error field or fail the method uniformly, but do not ignore and dereference.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "single_edit"
    },
    {
      "dimension": "error_consistency",
      "identifier": "openai_http_error_path_not_normalized",
      "summary": "OpenAI client lacks explicit HTTP status error handling",
      "related_files": [
        "internal/ai/openai.go",
        "internal/scraper/openapi.go",
        "internal/scraper/external/astrodocs.go"
      ],
      "evidence": [
        "`internal/ai/openai.go` decodes response bodies directly and only checks `embeddingResp.Error`/`chatResp.Error`; non-2xx responses without expected JSON become generic decode errors.",
        "`internal/scraper/openapi.go` and external scrapers explicitly validate HTTP status before body parsing and return explicit HTTP status errors."
      ],
      "suggestion": "Add explicit non-2xx handling in both OpenAI request paths: include status code, bounded response body excerpt, and endpoint context before JSON decode.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "error_consistency",
      "identifier": "mixed_wrapping_vs_passthrough_in_core_flows",
      "summary": "Adjacent paths alternate between wrapped and raw error returns",
      "related_files": [
        "internal/search/engine.go",
        "internal/scraper/openapi.go",
        "internal/config/config.go"
      ],
      "evidence": [
        "`internal/search/engine.go` frequently returns raw errors (`return nil, err`) from filesystem/index operations.",
        "`internal/scraper/openapi.go` similarly passes through request/file errors raw in `readSpec`, while other branches wrap with operation context.",
        "`internal/config/config.go` demonstrates contextual wrapping style (`read config`, `parse config`), creating drift against less-informative paths."
      ],
      "suggestion": "Adopt a package-level rule: wrap external boundary failures with operation context (read/write/parse/network) and preserve `%w`; apply consistently across search/openapi paths.",
      "confidence": "medium",
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "error_consistency",
      "identifier": "scanner_pipeline_fail_open_is_inconsistent",
      "summary": "Quality scan path mixes fail-open and fail-fast behaviors",
      "related_files": [
        "internal/quality/scanner.go",
        "internal/quality/plugins/go/analyzers/test_coverage.go",
        "internal/quality/plugins/go/analyzers/detectors.go"
      ],
      "evidence": [
        "`internal/quality/scanner.go` logs detector failures and continues, silently reducing coverage of reported findings.",
        "`test_coverage.go` returns `(nil, nil)` for missing `go` tool or missing generated coverage file, while other detector failures return explicit errors.",
        "`detectors.go` has parse/count paths that silently `continue` on per-file errors, further mixing behavior."
      ],
      "suggestion": "Standardize detector error semantics with typed outcomes (hard error, soft-skip with reason, per-file skip count) and surface these in scan results so degraded scans are explicit.",
      "confidence": "medium",
      "impact_scope": "subsystem",
      "fix_scope": "architectural_change"
    },
    {
      "dimension": "cross_module_architecture",
      "identifier": "rpc_cli_global_state_coupling",
      "summary": "RPC handlers mutate CLI global flags to execute workflows across command boundaries.",
      "related_files": [
        "cmd/serve.go",
        "cmd/scrape.go",
        "cmd/sync.go"
      ],
      "evidence": [
        "cmd/serve.go sets scrapeFormat/scrapeOutput/scrapeAllowEmpty before calling scrapeOne and restores afterward.",
        "cmd/serve.go sets syncForce/syncRebuild/syncSource before calling runSync and restores afterward."
      ],
      "suggestion": "Extract scrape/sync/query use-cases into stateless service functions (input structs + return structs), call them from both Cobra commands and RPC handlers, and remove shared mutable command globals from runtime execution paths.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "architectural_change"
    },
    {
      "dimension": "error_consistency",
      "identifier": "silent_error_drops_in_rpc_paths",
      "summary": "Several RPC paths ignore parse/index/state errors, producing inconsistent failure contracts.",
      "related_files": [
        "cmd/serve.go",
        "internal/search/engine.go",
        "internal/projectstate/state.go"
      ],
      "evidence": [
        "cmd/serve.go ignores JSON unmarshal errors for devour_query/devour_sync (`_ = json.Unmarshal(...)`).",
        "cmd/serve.go ignores errors from LoadSourceState and EnsureIndexed, yet reads fields from returned values.",
        "Other command paths generally propagate wrapped errors with `%w`."
      ],
      "suggestion": "Treat request decode, source-state load, and index-check failures uniformly: return explicit wrapped errors from each branch and avoid underscore-discarded errors in RPC handlers.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "single_edit"
    },
    {
      "dimension": "abstraction_fitness",
      "identifier": "external_scraper_transport_duplication",
      "summary": "External scrapers duplicate the same HTTP fetch/hash/change-detection boilerplate.",
      "related_files": [
        "internal/scraper/external/reactdocs.go",
        "internal/scraper/external/tsdocs.go",
        "internal/scraper/external/godocs.go",
        "internal/scraper/external/rustdocs.go",
        "internal/scraper/external/cloudflaredocs.go"
      ],
      "evidence": [
        "Each scraper reimplements equivalent `fetchPage` with identical request/header/response-body flow.",
        "Each scraper reimplements nearly identical `DetectChanges` and `generateHash` logic."
      ],
      "suggestion": "Introduce a shared base helper (e.g., `fetchPage`, `contentHash`, `detectChanges`) in internal/scraper/external/types.go or a dedicated internal transport module, then keep per-scraper files focused on parser-to-document mapping.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "test_strategy",
      "identifier": "missing_tests_on_orchestration_and_adapters",
      "summary": "Critical orchestration commands and most external adapters have no direct tests.",
      "related_files": [
        "cmd/serve.go",
        "cmd/sync.go",
        "cmd/query.go",
        "internal/scraper/external/godocs.go",
        "internal/scraper/external/reactdocs.go"
      ],
      "evidence": [
        "No *_test.go peers for cmd/serve.go, cmd/sync.go, cmd/query.go despite high-risk orchestration behavior.",
        "Only one external scraper has direct tests while many adapter implementations contain custom mapping and error-path logic."
      ],
      "suggestion": "Add table-driven tests for RPC method branches and error propagation in cmd/serve.go, plus httptest-based adapter contract tests covering fetch failures, parser errors, and document mapping for each external scraper family.",
      "confidence": "high",
      "impact_scope": "codebase",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "design_coherence",
      "identifier": "serve_handler_multi_responsibility_switch",
      "summary": "Single handler function mixes transport, parsing, orchestration, and mutable state management.",
      "related_files": [
        "cmd/serve.go",
        "cmd/scrape.go",
        "cmd/sync.go",
        "cmd/query.go"
      ],
      "evidence": [
        "handleServeMethod contains method routing plus per-method request schemas, domain calls, and response shaping in one body.",
        "The function coordinates temporary mutation of command globals to reuse CLI code paths."
      ],
      "suggestion": "Split each RPC method into dedicated handler functions (e.g., handleQueryRPC, handleScrapeRPC, handleSyncRPC) that depend on explicit service interfaces instead of command globals.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "multi_file_refactor"
    }
  ],
  "review_quality": {
    "batch_count": 6,
    "dimension_coverage": 0.367,
    "evidence_density": 1.667,
    "high_score_without_risk": 0,
    "finding_pressure": 48.928,
    "dimensions_with_findings": 5
  }
}
