Skip to content

Refactor (packages/codemode/src/tool-runtime.ts): Function with high complexity: copyBounded - #94

Open
aiyusuf-1 wants to merge 4 commits into
CMU-17313Q:mainfrom
aiyusuf-1:refactor/codemode-tool-runtime-copybounded
Open

aiyusuf-1 wants to merge 4 commits into
CMU-17313Q:mainfrom
aiyusuf-1:refactor/codemode-tool-runtime-copybounded

Conversation

@aiyusuf-1

@aiyusuf-1 aiyusuf-1 commented Sep 6, 2026

Copy link
Copy Markdown

1. Issue

Link to the associated GitHub issue:

#93

Full path to the refactored file:

packages/codemode/src/tool-runtime.ts

What do you think this file does?

This is the layer that sits between the host and the sandbox when CodeMode runs tools. Anything crossing it has to be plain data, so it checks each value before letting it through: depth, circularity, plain-object prototypes and blocked property names. It also converts value types (Date, RegExp, Map, Set, URL, URLSearchParams) between their host and sandbox forms.

What is the scope of your refactoring within that file?

Only copyBounded (lines 174-294), the recursive helper behind the exported copyIn. No other function in the file was touched.

Which Qlty-reported issue did you address?

Function with high complexity (count = 68): copyBounded at line 174, plus Function with many returns (count = 15) on the same function and three Complex binary expression smells inside it (lines 185, 216, 257).

2. Refactoring

How did the specific issue you chose impact the codebase's maintainability?

copyBounded packed six concerns into one 120-line body and matched every value type with hand-written instanceof chains, of which there were three. Adding one value type meant editing all three chains in the right order plus another return, which is how it reached 15 returns and complexity 68.

What changes did you make to resolve the issue?

I replaced the chains with two ordered lookup tables (HOST_VALUE_WRAPPERS, BOUNDARY_SERIALIZERS) dispatched by a shared matchType helper, and extracted isDataLeaf, isSandboxValue, hasNoJsonForm, isoOrNull, serializeForBoundary and copyContainer. Each table keeps the original chain order, which is what makes the change behavior-preserving.

How do your changes improve maintainability? Did you consider alternatives?

Adding a value type is now one row in one table instead of three chain edits, and copyBounded reads as its six phases in sequence (complexity 68 to 19, returns 15 to 5, binary-expression smells 3 to 0, file total 266 to 243). I first tried extracting each chain into its own function, but that only relocates the smells: a helper wrapping the 7-branch chain still has 7 returns (Qlty flags at 6), so table dispatch was the version that actually cleared them.

3. Validation

How did you validate that the change is correct?

All 263 pre-existing codemode tests still pass, and I ran 60 inputs through the original and refactored copyIn and diffed the results (both modes across every value type, nesting, cycles, depth limits and error paths), which came out byte-identical. I then added 18 tests in packages/codemode/test/tool-runtime.test.ts covering both dispatch tables and every error path.

Which tests cover the change, and why are they sufficient?

packages/codemode/test/tool-runtime.test.ts plus the existing suite, 281 tests total. They are sufficient because the new tests were written against the pre-refactor code and pass unchanged on both versions, so they pin observable behavior rather than the new structure. Running only the new test file, the uncovered lines of tool-runtime.ts jump from 143-148 straight to 346-361, and the refactored region is lines 174-345, so the new tests alone execute every changed line. They also close a real gap, since the old suite never drove a host Map or Set through an intra-sandbox checkpoint.

Line coverage of the file went from 99.63% to 99.64% (the two remaining uncovered lines predate this PR). bun run typecheck is clean.

Note on bun lint: opencode reports 698 warnings and 2 errors on a clean main, so it cannot pass for anyone. This branch reports exactly the same 698 and 2, meaning this PR introduces no new lint findings, and linting only the two files this PR changes reports 0 warnings and 0 errors. The screenshot below shows all three.

Note on CI: turbo.json only registers test tasks for listed packages and codemode had none, so its tests never ran on push. This PR adds @opencode-ai/codemode#test, and the CI screenshot shows the new tests running by name inside the unit job (281 tests, 10 tasks successful).

Attach a screenshot of the test coverage showing the lines were executed by the tests.

1-coverage-new-tests 2-coverage-full-suite **Attach a screenshot showing the tests that cover the change passing during CI** 3-ci-passing **Attach a screenshot of `qlty smells --no-snippets packages/codemode/src/tool-runtime.ts` showing fewer reported issues after the changes.** 4-qlty-before-after **bun lint and bun test passing locally** 5-lint-and-test-local

aiyusuf-1 and others added 4 commits August 30, 2026 12:12
copyBounded mixed six concerns in one 120-line body: the depth guard, leaf
passthrough, un-awaited promise rejection, intra-sandbox host wrapping,
boundary serialization and container recursion. Every value type was matched
by a hand-written instanceof chain, so adding one meant editing three
different chains and the function grew a return statement each time.

Replace the chains with ordered lookup tables (HOST_VALUE_WRAPPERS,
BOUNDARY_SERIALIZERS) dispatched by a shared matchType helper, and extract
isDataLeaf, isSandboxValue, hasNoJsonForm, serializeForBoundary and
copyContainer. Ordering within each table preserves the original chain order,
which is what makes this behavior-preserving; the sandbox value classes are
standalone rather than subclasses of their host counterparts, so no value
matches two entries.

Qlty (packages/codemode/src/tool-runtime.ts):
  before: complexity 68, 15 returns, 3 complex binary expressions (185/216/257)
  after:  complexity 19, returns and binary-expression smells cleared
  file total complexity 266 -> 243

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The suite reached copyIn only indirectly through CodeMode.execute, which never
drives a host Map/Set/RegExp/URL through an intra-sandbox checkpoint, leaving
the wrapper branches uncovered.

Add 18 characterization tests over both copyIn modes: leaf passthrough,
boundary serialization of Date/URL to strings and of the JSON-formless types
to {}, intra-sandbox identity passthrough and host wrapping, and every
contract violation (circular, depth limit and just inside it, blocked
properties, non-plain objects, non-data leaves).

These were written against the pre-refactor implementation and pass unchanged
on both it and the refactored one, so they pin behavior rather than describing
the new structure.

packages/codemode/src/tool-runtime.ts line coverage 99.63% -> 99.64%; the two
remaining uncovered lines predate this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
turbo.json only registers test tasks for the packages a change touches, and
codemode had no entry, so packages/codemode/test never ran on push despite
having its own test script. Add @opencode-ai/codemode#test so the tests
covering the copyBounded refactor actually run in CI.

The suite is 281 tests in ~1.4s, so this is a negligible addition to the
unit job.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant