feat(epic-execute): bound design-phase context and persist plans
Design phase improvements #1 and #2: #1 Bounded context: - Pass architecture.md by path instead of embedding full contents (the main unbounded size risk in this prompt) - Cap decision-log context at last 20KB (matches dev phase) - Add log_prompt_size guard, consistent with other phases - Replace hardcoded JS/TS exploration hints with language-agnostic guidance #2 File persistence: - Add DESIGN_DIR config and persist each plan to <DESIGN_DIR>/<story>-design.md - build_design_context_for_dev falls back to the persisted file when the in-memory plan is empty, so resumed runs keep their design context Story file remains inlined (small, bounded, needed in full by the planner). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cf03b6f50d
commit
8a41477ae0
|
|
@ -32,21 +32,30 @@ execute_design_phase() {
|
||||||
|
|
||||||
log ">>> DESIGN PHASE: $story_id"
|
log ">>> DESIGN PHASE: $story_id"
|
||||||
|
|
||||||
|
# Story is inlined (small, bounded, and the planner needs it in full)
|
||||||
local story_contents=$(cat "$story_file")
|
local story_contents=$(cat "$story_file")
|
||||||
|
|
||||||
# Load architecture file if available
|
# Locate the architecture file but pass it by PATH, not embedded contents.
|
||||||
local arch_contents=""
|
# architecture.md is the main unbounded size risk in this prompt.
|
||||||
|
local arch_file=""
|
||||||
for search_path in "$PROJECT_ROOT/docs/architecture.md" "$PROJECT_ROOT/docs/architecture/architecture.md" "$PROJECT_ROOT/architecture.md"; do
|
for search_path in "$PROJECT_ROOT/docs/architecture.md" "$PROJECT_ROOT/docs/architecture/architecture.md" "$PROJECT_ROOT/architecture.md"; do
|
||||||
if [ -f "$search_path" ]; then
|
if [ -f "$search_path" ]; then
|
||||||
arch_contents=$(cat "$search_path")
|
arch_file="$search_path"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Load previous decisions for context
|
# Load previous decisions for context, bounded to the last 20KB
|
||||||
|
# (matches the dev phase; the decision log grows across the epic).
|
||||||
local decision_context=""
|
local decision_context=""
|
||||||
if type get_decision_log_context >/dev/null 2>&1; then
|
if type get_decision_log_context >/dev/null 2>&1; then
|
||||||
decision_context=$(get_decision_log_context)
|
decision_context=$(get_decision_log_context)
|
||||||
|
local dec_size
|
||||||
|
dec_size=$(get_byte_size "$decision_context")
|
||||||
|
if [ "$dec_size" -gt 20000 ]; then
|
||||||
|
decision_context=$(printf '%s' "$decision_context" | tail -c 20000)
|
||||||
|
[ "$VERBOSE" = true ] && log_warn "Design: decision log truncated to last 20KB"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local design_prompt="You are a senior developer planning the implementation of a story.
|
local design_prompt="You are a senior developer planning the implementation of a story.
|
||||||
|
|
@ -74,9 +83,7 @@ $story_contents
|
||||||
|
|
||||||
## Architecture Reference
|
## Architecture Reference
|
||||||
|
|
||||||
<architecture>
|
**Read the architecture document at:** ${arch_file:-"(none found)"}
|
||||||
$arch_contents
|
|
||||||
</architecture>
|
|
||||||
|
|
||||||
## Previous Decisions in This Epic
|
## Previous Decisions in This Epic
|
||||||
|
|
||||||
|
|
@ -84,15 +91,12 @@ $arch_contents
|
||||||
$decision_context
|
$decision_context
|
||||||
</decision-context>
|
</decision-context>
|
||||||
|
|
||||||
## Exploration Commands
|
## Exploration
|
||||||
|
|
||||||
First, explore the codebase to understand existing patterns:
|
First, explore the codebase to understand existing patterns and conventions
|
||||||
\`\`\`bash
|
before planning. Use the repository's own structure and language to guide you
|
||||||
# Find similar implementations
|
(e.g. inspect the relevant source directories, find files similar to what this
|
||||||
find . -type f -name \"*.ts\" -o -name \"*.js\" | head -20
|
story touches, and follow existing patterns rather than introducing new ones).
|
||||||
# Check project structure
|
|
||||||
ls -la src/ 2>/dev/null || ls -la
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Required Output
|
## Required Output
|
||||||
|
|
||||||
|
|
@ -137,6 +141,9 @@ Be specific and concrete. This plan will guide the implementation phase.
|
||||||
After outputting the design block, output exactly:
|
After outputting the design block, output exactly:
|
||||||
DESIGN COMPLETE: $story_id"
|
DESIGN COMPLETE: $story_id"
|
||||||
|
|
||||||
|
# Log prompt size in verbose mode (consistent with other phases)
|
||||||
|
log_prompt_size "$design_prompt" "design-phase"
|
||||||
|
|
||||||
if [ "$DRY_RUN" = true ]; then
|
if [ "$DRY_RUN" = true ]; then
|
||||||
echo "[DRY RUN] Would execute design phase for $story_id"
|
echo "[DRY RUN] Would execute design phase for $story_id"
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -151,6 +158,10 @@ DESIGN COMPLETE: $story_id"
|
||||||
LAST_DESIGN=$(echo "$result" | sed -n '/DESIGN START/,/DESIGN END/p')
|
LAST_DESIGN=$(echo "$result" | sed -n '/DESIGN START/,/DESIGN END/p')
|
||||||
|
|
||||||
if [ -n "$LAST_DESIGN" ]; then
|
if [ -n "$LAST_DESIGN" ]; then
|
||||||
|
# Persist the plan to a per-story file so the dev phase can read it
|
||||||
|
# even after a resume (when the in-memory LAST_DESIGN is empty).
|
||||||
|
persist_design "$story_id" "$LAST_DESIGN"
|
||||||
|
|
||||||
# Save to decision log
|
# Save to decision log
|
||||||
if type append_to_decision_log >/dev/null 2>&1; then
|
if type append_to_decision_log >/dev/null 2>&1; then
|
||||||
append_to_decision_log "DESIGN" "$story_id" "$LAST_DESIGN"
|
append_to_decision_log "DESIGN" "$story_id" "$LAST_DESIGN"
|
||||||
|
|
@ -164,6 +175,27 @@ DESIGN COMPLETE: $story_id"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Persist a design plan to a per-story file under DESIGN_DIR.
|
||||||
|
# Arguments:
|
||||||
|
# $1 - story_id
|
||||||
|
# $2 - design content
|
||||||
|
persist_design() {
|
||||||
|
local story_id="$1"
|
||||||
|
local content="$2"
|
||||||
|
|
||||||
|
if [ -z "${DESIGN_DIR:-}" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$DESIGN_DIR" 2>/dev/null || true
|
||||||
|
local design_file="$DESIGN_DIR/${story_id}-design.md"
|
||||||
|
if printf '%s\n' "$content" > "$design_file" 2>/dev/null; then
|
||||||
|
[ "$VERBOSE" = true ] && log "Design plan saved: $design_file"
|
||||||
|
else
|
||||||
|
log_warn "Failed to persist design plan: $design_file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Get the last design for inclusion in dev phase prompt
|
# Get the last design for inclusion in dev phase prompt
|
||||||
# Returns the design output or empty string if not available
|
# Returns the design output or empty string if not available
|
||||||
get_last_design() {
|
get_last_design() {
|
||||||
|
|
@ -175,7 +207,17 @@ get_last_design() {
|
||||||
build_design_context_for_dev() {
|
build_design_context_for_dev() {
|
||||||
local story_id="$1"
|
local story_id="$1"
|
||||||
|
|
||||||
if [ -z "$LAST_DESIGN" ]; then
|
# Prefer the in-memory plan; fall back to the persisted file so a resumed
|
||||||
|
# run (where LAST_DESIGN is empty) still gets the design context.
|
||||||
|
local design="$LAST_DESIGN"
|
||||||
|
if [ -z "$design" ] && [ -n "${DESIGN_DIR:-}" ]; then
|
||||||
|
local design_file="$DESIGN_DIR/${story_id}-design.md"
|
||||||
|
if [ -f "$design_file" ]; then
|
||||||
|
design=$(cat "$design_file")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$design" ]; then
|
||||||
echo ""
|
echo ""
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
@ -186,7 +228,7 @@ build_design_context_for_dev() {
|
||||||
The following design was created in the planning phase. Follow this plan:
|
The following design was created in the planning phase. Follow this plan:
|
||||||
|
|
||||||
<design-plan>
|
<design-plan>
|
||||||
$LAST_DESIGN
|
$design
|
||||||
</design-plan>
|
</design-plan>
|
||||||
|
|
||||||
### Implementation Guidelines Based on Design
|
### Implementation Guidelines Based on Design
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,8 @@ SPRINTS_DIR="$PROJECT_ROOT/docs/sprints"
|
||||||
EPICS_DIR="$PROJECT_ROOT/docs/epics"
|
EPICS_DIR="$PROJECT_ROOT/docs/epics"
|
||||||
UAT_DIR="$PROJECT_ROOT/docs/uat"
|
UAT_DIR="$PROJECT_ROOT/docs/uat"
|
||||||
LOGS_DIR="$SPRINT_ARTIFACTS_DIR/logs"
|
LOGS_DIR="$SPRINT_ARTIFACTS_DIR/logs"
|
||||||
|
# Per-story design plans (persisted so dev phase can read them after resume)
|
||||||
|
DESIGN_DIR="$SPRINT_ARTIFACTS_DIR/design"
|
||||||
|
|
||||||
# Temporary log file during execution - will be copied to LOGS_DIR on completion
|
# Temporary log file during execution - will be copied to LOGS_DIR on completion
|
||||||
LOG_FILE="/tmp/bmad-epic-execute-$$.log"
|
LOG_FILE="/tmp/bmad-epic-execute-$$.log"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue