Skip to content

Work Units (WUs)

A Work Unit (WU) is a small, well-defined piece of work with clear acceptance criteria and proof of completion.

Traditional tickets are:

  • Vague (“Fix the login bug”)
  • Unbounded (no clear definition of done)
  • Trust-based (marked done without proof)

WUs are:

  • Specific (“Add email validation to login form”)
  • Cohesive (one independently shippable and rollbackable outcome)
  • Evidence-based (tests and artifacts prove completion)
id: WU-0042
title: Add email validation to login form
lane: 'Experience: UI'
type: feature
status: ready
priority: P2
exposure: ui

description: |
  Add client-side email validation before form submission.
  Show inline error message for invalid formats.

acceptance:
  - Email field validates on blur
  - Invalid emails show "Please enter a valid email"
  - Valid emails allow form submission
  - Unit test covers validation logic

test_paths:
  unit:
    - src/components/__tests__/login-form.test.ts
  e2e:
    - e2e/login.spec.ts

code_paths:
  - src/components/LoginForm.tsx
  - src/utils/validation.ts

external_work_items:
  - provider_id: hub
    item_ids:
      - PBI-123
      - SNAG-456
    desired_final_status: ReadyForReview
    lifecycle_intent: ready_for_review
    closeout_on: wu_done

Every Work Unit has a canonical id made of a configurable prefix and a zero-padded integer. Defaults preserve backward compatibility — every existing LumenFlow repo keeps emitting WU- prefixed ids without any config change.

A canonical id is <prefix><zero-padded-integer> where:

  • prefix matches /^[A-Z][A-Z0-9]{1,9}-$/ (default 'WU-').
  • the integer body is at least width characters wide (default 4).
  • the integer body may be wider than width once the sequence overflows (e.g. WU-10000 is canonical even when width=4); the sequence never truncates.

wu:create canonicalizes --id input before any file is written — passing --id WU-14 writes WU-0014.yaml. Auto-generated ids are zero-padded too.

Two different string forms of the same integer cannot coexist: WU-14 and WU-0014 resolve to the same canonical integer and wu:create hard-errors if the integer is already represented anywhere on disk. The error ships with a copy-pasteable wu:rename remediation command.

Canonical ID behaviour is controlled by software_delivery.wu_id.* in workspace.yaml:

software_delivery:
  wu_id:
    width: 4 # 1-6; default 4 → WU-0001
    strict: true # enforce canonical format at creation; lumenflow:doctor --deep flags drift
    prefix: 'WU-' # pattern /^[A-Z][A-Z0-9]{1,9}-$/; try TASK-, STORY-, TICKET-, …
# Fresh repo, no config changes — auto-generated ids are WU-0001, WU-0002, …
pnpm wu:create --lane "Experience: UI" --title "Add login form"
# ↳ WU-0001

# --id input is canonicalized before any file is written:
pnpm wu:create --id WU-14 --title "…"
# ↳ writes WU-0014.yaml with id: WU-0014

Teams that prefer a non-WU- prefix can pick a different one without touching any WU source file:

pnpm config:set --key software_delivery.wu_id.prefix --value TASK-
pnpm wu:create --lane "Experience: UI" --title "Add login form"
# ↳ TASK-0001

pnpm wu:create --id TASK-14 --title "…"
# ↳ writes TASK-0014.yaml with id: TASK-0014

pnpm lumenflow:doctor --deep flags any WU YAML whose id is valid but not canonical (e.g. WU-5.yaml when width=4). Offenders come with copy-pasteable repair commands:

pnpm wu:rename --from WU-5 --to WU-0005

wu:rename moves the YAML, rewrites the id: field, moves the .done stamp, appends a wu_renamed event to .lumenflow/state/packs/software-delivery/wu-events.jsonl, and regenerates backlog.md and status.md. When a downstream repo has already canonicalized its YAML out-of-band but the generated views still regenerate with legacy ids, use --replay-only to append the event without touching the filesystem:

pnpm wu:rename --from WU-5 --to WU-0005 --replay-only

Either form is append-only — history is never rewritten. Historical events retain their original wuId; a replay-time projection resolves them to the new canonical id so generated views only show the post-rename form.

  • strict: true (default) — lumenflow:doctor --deep treats drift as exit code 1, wu:rename’s CLI guard refuses a non-canonical --to.
  • strict: false — legacy unpadded ids are tolerated (warning-only). Use this as a migration ramp for repos that cannot normalize in one go; integer-level collision detection still hard-blocks same-integer duplicates.
ready → in_progress → [blocked] → done
StatusMeaning
readyApproved, can be claimed
in_progressSomeone is working on it
blockedWaiting on external dependency
doneAcceptance met, stamp created

