OpenSpec's core and schema layers carry the highest activity risk — 5 functions to address first

Fission-AI/OpenSpec's top risk is concentrated in src/core/archive.ts, src/core/update.ts, and src/commands/schema.ts — three files accounting for all five critical hotspots, with cyclomatic complexity peaking at 63 and fan-out reaching 62.

Stephen Collins ·
oss typescript refactoring code-health

Antipatterns Detected

complex_branching9exit_heavy9god_function8long_function8deeply_nested6

Key Points

What is a god function and why does it matter in OpenSpec?

A god function is one that tries to do too much — handling so many responsibilities that it becomes a central hub other code depends on. In OpenSpec, eight of the top hotspot functions carry this pattern, meaning a single change to one of them can unexpectedly break behavior in many other parts of the system. The `execute` function in `src/core/archive.ts` exemplifies this: with 25 distinct functions it calls and a cyclomatic complexity of 63, it concentrates enormous risk in one place.

How do I reduce fan-out in TypeScript?

Introduce intermediary service or facade modules that group related calls behind a single interface, so the high-fan-out function delegates to a smaller set of collaborators rather than reaching across the codebase directly — this is especially effective for command-registration functions like `registerSchemaCommand`, where 62 distinct callees suggest that schema concerns are not yet modularized.

Is OpenSpec actively maintained?

The data shows mixed signals: two functions in `src/core/update.ts` — `execute` and `upgradeLegacyTools` — are in the 'fire' quadrant with recent commit activity levels of 16.56 and 15.84 respectively, indicating active development in the update pipeline. However, the top-ranked hotspot overall (`execute` in `src/core/archive.ts`) and `registerSchemaCommand` are in the 'debt' quadrant, suggesting those areas have accumulated structural complexity without recent attention.

How do I reproduce this analysis?

Run the Hotspots CLI against the Fission-AI/OpenSpec repository at commit `f529b25` to reproduce these findings exactly.

What does activity-weighted risk mean?

Complexity × recent commit frequency — functions that are hard to understand AND actively changing are the highest priority for refactoring.

The highest-priority structural risk in Fission-AI/OpenSpec sits in src/core/archive.ts, where the execute function carries a cyclomatic complexity of 63, a nesting depth of 9, and calls 25 distinct functions — making it a high blast-radius refactoring target that hasn’t been recently touched but will be expensive when it finally is. Contrast that with execute in src/core/update.ts, which is actively changing right now with a recent commit activity of 16.56 and a fan-out of 45, combining live commit churn with substantial structural complexity into a genuine regression risk. Across 814 total functions, 91 are rated critical — and the top five hotspots are concentrated in just three files.

The table below ranks functions by activity-weighted risk — a score that multiplies structural complexity by recent commit frequency. A function that is both hard to understand (high cyclomatic complexity) and actively changing is a higher priority than one that is complex but untouched. CC = cyclomatic complexity (independent execution paths); ND = max nesting depth; FO = fan-out (distinct callees).

Top 5 Hotspots

FunctionFileRiskCCNDFO
executesrc/core/archive.ts19.463925
executesrc/core/update.ts17.532545
registerSchemaCommandsrc/commands/schema.ts17.021662
<anonymous>src/commands/schema.ts16.841635
upgradeLegacyToolssrc/core/update.ts16.825529

Hotspot Analysis

execute — src/core/archive.ts

Based on its name and location in the core layer, this function likely orchestrates the archiving pipeline — coordinating reads, writes, validation, and cleanup across multiple subsystems. Its cyclomatic complexity of 63 means there are at least 63 independent execution paths to reason about and test, its nesting depth of 9 far exceeds the refactoring threshold of 8, and its fan-out of 25 means changes here can ripple into 25 other call sites. This is a ‘debt’ quadrant function — it hasn’t been recently changed, but it is structural debt with an extremely high blast radius when next touched.

Recommendation: Before any future work touches this function, write characterization tests to capture current behavior across its major branches; then use extract-method refactoring to break the 63-path body into named, independently testable sub-functions — each covering a discrete stage of the archive workflow.

execute — src/core/update.ts

This function sits in the core update pipeline and, based on its name and a fan-out of 45, appears to coordinate a broad set of downstream operations when an update is triggered — touching more distinct callees than any other hotspot in the dataset. Unlike its archive counterpart, this is a ‘fire’ quadrant function with a recent commit activity of 16.56 and an activity risk of 17.5, meaning it is structurally complex and actively changing right now. A cyclomatic complexity of 32 across an actively committed function is a live regression risk: each new branch added under churn is a potential bug that existing tests may not cover.

