Releases: speakeasy-api/granary
v1.6.0
Entity metadata
Tasks, projects, and initiatives now support an optional free-form JSON metadata field. This lets you attach arbitrary structured information — environment config, tracking IDs, custom fields — to any entity without being limited to text-only tags.
CLI usage:
granary project my-proj tasks create "deploy service" \
--metadata '{"env": "production", "region": "us-east-1"}'
granary tasks update proj-task-1 \
--metadata '{"env": "staging", "retries": 3}'Metadata works the same way on projects (project create/update --metadata) and initiatives (initiative create/update --metadata).
Output behavior:
Metadata is included in --output json / --json output but is intentionally excluded from prompt and text formats, keeping those outputs clean for human and LLM consumption.
Event template access:
Metadata is embedded as a nested JSON object in all event payloads (task.created, task.updated, task.next, project.created, etc.), so event templates can reference individual metadata fields:
{metadata.env}
{metadata.region}
{metadata.config.timeout}
This enables metadata-driven automation — for example, a task.next handler that routes work based on {metadata.env} or {metadata.worktree}.
v1.5.0
Review mode workflow
Granary now supports an optional review gate in the task and project lifecycle. When enabled, completed work transitions to in_review instead of going straight to done/completed, giving a reviewer (human or agent) the chance to approve or reject it before it's finalized.
Two scopes:
taskmode —granary work donemoves the task toin_reviewand emits atask.reviewevent. A reviewer approves or rejects individual tasks.projectmode — Tasks still complete normally, but when all tasks are done, the project entersin_reviewinstead ofcompleted. Reviewers approve the project as a whole, or reject it by creating follow-up tasks and reopening the project.
New granary review command:
granary review <id>— displays reviewer context (task/project details, comments, suggested actions)granary review <id> approve ["comment"]— approves and completes the entitygranary review <id> reject "feedback"— rejects with feedback; tasks return totodo, projects reopen toactivewith draft tasks promoted totodo
Review comments use a new review comment kind, and review events (task.review, project.review) are emitted so downstream agents or integrations can react.
Review mode configuration
Review mode is stored in the workspace database (config table) under the key workflow.review_mode. Enable it with:
granary config set workflow.review_mode task # or 'project'
granary config unset workflow.review_mode # disable
Updated SQL triggers
The trg_project_auto_complete trigger is now config-aware — when workflow.review_mode is set to project, it transitions the project to in_review instead of completed. New triggers emit task.review and project.review events on status transitions.
v1.4.6
Daemon shutdown now cleans up worker and run state
Previously, when the granary daemon shut down (via granary daemon stop, signals, or any other reason), workers were signalled to stop but their database status was never updated. This left workers permanently marked as running in the global database with their active runs also stuck in running state.
On next daemon start, restore_workers would find these stale "running" workers and attempt to respawn them — potentially picking up orphaned runs from completely unrelated workspaces. Manually stopping individual workers worked correctly because stop_worker properly updated the DB, but the bulk shutdown_all path (used on daemon exit) skipped this cleanup entirely.
shutdown_all now mirrors the same cleanup that stop_worker does: after signalling and waiting for workers to finish, it marks each worker as stopped and cancels any active runs in the database.
Fix: unblock returns tasks to `todo` and clears stale claims
Previously, `unblock_task` checked `started_at` to decide whether to return a task to `in_progress` or `todo`. Since agents set `started_at` when they begin work, unblocking always returned tasks to `in_progress` — even when the block was due to external factors (e.g., missing files from concurrent projects) and the task needed to re-enter the scheduling queue.
Additionally, unblock didn't clear `claim_owner` or `claim_lease_expires_at`. This meant the `task.next` SQLite trigger's claim guard would suppress the event even after manually cycling the task through `draft → todo`. The net effect was that unblocked tasks became invisible to the worker daemon — no `task.next` event fired, so no worker would pick them up.
What changed:
- `unblock_task` now always transitions to `todo`, regardless of `started_at`
- Stale claim fields (`claim_owner`, `claim_claimed_at`, `claim_lease_expires_at`) are cleared on unblock
- The `task.next` trigger now fires correctly when tasks are unblocked
v1.4.5
1.4.5
Fixed progress bars showing as fully filled or empty
Progress bars throughout the initiative and project detail screens were rendering as binary — either completely empty or completely full — instead of showing actual proportional progress. A project at 15% completion would display an entirely filled bar.
The root cause was that FillPortion in Iced only distributes space among sibling elements. Each progress bar had a single child container inside the track, so FillPortion(15) behaved identically to FillPortion(100) — always taking 100% of the parent.
The fix adds a second container as a sibling to split the space proportionally, with conditional logic to omit the unused portion at 0% and 100% so both edge cases render correctly.
Affected locations:
- Reusable
ProgressBarwidget - Initiative detail screen (main progress bar and per-project mini bars)
- Project detail screen progress section
- Project card progress bar
Fix task.next event loop on todo task updates
Workers subscribing to task.next events experienced an event loop where the same task would emit repeated task.next events approximately every second. This caused unnecessary event spam and wasted runner capacity.
Root cause: The trg_task_next_on_status_todo trigger fired on any update to a task with status = 'todo', not just on transitions to todo. When a worker claimed a task (setting owner, worker_ids, etc.), the update triggered another task.next event, which the daemon dispatched back to a worker, creating an infinite loop at the poll interval.
What changed:
- Added
AND OLD.status != 'todo'guard totrg_task_next_on_status_todo, so it only fires on actual status transitions totodo— not on metadata updates to tasks already intodostatus - Added e2e tests verifying that owner/worker updates on todo tasks don't re-emit
task.next - Added full lifecycle test covering project dependency cascade through worker-style updates
v1.4.4
1.4.4
Improved agent workflow prompts to prevent initiative planning mistakes
Agents planning multi-project initiatives were incorrectly creating tasks directly instead of delegating to sub-agents. The root cause: granary plan showed inviting task-creation templates, and granary initiate buried the delegation requirement in Step 3 of 4.
All agent-facing prompts have been redesigned to make the correct workflow path unmistakable.
granary initiate now front-loads a delegation constraint
The output now begins with a ## CRITICAL: Delegation-Only Workflow section before any steps:
## CRITICAL: Delegation-Only Workflow
You are the initiative coordinator. Your job is to create projects and delegate.
Do NOT create tasks directly. Do NOT use `granary project <id> tasks create`.
Task creation is handled by sub-agents via `granary plan --project <id>`.
Step 3 was also renamed from "Launch Sub-Agents for Planning" to "Delegate Planning to Sub-Agents" with reinforcement:
Do NOT create tasks yourself — this is the sub-agent's responsibility.
granary plan now warns initiative agents to stop
When an agent runs granary plan "Feature name" (without --project), the output now includes a scope guard:
## Scope: Single-Project Planning
This workflow is for planning ONE project with tasks.
If this is part of a multi-project initiative, stop here.
Use `granary initiate "Initiative name"` instead — it guides you through
creating projects and delegating task planning to sub-agents.
Entrypoint and help text now distinguish the two paths
Both granary (bare command) and granary --help now present a decision tree instead of a flat list:
Choose ONE entry point based on scope:
- Single project (one feature/fix): `granary plan "Feature name"`
- Multiple projects (cross-cutting work): `granary initiate "Initiative name"`
The --help text also adds an explicit note:
NOTE: Do NOT use `granary plan` for multi-project work.
`initiate` guides you through creating projects and delegating task planning to sub-agents.
v1.4.3
1.4.3
Fix task.next events not firing on project dependency completion
Workers subscribing to task.next events were never notified when a project dependency was satisfied through auto-completion. This caused tasks in dependent projects to sit idle even though granary next --all correctly showed them as actionable.
Root cause: The trg_task_next_on_project_dep_completed trigger fired when a project's status changed to 'done' or 'archived', but the auto-complete system sets projects to 'completed' — a status the trigger didn't recognize. Since no code path ever sets a project to 'done', this cascade trigger has never fired.
What changed:
- The
trg_task_next_on_project_dep_completedtrigger now recognizes'completed'as a valid completion status, so it fires when auto-complete transitions a project - All other
task.nexttriggers updated to include'completed'in project dependency status checks for consistency - Rust-side
nextqueries updated to treat'completed'projects as satisfied dependencies (defensive, previously handled by the task-existence subquery)
Impact: Workers will now correctly pick up tasks as soon as all tasks in a dependency project are done, without needing the dependency project to be manually archived.
v1.4.2
1.4.2
FTS5 search no longer fails on special characters
Queries containing FTS5 operator characters like -, +, *, or keywords like NOT/OR/AND are now properly escaped before being passed to SQLite's FTS5 MATCH clause.
Previously, running something like granary plan "Fix TypeScript v2 build failures - 11 root causes from SDK battery tests" would fail because the - was interpreted as FTS5's NOT operator. Now, each token is individually quoted and purely-punctuation tokens (like a bare -) are dropped, so the search works regardless of what characters appear in the plan name.
Search queries also now use OR semantics instead of implicit AND, which makes prior-art matching more lenient — a project only needs to match some of the query terms to appear in results, rather than requiring all of them.
v1.4.1
1.4.1
Project dependency filtering in next commands
The next, next --all, project.next, and task.next mechanisms now correctly respect project-level dependencies. Previously, only task dependencies were checked — so when Project A depended on Project B, tasks from both projects would appear as actionable even if Project B still had incomplete work.
Now, tasks in a project with unmet project dependencies are excluded from all next-task queries and event triggers. A project dependency is considered met when the dependency project is either done or archived.
This also fixes the initiative ... next command, which had a similar issue where archiving a dependency project would leave dependents permanently blocked (it only checked task status, not project status).
Cascade trigger for project completion
A new SQLite trigger (trg_task_next_on_project_dep_completed) automatically emits task.next events when a dependency project transitions to done or archived. This ensures workers subscribed to task.next are notified when project-level blockers are resolved, without requiring any manual intervention.
E2E test coverage for project dependency blocking
Added a dedicated end-to-end test suite (tests/project_dep_next_e2e.rs) that runs the actual granary binary in an isolated sandbox to verify project dependency filtering works correctly across next, next --all, and initiative next. Tests cover blocking, unblocking via completion, unblocking via archival, and partial dependency satisfaction.
v1.4.0
1.4.0
Task–worker/run association
When a worker picks up a task event, granary now automatically records which workers and runs have operated on it. Each task gains two new JSON-array columns — worker_ids and run_ids — that accumulate IDs as work is performed. The first run to claim an unowned task also sets itself as the task's owner, giving downstream automation a single ID to query for the "current" run.
This means you can trace exactly which workers touched a task and in what order, without having to join across run tables manually.
Environment variables for spawned processes
Every process spawned by a worker now receives two new environment variables:
GRANARY_WORKER_ID— the ID of the worker that spawned the processGRANARY_RUN_ID— the ID of the current run
Scripts and actions can use these to call back into granary, tag artifacts, or correlate logs without needing to parse context from the event payload.
Clickable worker/run links in Silo
Task detail views across all three screens (initiative detail, project detail, and task list) now display worker and run IDs as clickable links that navigate directly to the corresponding worker or run detail screen.
v1.3.0
1.3.0
FTS5 full-text search
Search now uses SQLite's FTS5 full-text search engine instead of LIKE '%query%' pattern matching. This is a significant upgrade to how granary search and granary plan find relevant results.
What changed
A new shared FTS5 virtual table (search_index) indexes projects, tasks, and initiatives across their names/titles, descriptions, and tags. SQLite triggers keep the index in sync automatically on every insert, update, and delete.
Search results are now relevance-ranked using BM25 scoring, with title matches weighted higher than body or tag matches. Previously, results were returned in created_at DESC order with no relevance signal.
New capabilities
- Multi-word queries:
auth loginmatches documents containing both terms (implicit AND), instead of requiring the exact substring - Phrase matching:
"auth login"finds the exact phrase - Prefix matching:
auth*matches any term starting with "auth" - Boolean operators:
auth OR loginfor explicit OR queries - Broader coverage: descriptions and tags are now searched, not just names/titles
Impact on granary plan
Prior art discovery in granary plan benefits directly — it now finds related projects even when the name doesn't contain the exact query substring, and results are ordered by relevance rather than creation date.
Includes an RFC document at docs/rfcs/fts5-search.md with full design rationale.