Lifecycle commands reserve stdout for their own result content so scripts and agent callers can safely pipe or parse it. Coordination-signal notices come from the shared lifecycle middleware on stderr, with a bounded three-item preview, the unread count, and a pointer to pnpm mem:inbox. In particular, wu:claim does not print a second signal banner: its claim output helper is silent, preventing a large signal backlog from corrupting stdout. This is an internal output behavior; command names, options, arguments, and --help output are unchanged.

wu:create --type accepts seven canonical WU types:

TypeUse it for
featureNet-new user or system capability
bugBroken or regressed behavior fix
documentationDocumentation-only change
processWorkflow or operating-process maintenance
toolingTooling, automation, or developer-experience work
choreRepository upkeep or housekeeping
refactorBehavior-preserving code restructure

Two rules matter in practice:

  • feature WUs require spec_refs. A plan link or spec reference is part of the contract, not an optional note.
  • documentation and process are the non-code WU types. Use them when the work truly changes docs or operating workflow without touching runtime behavior.

One coherent outcome stays in one WU. Use checkpoint-resume when it will exceed a single session, and use an orchestrated strategy for complex atomic work. Split only when the pieces can ship, review, and roll back independently.

❌ “Implement user authentication system”

✅ “Add login form UI” ✅ “Add password validation” ✅ “Connect login to auth API” ✅ “Add session persistence”

See Sizing Guide for detailed heuristics.

In LumenFlow:

TraditionalLumenFlow
Create ticket → Code → Open PR → Review → MergeCreate WU → Claim → Code in worktree → Gates → Done

The “review” is automated gates, not human PR review. This unblocks flow while maintaining quality.

Some WUs correspond to external QA or work-item systems: Skynexe Hub, Jira, Linear, GitHub Issues, Azure DevOps, or a project-local review queue. Do not put “run this close-out command” in notes. Notes are context, not an enforcement boundary.

Use external_work_items for structured metadata:

FieldMeaning
provider_idProvider config key, such as hub, jira, linear, github, ado
item_idsOne or more external item identifiers
desired_final_statusStatus LumenFlow should set after successful local wu:done
lifecycle_intentimplementation_done, ready_for_review, or custom
closeout_onwu_done for automatic lifecycle-owned close-out, or manual
rollback_guidanceOptional reconciliation guidance shown/audited when close-out fails

Provider commands and APIs are configured under software_delivery.external_work_items using pnpm config:set, not by hand-editing workspace.yaml. wu:brief shows the external items as context, but workers must not run provider mutation commands. Successful local wu:done runs the configured close-out adapter, verifies the readback adapter, and writes .lumenflow/telemetry/external-closeout.ndjson.

Review systems can distinguish “ready for review” from final verdict. For example, a Hub provider can set implementation to PBI Done / origin snag ReadyForReview while reserving Pass/Fail for the PO or user.

LumenFlow provides two levels of completion evidence:

Every tool call during a WU’s execution is recorded in the evidence store:

  • Content-addressed inputs — Tool inputs are hashed with SHA-256 and stored as immutable blobs
  • Tool tracesTOOL_CALL_STARTED and TOOL_CALL_FINISHED entries with timing, scope enforcement results, and policy decisions
  • Denial records — Even denied tool calls produce evidence, proving what the agent attempted

At completion, wu:done creates:

  1. Stamp file in .lumenflow/state/packs/software-delivery/stamps/WU-XXX.done
  2. Test results proving acceptance criteria
  3. Git history linking to the WU spec

This audit trail answers: What was the agent asked to do? Was the action authorized? What actually happened? See Evidence Store for the full model.

# With required flags
pnpm wu:create \
  --title "Add password strength meter" \
  --lane "Experience: UI" \
  --type feature \
  --exposure ui \
  --description "Show password strength indicator during registration" \
  --acceptance "Strength meter shows weak/medium/strong" \
  --acceptance "Meter updates in real-time as user types" \
  --notes "Reuse the existing password policy and form tokens" \
  --code-paths "src/components/PasswordStrength.tsx" \
  --test-paths-unit "src/components/__tests__/password-strength.test.ts" \
  --test-paths-manual "Navigate to Registration with a keyboard and verify the labelled meter updates without disrupting focus" \
  --plan

The command prints the generated ID. The created WU goes into docs/operations/tasks/wu/WU-XXX.yaml with status ready.

Plan-less conversations: If your plan lives in chat or a meeting, keep it lightweight: use --plan to generate a stub at lumenflow://plans/WU-XXX-plan.md, summarize the plan there, and reference it in spec_refs. Feature WUs require spec_refs; notes do not replace the plan link.

  • Lanes — Organizing work streams with scope boundaries
  • Gates — Quality enforcement as kernel policies
  • Evidence Store — The immutable audit trail behind every WU
  • Kernel Runtime — How WU tasks map to kernel task lifecycle
  • Sizing Guide — How to size WUs properly