eeb5fa0d75
- CLAUDE.md: functional-first, type over interface, no optional props, Result type - biome.json: lint + format config (Biome 1.9) - tsconfig.json: strict mode, composite project references - .cursor/rules: global conventions + no-dynamic-import Adapted from uncaged/nerve for bun workspace. 小橘 <xiaoju@shazhou.work>
4.7 KiB
4.7 KiB
Workflow Coding Conventions
Project Overview
@uncaged/workflow is a workflow engine that executes single-file ESM bundles. Each workflow is a self-contained .esm.js file with an XXH64 hash as its version identifier.
Key Terms
| Concept | What it is |
|---|---|
| Workflow | A single-file ESM module that default-exports a workflow function. Identified by its XXH64 hash (Crockford Base32). |
| Bundle | The physical .esm.js file stored in ~/.uncaged/workflow/bundles/. |
| Thread | A single execution of a workflow, identified by a ULID. Persisted as .data.jsonl + .info.jsonl. |
| Role | A named actor within a workflow. Each role produces output with typed meta. |
| Registry | workflow.yaml — maps workflow names to current/historical bundle hashes. |
Monorepo Structure
workflow/
packages/
workflow/ # @uncaged/workflow — core lib (types, hash, ULID, JSONL, registry)
cli-workflow/ # @uncaged/cli-workflow — CLI (uncaged-workflow command)
docs/ # RFCs, conventions
biome.json # root Biome config
tsconfig.json # root TypeScript config
workflowis the core;cli-workflowdepends on it- Packages use
workspace:*protocol
Language & Paradigm
Functional-first
Use function + type, not class + interface.
// ✅ Good
type ThreadStart = {
name: string;
hash: string;
threadId: string;
timestamp: number;
};
function createThreadStart(name: string, hash: string, threadId: string): ThreadStart {
return { name, hash, threadId, timestamp: Date.now() };
}
// ❌ Bad — no class, no interface
class ThreadStart implements IThreadStart { ... }
Rules
| Rule | Description |
|---|---|
type over interface |
All type definitions use type |
function over class |
Pure functions + closures, no class |
No this |
Functions must not depend on this context |
| No inheritance | No extends, implements, abstract |
| Composition over inheritance | Use function composition |
| Immutability first | Use Readonly<T>, as const, avoid mutation |
| No optional properties | Use T | null instead of ?: — see below |
Exceptions
Classes are allowed when:
- Required by a third-party library
- Error subclasses (
class WorkflowError extends Error)
No Optional Properties
Never use ?:. All nullable fields must be explicit T | null.
// ✅ Good
type WorkflowEntry = {
hash: string;
timestamp: number;
description: string | null;
};
// ❌ Bad
type WorkflowEntry = {
hash: string;
timestamp: number;
description?: string;
};
Modules & Exports
- Always named exports, never default exports
- One module = one responsibility, filename = purpose
Exception: Workflow bundle files (.esm.js) use default export by design — this is the user-authored extension point.
Naming
| Type | Style | Example |
|---|---|---|
| Files | kebab-case | thread-manager.ts |
| Types | PascalCase | ThreadState |
| Functions/variables | camelCase | createThread |
| Constants | UPPER_SNAKE | MAX_ROUNDS |
| Generics | Single letter or descriptive | T, TMeta |
Workflow Naming
Workflow names use verb-first kebab-case:
- ✅
solve-issue,extract-knowledge,review-code - ❌
knowledge-extraction,issue-solver
ID Encoding
All IDs use Crockford Base32:
- Bundle hash: XXH64 → 13-char Crockford Base32
- Thread ID: ULID → 26-char Crockford Base32 (10 timestamp + 16 random)
Error Handling
- Use
Resulttype for expected failures throwonly for unrecoverable bugs (programmer errors)- No try-catch for flow control
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
Async
- Always
async/await, never.then()chains
No Dynamic Import
Do NOT use await import() in production code. Always use static top-level import.
Exception: The bundle loader must dynamically import user workflow files at runtime.
// Dynamic import required: user bundle path resolved at runtime
const mod = await import(bundlePath);
Test files (__tests__/**) are exempt.
Toolchain
| Tool | Purpose |
|---|---|
| bun | Package manager + runtime + test runner |
| TypeScript | Type checking (strict mode) |
| Biome | Lint + format (replaces ESLint + Prettier) |
Commands
bun run check # biome check (lint + format)
bun run format # biome format --write
bun run build # full build
bun test # run tests
Commit Convention
<type>(<scope>): <description>
type: feat | fix | refactor | docs | chore | test
scope: workflow | cli | rfc-001 | ...