{
  "assessments": {
    "abstraction_fitness": {
      "score": 40.7,
      "components": [
        "Abstraction Leverage",
        "Indirection Cost",
        "Interface Honesty"
      ],
      "component_scores": {
        "Abstraction Leverage": 60.6,
        "Indirection Cost": 71.4,
        "Interface Honesty": 63.6
      }
    },
    "cross_module_architecture": 68.3,
    "design_coherence": 40.9,
    "error_consistency": 45.2,
    "test_strategy": 46.4
  },
  "dimension_notes": {
    "cross_module_architecture": {
      "evidence": [
        "All assigned `internal/quality/*.go` files stay within a single package boundary (`package quality`) and only import standard library packages (`time`, `testing`, `strings`), with no cross-package dependency fan-out from this slice.",
        "`pkg/rustdocs/parser_test.go` is isolated to `package rustdocs` and imports only stdlib plus `github.com/PuerkitoBio/goquery`; it does not couple into `internal/quality` types or helpers.",
        "No `init()` functions, package-level mutable singleton wiring, or import-time execution patterns were found in the reviewed files; behavior is test-function scoped and constructor-invoked (`NewParser`, `NewScorer`, `NewNarrativeGenerator`).",
        "Type declarations in `internal/quality/types.go` and `internal/quality/enhanced_types.go` are cohesive data-model definitions within one module boundary rather than cross-module shims or compatibility layers."
      ],
      "impact_scope": "local",
      "fix_scope": "single_edit",
      "confidence": "high",
      "unreported_risk": "This batch covers only five files; architectural hotspots could still exist in non-assigned packages (e.g., runtime wiring or broader dependency graph) outside this evidence window."
    },
    "abstraction_fitness": {
      "evidence": [
        "Language-to-doc behavior is spread across multiple large switches: URL construction in cmd/get.go:78-173, type mapping in cmd/get.go:175-205, and term derivation in cmd/ask.go:205-260+.",
        "External scraper implementations repeat the same transport/change-detection scaffold (config+parser+http client fields, URL check, fetchPage, generateHash, DetectChanges) across multiple files, e.g. internal/scraper/external/godocs.go:17-121, internal/scraper/external/javadocs.go:16-115, internal/scraper/external/nuxtdocs.go:16-120, internal/scraper/external/cloudflaredocs.go:16-105.",
        "Vector store abstraction exposes implementations that are selected by default config but intentionally unimplemented: internal/config/config.go:121-125 defaults to chromem, while internal/vector/store.go:221-243 returns \"chromem store not implemented\" for all operations.",
        "Configuration defaults are duplicated in two representations (typed defaults and hand-written YAML template), increasing drift risk: cmd/init.go:92-149 and internal/config/config.go:104-160."
      ],
      "impact_scope": "subsystem",
      "fix_scope": "architectural_change",
      "confidence": "high",
      "unreported_risk": "",
      "sub_axes": {
        "abstraction_leverage": 62.0,
        "indirection_cost": 71.0,
        "interface_honesty": 60.0
      }
    },
    "test_strategy": {
      "evidence": [
        "`internal/quality/narrative_test.go` validates exact headline/action prose and directly tests internal helper behavior (e.g., `determinePhase`, `generateHeadline`, `classifyDimension`), creating high implementation-coupling.",
        "`internal/quality/scoring_test.go` similarly focuses on exact internal scoring details and string key literals, which makes refactors noisy and discourages safe design changes.",
        "`pkg/rustdocs/parser_test.go` is heavily happy-path: it checks successful parses and minimal field presence but has no malformed-input/error-path cases for parser resilience.",
        "`README.md` marks parts of the CLI as unstable/stubbed, but assigned tests do not provide cross-module contract/integration safety nets for those runtime boundaries."
      ],
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor",
      "confidence": "high",
      "unreported_risk": ""
    },
    "design_coherence": {
      "evidence": [
        "Parallel implementations of the same scorecard pipeline exist in `cmd/devour_scorecard.py` and `cmd/scorecard_generator.py` with near-identical function layouts (`ScorecardData`, `score_color`, `draw_left_panel`, `draw_right_panel`, `generate_scorecard`, `main`) and only minor line-level differences.",
        "Three variants of enhanced generator (`cmd/devour_enhanced.py`, `cmd/devour_enhanced_fixed.py`, `cmd/devour_enhanced_v2.py`) repeat almost the full rendering stack (`draw_header_section`, `draw_enhanced_left_panel`, `draw_enhanced_right_panel`, `draw_trends_section`, `load_enhanced_devour_data`), creating branch-by-copy evolution.",
        "Scraper adapters across providers (`internal/scraper/external/astrodocs.go`, `internal/scraper/external/cloudflaredocs.go`, `internal/scraper/external/reactdocs.go`) duplicate fetch/hash/change-detection and document assembly patterns with provider-specific data glued inline, indicating repeated structural pattern without shared orchestration abstraction.",
        "Within `cmd/devour_lighthouse.py`, `load_font` is defined twice (once near top and again later), showing local design drift and utility ownership ambiguity."
      ],
      "impact_scope": "codebase",
      "fix_scope": "architectural_change",
      "confidence": "high",
      "unreported_risk": ""
    },
    "error_consistency": {
      "evidence": [
        "Raw error passthrough is common in core flows (e.g., `return nil, err` in `internal/search/engine.go:114`, `internal/search/engine.go:122`, `internal/scraper/openapi.go:45`, `internal/scraper/openapi.go:50`) while nearby code wraps with operation context (e.g., `internal/search/engine.go:111`, `internal/scraper/openapi.go:153`).",
        "Failure handling style diverges between aborting, propagating, and suppressing in similar backend paths: `panic(...)` in `internal/quality/plugins/go/plugin.go:363`, warning print-and-continue in `internal/indexer/indexer.go:239`, and plain returns in `cmd/scrape.go:90`/`cmd/get.go:59`.",
        "Some call paths lose caller context at command boundaries (`cmd/scrape.go:90`, `cmd/scrape.go:125`, `cmd/get.go:59`) despite contextual wrapping being used in other command-layer branches (`cmd/scrape.go:131`, `cmd/scrape.go:145`)."
      ],
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor",
      "confidence": "high",
      "unreported_risk": ""
    }
  },
  "findings": [
    {
      "dimension": "abstraction_fitness",
      "identifier": "language_catalog_scattered_switches",
      "summary": "Language routing logic is duplicated across CLI flows instead of one catalog abstraction",
      "related_files": [
        "cmd/get.go",
        "cmd/ask.go"
      ],
      "evidence": [
        "cmd/get.go:78-173 defines a large language switch for URL building; cmd/get.go:175-205 defines a second switch for source type mapping.",
        "cmd/ask.go:205-260+ adds a third language switch for term heuristics, creating three independent sources of truth for one domain model."
      ],
      "suggestion": "Introduce a single `LanguageSpec` registry (aliases, source type, URL builder, optional query-term strategy) in one package and have both `get` and `ask` consume it; keep per-language behavior as data/functions attached to that registry.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "architectural_change"
    },
    {
      "dimension": "abstraction_fitness",
      "identifier": "external_scraper_scaffold_duplication",
      "summary": "External scraper adapters reimplement the same transport/hash lifecycle repeatedly",
      "related_files": [
        "internal/scraper/external/godocs.go",
        "internal/scraper/external/javadocs.go",
        "internal/scraper/external/nuxtdocs.go",
        "internal/scraper/external/cloudflaredocs.go"
      ],
      "evidence": [
        "Each file defines near-identical struct fields (`config`, `parser`, `client`), constructor wiring, URL-required guard, `fetchPage`, `generateHash`, and `DetectChanges` flow (e.g., godocs.go:17-121 and javadocs.go:16-115).",
        "Duplication scales linearly with each new source adapter, increasing edit surface for cross-cutting behavior (timeouts, headers, error mapping)."
      ],
      "suggestion": "Extract a shared `HTTPDocScraperBase` (or composable helper functions) for request execution, status handling, hashing, and change detection; keep each adapter focused on parser invocation and domain-specific document mapping.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "abstraction_fitness",
      "identifier": "default_selects_unimplemented_store",
      "summary": "Store interface contract is dishonest because default backend is not operational",
      "related_files": [
        "internal/vector/store.go",
        "internal/config/config.go"
      ],
      "evidence": [
        "internal/config/config.go:121-125 sets default vector DB type to `chromem`.",
        "internal/vector/store.go:221-243 returns `chromem store not implemented` for all `Store` operations after `NewStore` can select that backend (store.go:63-72)."
      ],
      "suggestion": "Either implement `ChromemStore` before exposing it as default, or switch default to a working backend and gate chromem behind explicit opt-in plus capability check at startup.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "architectural_change"
    },
    {
      "dimension": "abstraction_fitness",
      "identifier": "config_defaults_double_encoded",
      "summary": "Initialization defaults are encoded twice with different abstractions",
      "related_files": [
        "cmd/init.go",
        "internal/config/config.go"
      ],
      "evidence": [
        "cmd/init.go:92-149 hardcodes YAML defaults as a template string.",
        "internal/config/config.go:104-160 hardcodes defaults again in typed structs, requiring synchronized updates across two representations."
      ],
      "suggestion": "Generate init YAML from `config.Default()` via marshal + small post-processing/comments, or maintain a single canonical defaults schema consumed by both loader and init command.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "cross_module_architecture",
      "identifier": "status_contract_string_map_boundary",
      "summary": "Scorecard state uses string keys instead of shared Status type, weakening module contracts.",
      "related_files": [
        "internal/quality/types.go",
        "internal/quality/scoring_test.go",
        "README.md"
      ],
      "evidence": [
        "`internal/quality/types.go` defines `Status` constants but `Scorecard.StatusByType` is `map[string]int`.",
        "`internal/quality/scoring_test.go` asserts `card.StatusByType[\"open\"]` and `card.StatusByType[\"fixed\"]` directly.",
        "README promises resolution-state tracking, but this boundary is not type-safe."
      ],
      "suggestion": "Change `Scorecard.StatusByType` to `map[Status]int` (or a dedicated typed struct), update serialization adapters if needed, and update tests to assert using `StatusOpen`/`StatusFixed` constants.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "test_strategy",
      "identifier": "brittle_private_and_copy_assertions_in_quality_tests",
      "summary": "Quality tests are tightly coupled to private helpers and exact copy text, reducing refactor safety.",
      "related_files": [
        "internal/quality/narrative_test.go",
        "internal/quality/scoring_test.go"
      ],
      "evidence": [
        "`narrative_test.go` directly asserts exact strings for generated headlines/actions and tests helper internals rather than stable external behavior.",
        "`scoring_test.go` anchors on specific internal weighting outputs and literal status strings, which can fail on benign internal redesigns."
      ],
      "suggestion": "Shift to contract-level tests against exported APIs with invariant assertions (phase category, presence of required fields, monotonic score behavior), and keep only a small set of snapshot/copy tests for user-facing text.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "test_strategy",
      "identifier": "rust_parser_missing_negative_and_boundary_cases",
      "summary": "Rust parser tests miss malformed-input and degradation-path coverage.",
      "related_files": [
        "pkg/rustdocs/parser_test.go",
        "README.md"
      ],
      "evidence": [
        "`parser_test.go` cases are successful parses with valid fixture HTML and only basic assertions.",
        "No tests verify behavior for malformed HTML, missing selectors, empty documents, or unsupported result rows.",
        "README positions docs ingestion as core functionality, so parser failure behavior is a critical path."
      ],
      "suggestion": "Add table-driven negative tests for malformed/partial HTML, empty search blocks, and missing headings; assert stable fallback behavior (explicit error or safe zero-value output) for each parser entrypoint.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "single_edit"
    },
    {
      "dimension": "design_coherence",
      "identifier": "scorecard_variant_sprawl",
      "summary": "Scorecard generation is maintained as multiple copy-variants instead of one composable pipeline.",
      "related_files": [
        "cmd/devour_scorecard.py",
        "cmd/scorecard_generator.py",
        "cmd/devour_enhanced.py",
        "cmd/devour_enhanced_fixed.py",
        "cmd/devour_enhanced_v2.py"
      ],
      "evidence": [
        "Both `cmd/devour_scorecard.py` and `cmd/scorecard_generator.py` declare the same major functions and data model in the same order with only minor stylistic deltas.",
        "Enhanced variants repeat the same section render functions and data loading flow, then diverge by ad-hoc edits, increasing change fan-out for any layout or scoring rule update."
      ],
      "suggestion": "Extract a shared rendering core module (palette/fonts/layout primitives + data normalization), keep one canonical CLI entrypoint, and convert variant behavior into explicit theme/feature flags rather than duplicated files.",
      "confidence": "high",
      "impact_scope": "codebase",
      "fix_scope": "architectural_change"
    },
    {
      "dimension": "design_coherence",
      "identifier": "external_scraper_template_duplication",
      "summary": "Provider scrapers repeat the same orchestration flow with per-provider copy/paste adapters.",
      "related_files": [
        "internal/scraper/external/astrodocs.go",
        "internal/scraper/external/cloudflaredocs.go",
        "internal/scraper/external/reactdocs.go",
        "internal/scraper/external/godocs.go",
        "internal/scraper/external/vuedocs.go"
      ],
      "evidence": [
        "Each scraper reimplements nearly identical `Scrape`, `DetectChanges`, `fetchPage`, and `generateHash` scaffolding, then inlines provider-specific conversion methods.",
        "The repeated constructor/client/parser wiring pattern appears across multiple files, indicating systemic pattern duplication rather than isolated differences."
      ],
      "suggestion": "Introduce a shared `DocAdapter` contract and a generic `HTTPDocScraper` that owns fetch/hash/change-detect; keep provider files focused on mapping parsed domain objects to `Document`.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "architectural_change"
    },
    {
      "dimension": "design_coherence",
      "identifier": "utility_ownership_drift_in_lighthouse_script",
      "summary": "Duplicate utility definition in one file shows mixed responsibility boundaries.",
      "related_files": [
        "cmd/devour_lighthouse.py",
        "cmd/devour_enhanced.py"
      ],
      "evidence": [
        "`cmd/devour_lighthouse.py` defines `load_font` twice with effectively the same fallback behavior, creating hidden override risk and unclear source of truth.",
        "Comparable font utility exists in other renderer scripts, reinforcing that shared utility concerns are spread instead of centralized."
      ],
      "suggestion": "Remove the duplicate in `cmd/devour_lighthouse.py` and move font-loading helpers into a shared module imported by all renderer scripts.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "error_consistency",
      "identifier": "mixed_error_wrapping_in_scrape_and_search_paths",
      "summary": "Related scrape/search paths mix raw passthrough and contextual wrapping.",
      "related_files": [
        "internal/search/engine.go",
        "internal/scraper/openapi.go",
        "internal/scraper/localsearch.go",
        "cmd/scrape.go"
      ],
      "evidence": [
        "`internal/search/engine.go` frequently returns raw errors (`:114`, `:117`, `:122`, `:170`) but also uses contextual errors (`:111`, `:230`).",
        "`internal/scraper/openapi.go` propagates raw errors from `readSpec`/`parseOpenAPISpec` (`:45`, `:50`, `:123`, `:141`, `:149`, `:157`, `:164`) while also defining wrapped errors (`:135`, `:153`, `:217`).",
        "`internal/scraper/localsearch.go` returns raw errors from helper boundaries (`:79`, `:164`, `:191`, `:222`) mixed with rich wrapped messages in the same workflow (`:196`, `:203`, `:209`, `:217`)."
      ],
      "suggestion": "Define a package-level rule: public methods must wrap downstream errors with operation context (using `%w`), and helper internals may return raw errors. Apply this consistently to `Rebuild/EnsureIndexed`, `OpenAPIScraper.Scrape/DetectChanges/readSpec`, and `LocalSearchScraper` methods.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "error_consistency",
      "identifier": "inconsistent_failure_channel_panic_vs_error_vs_warning",
      "summary": "Failure signaling varies between panic, error return, and warning-only logging.",
      "related_files": [
        "internal/quality/plugins/go/plugin.go",
        "internal/indexer/indexer.go",
        "cmd/scrape.go"
      ],
      "evidence": [
        "`internal/quality/plugins/go/plugin.go:363` panics on plugin registration failure.",
        "`internal/indexer/indexer.go:239` prints a warning and suppresses deletion errors instead of returning them.",
        "`cmd/scrape.go` is structured around returned errors (`:131`, `:145`, `:207`) and has no panic-based handling, creating inconsistent contracts across subsystems."
      ],
      "suggestion": "Standardize on explicit error returns for recoverable startup/runtime failures; replace plugin `panic` with registration error propagation or controlled process-exit at the command entrypoint, and make indexer deletion behavior explicit (either aggregate and return partial-failure errors or document/encode best-effort mode).",
      "confidence": "high",
      "impact_scope": "codebase",
      "fix_scope": "architectural_change"
    },
    {
      "dimension": "error_consistency",
      "identifier": "command_boundary_context_loss",
      "summary": "CLI command boundaries sometimes return raw errors without command context.",
      "related_files": [
        "cmd/get.go",
        "cmd/scrape.go",
        "internal/config/config.go"
      ],
      "evidence": [
        "`cmd/get.go:59` and `cmd/scrape.go:90`/`:125` return raw errors directly from downstream calls.",
        "Other branches in the same command wrap with explicit context (`cmd/scrape.go:131`, `cmd/scrape.go:145`, `cmd/scrape.go:154`).",
        "Config layer already emits contextual wrapped errors (`internal/config/config.go:177`, `internal/config/config.go:181`), so command-layer inconsistency creates uneven user-facing diagnostics."
      ],
      "suggestion": "At CLI entrypoints, wrap all returned downstream errors with command/action context (e.g., `run get`, `load config`, `scrape source`) and preserve root cause with `%w`; keep user-readable validation errors as direct messages.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "cross_module_architecture",
      "identifier": "init_side_effect_registration_coupling",
      "summary": "Scraper registration depends on import-time side effects and global mutable registry state.",
      "related_files": [
        "cmd/root.go",
        "internal/scraper/external/register.go",
        "internal/scraper/registry_simple.go"
      ],
      "evidence": [
        "Blank import in root command triggers registration implicitly rather than explicit bootstrap wiring.",
        "Registration happens in `init()` and mutates shared global registry."
      ],
      "suggestion": "Replace import-time registration with explicit bootstrap registration (e.g., `RegisterExternalScrapers()` called from startup), and pass registry instances through constructors to remove hidden global coupling.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "architectural_change"
    },
    {
      "dimension": "error_consistency",
      "identifier": "mixed_process_termination_and_error_propagation",
      "summary": "Error handling mixes panic/log.Fatal/os.Exit with returned errors across adjacent layers.",
      "related_files": [
        "cmd/root.go",
        "cmd/scorecard.go",
        "internal/quality/plugins/go/plugin.go",
        "cleanup_unused.go"
      ],
      "evidence": [
        "`Execute()` exits process directly; scorecard helper exits inside utility flow; plugin registration panics on failure.",
        "Most other command paths return wrapped errors, creating inconsistent failure semantics."
      ],
      "suggestion": "Standardize on returning errors from library/command internals and only perform process exit in one top-level entrypoint; replace panic/log.Fatal in shared code with typed/wrapped errors.",
      "confidence": "high",
      "impact_scope": "codebase",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "abstraction_fitness",
      "identifier": "external_scraper_boilerplate_without_shared_base",
      "summary": "External scraper implementations duplicate fetch/hash/error/document plumbing instead of sharing a base abstraction.",
      "related_files": [
        "internal/scraper/external/godocs.go",
        "internal/scraper/external/rustdocs.go",
        "internal/scraper/external/reactdocs.go",
        "internal/scraper/external/nuxtdocs.go",
        "internal/scraper/external/cloudflaredocs.go",
        "internal/scraper/external/types.go"
      ],
      "evidence": [
        "Each scraper repeats `fetchPage`, status code checks, hash generation, and near-identical scrape control flow.",
        "Alias-only types file adds indirection without behavior."
      ],
      "suggestion": "Introduce a shared external-scraper base/helper for HTTP fetch, retries, hashing, and common error mapping; keep only parser-specific extraction and document shaping in each language scraper.",
      "confidence": "high",
      "impact_scope": "subsystem",
      "fix_scope": "architectural_change"
    },
    {
      "dimension": "test_strategy",
      "identifier": "untested_unimplemented_runtime_paths",
      "summary": "Core runtime paths are both under-tested and partially stubbed, leaving high-risk behavior unvalidated.",
      "related_files": [
        "internal/server/server.go",
        "pkg/client/client.go",
        "internal/vector/store.go",
        "internal/search/engine.go",
        "internal/indexer/indexer.go"
      ],
      "evidence": [
        "Server/client/store contain TODO or not-implemented branches without direct tests.",
        "No direct test files exist for several core modules that govern querying, indexing, and serving."
      ],
      "suggestion": "Add table-driven tests for client/server/store/indexer contracts first (error behavior and non-nil results), then implement missing paths behind those tests; prioritize integration tests that exercise scrape->index->query flow.",
      "confidence": "high",
      "impact_scope": "codebase",
      "fix_scope": "multi_file_refactor"
    },
    {
      "dimension": "design_coherence",
      "identifier": "command_files_mix_multiple_responsibilities",
      "summary": "Large CLI command files blend orchestration, domain logic, persistence, and formatting concerns.",
      "related_files": [
        "cmd/quality.go",
        "cmd/scrape.go",
        "cmd/ask.go"
      ],
      "evidence": [
        "`cmd/quality.go` combines scan setup, scoring/status persistence, resolve/fix/review workflows.",
        "`cmd/scrape.go` combines config parsing, source detection/profiling, scrape execution, indexing, and source-state updates.",
        "`cmd/ask.go` includes query derivation, source URL heuristics, ranking, summarization, and output formatting in one command module."
      ],
      "suggestion": "Split command files into focused packages (transport/CLI binding vs service layer vs persistence helpers) and keep Cobra handlers as thin adapters invoking composable use-case functions.",
      "confidence": "high",
      "impact_scope": "module",
      "fix_scope": "multi_file_refactor"
    }
  ],
  "review_quality": {
    "batch_count": 6,
    "dimension_coverage": 0.367,
    "evidence_density": 2.167,
    "high_score_without_risk": 0,
    "finding_pressure": 49.296,
    "dimensions_with_findings": 5
  }
}
