diff --git a/examples/06-task-dependencies/README.md b/examples/06-task-dependencies/README.md new file mode 100644 index 0000000..5d84a1a --- /dev/null +++ b/examples/06-task-dependencies/README.md @@ -0,0 +1,123 @@ +# 06 — Task Dependencies (Orchestrator Pattern) + +Chain multiple Tasks together so one agent's output feeds into the next. +This pattern lets you break complex work into specialized steps — for +example, one agent writes code and another reviews it. + +## Use Case + +Run a multi-step workflow where a coding agent creates a PR, then a +review agent examines the PR and leaves feedback. The review agent +automatically receives the branch name and PR URL produced by the first +agent through prompt templates. + +## How It Works + +Axon's `dependsOn` field keeps Task B in a `Waiting` phase until Task A +succeeds. When Task A completes, Axon captures its structured outputs +(lines matching `key: value` printed between output markers) and makes +them available to Task B via Go template syntax in the prompt: + +``` +{{ index .Deps "" "Results" "" }} +``` + +The controller resolves the template before creating Task B's Job, so the +downstream agent sees a fully rendered prompt with concrete values. + +## Resources + +| File | Kind | Purpose | +|------|------|---------| +| `github-token-secret.yaml` | Secret | GitHub token for cloning and PR creation | +| `credentials-secret.yaml` | Secret | Claude OAuth token for the agents | +| `workspace.yaml` | Workspace | Git repository to clone | +| `task-a-implement.yaml` | Task | Step 1: implement a feature and open a PR | +| `task-b-review.yaml` | Task | Step 2: review the PR (depends on Task A) | + +## Steps + +1. **Edit the secrets** — replace placeholders in both `github-token-secret.yaml` + and `credentials-secret.yaml` with your real tokens. + +2. **Edit `workspace.yaml`** — set your repository URL and branch. + +3. **Apply all resources at once:** + +```bash +kubectl apply -f examples/06-task-dependencies/ +``` + +4. **Watch the Tasks:** + +```bash +kubectl get tasks -w +``` + +You should see Task A start immediately while Task B stays in `Waiting`. +Once Task A succeeds, Task B transitions to `Pending` and then `Running`. + +5. **Stream logs for each task:** + +```bash +# In one terminal: +axon logs implement-feature -f + +# In another terminal (once Task B starts): +axon logs review-feature -f +``` + +6. **Cleanup:** + +```bash +kubectl delete -f examples/06-task-dependencies/ +``` + +## Key Behaviors + +- **Automatic waiting** — Task B never starts until Task A succeeds. No + polling or manual coordination needed. +- **Failure propagation** — if Task A fails, Task B immediately fails with + a message indicating the dependency failure. +- **Cycle detection** — the controller detects circular dependencies at + creation time and fails the task immediately. +- **Branch locking** — both tasks use the same branch. The controller + ensures only one runs at a time (Task B waits for the lock too). +- **Output forwarding** — Task A's captured outputs (branch, PR URL) are + injected into Task B's prompt via Go templates. + +## Extending This Pattern + +You can chain more than two tasks by adding additional `dependsOn` +references. For example, a three-step pipeline: + +```yaml +# task-c-merge.yaml +apiVersion: axon.io/v1alpha1 +kind: Task +metadata: + name: merge-feature +spec: + type: claude-code + prompt: | + The review for PR {{ index .Deps "review-feature" "Results" "pr" }} + has been completed. If the review approved the changes, merge the PR. + dependsOn: + - review-feature + credentials: + type: oauth + secretRef: + name: claude-oauth-token + workspaceRef: + name: my-workspace +``` + +A task can also depend on multiple upstream tasks: + +```yaml +dependsOn: + - implement-backend + - implement-frontend +``` + +In this case, the task waits until **all** listed dependencies succeed. diff --git a/examples/06-task-dependencies/credentials-secret.yaml b/examples/06-task-dependencies/credentials-secret.yaml new file mode 100644 index 0000000..4c8e2c4 --- /dev/null +++ b/examples/06-task-dependencies/credentials-secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: claude-oauth-token +type: Opaque +stringData: + # TODO: Replace with your Claude OAuth token + CLAUDE_CODE_OAUTH_TOKEN: "REPLACE-ME" diff --git a/examples/06-task-dependencies/github-token-secret.yaml b/examples/06-task-dependencies/github-token-secret.yaml new file mode 100644 index 0000000..1ffa06d --- /dev/null +++ b/examples/06-task-dependencies/github-token-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: github-token +type: Opaque +stringData: + # TODO: Replace with your GitHub Personal Access Token + # Required permissions: repo (for private repos), workflow (optional) + GITHUB_TOKEN: "ghp_REPLACE-ME" diff --git a/examples/06-task-dependencies/task-a-implement.yaml b/examples/06-task-dependencies/task-a-implement.yaml new file mode 100644 index 0000000..746d94f --- /dev/null +++ b/examples/06-task-dependencies/task-a-implement.yaml @@ -0,0 +1,17 @@ +apiVersion: axon.io/v1alpha1 +kind: Task +metadata: + name: implement-feature +spec: + type: claude-code + # TODO: Replace with a prompt describing the feature to implement + prompt: | + Implement a /health endpoint that returns JSON {"status": "ok"}. + Create a branch, commit the changes, and open a PR. + credentials: + type: oauth + secretRef: + name: claude-oauth-token + workspaceRef: + name: my-workspace + branch: "orchestrator-example" diff --git a/examples/06-task-dependencies/task-b-review.yaml b/examples/06-task-dependencies/task-b-review.yaml new file mode 100644 index 0000000..f674378 --- /dev/null +++ b/examples/06-task-dependencies/task-b-review.yaml @@ -0,0 +1,28 @@ +apiVersion: axon.io/v1alpha1 +kind: Task +metadata: + name: review-feature +spec: + type: claude-code + # This prompt uses Go template syntax to reference outputs from Task A. + # {{ index .Deps "" "Results" "" }} accesses structured outputs. + # {{ index .Deps "" "Outputs" }} accesses the raw output list. + prompt: | + Review the PR at {{ index .Deps "implement-feature" "Results" "pr" }} + on branch {{ index .Deps "implement-feature" "Results" "branch" }}. + + Check for: + - Correctness and edge cases + - Test coverage + - Code style and documentation + + Leave a review on the PR with your findings. + dependsOn: + - implement-feature + credentials: + type: oauth + secretRef: + name: claude-oauth-token + workspaceRef: + name: my-workspace + branch: "orchestrator-example" diff --git a/examples/06-task-dependencies/workspace.yaml b/examples/06-task-dependencies/workspace.yaml new file mode 100644 index 0000000..ddaf3e0 --- /dev/null +++ b/examples/06-task-dependencies/workspace.yaml @@ -0,0 +1,10 @@ +apiVersion: axon.io/v1alpha1 +kind: Workspace +metadata: + name: my-workspace +spec: + # TODO: Replace with your repository URL + repo: https://github.com/your-org/your-repo.git + ref: main + secretRef: + name: github-token diff --git a/examples/README.md b/examples/README.md index 7fcd9d1..274a1f2 100644 --- a/examples/README.md +++ b/examples/README.md @@ -16,6 +16,7 @@ Ready-to-use patterns and YAML manifests for orchestrating AI agents with Axon. | [03-taskspawner-github-issues](03-taskspawner-github-issues/) | Automatically create Tasks from labeled GitHub issues | | [04-taskspawner-cron](04-taskspawner-cron/) | Run agent tasks on a cron schedule | | [05-task-with-agentconfig](05-task-with-agentconfig/) | Inject reusable instructions and plugins via AgentConfig | +| [06-task-dependencies](06-task-dependencies/) | Chain Tasks with `dependsOn` and prompt templates (orchestrator pattern) | ## How to Use