galangal-orchestrate 0.6.3__tar.gz → 0.12.15__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/.gitignore +2 -1
  2. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/PKG-INFO +173 -11
  3. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/README.md +172 -10
  4. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/docs/local-development/workflow-pipeline.md +1 -1
  5. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/__init__.py +1 -1
  6. galangal_orchestrate-0.12.15/src/galangal/ai/__init__.py +167 -0
  7. galangal_orchestrate-0.12.15/src/galangal/ai/base.py +159 -0
  8. galangal_orchestrate-0.12.15/src/galangal/ai/claude.py +352 -0
  9. galangal_orchestrate-0.12.15/src/galangal/ai/codex.py +370 -0
  10. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ai/gemini.py +5 -2
  11. galangal_orchestrate-0.12.15/src/galangal/ai/subprocess.py +254 -0
  12. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/cli.py +101 -171
  13. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/commands/__init__.py +1 -16
  14. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/commands/complete.py +56 -19
  15. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/commands/github.py +48 -62
  16. galangal_orchestrate-0.12.15/src/galangal/commands/init.py +77 -0
  17. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/commands/prompts.py +5 -14
  18. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/commands/reset.py +1 -3
  19. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/commands/resume.py +1 -1
  20. galangal_orchestrate-0.12.15/src/galangal/commands/skip.py +62 -0
  21. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/commands/start.py +93 -23
  22. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/commands/status.py +5 -30
  23. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/config/defaults.py +26 -26
  24. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/config/loader.py +41 -2
  25. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/config/schema.py +63 -32
  26. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/core/artifacts.py +11 -1
  27. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/core/state.py +423 -32
  28. galangal_orchestrate-0.12.15/src/galangal/core/tasks.py +454 -0
  29. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/core/utils.py +8 -8
  30. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/core/workflow/core.py +272 -100
  31. galangal_orchestrate-0.12.15/src/galangal/core/workflow/engine.py +781 -0
  32. galangal_orchestrate-0.12.15/src/galangal/core/workflow/tui_runner.py +1322 -0
  33. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/github/client.py +22 -12
  34. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/github/images.py +23 -24
  35. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/github/issues.py +24 -12
  36. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/logging.py +6 -4
  37. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/builder.py +210 -67
  38. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/defaults/qa.md +1 -21
  39. galangal_orchestrate-0.12.15/src/galangal/prompts/defaults/review.md +90 -0
  40. galangal_orchestrate-0.12.15/src/galangal/prompts/defaults/review_codex.md +99 -0
  41. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/defaults/security.md +0 -20
  42. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/results.py +18 -6
  43. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ui/console.py +1 -3
  44. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ui/tui/app.py +40 -43
  45. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ui/tui/mixins.py +2 -0
  46. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ui/tui/modals.py +11 -20
  47. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ui/tui/widgets.py +1 -2
  48. galangal_orchestrate-0.12.15/src/galangal/validation/runner.py +1072 -0
  49. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/tests/conftest.py +18 -3
  50. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/tests/test_ai_claude.py +79 -20
  51. galangal_orchestrate-0.12.15/tests/test_ai_codex.py +426 -0
  52. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/tests/test_commands.py +159 -68
  53. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/tests/test_config.py +1 -15
  54. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/tests/test_discovery_qa.py +1 -12
  55. galangal_orchestrate-0.12.15/tests/test_docs_sync.py +169 -0
  56. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/tests/test_results.py +5 -6
  57. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/tests/test_tui.py +9 -3
  58. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/tests/test_validation_runner.py +80 -20
  59. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/tests/test_workflow_core.py +34 -23
  60. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/tests/test_workflow_integration.py +31 -152
  61. galangal_orchestrate-0.6.3/src/galangal/ai/__init__.py +0 -6
  62. galangal_orchestrate-0.6.3/src/galangal/ai/base.py +0 -63
  63. galangal_orchestrate-0.6.3/src/galangal/ai/claude.py +0 -334
  64. galangal_orchestrate-0.6.3/src/galangal/commands/approve.py +0 -196
  65. galangal_orchestrate-0.6.3/src/galangal/commands/init.py +0 -175
  66. galangal_orchestrate-0.6.3/src/galangal/commands/skip.py +0 -185
  67. galangal_orchestrate-0.6.3/src/galangal/commands/stats.py +0 -155
  68. galangal_orchestrate-0.6.3/src/galangal/core/metrics.py +0 -264
  69. galangal_orchestrate-0.6.3/src/galangal/core/tasks.py +0 -241
  70. galangal_orchestrate-0.6.3/src/galangal/core/workflow/tui_runner.py +0 -1329
  71. galangal_orchestrate-0.6.3/src/galangal/prompts/defaults/review.md +0 -85
  72. galangal_orchestrate-0.6.3/src/galangal/validation/runner.py +0 -645
  73. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/.github/workflows/publish.yml +0 -0
  74. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/LICENSE +0 -0
  75. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/docs/local-development/README.md +0 -0
  76. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/docs/local-development/architecture.md +0 -0
  77. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/docs/local-development/cli-commands.md +0 -0
  78. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/docs/local-development/configuration.md +0 -0
  79. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/docs/local-development/extending.md +0 -0
  80. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/docs/local-development/prompt-system.md +0 -0
  81. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/docs/local-development/state-management.md +0 -0
  82. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/docs/local-development/validation-system.md +0 -0
  83. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/docs/local-development/versioning.md +0 -0
  84. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/docs/pypi/RELEASE.md +0 -0
  85. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/pyproject.toml +0 -0
  86. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/__main__.py +0 -0
  87. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/commands/list.py +0 -0
  88. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/commands/pause.py +0 -0
  89. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/commands/switch.py +0 -0
  90. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/config/__init__.py +0 -0
  91. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/core/__init__.py +0 -0
  92. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/core/workflow/__init__.py +0 -0
  93. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/core/workflow/pause.py +0 -0
  94. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/exceptions.py +0 -0
  95. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/github/__init__.py +0 -0
  96. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/__init__.py +0 -0
  97. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/defaults/benchmark.md +0 -0
  98. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/defaults/contract.md +0 -0
  99. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/defaults/design.md +0 -0
  100. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/defaults/dev.md +0 -0
  101. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/defaults/docs.md +0 -0
  102. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/defaults/migration.md +0 -0
  103. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/defaults/pm.md +0 -0
  104. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/defaults/pm_questions.md +0 -0
  105. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/defaults/preflight.md +0 -0
  106. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/prompts/defaults/test.md +0 -0
  107. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ui/__init__.py +0 -0
  108. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ui/tui/__init__.py +0 -0
  109. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ui/tui/adapters.py +0 -0
  110. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ui/tui/entry.py +0 -0
  111. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ui/tui/styles/app.tcss +0 -0
  112. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ui/tui/styles/modals.tcss +0 -0
  113. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/ui/tui/types.py +0 -0
  114. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/src/galangal/validation/__init__.py +0 -0
  115. {galangal_orchestrate-0.6.3 → galangal_orchestrate-0.12.15}/tests/__init__.py +0 -0
@@ -69,4 +69,5 @@ galangal-tasks/
69
69
 
70
70
  # Ideas (local brainstorming)
71
71
  ideas/
72
- .test_venv
72
+ .test_venv
73
+ /GEMINI.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: galangal-orchestrate
3
- Version: 0.6.3
3
+ Version: 0.12.15
4
4
  Summary: AI-driven development workflow orchestrator
5
5
  Project-URL: Homepage, https://github.com/Galangal-Media/galangal-orchestrate
6
6
  Project-URL: Repository, https://github.com/Galangal-Media/galangal-orchestrate
@@ -97,7 +97,7 @@ galangal status
97
97
  | **PREFLIGHT** | Environment validation | PREFLIGHT_REPORT.md |
98
98
  | **DEV** | Implementation | Code changes |
99
99
  | **MIGRATION*** | Database migration checks | MIGRATION_REPORT.md |
100
- | **TEST** | Test implementation | TEST_PLAN.md |
100
+ | **TEST** | Test implementation | TEST_PLAN.md, TEST_SUMMARY.md |
101
101
  | **CONTRACT*** | API contract validation | CONTRACT_REPORT.md |
102
102
  | **QA** | Quality assurance | QA_REPORT.md |
103
103
  | **BENCHMARK*** | Performance validation | BENCHMARK_REPORT.md |
@@ -107,6 +107,15 @@ galangal status
107
107
 
108
108
  *Conditional stages - skipped automatically if not relevant
