Atomically update task status across TODO.md and state.json. For standalone use only.
Direct execution skill for atomic status synchronization across TODO.md and state.json. This skill executes inline without spawning a subagent, avoiding memory issues.
Reference (do not load eagerly):
.claude/context/core/patterns/jq-escaping-workarounds.md - jq escaping patterns (Issue #1132)IMPORTANT: This skill is for STANDALONE USE ONLY.
Workflow skills (skill-researcher, skill-planner, skill-implementer, etc.) now handle their own preflight/postflight status updates inline. This eliminates the multi-skill halt boundary problem where Claude may pause between skill invocations.
Use this skill for:
Do NOT use this skill in workflow commands (/research, /plan, /implement, /revise) - those commands now invoke a single skill that handles its own status updates.
This skill activates when:
This skill exposes three primary operations:
| Operation | Purpose | When to Use |
|---|---|---|
preflight_update | Set in-progress status | GATE IN checkpoint |
postflight_update | Set final status + link artifacts | GATE OUT checkpoint |
artifact_link | Add single artifact link (idempotent) | Post-artifact creation |
Validate required inputs based on operation type:
For preflight_update:
task_number - Must be provided and exist in state.jsontarget_status - Must be an in-progress variant (researching, planning, implementing)session_id - Must be provided for traceabilityFor postflight_update:
task_number - Must be provided and existtarget_status - Must be a final variant (researched, planned, implemented, partial)artifacts - Array of {path, type} to linksession_id - Must be providedFor artifact_link:
task_number - Must be provided and existartifact_path - Relative path to artifactartifact_type - One of: research, plan, summaryRoute to appropriate operation and execute using Bash (jq) and Edit tools.
Purpose: Set in-progress status at GATE IN checkpoint
Execution:
task_data=$(jq -r --arg num "{task_number}" \
'.active_projects[] | select(.project_number == ($num | tonumber))' \
specs/state.json)
if [ -z "$task_data" ]; then
echo "Error: Task {task_number} not found"
exit 1
fi
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg status "{target_status}" \
'(.active_projects[] | select(.project_number == {task_number})) |= . + {
status: $status,
last_updated: $ts
}' specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
grep -n "^### {task_number}\." specs/TODO.md[OLD_STATUS] to [NEW_STATUS]Status Mapping:
| state.json | TODO.md |
|---|---|
| not_started | [NOT STARTED] |
| researching | [RESEARCHING] |
| planning | [PLANNING] |
| implementing | [IMPLEMENTING] |
Return: JSON object with status "synced" and previous/new status fields.
Purpose: Set final status and link artifacts at GATE OUT checkpoint
Execution:
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg status "{target_status}" \
'(.active_projects[] | select(.project_number == {task_number})) |= . + {
status: $status,
last_updated: $ts
}' specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
IMPORTANT: Use two-step jq pattern to avoid Issue #1132 escaping bug. See jq-escaping-workarounds.md.
# Step 1: Update timestamp
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'(.active_projects[] | select(.project_number == {task_number})) |= . + {
last_updated: $ts
}' specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
# Step 2: Add artifact (append to array)
jq --arg path "{artifact_path}" \
--arg type "{artifact_type}" \
'(.active_projects[] | select(.project_number == {task_number})).artifacts += [{"path": $path, "type": $type}]' \
specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
Update TODO.md status marker:
[RESEARCHING] -> [RESEARCHED]Link artifacts in TODO.md:
Status Mapping:
| state.json | TODO.md |
|---|---|
| researched | [RESEARCHED] |
| planned | [PLANNED] |
| implemented | [IMPLEMENTED] |
| partial | [PARTIAL] |
Return: JSON object with target_status and artifacts_linked fields.
Purpose: Add single artifact link (idempotent)
Execution:
if grep -A 30 "^### {task_number}\." specs/TODO.md | grep -q "{artifact_path}"; then
echo "Link already exists"
# Return "skipped" status
fi
IMPORTANT: Use two-step jq pattern to avoid Issue #1132 escaping bug. See jq-escaping-workarounds.md.
# Step 1: Update timestamp
jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'(.active_projects[] | select(.project_number == {task_number})) |= . + {
last_updated: $ts
}' specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
# Step 2: Add artifact (append to array)
jq --arg path "{artifact_path}" \
--arg type "{artifact_type}" \
'(.active_projects[] | select(.project_number == {task_number})).artifacts += [{"path": $path, "type": $type}]' \
specs/state.json > /tmp/state.json && mv /tmp/state.json specs/state.json
| Type | Format in TODO.md |
|---|---|
| research | - **Research**: [research-{NNN}.md]({path}) |
| plan | - **Plan**: [implementation-{NNN}.md]({path}) |
| summary | - **Summary**: [implementation-summary-{DATE}.md]({path}) |
Insertion order:
Return: JSON object with status "linked" or "skipped".
{
"status": "synced",
"summary": "Updated task #{N} to [{STATUS}]",
"previous_status": "not_started",
"new_status": "researching"
}
{
"status": "{target_status}",
"summary": "Updated task #{N} to [{STATUS}] with {M} artifacts",
"artifacts_linked": ["path1", "path2"],
"previous_status": "researching",
"new_status": "researched"
}
{
"status": "linked|skipped",
"summary": "Linked artifact to task #{N}" | "Link already exists",
"artifact_path": "path/to/artifact.md",
"artifact_type": "research"
}
Return failed status with recommendation to verify task number.
Return failed status with current status and allowed transitions.
Return failed status with recommendation to check permissions.
If jq commands fail with INVALID_CHARACTER or syntax error (Issue #1132):
jq-escaping-workarounds.mdFor Workflow Commands: Do NOT use this skill directly. Workflow skills now handle their own status updates inline.
For Manual Operations: Use this skill for standalone status corrections:
### Manual Status Correction
Invoke skill-status-sync with:
- operation: preflight_update or postflight_update
- task_number: {N}
- target_status: {valid_status}
- session_id: manual_correction
- artifacts: [{path, type}, ...] (for postflight only)
This skill ensures: