diff --git a/examples/06-task-dependencies/README.md b/examples/06-task-dependencies/README.md new file mode 100644 index 0000000..b08a29f --- /dev/null +++ b/examples/06-task-dependencies/README.md @@ -0,0 +1,133 @@ +# 06 — Task Dependencies + +A three-stage pipeline that chains Tasks with `dependsOn`. Each stage waits +for the previous one to succeed before starting, and downstream prompts +reference upstream outputs using Go template syntax. + +## Use Case + +Break a feature into sequential stages — implement, test, review — where +each agent builds on the previous agent's work on the same branch. + +## Pipeline + +``` +implement ──> write-tests ──> review +``` + +1. **implement** — scaffolds the feature and pushes to a branch. +2. **write-tests** — waits for `implement`, then adds tests on the same branch. +3. **review** — waits for `write-tests`, then opens and reviews a PR. + +## 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 agent | +| `workspace.yaml` | Workspace | Git repository to clone | +| `tasks.yaml` | Task (x3) | The three-stage pipeline | + +## How Dependencies Work + +- A Task with `dependsOn: [other-task]` stays in `Waiting` phase until + every listed dependency reaches `Succeeded`. +- If any dependency fails, the dependent Task fails immediately. +- Downstream prompts can reference upstream outputs with Go `text/template` + syntax: + + | Template Expression | Resolves To | + |---------------------|-------------| + | `{{ index .Deps "implement" "Outputs" }}` | Raw output lines from `implement` | + | `{{ index .Deps "implement" "Results" "branch" }}` | The `branch` result value | + | `{{ index .Deps "implement" "Results" "pr" }}` | The `pr` URL result value | + | `{{ index .Deps "implement" "Name" }}` | The dependency task name | + + Agents automatically capture `branch`, `pr`, `commit`, `base-branch`, + `cost-usd`, `input-tokens`, and `output-tokens` as results. See the + [reference docs](../../docs/reference.md) for the full list. + +## 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. **Edit `tasks.yaml`** — customize the prompts for your feature. + +4. **Apply the resources:** + +```bash +kubectl apply -f examples/06-task-dependencies/ +``` + +5. **Watch the pipeline progress:** + +```bash +kubectl get tasks -w +``` + +You should see: + +``` +NAME TYPE PHASE AGE +implement claude-code Running 10s +write-tests claude-code Waiting 10s +review claude-code Waiting 10s +``` + +Then as `implement` succeeds: + +``` +NAME TYPE PHASE AGE +implement claude-code Succeeded 5m +write-tests claude-code Running 5m +review claude-code Waiting 5m +``` + +6. **Stream logs from a specific stage:** + +```bash +axon logs implement -f +axon logs write-tests -f +``` + +7. **Cleanup:** + +```bash +kubectl delete -f examples/06-task-dependencies/ +``` + +## CLI Equivalent + +You can create the same pipeline with `axon run`: + +```bash +axon run -p "Add a /healthz endpoint" \ + --name implement \ + --branch feature/healthz + +axon run -p "Write tests for the /healthz endpoint" \ + --name write-tests \ + --branch feature/healthz \ + --depends-on implement + +axon run -p "Review the changes and open a PR" \ + --name review \ + --branch feature/healthz \ + --depends-on write-tests +``` + +## Notes + +- All three tasks share the same `branch: feature/healthz`. Axon's branch + mutex guarantees that only one task runs on a given branch at a time, + even without `dependsOn`. The `dependsOn` field adds the additional + guarantee that a task only starts after its dependencies **succeed** + (not just finish). +- If `implement` fails, both `write-tests` and `review` fail immediately + with the message "Dependency 'implement' failed". +- Prompt templates are resolved once, right before the Job is created. + If a template variable cannot be resolved, the raw prompt is used as + a fallback. 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/tasks.yaml b/examples/06-task-dependencies/tasks.yaml new file mode 100644 index 0000000..11c4771 --- /dev/null +++ b/examples/06-task-dependencies/tasks.yaml @@ -0,0 +1,68 @@ +# Task dependency chain: implement -> test -> review +# +# - "implement" runs first and creates a branch with changes. +# - "write-tests" waits for "implement" to succeed, then adds tests on +# the same branch using the branch name from implement's outputs. +# - "review" waits for "write-tests" to succeed, then reviews the PR. +# +# All three tasks share the same branch, so they are also serialized by +# the branch mutex — only one runs at a time. + +apiVersion: axon.io/v1alpha1 +kind: Task +metadata: + name: implement +spec: + type: claude-code + # TODO: Replace with your feature description + prompt: | + Add a /healthz HTTP endpoint that returns 200 OK with a JSON body + containing the server version. Push your changes to the branch. + credentials: + type: oauth + secretRef: + name: claude-oauth-token + workspaceRef: + name: my-workspace + branch: feature/healthz +--- +apiVersion: axon.io/v1alpha1 +kind: Task +metadata: + name: write-tests +spec: + type: claude-code + # This prompt is a Go text/template. When "implement" succeeds, Axon + # resolves {{ index .Deps "implement" "Results" "branch" }} to the + # branch name captured from implement's outputs (e.g. "feature/healthz"). + prompt: | + The implementation is on branch {{ index .Deps "implement" "Results" "branch" }}. + Write unit tests for the /healthz endpoint. Make sure all tests pass + before pushing. + credentials: + type: oauth + secretRef: + name: claude-oauth-token + workspaceRef: + name: my-workspace + branch: feature/healthz + dependsOn: [implement] +--- +apiVersion: axon.io/v1alpha1 +kind: Task +metadata: + name: review +spec: + type: claude-code + prompt: | + Review the changes on branch {{ index .Deps "write-tests" "Results" "branch" }}. + Open a pull request if one does not exist yet, then review it for + correctness, test coverage, and style. Leave comments on the PR. + credentials: + type: oauth + secretRef: + name: claude-oauth-token + workspaceRef: + name: my-workspace + branch: feature/healthz + dependsOn: [write-tests] 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..d1c8c24 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` into a multi-stage pipeline | ## How to Use