Skip to content
This repository was archived by the owner on Apr 11, 2026. It is now read-only.

Commit de49f78

Browse files
committed
Merge branch 'fn-23-investigation-targets-in-task-specs'
2 parents f24c273 + 149f41c commit de49f78

7 files changed

Lines changed: 109 additions & 8 deletions

File tree

agents/plan-sync.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ Look for references to:
8181
- Names/APIs from completed task spec (now stale)
8282
- Assumptions about data structures
8383
- Integration points that changed
84+
- File paths in `## Investigation targets` sections — if the completed task renamed or moved files that are listed as Required/Optional targets in downstream tasks, those paths are now stale
8485

8586
Flag tasks that need updates.
8687

@@ -130,6 +131,7 @@ Changes should:
130131
- Update variable/function names to match actual
131132
- Correct API signatures
132133
- Fix data structure assumptions
134+
- Update stale file paths in `## Investigation targets` (e.g., if `src/old.ts` was moved to `src/new.ts`)
133135
- Add note: `<!-- Updated by plan-sync: fn-X.Y used <actual> not <planned> -->`
134136

135137
**DO NOT:**

agents/worker.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,40 @@ git diff --stat HEAD 2>/dev/null || true
219219
Save `GIT_BASELINE_REV` — you'll use it in Phase 5 to generate workspace change evidence.
220220
<!-- /section:core -->
221221

