aops-core/skills/cowork-sync/SKILL.md
Mirror PKB tasks onto the Cowork native task list at claim time and sync completion back to PKB. Cowork-only; ships only in the cowork build of aops-core.
npx skillsauth add nicsuzor/academicops cowork-syncInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Cowork forces every session to drive work through the harness-native task list (TaskCreate / TaskUpdate / TaskList / TaskGet). The PKB remains the system of record across sessions, but inside a Cowork session the agent's working surface is the native list. This skill defines how the two stay aligned.
/pull), mirror it onto the native list before any execution begins. Other claim paths (decompose-and-execute, supervisor tick) currently bypass this skill — see [[#scope]] below.TaskUpdate on the native id. The agent does not bulk-update PKB mid-task; the only PKB writes during execution are the per-completion echoes described in invariant 4.status="completed" (and that native task carries PKB <id> in its description), the same TaskUpdate call is paired with the matching PKB write — mcp__pkb__complete_task for a mirrored child, deferred to release_task in /end_session for the bound parent. This is the user-visible invariant: a child PKB row turns done the moment its native sibling is ticked off, not at session close.description (e.g. "PKB task-acba1234 — Implement X"). This is the join key for completion sync; without it the per-completion echo and the session-close reconciliation both no-op.The native list is allowed to be ahead of PKB momentarily (the half-second between TaskUpdate(status="completed") and the paired complete_task call). PKB is allowed to be ahead of the native list when work was done outside Cowork. Beyond that brief window, the two stay in lockstep.
This skill is invoked by /pull (claim path) and /end_session (final reconciliation). Other PKB-mutating skills inside a Cowork session — supervisor ticks, /q-style decompose-and-execute, ad-hoc agent calls that write directly to PKB — currently bypass the mirror. Their PKB writes will still take effect, but the native task list will not reflect them until the next /pull or TaskList refresh. If invariant 2 needs to broaden to cover those paths, the work is to add the same mirror call at the analogous claim point in each.
The Cowork harness presents TaskCreate / TaskUpdate / TaskList / TaskGet as deferred tools. Before the first call, load their schemas:
ToolSearch(query="select:TaskCreate,TaskUpdate,TaskList,TaskGet", max_results=4)
Skip this step if a previous call in the session already loaded them — the schemas persist for the session.
Called from /pull (Step 1.6 — Cowork only) and from any other skill that needs to surface a PKB task for execution.
Input: a PKB task id with body, AC, and optional children.
parent = mcp__pkb__get_task(id="<pkb-id>")
leaves = mcp__pkb__get_task_children(id="<pkb-id>") # if non-leaf
TaskCreate(
subject="<short title — first ~60 chars of PKB title>",
description="PKB <pkb-id> — <full title>\n\n<one-line goal + AC anchor>",
activeForm="<imperative present continuous — 'Implementing X'>"
)
Save the returned native task id (#N) — you will reference it via TaskUpdate for the rest of the session.addBlocks (or each child's addBlockedBy) so the parent only unblocks once children complete:
for child in leaves:
child_native = TaskCreate(
subject="<child title>",
description="PKB <child-id> — <child title>",
activeForm="<...>"
)
TaskUpdate(taskId=child_native, addBlocks=[parent_native_id])
If the PKB task is a leaf or you intend to handle AC inline in the parent body, skip this step.in_progress immediately to reflect that you have claimed it:
TaskUpdate(taskId=parent_native, status="in_progress")
/pull has already set PKB status to in_progress and bound the task to the session via the binding file (see [[../../commands/pull.md]] Step 1.4). The PKB mid-session writes stop there.Run this every time the agent flips a native task to completed. The rule is one paired write per completion — TaskUpdate(status="completed") immediately followed by the matching PKB write — so a future reader of the PKB graph sees the child task turn done at the same wall-clock moment the user sees the native checkbox flip.
TaskUpdate(taskId="<native-id>", status="completed")
# Read the native task to recover its PKB id from `description`
nt = TaskGet(taskId="<native-id>")
pkb_id = _parse_pkb_id(nt.description) # extracts the id from "PKB <id> — …"
if pkb_id is None:
return # ad-hoc native task, not mirrored from PKB
if pkb_id == "<bound-parent-pkb-id>":
return # defer to /end_session's release_task call
mcp__pkb__complete_task(id=pkb_id, summary="<one-line, derived from native subject>")
Edge cases:
complete_task rejects a row whose status is already done/cancelled/superseded/archived. Catch the error and continue; the desired state already holds./end_session will call release_task against the bound id with the full session payload (PR, branch, summary). Echoing now would race the closing call.addBlocks children. When the last child completes, the native parent will auto-transition out of blocked to whatever its prior state was. If the agent then ticks the parent to completed, the same paired-write rule applies — but typically the agent leaves the parent for /end_session rather than ticking it manually.deleted is the agent abandoning that working item; do NOT echo to PKB. PKB cancellation is a deliberate update_task(status="cancelled") decision and goes through /end_session or an explicit follow-up, not through native-list maintenance.Called from /end_session Step 0.5 (Cowork only), before release_task. This is a safety net for the rare case where per-completion sync was skipped — a TaskUpdate was made without the paired PKB write, the per-completion code path errored silently, or the session is closing mid-thought with native completions that never made it back.
Input: the bound PKB task id (resolved by /end_session from the binding file) and the current state of the native task list.
tasks = TaskList()
PKB <id> out of description. Native tasks with no PKB id in their description were created ad-hoc — leave them alone; they die with the session.status: completed AND a PKB id that is NOT the bound parent:
mcp__pkb__get_task(id="<pkb-child-id>") first to check status.done/cancelled/superseded/archived, skip — per-completion sync already handled it.mcp__pkb__complete_task(id="<pkb-child-id>", summary="reconciled at session close"). This is the safety net firing.in_progress or pending: do nothing. The supervisor or next /pull will pick them up; their PKB rows are still in the right state./end_session's release_task call, regardless of its native state. Do not echo it here./pull invoked twice in the same session/pull overwrites the binding file with the new task id (most-recent-claim-wins). The previously-mirrored native task is left in place. The agent SHOULD mark it cancelled before the new mirror call so the native list does not accumulate stale in_progress shells across re-pulls:
# Before mirroring the new claim, find and retire the prior native parent
tasks = TaskList()
for t in tasks:
if t.status == "in_progress" and "PKB " in t.description and t.description.startswith("PKB <previous-pkb-id>"):
TaskUpdate(taskId=t.id, status="deleted") # native-only retirement; do NOT echo to PKB
The PKB row for the previous claim was already set back to queued (or wherever) by whatever logic released the prior task — that is not this skill's concern. Cowork-sync is responsible only for keeping the native list in sync; PKB state-machine transitions for re-pulls live in /pull / release_task.
| Symptom | Cause | Recovery |
| ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| TaskCreate errors with "tool not loaded" | ToolSearch bootstrap was skipped | Run the ToolSearch(select:TaskCreate,…) call from Bootstrap and retry. |
| Native task has no PKB <id> in description | Mirror was bypassed, or the agent created a free-standing checklist item | Treat as session-local working note. Do not echo to PKB at completion or at close. |
| Native task is completed but PKB row is already done | Per-completion sync already ran, or task was completed externally between writes | No-op. complete_task on a terminal PKB row is rejected; catch the error and continue. |
| Native task flipped to completed but the paired PKB write errored | Server transient, MCP disconnect mid-call, or PKB row in an unexpected state | The session-close reconciliation in /end_session Step 0.5 catches the drift and retries. Don't re-flip the native task to recover; the safety net is the right repair surface. |
| Native parent is in_progress at session close but child tasks are completed | Children done, parent body still has unmet AC | Normal partial-completion case. Children were already echoed to PKB at completion; /end_session calls release_task on the parent with status="merge_ready" or "blocked" per its usual logic. |
| PKB has subtasks that were never mirrored (mirror happened before a later decompose_task) | Mirror is one-shot at claim, not continuous | Acceptable. Newly-created PKB subtasks land in queued and will be picked up by the next /pull. Do not retroactively mirror. |
| Native task cancelled (deleted) instead of completed | Agent retired a working item without completing it | Do NOT echo to PKB. PKB cancellation is a separate decision that goes through /end_session or an explicit update_task(status="cancelled") — never piggybacked on a native deletion. |
done the moment the agent ticks the native checkbox. Session-close batch sync was the earlier draft of this skill and is preserved as the safety net only; per-completion is the primary path.release_task payload (PR, branch, summary, follow-ups) that /end_session assembles. Echoing it mid-session would race the closing call and lose the structured payload, so the parent is deliberately deferred.TaskUpdate(status="in_progress"). Skills that don't mirror lose the affordance the harness gives for free.This skill ships ONLY in the cowork build of aops-core. The same source file is excluded by scripts/build.py for claude, gemini, and antigravity platforms — there is no native-list equivalent on those surfaces, and the PKB CLI / MCP suffices on its own.
tools
Program / portfolio supervision — the autonomous top loop above /supervisor. "Ready the release" → discover and decompose the constituent epics → run /supervisor on each → surface only escalations + merge-ready PRs. Stateless tick driven by /loop; all cross-tick state lives in the program task body.
testing
Instruction quality gate — reviews agent instructions (task bodies, workflow steps, skill procedures, self-test protocols) for shallow-execution vulnerabilities before deployment. Two modes: author (pre-hoc review) and audit (trace a failure back to the instruction gap). The bar is excellence, not compliance.
content-media
Design-stage fitness rubric — persona immersion, scenario design, dimensions that define what excellence looks like for the people a feature serves. Two modes — author (produce a rubric for a new spec) and critique (red-team an existing spec). Output lives on the spec, not in the verification brief. Owned by pauli.
tools
Analyze writing samples and create a comprehensive personal writing style guide