Skip to content

Add taskTemplates to TaskSpawner for per-item pipeline spawning#721

Open
kelos-bot[bot] wants to merge 6 commits intomainfrom
kelos-task-710
Open

Add taskTemplates to TaskSpawner for per-item pipeline spawning#721
kelos-bot[bot] wants to merge 6 commits intomainfrom
kelos-task-710

Conversation

@kelos-bot
Copy link

@kelos-bot kelos-bot bot commented Mar 17, 2026

/kind feature

What this PR does / why we need it:

Adds taskTemplates (plural) to TaskSpawnerSpec so that each discovered work item can spawn a pipeline of Tasks with automatic dependency wiring and result passing between steps.

Previously, TaskSpawner could only create one Task per work item via the singular taskTemplate field. This forced users to either cram everything into one monolithic prompt or set up multiple independent TaskSpawners with fragile label-based gating conventions.

With taskTemplates, a spawner can define a multi-step pipeline (e.g., plan → implement → test → open-pr) where each step's dependsOn references sibling step names. The spawner translates these to fully-qualified task names at creation time:

dependsOn: [plan]  →  dependsOn: [issue-pipeline-42-plan]

Key design decisions (per maintainer feedback on #710):

  • DAG dependencies supported from the start (Task controller already handles cycle detection)
  • Per-task TTL semantics (existing behavior, per-pipeline TTL deferred)
  • Conditional execution deferred to follow-up
  • taskTemplate (singular) preserved alongside taskTemplates for backward compatibility

What changes are included:

  • API types: NamedTaskTemplate struct with Name field embedding TaskTemplate; TaskSpawnerSpec.TaskTemplates field; TaskTemplate changed to pointer (optional); TotalPipelinesCreated in status
  • CEL validation: mutual exclusivity, unique step names (via exists_one), valid dependsOn refs, no self-references, workspaceRef check for GitHub/Jira sources; MaxItems/MaxLength constraints for CEL cost budget
  • Spawner: pipeline mode creates N tasks per item with {spawner}-{itemID}-{stepName} naming; kelos.dev/pipeline-item label; maxConcurrency counts pipeline instances; maxTotalTasks counts individual tasks; partial pipeline recovery creates only missing steps
  • Tests: 8 new unit tests for pipeline creation, dependency translation, concurrency, budget, labels, independent step fields, partial recovery
  • Example: examples/09-taskspawner-pipeline/ with 3-step pipeline YAML and README

Which issue(s) this PR is related to:

Fixes #710

Special notes for your reviewer:

  • TaskTemplate changed from value to pointer type — all existing test files updated to use &kelosv1alpha1.TaskTemplate{}
  • The controller's workspace resolution updated to check both taskTemplate and taskTemplates via taskSpawnerWorkspaceRef() helper
  • Pipeline partial recovery checks ALL steps for missing tasks (not just the first step)
  • CEL rules use exists_one() instead of toSet() for K8s 1.31 compatibility, with MaxItems=10/MaxLength=63 constraints to stay within cost budget
  • Step names constrained to DNS-safe pattern ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$

Does this PR introduce a user-facing change?

Add taskTemplates (plural) to TaskSpawner for per-item multi-step pipeline spawning. Each discovered work item can now spawn a pipeline of Tasks with automatic dependency wiring between steps.

@gjkim42 gjkim42 self-assigned this Mar 17, 2026
@gjkim42 gjkim42 added priority/important-soon triage-accepted kind/api Categorizes issue or PR as related to API changes kind/feature Categorizes issue or PR as related to a new feature labels Mar 17, 2026
@github-actions github-actions bot removed needs-triage needs-kind Indicates an issue or PR lacks a kind/* label needs-priority labels Mar 17, 2026
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 issues found across 16 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cmd/kelos-spawner/main_test.go">

<violation number="1" location="cmd/kelos-spawner/main_test.go:2456">
P2: The partial-recovery test should assert the exact expected task names, not only the total count, to reliably validate pipeline recovery semantics.</violation>
</file>

<file name="api/v1alpha1/taskspawner_types.go">

<violation number="1" location="api/v1alpha1/taskspawner_types.go:370">
P2: Constrain `taskTemplates[].name` to DNS-safe characters before using it in generated Task names.</violation>

<violation number="2" location="api/v1alpha1/taskspawner_types.go:396">
P1: Require at least one pipeline step when `taskTemplates` is set; an empty list can fall into single-task mode and dereference a nil `taskTemplate`.</violation>
</file>

<file name="internal/manifests/install-crd.yaml">

<violation number="1" location="internal/manifests/install-crd.yaml:1835">
P1: The one-of CEL rule allows `taskTemplates: []`, which leaves both modes effectively unset and can trigger a nil-pointer dereference in the spawner.</violation>
</file>

<file name="cmd/kelos-spawner/main.go">

<violation number="1" location="cmd/kelos-spawner/main.go:276">
P1: Using the first step as a sentinel for new item detection prevents partial pipeline recovery. If the spawner crashes after creating the first step, the remaining steps are never recovered.</violation>

<violation number="2" location="cmd/kelos-spawner/main.go:338">
P1: The `stepsToCreate` variable uses the total number of templates instead of counting only the missing steps, which causes the `maxTotalTasks` budget calculation to overestimate the required budget during partial pipeline recovery. This can permanently deadlock the spawner.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@gjkim42
Copy link
Collaborator

gjkim42 commented Mar 17, 2026

/kelos pick-up

@kelos-bot
Copy link
Author

kelos-bot bot commented Mar 17, 2026

🤖 Kelos Task Status

Task kelos-pr-responder-721 has succeeded. ✅

@kelos-bot
Copy link
Author

kelos-bot bot commented Mar 17, 2026

🤖 Kelos Agent @gjkim42

/kelos needs-input

All review feedback has been addressed in the latest commits:

  1. Partial pipeline recovery — Fixed sentinel logic to check ALL steps for missing tasks (not just the first step). Partial pipelines are now recovered correctly.
  2. Budget calculationstepsToCreate now counts only missing steps via missingStepsPerItem, preventing deadlock during partial recovery.
  3. Empty taskTemplates validation — Added MinItems=1 on taskTemplates to prevent taskTemplates: [] from bypassing mutual exclusivity.
  4. DNS-safe step names — Added Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ constraint on step names.
  5. Test assertions — Partial recovery test now asserts exact expected task names.
  6. CLI printer — Updated to show workspace name for pipeline-mode TaskSpawners.

Ready for re-review.

@gjkim42
Copy link
Collaborator

gjkim42 commented Mar 17, 2026

rebase on origin/main
/kelos pick-up

@kelos-bot
Copy link
Author

kelos-bot bot commented Mar 17, 2026

🤖 Kelos Task Status

Task kelos-pr-responder-721 has succeeded. ✅

@kelos-bot kelos-bot bot force-pushed the kelos-task-710 branch 2 times, most recently from babb98b to 993b240 Compare March 17, 2026 13:51
@kelos-bot
Copy link
Author

kelos-bot bot commented Mar 17, 2026

🤖 Kelos Task Status

Task kelos-pr-responder-721 has succeeded. ✅

gjkim42 and others added 3 commits March 17, 2026 15:09
Add support for multi-step pipeline spawning in TaskSpawner. When
taskTemplates is set, each discovered work item spawns N tasks (one per
step) with automatic dependency wiring. Step names in dependsOn are
translated to fully-qualified task names ({spawner}-{itemID}-{stepName}).

Key changes:
- Add NamedTaskTemplate struct with Name field that embeds TaskTemplate
- Add TaskTemplates field to TaskSpawnerSpec alongside existing TaskTemplate
- Make TaskTemplate a pointer (optional) with CEL mutual exclusivity rule
- Add CEL validation for unique step names and valid dependsOn references
- Spawner creates N tasks per item in pipeline mode with pipeline-item label
- maxConcurrency counts pipeline instances (not individual tasks)
- maxTotalTasks counts individual tasks for resource budgeting
- Add TotalPipelinesCreated to TaskSpawnerStatus
- Add 8 unit tests covering pipeline creation, dependency translation,
  concurrency, budget, labels, independent step fields, and partial recovery
- Add pipeline example under examples/09-taskspawner-pipeline/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reuse the pipelineMode variable already computed earlier instead of
recomputing it as isPipelineMode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address review feedback:
- Fix partial pipeline recovery: check all steps for missing tasks instead
  of using only the first step as a sentinel. This ensures that if the
  spawner crashes mid-pipeline, remaining steps are created on the next cycle.
- Fix maxTotalTasks budget calculation to count only missing steps instead
  of total templates, preventing deadlock during partial recovery.
- Add MinItems=1 validation on taskTemplates to prevent empty list from
  bypassing mutual exclusivity and causing nil-pointer dereference.
- Add DNS-safe pattern constraint on step names to ensure generated task
  names are valid Kubernetes resource names.
- Improve partial recovery test to assert exact expected task names.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
gjkim42 and others added 3 commits March 17, 2026 15:09
- Use taskSpawnerWorkspaceRef helper in CLI printer so pipeline-mode
  TaskSpawners display the workspace name instead of generic source
- Move missingStepsPerItem map allocation inside pipelineMode branch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The rebase on main introduced a new integration test that used
TaskTemplate as a value type, but the PR changed it to a pointer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace toSet() (unavailable in K8s 1.31) with exists_one() for unique
step name validation. Add MaxItems/MaxLength constraints to taskTemplates,
dependsOn, and step names to keep CEL rule cost within budget. Replace
nested map().exists() with simpler exists() to reduce estimated cost.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kelos-bot kelos-bot bot force-pushed the kelos-task-710 branch from 993b240 to 978fdb3 Compare March 17, 2026 15:23
@kelos-bot
Copy link
Author

kelos-bot bot commented Mar 17, 2026

🤖 Kelos Agent @gjkim42

/kelos needs-input

Rebased on origin/main and fixed CEL validation rules that were incompatible with K8s 1.31:

  • Replaced toSet() (unavailable in K8s 1.31) with exists_one() for unique step name validation
  • Added MaxItems=10/MaxLength=63 constraints to keep CEL rule cost within budget
  • All unit tests, integration tests, and verification pass locally

All previous review feedback was already addressed. Ready for re-review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

generated-by-kelos kind/api Categorizes issue or PR as related to API changes kind/feature Categorizes issue or PR as related to a new feature needs-actor ok-to-test priority/important-soon release-note triage-accepted

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API: Add taskTemplates (plural) to TaskSpawner for per-item multi-step pipeline spawning

1 participant