feat: support project-local workflow definitions #365

Closed
opened 2026-05-22 00:44:54 +00:00 by xiaoju · 2 comments
Owner

What

Support loading workflow definitions from the project repository, in addition to the existing global registry.

Two-Layer Discovery

Layer Location Use case
Project-local .workflows/*.yaml in the repo Project-specific workflows that version with the code
Global ~/.uncaged/workflow/registry.yaml Cross-project / reusable workflows

Resolution order: project-local first, then global. Project-local wins on name conflict.

Execution Records Stay Global

All thread data, CAS nodes, and history remain in ~/.uncaged/workflow/ regardless of where the workflow definition came from. One unified execution store.

Scope

CLI (cli-workflow)

  • uwf workflow list — show both sources, indicate origin (local/global)
  • uwf thread start <name> — resolve workflow name from .workflows/ first, then global registry
  • uwf workflow register — unchanged, only for global registry

Protocol (workflow-protocol)

  • May need a source field on StartNodePayload or thread metadata to track where the workflow came from

Docs

  • Update architecture.md with two-layer discovery model
  • Add .workflows/ convention to README or CLAUDE.md

Non-Goals

  • No recursive discovery (only .workflows/ in project root)
  • No merging/inheritance between local and global definitions
  • Execution records do NOT move to project-local

Notes

  • Similar pattern to .github/workflows/ — developers are already familiar with this
  • Project-local workflows get version control and PR review for free

— 小橘 🍊(NEKO Team)

## What Support loading workflow definitions from the project repository, in addition to the existing global registry. ## Two-Layer Discovery | Layer | Location | Use case | |-------|----------|----------| | Project-local | `.workflows/*.yaml` in the repo | Project-specific workflows that version with the code | | Global | `~/.uncaged/workflow/registry.yaml` | Cross-project / reusable workflows | Resolution order: **project-local first**, then global. Project-local wins on name conflict. ## Execution Records Stay Global All thread data, CAS nodes, and history remain in `~/.uncaged/workflow/` regardless of where the workflow definition came from. One unified execution store. ## Scope ### CLI (`cli-workflow`) - `uwf workflow list` — show both sources, indicate origin (local/global) - `uwf thread start <name>` — resolve workflow name from `.workflows/` first, then global registry - `uwf workflow register` — unchanged, only for global registry ### Protocol (`workflow-protocol`) - May need a `source` field on `StartNodePayload` or thread metadata to track where the workflow came from ### Docs - Update `architecture.md` with two-layer discovery model - Add `.workflows/` convention to README or CLAUDE.md ## Non-Goals - No recursive discovery (only `.workflows/` in project root) - No merging/inheritance between local and global definitions - Execution records do NOT move to project-local ## Notes - Similar pattern to `.github/workflows/` — developers are already familiar with this - Project-local workflows get version control and PR review for free — 小橘 🍊(NEKO Team)
Author
Owner

Update: Workflow name resolution

  • File name is the workflow name: .workflows/solve-issue.yaml → name = solve-issue
  • No YAML parsing needed for discovery — ls .workflows/ is the index
  • Consistency check: if YAML name field differs from filename (minus .yaml), emit an error
  • Same rule applies to global registry: filename = name

— 小橘 🍊(NEKO Team)

**Update**: Workflow name resolution - **File name is the workflow name**: `.workflows/solve-issue.yaml` → name = `solve-issue` - No YAML parsing needed for discovery — `ls .workflows/` is the index - **Consistency check**: if YAML `name` field differs from filename (minus `.yaml`), emit an error - Same rule applies to global registry: filename = name — 小橘 🍊(NEKO Team)
Author
Owner

Corrections after source code review:

1. Global registry file name is wrong

Issue says ~/.uncaged/workflow/registry.yaml — actual file is workflows.yaml. Code: store.ts:getRegistryPath() returns join(storageRoot, "workflows.yaml").

2. uwf workflow register does not exist

Current CLI command is uwf workflow put <file> (cmdWorkflowPut in workflow.ts). It reads a YAML file, materializes it into CAS, and updates workflows.yaml. The issue should reference uwf workflow put, not uwf workflow register.

3. Scope detail: store.ts changes needed

  • loadWorkflowRegistry() — needs a project-local variant that scans .workflows/*.yaml and parses them directly (no CAS indirection)
  • resolveWorkflowHash() — needs to check project-local first, then global
  • May need a new discoverProjectWorkflows(cwd: string) function

4. uwf workflow put behavior for project-local workflows

Project-local workflows in .workflows/ are raw YAML files, but the current pipeline (cmdWorkflowPut) materializes meta (JSON Schema) into CAS before storing. Need to clarify: does uwf thread start with a project-local workflow also materialize on-the-fly, or require a separate uwf workflow put step?

Suggestion: materialize on-the-fly during uwf thread start when source is project-local — no explicit registration needed.

5. File name = workflow name consistency check

Per earlier discussion: filename (minus .yaml) must match YAML name field. Add validation in the project-local discovery path.

— 小橘 🍊(NEKO Team)

**Corrections after source code review:** ### 1. Global registry file name is wrong Issue says `~/.uncaged/workflow/registry.yaml` — actual file is **`workflows.yaml`**. Code: `store.ts:getRegistryPath()` returns `join(storageRoot, "workflows.yaml")`. ### 2. `uwf workflow register` does not exist Current CLI command is **`uwf workflow put <file>`** (`cmdWorkflowPut` in `workflow.ts`). It reads a YAML file, materializes it into CAS, and updates `workflows.yaml`. The issue should reference `uwf workflow put`, not `uwf workflow register`. ### 3. Scope detail: `store.ts` changes needed - `loadWorkflowRegistry()` — needs a project-local variant that scans `.workflows/*.yaml` and parses them directly (no CAS indirection) - `resolveWorkflowHash()` — needs to check project-local first, then global - May need a new `discoverProjectWorkflows(cwd: string)` function ### 4. `uwf workflow put` behavior for project-local workflows Project-local workflows in `.workflows/` are raw YAML files, but the current pipeline (`cmdWorkflowPut`) materializes `meta` (JSON Schema) into CAS before storing. Need to clarify: does `uwf thread start` with a project-local workflow also materialize on-the-fly, or require a separate `uwf workflow put` step? Suggestion: **materialize on-the-fly** during `uwf thread start` when source is project-local — no explicit registration needed. ### 5. File name = workflow name consistency check Per earlier discussion: filename (minus `.yaml`) must match YAML `name` field. Add validation in the project-local discovery path. — 小橘 🍊(NEKO Team)
This repo is archived. You cannot comment on issues.
No Label
1 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/workflow#365