Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **F095**: Deterministic step display ordering in CLI execution summary — `--- Execution Details ---`, step stdout/stderr blocks, and `buildStepInfos` now display steps in workflow-defined order (following `Initial` → default transitions → `OnSuccess`) instead of non-deterministic Go map iteration; new domain-level `ExecutionOrder(*Workflow) []Step` and `NextDefaultStep(*Step) string` functions in `internal/domain/workflow/graph.go` provide a single source of truth for default-path graph traversal; TUI's private `orderedSteps`/`nextStepName` deleted and replaced by delegation to the domain functions; CLI display functions (`showExecutionDetails`, `showStepOutputs`, `showEmptyStepFeedback`, `buildStepInfos`) accept `*workflow.Workflow` parameter and iterate via `ExecutionOrder()` output; `showEmptyStepFeedback` switched from direct `execCtx.States` access to thread-safe `GetStepState` per-step lookup
- **F094**: Unified token counting via `ports.Tokenizer` port — all CLI-based agent providers (Claude, Gemini, Codex, GitHub Copilot, OpenCode) now count tokens through an injected `Tokenizer` interface instead of inline `len(output)/4` helpers; default `ApproximationTokenizer` preserves identical behavior; eliminates mutation side-effect on shared conversation turn state during input token estimation; dead `estimateTokens`/`estimateInputTokens` helpers removed; enables future swap to real token counting (e.g., tiktoken, stream-extracted counts) by changing a single injection point

## [0.8.1] - 2026-05-11
Expand Down
5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,6 @@ func TestWorkflowValidation(t *testing.T) {

## Common Pitfalls

- Always apply code deletions before writing tests that validate the deletion effect; tests may pass against overridden behavior instead of the intended code path
- Wrap YAML/JSON mapping errors (duration parse, type conversion) in domain error types; surface failures immediately to prevent silent defaults
- Never merge infrastructure provider stubs; always implement ExecuteConversation fully or return NotImplementedError with linked tracking issue
- When enabling session persistence in CLI providers, force JSON output format for reliable field extraction; document as known limitation that overrides user-specified format
Expand Down Expand Up @@ -284,10 +283,10 @@ func TestWorkflowValidation(t *testing.T) {
- Always stage all modified implementation files and run 'git status' before marking task complete; unstaged files indicate incomplete task closure.
- Update plan task status immediately when implementation completes; regenerate validation report to catch status-code mismatches before submission.
- Always replicate nolint:errcheck directives identically across all provider implementations; verify explanatory comments match before make lint
- Always test unplanned file modifications discovered during implementation; update task plan if intentional, revert if accidental

## Test Conventions

- Test context cancellation with context.WithCancel() and early ctx.Err() checks; verify operation fails with wrapped context.Canceled error within timeout
- Mock evaluators must have pre-configured results for every expression input; unconfigured expressions return zero value, which may bypass validation checks in evaluation pipelines
- Distinguish fixture path updates (allowed without review) from content changes (require explicit review); document rationale for content modifications in commit message
- Use _Integration suffix for tests requiring live agent execution or system dependencies; keep unit tests suffix-less in domain/application/infrastructure packages
Expand All @@ -307,7 +306,9 @@ func TestWorkflowValidation(t *testing.T) {
- Test event metadata persistence across all input variations for provider translation; include cases with missing optional nested fields to prevent silent metadata loss
- When testing YAML unmarshaling, assert on all nested struct fields; verify that arrays like Events.Subscribe and Events.Emit are populated, not defaulted to empty
- New gRPC and concurrency-heavy infrastructure requires >85% test coverage; run 'make test-race' to verify no data races in stream managers and lock-protected sections.
- Always write unit tests for CLI helper functions; parseInputFlags, resolvePromptInput, categorizeError must have >80% coverage before commit

## Review Standards

- Never mark implementation complete without confirming `make build`, `make lint`, and `make test` pass with zero violations
- Always verify all validation expert reports generate successfully; missing sections (Conformance, Coverage, Cleanup) indicate incomplete validation
17 changes: 17 additions & 0 deletions docs/user-guide/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,23 @@ awf run <workflow> [flags]

**Note:** For agent steps, the `output_format` field controls display filtering: `text` or omitted (default) shows human-readable output; `json` shows raw NDJSON. See [Output Formatting](agent-steps.md#streaming-output-display) for details.

### Execution Summary

After a workflow completes, AWF displays an execution summary with step statuses, durations, and output. Steps are always displayed in **workflow-defined order** — following the `Initial` state through default transitions and `on_success` references — regardless of actual execution timing or internal map ordering. This ensures consistent, readable output across runs:

```
--- Execution Details ---
Status: completed
Steps executed:
init completed (12ms)
build completed (1.234s)
test completed (856ms)
deploy completed (3.012s)
done completed (0ms)
```

Steps that were not executed (e.g., because the workflow branched to a different path) are omitted from the summary. Step stdout/stderr blocks follow the same ordering.

### Examples

```bash
Expand Down
51 changes: 45 additions & 6 deletions internal/domain/workflow/graph.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package workflow

import "strconv"
import (
"strconv"
"strings"
)

// VisitState represents the DFS visit state of a node during graph traversal.
// Used for three-color marking in cycle detection:
Expand Down Expand Up @@ -232,14 +235,50 @@ func DetectCycles(steps map[string]*Step, initial string) []string {

// formatCyclePath formats a cycle path as "A -> B -> C -> A".
func formatCyclePath(path []string) string {
if len(path) == 0 {
return strings.Join(path, " -> ")
}

// NextDefaultStep returns the next step name following the default (unconditional) path.
// Checks Transitions for an entry with empty When first; falls back to OnSuccess.
func NextDefaultStep(step *Step) string {
if step == nil {
return ""
}
result := path[0]
for i := 1; i < len(path); i++ {
result += " -> " + path[i]
for _, tr := range step.Transitions {
if tr.When == "" {
return tr.Goto
}
}
return step.OnSuccess
}

// ExecutionOrder returns steps in default-path order by walking the graph from Initial,
// following default transitions (empty When) and falling back to OnSuccess.
// Stops at terminal steps, visited steps (cycle prevention), or missing references.
func ExecutionOrder(wf *Workflow) []Step {
if wf == nil || len(wf.Steps) == 0 || wf.Initial == "" {
return nil
}
return result

visited := make(map[string]bool, len(wf.Steps))
steps := make([]Step, 0, len(wf.Steps))
current := wf.Initial

for current != "" && !visited[current] {
step, ok := wf.Steps[current]
if !ok {
break
}
visited[current] = true
steps = append(steps, *step)

if step.Type == StepTypeTerminal {
break
}
current = NextDefaultStep(step)
}

return steps
}

// GetTransitions returns all outbound transitions from a step.
Expand Down
Loading
Loading