Implement fanout gate: waits-for children aggregation (gt-8tmz.38)

- Add children-of(step-id) syntax for explicit spawner reference
- Add WaitsForSpec type and ParseWaitsFor() helper
- Update cook.go to create DepWaitsFor dependencies with metadata
- Infer spawner from first needs entry when using all-children
- Add validation tests for children-of() syntax
- Add unit tests for ParseWaitsFor()

This completes the Christmas Ornament aggregation pattern:
- survey-workers does for-each → creates N children
- aggregate waits-for children-of(survey-workers)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-25 17:33:37 -08:00
parent 54a051aba3
commit ba17898365
3 changed files with 182 additions and 8 deletions

View File

@@ -2,6 +2,7 @@ package main
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
@@ -720,6 +721,36 @@ func collectDependencies(step *formula.Step, idMapping map[string]string, deps *
})
}
// Process waits_for field (gt-8tmz.38) - fanout gate dependency
if step.WaitsFor != "" {
waitsForSpec := formula.ParseWaitsFor(step.WaitsFor)
if waitsForSpec != nil {
// Determine spawner ID
spawnerStepID := waitsForSpec.SpawnerID
if spawnerStepID == "" && len(step.Needs) > 0 {
// Infer spawner from first need
spawnerStepID = step.Needs[0]
}
if spawnerStepID != "" {
if spawnerIssueID, ok := idMapping[spawnerStepID]; ok {
// Create WaitsFor dependency with metadata
meta := types.WaitsForMeta{
Gate: waitsForSpec.Gate,
}
metaJSON, _ := json.Marshal(meta)
*deps = append(*deps, &types.Dependency{
IssueID: issueID,
DependsOnID: spawnerIssueID,
Type: types.DepWaitsFor,
Metadata: string(metaJSON),
})
}
}
}
}
// Recursively handle children
for _, child := range step.Children {
collectDependencies(child, idMapping, deps)