apps/ui-xr/src/main.ts is 9,617 of the 10,953 lines left in the app. Eight extractions have taken apps/ui-xr from 101 non-test sources / 33,795 lines to 10 / 10,953, and this one file is now the whole remaining gap against the CellixJs reference, whose largest app is 1,506 lines in total.
The last extraction (7789366) took only 312 lines and named the reason: most of what remains reads module-level mutable state declared in main.ts, and the brief forbade inventing a shared mutable singleton to move more of it.
factory_step: instrument
unblocks: staging
The staging station's runtime consumer is main.ts. Every placement, plant-offset and support-surface change has to be read and edited inside a 9,617-line file, which is why the last three staging-adjacent slices each spent their opening turns reading rather than editing.
main.ts is the learner runtime's composition root. Thinning it does not move a factory station; it makes the station consumers legible.
Direction - D9 dark factory: a composition root that holds 9,617 lines of behaviour cannot be reasoned about by a bounded worker, which is what every brief in this series has had to work around.
Prior art - none to search; this is a decomposition of local code.
Collision - apps/ui-xr/** is xr-systems-architect's write root and no other slice is open on it.
Size - one vertical per state cluster, not one slice for the file.
The approach that is NOT allowed
Do not create a module-level singleton, a context object, or a mutable export that the extracted modules import. That converts a large file into a large file plus hidden coupling, and the freeze would stop measuring anything real.
The approach that is
Take the state as a parameter, one cluster at a time. For each cluster of module-level let/const in main.ts that a group of functions reads:
- name the cluster and the functions that read it
- move those functions into a package, each taking the values it reads as arguments
- leave the state and the call sites in
main.ts
The call site grows by the argument list; the body leaves. Expect the reduction per cluster to be smaller than the moved function bodies, and say so in the report.
done_when
- run:pnpm --filter @openclinxr/ui-xr test
- run:pnpm --filter @openclinxr/ui-xr build
- run:pnpm --filter @openclinxr/architecture-rules exec vitest run --config vitest.arch.config.ts --root . src/archunit-tests/the-apps-are-composition-roots.test.ts src/archunit-tests/file-size-budgets.test.ts
- changed:packages/openclinxr/architecture-rules/src/checks/file-size-budgets.ts
The main.ts ceiling in SIZE_FREEZE is shrink-only and currently 9,617. Lowering it is what proves the slice did something; the archunit budget row must be lowered in the same change.
No target line count is set on purpose. A number here would become the design target and would be met by moving whatever is cheapest rather than whatever is not wiring.
The failure mode to avoid
apps/ui-xr/src/humanoid-runtime-asset-url.ts records it: a stale dist after a change that only updates src breaks the whole main.ts graph, so waitForStationShell times out for every scenario. Run pnpm packages:build then the ui-xr suite and build, and report whether the app still builds.
apps/ui-xr/src/main.tsis 9,617 of the 10,953 lines left in the app. Eight extractions have takenapps/ui-xrfrom 101 non-test sources / 33,795 lines to 10 / 10,953, and this one file is now the whole remaining gap against the CellixJs reference, whose largest app is 1,506 lines in total.The last extraction (7789366) took only 312 lines and named the reason: most of what remains reads module-level mutable state declared in
main.ts, and the brief forbade inventing a shared mutable singleton to move more of it.factory_step: instrument
unblocks: staging
The staging station's runtime consumer is main.ts. Every placement, plant-offset and support-surface change has to be read and edited inside a 9,617-line file, which is why the last three staging-adjacent slices each spent their opening turns reading rather than editing.
main.tsis the learner runtime's composition root. Thinning it does not move a factory station; it makes the station consumers legible.Direction - D9 dark factory: a composition root that holds 9,617 lines of behaviour cannot be reasoned about by a bounded worker, which is what every brief in this series has had to work around.
Prior art - none to search; this is a decomposition of local code.
Collision -
apps/ui-xr/**isxr-systems-architect's write root and no other slice is open on it.Size - one vertical per state cluster, not one slice for the file.
The approach that is NOT allowed
Do not create a module-level singleton, a context object, or a mutable export that the extracted modules import. That converts a large file into a large file plus hidden coupling, and the freeze would stop measuring anything real.
The approach that is
Take the state as a parameter, one cluster at a time. For each cluster of module-level
let/constinmain.tsthat a group of functions reads:main.tsThe call site grows by the argument list; the body leaves. Expect the reduction per cluster to be smaller than the moved function bodies, and say so in the report.
done_when
The
main.tsceiling in SIZE_FREEZE is shrink-only and currently 9,617. Lowering it is what proves the slice did something; the archunit budget row must be lowered in the same change.No target line count is set on purpose. A number here would become the design target and would be met by moving whatever is cheapest rather than whatever is not wiring.
The failure mode to avoid
apps/ui-xr/src/humanoid-runtime-asset-url.tsrecords it: a staledistafter a change that only updatessrcbreaks the wholemain.tsgraph, sowaitForStationShelltimes out for every scenario. Runpnpm packages:buildthen the ui-xr suite and build, and report whether the app still builds.