222+
<!-- section:core -->
223+
## Phase 1.5: Pre-implementation Investigation
224+
225+
**If the task spec contains `## Investigation targets` with content, execute this phase. Otherwise skip to Phase 2a/2.**
226+
227+
1. **Read every Required file** listed before writing any code. Note:
228+
- Patterns to follow (function signatures, naming conventions, structure)
229+
- Constraints discovered (validation rules, type contracts, env requirements)
230+
- Anything surprising that might affect your approach
231+
232+
2. **Similar functionality search** — before writing new code:
233+
```bash
234+
# Search for functions/modules that do similar things
235+
# Use terms from the task description + acceptance criteria
236+
grep -r "<key domain term>" --include="*.rs" --include="*.ts" --include="*.py" -l src/
237+
```
238+
If similar functionality exists, pick one:
239+
- **Reuse**: Use the existing code directly
240+
- **Extend**: Modify existing code to support the new case
241+
- **New**: Create new code (justify why existing isn't suitable)
242+
243+
Report what you found:
244+
```
245+
Investigation results:
246+
- Found: `existingHelper()` in src/utils.ts:23 — reusing
247+
- Found: `src/routes/api.ts:45` — following this pattern
248+
- No existing implementation found — creating new
249+
```
250+
251+
3. Read **Optional** files as needed based on Step 1 findings.
252+
253+
4. Continue to Phase 2a/2 only after investigation is complete.
254+
<!-- /section:core -->
255+
222256
<!-- section:tdd -->
223257
## Phase 2a: TDD Red-Green (if TDD_MODE=true)
224258

flowctl/crates/flowctl-cli/src/commands/task/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ pub enum TaskCmd {
5757
/// Acceptance section file.
5858
#[arg(long, alias = "acceptance")]
5959
accept: Option<String>,
60+
/// Investigation targets section file.
61+
#[arg(long)]
62+
investigation: Option<String>,
6063
},
6164
/// Reset task to todo.
6265
Reset {
@@ -171,7 +174,7 @@ fn parse_domain(s: &str) -> Domain {
171174
fn create_task_spec(id: &str, title: &str, acceptance: Option<&str>) -> String {
172175
let acceptance_content = acceptance.unwrap_or("- [ ] TBD");
173176
format!(
174-
"# {} {}\n\n## Description\nTBD\n\n## Acceptance\n{}\n\n## Done summary\nTBD\n\n## Evidence\n- Commits:\n- Tests:\n- PRs:\n",
177+
"# {} {}\n\n## Description\nTBD\n\n## Investigation targets\n\n## Acceptance\n{}\n\n## Done summary\nTBD\n\n## Evidence\n- Commits:\n- Tests:\n- PRs:\n",
175178
id, title, acceptance_content
176179
)
177180
}
@@ -338,7 +341,8 @@ pub fn dispatch(cmd: &TaskCmd, json: bool) {
338341
file,
339342
desc,
340343
accept,
341-
} => query::cmd_task_set_spec(json, id, file.as_deref(), desc.as_deref(), accept.as_deref()),
344+
investigation,
345+
} => query::cmd_task_set_spec(json, id, file.as_deref(), desc.as_deref(), accept.as_deref(), investigation.as_deref()),
342346
TaskCmd::Reset { task_id, cascade } => mutate::cmd_task_reset(json, task_id, *cascade),
343347
TaskCmd::Skip { task_id, reason } => mutate::cmd_task_skip(json, task_id, reason.as_deref()),
344348
TaskCmd::Split {

flowctl/crates/flowctl-cli/src/commands/task/query.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub(super) fn cmd_task_set_spec(
1818
file: Option<&str>,
1919
description: Option<&str>,
2020
acceptance: Option<&str>,
21+
investigation: Option<&str>,
2122
) {
2223
let flow_dir = ensure_flow_exists();
2324

@@ -28,8 +29,8 @@ pub(super) fn cmd_task_set_spec(
2829
));
2930
}
3031

31-
if file.is_none() && description.is_none() && acceptance.is_none() {
32-
error_exit("Requires --file, --description, or --acceptance");
32+
if file.is_none() && description.is_none() && acceptance.is_none() && investigation.is_none() {
33+
error_exit("Requires --file, --description, --acceptance, or --investigation");
3334
}
3435

3536
let mut doc = load_task_doc(&flow_dir, task_id);
@@ -67,6 +68,12 @@ pub(super) fn cmd_task_set_spec(
6768
sections_updated.push("## Acceptance");
6869
}
6970

71+
if let Some(inv_file) = investigation {
72+
let inv_content = read_file_or_stdin(inv_file);
73+
doc.body = patch_body_section(&doc.body, "## Investigation targets", &inv_content);
74+
sections_updated.push("## Investigation targets");
75+
}
76+
7077
doc.frontmatter.updated_at = Utc::now();
7178
write_task_doc(&flow_dir, task_id, &doc);
7279

flowctl/crates/flowctl-core/src/types.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ impl std::fmt::Display for PhaseStatus {
305305
pub const PHASE_DEFS: &[(&str, &str, &str)] = &[
306306
("0", "Verify Configuration", "OWNED_FILES verified and configuration validated"),
307307
("1", "Re-anchor", "Run flowctl show <task> and verify spec was read"),
308+
("1.5", "Investigation", "Required investigation target files read and patterns noted"),
308309
("2a", "TDD Red-Green", "Failing tests written and confirmed to fail"),
309310
("2", "Implement", "Feature implemented and code compiles"),
310311
("2.5", "Verify & Fix", "flowctl guard passes and diff reviewed"),
@@ -319,9 +320,9 @@ pub const PHASE_DEFS: &[(&str, &str, &str)] = &[
319320
/// Phase sequences by mode (from Python constants.py).
320321
/// Phase `5c` (outputs_dump) is NOT in these static sequences — it is added
321322
/// dynamically by `worker-phase next` based on the `outputs.enabled` config.
322-
pub const PHASE_SEQ_DEFAULT: &[&str] = &["0", "1", "2", "2.5", "3", "5", "5b", "6"];
323-
pub const PHASE_SEQ_TDD: &[&str] = &["0", "1", "2a", "2", "2.5", "3", "5", "5b", "6"];
324-
pub const PHASE_SEQ_REVIEW: &[&str] = &["0", "1", "2", "2.5", "3", "4", "5", "5b", "6"];
323+
pub const PHASE_SEQ_DEFAULT: &[&str] = &["0", "1", "1.5", "2", "2.5", "3", "5", "5b", "6"];
324+
pub const PHASE_SEQ_TDD: &[&str] = &["0", "1", "1.5", "2a", "2", "2.5", "3", "5", "5b", "6"];
325+
pub const PHASE_SEQ_REVIEW: &[&str] = &["0", "1", "1.5", "2", "2.5", "3", "4", "5", "5b", "6"];
325326

326327
// ── Evidence ─────────────────────────────────────────────────────────
327328

@@ -551,7 +552,7 @@ mod tests {
551552

552553
#[test]
553554
fn test_phase_defs_complete() {
554-
assert_eq!(PHASE_DEFS.len(), 11);
555+
assert_eq!(PHASE_DEFS.len(), 12);
555556
// Verify all phase sequences reference valid phase IDs
556557
let valid_ids: Vec<&str> = PHASE_DEFS.iter().map(|(id, _, _)| *id).collect();
557558
for seq_id in PHASE_SEQ_DEFAULT {

skills/flow-code-plan/examples.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,43 @@ flowchart LR
332332

333333
---
334334

335+
## Good vs Bad: Investigation Targets
336+
337+
### ✅ GOOD: Specific paths with purpose
338+
339+
```markdown
340+
## Investigation targets
341+
**Required** (read before coding):
342+
- `flowctl/crates/flowctl-cli/src/commands/task/mod.rs:170-177` — existing task spec template to extend
343+
- `flowctl/crates/flowctl-cli/src/commands/task/query.rs:15-82``--desc`/`--accept` pattern to follow
344+
345+
**Optional** (reference as needed):
346+
- `flowctl/crates/flowctl-core/src/types.rs:305-325` — phase registry structure
347+
```
348+
349+
**Why it works**: Exact paths with line ranges. Clear Required vs Optional. Brief purpose descriptions. Worker reads these before coding, grounding implementation in real patterns.
350+
351+
### ❌ BAD: Vague or overloaded targets
352+
353+
```markdown
354+
## Investigation targets
355+
- `src/` — look at the source code
356+
- `tests/` — check the tests
357+
- `lib/utils.ts` — might be useful
358+
- `src/auth/oauth.ts`
359+
- `src/auth/session.ts`
360+
- `src/auth/middleware.ts`
361+
- `src/auth/types.ts`
362+
- `src/auth/index.ts`
363+
- `src/auth/helpers.ts`
364+
- `src/auth/validators.ts`
365+
- `src/auth/errors.ts`
366+
```
367+
368+
**Why it's bad**: No Required/Optional labels. Vague descriptions ("might be useful"). Directory-level paths waste context. Too many targets (11) — worker reads everything and remembers nothing. Max 5-7 targets per task.
369+
370+
---
371+
335372
## Summary
336373

337374
| Include in specs | Don't include |
@@ -342,3 +379,4 @@ flowchart LR
342379
| Recent/surprising APIs | Obvious patterns |
343380
| Non-obvious gotchas | Every function body |
344381
| Acceptance criteria | Redundant details |
382+
| Investigation targets (Required/Optional) | Vague directory paths |

skills/flow-code-plan/steps.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,14 @@ Default to standard unless complexity demands more or less.
282282
- Follow pattern at `src/example.ts:42`
283283
- Reuse `existingHelper()` from `lib/utils.ts`
284284
285+
## Investigation targets
286+
**Required** (read before coding):
287+
- `src/auth/oauth.ts` — existing OAuth flow to extend
288+
- `src/middleware/session.ts:23-45` — session validation pattern
289+
290+
**Optional** (reference as needed):
291+
- `src/auth/*.test.ts` — existing test patterns
292+
285293
## Key context
286294
[Only for recent API changes, surprising patterns, or non-obvious gotchas]
287295
[If stack config exists, include relevant framework conventions here]
@@ -291,6 +299,13 @@ Default to standard unless complexity demands more or less.
291299
- [ ] Criterion 2
292300
```
293301
302+
**Investigation targets rules:**
303+
- Max 5-7 targets per task — enough to ground the worker, not so many it wastes context
304+
- Use exact file paths with optional line ranges (e.g., `src/auth.ts:23-45`)
305+
- **Required** = must read before implementing. **Optional** = helpful reference
306+
- Auto-populated from repo-scout/context-scout findings in Step 1 research
307+
- If no relevant files found by scouts, leave the section empty (worker skips Phase 1.5)
308+
294309
**Layer field**: If stack config is set, tag each task with its primary layer. This helps the worker select the right guard commands (e.g., `pytest` for backend, `pnpm test` for frontend). Full-stack tasks run all guards.
295310
296311
7. Add task dependencies (if not already set via `--deps`):

0 commit comments

Comments
 (0)