Issue: Build phase validation failures in ADW workflows due to external subprocess results being overwritten by parent workflow state management.
Status: β
FIXED in commit 536d81f (Dec 22, 2025)
Impact: CRITICAL - Affected all external Build/Lint/Test workflows, causing silent data loss and phase validation failures
Related Changes: Two additional fixes identified in uncommitted changes
- Root Cause Analysis
- The Fix
- Related Issues Identified
- Cascading Failure Analysis
- Regression Testing Strategy
- Prevention Mechanisms
- Verification Checklist
Scenario: ADW workflows running in "external" mode (Build, Lint, Test phases)
Problematic Flow:
1. Parent Workflow Starts
ββ> Loads ADWState β creates `state` object in memory
2. Parent Spawns External Subprocess
ββ> Launches adw_build_external.py (separate Python process)
3. External Subprocess Completes
ββ> Generates build results (errors, warnings, duration)
ββ> Saves to state: state.data["external_build_results"] = {...}
ββ> Writes to disk: state.save("external_build")
4. Parent Checks Results β BUG HAPPENS HERE
ββ> Reloads state: reloaded_state = ADWState.load(adw_id)
ββ> Checks results: reloaded_state.get("external_build_results")
ββ> Uses results for validation
ββ> BUT: Never merges results back into original `state` object
5. Parent Continues Execution
ββ> Performs other operations
ββ> Saves state: state.save("build_phase_complete")
ββ> **OVERWRITES external_build_results** β
6. Result
ββ> Phase validation fails: "Build phase incomplete after execution"
- Silent Data Loss: No errors thrown, results just disappear
- Observability Blind Spot: Build errors/warnings not recorded in analytics
- Workflow Failures: Phase validation checks fail, blocking Ship phase
- Cascading Impact: Affects downstream phases that depend on build results
Commit: 536d81f84a9d755b7282e00e2908f1299a308f99
-
adws/adw_build_iso.py:103-104# CRITICAL FIX: Merge external_build_results back into parent state state.data["external_build_results"] = build_results logger.debug(f"Merged external_build_results: {len(build_results.get('errors', []))} errors")
-
adws/adw_lint_iso.py:106-107# CRITICAL FIX: Merge external_lint_results back into parent state state.data["external_lint_results"] = lint_results
-
adws/adw_test_iso.py:330-331# CRITICAL FIX: Merge external_test_results back into parent state state.data["external_test_results"] = test_results
File: adws/tests/test_external_results_persistence.py
- β
test_external_build_results_persist_after_parent_save() - β
test_external_lint_results_persist() - β
test_external_test_results_persist()
Test Strategy:
- Create state, save initial version
- Simulate external subprocess saving results
- Reload state (simulating parent checking results)
- Save state again (this is where bug occurred)
- Verify results still exist after final save
File: adws/adw_modules/data_types.py (uncommitted)
Problem:
# BEFORE (strict schema)
class GitHubLabel(BaseModel):
id: str # β Required
name: str
color: str # β Required
description: Optional[str] = NoneGitHub API Behavior:
- REST API sometimes returns labels WITHOUT
idfield - REST API sometimes returns labels WITHOUT
colorfield - This causes Pydantic ValidationError when parsing responses
Fix:
# AFTER (lenient schema)
class GitHubLabel(BaseModel):
id: Optional[str] = None # β
Optional - not always in REST API
name: str # Required
color: Optional[str] = None # β
Optional - not always in REST API
description: Optional[str] = NoneImpact:
- Prevents: ValidationError crashes when fetching GitHub issues
- Affects: All GitHub API integrations (issue fetching, webhook processing)
- Severity: HIGH - Blocks workflow initiation if issue fetch fails
Cascading Failure Scenario:
1. User creates GitHub issue #280
2. ADW attempts to fetch issue details via REST API
3. GitHub returns label without `id` field
4. Pydantic raises ValidationError
5. Workflow creation fails β
6. User sees generic error, no workflow initiated
File: app/server/routes/workflow_routes.py (uncommitted)
New Feature: Automatically trigger next phase when current phase completes
Implementation:
# AUTOMATIC PHASE CONTINUATION
# Trigger next phase when current phase completes successfully
next_phase_triggered = False
if request.status == "completed":
from core.phase_continuation import should_continue_workflow, trigger_next_phase
if state and should_continue_workflow(request.status, request.current_phase):
workflow_template = state.get("workflow_template")
github_issue_number = state.get("github_issue_number")
if workflow_template and github_issue_number:
workflow_flags = {
"skip_e2e": state.get("skip_e2e", False),
"skip_resolution": state.get("skip_resolution", False),
"no_external": state.get("no_external", False),
}
next_phase_triggered = trigger_next_phase(
adw_id=request.adw_id,
issue_number=str(github_issue_number),
workflow_template=workflow_template,
current_phase=request.current_phase,
workflow_flags=workflow_flags
)Key Components:
-
core/phase_continuation.py- New module (already exists, uncommitted usage)WORKFLOW_PHASE_SEQUENCES- Maps workflow types to phase orderPHASE_TO_SCRIPT- Maps phase names to Python scriptsget_next_phase()- Returns next phase in sequencetrigger_next_phase()- Spawns next phase subprocessshould_continue_workflow()- Determines if auto-continuation should happen
-
Terminal Phases (don't auto-continue):
Ship- Final deployment phaseVerify- Final verification phaseCleanup- Cleanup phase (handled by orchestrator)
Safety Features:
- Only continues on
status="completed"(not "failed" or "running") - Skips terminal phases
- Gracefully handles missing workflow_template
- Non-blocking subprocess launch (doesn't hold API response)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. INITIAL FAILURE β
β External build results overwritten by parent save β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. VALIDATION FAILURE β
β Phase validation checks fail (no build results) β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. OBSERVABILITY BLIND SPOT β
β Build errors/warnings not recorded in analytics β
β - Cost attribution incorrect β
β - Pattern analysis incomplete β
β - Error rate metrics underreported β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 4. WORKFLOW PROGRESSION BLOCKED β
β Ship phase cannot proceed (build incomplete) β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 5. MANUAL INTERVENTION REQUIRED β
β Developer must debug, fix state manually β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. INITIAL FAILURE β
β GitHub API returns label without id/color field β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. PYDANTIC VALIDATION ERROR β
β ValidationError raised during JSON parsing β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. WORKFLOW CREATION FAILS β
β Cannot initialize ADW (missing issue data) β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 4. USER SEES GENERIC ERROR β
β "Failed to create workflow" (no useful debug info) β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 5. SUPPORT BURDEN β
β User creates support ticket, manual investigation β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β POTENTIAL FAILURE SCENARIO β
β Phase completes with errors, but auto-continues anyway β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β RISK: Incomplete phase triggers next phase β
β - Build has 10 type errors β
β - Phase marked "completed" (script didn't fail) β
β - Auto-continuation triggers Lint phase β
β - Lint runs on broken code β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Mitigation: Phase auto-continuation should verify phase quality, not just completion status
| Test Area | Existing Tests | Needed Tests | Priority |
|---|---|---|---|
| External Results Persistence | β
3 tests in test_external_results_persistence.py |
β Complete | β Done |
| GitHubLabel Schema | β None | π΄ HIGH | |
| Phase Auto-Continuation | β None | π΄ HIGH | |
| State Reload/Merge | β Covered by external results tests | β Complete | β Done |
| Cascading Failures | β None | π‘ MEDIUM |
File: app/server/tests/test_github_label_validation.py (NEW)
Test Cases:
def test_github_label_with_all_fields():
"""Test label with id, name, color, description"""
def test_github_label_without_id():
"""Test label missing id field (REST API behavior)"""
def test_github_label_without_color():
"""Test label missing color field (REST API behavior)"""
def test_github_label_minimal():
"""Test label with only name field"""
def test_github_issue_with_various_labels():
"""Test parsing issue with mixed label formats"""File: adws/tests/test_phase_continuation.py (NEW)
Test Cases:
def test_get_next_phase_complete_workflow():
"""Test phase sequence for adw_sdlc_complete_iso"""
def test_get_next_phase_last_phase():
"""Test returns None when on last phase"""
def test_get_next_phase_unknown_workflow():
"""Test handling of unknown workflow template"""
def test_should_continue_workflow_on_completion():
"""Test auto-continue only on completed status"""
def test_should_continue_workflow_terminal_phases():
"""Test skips Ship, Verify, Cleanup phases"""
def test_trigger_next_phase_success():
"""Test successful next phase trigger"""
def test_trigger_next_phase_with_flags():
"""Test workflow flags passed correctly"""
def test_trigger_next_phase_cleanup_skipped():
"""Test Cleanup phase not auto-triggered"""File: adws/tests/test_e2e_workflow_continuation.py (NEW)
Test Cases:
def test_plan_to_build_auto_continuation():
"""Test Plan phase auto-triggers Build phase"""
def test_build_to_lint_with_external_mode():
"""Test Build (external) β Lint continuation with results preserved"""
def test_failed_phase_does_not_auto_continue():
"""Test failed phase blocks auto-continuation"""
def test_auto_continuation_respects_workflow_flags():
"""Test skip_e2e flag propagates through auto-continuation"""RULE: Any code that reloads ADWState MUST merge critical fields back into original state before saving
Pattern:
# β
CORRECT: Merge after reload
original_state = ADWState.load(adw_id)
# ... do work ...
# Reload to check subprocess results
reloaded_state = ADWState.load(adw_id)
external_results = reloaded_state.get("external_build_results")
# CRITICAL: Merge back into original state
original_state.data["external_build_results"] = external_results
# Now safe to save
original_state.save("phase_complete")# β INCORRECT: Reload without merge
state = ADWState.load(adw_id)
# ... do work ...
# Reload to check results
new_state = ADWState.load(adw_id)
results = new_state.get("external_build_results")
# BUG: Saving original state overwrites external results
state.save("phase_complete") # β external_build_results lostRULE: Pydantic models for external APIs should be lenient by default
Pattern:
# β
CORRECT: Optional fields for API responses
class GitHubLabel(BaseModel):
id: Optional[str] = None # API may omit
name: str # Always required
color: Optional[str] = None # API may omit
description: Optional[str] = None# β INCORRECT: Strict schema for unreliable API
class GitHubLabel(BaseModel):
id: str # β Will fail if API omits
name: str
color: str # β Will fail if API omitsRULE: Auto-continuation should verify phase quality, not just completion
Current Implementation:
def should_continue_workflow(status: str, current_phase: str) -> bool:
if status != "completed": # β
Good: checks status
return False
if current_phase in ["Ship", "Verify", "Cleanup"]: # β
Good: skips terminal phases
return False
return TrueRecommended Enhancement:
def should_continue_workflow(status: str, current_phase: str, state: ADWState) -> bool:
if status != "completed":
return False
if current_phase in ["Ship", "Verify", "Cleanup"]:
return False
# NEW: Check phase quality
if current_phase == "Build":
build_results = state.get("external_build_results") or state.get("build_results")
if build_results and not build_results.get("success", False):
logger.warning("Build phase completed but has errors, blocking auto-continuation")
return False
# Similar checks for Lint, Test phases...
return True- All uncommitted changes reviewed
- GitHubLabel schema fix tested with real GitHub API responses
- Phase auto-continuation tested with multiple workflow types
- Regression tests written and passing
- No new ValidationError exceptions in logs
- Test external_build_results persist across parent save
- Test external_lint_results persist across parent save
- Test external_test_results persist across parent save
- All tests passing in
test_external_results_persistence.py
- Test label parsing with all fields present
- Test label parsing with missing
idfield - Test label parsing with missing
colorfield - Test label parsing with only
namefield - Test issue parsing with mixed label formats
- No ValidationError exceptions raised
- Test phase sequence determination for all workflow types
- Test terminal phase blocking (Ship, Verify, Cleanup)
- Test failed phase blocking auto-continuation
- Test workflow flags propagation (skip_e2e, no_external, etc.)
- Test subprocess launch doesn't block API response
- Test missing workflow_template gracefully handled
- Test Plan β Build auto-continuation
- Test Build β Lint auto-continuation
- Test Lint β Test auto-continuation
- Test external mode with auto-continuation
- Test workflow completes all phases automatically
- Test results properly recorded in all phases
- All regression tests passing
- Code review completed
- Documentation updated
- Monitoring alerts configured for ValidationError
- Rollback plan prepared
- Team notified of new auto-continuation feature
-
Commit the GitHubLabel fix
git add adws/adw_modules/data_types.py git commit -m "fix: Make GitHubLabel id and color optional GitHub REST API sometimes omits id and color fields in label responses, causing Pydantic ValidationError when parsing issues. Changes: - GitHubLabel.id: str β Optional[str] = None - GitHubLabel.color: str β Optional[str] = None Impact: - Prevents workflow creation failures when fetching GitHub issues - Fixes ValidationError on label parsing - Improves API response compatibility Related: #279 (Build phase validation failure)"
-
Review phase auto-continuation before committing
- Verify safety checks (terminal phase blocking)
- Test with multiple workflow types
- Ensure subprocess doesn't block API response
- Add quality checks (don't continue if phase has errors)
-
Create regression tests
test_github_label_validation.pytest_phase_continuation.pytest_e2e_workflow_continuation.py
-
Enhance auto-continuation safety
- Add phase quality checks (don't continue if Build has errors)
- Add configurable auto-continuation flag (opt-in/opt-out)
- Add metrics tracking for auto-continuation success rate
-
Improve observability
- Log all auto-continuation attempts
- Track auto-continuation failures
- Add metrics to dashboard
-
Documentation
- Update workflow documentation with auto-continuation behavior
- Add troubleshooting guide for phase validation failures
- Document state management best practices
-
State Management Improvements
- Create ADWState.merge() method for safe state merging
- Add state diff logging (what changed between saves)
- Implement state versioning for rollback capability
-
Schema Validation Framework
- Centralized API response validation
- Automatic schema lenience for external APIs
- Better error messages for ValidationError
-
Workflow Testing
- Complete E2E test coverage for all 10 workflow types
- Automated regression testing in CI/CD
- Performance testing for auto-continuation overhead
Issue #279 was a critical bug causing silent data loss in external Build/Lint/Test workflows. The fix has been implemented and tested, but has revealed two additional issues:
- GitHubLabel schema too strict - Causing ValidationError on issue fetching
- Phase auto-continuation feature - Needs quality checks before safe deployment
Recommended Actions:
- β Commit GitHubLabel fix immediately (prevents workflow creation failures)
β οΈ Review phase auto-continuation carefully (add quality checks)- π΄ Create comprehensive regression tests (prevent future regressions)
- π Document state management best practices (prevent similar bugs)
Success Metrics:
- Zero ValidationError exceptions in logs
- 100% phase validation success rate
- All auto-continuations result in successful next phase execution
- No manual interventions required for workflow progression
Created: 2025-12-22 Last Updated: 2025-12-22 Status: Ready for Review & Implementation