| name | task-workflow |
| description | Shared implementation workflow for task-based skills, with profile-check sites wrapped in Jinja conditionals. |
| user-invocable | false |
Context Requirements
This skill is invoked by other skills (e.g., aitask-pick, aitask-explore, aitask-review) after they have selected a task. The calling skill MUST establish the following context before handing off:
| Variable | Type | Description |
|---|
task_file | string | Path to selected task file (e.g., aitasks/t16_implement_auth.md or aitasks/t10/t10_2_add_login.md) |
task_id | string | Task identifier (e.g., 16 or 16_2) |
task_name | string | Filename stem for branches/worktrees (e.g., t16_implement_auth or t16_2_add_login) |
is_child | boolean | Whether this is a child task |
parent_id | string/null | Parent task number if child (e.g., 16), null otherwise |
parent_task_file | string/null | Path to parent task file if child (e.g., aitasks/t16_implement_auth.md), null otherwise |
active_profile | object/null | Loaded execution profile from calling skill (or null if no profile) |
active_profile_filename | string/null | Scanner-returned filename for the profile (e.g., fast.yaml or local/fast.yaml), null if no profile |
previous_status | string | Task status before workflow began (for abort revert, e.g., Ready) |
folded_tasks | array/null | List of task IDs folded into this task (e.g., [106, 129_5]), or null/empty if none. Set by aitask-explore when existing tasks are folded into a new task. |
skill_name | string | Name of the calling skill for feedback tracking (e.g., pick, explore, pr-import) |
feedback_collected | boolean | Guard flag — initialized to false. Set to true after the Satisfaction Feedback Procedure runs. Prevents double execution across workflow paths. |
usage_collected | boolean | Guard flag — initialized to false. Set to true before the unconditional usage bump fires in Satisfaction Feedback Step 0. Set-before-call so a mid-procedure failure does not cause a retry double-bump. |
detected_agent_string | string/null | Agent string (e.g., claudecode/opus4_6). Set by either the verify-path append in planning.md Step 6.1 or by Agent Attribution in Step 7. Consumed by Agent Attribution (fast-path) and by Satisfaction Feedback in Step 9b to skip re-detection. Initialized to null. |
Workflow
Step 3: Task Status Checks
After a task is selected and confirmed, perform these checks before proceeding to Step 4.
Check 1 - Done but unarchived task:
- Read the task file's frontmatter
status field
- If status is
Done:
Check 2 - Orphaned parent task (empty children_to_implement):
Check 3 - Manual-verification task:
- Read the task file's frontmatter
issue_type field
- If
issue_type is manual_verification:
- Execute the Manual Verification Procedure (see
manual-verification.md)
- Skip Steps 6-8; proceed to Step 9 after the procedure returns
- Steps 4 (ownership) and 5 (worktree) still run before dispatch — manual verification is work that should be owned and locked
Check 4 - In-flight gated task, all gates now pass:
- Run
./.aitask-scripts/aitask_gate.sh archive-ready <taskid> and parse:
NO_GATES or BLOCKED:<csv> → skip this check (fall through to normal selection). This is the common case today — no task declares gates yet.
ALL_PASS → the task's substantive work landed in an earlier session and every declared gate now passes, but archival was deferred (it was kept active). This is the next-pick backstop for the deferral in Step 9. Use AskUserQuestion:
- Question: "This task has all gates passing and is ready to archive. Would you like to archive it now?"
- Header: "Archive"
- Options:
- "Yes, archive it" (description: "Skip implementation and archive the now-complete task")
- "No, keep it active" (description: "Leave the task as-is and end the workflow")
- If "Yes, archive it" → skip Steps 4-8, proceed directly to Step 9 (Post-Implementation) for archival
- If "No, keep it active" → end the workflow
Check 5 - In-flight task, resume from first unmet checkpoint:
This makes task-workflow re-entrant: a task left Implementing (crash, session loss, multi-day work) resumes from the first unmet recorded checkpoint instead of restarting at planning.
- Read the task file's frontmatter
status. If it is not Implementing, skip this check (a fresh task plans from scratch).
- Run
./.aitask-scripts/aitask_gate.sh resume-point <taskid> and parse the single-word result:
Note: Check 1, Check 2, and Check 4 should NOT set the task status to "Implementing" — the task is already done (or its work is complete and gated). Skip Step 4 (Assign Task) entirely when archiving via Check 1, Check 2, or Check 4. Check 3 does run Step 4 as normal. Check 5 also runs Step 4 (the in-flight lock must be reclaimed) — it does not skip it; step-skipping happens post-reclaim via Re-entry Routing.
If none of the checks trigger, proceed to Step 4 as normal.
Step 4: Assign Task to User
-
Email resolution (priority order):
-
Check task metadata: Read the assigned_to field from the task file's frontmatter.
-
Check userconfig: Read aitasks/metadata/userconfig.yaml and extract the email: field (if file exists).
-
Mismatch check: If both assigned_to and userconfig email are non-empty and DIFFERENT, use AskUserQuestion:
- Question: "Task is assigned to <assigned_to> but your userconfig email is <userconfig_email>. Which email to use?"
- Header: "Email"
- Options:
- "Keep <assigned_to>" (description: "Continue with the existing assignment")
- "Use <userconfig_email>" (description: "Override with your local email")
- Use the selected email and proceed to the Claim task ownership step below.
-
If assigned_to is non-empty (and matches userconfig, or userconfig is empty): use assigned_to. Display: "Using email from task metadata: <email>". Skip to Claim task ownership.
-
Profile-driven email resolution (profile 'remote', default_email: userconfig):
-
Use the userconfig email (from step 2). If userconfig is empty/missing, fall back to reading aitasks/metadata/emails.txt (first email). Display: "Profile 'remote': using email <email> (from userconfig)". If both are empty, prompt the user via AskUserQuestion as described in step 6 below.
-
Then skip step 6 and proceed to the Userconfig sync check below.
-
Otherwise, ask for email using AskUserQuestion:
- Read stored emails:
cat aitasks/metadata/emails.txt 2>/dev/null | sort -u
- Question: "Enter your email to track who is working on this task (optional):"
- Header: "Email"
- Options:
- List each stored email from emails.txt (if any exist)
- "Enter new email" (description: "Add a new email address")
- "Skip" (description: "Don't assign this task to anyone")
- If "Enter new email" selected:
- Ask user to type their email via
AskUserQuestion with free text (use the "Other" option)
-
Userconfig sync check: After email is resolved, if the final email differs from the userconfig email (or userconfig doesn't exist):
- Use
AskUserQuestion:
- Question: "The selected email (<email>) differs from your userconfig (<userconfig_email>). Update userconfig.yaml?"
- Header: "Userconfig"
- Options:
- "Yes, update userconfig" (description: "Save this email to userconfig.yaml for future use")
- "No, keep current userconfig" (description: "Use this email for now but don't change userconfig")
- If "Yes": Write
email: <email> to aitasks/metadata/userconfig.yaml (create file if needed with comment header # Local user configuration (gitignored, not shared))
- If "No": Proceed without updating
- Skip this check if: the final email matches userconfig, or email was resolved from userconfig itself, or no email was selected ("Skip")
-
Claim task ownership (lock, update status, commit, push):
If email was provided (new or selected):
./.aitask-scripts/aitask_pick_own.sh <task_num> --email "<email>"
If no email (user selected "Skip"):
./.aitask-scripts/aitask_pick_own.sh <task_num>
Parse the script output:
-
OWNED:<task_id> — Success. Proceed to Step 5.
-
FORCE_UNLOCKED:<previous_owner> + OWNED:<task_id> — Force-unlock succeeded. Inform user: "Force-unlocked stale lock held by <previous_owner>." Proceed to Step 5.
-
One of LOCK_RECLAIM:, RECLAIM_CRASH:, or RECLAIM_STATUS: (in addition to OWNED:) — task was already in Implementing and re-locked. When multiple are present, prefer LOCK_RECLAIM > RECLAIM_CRASH > RECLAIM_STATUS. Parse the signal-specific fields and execute the Crash Recovery Procedure (see crash-recovery.md) with signal_type and the parsed fields.
Signal field formats:
LOCK_RECLAIM:<prev_hostname>|<prev_locked_at>|<current_hostname> — multi-PC reclaim (cross-host).
RECLAIM_CRASH:<prev_locked_at>|<prev_hostname>|<prev_pid> — same-host crash (PID anchor is dead). Common case after a tmux/host-shell crash.
RECLAIM_STATUS:<prev_status>|<prev_assigned_to> — anomaly fallback (lock missing or pre-PID-anchor lock).
When the procedure returns:
reclaim → ownership is held here (OWNED: confirms). Continue to the Re-entry Routing gate at the end of Step 4 (it checks resume_point); if no resume applies, proceed to Step 5 normally.
decline → return to the calling skill's task selection. Do NOT proceed. (The procedure has already released the lock and reverted the task to Ready.)
-
LOCK_FAILED:<owner>|<locked_at>|<hostname> — Task is locked by another user/PC. Parse the |-separated fields for lock details. Use AskUserQuestion:
-
LOCK_ERROR:<message> — Lock system error (fetch failure, race exhaustion, etc.). Display the error and suggest running ./.aitask-scripts/aitask_lock_diag.sh for troubleshooting. Use AskUserQuestion:
- Question: "Lock system error: <message>. How to proceed?"
- Header: "Lock error"
- Options:
- "Retry" (description: "Try acquiring the lock again")
- "Continue without lock" (description: "Proceed without locking (risky if multiple users)")
- "Abort" (description: "Stop the workflow")
- If "Retry": Re-run
aitask_pick_own.sh (same command). Parse output again.
- If "Continue without lock": Skip lock acquisition, proceed to Step 5 (task status will be updated but no lock held).
- If "Abort": End the workflow.
-
LOCK_INFRA_MISSING — Lock infrastructure not initialized. Inform user to run ait setup and abort.
Note: The script handles email storage, lock acquisition, task metadata update (status → Implementing, assigned_to), and git add/commit/push internally. If the script fails entirely (non-zero exit without structured output), display the error and abort.
-
Materialize the active-gates tuple (ALWAYS runs — never profile-omitted): With ownership held, derive and persist the task's enforced gate set under the current profile. If active_profile_filename is set, run:
./.aitask-scripts/aitask_gate.sh materialize-active <task_num> --profile aitasks/metadata/profiles/<active_profile_filename>
Parse the single stdout line:
MATERIALIZED:<csv> — active set persisted and committed. MATERIALIZED:(empty) means a fully profile-filtered (or ungated) task — that persisted empty set is exactly what makes a declared-but-unrendered gate invisible to every enforcer. Continue.
MATERIALIZED_UNCOMMITTED:<csv> — the tuple was written and is enforced locally, but the path-scoped git commit failed (e.g. an index lock). Warn: "active-gates tuple written but not committed — other checkouts won't see it until the task data is committed." and continue; a later ./ait git commit of aitasks/ picks it up.
NOOP:unchanged — re-pick under the same profile with unchanged inputs; nothing rewritten (any previously pending commit of the task file was verified/repaired). Continue.
NOOP_UNCOMMITTED:pending-persist — the tuple is unchanged and enforced locally, but the task file still carries changes git refused to commit. Warn as for MATERIALIZED_UNCOMMITTED and continue.
- Nonzero exit — the re-derivation failed (unreadable/invalid profile, compute backend unavailable). The helper clears any previously persisted tuple (its stderr says whether the clear succeeded), but the raw-
gates: fallback is only the task's declared intent — it does NOT include this profile's default_gates, so continuing could silently under-enforce the current profile. ABORT the pick: display "active-gates materialization failed (<output>) — fix the profile / compute backend and re-pick t<task_num>.", then execute the Task Abort Procedure (see task-abort.md). Do NOT proceed to Step 5.
If active_profile_filename is NOT set (a manual/resume invocation without a profile), skip the call. This is the claim-time-snapshot governance model: with no profile in scope there is nothing to re-derive against, so the previously persisted tuple (when present and intact) remains the enforced snapshot, and a task never materialized follows its raw gates: field. (./.aitask-scripts/aitask_gate.sh active-gates-status <task_num> --profile <file> shows the stored tuple + freshness when a profile is in scope.)
This claim-time materialization replaces the former Step-7 gates: backfill: raw gates: stays the task's declared intent, and the persisted active_gates tuple is the enforced set that planning, the Step-9 orchestrator, and the archival guard all read. Re-running on every (re-)pick re-derives the set under the CURRENT profile, so a profile switch can never leave stale enforcement. (Gate CLI verb shapes — decision vs action vs introspection — are documented once in gate-cli.md.)
-
Store previous status for potential abort (remember the previous_status from context)
-
Re-entry Routing gate: After ownership is held (via any success path above — OWNED, FORCE_UNLOCKED + OWNED, or crash-recovery → reclaim), check the resume_point context variable set by Step 3 Check 5. If it is IMPLEMENT or POSTIMPL, follow the Re-entry Routing procedure below instead of proceeding to Step 5 → Step 6. Otherwise (unset, or PLAN), proceed to Step 5 normally. (The routing is gated on resume_point, not on which ownership path was taken — a force-unlock takeover of an in-flight task returns plain OWNED with no reclaim signal, and its resume must not be lost.)
Re-entry Routing
Runs only when resume_point (from Step 3 Check 5) is IMPLEMENT or POSTIMPL and Step 4 left ownership held. It resumes the in-flight task from the first unmet checkpoint instead of restarting at planning. Profile-invariant.
-
Plan-existence guard: Run ./.aitask-scripts/aitask_query_files.sh plan-file <taskid>. If NOT_FOUND (a checkpoint was recorded but no plan was externalized — e.g. a failed externalization), discard the resume: clear resume_point and fall back to the normal flow (Step 5 → Step 6, re-plan). If PLAN_FILE:<path>, read the plan and continue below.
-
Environment setup (Step 5) with reuse: If a worktree for <task_name> already exists — git worktree list --porcelain shows a branch refs/heads/aitask/<task_name> line — reuse it (work in that directory); do NOT recreate the branch/worktree. Otherwise run Step 5 as normal. For current-branch profiles (no worktree), Step 5 is a no-op and you work on the current branch.
-
Route by resume_point:
IMPLEMENT → resume at Step 7's "Follow the approved plan" implementation body. Re-run only the Pre-implementation ownership guard and the Agent Attribution Procedure (both idempotent; attribution re-records the resuming agent), then go straight to implementation. Skip Step 7's post-approval one-time gates:
- Cross-Repo Child Assignment and the risk-mitigation pre-task creation (the post-approval "before" follow-ups) — these are non-idempotent task creators that end the workflow when they fire, so a task that is still a normal
Implementing single task is necessarily past them; re-running would double-create.
- The
plan_approved / risk_evaluated gate re-recordings and the risk-level field write — already done in the original session; re-running only adds redundant commits.
POSTIMPL → resume at Step 9 (Post-Implementation), skipping Steps 6–8 (the code is already committed and review_approved was recorded after the Step 8 commit). Step 9 is safe to re-enter: its merge approval is NON-SKIPPABLE (re-asked), a re-merge of an already-merged branch is a git no-op, and archival just moves and commits the task file. For child tasks, the "verify plan completeness before archival" sub-step backstops the Final Implementation Notes.
Step 5: Environment and Branch Setup
Note: For fully autonomous remote workflows (Claude Code Web), use the aitask-pickrem skill instead — it skips all environment setup and always works on the current branch.
If Yes:
-
Extract <task_name> from the filename
- For parent:
t16_implement_channel_settings from t16_implement_channel_settings.md
- For child:
t16_2_add_login from t16_2_add_login.md
-
Profile check: If the active profile has base_branch set:
- Use the specified branch name. Display: "Profile '<name>': using base branch <branch>"
- Skip the AskUserQuestion below
Otherwise, ask which branch to base the new branch on using AskUserQuestion:
- "Which branch should the new task branch be based on?"
- Options: "main (Recommended)" / "Other branch"
- If "Other branch", ask user to specify the branch name
-
Create worktree directory:
mkdir -p aiwork
-
Create both the branch and worktree in a single command:
git worktree add -b aitask/<task_name> aiwork/<task_name> <base-branch>
Where <base-branch> is main or the user-specified branch.
-
Work in the aiwork/<task_name>/ directory for implementation
If No:
- Work directly on the current branch in the current directory
Step 6: Create Implementation Plan
Full planning workflow: Read planning.md for the complete Step 6 procedure including:
- 6.0: Check for Existing Plan (profile-aware)
- 6.1: Planning (EnterPlanMode, child tasks, complexity assessment)
- Child Task Documentation Requirements
- Save Plan to External File (naming conventions, metadata headers)
- Checkpoint (post-plan action)
After the checkpoint in planning.md:
- If child tasks were created and the child checkpoint returned "Stop here" → collect Satisfaction Feedback Procedure (see
satisfaction-feedback.md) with skill_name from context variables, then END the workflow (do NOT proceed to Step 7/8/9)
- If child tasks were created and the child checkpoint returned "Start first child" → restart with
/aitask-pick <parent>_1 (do NOT proceed to Step 7)
- Otherwise (normal single-task plan) → proceed to Step 7
Step 7: Implement
Pre-implementation ownership guard:
Before starting implementation, verify that ownership/lock was acquired (Step 4 should have done this, but this guard catches edge cases like plan mode deferral):
- Read the task file's frontmatter
status and assigned_to fields
- Resolve the current user's email: use the email from Step 4 if available, otherwise read from
aitasks/metadata/userconfig.yaml
- If status is
Implementing AND assigned_to matches the current user's email: Ownership appears to have been acquired in Step 4 — but verify the lock is held on this host before assuming so. Run ./.aitask-scripts/aitask_lock.sh --check <task_id> and parse the hostname: line from the output. Compare against hostname (the running shell's hostname).
- If the hostname matches or
--check shows no lock at all (single-user / no-remote mode): ownership is confirmed for this host. Proceed normally.
- If the hostname differs (a different machine holds the lock under your email): a multi-PC reclaim has been detected by the guard. Execute the Crash Recovery Procedure (see
crash-recovery.md) with signal_type=LOCK_RECLAIM, parsing prev_hostname from --check output and using the current hostname as current_hostname. If the procedure returns reclaim, run ./.aitask-scripts/aitask_pick_own.sh <task_num> --email "<email>" to refresh the lock to this host before proceeding. If decline, return to the calling skill's task selection. (Same-host crash recovery is moot here: by the time Step 7 fires, Step 4's aitask_pick_own.sh already owned the lock and surfaced any RECLAIM_CRASH: signal.)
- Otherwise (status is not
Implementing, or assigned_to is empty/missing, or assigned_to does not match the current user's email): Ownership was not properly acquired. Display: "Guard: task ownership not confirmed — acquiring ownership now."
Record implementing agent: Execute the Agent Attribution Procedure (see agent-attribution.md) to record which code agent and model is implementing this task.
Repository structure awareness: Before starting implementation, read repo-structure.md
Cross-repo child assignment (post-approval creation): If cross_repo_planned is true (set in planning.md §6.1 — the approved plan is a cross-repo paired design), execute the Cross-Repo Child Assignment Procedure (see cross-repo-child-assignment.md) now. It creates the cross-repo parent first, then assigns all children (local + cross-repo) to their parents with their plans, demotes the local parent to a parent-of-children, and presents its own child checkpoint. When it returns, the workflow has ended (via that checkpoint's "Start first child" / "Stop here") — do NOT continue with the normal single-task implementation below or proceed to Step 8. (This is the post-approval creation gate: planning runs in read-only plan mode, so no tasks were created during Step 6.)
Risk fields (post-approval write): If the approved plan contains a ## Risk section (authored by the Risk Evaluation Procedure during planning), write the two decided levels to the task's frontmatter now:
./.aitask-scripts/aitask_update.sh --batch <task_id> \
--risk-code-health <risk_level_code_health> \
--risk-goal-achievement <risk_level_goal_achievement>
Skip silently if the plan has no ## Risk section (e.g. the task is not risk-gated). This is the post-approval write gate: planning runs in read-only plan mode, so the fields are not written during Step 6.
Follow the approved plan, working in the directory specified in the plan metadata.
Update the external plan file as you progress:
- Mark steps as completed
- Note any deviations or changes from the original plan
- Record issues encountered during implementation
IMPORTANT: Do NOT commit changes automatically after implementation. Proceed to Step 8 for user review and approval.
Note: When committing implementation changes (in Step 8), the commit message must follow the <issue_type>: <description> (t<task_id>) format. See Step 8 for details.
Step 8: User Review and Approval
⚠️ NON-SKIPPABLE — Auto mode and execution profiles do NOT bypass this review.
The AskUserQuestion below is load-bearing infrastructure, not a routine
confirmation. Auto mode's "minimize interruptions / prefer assumptions for
routine decisions" guidance and execution-profile shortcuts
(skip_task_confirmation, post_plan_action, etc.) target other prompts in
the flow — they do NOT cover this review. The only valid skips are profile
keys explicitly named in this SKILL.md as covering Step 8 review (currently:
none). Skipping this prompt removes the user's last chance to test the change
before it lands in git.
Explicit acceptance required — every iteration. When the user picks
"Need more changes", the loop returns to the top of Step 8: after applying
the requested changes, the AskUserQuestion review prompt MUST be re-issued.
Repeat for every iteration. The ONLY green light to commit is the user
explicitly selecting "Commit changes" with no accompanying notes, requests,
or open concerns. Tacit consent — silence, lack of objection, "looks fine
I guess", a comment that mentions any further change — is NOT acceptance;
keep iterating. There is no upper bound on iterations.
After implementation is complete, the user MUST be given the opportunity to review and test changes before any commits are made.
Step 8b: Upstream Defect Follow-up
Entered from Step 8 after the "Commit changes" branch has committed code and plan files. Offers the user a chance to spawn a standalone aitask for an upstream defect surfaced during diagnosis (when the failure was seeded by a separate, pre-existing bug elsewhere — a different script, helper, or module).
Execute the Upstream Defect Follow-up Procedure (see upstream-followup.md) with:
task_file, task_id, is_child, active_profile, parent_id from the current context.
task_slug — filename stem with the t<id>_ prefix stripped (e.g. aitasks/t42_add_login.md → add_login).
When the procedure returns, proceed to Step 8c.
Step 8c: Manual Verification Follow-up
Entered from Step 8b (or directly from Step 8 if 8b was a no-op). At this point code and plan files have already been committed. Offers the user a chance to queue a standalone manual-verification task that will be picked after this task archives.
Execute the Manual Verification Follow-up Procedure (see manual-verification-followup.md) with:
task_file, task_id, is_child, active_profile, parent_id from the current context.
task_slug — filename stem with the t<id>_ prefix stripped (e.g. aitasks/t42_add_login.md → add_login).
When the procedure returns, proceed to Step 9.
Step 9: Post-Implementation
Execute the post-implementation cleanup steps.
If a separate branch was created:
⚠️ NON-SKIPPABLE — Auto mode and execution profiles do NOT bypass this merge approval.
The AskUserQuestion below is a workflow gate, not a routine confirmation. The
following do NOT cover this prompt:
- Execution profiles (no profile key currently bypasses Step 9 merge approval).
- Auto mode / 'work without stopping' system-injected directives.
- Generic user instructions to 'be brief' or 'don't ask'.
The only valid skips are profile keys explicitly named in this SKILL.md as
covering Step 9 merge approval (currently: none) or the user explicitly
authorizing the merge in chat before the prompt fires.
IMPORTANT: Use AskUserQuestion to ask: "Proceed with merge of code changes to main branch?" with options "Yes, proceed with merge" / "No, not yet". Do NOT proceed until the user approves.
-
Check for uncommitted changes:
git status --porcelain
-
Merge branch into main:
git checkout main
git merge aitask/<task_name>
-
Handle merge conflicts: Ask user for guidance if needed.
-
Verify implementation (build / tests / lint):
Do not re-derive which gates this task declares — the gate orchestrator
owns that decision and reports it. Dispatch it once, capturing both its output
and exit status, then branch on the result:
gates_out="$(./ait gates run <task_id> 2>&1)"; gates_rc=$?
-
If gates_rc is nonzero — an infrastructure failure (ait/wrapper
error, task resolution failed, Python unavailable, bad registry path, usage
error). ait gates run exits 0 for every normal gate outcome (a fail/error
is a recorded result, not a process error), so a nonzero exit is never an
ordinary gate failure. STOP and diagnose using gates_out; do NOT fall
through to either branch below (the declared-vs-not decision is only meaningful
on a clean exit).
-
Else if gates_out contains the line No gates declared; nothing to do. —
the task has not opted into the gate system (the common case today). Run the
legacy inline build verification:
- Read
aitasks/metadata/project_config.yaml and check the verify_build field
- If
verify_build is absent, null, or empty (or file doesn't exist): Display "No verify_build configured — skipping build verification." and skip this step.
- If
verify_build is a single command string: Run it.
- If
verify_build is a list of commands: Run each sequentially (stop on first failure).
- If the build fails:
- Analyze the error output and compare against the changes introduced by this task (
git diff against the base)
- If the failure is caused by this task's changes: Go back to the implementation to fix the build errors. After fixing, re-run the build command(s). Repeat until the build passes.
- If the failure is NOT related to this task's changes (pre-existing issue, environment problem, etc.): Log the build failure details in the plan file's "Final Implementation Notes" section under a "Build verification" entry and proceed with the workflow. Do not attempt to fix pre-existing issues.
-
Otherwise — the orchestrator ran the task's declared gates and recorded
each run itself. Read its per-gate report lines ( <gate>: <status> …) and
act per status:
pass / skip — satisfied (skip = not applicable, e.g. no command configured); continue.
fail — an ordinary gate failure: inspect ./ait gate log <task_id> <gate> and diff against the base. If caused by this task, fix and re-run ./ait gates run <task_id>; repeat until it passes. If pre-existing/unrelated, record ./ait gate fail <task_id> <gate> --reason "…" and log it in the plan's Final Implementation Notes.
error ( <gate>: error …) or a malformed-correction line ( ⚠ <gate>: malformed …) — a verifier infrastructure failure (launch failure, timeout, exit 3, or a status that contradicted the exit code), NOT an ordinary gate result. Diagnose the verifier/config (its log, the command, the timeout); do not "fix the code" as if it were a fail, do not record a manual pass, and do not proceed to archival until the verifier itself runs cleanly.
blocked: … — an unlocked gate could not run / none remain runnable. blocked: exhausted … or blocked: upstream … not satisfied means the gate is unsatisfied — surface and diagnose; do not treat it as satisfied or proceed. blocked: pending human signal → route to the human sign-off action (never self-signal).
pending (human gate) — surface to the user; never self-signal.
- Do NOT also run the manual "Record build-verified gate" step in this branch — the orchestrator already appended each gate's run (no double-record).
-
Clean up branch and worktree:
git worktree remove aiwork/<task_name>
rm -rf aiwork/<task_name>
git branch -d aitask/<task_name>
For child tasks — verify plan completeness before archival:
- Read the plan file from
aiplans/p<parent>/<child_plan>
- Verify it contains a "Final Implementation Notes" section with comprehensive details
- If missing or incomplete, add/update it now — the archived plan will serve as the primary reference for subsequent sibling tasks
- Ensure the notes include: actual work done, issues encountered and resolutions, and any information useful for sibling tasks
Run the archive script:
All archival operations (metadata updates, file moves, lock releases, folded task cleanup, git staging, and commit) are handled by a single script call:
For parent tasks:
./.aitask-scripts/aitask_archive.sh <task_num>
For child tasks:
./.aitask-scripts/aitask_archive.sh <parent>_<child>
The script automatically handles:
- Updating task metadata (status → Done, updated_at, completed_at)
- Creating archive directories and moving task/plan files
- For child tasks: removing child from parent's children_to_implement
- For child tasks: archiving parent too if all children are complete
- Releasing task locks (and parent locks if parent was also archived)
- For parent tasks: deleting folded tasks (if any, where status is not Implementing/Done)
- Git staging and committing all changes
Gate guard — pending declared gates (handle BEFORE parsing the success output):
The archive script refuses to archive a task whose declared gates: are not all
pass: it exits non-zero (code 2) and prints a GATE_PENDING:<csv> line (plus a
GATE_BLOCKED line). If you see that, the archival did not happen — do NOT
treat it as success or run the parse step below. (A task with no declared gates,
or all gates passing, exits 0 and archives straight through — the common case
today.) Instead use AskUserQuestion:
-
Question: "Task t<task_id> can't archive yet — pending gate(s): . How would you like to proceed?"
-
Header: "Gates"
-
Options:
- "Resolve now & archive" (description: "Satisfy the pending gate(s) in this session, then archive immediately")
- "Defer — keep in-flight" (description: "Leave the task active and re-enterable; archive on a later pick")
-
If "Resolve now & archive": For each pending gate the user can satisfy now
(run docs/tests, perform the review, etc.), record the pass:
./.aitask-scripts/aitask_gate.sh append <task_id> <gate> pass [k=v ...]
For a procedure-backed gate (kind: procedure — e.g. docs_updated; check
./.aitask-scripts/aitask_gate.sh procedure-gates <task_id>), do not hand-append
a pass — dispatch its skill instead (as in Step 8): begin-procedure <task_id> <gate> → Read-and-follow the gate's aitask-gate-<name> skill in your agent's
skill tree, which records the terminal pass/skip/fail itself.
After each, re-check ./.aitask-scripts/aitask_gate.sh archive-ready <task_id>.
The moment it prints ALL_PASS, re-run the archive script (same command as
above) and continue with the normal success-path parsing below — no re-pick
needed (the archival commit persists the recorded gate runs). If a gate
genuinely cannot be satisfied in this session (e.g. an async reviewer you do
not control), fall through to "Defer".
-
If "Defer — keep in-flight": Do not archive. The task stays active
(Implementing) and re-enterable — its committed code and recorded gate runs
are the resume state, and the task lock is intentionally left held. Inform the
user: "Archival deferred — t<task_id> stays in-flight. Re-pick it with
/aitask-pick <task_id> once the pending gate(s) pass." Skip the
push-after-archival step and proceed directly to Step 9b (Satisfaction
Feedback) — the implementation work was completed.
Parse the script output and handle interactive follow-ups:
The script outputs structured lines. Parse each line and handle accordingly:
ISSUE:<task_num>:<issue_url> — Execute the Issue Update Procedure (see issue-update.md) for the task
RELATED_ISSUE:<task_num>:<issue_url> — A related/merged issue. Execute the Related Issue Update Procedure (see issue-update.md, "Related Issues" section) using --issue-url
PARENT_ISSUE:<task_num>:<issue_url> — Execute the Issue Update Procedure (see issue-update.md) for the parent task
PARENT_RELATED_ISSUE:<task_num>:<issue_url> — A related/merged issue on the parent. Execute the Related Issue Update Procedure (see issue-update.md, "Related Issues" section) using --issue-url
FOLDED_RELATED_ISSUE:<folded_task_num>:<issue_url> — A related issue on a folded task (file deleted). Handle identically to FOLDED_ISSUE: below (same AskUserQuestion, same --issue-url commands, same task_id note)
FOLDED_ISSUE:<folded_task_num>:<issue_url> — The folded task's file has been deleted, so the standard Issue Update Procedure cannot be used (it requires the task file). Instead, handle inline:
- Use
AskUserQuestion:
- Question: "Folded task t<folded_task_num> had a linked issue: <issue_url>. Update/close it?"
- Header: "Issue"
- Options:
- "Close with notes" (description: "Post implementation notes from primary task and close")
- "Comment only" (description: "Post implementation notes but leave open")
- "Close silently" (description: "Close without posting a comment")
- "Skip" (description: "Don't touch the issue")
- If "Close with notes":
./.aitask-scripts/aitask_issue_update.sh --issue-url "<issue_url>" --close <task_id>
- If "Comment only":
./.aitask-scripts/aitask_issue_update.sh --issue-url "<issue_url>" <task_id>
- If "Close silently":
./.aitask-scripts/aitask_issue_update.sh --issue-url "<issue_url>" --close --no-comment <task_id>
- If "Skip": do nothing
- Note: Uses the primary
task_id (not folded_task_num) so the comment references the primary task's commits and plan file
PR:<task_num>:<pr_url> — Execute the PR Close/Decline Procedure (see pr-close-decline.md) for the task
PARENT_PR:<task_num>:<pr_url> — Execute the PR Close/Decline Procedure (see pr-close-decline.md) for the parent task
FOLDED_PR:<folded_task_num>:<pr_url> — The folded task's file has been deleted, so the standard PR Close/Decline Procedure cannot be used. Instead, handle inline:
- Use
AskUserQuestion:
- Question: "Folded task t<folded_task_num> had a linked PR: <pr_url>. Close/decline it?"
- Header: "PR"
- Options:
- "Close with notes" (description: "Post implementation notes from primary task and close/decline")
- "Comment only" (description: "Post implementation notes but leave open")
- "Close silently" (description: "Close/decline without posting a comment")
- "Skip" (description: "Don't touch the PR")
- If "Close with notes":
./.aitask-scripts/aitask_pr_close.sh --pr-url "<pr_url>" --close <task_id>
- If "Comment only":
./.aitask-scripts/aitask_pr_close.sh --pr-url "<pr_url>" <task_id>
- If "Close silently":
./.aitask-scripts/aitask_pr_close.sh --pr-url "<pr_url>" --close --no-comment <task_id>
- If "Skip": do nothing
- Note: Uses the primary
task_id (not folded_task_num) so the comment references the primary task's commits and plan file
FOLDED_WARNING:<task_num>:<status> — Warn the user: "Folded task t has status '' — skipping automatic deletion. Please handle it manually."
PARENT_ARCHIVED:<path> — Inform user: "All child tasks complete! Parent task also archived."
COMMITTED:<hash> — Archival commit was created
Push after archival:
./ait git push
Step 9b: Satisfaction Feedback
Execute the Satisfaction Feedback Procedure (see satisfaction-feedback.md) with skill_name and detected_agent_string from the context variables.
Procedures
The following procedures are in individual files — read on demand when referenced:
- Task Abort Procedure (
task-abort.md) — Lock release, status revert, worktree cleanup. Referenced from Step 6 checkpoint and Step 8.
- Issue Update Procedure (
issue-update.md) — Update/close linked issues during archival. Referenced from Step 9.
- PR Close/Decline Procedure (
pr-close-decline.md) — Close/decline linked pull requests during archival. Referenced from Step 9.
- Contributor Attribution Procedure (
contributor-attribution.md) — Credit PR contributors in commit messages. Referenced from Step 8.
- Code-Agent Commit Attribution Procedure (
code-agent-commit-attribution.md) — Resolve code-agent Co-Authored-By trailer. Referenced from Step 8.
- Plan Externalization Procedure (
plan-externalization.md) — Claude Code only. Copy the approved internal plan file to aiplans/ and parse externalize helper output. Referenced from planning.md (Step 6) and Step 8.
- Model Self-Detection Sub-Procedure (
model-self-detection.md) — Detect the current code agent and model. Referenced from Agent Attribution and Satisfaction Feedback.
- Agent Attribution Procedure (
agent-attribution.md) — Record implementing code agent and model. Referenced from Step 7.
- Satisfaction Feedback Procedure (
satisfaction-feedback.md) — Collect user feedback and update verified model scores. Referenced from Step 9b and standalone skills.
- Lock Release Procedure (
lock-release.md) — Release task locks. Referenced from Task Abort Procedure.
- Crash Recovery Procedure (
crash-recovery.md) — Surveys in-progress work and prompts the user when a reclaim signal is detected (multi-PC, same-host crash via PID anchor, or lock anomaly). Referenced from Step 4 dispatcher and Step 7 ownership guard.
- Manual Verification Procedure (
manual-verification.md) — Interactive checklist runner for issue_type: manual_verification tasks. Referenced from Step 3 (Check 3).
- Auto-Verification Procedure (
auto-verification.md) — Automated verification for manual_verification tasks. Supports two strategies: autonomous (execute inline, document at end) and pre-built (design plan, optionally approve, then execute). Persists to aiplans/p<id>_manual_verification_auto.md. Referenced from manual-verification.md Step 1.5 (whole checklist) and Step 2 (per-item auto verb).
- Manual Verification Follow-up Procedure (
manual-verification-followup.md) — Post-implementation prompt offering to create a standalone manual-verification task, with multi-source candidate discovery. Referenced from Step 8c.
- Upstream Defect Follow-up Procedure (
upstream-followup.md) — Post-implementation prompt offering to spawn a standalone bug aitask for an upstream defect surfaced during diagnosis. Reads the plan file's "Upstream defects identified" subsection. Referenced from Step 8b.
- Remote Drift Check Procedure (
remote-drift-check.md) — Warn before implementation if origin/<base-branch> is ahead of local, with strong emphasis on files the plan touches. Referenced from planning.md Checkpoint.
- Execution Profile Selection Procedure (
execution-profile-selection.md) — Interactive profile scan and selection. Referenced from Step 0a in calling skills.
- Execution Profile Selection Procedure — Auto-Select (
execution-profile-selection-auto.md) — Non-interactive auto-select for remote/web skills. Referenced from Step 1 in aitask-pickrem/aitask-pickweb.
- Batch Task Creation Procedure (
task-creation-batch.md) — Canonical command templates for creating tasks via aitask_create.sh --batch. Referenced from planning.md and multiple skills (explore, review, qa, wrap, pr-import, revert).
Notes
- When working on a child task, always include links to parent and sibling task files for context, plus archived sibling plan files as primary reference for completed siblings
- Archived sibling context priority: When gathering context for a child task, prefer archived plan files (
aiplans/archived/p<parent>/) over archived task files (aitasks/archived/t<parent>/). Plan files contain the full implementation record; task files are just initial proposals. Only use archived task files as fallback when no corresponding plan exists.
- Child tasks are archived to
aitasks/archived/t<parent>/ preserving the directory structure
- Child plans are archived to
aiplans/archived/p<parent>/ preserving the directory structure
- IMPORTANT: When modifying any task file, always update the
updated_at field in frontmatter to the current date/time using format YYYY-MM-DD HH:MM
- Child task naming: Use format
t{parent}_{child}_description.md where both parent and child identifiers are numbers only. Do not insert tasks "in-between" (e.g., no t10_1b between t10_1 and t10_2). If you discover a missing implementation step, add it as the next available number and adjust dependencies accordingly
- When archiving a task with an
issue field, the workflow offers to update/close the linked issue using aitask_issue_update.sh. The SKILL.md workflow is platform-agnostic; the script handles platform specifics (GitHub, GitLab, etc.). It auto-detects commits and includes "Final Implementation Notes" from the archived plan file.
- Folded tasks: When a task has a
folded_tasks frontmatter field (set by aitask-explore or aitask-fold), the listed tasks are deleted during Step 9 archival. Folded tasks have status Folded with a folded_into property pointing to the primary task. They are deleted (not archived) because their full content was incorporated into the primary task's description at creation/fold time.
- Note: Folded tasks are handled by
handle_folded_tasks() in both parent and child archival paths. /aitask-fold and manual folding can add folded_tasks to any task type.
- Symlinks and data worktree: When the project uses a separate
aitask-data branch, aitasks/ and aiplans/ are symlinks to .aitask-data/. See repo-structure.md for the full architecture and rules.
Project Configuration
Project-level settings are stored in aitasks/metadata/project_config.yaml (git-tracked, shared across team). This is separate from execution profiles (workflow behavior) and userconfig.yaml (per-user, gitignored).
| Key | Type | Default | Description | Used in |
|---|
verify_build | string or list | (none — skip) | Shell command(s) to verify the build after implementation | Step 9; build_verified gate |
test_command | string or list | (none — auto-detect) | Shell command(s) for running project tests | aitask-qa Step 4; tests_pass gate |
lint_command | string or list | (none — skip) | Shell command(s) for linting project code | aitask-qa Step 4; lint gate |
attachments_gc_grace | duration (30d/24h/90m/120s/int seconds) | 30d | Grace window before a fully-orphaned attachment (no active or archived task references it) is reclaimed by ait attach gc. Archiving never decrefs, so an archived task's attachments are kept indefinitely; this knob only governs blobs left unreferenced by ait attach rm or task deletion. | ait attach gc |
If the file does not exist or a field is absent, the corresponding feature is skipped.
Execution Profiles
Full reference: See profiles.md for the complete profile schema, available keys, and customization guide.
Profiles are YAML files in aitasks/metadata/profiles/ that pre-answer workflow questions. Default profiles: default (all questions asked) and fast (skip confirmations).