workspace-layout.decision-log.md
May 21, 2026 · View on GitHub
Title: Workspace layout — relocate libraries to packages/
Proposed by: Kuba Skibiński
Date: 05.05.2026
Context
§2 of packages/sdk/sdk-restructuring.decision-log.md set the workspace rule: apps/ for runnable apps, packages/ for libraries. Two workspaces were violating that rule:
apps/execution-core/is a library. Itspackage.jsondeclares onlyexports, nodevorstartscript, and is consumed byapps/backendandapps/execution-workerviaworkspace:*.apps/types/is a library. Itspackage.jsondeclaresexports: "./*"and nothing else; consumed by the backend, the worker, and the SDK.
Both belonged under packages/. Leaving them in apps/ meant a new contributor could not tell libraries from runnable apps by directory alone — undermining the rule §2 had just set. With apps/ai-studio recently added by the AI Studio extraction PR, the contrast was sharpening: that directory is unambiguously runnable, while these two siblings sitting next to it are not.
Decision
Two directory-level moves, no package-name renames, no API changes:
apps/execution-core/→packages/execution-core/. Already package-shaped (exports, nodevscript).apps/types/→packages/types/. Already package-shaped (exports: "./*", no scripts beyond typecheck/lint).
apps/backend/ and apps/execution-worker/ keep their names. Both are runnable, both belong in apps/, neither needed a rename to communicate that.
Cascade updates kept in scope:
pnpm-workspace.yamlglobs (./apps/*,./packages/*) already cover both target locations — no change.pnpm-lock.yamlregenerated; importer keys auto-updated to the new directory paths.- Root
package.json: no script changes required — every filter expression resolves by package name (which is preserved), not by directory path. knip.config.jsworkspace keys repointed.tools/check-env.mjsenv file paths needed no change (onlyapps/backendandapps/execution-workerpaths, neither moved).- README references in the root, the moved READMEs, the root
CLAUDE.md, and the four decision logs that referenced the moved paths (local-dev-binding,cancellation-handling,topological-scheduling,decision-no-match) updated to current paths. The.claude/commands/wb.{add-execution-handler,create-node,run-locally}.mdskill files that referenced the old paths updated likewise. The comment inapps/backend/src/domain/mapper/snapshot-schema.tspointing atapps/execution-core/src/executors/decision.tsrepointed atpackages/execution-core/.... - Pre-existing stale sentence in
packages/sdk/src/features/json-form/form-generation.mdpointed atapps/backend/src/diagram/data/mocks/mocked.data.ts, which had not existed since the SDK restructuring. Removed the sentence rather than maintaining a confidently-worded pointer at nothing. DECISION-LOGS.mdregenerated viapnpm -F tools collect-decision-logs.
Alternative Options Considered
- Rename the runnable apps too (
apps/backend→apps/reference-backend,apps/execution-worker→apps/reference-workeror justapps/worker). Rejected. The reference-implementation framing is already carried by three high-volume doc surfaces (backend README header, root README warning,local-dev-bindingdecision log); encoding it in the directory name as well pays no marginal dividend and lengthens every script and path reference. Shorteningexecution-workertoworkeris similarly cosmetic —execution-workerreads cleanly and matches the npm package name, so the churn pays for nothing. - Move
apps/icons/topackages/icons/in the same round. Rejected.apps/iconshas its own pending decisions in two existing decision logs; bundling its move here would conflate two distinct conversations. - Introduce a published
backend-sdkpackage now. Rejected. The frontend SDK has obvious standalone value (an embeddable graph editor). A backend SDK would mostly be the contract (ports + event types), and that contract already lives inpackages/execution-core. With no second consumer to validate the API shape against, designing in the void violates §4 of the SDK decision log. Revisit when a non-Temporal consumer arrives. - Defer the move and only update READMEs. Rejected. The directory name is what a contributor sees first; docs are not a substitute for a layout that already says the right thing. §10 of the SDK decision log calls out the same lesson: stale paths survive in
*.mdxand top-level docs because nothing typechecks them, so the structural fix has to land structurally.
Consequences
-
Pros
- The §2 rule is now visible from the directory tree alone.
apps/≡ runnable,packages/≡ library — no exceptions, no mental subtraction. - Blame preserved. Renames registered in
git status;git log --followwalks across the moves. - No API surface change. Every consumer keeps
workspace:*against the same npm package names; nothing outside the moved directories needed import-path edits beyond doc/comment references to the directory layout itself. - Stale-path debt paid down on the way through. The four decision logs that referenced the moved paths, the root
CLAUDE.md, the three.claude/commands/wb.*.mdskill files, and the danglingmocked.data.tssentence inform-generation.mdall got fixed in the same diff. Nothing new added to the stale-path pile. - Minimal blast radius. Only the two libraries moved; the runnable apps are untouched. No script renames, no Docker/CI path updates, no orchestrator changes.
- The §2 rule is now visible from the directory tree alone.
-
Cons
- Branch conflicts with any in-flight refactor touching the same files. Anything that modifies the moved decision logs, the root
CLAUDE.md, or the four.claude/commands/wb.*.mdskill files will conflict on rebase; whichever lands second carries the small mechanical merge. No semantic surprises — the conflicts are pure path substitutions.
- Branch conflicts with any in-flight refactor touching the same files. Anything that modifies the moved decision logs, the root
Status
Implemented. Verified:
pnpm installclean — pnpm-lock.yaml importer keys repointed at the new paths automatically.pnpm typecheckclean across the seven touched workspaces (sdk, demo, ai-studio, backend, execution-worker, execution-core, types). Pre-existingapps/docstypecheck errors on.astrofiles are present on master too and unrelated to this PR.pnpm lintclean across the seven touched workspaces. Same pre-existingapps/docsparsing errors on.astrofiles.pnpm testclean — all 133 tests pass (116 SDK + 17 execution-core).pnpm knipclean.git statusshows the two moves as renames, blame preservation confirmed.- Repo-wide grep for
apps/types|apps/execution-corereturns only the historical paragraph inpackages/sdk/sdk-restructuring.decision-log.md§10 (intentionally left as historical context describing the section 1–3 migration).
Out of scope (deferred follow-ups)
apps/icons→packages/iconsmove. Deferred behind its existing decision logs.- Reference-implementation framing in directory names. Considered and rejected (see Alternative Options Considered).
- Pre-existing stale
apps/frontend/...references indocs/using-app-as-component.mdand the docs-site.mdxfiles. Survived the §3 rename in the SDK restructuring; out of scope here, belongs to a docs-side cleanup pass. - Pre-existing
apps/docslint and typecheck errors on.astrofiles. Unrelated to this PR; visible on master.
Accepted.