refactor: complexity wave 2 — zero functions over CC 50, ceiling enforced - #185
Merged
Conversation
… helpers collectModelBindings (CC 48) folded four independently-sourced binding families (kit.json routes+escalations, kit.json integrations, agentic-qe config, ruflo candidates) into one function. Extract one helper per family (routeBindings/escalationBindings, integrationBindings, aqeBindings/aqeFallbackBindings/aqeAgentOverrideBindings, rufloBindings) so each stays independently testable; collectModelBindings now just concatenates their outputs in the original order (CC 6). No behavior change: binding shape, ids, ordering, and diagnostics codes are byte-identical to before.
Splits the ADR-0031 §2 tiered-conformance orchestrator (CC 60) into small, independently testable helpers: resolveConsentFile/resolveWorkerCwd for setup, resolveBaseDir/resolveEffectiveManifest/resolveEffectiveTimeoutMs/ computeActivityRoutingResult for context derivation, a shared runActivityRoutingDependentTier gate (replacing two copy-pasted skip blocks for aqe-provider and primary-eligible), buildTierRunners for the per-tier closures, persistTierResults for the ADR-0031 §1 recording step, and cleanupConformanceRun for the finally-block teardown. The orchestrator itself now just resolves context and loops CONFORMANCE_TIERS (the fixed graduation order) through the runner map — CC 60 -> under 25, with 0 new lint warnings and identical tier-push order/behavior (all 28 conformance-tiers.test.mjs cases pass unchanged).
discoverOllamaApi (CC 65) mixed tags/ps response decoding, per-row validation, /api/show detail resolution, variant/capability field mapping, and diagnostic/completion accounting into one function. Extract parseOllamaTags/parseOllamaRuntime (response decoding), resolveOllamaShow (bounded per-model detail lookup), ollamaFamilies/ ollamaAdvertisedCapabilities/ollamaVariant/ollamaCapabilities (field mapping, using new whenPresent/whenTruthy conditional-field helpers to keep the variant builder's CC at 11 instead of the boundary 25), ollamaModelFromRow/ollamaModelsFromRows (per-row orchestration), and markOllamaRuntimeEvidence (evidence-class patching). discoverOllamaApi itself now only wires these together (CC 65 -> 13). Diagnostic ordering, evidence classes, and the exact variant/capability shapes are preserved byte-for-byte (existing fixtures assert this).
…named helpers collectProjects (CC 59 -> resolveProjectCatalog/measureSelectedProjects/ buildProjectsSection, orchestrator now trivial) and collectCatalog (CC 44 -> pluginSurfaceSpecs/readCatalogSurfaces/trackIncompleteness/sortCatalogItems/ tallyCatalogCounts/tallyCatalogPerHost) are mechanical extractions: identical branching, identical payload shapes, no behavior change.
…elow CC 25 decodeCodexRecord (CC 26) now dispatches through a per-record-type table (decodeSessionMeta/decodeTurnContext/decodeEventMsg/decodeResponseItem) instead of one branch-heavy function. decodeClaudeRecord (CC 29) splits into splitClaudeBlocks/claudeRole/claudeUsage. Decode-layer behavior is unchanged; realigns the two USAGE-SCORECARD-METRICS.md citations that moved.
…71→21)
applyOpencode hand-instantiated the same prune-stale-owned →
collision-detect → adopt/own-with-{prior,written} algorithm three times
(mcp block, skills.paths block, permission block). Extract it into one
generic reconcileOwnedMap(obj, desiredEntries, prevRecords, opts), a
reconcileFamilyPermissions wrapper that preserves the permission block's
family-atomicity subtleties (a collision on either claude-flow_*/claude_flow_*
spelling blocks both), and a reconcileSkillPaths helper for the
collision-free skills.paths case. Also extract applyOpencodeSummary for the
trailing result-notes assembly. Mechanical, behavior-preserving — every
prior/written/collision/prune decision point is reproduced exactly,
including the mcp-vs-permission prune-label asymmetry (mcp prunes push the
bare name; permission prunes push "permission.<key>").
No behavior change; tests/kit/opencode.test.mjs (46/46) and the full suite
pass unchanged.
admitOne (CC 36) becomes a sequence of small pipeline steps
(validateEntryShape, readEntryManifest, checkContractPin,
parseEntryManifest, checkHostIdentity, computeEntryIntegrity,
checkConsent), each preserving its exact original {admitted:false,
reason, detail} failure shape and short-circuit order — fail-closed
admission semantics unchanged.
bootstrapHostAdapters (CC 55, plus a max-depth-6 violation at the
lifecycle-registration block) becomes an orchestrator over five
extracted stages: resetAqeProviderBridge, resolveConsentStore,
buildGrantsByName, registerAqeProviderCandidates,
registerExecutionCandidates, registerLifecycleCandidates. Each stage
keeps its own guarded/non-fatal posture and warning strings verbatim;
extracting the lifecycle stage into its own function also resolves the
max-depth violation (nesting resets per function).
CC: admitOne 36 -> under 25, bootstrapHostAdapters 55 -> under 25.
0 new lint warnings; all 175 admission/conformance/execution/aqe-
provider/host-adapters-cli/external-lifecycle tests pass unchanged,
plus the full suite and typecheck.
…Opencode/removeArtifacts Continue the CC>25 cleanup in opencode.mjs's config-wiring section: - normalizeManaged (32->~4): extract normalizeOwnedRecordMap (the mcp and permissions loops were literally duplicated) and normalizeManagedArtifacts. - opencodeConverged (28->~8): extract mcpConvergenceReasons, staleOwnedReasons (shared by the mcp and permission stale checks, another duplication), skillPathReasons, permissionAllowReasons. - undoOpencode (26->16): extract restoreSkillPaths and collapsePermissionScalar. - removeArtifacts (26->~5): extract removeIfReceiptOwned (the exists+hash-match+remove pattern was repeated 4x) and removeGeneratedAgents. Mechanical, behavior-preserving — same reasons/removal order, same JSDoc positioning (kept immediately above each exported function so tsc's checkJs keeps inferring param types from it). All new/extracted functions land under CC 25 (mostly under 15). tests/kit/opencode.test.mjs (46/46) and the full suite pass unchanged; typecheck clean.
…C 25 adaptCodexRecord (CC 48) and adaptCodexLedger (CC 65) split their event-base/actor/source-fields construction and per-decoded-type dispatch into small named functions; adaptClaudeRecord (CC 47) does the same for its base/containment/tool-event construction. No behavior change — same live event shapes, same field values, same array ordering.
The withDb() callback (CC 48) splits into buildPartsIndex, recordUserMessage/recordAssistantMessage (with recordAssistantUsage/ recordAssistantTurn), processMessageRow, collectScanToolCounts, and initSessionRecord. Provider/model bookkeeping moves inline per assistant message instead of a deferred lastProviderId — same final value, since only a truthy providerID ever overwrote it. No output change.
Splits the two over-CC-25 async arrow functions in the deja-vu companion lifecycle adapter into small, independently testable helpers: detect() (CC 45) -> resolveDoctorState, buildHostFact (per-host target fact loop body), buildInstallFacts (install sub-object + repair-capable check), computeDetectError. plan() (CC 49) -> resolveLatestVersion (registry read + bounded fallback ladder), planPackageOperation (the install/upgrade decision, still first-match-wins via early return, same priority order as the original if/else-if chain), planHostOperations (per-host target loop body, `continue` mapped to `return`), needsIndexOperation. Every extracted function reproduces its slice of the original logic verbatim — same warning strings, same error strings, same operation shapes, same short-circuit order. CC: detect 45 -> under 25, plan 49 -> under 25, 0 new lint warnings. All 46 deja-vu-specific tests plus the footprint/companion-registry/integration-config/setup/sync/verify suites and the full test/typecheck/lint gates pass unchanged.
read-model.mjs had 12 functions over CC 10 (up to CC 38), all in the owner-visible/public-catalog privacy projection that /api/models and the Dashboard depend on. Split each by concern, preserving output shape and privacy behavior exactly (existing fixtures assert both): - ownerVisibleCapabilities (16) -> booleanCapabilityFields/ limitCapabilityFields/capabilityModalities/directionalCapabilityFields - ownerVisiblePricing (15) -> evidencedPricing/publishedPricing - trustedModelLinks's flatMap callback (13) -> trustedModelLink - publicModelIdentity (19) -> codexPublicIdentity/claudePublicIdentity (+ isOfficialClaudeIdentity)/catalogPublicIdentity, tried in order - sanitizeModel (27) -> sanitizedIdentity/sanitizedAliases/ sanitizedVariantBlock/sanitizedLifecycle/sanitizedEdges/ sanitizedDimensions - consumerLabel (11) -> routeConsumerLabel/ordinalConsumerLabel/ agentOverrideConsumerLabel, tried in order like publicModelIdentity - sanitizeBinding (28) -> bindingPublishedPricing - observedWindow (38) -> matchModelForSession/newObservedGroupRow/ observedGroupRow/accumulateSession/foldSessionIntoGroups/ sortObservedGroups - changeDetail (26) -> a CHANGE_DETAIL_BY_KIND lookup table (mirrors the existing CHANGE_LABELS table just above it) plus changeDetailLifecycle/Visibility/Capability/Variant - sanitizeChange (13) -> changeRawSubject - the attention-sanitizing arrow in createDashboardModelReadModel (30) -> sanitizeAttentionItem dispatching to sanitizedSourceAttention/ sanitizedMigrationAttention (+ affectedRoutesFor/migrationAction)/ sanitizedConsumerAttention/sanitizedAliasAttention - createDashboardModelPayload (13) -> timestampIso/ dashboardHistoryEntries/dashboardComparisonBlock Every function in the file is now under CC 25 (worst case 21). No behavior change: identity/pricing precedence, evidence classes, diagnostic ordering, and the exact owner-visible/public-catalog field shapes are unchanged (model-dashboard-read-model.test.mjs and model-inventory-impact.test.mjs assert this).
Finish the eslint complexity:25 gate cleanup for opencode.mjs's remaining named targets: - opencodeStack (44->20): extract ownershipMarkersSnapshot (the before/after JSON.stringify([...]) pattern was repeated 3x), blockedArtifactResults (the adoptionBlocked skip-shape), and deployOpencodeArtifacts (the plugin/gateway/agents/skill orchestration body). - syncAgents (50->15): extract deployDesiredAgents, retireStaleGeneratedAgents, writeAgentStamp. - agentsStatus (46->16): extract readAgentStamp, countGeneratedAgentFiles, onDiskAgentFiles, agentContentDiverged, expectedAgentStampText, agentReceiptDivergence, and agentsStatusResult for the final shape. - Also dedupe the receipts-tolerant-map coercion (identical in syncAgents and agentsStatus) into a shared `asReceiptMap` helper. Every function in the file is now under eslint's complexity:25 gate (worst remaining is 21). Mechanical, behavior-preserving — same write/removal order, same stamp/receipt semantics. tests/kit/opencode.test.mjs (46/46) and the full suite pass unchanged; typecheck clean; lint has 0 complexity warnings left for this file (only the pending max-lines warning, addressed by the next commit's file split).
…orage CC storage.mjs was 1,482 lines (max-lines warning) with collectStorage at CC 35. Split along the tree-vs-reclaim seam the file's own section comments already drew: - storage.mjs (629 lines): the category/host/project/session tree collector (collectStorage, defaultStorageRoots, growth/session-label helpers). - storage-reclaim.mjs (251 lines): collectReclaimables orchestration, the safety-tier vocabulary, candidate()/summarizeReclaimables, npx and adopted-consumer-figure plumbing. - storage-reclaim-detectors.mjs (677 lines): the per-accumulation-pattern detectors (superseded snapshots, regenerable caches, browser revisions, runtime versions, orphaned transcripts, orphaned worktrees). storage.mjs re-exports every one of the 23 original named/const exports so both existing importers (index.mjs, tests/kit/footprint-collectors.test.mjs, including its `Object.keys(storageModule)` advisory-only sweep) keep working unchanged. Also extracted walkStorageRoots/fillMissingStorageCategories out of collectStorage (CC 35 -> under 25; the walk loop and its onFile handler are byte-for-byte unchanged, just relocated). docs/ddd/machine-footprint.md's read-surface table is updated to name the file that now actually performs the gitdir read.
…st.mjs Both are sequential fail-closed validators (Adapter Contract Dossier structural cap layer) — same rule-list shape as the rest of the validator family, decomposed into small, order-preserving steps rather than a validator registry (each check depends on state computed by the previous one, e.g. host -> capabilities -> execution coupling, so a literal (input)->error|null rule list doesn't fit; a sequential pipeline of named steps does, without changing throw order). validateAqe (CC 49) -> checkAqeProviderEligibility, validateAqeProviderHook, validateAqeProviderEnvPolicy, validateAqeProviderModels, validateAqeProviderLimits, buildAqeProviderRecord. Throw-precedence preserved exactly (billingMode check stays inline, between the env-policy and models steps, matching the original body's order). validateAdapterManifest (CC 33) -> validateManifestIdentity, assertHostAllowedKeys, parseHostAdapter, enforceHostCapabilityCaps, checkExecutionRoutable. Every extracted function reproduces its slice of the original logic verbatim — same ManifestRejected reasons, same messages, same order. CC: validateAqe 49 -> under 25, validateAdapterManifest 33 -> under 25, 0 new lint warnings. All 144 adapter-manifest/trust-manifest/aqe- provider/admission/conformance tests pass unchanged, plus the full suite, typecheck, and lint gates.
…under 25) readQueuedManifests() and matchSignatures() pull the manifest-dependency fold and the signature-matching loop out of detectStack's own scope, each keeping its exact branching. detectStack's remaining complexity is dominated by its options-object defaults, not control flow.
…y 62->17, models 59->11, host status 38->4)
…nctions discovery/opencode.mjs had 11 functions over CC 10 (max 23, plus a nested arrow at 18), covering catalog-document validation, capability normalization, the bounded JSON-block scanner, verbose-listing metadata parsing, streamed models.dev fetch, output-line parsing, config selector resolution, and the two collectors. Split each by concern: - catalogDocument (17) -> parseCatalogValue/catalogModelCount/ countValidCatalogModels/isValidCatalogProvider/isValidCatalogModel - safeCapabilities (11) -> safeCapabilityBooleans/Modalities/ Interleaved/Limits - jsonBlock (12) -> advanceJsonScan (one mutable-state char-scan step) - metadataFor (21) -> parseModelMetadataJson/validatedSelectorParts/ metadataStatus/metadataFamily/metadataCatalogBlock/ metadataAvailableVariants - fetchCatalog (14) -> readBoundedCatalogText/readBoundedCatalogStream - outputRows (11) -> nextNonBlankLineIndex/readRowMetadata - selection (13) -> referenceString/splitSelectorVariant - configuredRefs (21) -> parseOpenCodeConfig/modelRefsFromGroup/ dedupeSelections - discoverOpenCode (12) + its models.map arrow (18) -> openCodeModelFromId/openCodeModelStates/openCodeModelVariant/ openCodeSourceUnsupportedResult - collectOpenCode (23) -> unavailableOpenCodeResult/ resolveOpenCodeConfigRaw/resolveModelsDevCatalogRaw/ refreshOpenCodeOnline Every function is now under CC 15 (worst case: collectOpenCode 15). No behavior change: selector/variant parsing, diagnostic codes and messages, catalog-proof precedence, and CLI argv are unchanged (model-discovery-opencode-ollama.test.mjs and the collect/contracts/ route-feed suites assert this).
…checks validateRegistries (CC 49) becomes a validator-registry: four independent `(registries) => Error[]` rules — checkDuplicateIds, checkHostInvariants (configProjection/observability referential integrity plus the canDriveSession -> canBePrimary/canRouteActivities dependency, kept as one function so a host violating more than one invariant still reports them in the original interleaved per-host order), checkProviderInvariants, checkModelDiscoveryOwners — run in the same order the original body evaluated its blocks, via flatMap. validateRegistries itself drops to a single flatMap call. Note: the check-function array is built fresh inside validateRegistries rather than hoisted to a module-level const, because validateRegistries runs once at MODULE LOAD time (registries.mjs's own construction-time self-check) before a later top-level const's temporal dead zone would have cleared — confirmed by a direct import smoke test in addition to the test suite. CC 49 -> 1 (a single flatMap), 0 new lint warnings. All 24 adapter-registries tests pass unchanged, including the exact-array-order deepEqual assertions and the construction-time import check, plus the full suite, typecheck, and lint gates.
…functions renderSysStorage (CC 88), renderSysRuntime (CC 39), renderSysCatalog (CC 29), and renderSysProjects (CC 33) each mixed several independent DOM regions (or, for renderSysProjects, eligibility filtering + row building + table assembly) in one function. Split each into one function per region/concern, all under CC 25, with no behavior change -- every region keeps its original guard and logic verbatim, just relocated. This also resolves the file's one max-depth violation (renderSysStorage's per-host split block nested 6 deep; flattening its wrapper ifs to early returns brings it to 4). Incidental: dropped a dead `_diskBar` variable in the old renderSysProjects row loop that was computed but never read (no rendered-output change). pnpm test (356/356) and pnpm run test:ui (331/331) both pass unchanged from baseline; typecheck, lint, and build are clean.
…>19, storage.mjs split 3-way)
migrateIntegrationConfig (CC 45) becomes a sequence of extracted helpers, in the same order as the original body: hasUnmigratableIntegrationsShape / hasUnmigratableProvidersShape — the seven shape-guard checks all produced the identical immutable(out) bail-out, so they collapse into two boolean predicates rather than a rule list (nothing distinct to report — just "still safely migratable or not"). resolveMigratedHosts, deriveInferredBindings, resolveMigratedTools, applyLegacyCodexOwnership, applyLegacyOpenCodeOwnership, pruneLegacyProviderKeys — each reproduces one independent slice of the original computation verbatim (same field names, same merge/ownership semantics, same legacy-key pruning list). CC 45 -> under 25, 0 new lint warnings. All 24 integration-config tests pass unchanged, plus the full suite, typecheck, and lint gates.
renderHistory mixed four independent data-shaping computations (patterns/deltas, pattern-store day-bucketing, graph series, curve values -- each dense with ternaries/&&/|| chains, which is what drove the cyclomatic count) with five independent sparkline DOM renders. Split into historySeriesAndDeltas/historyPatternStoreSeries/historyGraphSeries/ historyCurveValues (pure data) and renderPatternsSpark/ renderPatternStoreSpark/renderGraphSpark/renderDeltaSpark/renderCurveSpark (DOM writes), each under CC 25. renderHistory itself is now a thin orchestrator; all logic and DOM output is otherwise byte-identical to the original. pnpm test (356/356) and pnpm run test:ui (331/331) both pass unchanged from baseline; typecheck, lint, and build are clean.
…the gate
Wave-2 audit item 5 ("everything else over CC 25"). Three functions:
- dashboard-query.mjs parseQuery (27) -> parseNonInventoryQuery (the
full/summary branch) and parseInventoryQuery (+ queryInteger/
inventorySortSelection/inventoryEvidenceFilter), dispatched on `view`
- discovery/codex.mjs discoverCodex (41) -> codexUnsupportedSchemaResult/
codexModelsFromCache/codexModelFromCacheEntry/codexReasoningEfforts/
codexModelStates/codexConfiguredFallbackModel/codexOverallStatus (the
latter also de-duplicates a repeated `complete ? (stale ? ...) : ...`)
- discovery/index.mjs modelRecord (40, the shared record builder every
discovery/*.mjs module calls) -> resolveEvidenceClass/
normalizedModelStates/makeEvidenceFor/stateDimensions/
resolveReplacement/normalizedModelAliases/lifecycleEvidenceRefs/
normalizedModelPricing/modelEdges
modelRecord needed extra care: its evidenceFor() calls append to a
shared `evidence` array in a specific sequence (dimensions, then
aliases, then lifecycle, then digest, then variant fields, then
capability fields, then pricing), and at least one caller (Codex's
discoverCodex) indexes evidence[0] positionally. The extracted helpers
take evidenceFor as a parameter and are invoked in the same lexical
order as the original inline code, so the call sequence — and every
evidence id (a hash of source/host/provider/modelId/scope/field/
capturedAt) — is unchanged. A comment on modelRecord documents this.
All three are now under CC 25 (parseQuery ~5, discoverCodex 20,
modelRecord 23). Full suite (2284 tests, all model-inventory discovery
and contract suites) passes unchanged.
runAdapterHook (CC 31) becomes a sequence of extracted lifecycle-phase helpers, in the same order as the original body: assertRunAdapterHookArgs (sync arg-shape guards), verifyHookIntegrityOrFailure (the pre-spawn content re-check), spawnAdapterChild (isolates the synchronous ENOENT-class spawn throw), wireAdapterStdin (stdin write + EPIPE/already-closed-stream tolerance), awaitAdapterClose (the close/error race setup, returning a getSpawnError() accessor over the same shared-mutable-then-read-after-await semantics the original body used), buildAdapterTimeoutResult and buildAdapterCloseResult (the two terminal result shapes). A shared EMPTY_HOOK_RESULT constant replaces three copies of the identical empty-output literal the pre-refactor code repeated at each early-failure return site. Every extracted function reproduces its slice of the original process- supervision logic verbatim — same kill-group behavior, same truncation/ capture semantics, same detail strings. CC 31 -> under 25, 0 new lint warnings. All 26 adapter-hook-runner tests (timeout-and-kill, ENOENT, stdin EPIPE, cwd anchoring, output truncation/capping) pass unchanged, plus 133 admission/conformance/execution/integrity/lifecycle tests, the full suite, typecheck, and lint gates.
…ission 55, validators 45-49 all <25)
…h.mjs)
Last two functions over CC 25 in the model-inventory family:
- observed.mjs collectObservedModels (29) -> unavailableObservedResult/
observedModelsMap/foldSessionModels/observedModelRecord/
finalizeObservedResult/observedResultStatus. The evidence[0] mutation
(tagging providerProvenance) still targets the same entry: modelRecord's
dimensions-first evidence ordering (fixed in the prior commit) means
the "observed" dimension's evidence is still evidence[0] for this
states shape ({observed, entitled, policyAllowed, routable}: true,
configured/effective/discoverable/recommended default 'unknown').
- refresh.mjs composeModelSnapshot (26) -> discoveryProfileFingerprints/
snapshotSources/discoveredAndObservedModels/snapshotDiagnosticCodes/
modelSnapshotId
Every named function in observed.mjs is now under CC 10; refresh.mjs's
composeModelSnapshot is 19 (mergeModels/applyBindings/
refreshModelDiscovery were already under 25 and untouched — out of
this item's scope). Full suite (2284 tests) and model-inventory-collect
suite pass unchanged; lint 0 errors (91 warnings machine-wide, down
from the 102 baseline before this track's work).
…r 1000
opencode.mjs had grown to 1,747 lines and tripped the eslint max-lines:1000
warning gate. Split along the file's own pre-existing section markers,
respecting one-directional dependencies to avoid an import cycle:
- opencode-receipts.mjs (27 lines): shared primitives (deepEqual,
contentHash, hasReceiptValue, receiptMatches, asReceiptMap) — previously
defined once and used across every section; now imported by all four
implementation modules below instead of only reachable from one file.
- opencode-core.mjs (693 lines): opencode.json config-wiring —
applyOpencode/undoOpencode, opencodeConverged/opencodeMcpStatus, the
receipt ledger, mcp entry resolution.
- opencode-agents.mjs (489 lines): ruflo catalog resolution (catalogSource,
skillPathsFor) + the Claude Code agent .md -> OpenCode subagent .md
conversion/sync/status pipeline.
- opencode-artifacts.mjs (282 lines): the plugin (lifecycle bridge + lazy
rUv gateway) and platform-skill deployment/status, plus the shared
teardown (removeArtifacts).
- opencode-lifecycle.mjs (311 lines): the shared enable/retire stack
composition (opencodeStack/retireOpencode) and the ADR-0016 lifecycle
adapter — depends on opencode-core.mjs, opencode-agents.mjs, and
opencode-artifacts.mjs, never the other way around.
- opencode.mjs (74 lines): now a pure re-export barrel. Every name any
consumer imports from './opencode.mjs' (grepped across src/, bin/,
tests/: providers.mjs, adapters/lifecycle-registry.mjs, commands/setup.mjs,
commands/status/host-detail.mjs, commands/x/host.mjs, and the two owned
test files) is re-exported unchanged — no import path elsewhere in the
repo needed to change. A handful of previously-private helpers
(normalizeManaged, opencodeOwnership, mutableOpencodeOwnership,
specialistDispatcherState, gatewayAgentCatalog, STAMP_FILE,
parseFrontmatter, SPECIALIST_AGENT) gained `export` only for this
cross-module wiring; they are deliberately NOT re-exported through the
opencode.mjs barrel, so the public surface is unchanged (still exactly
the 33 names the original file exported).
Mechanical, behavior-preserving — no logic changed, only file boundaries.
All CC numbers are identical to before the split (verified). typecheck
clean (added two `@typedef {import(...)}` cross-file type references for
CatalogSource); lint 0 errors; tests/kit/opencode.test.mjs (46/46),
tests/kit/lifecycle-registry.test.mjs (31/31), and `pnpm run check` (full
typecheck+lint+markdownlint+build+test) all pass.
… key mliRouteValue(binding,"used") concatenated binding.activity and binding.role with a literal NUL control byte (0x00) instead of a space character -- pre-existing in the source, not introduced by this branch. This value feeds mliRouteCompare's string comparison, which backs the real, user-clickable "Used for" column sort in the Model Routes table (data-mli-route-sort="used" in page.mjs), so it was a live sort-key defect rather than dead code. Replaced the NUL byte with a plain space. pnpm test (356/356) and pnpm run test:ui (331/331) both pass; typecheck, lint, and build are clean.
… 48->6, family 0 over CC 25)
…36 -> 12/12)
Both functions' complexity came almost entirely from optional-chain/nullish
operators, not obvious branching, so extraction targets the shape rather
than the individual conditions:
- terminalResult (30->12): extract classifyObservation (the
observation-type if/else-if chain mapping to {status, exitCategory,
failure}) and terminalResultUsage (the tokens/cost ternary).
- launch (36->12): extract spawnServerChild (invocation resolution + spawn +
port acquisition, with its own try/catch cleanup) and startSession
(health-check + session open + event-stream subscribe + prompt post),
both as closures inside createOpenCodeExecutionAdapter so they keep
direct access to its injected fetchFn/spawnFn/resolveFn/etc. without
threading ten parameters through.
Mechanical, behavior-preserving — same request order, same try/catch
boundaries and cleanup-on-abort behavior. tests/kit/opencode-execution.test.mjs
(22/22) and the full suite pass unchanged; typecheck clean; lint has 0
warnings left for this file.
…28->~3) The deployed lazy-gateway plugin's config(cfg) hook (called by OpenCode on every config load) mixed three concerns: computing today's availability map, pruning tool entries for unavailable families, and projecting cfg.tools/cfg.permission. Extract each into its own top-level function (computeAvailability stays a closure inside rufloGateway — it needs rufloClient/aqeClient; pruneUnavailableTools/projectGatewayTools/ projectGatewayPermissions are pure functions of their params, no closure needed). This is an emitted template (deployed verbatim to ~/.config/opencode/plugins/ruflo-gateway.js, receipt-hashed by content) — plain ESM, no build step, no constraint beyond "valid self-contained module"; adding top-level function declarations is safe the same way the file's existing dozen helpers already are. Mechanical, behavior-preserving — identical availability/tools/permission computation and identical mutation order (prune tools before assigning cfg.tools/cfg.permission). tests/kit/opencode-ruflo-gateway.test.mjs (13/13) and the full suite pass unchanged; lint has 0 warnings left for this file (worst remaining function is CC 19, deployManagedPlugin's sibling terminateChild, already below the audit's original findings).
… (CC 27) mliDetail computed several dense ternary-chain text fields (published/ access/routable/next-step status, lifecycle-scope/availability/retirement, and the ollama-only local-install block) inline while also building the whole detail-panel DOM. Extracted the three text/HTML computations into mliDetailStatusText, mliDetailVariantFields, and mliDetailLocalBlock; mliDetail itself now only assembles the panel from their results. renderModelLifecycle wrote to ~10 unrelated DOM targets (badge/asof, attention list, routes+observed, history, consumers+impact) in one function. Split into mliRenderBadgeAndAsof/mliRenderAttention/ mliRenderRoutesAndObserved/mliRenderHistory/mliRenderConsumersAndImpact, called in the original order. Both splits keep every original expression verbatim; only the grouping changed. pnpm test (356/356) and pnpm run test:ui (331/331) both pass unchanged from baseline; typecheck, lint, and build are clean.
…C cleanup ADR-0017 named src/lib/opencode.mjs as the literal home of opencodeStack/retireOpencode/reconcileOpencodeGuidance and described it as "one owner module" in the singular-file sense; ADR-0037 listed the opencode.mjs receipt-reconciliation family as deferred backlog. Both are now stale after this track's work: - ADR-0017: note the 2026-08-26 split (opencode.mjs is now a re-export barrel over opencode-core/agents/artifacts/lifecycle.mjs) in the update note, the "One owner module" section, and the References file list — no architectural change, still one owner module from every caller's point of view. - ADR-0037: replace the opencode.mjs backlog line with a "Wave 2 update" noting reconcileOwnedMap/reconcileFamilyPermissions now back applyOpencode (71->21) and every other named opencode.mjs/ execution/opencode.mjs/opencode-ruflo-gateway.js function over CC 25 is under the gate.
…ncileOwnedMap, 5-file split)
renderScore wrote ~10 independent scorecard regions (hero KPIs, cost-per-day bars, host cards, token bar/legend, punchcard, models, OpenRouter, projects, categories) in one function. Split into renderScoreHero/DayBars/Hosts/ TokBar/Punchcard/Models/OpenRouter/Projects/Categories, called in the original order; renderScore is now a thin orchestrator. renderLimits mixed the Claude window, the Codex lane, and the insights panel. Split into renderLimitsClaude/renderLimitsCodex/ renderLimitsInsights. Both splits keep every original expression verbatim. pnpm test (356/356) and pnpm run test:ui (331/331) both pass unchanged from baseline; typecheck, lint, and build are clean.
… split into usage-parsers.mjs + usage-aggregate.mjs parseClaude (CC 33), parseCodex (CC 53), and aggregate (CC 44) each decompose into small per-record/per-phase helper functions, all under CC 25. usage-index.mjs (1,736 raw / 1,067 counted lines) then splits along its natural seams: - usage-parsers.mjs: the per-vendor transcript parsers (parseClaude, parseCodex, blankSession, addUsage, projectLabel, IDLE_GAP_MS) and their private helpers. - usage-aggregate.mjs: pure arithmetic over already-parsed records (mergeIntervals, maskSecrets, normalizeSessionIdentity, applyCodexLedger, aggregate, sessionPayload, MAX_TURN_CHARS) — a leaf module with no dependency on its siblings. - usage-index.mjs: index I/O, the on-disk cache, build/scan orchestration, and single-session resolution; re-exports the moved names so every existing import path (tests, dashboard-server, usage-opencode.mjs) keeps working unchanged. usage-opencode.mjs now imports blankSession/addUsage directly from usage-parsers.mjs instead of usage-index.mjs, removing the pre-existing circular import between the two (usage-index.mjs -> usage-opencode.mjs -> usage-parsers.mjs is now a straight line, not a cycle). All three files land under the 1,000-counted-line max-lines threshold (usage-index.mjs 771 raw lines / usage-parsers.mjs 594 / usage-aggregate.mjs 517), and every eslint complexity warning in this file family is gone. No behavior change: identical aggregation output, identical cache schema, identical exports. Realigns every usage-index.mjs citation in docs/USAGE-SCORECARD-METRICS.md and docs/TRANSCRIPTS.md that moved file or line.
….mjs renderSysSummary mixed the KPI band, the disk gauge band, and a call into renderSysConsumers in one function. Split into renderSysKpis and renderSysGaugeBand; renderSysSummary is now a thin orchestrator calling both plus renderSysConsumers, in the original order. pnpm test (356/356) and pnpm run test:ui (331/331) both pass unchanged from baseline; typecheck, lint, and build are clean.
setTab mixed lazy per-tab data loads, the tab button/area/secondary-rail
paint loop, and sub-view/scroll bookkeeping in one function. Extracted
tabLazyLoad(id) and paintTabButtons(id,focus), called in their original
order; setTab is now a thin orchestrator.
This was the last function over CC 25 in src/lib/dashboard/client/**;
`pnpm exec eslint src/lib/dashboard/client --rule '{"complexity":["warn",25]}'`
now reports zero warnings.
pnpm run check (typecheck + lint + markdownlint + build + test, 356/356)
and pnpm run test:ui (331/331) both pass unchanged from baseline.
…rage 88->2 orchestrator; NUL-byte sort-key bug fixed)
providers.mjs (1,086 counted lines, over the max-lines warning) hosted the whole applyAqeRouter/aqeRouterDrift/undoAqeRouter convergence pipeline: AQE_ROUTER_SURFACES, the four surface reconcilers, buildAqeRouterContext/runAqeRouterFold, and reconcileExternalProviders (CC 40). All of it moves to a new src/lib/aqe-router.mjs, which imports back the shared hash/ownership primitives (AQE_OWNERSHIP_KEY, stableValue, declarationHash, plainRecord — now exported) and the provider-detection helpers it needs (aqeExternalProviders, aqeRouterFile, aqeSupportsExternalProviders, aqeSupportsAgentOverrides, aqeSelectableChainProviderTypes, credentialGaps) from providers.mjs. providers.mjs re-imports applyAqeRouter/aqeRouterDrift/undoAqeRouter and re-exports them, so every existing `./providers.mjs` import path (commands, status sections, tests) keeps working unchanged — the two modules do have a real bidirectional dependency (mirroring the pre-existing, safe usage-index.mjs/usage-opencode.mjs pattern: hoisted function declarations, nothing read at module-eval time), which is a deliberate tradeoff against pulling much more of providers.mjs's public detection API into the new file. reconcileExternalProviders (CC 40) decomposes into reconcileDesiredProvider/retireStaleProvider (with reconcileConflictingDeclaration/reconcileProviderActivation) — each well under CC 25. No behavior change: identical router-file writes, identical drift detection, identical ownership receipts.
applyProviders (CC 34) splits its per-model registration loop into validProviderEntry, resolveProviderEndpointArgs, and applyOneProvider. Same id/model grammar check (De Morgan-equivalent, verified), same Ollama default-endpoint heuristic, same done-list labels and attempted count. No behavior change.
…parseCodex 53->11, usage-index + providers split)
Zero functions over CC 50 remain, so the ceiling earned by the complexity program graduates from advisory to enforced: `pnpm run lint:cc` (error at 50) joins `check` and CI's quality job. The warn-25 tier stays advisory. ADR-0037 carries the wave-2 closure record. Claude-Session: https://claude.ai/code/session_01WMKwDpp14PPjj8Bn1g7Uza
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.
What this is
Wave 2 of the complexity program (ADR-0037; wave 1 was #184). Seven parallel file-disjoint tracks cleared the entire residual backlog wave 1 recorded, then graduated the earned gate: no function in the repo exceeds CC 50, enforced as an ESLint error (
pnpm run lint:cc, wired intocheckand CI's quality job). The warn-25 tier stays advisory (~30 functions in the 26–49 band, led by the deliberatecreateLiveEventvalidation-boundary exemption at 49).Headline decompositions (all behavior-preserving)
uninstall run()renderSysStorage(client)applyOpencodereconcileOwnedMap)adaptCodexLedgerdiscoverOllamaApirenderHistory(client)verifyDejaVurunTieredConformancecollectProjectsparseCodexStructural splits (exports preserved, importers untouched)
opencode.mjs1,573 → 74-line barrel over 5 filesusage-index.mjs1,736 → index-I/O +usage-parsers.mjs+usage-aggregate.mjs(one-way dependency graph; removed a latent circular import withusage-opencode.mjs)providers.mjs→ AQE-router machinery extracted toaqe-router.mjs; both under the 1,000-line warningfootprint/storage.mjs1,482 → 3 files (tree collector / reclaim orchestration / detectors)Bug found and fixed (6th of the program)
A literal NUL byte in
mliRouteValue's "used" sort key — the Models tab's user-clickable "Used for" column sort compared strings joined by\0instead of a space. Pre-existing, isolated in its own commit (a7dc519).Verification
Serial gated integration: full
pnpm run checkafter each of the 7 track merges, exit 0 every time; Playwright suite (331 cases) green after the client track; golden status snapshot and drift-parity test byte-identical throughout (never regenerated); the 73 doc-enforced file:line citations realigned anddoc-citationsgreen. Docs gates ran per track (ADR-0017 note,machine-footprint.mdtable cell, citation realignment); ADR-0037 carries the wave-2 closure record.🤖 Generated with Claude Code
https://claude.ai/code/session_01WMKwDpp14PPjj8Bn1g7Uza