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
| Function | File | Risk | CC | ND | FO |
|---|---|---|---|---|---|
execute | src/core/archive.ts | 19.4 | 63 | 9 | 25 |
execute | src/core/update.ts | 17.5 | 32 | 5 | 45 |
registerSchemaCommand | src/commands/schema.ts | 17.0 | 21 | 6 | 62 |
<anonymous> | src/commands/schema.ts | 16.8 | 41 | 6 | 35 |
upgradeLegacyTools | src/core/update.ts | 16.8 | 25 | 5 | 29 |
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:
| Pattern | Occurrences |
|---|---|
complex_branching | 9 |
exit_heavy | 9 |
god_function | 8 |
long_function | 8 |
deeply_nested | 6 |
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
executefunction insrc/core/archive.tshas 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. executeinsrc/core/update.tsis 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.registerSchemaCommandinsrc/commands/schema.tshas 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 →