109
109
 
110
+ ### Validation Artifacts
111
+
112
+ When validation commands run (tests, linters, etc.), Galangal creates debugging artifacts:
113
+
114
+ - **VALIDATION_REPORT.md** - Full output from all validation commands, useful for debugging failures
115
+ - **TEST_SUMMARY.md** - Concise test results (pass/fail counts, failed test names, coverage) included in downstream stage prompts
116
+
117
+ These artifacts help you understand what failed without digging through logs, and give downstream stages (QA, REVIEW) context about test results without bloating prompts with verbose output.
118
+
110
119
  ## Task Types
111
120
 
112
121
  Choose the right workflow for your task:
@@ -114,9 +123,9 @@ Choose the right workflow for your task:
114
123
  | Type | Stages | When to Use |
115
124
  |------|--------|-------------|
116
125
  | **Feature** | All stages | New functionality |
117
- | **Bug Fix** | PM → DEV → TEST → QA | Fixing bugs |
118
- | **Refactor** | PM → DESIGN → DEV → TEST | Code restructuring |
119
- | **Chore** | PM → DEV → TEST | Config, dependencies |
126
+ | **Bug Fix** | PM → PREFLIGHT → DEV → TEST → QA | Fixing bugs |
127
+ | **Refactor** | PM → DESIGN → PREFLIGHT → DEV → TEST | Code restructuring |
128
+ | **Chore** | PM → PREFLIGHT → DEV → TEST | Config, dependencies |
120
129
  | **Docs** | PM → DOCS | Documentation only |
121
130
  | **Hotfix** | PM → DEV → TEST | Critical fixes |
122
131
 
@@ -167,6 +176,32 @@ After analyzing your task, the PM stage outputs a `STAGE_PLAN.md` recommending w
167
176
 
168
177
  The progress bar updates dynamically to show only relevant stages.
169
178
 
179
+ ### Workflow Preview
180
+
181
+ After PM approval, you'll see a preview showing exactly which stages will run and why others are skipped:
182
+
183
+ ```
184
+ Workflow Preview
185
+
186
+ Stages to run:
187
+ PM → DESIGN → PREFLIGHT → DEV → TEST → QA → REVIEW → DOCS
188
+
189
+ Skipping:
190
+ MIGRATION (no files match: **/migrations/*)
191
+ CONTRACT (no files match: **/api/*, **/openapi.*)
192
+ BENCHMARK (task type: bug_fix)
193
+ SECURITY (PM: simple UI change, no security impact)
194
+
195
+ Controls during execution:
196
+ ^N Skip stage ^B Back ^E Pause for edit ^I Interrupt
197
+ ```
198
+
199
+ Skip reasons include:
200
+ - **Task type** - Based on the workflow template (e.g., bug fixes skip DESIGN)
201
+ - **Config** - Stages listed in `stages.skip` configuration
202
+ - **PM recommendation** - From STAGE_PLAN.md analysis
203
+ - **skip_if condition** - No changed files match the glob pattern
204
+
170
205
  ## Commands
171
206
 
172
207
  | Command | Description |
@@ -403,6 +438,9 @@ validation:
403
438
  - name: "Integration tests"
404
439
  command: "pytest tests/integration"
405
440
  optional: true # Don't fail if integration tests missing
441
+ # Use array form for paths with spaces or special characters
442
+ - name: "Task-specific tests"
443
+ command: ["pytest", "{task_dir}/tests"] # {task_dir} is substituted
406
444
 
407
445
  # Contract stage (API compatibility)
408
446
  contract:
@@ -458,21 +496,37 @@ ai:
458
496
  # Default backend to use
459
497
  default: claude
460
498
 
461
- # Available backends
499
+ # Available backends with customizable CLI flags
462
500
  backends:
463
501
  claude:
464
- command: claude
465
- args:
466
- - "-p"
467
- - "{prompt}"
502
+ command: claude # CLI command to invoke
503
+ args: # Arguments with {placeholder} substitution
468
504
  - "--output-format"
469
505
  - "stream-json"
470
506
  - "--verbose"
507
+ - "--max-turns"
508
+ - "{max_turns}" # Replaced with max_turns value
509
+ - "--permission-mode"
510
+ - "bypassPermissions"
471
511
  max_turns: 200 # Maximum conversation turns per stage
