Skip to content

Commit df3764e

Browse files
committed
Refactor plan file template for clarity and structure
1 parent 91a3c73 commit df3764e

File tree

9 files changed

+239
-563
lines changed

9 files changed

+239
-563
lines changed

docs/guides/PLAN_MODE.md

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -44,47 +44,28 @@ vtcode --permission-mode plan
4444
1. **Start in Plan Mode**: `vtcode --permission-mode plan`
4545
2. **Describe your goal**: Explain what you want to build or change
4646
3. **Iterate on the plan**: Ask clarifying questions, explore files, refine the approach
47-
4. **Review the plan**: The agent emits a structured reasoning + decision log, then one `<proposed_plan>` block
47+
4. **Review the plan**: The agent captures repository facts, closes any open decisions, then emits one `<proposed_plan>` block
4848
5. **Choose next action**: Use the implementation prompt to switch to Edit mode or continue planning (fallback: manually switch with `/plan off` or `/mode`, or `Shift+Tab`/`Alt+M`)
4949
6. **Execute the plan**: If approved, coding proceeds in Edit mode
5050

5151
## Plan Output Format
5252

53-
When in Plan Mode, the agent should follow this exact structure:
53+
When in Plan Mode, the agent should keep the output decision-complete but sparse:
5454

5555
```markdown
56-
• Scope checkpoint: [what is locked] / [what remains open].
57-
• Decision needed: [single high-impact choice] and why it affects
58-
implementation.
56+
Repository facts checked:
57+
- [file, symbol, or behavior confirmed from the repo]
58+
- [existing pattern or constraint verified before planning]
5959

60-
• Questions 1/1 answered
61-
[exact question text]
62-
answer: [selected option label]
63-
64-
• Locked decision: [choice], so implementation will [concrete consequence].
65-
• Next open decision: [if any], otherwise: "No remaining scope decisions;
66-
drafting final plan."
60+
Next open decision: [if any], otherwise: No remaining scope decisions.
6761

6862
<proposed_plan>
69-
• Proposed Plan
70-
71-
7263
# [Task Title]
7364

7465
## Summary
7566
[2-4 lines: goal, user impact, what will change, what will not]
7667

77-
## Scope Locked
78-
1. [Decision A]
79-
2. [Decision B]
80-
3. [Decision C]
81-
82-
## Public API / Interface Changes
83-
1. [Removed/added/changed API, command, config, schema]
84-
2. [Tooling/runtime behavior changes]
85-
3. [Compatibility or break behavior]
86-
87-
## Implementation Plan
68+
## Implementation Steps
8869
1. [Step] -> files: [paths] -> verify: [check]
8970
2. [Step] -> files: [paths] -> verify: [check]
9071
3. [Step] -> files: [paths] -> verify: [check]
@@ -93,15 +74,14 @@ drafting final plan."
9374
1. Build and lint: [project build and lint command(s) based on detected toolchain]
9475
2. Tests: [project test command(s) based on detected toolchain]
9576
3. Targeted behavior checks: [explicit commands/manual checks]
96-
4. Regression checks: [what must not break]
9777

9878
## Assumptions and Defaults
9979
1. [Explicit assumption]
10080
2. [Default chosen when user did not specify]
10181
3. [Out-of-scope items intentionally not changed]
10282
</proposed_plan>
10383

104-
> Note: Edit this plan directly at `[plan file path]`.
84+
Only `Next open decision` is used as the explicit reopen marker for follow-up planning.
10585
```
10686

10787
## Plan Review Gate

src/agent/runloop/unified/incremental_system_prompt_tests.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,8 +450,9 @@ async fn test_plan_mode_notice_appended() {
450450
assert!(prompt.contains(vtcode_core::prompts::system::PLAN_MODE_READ_ONLY_HEADER));
451451
assert!(prompt.contains(vtcode_core::prompts::system::PLAN_MODE_EXIT_INSTRUCTION_LINE));
452452
assert!(prompt.contains(vtcode_core::prompts::system::PLAN_MODE_PLAN_QUALITY_LINE));
453-
assert!(prompt.contains("Scope checkpoint"));
454-
assert!(prompt.contains("• Proposed Plan"));
453+
assert!(prompt.contains("<proposed_plan>"));
454+
assert!(prompt.contains("Next open decision"));
455+
assert!(!prompt.contains("Scope checkpoint"));
455456
assert!(prompt.contains(vtcode_core::prompts::system::PLAN_MODE_NO_AUTO_EXIT_LINE));
456457
assert!(prompt.contains(vtcode_core::prompts::system::PLAN_MODE_TASK_TRACKER_LINE));
457458
}

