chore: add fallow config and fix high-signal findings#938
Conversation
Configure fallow via .fallowrc.jsonc so its analysis reflects this repo's
real entry surface, then fix the genuine issues it found.
Fallow noise reduction (601 → 276 dead-code findings):
- Ignore docs/, test fixtures, skill test-corpora, registry/, examples/
- Declare worker entry points loaded dynamically by file path
(pngDecodeBlitWorker.ts, shaderTransitionWorker.ts)
- Declare runtime IIFE entry (core/src/runtime/entry.ts) built outside the
import graph by build-hyperframes-runtime-artifact.ts
- Declare bun:test files in producer + aws-lambda as test entries
- Ignore dynamically-resolved deps: tsup external (puppeteer-core, esbuild,
giget), peer/static-file (gsap in player perf tests), workspace deps
hoisted by bun (happy-dom, @hyperframes/*), and @fontsource/* packages
read via readFileSync in generate-font-data.ts
Extract inline build:fonts scripts:
- packages/{cli,producer}/package.json had multi-line `node -e ...` blobs
containing braces that fallow mis-parsed as glob alternate groups. Moved
to dedicated build-fonts.mjs scripts.
Fix duplicate exports:
- Remove dead FileIcon alias in studio/SystemIcons.tsx (FileTreeIcons.tsx
has the real, used one)
- Consolidate ValidationResult: drop the identical duplicate in
gsapParser.ts; both parsers now import from core.types
- Suppress intentional namespace patterns (per-namespace ML manager
exports; CLI per-command 'examples' convention; fileServer.ts test-only
isPathInside which has different symlink semantics from utils/paths.ts)
Break circular dep (studio/components/editor):
- manualEditsDom.ts re-exported clearStudioPathOffset / clearStudioRotation
/ clearStudioBoxSize from manualEditsSnapshot.ts, which imports four
helpers from manualEditsDom.ts — back-edge cycle
- Re-export moved to manualEdits.ts (the package-public barrel) where the
rest of the snapshot re-exports already live; underlying files now form
a clean DAG
Remove genuinely unused deps:
- studio: motion (no imports anywhere), codemirror (umbrella package; the
@codemirror/* sub-packages are used directly)
- cli: mime-types (plus its only consumer src/utils/mime.ts, which was a
hardcoded mime table that didn't use the package), and its now-stale
tsup external entry
Verified: typecheck across core/cli/producer/studio is clean, oxlint
+ oxfmt pass, manualEdits.test.ts (18 tests) and core parser tests (69
tests) still pass.
Deferred follow-ups (real findings, separate PRs):
- 8 circular deps in producer/services/render/stages/ — renderOrchestrator
↔ captureHdr* / captureStage / extractVideosStage form a hub cycle
- ~14 unused files in producer/src/services/ that look like dead
re-export shims to @hyperframes/engine, but aren't in the public
exports map — need to confirm no deep-import consumers before deletion
- waveform.ts complexity hotspot
miguel-heygen
left a comment
There was a problem hiding this comment.
Clean, well-scoped PR. Every change is justified and the config is well-documented.
Verified:
FileIconin SystemIcons.tsx is dead —FileTreeNodes.tsximports fromFileTreeIcons.tsx, not SystemIconsmime-typeshas zero imports across cli/srccodemirrorumbrella andmotionhave zero imports in studio/src — sub-packages (@codemirror/*) are used directlyValidationResultconsolidation:core.types.tsis the canonical location, both parsers now import from there- Circular dep fix: moving the
clearStudio*re-exports frommanualEditsDom.tsto themanualEdits.tsbarrel breaks the back-edge without changing the public API build-fonts.mjsextraction: eliminates the fallow glob-parsing false positives from inlinenode -eblobs
The deferred follow-ups (8 circular deps in producer/services/render/stages, ~14 unused shim files, waveform complexity) are the right call for separate PRs.
CI green except one regression shard still in progress — no code changes that would affect render output.
vanceingalls
left a comment
There was a problem hiding this comment.
Solid, well-scoped cleanup. Config is explained (not bulk-allowlisted), each ignore has a one-line rationale, and the structural fixes (cycle break, duplicate-type merge, dead-code drops) are individually verifiable. Public-API surfaces preserved. Recommend ship.
Calibrated strengths
.fallowrc.jsoncignore rationales are concrete and load-bearing — e.g.isPathInsidecarries the "different symlink semantics from utils/paths.ts" note, so the next reader knows the duplication is intentional rather than a latent dedup target. This is exactly the right discipline for a tool config — it's documentation for the next person who hits a fallow flag.manualEdits.ts:42-50— moving theclearStudio*re-export off the back-edge inmanualEditsDom.ts:464and onto the package-public barrel is the structurally correct cycle break. Verified the only external consumer (hooks/useDomEditCommits.ts:11-15) goes through the./manualEditsbarrel, so the public surface is unchanged.manualEdits.test.tsalso imports via the barrel — tests cover the new path.ValidationResultconsolidation: the duplicate atgsapParser.ts:347is dropped, both parsers now import fromcore.types, andcore/index.ts:20continues to re-export the canonical one. No deep-import consumers (from "@hyperframes/core/parsers/gsapParser") were depending on the type — verified across the workspace.- The
node -e→scripts/build-fonts.mjsextraction is the right call. Even setting aside fallow's glob-parser issue, the inline blob was unreadable and unreviewable; a dedicated 14-line script withexistsSync+execSyncis much easier to maintain. Symmetric move in both cli and producer.
Findings
nit — bun.lock carries a stray aws-lambda version bump (0.0.1 → 0.6.22). Not in the PR description, and packages/aws-lambda/package.json doesn't appear in the diff. Looks like an unrelated lockfile resync from running bun install against a workspace whose version fields had drifted on main. Not load-bearing (aws-lambda isn't published as 0.6.x afaict), but worth a sentence in the description so the next person reviewing the lockfile delta isn't confused. Same goes for the synchronized 0.6.15 → 0.6.22 bumps across every workspace package.
nit — mime-db regressed 1.54.0 → 1.52.0 in the lockfile after dropping the direct mime-types dep. The remaining aws-cdk-lib/mime-types chain now wins resolution at the older version. No functional impact (only consumed transitively by aws-cdk-lib), but if some downstream tool reads top-level mime-db it'll see the older version. Worth a glance, but not a blocker.
nit — fallow worker-entry declarations point at file paths that don't exist on main yet (packages/producer/src/services/pngDecodeBlitWorker.ts, shaderTransitionWorker.ts). Verified the typecheck CI is green on this PR, so either (a) those files exist on the branch I haven't traversed, or (b) fallow tolerates missing entries gracefully. Just flagging that adding entries-that-don't-yet-exist sets up a foot-gun if a future rename silently drops them off the entry list — a comment near those two lines noting the companion *Pool.ts files would prevent future drift.
nit — deferred follow-up bullet for the 8 producer/services circular deps is the right move, but consider opening a tracking issue before this merges. "Real refactor, listed in PR body" tends to fall off when the PR merges and the body scrolls off; an issue keeps it indexed.
CI (verbatim, fresh)
{"failed":[],"pending":["regression-shards (shard-1, ...)","regression-shards (shard-2, ...)","regression-shards (shard-3, ...)","regression-shards (shard-4, ...)","regression-shards (shard-5, ...)","regression-shards (shard-6, ...)","regression-shards (shard-7, ...)","regression-shards (shard-8, ...)"]}
All required & non-shard checks green (Build, Typecheck, Lint, Test, CLI smoke, runtime contract, Format, Preflight, file size, semantic title, perf gates, Windows render, preview-regression, CodeQL). 8 regression shards still in progress; nothing failed.
Verdict
Verdict: APPROVE
Reasoning: Every non-trivial deletion verifies — FileIcon alias isn't consumed outside its file (the FileTree.tsx FileIcon is a separate local symbol, not the same export), mime-types / motion / codemirror umbrella have zero imports, and the ValidationResult + cycle-break refactors preserve the public surface and pass typecheck. Config rationales are the right level of detail. The lockfile drift is a noise item, not a correctness one.
Review by Vai
vanceingalls
left a comment
There was a problem hiding this comment.
Re-stamp converting the prior COMMENT to a formal APPROVE — content of the review body still applies.
Verified deletions
Every removed symbol grep-verified across workspace including registry/, examples/, docs/:
FileIcon— only out-of-file ref is a separate local symbol inFileTree.tsx. Clean.mime-types,motion,codemirrorpackages — zero imports across the workspace. Clean.MIME_TYPESincli/utils/mime.ts— file-local only; other files have their own MIME tables. Clean.
Cycle break
useDomEditCommits.ts:11 (only external consumer of clearStudio*) goes through the ./manualEdits barrel, which the PR updates to re-export directly from manualEditsSnapshot. Public surface preserved — no adopter break.
Findings
nit: lockfile churn — every workspace's 0.6.15 → 0.6.22 bump plus a stray aws-lambda 0.0.1 → 0.6.22 bump aren't in the PR body. Either intentional (version sync) or accidental. Worth a line in the description either way.
nit: the fallow tool itself is a nice addition for the team's static-analysis bar. If you intend to keep using it, a bunx fallow pre-commit lefthook or CI step would lock in the cleanup discipline.
Verdict: APPROVE.
Re-stamp by Vai

What
Add a
.fallowrc.jsoncand fix the genuine code-quality issues fallow surfaces. Drops dead-code findings from 601 → 276 (45% noise reduction from config, the rest are real signal that's either fixed here or listed as deferred follow-ups).Why
npx fallow --summaryonmainwas reporting 601 dead-code issues, but ~half were false positives: docs.mdxfiles flagged as unused, vitest/bun-test files not registered as entry points, worker entry points loaded via file path invisible to static analysis, deps consumed dynamically (tsupexternal, peer/static-file in tests,readFileSyncreads of@fontsource/*). Without config, fallow can't be a useful signal; with config, the remaining findings are real.How
Config (
.fallowrc.jsonc)@fontsource/*, workspace siblings)ignoreExportsfor intentional namespace barrels (per-namespace ML manager exports; CLI per-commandexamplesconvention from CLAUDE.md;isPathInsidein fileServer.ts which is test-only and has different symlink semantics from the canonical one in utils/paths.ts)Inline build:fonts blob extraction
packages/{cli,producer}/package.jsonhadnode -e "..."blobs containing{...}that fallow mis-parsed as glob alternate groups, emitting 4 warnings and skipping the real entry. Moved each into a dedicatedscripts/build-fonts.mjs.Duplicate-export fixes
FileIconinstudio/SystemIcons.tsx— removed the dead alias;FileTreeIcons.tsxhas the real, used oneValidationResult—gsapParser.tshad an identical-shape duplicate ofcore.types.ts. Dropped the duplicate; both parsers now import fromcore.typesCircular dep
manualEditsDom.tsre-exported threeclearStudio*functions frommanualEditsSnapshot.ts, which imports four helpers frommanualEditsDom.ts— a back-edge cycle. Moved the re-export tomanualEdits.ts(the package-public barrel) where the rest of the snapshot re-exports already live.Unused-dep cleanup
motion(no imports anywhere) andcodemirror(umbrella package;@codemirror/*sub-packages are used directly)mime-types(zero imports), its now-orphansrc/utils/mime.ts(hardcoded mime table that didn't even use the package), and its tsupexternalentryTest plan
bun run typecheckclean across core, cli, producer, studiobunx oxlint+bunx oxfmt --checkclean on changed filesbunx vitest run packages/studio/src/components/editor/manualEdits.test.ts— 18/18 pass (verifies the cycle break doesn't break behavior)bunx vitest run packages/core/src/parsers— 69/69 pass (gsapParser + htmlParser tests verify the ValidationResult consolidation)bunx vitest run packages/studio— 505/505 passbunx vitest run packages/cli— 332/332 passnode scripts/build-fonts.mjsin bothpackages/cliandpackages/producerruns successfullyDeferred follow-ups
Real findings that need their own PRs:
producer/services/render/stages/—renderOrchestrator↔captureHdr*/captureStage/extractVideosStageform a hub cycle. Real refactor.producer/src/services/(audioExtractor, audioMixer, browserManager, chunkEncoder, compilationRunner, parallelCoordinator, screenshotService, streamingEncoder, videoFrameExtractor) — most are thin re-export shims to@hyperframes/enginethat aren't in the publicexportsmap. Need to confirm no deep-import consumers before deletion.waveform.tscomplexity hotspot — fallow'sstart with...suggestion. Separate audit.