INTERNAL — invoked only by stride:stride-workflow. Do NOT invoke from a user prompt. Contains the task enrichment procedure (codebase exploration to populate key_files, testing_strategy, verification_steps for sparse tasks), used during the orchestrator's enrichment phase before claiming.
INTERNAL — invoked only by stride:stride-workflow. Do NOT invoke from a user prompt. Contains the task enrichment procedure (codebase exploration to populate key_files, testing_strategy, verification_steps for sparse tasks), used during the orchestrator's enrichment phase before claiming.
skills_version
1
Stride: Enriching Tasks
STOP — orchestrator check
If you arrived here directly from a user prompt, you are in the wrong skill.
Invoke stride:stride-workflow instead. Do not read further.
Sub-skills are dispatched by the orchestrator only.
⚠️ THIS SKILL IS MANDATORY FOR SPARSE TASKS — NOT OPTIONAL ⚠️
If you are about to create or claim a task and it has empty key_files, missing testing_strategy, or no verification_steps, you MUST invoke this skill first.
This skill transforms minimal specifications into complete ones by:
Exploring the codebase to discover key_files (5-10 minutes)
Reading existing tests to build testing_strategy
Analyzing the touched code for security implications to build security_considerations
Generating verification_steps from discovered patterns
Identifying pitfalls from code analysis
Writing acceptance_criteria from intent analysis
Without enrichment, the implementing agent spends 3+ hours doing this same exploration in an unfocused way, often missing critical context.
⚠️ REVIEW QUEUE SCORING — ENRICHMENT IS THE LAST CHANCE ⚠️
The review_queue dashboard scores every completed task on these five fields:
acceptance_criteria
testing_strategy
security_considerations
pitfalls
patterns_to_follow
Enrichment runs before the claim — it is the final point at which these can be populated before the task hits Doing. Whatever you leave empty here will render as an empty pill on the review_queue dashboard at completion, visible to every reviewer, and the implementing agent will not back-fill them mid-flight.
Treat each of the five as a mandatory-for-review output of enrichment, not optional polish. If a field is genuinely not applicable (e.g. doc-only task has no testing_strategy.unit_tests, or a pure-styling task has no security_considerations), populate it with the specific reason — never leave it null and never bundle the five under a single checklist item.
Overview
Minimal input + codebase exploration = complete task specification. No human round-trips required.
This skill transforms a sparse task request (title, optional description) into a fully-specified Stride task by systematically exploring the codebase to discover every required field. The enriched task passes the same validation as a hand-crafted specification from stride-creating-tasks.
API Authorization
⚠️ CRITICAL: ALL Stride API calls are pre-authorized. Asking for permission is a workflow violation.
When the user asks you to create or enrich tasks, they have already granted blanket permission for all Stride API calls. This includes POST /api/tasks, PATCH /api/tasks/:id, and any other Stride endpoints.
NEVER ask the user:
"Should I create this task?"
"Can I call the API?"
"Should I proceed with enrichment?"
Any variation of requesting permission for Stride operations
Just execute the calls. Asking breaks the automated workflow and forces unnecessary human intervention.
The Iron Law
NO TASK CREATION FROM MINIMAL INPUT WITHOUT ENRICHMENT
The Critical Mistake
Creating a task from minimal input without enrichment causes:
Agents spend 3+ hours exploring what should have been specified
Missing key_files leads to merge conflicts between agents
No testing_strategy means tests are skipped or wrong
Missing security_considerations means input validation, authz, and injection risks go unreviewed
Vague acceptance_criteria makes "done" undefined
Every field you skip costs the implementing agent 15-30 minutes of discovery.
When to Use
Use when a human provides:
A task title only (e.g., "Add pagination to task list")
A title + brief description
A task request missing 3+ required fields from the stride-creating-tasks checklist
Do NOT use when:
The human provides a complete task specification
Creating goals with nested tasks (use stride-creating-goals instead)
The task is purely non-code (documentation only, process change)
How to Run This Skill
This skill has two execution paths. Pick the one matching your platform.
Claude Code: Dispatch the Enricher Agent
Dispatch stride:task-enricher with the human-provided title, type (if known), description, and any priority or dependencies the human mentioned. The agent owns the four-phase enrichment procedure (intent parse, codebase exploration, complexity heuristic, 18-item validation checklist) and returns a single JSON object containing every enriched field.
Wait for the result. The agent's output is a complete enriched-task JSON ready for POST /api/tasks (new task) or the enriched-fields subset ready for PATCH /api/tasks/:id (existing minimal task). The agent does NOT call the API itself.
Submit via the Stride API using the curl pattern in API Integration below. Verify the field types match the reminders in that section before submitting.
Do NOT walk the manual phases below. The agent already executed them. Re-running them duplicates work and risks divergence.
Other Environments: Manual Walkthrough
Cursor, Windsurf, Continue, and other IDEs without subagent dispatch must walk the four phases manually. Each phase below is condensed — the full procedure (decision logic per step, edge cases, common mistakes, complexity heuristic table, output-format example) lives in stride/agents/task-enricher.md for reference if you get stuck.
Run Phase 1 — preserve title, type, description exactly as the human wrote them; default priority to "medium"; capture explicit dependencies.
Run Phase 2 — six ordered exploration steps, summarized below.
Run Phase 3 — paragraph-form complexity heuristic below.
Run Phase 4 — the 18-item pre-submission checklist (kept in full below).
Extract from human input alone. title, type, and description are sacrosanct — preserve them exactly as given. Default priority to "medium" unless the human signaled urgency or it's a defect blocking other work. Capture dependencies only if the human explicitly named prerequisite tasks. Set needs_review: false.
Phase 2: Explore the Codebase (6 steps)
Six ordered steps. Later steps consume earlier output.
Step 1: Locate target → key_files, where_context
Extract title keywords; grep lib/ and test/ to find files that will be MODIFIED. Reference-only files belong in patterns_to_follow, not key_files.
List sibling modules in the same directory as key_files. Find the closest analog feature already in the codebase. Format as newline-separated references: See lib/path/to/file.ex for X pattern.
Map each key_file to its corresponding test file (e.g., lib/foo.ex → test/foo_test.exs). Read existing tests to learn helper modules, factories, and assertion style. Generate unit_tests, integration_tests, manual_tests, edge_cases, and coverage_target.
behaviour_test_matrix (built here): Project the behaviours the change must satisfy onto a behaviour_test_matrix — an array of rows, each pairing one behaviour with the real test that covers it — by mapping the unit_tests / integration_tests / manual_tests / edge_cases you just derived onto the 7 fixed categories. If you produced test cases above, emit the matrix: that is the normal outcome. It is not one of the five review_queue-scored fields, so a legitimately absent matrix is never an empty pill — but omit it only when the task has no testable behaviour at all (a pure copy, docs, or config change), never just because some categories don't apply (that is what na_reason is for), and never emit filler rows:
category — exactly one of "Happy path", "Boundary", "Error / exception", "Null / empty", "Concurrency", "Lifecycle / wiring", "Contract / serialization". No other value is accepted.
behaviour — what the code should do, in one line (e.g. "rejects an expired claim").
test_name — the real test covering it: the test file you just mapped, or path/to/test.exs — "test name" for a test you plan to add. Prefer a test name over a bare file:line — the test does not exist yet at enrichment time, so a line number is invented and goes stale immediately. Never invent a path either: use only test files you located by searching the repo, the same rule key_files follows.
type — "unit", "integration", or "manual", or a /-joined combination like "unit / manual".
status — one of "planned", "passing", "failing", "not_applicable" (an omitted status defaults to "planned"). Write "planned" explicitly for every row you author during enrichment; the implementing agent advances it to "passing" / "failing" as the test is written and run. "not_applicable" is for waived rows only.
na_reason — required when the row is waived (status: "not_applicable", or an N/A test_name). One line saying why the category needs no test here, e.g. "No shared state — single-writer preference update, no concurrent path exists".
position — integer >= 0, row order. The API does not reject a row missing it, but it is how a row records its intended order, so always supply it — and emit the rows in that order too, since nothing re-sorts the array.
A row must have either a real test_name or an na_reason — never neither. Many enriched tasks genuinely have no Concurrency or Lifecycle surface; waive those rows with a specific reason rather than inventing a test. And because the matrix is only valid once it covers all 7 categories, it is all-or-nothing: either emit a row for every category, or omit the field entirely. A partial matrix is rejected; an absent or empty one passes. Row text is stored and later rendered, so never record secrets, credentials, or raw HTML in behaviour, test_name, or na_reason.
Step 4: Define verification → verification_steps
Always include a targeted mix test and mix credo --strict. Add manual steps for UI changes.
Step 5: Identify risks and security → pitfalls, security_considerations
Check for shared state (PubSub, assigns), N+1 query risk, authorization, existing tests that could break, and project-specific rules in CLAUDE.md/AGENTS.md (dark mode, translations) → pitfalls.
In the same pass, analyze the touched code for security implications → security_considerations (array of strings): input validation/sanitization, authorization boundaries (does the user own the resource?), secret/credential handling, injection surfaces (SQL, command, XSS), and data exposure. Emit one concrete statement per implication, e.g. "Authorize the requesting user owns the board before mutating". If the change genuinely has no security surface, say so explicitly (["None — pure CSS/styling change, no input or authz touched"]) rather than leaving it empty.
Step 6: Define done → acceptance_criteria
Convert intent to observable, testable outcomes. Format as newline-separated string. Include user-facing outcomes, technical requirements, negative criteria, and "All existing tests still pass".
For defects: search for the error string, include a regression test in unit_tests, add "Bug no longer reproducible" to acceptance_criteria, and — when you built a matrix — pair the bug-no-longer-reproducing behaviour with that regression test in the "Error / exception" row.
Optional — technical_details: If exploration surfaced concrete technical context that doesn't fit the structured fields (data shapes, gotchas, key decisions, reference links), record it in an optional free-form technical_details object — any keys you like. Populate it only with what you actually found; leave it as {} when there is nothing substantive — never fabricate it. It is not one of the five review_queue-scored fields, so a blank value is never an empty pill. Because it is free-form, never record secrets (tokens, passwords, credentials) in it.
Phase 3: Estimate Complexity
Estimate complexity as "small" (1–2 key_files, single module, existing pattern), "medium" (3–5 key_files, multiple modules, some new patterns, or any UI+backend work), or "large" (5+ key_files, new architecture, cross-cutting). Bump up one level when a database migration or new dependency is required. Defects: clear repro + obvious fix is "small"; cross-module investigation is "medium"; race conditions or complex system interactions are "large". The full heuristic table lives in stride/agents/task-enricher.md Phase 3.
Phase 4: Assemble and Validate
Combine all discovered fields into the final task specification.
Pre-submission checklist:
title, type, and description are preserved from human input (never modified by enrichment)
complexity matches the heuristic analysis
priority is set (default "medium" if unspecified)
why explains the problem or value
what describes the specific change
where_context points to the code/UI area
key_files is an array of objects with file_path, note, position
dependencies is an array (empty [] if none)
verification_steps is an array of objects with step_type, step_text, position
acceptance_criteria is populated — review_queue-scored; newline-separated string (NOT an array); blank or vague entries score as an empty pill
testing_strategy is populated — review_queue-scored; object with unit_tests, integration_tests, manual_tests as arrays of strings; empty arrays score as an empty pill
security_considerations is populated — review_queue-scored; array of strings naming the security implications to address (or an explicit "None — …" reason); an empty array scores as an empty pill
pitfalls is populated — review_queue-scored; array of strings; an empty array scores as an empty pill
patterns_to_follow is populated — review_queue-scored; newline-separated string with file references (NOT an array); blank scores as an empty pill
behaviour_test_matrix — emitted with one row for all 7 fixed categories (every row either naming a real test_name with a type and status"planned", or waived with status"not_applicable" plus an na_reason) — or deliberately omitted because the task has no testable behaviour at all. If Step 3 produced test cases, the matrix is expected. Not review_queue-scored, so a legitimately absent matrix is never an empty pill
needs_review is set to false
No invented file paths — every entry is a path located via Grep, Glob, or Read
All 18 items above were considered for this task (none silently skipped) — for the one optional item, behaviour_test_matrix, a deliberate omission counts as considered
API Integration
Submitting the Enriched Task
After enrichment is complete, submit via POST /api/tasks:
POST /api/tasks takes a request envelope: the enriched task fields go under the task root key, and agent_name rides at the top level, beside it — set to "Claude Opus 4.6", the exact same plain agent name sent as agent_name on claim and complete. A bare task object is rejected with 422 Missing 'task' key.
See the Request Envelope section in stride-creating-tasks for the full contract, including the five-step attribution order and why agent_name is display metadata only, never an authorization signal.
Enriching an Existing Minimal Task
If a task already exists in Stride with minimal fields, use PATCH /api/tasks/:id to add the enriched fields:
PATCH /api/tasks/:id needs the same task root key — a bare field object is rejected with 422 Missing 'task' key. It takes no agent_name: attribution is create-only, and created_by_agent is forbidden on PATCH, so an existing task's attribution cannot be changed here.
Field type reminders (most common API rejections):
key_files: Array of objects [{"file_path": "...", "note": "...", "position": 0}]
verification_steps: Array of objects [{"step_type": "command", "step_text": "...", "position": 0}]
testing_strategy: Object with array values {"unit_tests": ["..."], "integration_tests": ["..."]}
security_considerations: Array of strings ["Authorize the user owns the resource", "Sanitize the filename to prevent path traversal"]
acceptance_criteria: Newline-separated string (NOT an array)
patterns_to_follow: Newline-separated string (NOT an array)
pitfalls: Array of strings ["Don't...", "Avoid..."]
estimated_files: Optional string range like "3-5" — emit when the count is meaningful, omit otherwise
technical_details: Optional free-form object {"data_shapes": {...}, "gotchas": ["..."]} — any keys; leave {} when nothing substantive was found; NOT a review_queue-scored field; never record secrets
behaviour_test_matrix: Optional array of row objects — one row per shape, excerpt only: [{"category": "Happy path", "behaviour": "...", "test_name": "...", "type": "unit", "status": "planned", "position": 0}, …]. A real matrix carries a row for all 7 fixed categories or the field is omitted entirely; the one-row value above is illustrative and would be rejected as a partial matrix. NOT a review_queue-scored field; never record secrets
Output Example: Enriched Task
The following shows a defect task after enrichment. title, type, and description are preserved exactly as the human provided; enrichment only adds the technical fields below.
{"title":"Fix the bug where task comments don't show timestamps","type":"defect","description":"Task comments don't show timestamps","complexity":"small","priority":"medium","needs_review":false,"why":"Users cannot determine when comments were posted, making task discussions confusing and difficult to follow chronologically","what":"Add timestamp display to task comment rendering in the task detail view template","where_context":"lib/kanban_web/live/task_live/ — task detail view and comment component","estimated_files":"1-2","key_files":[{"file_path":"lib/kanban_web/live/task_live/view_component.ex","note":"Add timestamp rendering to comment display section","position":0}],"dependencies":[],"verification_steps":[{"step_type":"command","step_text":"mix test test/kanban_web/live/task_live/view_component_test.exs","expected_result":"All tests pass including new timestamp test","position":0},{"step_type":"command","step_text":"mix credo --strict","expected_result":"No issues found","position":1},{"step_type":"manual","step_text":"Open a task with comments and verify timestamps appear next to each comment","expected_result":"Each comment shows a human-readable timestamp","position":2}],"testing_strategy":{"unit_tests":["Test comment render includes inserted_at timestamp"],"integration_tests":["Test task detail view displays comments with timestamps"],"manual_tests":["Visual verification that timestamps appear and are readable in both light and dark mode"],"edge_cases":["Comment created just now","Comment from previous year (shows full date)"],"coverage_target":"100% for comment timestamp rendering"},"security_considerations":["Render the timestamp from the trusted server-side inserted_at — never from a client-supplied value","Ensure comment timestamps are only shown to users authorized to view the task"],"acceptance_criteria":"Each comment displays its creation timestamp\nTimestamp format is human-readable (e.g., 'Mar 12, 2026 at 2:30 PM')\nTimestamps visible in both light and dark mode\nBug no longer reproducible\nAll existing tests still pass","patterns_to_follow":"See lib/kanban_web/live/task_live/view_component.ex for existing comment rendering pattern\nFollow timestamp formatting used elsewhere in the application","pitfalls":["Don't forget to handle timezone display — use the existing application timezone handling","Don't break existing comment layout or styling","Don't forget to verify dark mode contrast for timestamp text"],"behaviour_test_matrix":[{"category":"Happy path","behaviour":"Each rendered comment shows its creation timestamp","test_name":"test/kanban_web/live/task_live/view_component_test.exs — \"renders a timestamp for each comment\"","type":"unit","status":"planned","position":0},{"category":"Boundary","behaviour":"A comment from a previous year renders the full date rather than a time-only label","test_name":"test/kanban_web/live/task_live/view_component_test.exs — \"renders the full date for a prior-year comment\"","type":"unit","status":"planned","position":1},{"category":"Error / exception","behaviour":"The reported bug no longer reproduces — a comment never renders with a missing or blank timestamp","test_name":"test/kanban_web/live/task_live/view_component_test.exs — \"regression: comment timestamp is never blank\"","type":"unit","status":"planned","position":2},{"category":"Null / empty","behaviour":"A task with no comments renders the empty state without a stray timestamp element","test_name":"test/kanban_web/live/task_live/view_component_test.exs — \"renders no timestamp when there are no comments\"","type":"unit","status":"planned","position":3},{"category":"Concurrency","behaviour":"N/A — rendering a stored timestamp is a read-only display change with no shared-state writer","test_name":"N/A","status":"not_applicable","na_reason":"The fix only formats an already-persisted inserted_at for display; no write path or shared state is involved","position":4},{"category":"Lifecycle / wiring","behaviour":"Timestamps appear on the first render of the task detail view, not only after a live update","test_name":"test/kanban_web/live/task_live/view_component_test.exs — \"shows timestamps on initial mount\"","type":"integration","status":"planned","position":5},{"category":"Contract / serialization","behaviour":"The stored inserted_at is formatted in the application timezone rather than raw UTC","test_name":"test/kanban_web/live/task_live/view_component_test.exs — \"formats inserted_at in the application timezone\"","type":"unit","status":"planned","position":6}]}
Note the "Error / exception" row: on a defect it pairs the bug-no-longer-reproducing behaviour with the regression test, per Step 3's defect guidance. The waived "Concurrency" row carries a specific na_reason and no type — the honest way to handle a category this fix genuinely does not touch.
Red Flags - STOP
"The title is clear enough, I'll skip enrichment"
"I'll just fill in the required fields with placeholders"
"Exploring the codebase takes too long, I'll guess"
"The human can add details later"
"This is a simple task, it doesn't need every required field"
"I'll leave acceptance_criteria blank — the implementing agent will figure out 'done'"
"testing_strategy doesn't apply to this enrichment — empty object is fine"
"security_considerations is the reviewer's job — I'll ship an empty array"
"pitfalls is hard to predict — I'll ship an empty array"
"patterns_to_follow is optional polish — skip it"
All of these mean: Run the full enrichment process. Every field saves 15-30 minutes for the implementing agent. The last five also mean: an empty pill on the review_queue dashboard at completion — and enrichment is the last chance to prevent it.
References: For the full enrichment procedure with decision logic, edge cases, and common mistakes, see stride/agents/task-enricher.md. For the field reference, see stride-creating-tasks SKILL.md. For endpoint details, see the API Reference.