Recommendation: Treat this as the highest-urgency item for immediate action: audit the 45 fan-out dependencies to understand the blast radius of any in-flight changes, and prioritize adding branch-coverage tests before the next commit merges.

registerSchemaCommand — src/commands/schema.ts

As a command registration function in the schema layer, this function likely wires up CLI subcommands, attaches option parsers, and binds handlers — a role that naturally accumulates broad coupling over time. Its fan-out of 62 is the highest in the entire top-five dataset, meaning changes here can propagate into 62 distinct downstream functions. At cyclomatic complexity 21 and nesting depth 6, the branching logic is moderately complex, but the extraordinary fan-out is the dominant risk. This is a ‘debt’ quadrant function — it is structural debt that warrants refactoring before the next development push, not an active churn concern.

Recommendation: Map the 62 fan-out dependencies to identify which downstream functions are most sensitive to interface changes here, then consider decomposing command registration into smaller, feature-scoped registration modules to reduce coupling.

<anonymous> — src/commands/schema.ts

This anonymous function sits alongside registerSchemaCommand in the schema command layer and likely serves as a handler or callback wired up during command registration. With a cyclomatic complexity of 41 and fan-out of 35, it carries substantial branching logic and broad coupling to downstream functions. At nesting depth 6, it sits at the refactoring threshold. Its anonymous definition makes it harder to test and trace in isolation — there is no stable identifier to target in test suites or call graphs.

Recommendation: Extract this anonymous function into a named, exported function with a clear responsibility boundary. The naming exercise itself often reveals whether the function is doing too much; named functions are also easier to test independently and easier to trace during code review.

upgradeLegacyTools — src/core/update.ts

This function’s name suggests it handles migration of legacy tooling configurations — a category of code that historically accumulates branching logic as each new migration case is added. Its cyclomatic complexity of 25 and fan-out of 29 reflect that pattern: many conditional paths, each reaching into a distinct set of downstream functions. Like execute in the same file, it sits in the actively-changing quadrant, making it a secondary regression risk alongside its higher-ranked sibling — two functions in the same file accumulating churn simultaneously amplifies the risk of interfering changes.

Recommendation: Audit the 25 branches in this function to determine whether they can be collapsed into a data-driven migration table mapping old tool identifiers to their upgrade targets, which would replace conditional branching with a lookup — reducing CC and shrinking the surface area for future migration bugs.

Patterns Found

Antipatterns detected across the top functions in this snapshot:

PatternOccurrences
complex_branching9
exit_heavy9
god_function8
long_function8
deeply_nested6

These labels belong to two tiers — Tier 1 (structural): complex_branching, deeply_nested, exit_heavy, long_function, god_function. Tier 2 (relational/temporal): hub_function, cyclic_hub, middle_man, neighbor_risk, stale_complex, churn_magnet, shotgun_target, volatile_god.

Key Takeaways

  • The execute function in src/core/archive.ts has a cyclomatic complexity of 63 and nesting depth of 9 — write characterization tests before anyone modifies it, because its 25-function fan-out makes unguided changes extremely risky.
  • execute in src/core/update.ts is a live regression risk right now: a recent commit activity of 16.56 combined with CC 32 and fan-out 45 means it is actively changing across a wide dependency surface — branch-coverage tests should be added before the next merge.
  • registerSchemaCommand in src/commands/schema.ts has the highest fan-out in the top five at 62 — decomposing it into feature-scoped registration modules would isolate change impact and reduce the blast radius for future schema work.

Reproduce This Analysis

git clone https://github.com/Fission-AI/OpenSpec
cd OpenSpec
git checkout f529b2596896afd95d855507b0c53b5d515433b5
hotspots analyze . --mode snapshot --explain-patterns --force

To run the same analysis on your own codebase, run hotspots analyze . --mode snapshot in any local git repo — no configuration required.

Hotspots highlights structural and activity risk — not “bad code.” Findings are a prioritization aid, not a bug predictor. Editorial policy →

Run this on your own codebase

Hotspots runs locally in under a minute — no account, no data leaves your machine.

macOS
$ brew install Stephen-Collins-tech/tap/hotspots
Linux / cargo
$ cargo install hotspots-cli
Run in any repo
$ hotspots analyze .
★ Star on GitHub

Related Analyses