Reaktor Build Tools
Reaktor and BestBuds already use many good tools. The problem is not that the tools are bad. The problem is that their facts are spread across Gradle files, package.json, target folders, shell scripts, Worker configs, mobile release files, test folders, and cloud manifests.
The goal is simple: keep the tools, but stop making every developer and agent remember where each piece lives.
Dependeasy and Reaktor CLI should work together:
- Dependeasy produces build facts. It reads the project through Gradle and related project files, then writes a clear model of modules, targets, source sets, artifacts, tests, budgets, deployables, and required tools.
- Reaktor CLI operates those facts. It gives humans and agents one command surface for build, test, run, deploy, logs, graph, doctor, performance, and security checks.
- Other tools stay as executors. Gradle still builds Kotlin. Wrangler still deploys Workers. Fastlane still packages mobile apps. Playwright still tests web flows. Dependeasy should coordinate them, not replace them.
This page is the map. Use Dependeasy for the Gradle/KMP build plugin details, Reaktor CLI for the command-line implementation, and Reaktor DevOps for deployment and operations.
The Split
The boundary should stay sharp.
| Layer | Job | Should not do |
|---|---|---|
| Dependeasy | Discover and publish build facts; configure common Gradle/KMP/native defaults; attach tools by module profile | Become a new build system or ask modules to repeat facts Gradle already knows |
| Reaktor CLI | Show and run the current task catalog; explain targets; run doctor checks; call tool adapters safely | Hardcode BestBuds service names or scrape scripts forever when Dependeasy facts exist |
| Reaktor Desktop/MCP | Show the same facts visually and expose safe agent operations | Keep a separate model of modules, targets, and tools |
| Executors | Gradle, npm, Wrangler, Fastlane, Playwright, Maestro, Karate, k6, Dagger, Pulumi, k3s, and others do their own work | Own Reaktor's mental model |
The important rule:
One tool can be clever. Twenty hidden one-off patterns cannot be the system.
What Exists Today
Build and language tools
| Tool | Used for | Current home | Dependeasy status |
|---|---|---|---|
| Gradle | Kotlin, Android, JVM, desktop, publication, task graph | settings.gradle.kts, build.gradle.kts | Core supported |
| Kotlin Multiplatform | common, Android, iOS, JS, JVM modules | Reaktor and BestBuds modules | Core supported |
| Android Gradle Plugin | Android app/library builds | Gradle plugin management and Dependeasy | Core supported |
| KSP | symbol processing and future graph/service extraction | Gradle plugin management and Dependeasy | Applied today; extraction not built yet |
| Kotlin serialization | DTOs and config parsing | Gradle plugins and code | Supported |
| SQLDelight | local database and schema work | Gradle plugin management, module build files | Outside central Dependeasy model |
| BuildKonfig | generated build constants | plugin classpath/settings | Available, not centrally modeled |
| Kotlin benchmark | benchmark tasks | Gradle plugin management | Outside central model |
| Foojay resolver | JDK toolchain resolution | BestBuds settings | Outside Dependeasy |
Native and mobile tools
| Tool | Used for | Current home | Dependeasy status |
|---|---|---|---|
| CMake and Ninja | C++ sidecars, native libraries, ABI builds | module cpp/, Dependeasy native tasks | Supported for Android/iOS paths |
| Android NDK and Prefab | JNI, FBJNI, Hermes, native dependencies | Dependeasy native config | Supported |
| Kotlin/Native cinterop | iOS native interop | Dependeasy generated .def files | Supported |
| CocoaPods | iOS app dependencies and KMP framework integration | targets/appDarwin/Podfile, Gradle | Partly supported |
| Xcodebuild | iOS app build/test package | npm scripts and Fastlane | Outside Dependeasy |
| Fastlane | Android/iOS release and distribution lanes | fastlane/, npm scripts | Outside Dependeasy |
| Maestro | Android/iOS/web/desktop user-flow tests | reaktor/tools/maestro, tests/* | Outside Dependeasy |
Web and Worker tools
| Tool | Used for | Current home | Dependeasy status |
|---|---|---|---|
| npm workspaces | target packages and scripts | root and target package.json files | CLI reads today; Dependeasy should publish cleaner facts |
| Vite | web target builds | target vite.config.ts | Outside Dependeasy |
| React | web UI surfaces | web target packages | Outside Dependeasy |
| TypeScript | Worker/web checks and packages | target configs and package scripts | Outside Dependeasy |
| Wrangler | Cloudflare Worker dev, deploy, logs, types, bindings | target wrangler.json, npm scripts | CLI can run today; Dependeasy should model bindings |
| Docusaurus | Reaktor documentation site | targets/reaktorWeb/docusaurus | Outside Dependeasy |
| Karakum | Kotlin/JS to TypeScript package wrapping | Reaktor ts/package.json files | Outside central model |
| Metro | JS bundle path for reaktor-ffi | reaktor-ffi/js/package.json | Outside central model |
Test, performance, and replay tools
| Tool | Used for | Current home | Dependeasy status |
|---|---|---|---|
| Kotlin/JVM tests | unit and regression tests | Gradle modules | Partly covered through Gradle |
| Playwright | web, Worker, CompanyOS, notification, chat, surface tests | tests/*/playwright | CLI reads scripts; not modeled |
| Karate | HTTP/API contract tests | reaktor/tools/karate, tests/*/karate | CLI reads script family; not modeled |
| k6 | smoke/load probes | reaktor/tools/k6, tests/reaktorServer/k6 | CLI reads script family; not modeled |
| Keploy | production-like replay for app-server | cloud/k3s, targets/reaktorServer | CLI reads script family; not modeled |
| Lighthouse | web performance budgets | reaktor-performance/tools, npm scripts | Outside Dependeasy |
| Bundle budgets | JS output size checks | tests/reaktorWeb/performance | Outside Dependeasy |
Deploy, cloud, and operations tools
| Tool | Used for | Current home | Dependeasy status |
|---|---|---|---|
| Dagger | CI and task orchestration direction | .dagger | CLI can call; not modeled by Dependeasy |
| Pulumi | cloud resource materialization in Reaktor | reaktor-cloud/Pulumi.yaml | CLI can call; not modeled by Dependeasy |
| k3s and kubectl | JVM server, Memgraph, ClickHouse, Cloudflared | cloud/k3s | Outside Dependeasy |
| Helm values | monitoring/radar stack | cloud/k3s/radar, reaktor-cloud resources | Outside Dependeasy |
| Docker Compose | model/data support services | model folders | Outside Dependeasy |
| GitHub Actions | external CI runner | .github/workflows | Outside Dependeasy |
| Cloudflare bindings | Workers, R2, D1, service bindings, logs | wrangler.json, Cloudflare account | Outside Dependeasy |
Security tools to add
These should be added through Dependeasy profiles and CLI commands, not scattered scripts.
| Tool family | Likely tools | Why it matters |
|---|---|---|
| Secret scanning | Gitleaks, TruffleHog | Catch committed tokens and leaked credentials |
| Dependency and image scanning | OSV-Scanner, Syft, Grype, Trivy | Know vulnerable packages, containers, and native dependencies |
| Static code checks | Semgrep, CodeQL where useful | Enforce cross-language security and architecture rules |
| Kotlin structure checks | Konsist or KSP-based checks, detekt/ktlint if useful | Keep Reaktor module patterns repeated |
| API and web scanning | OWASP ZAP, Nuclei | Test deployed HTTP surfaces and known risky endpoints |
| SBOM and license reports | Syft, Gradle/npm license reports | Track what ships and what license obligations exist |
What Reaktor CLI Does Today
The CLI already reads the current project instead of hardcoding BestBuds.
It discovers:
- the nearest
package.jsonwith a top-levelreaktorkey; - root npm scripts and workspaces;
- concrete folders under
targets/; - target-local
package.jsonscripts; - Gradle modules from
settings.gradle.kts; - script families such as
fastlane:*,maestro:*,karate:*,k6:*,keploy:*,perf:*, andtest:*; - Wrangler targets by seeing
wrangler.jsonorwrangler.jsonc; - direct shortcuts such as
radar,ssh,memgraph,clickhouse, andweb; - cloud entries such as the Dagger directory.
That gives useful commands now:
reaktor
reaktor tasks
reaktor appAndroid
reaktor appAndroid maestro login
reaktor socialServer npm dev
reaktor deploy reaktorWeb
reaktor reaktorServer logs
reaktor test reaktorWeb playwright
reaktor fastlane android release
reaktor doctor
This is a good bridge. It is not the final model. The CLI should move from "discover from many files" to "read Dependeasy facts first, fall back to discovery only when facts are missing."
What Dependeasy Owns Today
Dependeasy already owns a useful part of the build.
It ships:
dev.shibasis.dependeasy.settings;dev.shibasis.dependeasy.library;dev.shibasis.dependeasy.application;- common Kotlin opt-ins and source-set helpers;
- Android app/library defaults;
- KMP common, Android, iOS, JS, and JVM helper shapes;
- Kotlin/JS package output conventions;
- Android/iOS CMake tasks;
- Android Prefab extraction;
- Kotlin/Native cinterop
.defgeneration; - native dependency discovery across Gradle project dependencies;
buildReleaseBinariessize logging;- Dokka copy helper tasks.
That is enough to prove the direction. It is not enough to tame the whole toolchain.
Build and Deployment Speed
The current build shape is mostly understandable, but it does too much work in the wrong places.
There is no first-party reaktor-apps/ directory in this checkout. The effective app surface is:
- Reaktor framework modules under
reaktor/*, almost all using the samecommon { },web { },droid { },darwin { }, andserver { }KMP shape; - BestBuds app modules under
modules/*, which source-build Reaktor throughincludeBuild("../reaktor"); - deployable app and service targets under
targets/*; - generated Kotlin/JS packages such as
modules/engine/engine-ktandmodules/app/bestbuds-kt; - native sidecars under module
cpp/folders, coordinated by Dependeasy CMake tasks androot.cmake; - Worker deploy descriptors in target-local
wrangler.jsonfiles.
That is a workable pattern. The slow part is that the target boundary is not yet the build boundary.
Patterns Found In The Current Tree
| Area | Current pattern | Speed risk |
|---|---|---|
| Reaktor modules | Repeated Dependeasy KMP shape across common, web, droid, darwin, and server | Good repetition, but the target matrix is not emitted as a machine-readable manifest |
| BestBuds source build | BestBuds substitutes many Reaktor modules from ../reaktor | Correct for framework development, expensive when a target only needs published or already-generated outputs |
| Web targets | appWeb, reaktorWeb, companyOS, and prototypes use Vite plus Cloudflare Workers | Vite aliases and generated Kotlin/JS package contracts are hand-maintained per target |
reaktorWeb | prebuild always runs :engine:jsBrowserProductionLibraryDistribution before build | Doc-only edits can trigger Kotlin/JS work and fail on unrelated app code |
| Worker targets | Many targets repeat predeploy config merging and wrangler deploy --minify | Same deployment logic is copied across packages and mutates wrangler.json in place |
| Generated JS packages | engine-kt and bestbuds-kt are large local file packages consumed by Vite and Workers | Rebuilding or rebundling them without an input fingerprint wastes time |
| Native builds | Dependeasy uses CMake/Ninja, per-variant build dirs, Prefab extraction, and generated cinterop defs | CMake inputs, Prefab inputs, ABI, SDK, and dependency native projects are not summarized in one cache report |
| Mobile tests | npm scripts wrap Gradle, Xcodebuild, Fastlane, and Maestro | Useful scripts, but target facts are repeated in command names and env vars |
Duplicate Work To Remove First
- Docs deploy must not rebuild the app. A docs-only change should run Docusaurus, place assets under
targets/reaktorWeb/dist/client/docs, and deployreaktor-webassets. It should not run Kotlin/JS generation, Vite client build, app compilation, or unrelated Worker checks. - Generated Kotlin/JS packages need fingerprints. Dependeasy should record the Gradle task, source inputs, dependency graph, Kotlin version, and output hash for
bestbuds-kt,engine-kt, and Reaktorts/exportpackages. Vite and Workers should consume those facts instead of guessing from file paths. - Worker deploy scripts should be generated. The repeated
predeployJSON merge andwrangler deploypattern belongs in a Dependeasy/Reaktor CLI Worker adapter. The adapter should write generated config underbuild/dependeasy/wrangler/<target>.jsoninstead of rewriting the checked-inwrangler.json. - Native build decisions need reports. Dependeasy already knows CMake executable, generator, ABI, SDK, Prefab modules, source files, output dirs, and native project dependencies. It should write that as a cacheable native report so a rebuild explains what changed.
- Root npm script sprawl should become generated aliases. Keep the nice commands, but generate most target aliases from the manifest. Humans should not maintain every
deploy<Name>,logs<Name>,karate:<target>:..., andmaestro:<platform>:...spelling by hand.
Gradle And KMP Speed Practices
Dependeasy should make Gradle fast by making the smallest correct task obvious.
- Keep
org.gradle.caching=true,org.gradle.parallel=true, and Kotlin incremental flags on by default, as they already are. - Turn Gradle configuration cache into an explicit project goal: run it on the main task families, list incompatible tasks, and fix them one by one. Reaktor currently leaves configuration cache commented out, which is reasonable while tasks are unstable, but it should not stay unknown.
- Prefer the Gradle daemon for repeated local loops. Use
--no-daemononly when isolation is more important than speed. - Split target slices:
docs,web-client,worker,kotlin-js-package,android-app,ios-app,desktop,server, andnative. A target should state which slices it needs. - Give every custom task real inputs and outputs. Dependeasy's CMake and cinterop tasks already move in this direction; reports and generated descriptors should follow it.
- Make source-build versus published-build a first-class choice. BestBuds should source-build Reaktor while framework code is being changed, but app/docs deploys should be able to use already-built or published artifacts when framework code did not change.
- Put build timing, task outcome, and cache-miss summaries under
build/dependeasy/reports/build/so agents can see the reason for slowness instead of guessing.
Vite And Web Speed Practices
The web targets should stay Vite-based, but their Reaktor/Kotlin inputs should come from the build graph.
- Generate Vite alias maps for Reaktor packages, generated Kotlin/JS packages, and browser-only stubs from Dependeasy facts.
- Keep generated Kotlin/JS packages outside Vite dependency prebundling when needed, but explain that through the manifest rather than per-target tribal knowledge.
- Use TypeScript project references where a target has both client and Worker TypeScript so
tsc -bcan skip unchanged projects. - Split Docusaurus from the app client.
reaktorWebshould have separate commands for docs assets, app assets, Worker code, and full production build. - Keep stable manual chunks for large vendor/runtime groups where bundle reports show churn.
- Store bundle-size reports beside the Dependeasy build reports and key them by target slice.
CMake And Native Speed Practices
Native builds are where hidden duplicate work becomes expensive.
- Keep Ninja as the default generator. It is already used by Dependeasy and is the right default for fast incremental native builds.
- Keep separate build directories by module, platform, ABI, SDK, build type, and toolchain version. A stale arm64 simulator cache should never poison an Android ABI build, and one ABI should not force all ABIs to rebuild.
- Avoid clean builds as a normal command. Clean should be an explicit operation with a safety class because it destroys useful native and Kotlin caches.
- Replace recursive CMake source globs with generated file lists or a Dependeasy source-file report where practical. If globs remain, Dependeasy should still record the expanded file set so cache misses are explainable.
- Treat third-party native dependencies such as Hermes, FlatBuffers, FBJNI, and OpenSSL as artifacts with versions, checksums, headers, and ABI outputs. Rebuilding them should be rare and obvious.
- Emit
compile_commands.json, symbols, library sizes, and static/shared library paths into the target report. Native outputs should be visible inreaktor graph, not hidden in build folders.
Deployment Speed Practices
Deployment should be target-aware and slice-aware.
reaktor deploy reaktorWeb --slice docsshould run only docs validation/build and deploy thereaktor-webasset bundle.reaktor deploy reaktorWeb --slice workershould build or check only the Worker code and bindings.reaktor deploy reaktorWeb --slice fullshould run Kotlin/JS, Vite, docs, Worker checks, and Wrangler deploy.- Worker adapters should show the merged bindings, routes, compatibility flags, assets directory, and
run_worker_firstpaths before deploy. - The deploy report should record git SHA, dirty state, target slice, artifact hashes, Wrangler version, Worker version, route, and live verification result.
This would have prevented the recent docs deploy slowdown: a docs-only update would not have depended on :engine:jsBrowserProductionLibraryDistribution, and unrelated Kotlin app errors would not have blocked publishing docs assets.
Highest Priority Features
| Priority | Feature | Why it matters |
|---|---|---|
| 1 | Target-slice manifest | Lets Dependeasy say exactly what docs, web-client, worker, native, android, and ios need |
| 2 | Docs-only Reaktor Web deploy | Removes the most visible wasted work today |
| 3 | Build fingerprint report | Makes cache hits, cache misses, generated JS packages, CMake variants, and deploy artifacts explainable |
| 4 | Generated Worker adapter | Removes repeated Wrangler script logic and keeps bindings/routes visible |
| 5 | Generated Vite alias/package adapter | Stops every web target from hand-maintaining Reaktor package paths |
| 6 | Configuration-cache campaign | Turns Gradle startup/configuration cost from unknown into tracked work |
| 7 | Native artifact cache model | Prevents ABI/toolchain/native dependency rebuilds from feeling random |
| 8 | Script alias generation | Keeps the friendly npm/CLI surface while removing hand-written command sprawl |
The principle is the same as the rest of Reaktor: keep the powerful tools, but make the graph of work visible. The fastest build is not the one with the most tricks. It is the one that knows the smallest correct thing to do.
The Missing Manifest
The next important artifact is a versioned manifest. It should be generated, checked in only if we intentionally want stable snapshots, and read by CLI/Desktop/MCP/CI.
The root file can look like this:
{
"schema": "dev.shibasis.dependeasy.project.v1",
"project": "bestbuds",
"modules": ["app", "engine", "reaktor-auth"],
"targets": ["appAndroid", "appDarwin", "appWeb", "reaktorWeb", "socialServer"],
"tools": ["gradle", "npm", "wrangler", "fastlane", "maestro", "playwright"],
"reports": ["build", "test", "performance", "security"]
}
Per module and per target, it should answer plain questions:
- What is this thing?
- What does it build?
- Which source sets does it use?
- Which tool builds it?
- Which tests prove it?
- Which deploy command ships it?
- Which secrets or external services does it need?
- Which reports and budgets apply?
- Which facts were inferred, and from which file?
The last point matters. Every fact needs provenance: "from Gradle", "from package.json", "from wrangler.json", "from KSP", "from CMake", "from a generated Dependeasy descriptor", or "manual exception".
The DSL Rule
Do not make module authors repeat facts that the build already knows.
Bad shape:
dependeasy {
ownsGraph("AuthRuntimeGraph")
exposesService("AuthService")
sourceRoots("src/commonMain", "src/jvmMain")
}
Why it is bad:
sourceRootsare already Gradle/KMP facts;- graph and service names should be found from code, KSP, registration, or runtime graph descriptors;
- repeated declarations drift;
- people learn a second fake model instead of the real one.
Better shape:
dependeasy {
profile(Profiles.StableRuntimeLibrary)
policy {
stability = Stability.Stable
sensitivity = Sensitivity.Auth
requireGraphConformance = true
requireThinAdapters = true
}
exceptions {
allow("js-interop-bridge", path = "src/jsMain/**", until = "2026-09-01")
}
}
That DSL says what humans must decide: stability, sensitivity, required checks, and exceptions. Dependeasy should infer the rest.
Tool Adapters To Build
Build adapters in this order.
1. Fact adapters
These make the map reliable.
| Adapter | Produces |
|---|---|
| Gradle/KMP | modules, source sets, dependencies, tasks, artifacts |
| Dependeasy native | CMake, Prefab, cinterop, native outputs |
| npm/package | workspaces, scripts, package names, JS outputs |
| Wrangler | Worker names, bindings, routes, compatibility dates, deploy commands |
| Vite/Docusaurus | web build outputs, dev commands, bundle entry points |
| Fastlane/Xcode/Pods | mobile lanes, schemes, pods, signing requirements |
| Test config | Playwright, Maestro, Karate, k6, Keploy entry points |
2. Report adapters
These make the map useful.
| Adapter | Produces |
|---|---|
| Test reports | JUnit-style pass/fail results across Gradle, Playwright, Karate, Maestro, k6 |
| Performance reports | bundle sizes, Lighthouse, desktop timings, k6 numbers, build timings |
| Security reports | SARIF/JSON for secrets, static checks, dependencies, containers, web/API scans |
| Artifact reports | APK/AAB/AAR, XCFramework, npm package, Worker bundle, jar, container image, checksums |
3. Operation adapters
These let CLI/Desktop/MCP act safely.
| Adapter | Runs |
|---|---|
| Gradle runner | build, test, publish, installDist |
| npm runner | target scripts and generated package scripts |
| Wrangler runner | dev, deploy, tail, typegen, binding checks |
| Fastlane runner | Android/iOS build, release, distribute |
| k3s runner | apply, rollout, logs, status |
| Dagger runner | local/CI task graph |
| Pulumi runner | preview, up, stack inventory |
How It Should Feel
The user experience should be boring and clear.
reaktor graph
reaktor doctor
reaktor explain appAndroid
reaktor build reaktorWeb
reaktor test notificationServer
reaktor perf reaktorWeb
reaktor security auth
reaktor deploy socialServer
Each command should say:
- what it is going to run;
- why that command was chosen;
- which files produced the facts;
- which reports were written;
- whether the command is read-only, local, deploy-like, or destructive.
That is how we keep power without making a tool jungle.
Priority Plan
- Project manifest. Emit
.reaktor/build-graph.jsonfrom Dependeasy and current project files. - CLI reads manifest first. Keep current discovery as fallback.
- Tool registry. Add adapter IDs, report formats, safety class, and install checks.
- Profiles. Add profiles such as
stable-kmp-library,cloudflare-worker,mobile-app,docs-site,native-interop,security-sensitive, andexperimental. - Reports. Normalize test, performance, artifact, and security outputs under
build/dependeasy/reports. - Architecture checks. Add KSP/Konsist/Semgrep-style rules for repeated Reaktor patterns.
- Security checks. Add secret, dependency, container, static, and API scanning through Dependeasy profiles.
- Desktop/MCP projection. Show the same facts in the workbench and expose safe agent operations.
The end state is not fewer tools. It is fewer places to understand.