✨ feat(turbo): automate the workspace with Turborepo - #225
Open
roninjin10 wants to merge 6 commits into
Open
Conversation
added 6 commits
August 18, 2026 13:14
main is red at the Lint all workspaces step: 31 files (mostly README markdown tables) drifted from what the pinned dprint plugins produce. This commit is the output of pnpm --recursive --if-present run format and nothing else.
Root turbo.json models build, check, lint, format, circular, test, coverage, and typecheck over all 51 workspaces, plus the root gates as //# tasks: browser bundle guard, the five node --test script gates as one test:scripts invocation, docs parity, and the cargo fmt/clippy/test gates. envMode is strict; every task declares the env it reads, with secrets in passThroughEnv so they never enter cache keys. Per-package turbo.json files extend the root (Package Configurations): each carries the Boundaries tag mapped from smthrs.group, and the packages that genuinely differ carry their own task definitions — the host-locked wasm reproducibility build in @smthrs/jj (uncached on purpose), smithers-server's wrangler deploy gated on smithers-ui#build, smithers-ui's vite build and persistent dev tasks, the infra worker's deploys, and the chain prompts / evals score-gate tasks. No ^build edges: workspace packages resolve each other through exports -> src/*.ts, so typecheck, lint, and test never consume dist. Verified empirically — a package builds clean with every dependency dist removed. The vitestCoverageIsolation pin (issue #166) is widened for the five new root scripts, which is the review path its comment prescribes.
…tion .github/workflows/turbo.yml runs the affected task graph on pull requests and the full graph on main pushes: build, check, lint, circular, and test with --summarize, the recorded --dry=json affected graph as an artifact, turbo boundaries as an advisory step, the root gates, and run-summary uploads. Remote caching activates when TURBO_TOKEN, TURBO_TEAM, and TURBO_REMOTE_CACHE_SIGNATURE_KEY are set; without them the lane degrades to local caching. scripts/turbo-engine-boundary.mjs asserts the release-train rule (engine never depends on agent or tooling) through turbo query's GraphQL package graph, so the guarantee does not rest on Boundaries' experimental status alone. ci.yml gains turbo.yml in its actionlint args; no gate changes.
docs/build-systems/turborepo.md: what was added file by file, how each BUILD.ts property maps (targets, toolchain, cache keys, macros, drift checks, secrets, node_modules), where Turbo is ahead with measured numbers, where it cannot express the repo's invariants (package-task granularity, glob-only file model, overlapping outputs, root-task isolation, host-blind caching, unscoped Boundaries), the costs, and a concrete adoption sequence.
tsc -b writes tsconfig.tsbuildinfo inside dist/, so the extra *.tsbuildinfo output globs were redundant. Verified by restoring a deleted dist from cache in 33ms.
The comparison doc states the workflow uses TURBO_SCM_BASE for the --affected diff, but the workflow never set it and relied on merge-base detection alone. Pin it to the PR base SHA so the affected graph is exactly the PR's changes regardless of checkout merge history.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Turborepo automation for the flows workspace
One of four build-system lanes being compared (in-repo
BUILD.ts, Nx, Bazel, this one). Turborepo 2.10.11 (turbo@latest), Turbo 2 idioms throughout:tasks,//#root tasks, Package Configurations,envMode: "strict", Boundaries,turbo query,turbo prune. Nothing existing was removed — the recursive pnpm scripts andBUILD.tsare untouched, andci.ymlgains one actionlint arg.The full comparison is in
docs/build-systems/turborepo.md. This body is the summary.What is wired
turbo.json— the complete task graph over 51 workspaces:build,check,lint,format(uncached mutation),circular,test,coverage,typecheck, plus the root gates as//#tasks://#browser,//#test:scripts(the fivenode --test scripts/*.test.mjsgates as one invocation),//#docs(docs parity), and//#cargo:fmt|clippy|testfor the Rust half with inputscrates/**+vendor/jj/**+Cargo.lock+rust-toolchain.toml.^buildedges, deliberately. Workspace manifests resolveexportsto./src/*.ts, so check/lint/test/build never consumedist. Verified: all dists deleted, thentsc -pon@smthrs/flows(19 workspace deps) exits 0. The premise that project references would demand^builddoes not hold in this repo — no tsconfig declaresreferences.turbo.json(extends: ["//"]+ a Boundaries tag mapped fromsmthrs.group). Real task config only where packages differ:packages/jj(build:wasm, inputs reaching into../../cratesand../../vendor/jj,cache: false— see below),apps/server(deploydependsOn: ["smithers-ui#build"]because wrangler serves../ui/dist),apps/ui(vite build →dist/**, persistent interactive dev tasks),packages/build/infra,packages/chain,packages/evals,examples.envMode: "strict"; test-affecting vars (FC_SEED,FLOWS_SLOW_TESTS,FLOWS_FAKE_JJ, …) hashed viaenv; secrets (CLOUDFLARE_API_TOKEN,SMITHERS_CACHE_TOKEN, …) available-but-unhashed viapassThroughEnv;CI/NODE_ENVinglobalEnv.remoteCache.signature: truewired; activates withTURBO_TOKEN,TURBO_TEAM,TURBO_REMOTE_CACHE_SIGNATURE_KEY(absent → local cache, nothing fails). On pointing Turbo at the repo's own cache worker inpackages/build/infra: not today — that worker speaks an action-cache/CAS protocol (/ac+/cas, seeworker/protocol.ts), Turbo speaksGET/PUT /v8/artifacts/:hash. Same storage model, so an adapter route is a day of work, not a redesign.turbo boundaries— tag rules encode the release train:enginemay depend only onengine,agentonengine+agent,toolingon all three..github/workflows/turbo.yml—--affectedon PRs, full graph on main,--dry=jsonaffected graph recorded as an artifact,--summarizewith run-summary uploads, advisory boundaries step, root gates, and the query-based assertion.turbo query—scripts/turbo-engine-boundary.mjsasserts the release-train rule through the GraphQL package graph:engine boundary holds: 23 engine packages, 0 agent/tooling dependencies.turbo prune—turbo prune smithers-server --docker→ 15 MB subworkspace (out/jsonpruned lockfile layer +out/full20-package source closure).ui: "tui",turbo watch(verified), persistent/interactive dev tasks,--graphSVG output.Measured numbers (16-core aarch64 macOS, controlled cache dir)
buildcheck+lint+circulartest(--concurrency=2)Whole-graph local cache: 13 MB. Cache restore verified by hand (
rm -rfa dist → 33ms hit, outputs return).--affectedproof: a one-file edit inpackages/observability/srcnarrowed 51 packages → 5 (the package + its dependent closure: flows, engine-harness, cli, examples), 232 tasks → 25, measured via--dry=json.In a git worktree, Turbo silently redirects the local cache into the shared git common dir (
.../.git/modules/.turbo/cache) — all linked worktrees share one cache. Nice default; it also means clearing.turbo/cachein the worktree is not a cache clear. Ask me how I know.What does not work / honest limitations
turbo boundariesis red on this compliant repo: 210 findings. 182 are config-file imports Boundaries cannot scope out (BUILD.ts→@smthrs/targets,eslint.config.js→../../eslint.jsdoc.js; there is no include/exclude). The rest are real bugs it surfaced: 27 imports of undeclared packages (mostly@effect/platform-nodein test files, rescued by ancestornode_modulesresolution) and the pre-existing@smthrs/kernel↔@smthrs/platform-browserpackage cycle. CI runs it advisory; the query script carries enforcement.flows_jj.wasmis a reproducibility contract — a cache hit would skip the rebuild that is the gate, and the bytes are host-locked to x86_64-linux.build:wasmiscache: false; the dedicatedwasm-reproCI job stays as-is. "This task must execute even when inputs are unchanged" is inexpressible in Turbo.buildandcheckboth emitdist/**. No sandbox, no disjointness check, no same-package serialization guarantee —turbo run build checkcan interleavebuild'srmSync("dist")withcheck's emit. The CI lane runs them as separate steps. (236-task full graph verified green regardless.)--concurrency=2— same finding the repo's own shadow job encodes as--jobs 2; an unbounded local run flaked 10 suites that pass in isolation.--affected-filterable; they sit beside the graph.Lint all workspacesred on main. Also found:ci.yml's docs-parity step callspnpm exec smthrs, a bin the build-cli rename removed —//#docsinvokesnode packages/build-cli/src/main.js docs '//...'from source, same as the shadow job. Neither is caused by or scoped to this PR beyond those notes.turbo infomisreports the package manager aspnpm9(cosmetic inference bug against pnpm 11.21.0).Verdict vs
BUILD.tsTurbo is ahead on scheduling ergonomics:
--affected, TUI, watch, prune, query, worktree-shared cache, and warm runs that are effectively free. The in-repo system is ahead on everything that requires the graph to own things: typed config and macros without 45 tag files, declared toolchains, generated-and-drift-checked root files (Turbo readpnpm-workspace.yaml; pnpm auto-edited it on a machine with a global policy and nothing in Turbo noticed), secrets by name,node_modulesas a target, sub-package granularity, and the wasm contract. Details, file references, and the adoption sequence:docs/build-systems/turborepo.md.🤖 Generated with Claude Code