[[task]]
name = "fix-truncate-dup"
agent = "opencode"
skills = ["implementer"]
prompt = """
Fix unsafe byte-level truncate in src/cmd/run.rs by replacing it with the existing safe truncate_text from src/agent/truncate.rs.
BUG: src/cmd/run.rs line 343-349 has a `truncate()` function that does `&s[..max.saturating_sub(3)]` which panics on multi-byte UTF-8 characters. Meanwhile, src/agent/truncate.rs already has a safe `truncate_text()` using `floor_char_boundary`.
FIX:
1. In src/cmd/run.rs, delete the local `truncate()` function (lines 343-349)
2. Replace all calls to `truncate(...)` in that file with `crate::agent::truncate::truncate_text(...)`
3. The truncate_text function also replaces newlines with spaces, which is fine for prompt display
CONSTRAINTS:
- Only modify src/cmd/run.rs
- Do NOT reformat existing code
- cargo check and cargo test must pass
"""
dir = "."
worktree = "fix/truncate-dup"
verify = "auto"
[[task]]
name = "fix-bg-parent-id"
agent = "opencode"
skills = ["implementer"]
prompt = """
Add parent_task_id to BackgroundRunSpec so background retries preserve the parent link.
BUG: BackgroundRunSpec in src/background.rs (lines 18-36) has no parent_task_id field.
When retry_if_needed runs from run_task_inner (background mode), the retry's RunArgs gets
parent_task_id set at src/cmd/run.rs:270, but when that retry itself runs in background mode,
the parent_task_id is lost because BackgroundRunSpec doesn't carry it.
FIX:
1. In src/background.rs BackgroundRunSpec struct, add:
#[serde(default)]
pub parent_task_id: Option<String>,
2. In src/cmd/run.rs where BackgroundRunSpec is constructed (search for BackgroundRunSpec {),
add: parent_task_id: args.parent_task_id.clone(),
3. In src/background.rs run_task_inner() where RunArgs is constructed from the spec
(search for RunArgs {), add: parent_task_id: spec.parent_task_id.clone(),
CONSTRAINTS:
- Only modify src/background.rs and src/cmd/run.rs
- ~3 lines of actual change
- Do NOT reformat existing code
- cargo check and cargo test must pass
"""
dir = "."
worktree = "fix/bg-parent-id"
verify = "auto"
[[task]]
name = "fix-gemini-model"
agent = "opencode"
skills = ["implementer"]
prompt = """
Extract model name from Gemini JSON output so usage reports show the actual model instead of None.
CURRENT: src/agent/gemini.rs parse_completion() returns CompletionInfo with model: None.
The extract_tokens() function parses the JSON but never looks for the model field.
FIX: Add an extract_model() function and use it in parse_completion().
1. Add to src/agent/gemini.rs after extract_tokens():
fn extract_model(v: &serde_json::Value) -> Option<String> {
// Try common paths in gemini CLI JSON output
for path in ["/modelVersion", "/model", "/stats/models/0/model"] {
if let Some(m) = v.pointer(path).and_then(|v| v.as_str()) {
return Some(m.to_string());
}
}
None
}
2. In parse_completion() (around line 38), after calling extract_tokens(),
also call extract_model() and set it on CompletionInfo:
model: extract_model(&v),
3. Add a unit test that verifies extract_model parses a sample gemini JSON blob.
CONSTRAINTS:
- Only modify src/agent/gemini.rs
- ~15 lines of new code
- Do NOT reformat existing code
- cargo check and cargo test must pass
"""
dir = "."
worktree = "fix/gemini-model"
verify = "auto"
[[task]]
name = "refactor-run-retry"
agent = "codex"
skills = ["implementer"]
prompt = """
Extract retry logic from src/cmd/run.rs into a separate module to reduce file size (504 lines → under 400).
CURRENT STATE:
- src/cmd/run.rs has retry-related functions at lines 229-273 and 393-438:
- retry_if_needed (lines 229-273): async, re-dispatches failed tasks
- read_stderr_tail (lines 393-404): reads last N lines of stderr log
- retry_depth (lines 406-417): walks parent chain to count depth
- backoff_for_attempt (lines 419-425): exponential backoff calculator
- root_prompt (lines 427-438): walks parent chain to find original prompt
REFACTOR:
1. Create src/cmd/retry_logic.rs with these functions moved from run.rs:
- retry_if_needed (pub(crate))
- read_stderr_tail (pub(crate) — also used by show.rs explain)
- retry_depth (private)
- backoff_for_attempt (private)
- root_prompt (private)
2. The file header should be:
// Retry logic for failed tasks: depth tracking, backoff, re-dispatch.
// Called from run.rs on task failure when --retry > 0.
3. retry_if_needed calls run::run() recursively via Box::pin. To avoid circular imports,
have retry_logic.rs accept a closure or function pointer for the re-dispatch,
OR keep the Box::pin(run(...)) call in run.rs and have retry_logic.rs return
a RetryAction enum (Retry(RunArgs) | Skip | Stop) that run.rs acts on.
Preferred approach: retry_logic.rs exports prepare_retry() that returns Option<RunArgs>.
run.rs calls prepare_retry(), and if Some(retry_args), calls Box::pin(run(store, retry_args)).
4. Update src/cmd/mod.rs to add: pub mod retry_logic;
5. Update run.rs to import and call retry_logic::prepare_retry() instead of the moved functions.
6. Move the two tests for effective_skills that are in run.rs's #[cfg(test)] — leave them in run.rs (they test run.rs code). Add new tests in retry_logic.rs for:
- backoff_for_attempt returns increasing values
- retry_depth returns 0 for task with no parent
CONSTRAINTS:
- Create src/cmd/retry_logic.rs (under 120 lines)
- Modify src/cmd/run.rs (should drop to under 400 lines)
- Modify src/cmd/mod.rs (add the module)
- Do NOT reformat existing code in run.rs beyond what's needed for the extraction
- cargo check and cargo test must pass
"""
dir = "."
worktree = "refactor/run-retry"
verify = "auto"
[[task]]
name = "refactor-show-explain"
agent = "codex"
skills = ["implementer"]
prompt = """
Extract the explain subsystem from src/cmd/show.rs into src/cmd/explain.rs to reduce file size (463 lines → under 350).
CURRENT STATE:
- src/cmd/show.rs lines 171-314 contain the explain feature:
- run_explain (async fn, lines 171-208): dispatches a gemini task to analyze another task
- build_explain_context (lines ~210-240): gathers task info, events, stderr, log
- build_explain_prompt (lines ~242-270): formats the analysis prompt
- format_task_info (lines ~272-295): formats task metadata
- format_events (lines ~297-314): formats event list
REFACTOR:
1. Create src/cmd/explain.rs with all 5 functions moved from show.rs:
- run_explain (pub(crate) async)
- build_explain_context (pub(crate) for testing)
- build_explain_prompt (pub(crate) for testing)
- format_task_info (private)
- format_events (private)
2. File header:
// AI-powered task explanation: dispatches a research agent to analyze task artifacts.
// Called from show.rs when `aid show --explain` is used.
3. explain.rs will need these imports from show.rs:
- load_task, read_tail (these are shared helpers in show.rs)
- Make load_task and read_tail pub(crate) in show.rs so explain.rs can use them
4. Update src/cmd/mod.rs to add: pub mod explain;
5. In show.rs, replace the call to run_explain with cmd::explain::run_explain.
6. Move the build_explain_prompt test from show.rs's #[cfg(test)] to explain.rs's #[cfg(test)].
CONSTRAINTS:
- Create src/cmd/explain.rs (under 150 lines)
- Modify src/cmd/show.rs (should drop to under 350 lines)
- Modify src/cmd/mod.rs (add the module)
- Do NOT reformat existing code beyond what's needed
- cargo check and cargo test must pass
"""
dir = "."
worktree = "refactor/show-explain"
verify = "auto"
[[task]]
name = "test-retry-logic"
agent = "codex"
skills = ["implementer", "test-writer"]
depends_on = ["refactor-run-retry"]
prompt = """
Add comprehensive tests for the retry logic in src/cmd/retry_logic.rs.
After the refactor-run-retry task extracted retry logic into retry_logic.rs,
add thorough unit tests for the extracted functions.
TESTS TO ADD (in retry_logic.rs #[cfg(test)]):
1. test_backoff_exponential — verify backoff increases: attempt 1 → 2s, 2 → 4s, 3 → 8s, capped reasonably
2. test_retry_depth_no_parent — task with no parent_task_id → depth 0
3. test_retry_depth_with_chain — create 3 tasks in store with parent links, verify depth = 2
4. test_prepare_retry_skips_on_success — task with status Done → returns None
5. test_prepare_retry_skips_on_zero_retries — retry count 0 → returns None
6. test_prepare_retry_returns_args — failed task with retry > 0 → returns Some(RunArgs) with correct prompt containing error context
7. test_prepare_retry_stops_on_identical_stderr — same stderr as parent → returns None
Use Store::open_memory() for in-memory DB in tests.
CONSTRAINTS:
- Only modify src/cmd/retry_logic.rs (add #[cfg(test)] block)
- Tests must use real Store (open_memory), not mocks
- cargo test must pass
"""
dir = "."
worktree = "test/retry-logic"
verify = "auto"
[[task]]
name = "test-e2e-workflow"
agent = "codex"
skills = ["implementer", "test-writer"]
prompt = """
Add E2E tests that exercise core aid workflows via the compiled binary.
EXISTING E2E TESTS: tests/e2e_test.rs has basic CLI tests (--help, board, watch, config, group CRUD).
EXISTING PATTERN: tests use `Command::cargo_bin("aid")` style via std::process::Command pointing to cargo build output.
ADD these E2E tests in a new file tests/e2e_workflow.rs:
1. test_run_with_fake_agent — Create a fake agent script that echoes output, run `aid run <fake> "test prompt" --dir .`,
verify task appears in `aid board` output and `aid show <task-id>` works.
Fake agent setup (use a temp dir with a shell script):
- Create a script called "fake-agent" that prints "[MILESTONE] started" then "Hello from fake agent" then exits 0
- Set PATH to include the temp dir
- This requires setting AID_HOME to a temp dir too
2. test_show_output_mode — Run a task with `--output /tmp/test-output.txt` using the fake agent,
then verify `aid show --output <task-id>` reads the file content.
3. test_batch_dispatch — Create a simple batch TOML with 2 tasks using fake agent,
run `aid batch test.toml --parallel --wait`, verify both tasks appear in board as Done.
4. test_board_filters — Create tasks, then verify `aid board --today` shows them
and `aid board --mine` filters correctly.
Set AID_HOME to a temp directory in each test to isolate state.
Use #[ignore] attribute on tests that require the aid binary to be built first (cargo test -- --ignored to run them).
CONSTRAINTS:
- Create tests/e2e_workflow.rs
- Use std::process::Command to invoke the aid binary
- Each test must clean up its temp dirs
- Tests should be #[ignore] since they need a built binary
- cargo test (without --ignored) must still pass
- cargo test -- --ignored should pass the new tests
"""
dir = "."
worktree = "test/e2e-workflow"
verify = "auto"