512
+ read_only: false # If true, backend cannot write files
513
+
514
+ codex:
515
+ command: codex
516
+ args:
517
+ - "exec"
518
+ - "--full-auto"
519
+ - "--output-schema"
520
+ - "{schema_file}" # Replaced with schema file path
521
+ - "-o"
522
+ - "{output_file}" # Replaced with output file path
523
+ max_turns: 50
524
+ read_only: true # Codex runs in read-only sandbox
472
525
 
473
526
  # Use different backends for specific stages
474
527
  stage_backends:
475
- # qa: gemini # Use Gemini for QA stage (when supported)
528
+ REVIEW: codex # Use Codex for code review
529
+ # QA: gemini # Use Gemini for QA (when supported)
476
530
 
477
531
  # =============================================================================
478
532
  # DOCUMENTATION CONFIGURATION
@@ -569,6 +623,112 @@ stage_context:
569
623
  - Secrets must use environment variables
570
624
  ```
571
625
 
626
+ ## AI Backend Customization
627
+
628
+ Galangal invokes AI backends (like Claude Code CLI) using configurable commands and arguments. This allows you to customize CLI flags without modifying code.
629
+
630
+ ### Default Behavior
631
+
632
+ By default, Galangal invokes Claude with:
633
+ ```bash
634
+ cat prompt.txt | claude --output-format stream-json --verbose --max-turns 200 --permission-mode bypassPermissions
635
+ ```
636
+
637
+ ### Customizing CLI Flags
638
+
639
+ Override any flags in `.galangal/config.yaml`:
640
+
641
+ ```yaml
642
+ ai:
643
+ backends:
644
+ claude:
645
+ command: claude
646
+ args:
647
+ - "--output-format"
648
+ - "stream-json"
649
+ - "--verbose"
650
+ - "--max-turns"
651
+ - "{max_turns}"
652
+ - "--permission-mode"
653
+ - "bypassPermissions"
654
+ - "--model" # Add custom flags
655
+ - "opus"
656
+ max_turns: 300 # Increase max turns
657
+ ```
658
+
659
+ ### Placeholder Reference
660
+
661
+ Arguments can include placeholders that are substituted at runtime:
662
+
663
+ | Placeholder | Backend | Description |
664
+ |-------------|---------|-------------|
665
+ | `{max_turns}` | claude | Maximum conversation turns |
666
+ | `{schema_file}` | codex | Path to JSON schema file |
667
+ | `{output_file}` | codex | Path for structured output |
668
+
669
+ ### Common Customizations
670
+
671
+ **Use a specific model:**
672
+ ```yaml
673
+ ai:
674
+ backends:
675
+ claude:
676
+ args:
677
+ - "--output-format"
678
+ - "stream-json"
679
+ - "--model"
680
+ - "sonnet" # Use Sonnet instead of default
681
+ - "--max-turns"
682
+ - "{max_turns}"
683
+ ```
684
+
685
+ **Increase turn limit for complex tasks:**
686
+ ```yaml
687
+ ai:
688
+ backends:
689
+ claude:
690
+ max_turns: 500 # Default is 200
691
+ args:
692
+ - "--output-format"
693
+ - "stream-json"
694
+ - "--max-turns"
695
+ - "{max_turns}" # Will use 500
696
+ ```
697
+
698
+ **Use different backends per stage:**
699
+ ```yaml
700
+ ai:
701
+ default: claude
702
+ stage_backends:
703
+ REVIEW: codex # Use Codex for code review
704
+ ```
705
+
706
+ ### Adding a Custom Backend
707
+
708
+ Define any CLI tool as a backend:
709
+
710
+ ```yaml
711
+ ai:
712
+ backends:
713
+ my-backend:
714
+ command: my-ai-tool
715
+ args:
716
+ - "--prompt-file"
717
+ - "-" # Read from stdin
718
+ - "--json-output"
719
+ max_turns: 100
720
+ read_only: true # Cannot write files directly
721
+ ```
722
+
723
+ Then use it:
724
+ ```yaml
725
+ ai:
726
+ default: my-backend
727
+ # Or per-stage:
728
+ stage_backends:
729
+ QA: my-backend
730
+ ```
731
+
572
732
  ## Customizing Prompts
573
733
 
574
734
  Galangal uses a layered prompt system:
@@ -712,6 +872,8 @@ If the TEST stage keeps retrying instead of rolling back to DEV:
712
872
  2. If tests fail due to implementation bugs, the AI should report FAIL (not try to fix the code)
713
873
  3. Check that test commands exit with proper exit codes (0 for success, non-zero for failure)
714
874
 
875
+ **Note:** As of v0.12.0, when artifact markers are unclear (missing PASS/FAIL), Galangal prompts you to manually approve or reject instead of retrying indefinitely. You'll see the artifact content and can make the decision yourself.
876
+
715
877
  ### "Galangal has not been initialized" Error
716
878
 
717
879
  Run `galangal init` in your project root before using other commands.
@@ -61,7 +61,7 @@ galangal status
61
61
  | **PREFLIGHT** | Environment validation | PREFLIGHT_REPORT.md |
62
62
  | **DEV** | Implementation | Code changes |
63
63
  | **MIGRATION*** | Database migration checks | MIGRATION_REPORT.md |
64
- | **TEST** | Test implementation | TEST_PLAN.md |
64
+ | **TEST** | Test implementation | TEST_PLAN.md, TEST_SUMMARY.md |
65
65
  | **CONTRACT*** | API contract validation | CONTRACT_REPORT.md |
66
66
  | **QA** | Quality assurance | QA_REPORT.md |
67
67
  | **BENCHMARK*** | Performance validation | BENCHMARK_REPORT.md |
@@ -71,6 +71,15 @@ galangal status
71
71
 
72
72
  *Conditional stages - skipped automatically if not relevant
73
73
 
74
+ ### Validation Artifacts
75
+
76
+ When validation commands run (tests, linters, etc.), Galangal creates debugging artifacts:
77
+
78
+ - **VALIDATION_REPORT.md** - Full output from all validation commands, useful for debugging failures
79
+ - **TEST_SUMMARY.md** - Concise test results (pass/fail counts, failed test names, coverage) included in downstream stage prompts
80
+
81
+ These artifacts help you understand what failed without digging through logs, and give downstream stages (QA, REVIEW) context about test results without bloating prompts with verbose output.
82
+
74
83
  ## Task Types
75
84
 
76
85
  Choose the right workflow for your task:
@@ -78,9 +87,9 @@ Choose the right workflow for your task:
78
87
  | Type | Stages | When to Use |
79
88
  |------|--------|-------------|
80
89
  | **Feature** | All stages | New functionality |
81
- | **Bug Fix** | PM → DEV → TEST → QA | Fixing bugs |
82
- | **Refactor** | PM → DESIGN → DEV → TEST | Code restructuring |
83
- | **Chore** | PM → DEV → TEST | Config, dependencies |
90
+ | **Bug Fix** | PM → PREFLIGHT → DEV → TEST → QA | Fixing bugs |
91
+ | **Refactor** | PM → DESIGN → PREFLIGHT → DEV → TEST | Code restructuring |
92
+ | **Chore** | PM → PREFLIGHT → DEV → TEST | Config, dependencies |
84
93
  | **Docs** | PM → DOCS | Documentation only |
85
94
  | **Hotfix** | PM → DEV → TEST | Critical fixes |
86
95
 
@@ -131,6 +140,32 @@ After analyzing your task, the PM stage outputs a `STAGE_PLAN.md` recommending w
131
140
 
132
141
  The progress bar updates dynamically to show only relevant stages.
133
142
 
143
+ ### Workflow Preview
144
+
145
+ After PM approval, you'll see a preview showing exactly which stages will run and why others are skipped:
146
+
147
+ ```
148
+ Workflow Preview
149
+
150
+ Stages to run:
151
+ PM → DESIGN → PREFLIGHT → DEV → TEST → QA → REVIEW → DOCS
152
+
153
+ Skipping:
154
+ MIGRATION (no files match: **/migrations/*)
155
+ CONTRACT (no files match: **/api/*, **/openapi.*)
156
+ BENCHMARK (task type: bug_fix)
157
+ SECURITY (PM: simple UI change, no security impact)
158
+
159
+ Controls during execution:
160
+ ^N Skip stage ^B Back ^E Pause for edit ^I Interrupt
161
+ ```
162
+
163
+ Skip reasons include:
164
+ - **Task type** - Based on the workflow template (e.g., bug fixes skip DESIGN)
165
+ - **Config** - Stages listed in `stages.skip` configuration
166
+ - **PM recommendation** - From STAGE_PLAN.md analysis
167
+ - **skip_if condition** - No changed files match the glob pattern
168
+
134
169
  ## Commands
135
170
 
136
171
  | Command | Description |
@@ -367,6 +402,9 @@ validation:
367
402
  - name: "Integration tests"
368
403
  command: "pytest tests/integration"
369
404
  optional: true # Don't fail if integration tests missing
405
+ # Use array form for paths with spaces or special characters
406
+ - name: "Task-specific tests"
407
+ command: ["pytest", "{task_dir}/tests"] # {task_dir} is substituted
370
408
 
371
409
  # Contract stage (API compatibility)
372
410
  contract:
@@ -422,21 +460,37 @@ ai:
422
460
  # Default backend to use
423
461
  default: claude
424
462
 
425
- # Available backends
463
+ # Available backends with customizable CLI flags
426
464
  backends:
427
465
  claude:
428
- command: claude
429
- args:
430
- - "-p"
431
- - "{prompt}"
466
+ command: claude # CLI command to invoke
467
+ args: # Arguments with {placeholder} substitution
432
468
  - "--output-format"
433
469
  - "stream-json"
434
470
  - "--verbose"
471
+ - "--max-turns"
472
+ - "{max_turns}" # Replaced with max_turns value
473
+ - "--permission-mode"
474
+ - "bypassPermissions"
435
475
  max_turns: 200 # Maximum conversation turns per stage
476
+ read_only: false # If true, backend cannot write files
477
+
478
+ codex:
479
+ command: codex
480
+ args:
481
+ - "exec"
482
+ - "--full-auto"
483
+ - "--output-schema"
484
+ - "{schema_file}" # Replaced with schema file path
485
+ - "-o"
486
+ - "{output_file}" # Replaced with output file path
487
+ max_turns: 50
488
+ read_only: true # Codex runs in read-only sandbox
436
489
 
437
490
  # Use different backends for specific stages
438
491
  stage_backends:
439
- # qa: gemini # Use Gemini for QA stage (when supported)
492
+ REVIEW: codex # Use Codex for code review
493
+ # QA: gemini # Use Gemini for QA (when supported)
440
494
 
441
495
  # =============================================================================
442
496
  # DOCUMENTATION CONFIGURATION
@@ -533,6 +587,112 @@ stage_context:
533
587
  - Secrets must use environment variables
534
588
  ```
