- Add multi-task run support (#162)
- Add Windows daemon support (#163)
- Add zsh task completion plugin docs and script (#149)- Add kiro-cli as supported ACP execution runtime (#160)- Discover task files recursively in nested subdirectories (#153)
- Homebrew formula- Emit one task slug per compozy completion candidate (#159)- Run managed upgrade commands (#158)
- Add star history on readme- Release notes
- Release fix
- Internal test fix
Compozy now ships first-class support for the Kiro CLI as an ACP execution runtime, alongside Claude, Codex, Cursor, and Droid. Selecting --ide kiro (or persisting it in workspace config) is enough — Compozy locates kiro-cli, probes the ACP adapter, and wires the correct bootstrap arguments per session.
- New IDE constant
kiroand default modelanthropic/claude-opus-4-6(internal/core/model/constants.go). - New registry entry under
internal/core/agent/registry_specs.goregistering the Kiro runtime with:- Command:
kiro-cli - Fixed args:
acp - Probe:
kiro-cli acp --help - Setup agent name:
kiro-cli - Docs: https://kiro.dev/docs/cli/acp
- Command:
- Bootstrap forwards
--model <name>when set and appends-awhen the run uses--access-mode full.
# One-off run
compozy tasks run my-task --ide kiro --model anthropic/claude-opus-4-6
# Per-workspace default
[tasks.run]
ide = "kiro"
model = "anthropic/claude-opus-4-6"Install the Kiro CLI and ensure kiro-cli acp is reachable on PATH. Compozy's preflight probes the adapter on first run and surfaces a clear InstallHint if the binary is missing.
| IDE | CLI flag | Default model | ACP command |
|---|---|---|---|
| Kiro CLI | --ide kiro |
anthropic/claude-opus-4-6 |
kiro-cli acp |
compozy tasks run can now discover task_NNN.md files in nested subdirectories under .compozy/tasks/<slug>/, so multi-feature workflows can be organized by area instead of flattened into a single root. The behavior is opt-in via a new --recursive / -r flag — existing flat workflows are byte-identical to before.
# One-off
compozy tasks run my-workflow --recursive
compozy tasks run my-workflow -r
# Per-workspace default
[tasks.run]
recursive = trueThe option is also exposed in the interactive task-runtime form and threaded through the daemon runtime overrides (internal/cli/daemon_commands.go).
When --recursive is set:
task_NNN.mdfiles are walked across nested subdirectories.- The following directories are skipped during the walk:
- Any directory starting with
.or_ reviews-*review roundsadrs/memory/
- Any directory starting with
- Tasks are grouped by directory: root tasks first, then each subdirectory in alphabetical order, sorted numerically within each group.
- Path traversal is hardened via
os.OpenRootand validated forward-slash relative paths ininternal/core/tasks/store.go.
Example layout:
.compozy/tasks/my-feature/
├── task_001.md # root group (first)
├── features/auth/
│ ├── task_001.md
│ └── task_002.md
└── features/billing/
└── task_001.md
Run order: root → features/auth/task_001 → features/auth/task_002 → features/billing/task_001.
_meta.md totals and bulk completion now always traverse recursively, so a flat run that happens to have stray nested files no longer under-reports counts. Recursion-mode mismatches between metadata and execution are eliminated.
Workflow memory is scoped per subdirectory to keep any single MEMORY.md well below its 12 KB / 150-line soft cap when recursing across many sub-features:
- A task at
features/auth/task_001.mdwrites workflow notes tomemory/features/auth/MEMORY.md. - Reads walk up to the closest-ancestor
MEMORY.md, so shared context still propagates. - Writes stay isolated to the task's immediate scope.
Path sanitization (sanitizeTaskMemoryRelpath / validateTaskMemoryRelpath in internal/core/memory/store.go) rejects leading slashes, empty segments, and .. to prevent escape.
- DB sync and the extension Host API still operate on the slug root only.
- Skip-list directories cannot currently be customized per workspace.
Compozy now ships a self-contained zsh completion plugin that completes task slugs for compozy tasks run from the nearest .compozy/tasks directory relative to your shell's $PWD. It works across worktrees and repository copies — no global config needed.
tasksaftercompozyrunaftercompozy tasks- Task slugs after
compozy tasks run— one suggestion per task directory under the discovered.compozy/tasks
If no .compozy/tasks is found in any ancestor of $PWD, completion falls back to the default zsh behavior at that position.
# 1. Copy the plugin into your shell folder
cp /path/to/compozy/zsh/compozy-completion/compozy-completion.plugin.zsh \
"$HOME/.zsh/compozy-completion/compozy-completion.plugin.zsh"
# 2. Source it from ~/.zshrc
if [[ -f "$HOME/.zsh/compozy-completion/compozy-completion.plugin.zsh" ]]; then
source "$HOME/.zsh/compozy-completion/compozy-completion.plugin.zsh"
fi
# 3. Reload
source ~/.zshrccd /path/to/repo
compozy tasks run <TAB> # → suggests task directory names from .compozy/tasksFull docs live at zsh/compozy-completion/README.md in the repo.
_compozy_tasks_workspace walks upward from $PWD until it finds a directory containing .compozy/tasks, then enumerates that directory for slugs. This keeps completion fast and avoids spawning Compozy on every TAB.
A bug in the initial plugin was also fixed in this release: when multiple subdirectories existed under .compozy/tasks, the same slug could be emitted more than once on TAB. Slug discovery now goes through a dedicated _compozy_task_slugs helper that:
- Returns one slug per task directory, deduplicated.
- Uses zsh
(N/)glob qualifiers so it gracefully no-ops when the directory is empty. - Splits results with
(@f)to keep slugs containing spaces or special characters intact.
End result: compozy tasks run <TAB> shows each available task slug exactly once.
Compozy's Homebrew distribution moves from a cask to a proper formula. This simplifies installation (no separate brew tap step), enables brew test-driven smoke checks, and aligns the upgrade flow with how CLI tools are normally distributed on Homebrew.
# Before (cask)
brew tap compozy/compozy
brew install --cask compozy
# After (formula)
brew install compozy/compozy/compozyThe shorthand auto-taps compozy/compozy and installs the compozy formula in a single command.
compozy upgrade (and the compozy upgrade flow inside internal/update/install.go) now targets the formula instead of the cask:
brew upgrade compozy/compozy/compozyExisting users on the cask should reinstall via the formula path; both can't coexist on the same prefix.
.goreleaser.ymlreplaces thehomebrew_casks:block with abrews:block:directory: Formulacommit_msg_template: "Brew formula update for {{ .ProjectName }} version {{ .Tag }}"license: "BSL-1.1"test: system "#{bin}/compozy", "--version"— every published formula now smoke-testscompozy --version.
- The release artifact for Homebrew is keyed via
ids: [compozy-archive]so the formula picks the right archive. - The archive comment now reads:
Keep the binary at the archive root so Homebrew formulas can install it directly.
README.md is updated with the new one-liner install command.
- Codex acp integration (#151)
- Cwd path
- Add qa extension (#138)
- Workspace register (#140)- Workspace discover path- Prevent false task completion via prompt kickoff + worktree diff-check (#144) (#145)
- Update- Release notes
- Release tool
Archiving a workflow that still has open work no longer silently succeeds. The daemon now returns a typed workflow_force_required error when the target workflow has non-terminal tasks or unresolved review issues, and the dashboard surfaces it as an inline confirmation dialog so you can either resolve the open items first or explicitly archive with force = true.
-
internal/core/archive.gointroducesErrWorkflowForceRequiredand a structuredWorkflowArchiveForceRequiredErrorthat reports task and review counts:type WorkflowArchiveForceRequiredError struct { WorkspaceID string WorkflowID string Slug string Reason string TaskTotal int TaskNonTerminal int ReviewTotal int ReviewUnresolved int }
-
The daemon HTTP API maps that error to
code: "workflow_force_required"with a 409 response, so frontends can detect it without parsing strings. -
model.ArchiveConfig.Forceand the kernelWorkflowArchiveCommand.Forcefield now flow end-to-end, so a retry withforce=truebypasses the gate. -
The web archive flow (
web/src/routes/_app/workflows.tsx+web/src/systems/workflows/adapters/workflows-api.ts) catches the typed error, opens an alert dialog with task/review counts, and re-issues the archive call withforce: trueif you confirm.
A new AlertDialog primitive in @compozy/ui powers the confirmation. The flow is:
- Click Archive on a workflow with open tasks or reviews.
- The daemon returns
workflow_force_requiredwith counts (e.g.task_non_terminal: 2,review_unresolved: 1). - The UI opens a confirmation dialog explaining what will be archived anyway.
- Confirm → the same archive request is retried with
force: true; the response includesforced: trueand the counts that were overridden.
Workflows whose state is already clean continue to archive on the first call with no prompt — the gate only fires when there is genuinely open work.
Compozy now ships a built-in cy-qa-workflow extension that automatically attaches QA-planning and QA-execution tasks to any PRD-driven workflow, with curated runtimes per task. The extension lives at extensions/cy-qa-workflow/ and follows the same on-disk contract as user extensions, so it can be customized or replaced project-by-project.
When enabled, every compozy tasks run <slug> over a PRD-mode workflow ends up with two extra tasks at the tail of _tasks.md:
| Task | Purpose | Type | Complexity |
|---|---|---|---|
<Workflow> QA plan and regression artifacts |
Generates feature-level test plans, execution-ready test cases, and regression suites under .compozy/tasks/<workflow>/qa/ |
docs |
high |
<Workflow> QA execution and operator-flow validation |
Executes the generated plan, files bug reports for confirmed failures, fixes root causes, and finishes only after make verify passes |
test |
critical |
The execution task depends on the report task; the report task depends on every other implementation task in the workflow, so QA always runs last.
The extension also pins per-task runtimes via the new plan.pre_resolve_task_runtime hook so each QA task runs on the IDE/model best suited to it — no manual --task-runtime needed:
| QA task | IDE | Model | Reasoning effort |
|---|---|---|---|
| QA report | claude |
opus |
xhigh |
| QA execution | codex |
gpt-5.5 |
xhigh |
Override on a per-run basis with --task-runtime, or per-project via [[tasks.run.task_runtime_rules]].
cy-qa-workflow also patches the agent session at create time:
- The QA execution prompt is prefixed with
/goal …so the agent enters goal-driven mode and only finishes aftermake verifypasses. - The QA report prompt sets
CLAUDE_CODE_EFFORT_LEVEL=xhighin the session env to lift Claude's effort ceiling for plan generation.
# extensions/cy-qa-workflow/extension.toml
[extension]
name = "cy-qa-workflow"
version = "0.1.0"
description = "Adds Compozy QA report and QA execution tasks to workflow runs"
min_compozy_version = "0.1.10"
[subprocess]
command = "go"
args = ["run", "."]
[security]
capabilities = ["plan.mutate", "agent.mutate", "tasks.read", "tasks.create"]
[[hooks]]
event = "plan.pre_discover"
required = true
[[hooks]]
event = "plan.pre_resolve_task_runtime"
required = true
[[hooks]]
event = "agent.pre_session_create"
required = true- Tasks are detected by HTML markers (
<!-- compozy-qa-workflow:qa-report -->/<!-- compozy-qa-workflow:qa-execution -->) plus title/type heuristics, so re-running the workflow does not duplicate them. update_index = trueis set on the newhost.tasks.createrequest, so the entries appear in_tasks.mdin the right order on first run.
TaskCreateRequest.UpdateIndex(update_indexin JSON / TS) — whentrue, the host appends the created task to_tasks.md. Documented indocs/extensibility/host-api-reference.md.TaskFrontmatter.Dependencies— extensions can now seed task dependencies directly when creating a task.SessionRequest/ResumeSessionRequestnow use a stable readable JSON contract (prompts are plain strings, not base64), matching the runtime-side ACP contract used by hook payloads and patches.
Two long-standing workspace-discovery papercuts are fixed. compozy workspaces register and resolve now accept relative paths the same way every other Compozy command does, and workspace auto-discovery no longer treats the home-scoped ~/.compozy/ runtime directory as a project-local workspace marker.
Closes #139.
Before, the API client sent paths through unchanged after strings.TrimSpace. A relative path like . or ./my-project was forwarded to the daemon as-is, where it resolved against the daemon's working directory instead of the caller's, producing confusing "workspace not found" errors or registering the wrong directory.
The client now normalizes the argument before sending it:
// internal/api/client/operator.go
func normalizeWorkspacePathArg(path string) (string, error) {
trimmed := strings.TrimSpace(path)
if trimmed == "" {
return "", nil
}
if filepath.IsAbs(trimmed) {
return filepath.Clean(trimmed), nil
}
absolutePath, err := filepath.Abs(trimmed)
if err != nil {
return "", fmt.Errorf("resolve workspace path %q: %w", path, err)
}
return filepath.Clean(absolutePath), nil
}This normalization runs for both RegisterWorkspace and ResolveWorkspace, so:
cd ~/code/my-feature
compozy workspaces register . # now registers /Users/you/code/my-feature
compozy workspaces resolve ./sub-project # resolves against the caller's CWDdiscoverWorkspaceRootFromStart walks up the filesystem looking for a .compozy/ marker directory. When compozy was invoked from anywhere under $HOME that did not contain its own .compozy/, the walk would eventually find ~/.compozy/ — the home-scoped daemon runtime root — and register the user's home directory (or some ancestor) as a workspace.
The discovery loop now resolves the global Compozy marker once and skips it during the walk, so only project-local .compozy/ directories are treated as workspace roots:
// internal/core/workspace/config.go
globalMarkerDir, hasGlobalMarker := discoverGlobalWorkspaceMarkerDir()
// ...
if err == nil && info.IsDir() {
// The home-scoped Compozy directory stores global runtime/config state.
// It must not redefine arbitrary paths under HOME as local workspaces.
if !hasGlobalMarker || !sameWorkspaceMarkerDir(candidate, globalMarkerDir) {
return current, nil
}
}Comparison is symlink-aware (filepath.EvalSymlinks on both sides), so installs that symlink ~/.compozy/ are still correctly excluded.
New tests pin the behavior end-to-end:
internal/api/client/client_transport_test.go— relative paths are normalized before transport.internal/cli/operator_commands_integration_test.go—register/resolvefrom a relative CWD produce absolute paths in the registry.internal/core/workspace/config_test.go— discovery skips~/.compozy/even when started from$HOME.internal/store/globaldb/registry_test.go— registry insert/lookup is consistent with the normalized paths.
- Binary release
- Daemon improvements (#121)
- Add optional sound notifications on run lifecycle events (#96)
- Global config defaults (#106)
- Add per task prop selection (#109)
- Migrate to daemon — BREAKING: (#112)
- Daemon web UI (#122)
- Web UI polish (#125)
- Review watch (#133)
- Daemon adjustments (#116)
- Harden runtime activity and version handling (#127)
- Release adjustments (#131)
- Infer task type during migrate (#129)
- Watch adjustments
- Lint errors
- Release notes
- Daemon PRD
- New PRDs
- Updates
- Add release notes
- Fix auto-docs
- Add release notes
- Fix Windows
Compozy now runs every task, review, and exec workflow through a long-lived, home-scoped daemon at ~/.compozy/. The daemon owns runtime state in SQLite (~/.compozy/db/global.db plus per-run run.db), exposes a UDS + HTTP API, and supports re-attach and observe across separate CLI invocations. Existing scripts that called compozy start or compozy fix-reviews need updates — most legacy top-level commands are gone or moved under the new tasks, reviews, runs, and daemon groups. The CLI auto-starts the daemon on first invocation, so most users do not need to start it explicitly.
compozy daemon start | status | stoplifecycle commands, plus--foregroundfor attached runs and--web-dev-proxy <url>for proxying a frontend dev origin through the daemon HTTP transport.compozy workspaces list | show | register | unregister | resolveworkspace registry — workspaces are registered lazily on first use, or explicitly viaregister.compozy tasks run <slug>daemon-backed workflow runner with--attach auto|ui|stream|detach,--ui,--stream,--detach, and--task-runtimeoverrides.compozy reviews fetch | list | show | fixreview command family (replaces the old top-levelfix-reviews/fetch-reviews).compozy runs attach <run-id> | watch <run-id> | purgefor re-attaching to live runs and pruning terminal artifacts.--format text|jsonflag on operator/daemon commands for machine-readable output.- New durable stores:
~/.compozy/db/global.db(workspaces, runs index) and per-run~/.compozy/runs/<run-id>/run.db. compozy migrateis now required before daemon-backed commands run on legacy projects (it also infers task types — see the dedicated note).
| Area | Before | After |
|---|---|---|
| Workflow run | compozy start --name <slug> |
compozy tasks run <slug> (top-level start is removed) |
| Review fix | compozy fix-reviews |
compozy reviews fix (top-level fix-reviews kept as alias) |
| Review fetch | compozy fetch-reviews |
compozy reviews fetch (top-level fetch-reviews kept as alias) |
| Per-task runtime config | [[start.task_runtime_rules]] |
[[tasks.run.task_runtime_rules]] (TOML), or --task-runtime (CLI/TUI) |
| Runtime artifacts | <workspace>/.compozy/runs/<run-id>/ |
~/.compozy/runs/<run-id>/ (now includes durable run.db) |
| Sync semantics | compozy sync regenerated _meta.md |
Reconciles workflow state into global.db; one-time cleanup of legacy _meta.md / _tasks.md |
| Preflight | compozy start skill check |
tasks run and reviews fix block on missing skill installs |
| Public Go API | File-based readers in pkg/compozy/runs |
Daemon-transport readers; signature changes in Run, watch, tail, replay |
| Migrate | Recommended | Required before any daemon-backed workflow command on legacy projects |
# Lifecycle (most users do not need explicit start; tasks/reviews auto-start)
compozy daemon start # detached, returns status
compozy daemon start --foreground # attached
compozy daemon start --foreground \
--web-dev-proxy http://127.0.0.1:3000 # for UI development
compozy daemon status --format json
compozy daemon stop --force # cancel runs, then stop
# Workspaces
compozy workspaces register .
compozy workspaces list --format json
compozy workspaces show <id-or-path>
# Run a workflow
compozy tasks run user-auth # auto-attach (TUI if interactive)
compozy tasks run user-auth --stream # textual stream
compozy tasks run user-auth --detach # fire-and-forget
compozy tasks run user-auth \
--task-runtime type=frontend,ide=codex,model=gpt-5.5
# Reattach / observe / purge
compozy runs attach <run-id>
compozy runs watch <run-id>
compozy runs purge
# Reviews
compozy reviews fetch user-auth --provider coderabbit --pr 42
compozy reviews list user-auth
compozy reviews fix user-auth --ide claude --concurrent 2 --batch-size 3daemon start --foregroundruns the daemon attached to the current shell with structured logs.- HTTP port defaults to OS-chosen (ephemeral) and is reported by
daemon status. Pin it withCOMPOZY_DAEMON_HTTP_PORT=<n>. Bind host is loopback-only (127.0.0.1) and non-loopback origins are rejected at the middleware layer. - Attaching to a run that has already settled now falls back to streaming the persisted event log instead of erroring.
daemon stopaccepts--forceto cancel owned runs before shutdown; otherwise it drains gracefully.
- Upgrade the binary. On first invocation the daemon creates
~/.compozy/{config.toml,agents,extensions,state,daemon,db,runs,logs,cache}. - For legacy projects with XML-tagged artifacts: run
compozy migrateonce before any daemon-backed command. - Replace scripts:
compozy start --name X→compozy tasks run Xcompozy fix-reviews→compozy reviews fix(alias still works)compozy fetch-reviews→compozy reviews fetch(alias still works)
- Update TOML: rename
[[start.task_runtime_rules]]to[[tasks.run.task_runtime_rules]]. Moveid=selectors to--task-runtime(TOML rejectsid=rules). - Stop reading
<workspace>/.compozy/runs/directly — runtime artifacts now live in~/.compozy/runs/<run-id>/and include a durablerun.db. Usepkg/compozy/runs(daemon transport) or the daemon HTTP/UDS API. - Optional: set
COMPOZY_DAEMON_HTTP_PORT=<n>to pin the HTTP port (0requests an ephemeral port).COMPOZY_WEB_DEV_PROXYmirrors--web-dev-proxy.
Set personal defaults once in ~/.compozy/config.toml and have them apply across every project. Project-level .compozy/config.toml always takes precedence, so teams keep control while individuals stop repeating themselves.
# ~/.compozy/config.toml (global — applies to all projects)
[defaults]
ide = "claude"
model = "sonnet"
access_mode = "default"
auto_commit = true
[sound]
enabled = true
on_completed = "glass"
on_failed = "basso"
[exec]
model = "gpt-5.5"
verbose = true# .compozy/config.toml (project — overrides global)
[defaults]
model = "o4-mini"
[start]
include_completed = trueWith both files above the effective config resolves to:
| Field | Value | Source |
|---|---|---|
defaults.ide |
"claude" |
global |
defaults.model |
"o4-mini" |
project wins |
defaults.auto_commit |
true |
global |
sound.enabled |
true |
global |
exec.model |
"gpt-5.5" |
global |
start.include_completed |
true |
project |
All sections supported in project config ([defaults], [start], [exec], [fix_reviews], [fetch_reviews], [tasks], [sound]) work in the global file with the same schema.
Opt-in audio cues that play when a run completes or fails, so you can step away from long-running sessions without missing the result. Ships disabled by default — no sound unless you explicitly enable it.
Add a [sound] section to .compozy/config.toml (project or global):
[sound]
enabled = true
on_completed = "glass" # plays on successful completion
on_failed = "basso" # plays on failure or cancellationSeven presets work cross-platform out of the box:
| Preset | macOS | Linux | Windows |
|---|---|---|---|
glass |
/System/Library/Sounds/Glass.aiff |
freedesktop/stereo/complete.oga |
Media\Windows Notify Calendar.wav |
basso |
/System/Library/Sounds/Basso.aiff |
freedesktop/stereo/dialog-error.oga |
Media\chord.wav |
ping |
/System/Library/Sounds/Ping.aiff |
freedesktop/stereo/message.oga |
Media\notify.wav |
hero |
/System/Library/Sounds/Hero.aiff |
freedesktop/stereo/bell.oga |
Media\tada.wav |
funk |
/System/Library/Sounds/Funk.aiff |
freedesktop/stereo/bell.oga |
Media\Ring06.wav |
tink |
/System/Library/Sounds/Tink.aiff |
freedesktop/stereo/message.oga |
Media\ding.wav |
submarine |
/System/Library/Sounds/Submarine.aiff |
freedesktop/stereo/phone-incoming-call.oga |
Media\ringin.wav |
Pass an absolute path to use your own audio file:
[sound]
enabled = true
on_completed = "/Users/you/sounds/success.wav"
on_failed = "/Users/you/sounds/fail.wav"| Event | Config field | When it fires |
|---|---|---|
| Run completed | on_completed |
Task finishes successfully |
| Run failed | on_failed |
Task errors out |
| Run cancelled | on_failed |
Task is interrupted (reuses the failure sound) |
Playback is synchronous with a 3-second timeout — a missing or slow audio file never blocks shutdown. Errors are logged at debug level and never surface to the user.
Pick a different IDE, model, or reasoning effort per task type — or per individual task — for a single compozy tasks run invocation, instead of running the whole batch on one global runtime. Selection is exposed three ways: a repeatable --task-runtime CLI flag, an interactive form on tasks run, and a [[tasks.run.task_runtime_rules]] TOML section. A new plan.pre_resolve_task_runtime extension hook lets extension authors resolve per-task runtime programmatically.
--task-runtime is repeatable. Each value is a comma-separated rule with a selector (id= or type=) and at least one override (ide=, model=, reasoning-effort=).
# All frontend tasks → Codex with high reasoning, plus one task forced to xhigh
compozy tasks run multi-repo \
--ide claude --model opus \
--task-runtime "type=frontend,ide=codex,model=gpt-5.5,reasoning-effort=high" \
--task-runtime "id=task_07,reasoning-effort=xhigh"Persistent type-scoped defaults live under [[tasks.run.task_runtime_rules]]. id= selectors are CLI/TUI-only by design — config rejects them.
[defaults]
ide = "codex"
model = "gpt-5.5"
reasoning_effort = "medium"
[[tasks.run.task_runtime_rules]]
type = "frontend"
model = "gpt-5.5"
reasoning_effort = "high"
[[tasks.run.task_runtime_rules]]
type = "docs"
ide = "claude"
model = "opus"| Key | Where | Description |
|---|---|---|
id |
CLI/TUI only | Match a single task by PRD task id |
type |
CLI/TUI/TOML | Match all tasks of this type (e.g. frontend, docs) |
ide |
all | claude, codex, copilot, cursor-agent, droid, gemini, opencode, pi |
model |
all | Any model accepted by the chosen IDE |
reasoning-effort / reasoning_effort |
all | low, medium, high, xhigh |
Each rule must have a selector and at least one override. Mixing id and type in a single rule is an error.
- CLI/TUI
id=rules - CLI/TUI
type=rules - Config
[[tasks.run.task_runtime_rules]](type-only) [defaults]
Extension authors can resolve runtime programmatically via the new plan.pre_resolve_task_runtime hook (helper: onPlanPreResolveTaskRuntime). Later hooks (plan.post_prepare_jobs, job.pre_execute, run.pre_start) are now hard-guarded against runtime mutation for workflow runs — use the new hook instead.
compozy reviews watch runs a long-lived loop that polls your review provider, fetches each new actionable round, runs reviews fix, optionally auto-pushes the resulting commits, and repeats until the PR is clean or a max-rounds cap is hit. The watch run shows up in the dashboard as a parent run with each round's reviews fix linked underneath, so you can step away from a noisy PR and come back to a finished branch.
# Auto-push each round until clean (or max 6 rounds)
compozy reviews watch tools-registry --provider coderabbit --pr 85 \
--auto-push --until-clean --max-rounds 6
# Follow events live instead of backgrounding
compozy reviews watch tools-registry --provider coderabbit --pr 85 --stream
# Tune timing
compozy reviews watch my-feature --provider coderabbit --pr 85 \
--poll-interval 30s --review-timeout 30m --quiet-period 20sreviews watch does not support cockpit UI attach — --ui, --attach ui, and --tui are rejected. Use --stream to follow events or --detach for fire-and-forget.
[defaults]
auto_commit = true # required when watch_reviews.auto_push = true
[fetch_reviews]
provider = "coderabbit"
[watch_reviews]
max_rounds = 6
poll_interval = "30s"
review_timeout = "30m"
quiet_period = "20s"
auto_push = true
until_clean = true
push_remote = "origin"
push_branch = "feature/reviews" # must be set together with push_remote- Take a snapshot of git state and reconcile any already-committed unpushed commits (emitted as
round = 0push events). - Poll the provider every
poll_intervaluntil the PR head is settled — for CodeRabbit, that means the latest commit status issuccess, not just any submitted review. - Wait
quiet_periodfor in-flight review activity to drain, then re-check status. - If the next round has actionable issues, spawn a child
reviews fixrun, await its terminal state, and (with--auto-push)git push <remote> HEAD:<branch>. - Loop until
clean(provider returns no actionable issues) ormax_rounds.
Defaults: 6 rounds, 30 s poll, 30 m review timeout, 20 s quiet period.
- Forces
auto_commit=trueon child runs; rejects--auto-commit=false. - Only ever runs
git push <remote> HEAD:<branch>— neverrestore,reset,clean, or branch switching. - Reconciles existing unpushed commits at startup so a watch run never re-pushes work it didn't produce.
- Config-driven
auto_push=truerequiresdefaults.auto_commit=true. push_remoteandpush_branchmust be set together (or both omitted to resolve upstream).
Four new hooks let extensions observe and gate the loop. Hooks can veto a round / push (continue=false/push=false + stop_reason) but cannot fake a clean state:
| Hook | Fires |
|---|---|
review.watch_pre_round |
Before each provider poll / fix round |
review.watch_post_round |
After a child reviews fix run reaches terminal state |
review.watch_pre_push |
Before auto-push, with the resolved remote/branch |
review.watch_finished |
When the watch loop ends (clean, max-rounds, or error) |
- Provider support: CodeRabbit only for the settle-gating logic in this release; other providers are wired via the registry but settle behavior is CodeRabbit-specific.
- Provider auth still uses the existing fetch path (CodeRabbit token, GitHub PR access). Shorter
poll_intervalvalues increase pressure on those rate limits. - Each watch run shows up in the dashboard with a persisted
parent_run_id; the parent and the active child collapse into a single active row, full history retained.
A bundle of reliability fixes across the update notifier, native Codex ACP runtime, ACP activity tracking, the extension SDK, and the build toolchain.
- Update notifier no longer prompts a "downgrade" on git-describe builds. Pre-release suffixes like
-15-g834fec6are stripped before semver comparison, so a binary built ahead of the latest tag stops nagging users to install the older release. Identifiers like1.2.3-1-gammaare preserved unless the suffix is a plausible short SHA. - Native Codex ACP runtime accepts
codex/<model>aliases. The provider prefix is stripped beforeSetSessionModel, fixing rejections for ChatGPT-account Codex sessions. - ACP activity stays "active" for the full lifecycle of a session update, including nested or concurrent submissions. Previously the tracker could mark a session idle while in-flight work was still being submitted, dropping events on the floor.
- Extension SDK publishes
initializedstate before sending the initialize response, fixing a host-side race whereruns.startcould be rejected as "extension not initialized" immediately after handshake. BUN_VERSIONis now a minimum supported version, not an exact pin. Error messaging updated to "or newer / at least", so contributors with a newer Bun release stop seeing spurious version errors.
# Before: a binary built between releases prompted a "downgrade" install
$ compozy --version
v0.1.12-15-g834fec6
$ compozy ...
Update available: 0.1.12 (you have v0.1.12-15-g834fec6)
# After: git-describe suffix is stripped; no spurious prompt
$ compozy ...
(no update notice)
compozy migrate no longer emits type: "" for legacy feature / feature implementation tasks, which previously broke compozy sync on the migrated workflow. A valid v2 task type is now inferred from the legacy type, with domain used as a constrained fallback only when the direct remap is genuinely ambiguous. The unmapped-type follow-up prompt is now emitted only when inference is unsafe.
This release also tightens API error reporting: validation/parse failures from the daemon HTTP API now return 422 Unprocessable Entity with cleaner messages instead of generic 500, and the API core preserves original error identity so callers using errors.Is / errors.As get consistent results.
# Before — produced workflow with empty `type`, then failed on sync.
compozy migrate
compozy sync # error: missing/invalid task type
# After — migrated workflow has a valid inferred type; sync succeeds.
compozy migrate
compozy sync # okCompozy now ships a built-in web UI served straight from the daemon. Start the daemon and you get a single-binary, localhost-only dashboard for browsing workspaces, workflows, tasks, runs, reviews, and memory — with live SSE-backed run streaming, raw event diagnostics, a run transcript viewer, and skeletons / empty-states throughout. Frontend assets are embedded in the Go binary, so there is nothing extra to install. Contributors can point the daemon at a Vite dev server with --web-dev-proxy.
- Dashboard with workspace KPIs and a "Sync all workflows" action.
- Workflows inventory, per-workflow task board, and task detail page.
- Workflow Spec viewer (PRD / TechSpec / ADR markdown rendered inline).
- Memory index plus per-workflow memory view.
- Reviews index, per-round view, and issue detail pages.
- Runs list with workflow filter, plus run detail with live event stream.
- Run Event Feed — raw daemon events with in-memory event store, SSE snapshots, heartbeat, and overflow framing.
- Run Transcript Panel — full transcript view of agent turns and tool calls for any run.
- Workspace picker with onboarding shell and live workspace WebSocket sync.
- New shared UI primitives:
Alert,EmptyState,Markdown,Metric,Skeleton,StatusBadge, plus button loading state and token refresh.
# Start the daemon (foreground for visibility); the UI is served at the daemon HTTP port
compozy daemon start --foreground
# Discover the URL
compozy daemon status # prints "http_port: <N>"
open "http://127.0.0.1:<N>"
# UI contributors: proxy the daemon to a Vite dev server
compozy daemon start --foreground --web-dev-proxy http://127.0.0.1:3000| Setting | Default | Override |
|---|---|---|
| Bind host | 127.0.0.1 (loopback only) |
hard-coded; non-loopback binds are rejected |
| HTTP port | OS-chosen (ephemeral) | COMPOZY_DAEMON_HTTP_PORT=<n> |
| Frontend dev proxy | off (embedded web/dist) |
--web-dev-proxy <url> or COMPOZY_WEB_DEV_PROXY |
# Pin the daemon UI to a known port
export COMPOZY_DAEMON_HTTP_PORT=4444
export COMPOZY_WEB_DEV_PROXY=http://127.0.0.1:3000 # only for UI dev
compozy daemon startThere is no login — the UI is loopback-only and the API enforces:
- Host header must match localhost; non-
127.0.0.1binds are rejected. - Origin validation against the bound host.
- Per-session CSRF cookie + header.
X-Compozy-Active-Workspaceheader propagated by the SPA.- Standard hardening headers via
securityHeadersMiddleware, plus ETag/304 caching for the embedded static assets.