Workspace Spec
The workspace spec (workspace.yaml) is the kernel-level configuration file that defines the runtime environment for a LumenFlow workspace. It declares which packs are loaded, which lanes exist, and what security boundaries apply.
Portability Baseline
Section titled “Portability Baseline”LumenFlow is designed to work across multiple consumer repo shapes, but the portability contract is explicit:
- LumenFlow remains a Node-distributed CLI.
- Consumer repos do not need to be Next.js apps or pnpm monorepos.
- Framework-specific surfaces such as
apps/webdashboards are optional and should only be referenced when present or configured.
Current baseline archetypes for generic lifecycle support:
| Archetype | Expected shape | Generic lifecycle expectation |
|---|---|---|
| Single-package service repo | src/**, no apps/ or packages/ | init, wu:create, wu:brief, wu:prep, and wu:done work without monorepo-specific guidance |
| Monorepo without web app | multiple packages or services, no apps/web | gate planning and onboarding do not instruct the user to start a dashboard |
| Backend/docs-oriented repo | service code plus docs/content paths | work classification does not infer UI-only guidance from framework-like filenames alone |
Smoke verification for this baseline should confirm:
- onboarding does not advertise dashboard startup when no dashboard surface exists
- classifier defaults stay backend/docs/infra-oriented unless generic UI signals or workspace config justify UI guidance
- docs and generated workflow guidance describe these boundaries explicitly
Full Schema
Section titled “Full Schema”Root-Level Fields
Section titled “Root-Level Fields”The kernel recognizes these workspace root keys:
| Field | Type | Required | Managed By | Description |
|---|---|---|---|---|
id | string | Yes | Workspace setup | Unique workspace identifier |
name | string | Yes | Workspace setup | Human-readable workspace name |
packs | PackPin[] | Yes | pack:install | Array of pack pins to load |
lanes | LaneSpec[] | Yes | Complete JSON root via config:set; lane:edit for its existing workflows | Array of kernel lane definitions |
policies | object | No | config:set / config:unset subkeys | Workspace-level policy overrides |
security | object | Yes | Complete JSON root via config:set; optional validated deletions via config:unset | Workspace-level security config |
software_delivery | object | No | config:set (pack config) | Software Delivery pack configuration (see below) |
control_plane | object | No | config:set (validated subkeys) | Control plane connection and sync settings |
Replace the complete kernel lanes array or security object with
config:set --json-value. The strict writer rejects partial dotted mutations,
unknown keys, malformed scopes, and duplicate lane IDs before effects. Operators
supply scopes explicitly; no grants are inferred. config:unset keeps required
roots and fields protected. policies subkeys continue through validated
config:set and config:unset routes. The top-level id, name, and packs
remain managed by workspace setup and pack management. lane:edit retains its
existing two-plane behavior; software_delivery.lanes remains pack-owned and
is not changed by replacing kernel lanes.
Initializing an existing workspace
Section titled “Initializing an existing workspace”Running pnpm lumenflow --merge on an existing project fills only missing required kernel
fields from the maintained workspace defaults. Existing identity, pack pins,
top-level lanes, security settings, policies, and pack-owned configuration are preserved.
Fresh initialization does not serialize retired kernel namespaces. Existing unowned
memory_namespace and event_namespace roots, or security.network_default, are refused
before the file is rewritten; init does not silently delete them. Use sequential governed
config:unset cleanup first. Pinned pack-owned namespace roots retain their pack ownership.
Invalid explicit kernel values also leave workspace.yaml unchanged.
The software_delivery block is still merged with its maintained defaults on each init run;
existing gate overrides and delivery lane definitions take precedence. Custom pack config
roots are retained in the workspace document. Use --merge for normal existing-project
initialization; --force can replace managed workspace content and is not a policy-preserving
legacy migration.
Key categories
Section titled “Key categories”Root keys fall into three categories based on how they are modified:
- Validated roots and subkeys —
lanesandsecurityaccept complete JSON root replacement throughconfig:set;control_planeandpoliciesretain validated subkey writes. Optional deletions useconfig:unsetand validate the remaining object. - Pack config keys — Keys like
software_deliveryare declared by a pack’sconfig_keyfield in its manifest. They are written withconfig:setand validated against the pack’s config schema. - Managed roots —
id,name, andpacksremain owned by workspace setup and pack management.lanesdotted edits and its existing Software Delivery two-plane workflow remain owned bylane:edit.
Pack config keys
Section titled “Pack config keys”When a pack declares a config_key in its manifest, that key becomes a valid root-level field in workspace.yaml. For example, the Software Delivery pack declares config_key: software_delivery, which makes the software_delivery root key available.
The kernel remains domain-agnostic; Software Delivery is one domain pack. The software_delivery field is optional. If the Software Delivery pack is not pinned in the packs array, the workspace boots without it. This allows workspaces that use other domain packs (or no packs at all) to operate with a minimal kernel-only configuration.
Pack Pins
Section titled “Pack Pins”Each entry in the packs array is a pack pin — a versioned, integrity-checked reference to a domain pack that the kernel loads at startup.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Pack identifier (e.g., software-delivery) |
version | string | Yes | Semantic version (e.g., 0.1.0) |
integrity | string | Yes | sha256:<64-hex> hash or dev for local development |
source | string | Yes | One of local, git, or registry |
url | string | No | Source URL (required for git and registry sources) |
registry_url | string | No | Override the default pack registry URL |
Integrity Verification
Section titled “Integrity Verification”The integrity field ensures packs cannot be tampered with. It uses the same sha256:<hex> format as Subresource Integrity (SRI):
sha256:<64-char-hex>— Production mode. The kernel verifies the pack manifest hash matches before loading.dev— Non-production integrity mode. Production loading rejects it.
Lane Definitions
Section titled “Lane Definitions”Each entry in lanes defines a kernel lane, separate from Software Delivery lane definitions under software_delivery.lanes.definitions. Replace this complete array with pnpm config:set --key lanes --json-value '<array>'; dotted lanes.* mutation is refused, and omitted scopes never receive inferred grants. The existing lane:edit two-plane behavior for Software Delivery remains unchanged.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Lane identifier (kebab-case, e.g., framework-core) |
title | string | Yes | Human-readable name (e.g., Framework: Core) |
allowed_scopes | ToolScope[] | No | Scopes this lane grants (defaults to []) |
wip_limit | integer | No | Maximum concurrent active tasks |
policy_overrides | object | No | Lane-level policy configuration |
The lane’s allowed_scopes are used as the second level of the scope intersection algorithm:
Security
Section titled “Security”The security object defines the workspace-level permission boundary — the outermost layer of the scope intersection. Replace the complete object with pnpm config:set --key security --json-value '<object>'. Dotted security.* writes are refused; config:unset can remove only optional fields that leave the required object valid.
| Field | Type | Required | Description |
|---|---|---|---|
allowed_scopes | ToolScope[] | Yes | Maximum permissions any tool can receive |
deny_overlays | string[] | Yes | Glob patterns for unconditionally denied paths |
security.network_default is retired. Specify network posture on each network
entry in security.allowed_scopes; do not set a workspace-wide default.
The old memory_namespace and event_namespace root fields are retired and
should be removed from existing workspace files. control_plane.policy_mode is
temporarily accepted as deprecated workspace input for compatibility, but the parsed runtime configuration
ignores it and lumenflow:doctor warns while it is present. Remove it with
pnpm config:unset --key control_plane.policy_mode. It remains accepted only
for at least one release. A provided value is enum validated and stripped from effective workspace configuration. The programmatic SDK Mock policy mode and Cloud database/wire contracts are unchanged.
cloud:connect --policy-mode is likewise retired with no effect: the flag is still
accepted and validated for compatibility, prints one warning when passed, and
never writes policy_mode. Policy mode is governed by the Cloud workspace policy
setting, not by workspace.yaml.
Scope Types
Section titled “Scope Types”Scopes can be path or network types:
Network scope schema
Section titled “Network scope schema”The network scope posture field accepts three values:
| Posture | Description |
|---|---|
off | All network access blocked |
allowlist | Only traffic to listed hosts/CIDRs is permitted |
full | Unrestricted network access |
When posture is allowlist, the allowlist_entries field is required:
| Field | Type | Required | Description |
|---|---|---|---|
allowlist_entries | string[] | Yes (when posture=allowlist) | Array of permitted host:port or CIDR entries |
The kernel validates that allowlist_entries is present and non-empty when posture is allowlist. Omitting it or passing an empty array produces a Zod validation error at startup.
Network scopes participate in scope intersection across all four layers (workspace, lane, task, tool). See the scope intersection page for the deny-wins semantics and entry-level set intersection behavior.
Deny Overlays
Section titled “Deny Overlays”Paths in deny_overlays are unconditionally denied regardless of scope intersection. The kernel checks these before any other authorization step:
How commands find workspace.yaml
Section titled “How commands find workspace.yaml”Commands locate the project by walking up from the current directory and stopping at the first directory that holds workspace.yaml or .git. That walk has fixed boundaries:
- The enclosing repository wins. A
workspace.yamlin a directory above your repository is never adopted: the walk stops at the repository’s.gitfirst. - A temp directory root is never a project. The system temp directory (
os.tmpdir(), and/tmpor/var/tmpon macOS and Linux) is not treated as a project root, and the walk never climbs past it. A strayworkspace.yamlor.gitleft in the temp directory is ignored, and so is anything above a temp directory (for example, a home-directory dotfiles repository above a customTMPDIR). - An explicit project root is a boundary. When a command or API is given a project root, it reads
workspace.yamlat that root only. If the root has none, the built-in defaults apply.
Running a command from a scratch directory under the temp directory, with no project inside it, fails with a “missing workspace.yaml” error instead of silently using another project’s configuration. Run pnpm lumenflow:init there, or run the command from inside your project.
Validation
Section titled “Validation”The kernel validates workspace.yaml against WorkspaceSpecSchema (defined in @hellmai/lumenflow-kernel) at startup. Invalid specs cause initializeKernelRuntime() to throw with a Zod validation error.
Relationship to software_delivery
Section titled “Relationship to software_delivery”The software_delivery root key is a pack config block owned by the Software Delivery pack. It exists in workspace.yaml only when the pack is pinned and declares config_key: software_delivery in its manifest.
| Concern | workspace.yaml top-level (kernel) | workspace.yaml > software_delivery (pack) |
|---|---|---|
| Audience | Kernel runtime | CLI and tooling layer |
| Lanes | LaneSpec (id, scopes, wip) | Lane definitions (name, code_paths) |
| Packs | PackPin (id, version, integrity) | Not present |
| Security | Scope intersection boundaries | Git hooks, safe-git wrapper |
| Gates | Pack policies (on_completion) | Gate commands, conditional commands |
| Validation | Zod WorkspaceSpecSchema at init | Pack config schema via config:set |
Gates Configuration
Section titled “Gates Configuration”The software_delivery.gates block configures quality gate commands and conditional commands.
Conditional Commands (v3.19.0)
Section titled “Conditional Commands (v3.19.0)”Pattern-triggered commands that run only when matching files change:
| Field | Type | Required | Description |
|---|---|---|---|
trigger_patterns | string[] | Yes | Glob patterns matched against changed files (min 1) |
command | string | Yes | Shell command to execute |
severity | string | No | error (default), warn, or off |
fresh_on_completion | boolean | No | Rerun at wu:done after reconciliation; defaults to false |
guidance | string | No | Actionable text shown on failure |
guidance_ref | string | No | File path whose content is appended to guidance on failure |
Fresh completion commands ignore otherwise valid preparation-checkpoint reuse and fail closed if they fail or cannot run. Their completion flow evidence records the WU, reconciled commit and diff, command, completion timestamp, and result. Conditional commands without the flag retain ordinary checkpoint reuse.
See Gates for usage details.
Next Steps
Section titled “Next Steps”- Kernel Runtime — How the workspace spec is loaded and used
- Packs — Domain pack architecture and the Software Delivery Pack
- Scope Intersection — How workspace security becomes effective permissions
- Configuration —
software_deliveryconfiguration reference