535
589
 
590
+ ## AI Backend Customization
591
+
592
+ Galangal invokes AI backends (like Claude Code CLI) using configurable commands and arguments. This allows you to customize CLI flags without modifying code.
593
+
594
+ ### Default Behavior
595
+
596
+ By default, Galangal invokes Claude with:
597
+ ```bash
598
+ cat prompt.txt | claude --output-format stream-json --verbose --max-turns 200 --permission-mode bypassPermissions
599
+ ```
600
+
601
+ ### Customizing CLI Flags
602
+
603
+ Override any flags in `.galangal/config.yaml`:
604
+
605
+ ```yaml
606
+ ai:
607
+ backends:
608
+ claude:
609
+ command: claude
610
+ args:
611
+ - "--output-format"
612
+ - "stream-json"
613
+ - "--verbose"
614
+ - "--max-turns"
615
+ - "{max_turns}"
616
+ - "--permission-mode"
617
+ - "bypassPermissions"
618
+ - "--model" # Add custom flags
619
+ - "opus"
620
+ max_turns: 300 # Increase max turns
621
+ ```
622
+
623
+ ### Placeholder Reference
624
+
625
+ Arguments can include placeholders that are substituted at runtime:
626
+
627
+ | Placeholder | Backend | Description |
628
+ |-------------|---------|-------------|
629
+ | `{max_turns}` | claude | Maximum conversation turns |
630
+ | `{schema_file}` | codex | Path to JSON schema file |
631
+ | `{output_file}` | codex | Path for structured output |
632
+
633
+ ### Common Customizations
634
+
635
+ **Use a specific model:**
636
+ ```yaml
637
+ ai:
638
+ backends:
639
+ claude:
640
+ args:
641
+ - "--output-format"
642
+ - "stream-json"
643
+ - "--model"
644
+ - "sonnet" # Use Sonnet instead of default
645
+ - "--max-turns"
646
+ - "{max_turns}"
647
+ ```
648
+
649
+ **Increase turn limit for complex tasks:**
650
+ ```yaml
651
+ ai:
652
+ backends:
653
+ claude:
654
+ max_turns: 500 # Default is 200
655
+ args:
656
+ - "--output-format"
657
+ - "stream-json"
658
+ - "--max-turns"
659
+ - "{max_turns}" # Will use 500
660
+ ```
661
+
662
+ **Use different backends per stage:**
663
+ ```yaml
664
+ ai:
665
+ default: claude
666
+ stage_backends:
667
+ REVIEW: codex # Use Codex for code review
668
+ ```
669
+
670
+ ### Adding a Custom Backend
671
+
672
+ Define any CLI tool as a backend:
673
+
674
+ ```yaml
675
+ ai:
676
+ backends:
677
+ my-backend:
678
+ command: my-ai-tool
679
+ args:
680
+ - "--prompt-file"
681
+ - "-" # Read from stdin
682
+ - "--json-output"
683
+ max_turns: 100
684
+ read_only: true # Cannot write files directly
685
+ ```
686
+
687
+ Then use it:
688
+ ```yaml
689
+ ai:
690
+ default: my-backend
691
+ # Or per-stage:
692
+ stage_backends:
693
+ QA: my-backend
694
+ ```
695
+
536
696
  ## Customizing Prompts