src/agent/runloop/unified/turn/turn_processing/plan_mode.rs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -449,11 +449,6 @@ pub(crate) fn maybe_force_plan_mode_interview(
449449
}
450450

451451
fn has_open_decision_markers(text: &str) -> bool {
452-
let lowered = text.to_ascii_lowercase();
453-
if contains_any(&lowered, &["to be decided", "tbd", "pending decision"]) {
454-
return true;
455-
}
456-
457452
text.lines().any(line_has_open_decision_marker)
458453
}
459454

@@ -464,22 +459,15 @@ fn line_has_open_decision_marker(line: &str) -> bool {
464459
}
465460

466461
let lower = trimmed.to_ascii_lowercase();
467-
if !contains_any(
468-
&lower,
469-
&[
470-
"decision needed",
471-
"next open decision",
472-
"open question",
473-
"unresolved decision",
474-
],
475-
) {
462+
if !lower.contains("next open decision") {
476463
return false;
477464
}
478465

479466
!contains_any(
480467
&lower,
481468
&[
482469
"none",
470+
"no remaining",
483471
"no further",
484472
"resolved",
485473
"closed",

src/agent/runloop/unified/turn/turn_processing/plan_mode/tests.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,20 @@ fn collect_interview_research_context_includes_custom_note_policy() {
488488
);
489489
}
490490

491+
#[test]
492+
fn line_has_open_decision_marker_only_tracks_next_open_decision() {
493+
assert!(line_has_open_decision_marker(
494+
"Next open decision: validate migration order"
495+
));
496+
assert!(!line_has_open_decision_marker(
497+
"Decision needed: choose validation scope"
498+
));
499+
assert!(!line_has_open_decision_marker("Next open decision: none"));
500+
assert!(!line_has_open_decision_marker(
501+
"Next open decision: No remaining scope decisions."
502+
));
503+
}
504+
491505
#[test]
492506
fn collect_interview_research_context_extracts_recent_paths_and_symbols() {
493507
let stats = SessionStats::default();

src/agent/runloop/unified/turn/turn_processing/response_processing.rs

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -672,43 +672,30 @@ mod tests {
672672
}
673673

674674
#[test]
675-
fn process_llm_response_golden_transcript_requires_kiss_dry_plan_sections() {
675+
fn process_llm_response_extracts_sparse_proposed_plan_blocks() {
676676
let response = LLMResponse {
677677
content: Some(
678678
r#"Intro
679+
Next open decision: none.
679680
<proposed_plan>
680-
• Proposed Plan
681-
682-
# Enforce Plan Mode Blueprint
681+
# Apply Slate-Style Prompting
683682
684683
## Summary
685-
Make Plan Mode output deterministic by enforcing one shared blueprint for reasoning logs and final plans.
686-
687-
## Scope Locked
688-
1. Use hard contract enforcement.
689-
2. Keep the 4-way HITL gate text.
690-
3. Remove legacy section contract tests.
691-
692-
## Public API / Interface Changes
693-
1. Update plan prompt contract wording.
694-
2. Update Plan Mode scaffold template.
695-
3. Treat old section contract checks as deprecated.
684+
Keep the default runtime prompt sparse and consistent with Plan Mode.
696685
697-
## Implementation Plan
686+
## Implementation Steps
698687
1. Update prompt constants -> files: [vtcode-core/src/prompts/system.rs] -> verify: [cargo check]
699-
2. Update template + docs -> files: [vtcode-core/src/tools/handlers/plan_mode.rs, docs/guides/PLAN_MODE.md] -> verify: [cargo check]
700-
3. Update tests -> files: [src/agent/runloop/unified/turn/turn_processing/response_processing.rs] -> verify: [cargo test -p vtcode process_llm_response_golden_transcript_requires_kiss_dry_plan_sections -- --nocapture]
688+
2. Update plan scaffold -> files: [vtcode-core/src/tools/handlers/plan_mode.rs] -> verify: [cargo test -p vtcode-core test_enter_plan_mode -- --nocapture]
689+
3. Update parser tests -> files: [src/agent/runloop/unified/turn/turn_processing/response_processing.rs] -> verify: [cargo test -p vtcode process_llm_response_extracts_sparse_proposed_plan_blocks -- --nocapture]
701690
702691
## Test Cases and Validation
703692
1. Build and lint: [project build and lint command(s) based on detected toolchain]
704693
2. Tests: [project test command(s) based on detected toolchain]
705-
3. Targeted behavior checks: plan-mode transcript checks
706-
4. Regression checks: proposed plan extraction still strips wrapper tags only
694+
3. Targeted behavior checks: plan-mode transcript extraction
707695
708696
## Assumptions and Defaults
709-
1. Title/content can vary, section headers stay fixed.
710-
2. Reasoning log appears before proposed plan.
711-
3. Non-plan execution flow remains unchanged.
697+
1. `Next open decision` remains the only explicit reopen marker.
698+
2. The proposed plan stays decision-complete without rigid extra sections.
712699
</proposed_plan>
713700
Outro"#
714701
.to_string(),
@@ -737,11 +724,8 @@ Outro"#
737724
} => {
738725
let plan = proposed_plan.expect("expected extracted proposed plan");
739726
for required_section in [
740-
"• Proposed Plan",
741727
"## Summary",
742-
"## Scope Locked",
743-
"## Public API / Interface Changes",
744-
"## Implementation Plan",
728+
"## Implementation Steps",
745729
"## Test Cases and Validation",
746730
"## Assumptions and Defaults",
747731
] {
@@ -750,6 +734,8 @@ Outro"#
750734
"proposed_plan missing required section: {required_section}"
751735
);
752736
}
737+
assert!(!plan.contains("## Scope Locked"));
738+
assert!(!plan.contains("## Public API / Interface Changes"));
753739
assert!(!text.contains("<proposed_plan>"));
754740
assert!(text.contains("Intro"));
755741
assert!(text.contains("Outro"));

src/agent/runloop/welcome.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub(crate) async fn prepare_session_bootstrap(
7373
&onboarding_cfg,
7474
language_summary.as_deref(),
7575
guideline_highlights.as_deref(),
76+
todo_planning_enabled,
7677
)
7778
} else {
7879
None
@@ -331,6 +332,7 @@ fn build_prompt_addendum(
331332
onboarding_cfg: &AgentOnboardingConfig,
332333
language_summary: Option<&str>,
333334
guideline_highlights: Option<&[String]>,
335+
todo_planning_enabled: bool,
334336
) -> Option<String> {
335337
let mut lines = Vec::with_capacity(15); // Estimate 15 lines for prompt addendum
336338
lines.push("## SESSION CONTEXT".to_string());
@@ -352,6 +354,11 @@ fn build_prompt_addendum(
352354
}
353355
}
354356

357+
if todo_planning_enabled {
358+
lines.push("### Workflow Hint".to_string());
359+
lines.push("- Start with the goal, use `@file` or IDE context to focus, switch to Plan Mode for research/spec work, and use `task_tracker` for multi-step execution.".to_string());
360+
}
361+
355362
push_prompt_usage_tips(&mut lines, &onboarding_cfg.usage_tips);
356363
push_prompt_recommended_actions(&mut lines, &onboarding_cfg.recommended_actions);
357364

src/agent/runloop/welcome_tests.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ async fn test_prepare_session_bootstrap_builds_sections() {
110110

111111
let prompt = bootstrap.prompt_addendum.expect("prompt addendum");
112112
assert!(prompt.contains("## SESSION CONTEXT"));
113+
assert!(prompt.contains("Workflow Hint"));
114+
assert!(prompt.contains("@file"));
115+
assert!(prompt.contains("Plan Mode"));
116+
assert!(prompt.contains("task_tracker"));
113117
assert!(prompt.contains("Suggested Next Actions"));
114118

115119
assert_eq!(bootstrap.placeholder.as_deref(), Some("Type your plan"));
@@ -212,4 +216,9 @@ async fn test_prepare_session_bootstrap_hides_placeholder_when_planning_disabled
212216

213217
let bootstrap = prepare_session_bootstrap(&runtime_cfg, Some(&vt_cfg), None).await;
214218
assert!(bootstrap.placeholder.is_none());
219+
220+
if let Some(prompt) = bootstrap.prompt_addendum.as_deref() {
221+
assert!(!prompt.contains("Workflow Hint"));
222+
assert!(!prompt.contains("task_tracker"));
223+
}
215224
}

0 commit comments

Comments
 (0)