Refs #320
14 KiB
Extract Workflow Engine into @uncaged/workflow — Implementation Plan
For Hermes: Use subagent-driven-development skill to implement this plan task-by-task.
Goal: Extract the workflow engine (types, runtime, IPC, manager) from nerve-core and nerve-daemon into a standalone @uncaged/workflow package.
Architecture: Create packages/workflow/ as a new pnpm workspace package. Move workflow types from core and workflow runtime from daemon into it. The daemon becomes a consumer of @uncaged/workflow. No backward-compat re-exports — breaking change, update all consumers in one shot.
Tech Stack: TypeScript, pnpm workspace, rslib (bundler, same as other packages), Biome
Ref: Fixes #320
Phase 1: Scaffold packages/workflow/
Task 1: Create package skeleton
Objective: Create the @uncaged/workflow package with package.json, tsconfig, rslib config.
Files:
- Create:
packages/workflow/package.json - Create:
packages/workflow/tsconfig.json - Create:
packages/workflow/rslib.config.ts - Create:
packages/workflow/src/index.ts(empty barrel)
Steps:
-
Copy
packages/workflow-utils/package.jsonas template, change name to@uncaged/workflow, remove all dependencies except dev deps (typescript, rslib, etc.). No runtime deps initially. -
Copy
packages/workflow-utils/tsconfig.jsonandrslib.config.tsas-is (same monorepo conventions). -
Create empty
packages/workflow/src/index.ts:
// @uncaged/workflow — standalone workflow orchestration engine
- Verify:
cd packages/workflow && pnpm install && pnpm run build
- Commit:
git add packages/workflow/
git commit -m "chore(workflow): scaffold @uncaged/workflow package
Refs #320"
Phase 2: Move Types from Core
Task 2: Move workflow.ts types to @uncaged/workflow
Objective: Move all workflow types and constants from packages/core/src/workflow.ts → packages/workflow/src/types.ts.
Files:
- Create:
packages/workflow/src/types.ts - Modify:
packages/workflow/src/index.ts
Steps:
-
Copy
packages/core/src/workflow.ts→packages/workflow/src/types.tsverbatim (all 83 lines —START,END,DEFAULT_ENGINE_MAX_ROUNDS, all types). -
Export everything from
packages/workflow/src/index.ts:
export {
START,
END,
DEFAULT_ENGINE_MAX_ROUNDS,
} from "./types.js";
export type {
WorkflowMessage,
RoleResult,
Role,
RoleMeta,
StartStep,
ThreadContext,
WorkflowContext,
AgentFn,
RoleStep,
ModeratorContext,
Moderator,
WorkflowDefinition,
} from "./types.js";
- Build to verify types compile:
cd packages/workflow && pnpm run build
- Commit:
git commit -am "refactor(workflow): move workflow types from core to @uncaged/workflow
Refs #320"
Task 3: Move WorkflowConfig to @uncaged/workflow
Objective: Move the WorkflowConfig type (and its constituent types DropOverflowConfig, QueueOverflowConfig) from core/config.ts to the workflow package.
Files:
- Create:
packages/workflow/src/config.ts - Modify:
packages/workflow/src/index.ts - Modify:
packages/core/src/config.ts— removeWorkflowConfig,DropOverflowConfig,QueueOverflowConfig; import from@uncaged/workflowinstead
Steps:
- Create
packages/workflow/src/config.tswith the three types:
export type DropOverflowConfig = {
concurrency: number;
overflow: "drop";
};
export type QueueOverflowConfig = {
concurrency: number;
overflow: "queue";
maxQueue: number;
};
export type WorkflowConfig = DropOverflowConfig | QueueOverflowConfig;
-
Re-export from
packages/workflow/src/index.ts. -
In
packages/core/package.json, add dependency:"@uncaged/workflow": "workspace:*". -
In
packages/core/src/config.ts:- Remove the three type definitions
- Add
import type { WorkflowConfig, DropOverflowConfig, QueueOverflowConfig } from "@uncaged/workflow"; - Keep the re-export from
packages/core/src/index.tspointing to config.ts (which now re-exports from workflow)
-
Build entire workspace:
pnpm run build
- Commit:
git commit -am "refactor(workflow): move WorkflowConfig types to @uncaged/workflow
Refs #320"
Task 4: Remove workflow types from core, update core exports
Objective: Delete packages/core/src/workflow.ts entirely. Core re-exports workflow types from @uncaged/workflow.
Files:
- Delete:
packages/core/src/workflow.ts - Modify:
packages/core/src/index.ts— change workflow exports to re-export from@uncaged/workflow - Modify:
packages/core/src/config.ts— remove import ofDEFAULT_ENGINE_MAX_ROUNDSfrom deleted file
Steps:
-
In
packages/core/src/config.ts, replace:import { DEFAULT_ENGINE_MAX_ROUNDS } from "./workflow.js";with:
import { DEFAULT_ENGINE_MAX_ROUNDS } from "@uncaged/workflow"; -
In
packages/core/src/index.ts, replace all workflow.js imports with re-exports from@uncaged/workflow:// Workflow types — re-exported from @uncaged/workflow export { START, END, DEFAULT_ENGINE_MAX_ROUNDS, } from "@uncaged/workflow"; export type { WorkflowMessage, RoleResult, Role, RoleMeta, StartStep, ThreadContext, WorkflowContext, AgentFn, RoleStep, ModeratorContext, Moderator, WorkflowDefinition, } from "@uncaged/workflow"; -
Delete
packages/core/src/workflow.ts. -
Full build + test:
pnpm run build && pnpm test
- Commit:
git commit -am "refactor(core): remove workflow.ts, re-export from @uncaged/workflow
Refs #320"
Phase 3: Move Workflow IPC Messages
Task 5: Extract workflow IPC types to @uncaged/workflow
Objective: Move workflow-related IPC message types from packages/daemon/src/ipc.ts to packages/workflow/src/ipc.ts. Sense IPC stays in daemon.
Files:
- Create:
packages/workflow/src/ipc.ts - Modify:
packages/workflow/src/index.ts - Modify:
packages/daemon/src/ipc.ts— remove workflow IPC types, import from@uncaged/workflow
Steps:
-
Extract to
packages/workflow/src/ipc.ts:StartThreadMessage,ResumeThreadMessage,KillThreadMessageThreadLifecycleEvent,ThreadEventMessage,WorkflowErrorMessage,ThreadWorkflowMessage- Workflow-related validation logic from
parseParentToWorkerMessage - Union type for workflow parent→worker messages
-
Keep in daemon
ipc.ts:ComputeMessage,ShutdownMessage,HealthRequestMessage- Sense-related worker→parent messages
- The combined
ParentToWorkerMessageunion (imports workflow types from@uncaged/workflow)
-
Add
@uncaged/workflowas dependency topackages/daemon/package.json. -
Build + test:
pnpm run build && pnpm test
- Commit:
git commit -am "refactor(workflow): move workflow IPC types to @uncaged/workflow
Refs #320"
Phase 4: Move Workflow Runtime
Task 6: Move workflow-worker.ts to @uncaged/workflow
Objective: Move workflow execution runtime (the worker that runs inside a child process) from daemon to the workflow package.
Files:
- Move:
packages/daemon/src/workflow-worker.ts→packages/workflow/src/worker.ts - Modify:
packages/workflow/src/index.ts - Modify:
packages/daemon/— update worker spawn path
Steps:
-
Copy
workflow-worker.tstopackages/workflow/src/worker.ts. -
Update imports: replace
@uncaged/nerve-corewith local imports from./types.js,./ipc.js. -
Export the worker entry point or the worker file path from
@uncaged/workflowso daemon can spawn it. -
In daemon, update worker spawn to reference
@uncaged/workflow's worker. -
Delete
packages/daemon/src/workflow-worker.ts. -
Build + test:
pnpm run build && pnpm test
- Commit:
git commit -am "refactor(workflow): move workflow-worker runtime to @uncaged/workflow
Refs #320"
Task 7: Move workflow-manager.ts and workflow-manager-support.ts to @uncaged/workflow
Objective: Move workflow process management from daemon to workflow package.
Files:
- Move:
packages/daemon/src/workflow-manager.ts→packages/workflow/src/manager.ts - Move:
packages/daemon/src/workflow-manager-support.ts→packages/workflow/src/manager-support.ts - Modify:
packages/daemon/src/kernel.ts— import from@uncaged/workflow
Steps:
-
Copy both files to
packages/workflow/src/. -
Update imports to use local paths within the workflow package.
-
Export manager creation function from
packages/workflow/src/index.ts. -
Update
packages/daemon/src/kernel.tsto import workflow manager from@uncaged/workflow. -
Delete the original files from daemon.
-
Build + test:
pnpm run build && pnpm test
- Commit:
git commit -am "refactor(workflow): move workflow-manager to @uncaged/workflow
Refs #320"
Phase 5: Update All Consumers
Task 8: Update workflow-utils to depend on @uncaged/workflow
Objective: Change @uncaged/nerve-workflow-utils to import workflow types from @uncaged/workflow instead of @uncaged/nerve-core.
Files:
- Modify:
packages/workflow-utils/package.json— replace@uncaged/nerve-coredep with@uncaged/workflow - Modify: all
packages/workflow-utils/src/*.ts— update import paths
Steps:
-
In
package.json, replace"@uncaged/nerve-core": "workspace:*"with"@uncaged/workflow": "workspace:*". -
Find-and-replace all
from "@uncaged/nerve-core"→from "@uncaged/workflow"inpackages/workflow-utils/src/. -
Build + test:
pnpm run build && pnpm test
- Commit:
git commit -am "refactor(workflow-utils): import from @uncaged/workflow
Refs #320"
Task 9: Update workflow-meta to depend on @uncaged/workflow
Objective: Same as Task 8 but for packages/workflow-meta/.
Files:
- Modify:
packages/workflow-meta/package.json - Modify: all
packages/workflow-meta/src/**/*.ts
Steps: Same pattern as Task 8.
Task 10: Update adapter packages
Objective: Update adapter-cursor and adapter-hermes to import workflow types from @uncaged/workflow.
Files:
- Modify:
packages/adapter-cursor/src/index.ts - Modify:
packages/adapter-cursor/package.json - Modify:
packages/adapter-hermes/src/index.ts - Modify:
packages/adapter-hermes/package.json
Steps: Same pattern — add @uncaged/workflow dep, update imports.
Task 11: Update CLI package
Objective: Update CLI to import workflow types from @uncaged/workflow.
Files:
- Modify:
packages/cli/package.json - Modify:
packages/cli/src/commands/workflow.ts - Modify:
packages/cli/src/commands/thread.ts - Modify:
packages/cli/src/commands/create.ts - Modify:
packages/cli/src/workflow-agent-validation.ts - Modify: other files that import workflow types from nerve-core
Steps:
-
Add
"@uncaged/workflow": "workspace:*"topackages/cli/package.json. -
In each file, change workflow-related imports from
@uncaged/nerve-coreto@uncaged/workflow. -
Build + test:
pnpm run build && pnpm test
- Commit:
git commit -am "refactor(cli): import workflow types from @uncaged/workflow
Refs #320"
Phase 6: Clean Up Core Re-exports
Task 12: Remove workflow re-exports from @uncaged/nerve-core
Objective: Core no longer re-exports any workflow types. All consumers import directly from @uncaged/workflow.
Files:
- Modify:
packages/core/src/index.ts— remove all workflow re-exports - Modify:
packages/core/package.json— remove@uncaged/workflowdependency (core no longer needs it)
Steps:
-
Remove all workflow-related
exportlines frompackages/core/src/index.ts. -
Remove
@uncaged/workflowfrom core's dependencies. -
Full build + full test:
pnpm run build && pnpm test
- Run biome check:
pnpm run check
- Commit:
git commit -am "refactor(core): remove workflow re-exports, clean break
Fixes #320"
Phase 7: Verify & PR
Task 13: Final verification
Objective: Full workspace build, all tests pass, biome clean.
Steps:
- Clean build:
pnpm run build
- Full tests:
pnpm test
- Biome:
pnpm run check
- Verify monorepo structure matches issue spec:
packages/
core/ # @uncaged/nerve-core — sense types, config (no workflow)
workflow/ # @uncaged/workflow — standalone orchestration engine
workflow-utils/ # helper roles, extract layer
workflow-meta/ # meta-workflows
daemon/ # @uncaged/nerve-daemon — sense engine + workflow integration
cli/ # @uncaged/nerve-cli
- Create PR:
tea pr create --title "refactor: extract workflow engine into @uncaged/workflow" \
--description "## What
Extract workflow engine into standalone @uncaged/workflow package.
## Why
Workflow engine is now independent of sense observation — can be used standalone.
## Changes
- packages/workflow/ — new package with types, IPC, worker, manager
- packages/core/ — removed workflow types, no longer re-exports them
- packages/daemon/ — imports workflow runtime from @uncaged/workflow
- packages/cli/ — imports workflow types from @uncaged/workflow
- packages/workflow-utils/ — depends on @uncaged/workflow
- packages/workflow-meta/ — depends on @uncaged/workflow
- packages/adapter-*/ — depends on @uncaged/workflow
## Ref
Fixes #320"
Pitfalls & Notes
-
Worker spawn path:
workflow-worker.tsis spawned as a child process vianode. After moving to@uncaged/workflow, the daemon needs to resolve the worker entry point from the package (e.g.require.resolve("@uncaged/workflow/worker")). This likely requires a separate export inpackage.jsonexports map. -
Dynamic import exception:
workflow-worker.tsuses dynamicimport()for user workflow modules — this exception carries over to the new package. Add comment per CLAUDE.md conventions. -
IPC split: The
ipc.tsin daemon has both sense and workflow messages in one file with shared validation. The split needs careful handling of theParentToWorkerMessageunion type andparseParentToWorkerMessagefunction. -
No backward compat: Per user preference, no deprecated re-exports — straight breaking change. Phase 6 removes re-exports from core entirely.
-
workflow-utilsmay still neednerve-core: If workflow-utils imports non-workflow types (likeSchema,ExtractFn,ExtractError) from core, it will need both deps. Check carefully. -
Test files: Many test files in daemon import workflow types. They need updating in Phase 5 alongside the source files.