537
697
 
538
698
  Galangal uses a layered prompt system:
@@ -676,6 +836,8 @@ If the TEST stage keeps retrying instead of rolling back to DEV:
676
836
  2. If tests fail due to implementation bugs, the AI should report FAIL (not try to fix the code)
677
837
  3. Check that test commands exit with proper exit codes (0 for success, non-zero for failure)
678
838
 
839
+ **Note:** As of v0.12.0, when artifact markers are unclear (missing PASS/FAIL), Galangal prompts you to manually approve or reject instead of retrying indefinitely. You'll see the artifact content and can make the decision yourself.
840
+
679
841
  ### "Galangal has not been initialized" Error
680
842
 
681
843
  Run `galangal init` in your project root before using other commands.
@@ -91,7 +91,7 @@ cat '<prompt_file>' | claude \
91
91
  --output-format stream-json \
92
92
  --verbose \
93
93
  --max-turns 200 \
94
- --permission-mode acceptEdits
94
+ --permission-mode bypassPermissions
95
95
  ```
96
96
 
97
97
  The backend:
@@ -19,7 +19,7 @@ from galangal.logging import (
19
19
  workflow_logger,
20
20
  )
21
21
 
22
- __version__ = "0.6.3"
22
+ __version__ = "0.12.15"
23
23
 
24
24
  __all__ = [
25
25
  # Exceptions
@@ -0,0 +1,167 @@
1
+ """AI backend abstractions and factory functions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import shutil
6
+ from typing import TYPE_CHECKING
7
+
8
+ from galangal.ai.base import AIBackend
9
+ from galangal.ai.claude import ClaudeBackend
10
+ from galangal.ai.codex import CodexBackend
11
+
12
+ if TYPE_CHECKING:
13
+ from galangal.config.schema import AIBackendConfig, GalangalConfig
14
+ from galangal.core.state import Stage
15
+
16
+ # Registry of available backends
17
+ BACKEND_REGISTRY: dict[str, type[AIBackend]] = {
18
+ "claude": ClaudeBackend,
19
+ "codex": CodexBackend,
20
+ }
21
+
22
+ # Default fallback chain: backend -> fallback
23
+ DEFAULT_FALLBACKS: dict[str, str] = {
24
+ "codex": "claude",
25
+ "gemini": "claude",
26
+ }
27
+
28
+
29
+ def get_backend(
30
+ name: str,
31
+ config: GalangalConfig | None = None,
32
+ ) -> AIBackend:
33
+ """
34
+ Factory function to instantiate backends by name.
35
+
36
+ Args:
37
+ name: Backend name (e.g., "claude", "codex")
38
+ config: Optional project config to get backend-specific settings
39
+
40
+ Returns:
41
+ Instantiated backend with configuration
42
+
43
+ Raises:
44
+ ValueError: If backend name is unknown
45
+ """
46
+ backend_class = BACKEND_REGISTRY.get(name.lower())
47
+ if not backend_class:
48
+ available = list(BACKEND_REGISTRY.keys())
49
+ raise ValueError(f"Unknown backend: {name}. Available: {available}")
50
+
51
+ # Get backend-specific config if available
52
+ backend_config: AIBackendConfig | None = None
53
+ if config:
54
+ backend_config = config.ai.backends.get(name.lower())
55
+
56
+ return backend_class(backend_config)
57
+
58
+
59
+ def is_backend_available(
60
+ name: str,
61
+ config: GalangalConfig | None = None,
62
+ ) -> bool:
63
+ """
64
+ Check if a backend's CLI tool is available on the system.
65
+
66
+ Args:
67
+ name: Backend name (e.g., "claude", "codex")
68
+ config: Optional project config to get custom command names
69
+
70
+ Returns:
71
+ True if the backend's CLI is installed and accessible
72
+ """
73
+ # Check config for custom command name
74
+ cmd: str | None
75
+ if config and name.lower() in config.ai.backends:
76
+ cmd = config.ai.backends[name.lower()].command
77
+ else:
78
+ # Fallback to default command names
79
+ cli_commands = {
80
+ "claude": "claude",
81
+ "codex": "codex",
82
+ "gemini": "gemini", # Future
83
+ }
84
+ cmd = cli_commands.get(name.lower())
85
+
86
+ if not cmd:
87
+ return False
88
+ return shutil.which(cmd) is not None
89
+
90
+
91
+ def get_backend_with_fallback(
92
+ name: str,
93
+ fallbacks: dict[str, str] | None = None,
94
+ config: GalangalConfig | None = None,
95
+ ) -> AIBackend:
96
+ """
97
+ Get a backend, falling back to alternatives if unavailable.
98
+
99
+ Args:
100
+ name: Primary backend name
101
+ fallbacks: Optional custom fallback mapping. Defaults to DEFAULT_FALLBACKS.
102
+ config: Optional project config for backend settings
103
+
104
+ Returns:
105
+ The requested backend if available, otherwise the fallback backend
106
+
107
+ Raises:
108
+ ValueError: If neither primary nor fallback backends are available
109
+ """
110
+ fallbacks = fallbacks or DEFAULT_FALLBACKS
111
+
112
+ if is_backend_available(name, config):
113
+ return get_backend(name, config)
114
+
115
+ # Try fallback
116
+ fallback_name = fallbacks.get(name.lower())
117
+ if fallback_name and is_backend_available(fallback_name, config):
118
+ return get_backend(fallback_name, config)
119
+
120
+ # Last resort: try claude if it exists
121
+ if name.lower() != "claude" and is_backend_available("claude", config):
122
+ return get_backend("claude", config)
123
+
124
+ raise ValueError(f"Backend '{name}' not available and no fallback found")
125
+
126
+
127
+ def get_backend_for_stage(
128
+ stage: Stage,
129
+ config: GalangalConfig,
130
+ use_fallback: bool = True,
131
+ ) -> AIBackend:
132
+ """
133
+ Get the appropriate backend for a specific stage.
134
+
135
+ Checks config.ai.stage_backends for stage-specific overrides,
136
+ otherwise uses config.ai.default.
137
+
138
+ Args:
139
+ stage: The workflow stage
140
+ config: Project configuration
141
+ use_fallback: If True, fall back to alternative backends if primary unavailable
142
+
143
+ Returns:
144
+ The configured backend for the stage
145
+ """
146
+ # Check for stage-specific backend override
147
+ stage_key = stage.value.upper()
148
+ if stage_key in config.ai.stage_backends:
149
+ backend_name = config.ai.stage_backends[stage_key]
150
+ else:
151
+ backend_name = config.ai.default
152
+
153
+ if use_fallback:
154
+ return get_backend_with_fallback(backend_name, config=config)
155
+ return get_backend(backend_name, config)
156
+
157
+
158
+ __all__ = [
159
+ "AIBackend",
160
+ "ClaudeBackend",
161
+ "CodexBackend",
162
+ "BACKEND_REGISTRY",
163
+ "get_backend",
164
+ "get_backend_for_stage",
165
+ "get_backend_with_fallback",
166
+ "is_backend_available",
167
+ ]