This repository has been archived on 2026-06-01. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
nerve/docs/plans/2026-05-05-extract-workflow-package.md
T

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:

  1. Copy packages/workflow-utils/package.json as template, change name to @uncaged/workflow, remove all dependencies except dev deps (typescript, rslib, etc.). No runtime deps initially.

  2. Copy packages/workflow-utils/tsconfig.json and rslib.config.ts as-is (same monorepo conventions).

  3. Create empty packages/workflow/src/index.ts:

// @uncaged/workflow — standalone workflow orchestration engine
  1. Verify:
cd packages/workflow && pnpm install && pnpm run build
  1. 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.tspackages/workflow/src/types.ts.

Files:

  • Create: packages/workflow/src/types.ts
  • Modify: packages/workflow/src/index.ts

Steps:

  1. Copy packages/core/src/workflow.tspackages/workflow/src/types.ts verbatim (all 83 lines — START, END, DEFAULT_ENGINE_MAX_ROUNDS, all types).

  2. 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";
  1. Build to verify types compile:
cd packages/workflow && pnpm run build
  1. 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 — remove WorkflowConfig, DropOverflowConfig, QueueOverflowConfig; import from @uncaged/workflow instead

Steps:

  1. Create packages/workflow/src/config.ts with the three types:
export type DropOverflowConfig = {
  concurrency: number;
  overflow: "drop";
};

export type QueueOverflowConfig = {
  concurrency: number;
  overflow: "queue";
  maxQueue: number;
};

export type WorkflowConfig = DropOverflowConfig | QueueOverflowConfig;
  1. Re-export from packages/workflow/src/index.ts.

  2. In packages/core/package.json, add dependency: "@uncaged/workflow": "workspace:*".

  3. 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.ts pointing to config.ts (which now re-exports from workflow)
  4. Build entire workspace:

pnpm run build
  1. 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 of DEFAULT_ENGINE_MAX_ROUNDS from deleted file

Steps:

  1. In packages/core/src/config.ts, replace:

    import { DEFAULT_ENGINE_MAX_ROUNDS } from "./workflow.js";
    

    with:

    import { DEFAULT_ENGINE_MAX_ROUNDS } from "@uncaged/workflow";
    
  2. 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";
    
  3. Delete packages/core/src/workflow.ts.

  4. Full build + test:

pnpm run build && pnpm test
  1. 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:

  1. Extract to packages/workflow/src/ipc.ts:

    • StartThreadMessage, ResumeThreadMessage, KillThreadMessage
    • ThreadLifecycleEvent, ThreadEventMessage, WorkflowErrorMessage, ThreadWorkflowMessage
    • Workflow-related validation logic from parseParentToWorkerMessage
    • Union type for workflow parent→worker messages
  2. Keep in daemon ipc.ts:

    • ComputeMessage, ShutdownMessage, HealthRequestMessage
    • Sense-related worker→parent messages
    • The combined ParentToWorkerMessage union (imports workflow types from @uncaged/workflow)
  3. Add @uncaged/workflow as dependency to packages/daemon/package.json.

  4. Build + test:

pnpm run build && pnpm test
  1. 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.tspackages/workflow/src/worker.ts
  • Modify: packages/workflow/src/index.ts
  • Modify: packages/daemon/ — update worker spawn path

Steps:

  1. Copy workflow-worker.ts to packages/workflow/src/worker.ts.

  2. Update imports: replace @uncaged/nerve-core with local imports from ./types.js, ./ipc.js.

  3. Export the worker entry point or the worker file path from @uncaged/workflow so daemon can spawn it.

  4. In daemon, update worker spawn to reference @uncaged/workflow's worker.

  5. Delete packages/daemon/src/workflow-worker.ts.

  6. Build + test:

pnpm run build && pnpm test
  1. 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.tspackages/workflow/src/manager.ts
  • Move: packages/daemon/src/workflow-manager-support.tspackages/workflow/src/manager-support.ts
  • Modify: packages/daemon/src/kernel.ts — import from @uncaged/workflow

Steps:

  1. Copy both files to packages/workflow/src/.

  2. Update imports to use local paths within the workflow package.

  3. Export manager creation function from packages/workflow/src/index.ts.

  4. Update packages/daemon/src/kernel.ts to import workflow manager from @uncaged/workflow.

  5. Delete the original files from daemon.

  6. Build + test:

pnpm run build && pnpm test
  1. 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-core dep with @uncaged/workflow
  • Modify: all packages/workflow-utils/src/*.ts — update import paths

Steps:

  1. In package.json, replace "@uncaged/nerve-core": "workspace:*" with "@uncaged/workflow": "workspace:*".

  2. Find-and-replace all from "@uncaged/nerve-core"from "@uncaged/workflow" in packages/workflow-utils/src/.

  3. Build + test:

pnpm run build && pnpm test
  1. 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:

  1. Add "@uncaged/workflow": "workspace:*" to packages/cli/package.json.

  2. In each file, change workflow-related imports from @uncaged/nerve-core to @uncaged/workflow.

  3. Build + test:

pnpm run build && pnpm test
  1. 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/workflow dependency (core no longer needs it)

Steps:

  1. Remove all workflow-related export lines from packages/core/src/index.ts.

  2. Remove @uncaged/workflow from core's dependencies.

  3. Full build + full test:

pnpm run build && pnpm test
  1. Run biome check:
pnpm run check
  1. 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:

  1. Clean build:
pnpm run build
  1. Full tests:
pnpm test
  1. Biome:
pnpm run check
  1. 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
  1. 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

  1. Worker spawn path: workflow-worker.ts is spawned as a child process via node. 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 in package.json exports map.

  2. Dynamic import exception: workflow-worker.ts uses dynamic import() for user workflow modules — this exception carries over to the new package. Add comment per CLAUDE.md conventions.

  3. IPC split: The ipc.ts in daemon has both sense and workflow messages in one file with shared validation. The split needs careful handling of the ParentToWorkerMessage union type and parseParentToWorkerMessage function.

  4. No backward compat: Per user preference, no deprecated re-exports — straight breaking change. Phase 6 removes re-exports from core entirely.

  5. workflow-utils may still need nerve-core: If workflow-utils imports non-workflow types (like Schema, ExtractFn, ExtractError) from core, it will need both deps. Check carefully.

  6. Test files: Many test files in daemon import workflow types. They need updating in Phase 5 alongside the source files.