feat(FN-4371): complete Step 4 — triage decision-only heuristic

Fusion-Task-Id: FN-4371
Fusion-Task-Lineage: 4175ba4c-b7cb-4f3a-87c5-75e51f06c0ff
This commit is contained in:
Fusion
2026-05-14 09:38:48 -07:00
committed by gsxdsm
parent 7776557e60
commit c8a2caf948
2 changed files with 44 additions and 1 deletions

View File

@@ -1513,7 +1513,7 @@ describe("approved triage recovery", () => {
await mkdir(join(rootDir, ".fusion", "tasks", "FN-001"), { recursive: true });
await writeFile(
join(rootDir, ".fusion", "tasks", "FN-001", "PROMPT.md"),
"# Task: FN-001\n\n**Size:** M\n\n## Review Level: 2\n\nRecovered specification",
"# Task: FN-001\n\n**Size:** M\n\n**No commits expected:** true\n\n## Review Level: 2\n\nRecovered specification",
);
});
@@ -1558,6 +1558,7 @@ describe("approved triage recovery", () => {
dependencies: ["FN-1247"],
size: "M",
reviewLevel: 2,
noCommitsExpected: true,
});
expect(store.moveTask).toHaveBeenCalledWith("FN-001", "todo");
expect(store.logEntry).toHaveBeenCalledWith(
@@ -1644,6 +1645,13 @@ describe("approved triage recovery", () => {
);
});
it("includes decision-only noCommitsExpected heuristic instructions in system prompts", () => {
expect(TRIAGE_SYSTEM_PROMPT).toContain("**No commits expected:** true");
expect(TRIAGE_SYSTEM_PROMPT).toContain("Decide whether FN-XYZ needs a fix");
expect(TRIAGE_SYSTEM_PROMPT).toContain("Investigate FN-XYZ and fix if needed");
expect(FAST_TRIAGE_SYSTEM_PROMPT).toContain("**No commits expected:** true");
});
it("preserves imported GitHub issue titles during planning recovery", async () => {
await writeFile(
join(rootDir, ".fusion", "tasks", "FN-001", "PROMPT.md"),

View File

@@ -250,6 +250,21 @@ When the planning conversation produces a structured plan, save it as a document
- Testing & Verification must run before Documentation & Delivery
- Avoid giant catch-all steps; split outcomes so execution can be verified incrementally
## Decision-only task flag (noCommitsExpected)
When ALL of the following are true, include this metadata line in the header block after Size/Review Level:
- Add this exact line: **No commits expected:** true
Set it only when all of these conditions hold:
- Title/mission starts with decision verbs like "Decide", "Evaluate", "Verify", "Confirm", "Audit", "Review whether", or "Investigate and report"
- Acceptance criteria are strictly observational (record findings, log a decision, update task log/docs) with no required code/config/file mutations
- Task description explicitly says things like "no code changes expected" or "the deliverable is the recorded decision"
Anti-heuristics (bias to false-negative when ambiguous):
- SET: Decide whether FN-XYZ needs a fix
- LEAVE UNSET: Investigate FN-XYZ
- LEAVE UNSET: Investigate FN-XYZ and fix if needed
## Guidelines
- Read the project structure and relevant source files to understand context BEFORE writing
- Check package.json/scripts and explicit project commands to align real lint/test/build/typecheck commands
@@ -451,6 +466,21 @@ If an existing task already covers the same work, do NOT write a PROMPT.md. Inst
When adding a dependency in \`## Dependencies\`, first call \`fn_task_get\` for that task and read its PROMPT.md.
Use that context to align file paths, APIs, assumptions, and completion expectations. If the dependency has no PROMPT.md yet, note that explicitly.
## Decision-only task flag (noCommitsExpected)
When ALL of the following are true, include this metadata line in the header block after Size:
- Add this exact line: **No commits expected:** true
Set it only when all of these conditions hold:
- Title/mission starts with decision verbs like "Decide", "Evaluate", "Verify", "Confirm", "Audit", "Review whether", or "Investigate and report"
- Acceptance criteria are strictly observational (record findings, log a decision, update task log/docs) with no required code/config/file mutations
- Task description explicitly says things like "no code changes expected" or "the deliverable is the recorded decision"
Anti-heuristics (bias to false-negative when ambiguous):
- SET: Decide whether FN-XYZ needs a fix
- LEAVE UNSET: Investigate FN-XYZ
- LEAVE UNSET: Investigate FN-XYZ and fix if needed
## Guidelines
- Read relevant source files before writing the spec
- Be specific: reference concrete files, modules, and commands from this repo
@@ -2085,6 +2115,11 @@ export class TriageProcessor {
taskUpdates.reviewLevel = parseInt(reviewMatch[1], 10);
}
const noCommitsExpectedMatch = written.match(/^\*\*No commits expected:\*\*\s*(true|yes)\b/im);
if (noCommitsExpectedMatch) {
taskUpdates.noCommitsExpected = true;
}
// Apply non-title metadata first. The title is held back and applied AFTER
// the column transition (see below) because store.updateTask regenerates
// PROMPT.md when title/description change, and the triage-stub regen path