with one click
granary-plan-work
// Plan and organize work into granary projects and tasks. Use when breaking down a feature, creating tasks, or setting up dependencies.
// Plan and organize work into granary projects and tasks. Use when breaking down a feature, creating tasks, or setting up dependencies.
Set up granary in a new project. Use when asked to initialize granary, install it, or get started with task management.
Plan and organize multi-project initiatives or significant, substantial projects. Use when work spans multiple services, repositories, or has natural project boundaries.
Orchestrate sub-agents and coordinate multi-agent workflows with granary. Use when delegating tasks, spawning workers, or managing parallel execution.
Execute an assigned granary task as a sub-agent. Use when you receive a task ID to complete.
| name | granary-plan-work |
| description | Plan and organize work into granary projects and tasks. Use when breaking down a feature, creating tasks, or setting up dependencies. |
Use this skill when you need to break down work into projects and tasks, or when an orchestrator determines a project needs planning before implementation.
Critical: Before creating any tasks, conduct comprehensive research of the codebase. Each task description is the ONLY context a sub-agent receives—they cannot ask follow-up questions or explore further. Your research directly determines whether tasks succeed or fail.
Before creating a new project or task, search for similar existing work:
# Search for similar projects and tasks
granary search "user profile"
granary search "authentication"
granary search "api endpoint"
Decision tree based on search results:
| Search Result | Action |
|---|---|
| Exact match found (same feature) | Don't duplicate—add tasks to existing project or update existing tasks |
| Similar project exists | Review it—maybe extend it instead of creating a new project |
| Related tasks exist in other projects | Consider dependencies or consolidation |
| No matches | Safe to create new project |
Example: Avoiding duplication
$ granary search "profile"
Projects:
user-profile-abc1: "User Profile" (3 tasks, 1 completed)
Tasks:
settings-xyz9-task-2: "Add profile settings page"
In this case, don't create a new "User Profile" project—add your tasks to user-profile-abc1 or coordinate with the existing work.
# Check for existing planned work
granary search "feature keywords"
granary projects # List all projects
granary project <id> tasks # See tasks in a specific project
# Find relevant files in codebase
glob "**/*.rs" | grep -i auth
grep -r "UserService" src/
# Understand existing patterns
cat src/services/user.rs | head -100
Document your findings in steering files (see Section 5) before creating tasks.
Group related tasks into a project:
granary projects create "Feature Name" --description "Clear description of what this project achieves"
Output gives you a project ID like feature-name-abc1.
Create focused, actionable tasks:
granary project <project-id> tasks create "Task title" \
--description "**Goal:** What this task accomplishes
**Context:** Why this task exists
**Requirements:**
- Specific deliverable 1
- Specific deliverable 2
**Acceptance Criteria:**
- [ ] Criterion 1
- [ ] Criterion 2" \
--priority P1
| Priority | When to Use |
|---|---|
| P0 | Critical, blocking other work |
| P1 | Important, should do soon |
| P2 | Normal priority (default) |
| P3 | Nice to have |
The task description is the ONLY context a sub-agent receives. Sub-agents cannot explore the codebase or ask questions—they rely entirely on what you provide.
Include:
Bad: "Fix the auth bug"
Good:
**Goal:** Fix null pointer in UserService.getById when user not found.
**Context:** Currently throws NullPointerException when querying non-existent user IDs. Should gracefully handle missing users.
**Files to Modify:**
- `src/services/user_service.rs:142-156` - The `get_by_id` function
- `src/errors/mod.rs:23` - Add UserNotFoundException if not present
- `tests/services/user_service_test.rs` - Add test case
**Implementation Details:**
The existing pattern in `src/services/post_service.rs:89` handles this well:
```rust
pub fn get_by_id(id: Uuid) -> Result<Option<Post>, ServiceError> {
// Returns Ok(None) for missing records
}
Follow this pattern—return Result<Option<User>, ServiceError> instead of Result<User, ServiceError>.
Acceptance Criteria:
get_by_id returns Ok(None) for missing usersOption
### Linking Files in Descriptions
Always provide exact file paths and line numbers:
| Instead of... | Write... |
| ---------------------------- | ------------------------------------------------- |
| "the user service" | `src/services/user_service.rs` |
| "the auth middleware" | `src/middleware/auth.rs:45-67` |
| "follow the existing pattern"| "follow pattern in `src/handlers/posts.rs:23-45`" |
| "add tests" | "add tests in `tests/unit/user_test.rs`" |
### Implementation Detail Examples
**For new endpoints:**
Implementation Details:
Add handler in src/handlers/profile.rs. Follow the pattern from src/handlers/user.rs:34-56:
GetProfileHandler struct implementing Handler traitProfileService::get_by_user_id() (already exists at src/services/profile.rs:12)ApiResponse<ProfileDto> matching existing response formatsrc/routes/mod.rs:45 following existing pattern
**For refactoring:**
Implementation Details:
Extract validation logic from src/handlers/user.rs:78-112 into src/validators/user_validator.rs.
Existing validator pattern at src/validators/post_validator.rs:
Validator<T> traitValidationResult with field-level errors#[derive(Validate)] macro for struct validationThe three validation functions to extract:
validate_email() (lines 82-89)validate_password() (lines 91-98)validate_username() (lines 100-110)
**For bug fixes:**
Implementation Details:
Root cause: calculate_total() at src/cart/pricing.rs:45 doesn't account for null discounts.
Fix approach:
discount.unwrap_or(Decimal::ZERO) pattern (see src/cart/tax.rs:23)tracing::debug! macro
## 3. Add Dependencies
If tasks must be done in order:
```bash
# task-2 depends on task-1 (task-2 cannot start until task-1 is done)
granary task <project-id>-task-2 deps add <project-id>-task-1
# View dependencies
granary task <project-id>-task-2 deps graph
You can also specify dependencies at task creation time:
granary project <project-id> tasks create "Dependent task" \
--dependencies <project-id>-task-1
Only add dependencies when truly required - over-constraining reduces parallelism.
For complex planning:
granary session start "planning-feature-x" --mode plan
granary session add <project-id>
Steering files provide standards, conventions, and context that sub-agents should follow during implementation. Set these up during planning so orchestrators and sub-agents have the guidance they need.
| Scope | When Included | Use Case |
|---|---|---|
| Global (default) | Always in context/handoffs | Project-wide standards |
--project <id> | When project is in session scope | Project-specific patterns |
--task <id> | When handing off that specific task | Task-specific research |
--for-session | During session, auto-deleted on close | Temporary research notes |
Steering files are useful for:
# Global steering (always included)
granary steering add docs/coding-standards.md
# Project-attached (only when this project is in context)
granary steering add docs/auth-patterns.md --project auth-proj-abc1
# Task-attached (only in handoffs for this specific task)
granary steering add .granary/task-research.md --task auth-proj-abc1-task-3
# Session-attached (temporary, auto-deleted on session close)
granary steering add .granary/temp-notes.md --for-session
# List current steering files
granary steering list
# Remove steering (specify scope to match)
granary steering rm docs/auth-patterns.md --project auth-proj-abc1
# During planning, document patterns you discover
cat > docs/auth-patterns.md << 'EOF'
# Authentication Patterns
## Existing Conventions
- Auth middleware in src/middleware/auth.rs uses JWT tokens
- User model in src/models/user.rs with bcrypt password hashing
- Session storage uses Redis (see src/services/session.rs)
## Key Conventions
- All API endpoints return JSON with {data, error, meta} structure
- Use `ApiError` type for error handling
- Tests go in tests/ directory, not inline
EOF
# Attach to the project so sub-agents get this context
granary steering add docs/auth-patterns.md --project auth-proj-abc1
First, search granary for similar projects or tasks:
$ granary search "profile"
No matching projects or tasks found.
$ granary search "user"
Projects:
user-auth-def2: "User Authentication" (4 tasks, 4 completed)
Tasks:
user-auth-def2-task-3: "Implement user login endpoint"
Analysis: No existing profile work, but there's a completed auth project. We can reference patterns from user-auth-def2 when building our profile feature. Safe to create a new project.
Now thoroughly research the codebase:
# Find existing user-related code
glob "**/*user*"
grep -r "struct User" src/
# Understand the API patterns
cat src/handlers/mod.rs
cat src/routes/mod.rs | head -50
# Check existing types
cat src/types/user.rs
# Find test patterns
ls tests/
cat tests/handlers/user_test.rs | head -30
Research findings to document:
src/models/user.rs:12-45 has id, email, name, created_atbio, avatar_url fields needed for profilesrc/handlers/user.rs:23-67 - uses axum::Json<T>src/routes/api.rs:34mockall for service mocking (see tests/handlers/user_test.rs:5)# Create project
granary projects create "User Profile" --description "Add user profile page with edit capability. Extends existing User model with profile fields."
# Output: user-profile-abc1
# Task 1: Extend the data model
granary project user-profile-abc1 tasks create "Extend User model with profile fields" \
--description "**Goal:** Add profile fields to the User model and create database migration.
**Context:** User model exists but lacks profile-specific fields.
**Files to Modify:**
- \`src/models/user.rs:12-45\` - Add new fields to User struct
- \`src/schema.rs\` - Update diesel schema (auto-generated after migration)
- \`migrations/\` - Create new migration file
**Implementation Details:**
Add these fields to the User struct at \`src/models/user.rs:12\`:
\`\`\`rust
pub struct User {
// existing fields...
pub bio: Option<String>, // max 500 chars
pub avatar_url: Option<String>, // validated URL
}
\`\`\`
Create migration following pattern in \`migrations/2024_01_15_create_users/up.sql\`:
\`\`\`sql
ALTER TABLE users ADD COLUMN bio TEXT;
ALTER TABLE users ADD COLUMN avatar_url VARCHAR(2048);
\`\`\`
**Acceptance Criteria:**
- [ ] Migration runs successfully (\`diesel migration run\`)
- [ ] User struct compiles with new fields
- [ ] Existing user queries still work" \
--priority P0
# Output: user-profile-abc1-task-1
# Task 2: Create Profile DTOs and validation
granary project user-profile-abc1 tasks create "Create Profile DTOs with validation" \
--description "**Goal:** Create request/response DTOs for profile endpoints.
**Files to Modify:**
- \`src/types/mod.rs:1\` - Add mod declaration
- \`src/types/profile.rs\` - New file for DTOs
**Implementation Details:**
Follow existing DTO pattern at \`src/types/user.rs:8-25\`:
\`\`\`rust
#[derive(Serialize, Deserialize)]
pub struct ProfileResponse {
pub id: Uuid,
pub name: String,
pub email: String,
pub bio: Option<String>,
pub avatar_url: Option<String>,
}
#[derive(Deserialize, Validate)]
pub struct UpdateProfileRequest {
#[validate(length(max = 100))]
pub name: Option<String>,
#[validate(length(max = 500))]
pub bio: Option<String>,
#[validate(url)]
pub avatar_url: Option<String>,
}
\`\`\`
Use \`validator\` crate (already in Cargo.toml) for validation.
**Acceptance Criteria:**
- [ ] DTOs serialize/deserialize correctly
- [ ] Validation rejects invalid inputs
- [ ] Unit tests in \`tests/types/profile_test.rs\`" \
--priority P0 \
--dependencies user-profile-abc1-task-1
# Output: user-profile-abc1-task-2
# Task 3: Implement API handlers
granary project user-profile-abc1 tasks create "Implement profile API handlers" \
--description "**Goal:** Create GET and PUT handlers for profile endpoints.
**Context:** DTOs defined in task-2. User model extended in task-1.
**Files to Modify:**
- \`src/handlers/mod.rs:5\` - Add mod declaration
- \`src/handlers/profile.rs\` - New handler file
- \`src/routes/api.rs:34\` - Register new routes
- \`tests/handlers/profile_test.rs\` - New test file
**Implementation Details:**
Follow handler pattern at \`src/handlers/user.rs:23-67\`:
\`\`\`rust
// GET /api/profile/:id
pub async fn get_profile(
State(pool): State<DbPool>,
Path(user_id): Path<Uuid>,
) -> Result<Json<ProfileResponse>, ApiError> {
let user = UserService::get_by_id(&pool, user_id)
.await?
.ok_or(ApiError::NotFound)?;
Ok(Json(ProfileResponse::from(user)))
}
// PUT /api/profile/:id (requires auth)
pub async fn update_profile(
State(pool): State<DbPool>,
Extension(current_user): Extension<AuthUser>,
Path(user_id): Path<Uuid>,
Json(req): Json<UpdateProfileRequest>,
) -> Result<Json<ProfileResponse>, ApiError> {
// Verify user can only update own profile
if current_user.id != user_id {
return Err(ApiError::Forbidden);
}
req.validate()?;
// ... update logic
}
\`\`\`
Register routes in \`src/routes/api.rs:34\` following existing pattern:
\`\`\`rust
.route(\"/profile/:id\", get(get_profile))
.route(\"/profile/:id\", put(update_profile).layer(auth_middleware()))
\`\`\`
**Acceptance Criteria:**
- [ ] GET returns profile data
- [ ] PUT requires authentication
- [ ] PUT validates ownership (users can only edit own profile)
- [ ] Integration tests pass" \
--priority P1 \
--dependencies user-profile-abc1-task-2
# Output: user-profile-abc1-task-3
# Add steering file with research findings
cat > docs/profile-patterns.md << 'EOF'
# Profile Feature Patterns
## Existing Codebase Patterns
- Handlers use `axum` extractors: `State`, `Path`, `Json`, `Extension`
- Auth middleware at `src/middleware/auth.rs:12` adds `AuthUser` to extensions
- Error handling uses `ApiError` enum at `src/errors/api_error.rs`
- All responses wrapped in `Json<T>`
## Database Access
- Use `UserService` at `src/services/user.rs` for queries
- Connection pool passed via `State<DbPool>`
- Async queries with `sqlx`
## Testing
- Integration tests in `tests/handlers/`
- Use `TestApp::new()` helper at `tests/common/mod.rs:8`
- Mock services with `mockall` when needed
EOF
granary steering add docs/profile-patterns.md --project user-profile-abc1
granary search to find similar projects/tasks before creating duplicates/granary:orchestrate for implementationRemember: Sub-agents only see task descriptions. Every detail they need must be in the task or steering files—they cannot explore or ask questions.