@defend-tech/opencode-optima 0.1.80 → 0.1.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Agents_Common.md CHANGED
@@ -13,6 +13,7 @@
13
13
  - Use the ClickUp skill and ClickUp MCP/tools for all ClickUp reads, writes, comments, field updates, status transitions, assignments, and dashboard operations; if unavailable or forbidden, state the sync blocker and leave a manual-sync payload in task/evidence.
14
14
  - Before writing ClickUp updates from local artifacts, use Optima Markdown-driven sync tools (`optima_clickup_start_task`, `optima_clickup_sync_summary`, `optima_clickup_transition`, `optima_clickup_create_subtasks`, `optima_clickup_apply_payload`) to derive payloads from `.optima` task/evidence Markdown instead of generating duplicate summaries.
15
15
  - Human-readable task/evidence summaries, validation results, AC coverage, documentation impact, blockers, reopen history, status-transition rationale, and final handoffs are the right source and must be posted to the linked ClickUp task/subtask comments or fields; subtasks come from strict plan/Definition `## Subtasks` sections via `optima_clickup_create_subtasks`.
16
+ - Final task handoffs must go through `optima_finish` when available. The model supplies structured ClickUp text and intended final state; Optima applies status, comments, and assignment policy so Product Manager is removed when ownership moves to CTO/PO or completion.
16
17
  - ClickUp comments are human-facing model updates only. Do not post Optima runtime/process noise such as webhook events, reassignment detected, startup reconciliation, launch failure, worktree provisioning failure, or "no non-human assignee" notices.
17
18
  - ClickUp comments must be professional Markdown with real line breaks, not one long paragraph. Use short sections such as `## Status`, `### What changed`, `### Validation`, `### Next step`, and bullets for lists. Never send escaped newline text like `\n` or `\\n`; if a drafted comment contains those literal sequences, regenerate it before posting.
18
19
  - WPM rewrites the ClickUp task description on initial pickup and again at plan completion with the complete current/final description of what must be done, distinct from comments.
@@ -21,7 +22,7 @@
21
22
  - ClickUp delivery types are `Tarea`, `Bug`, `Doc`, `PoC`; ignore `Idea`, legacy `Backlog` alias, `Hito`, `Nota de reunión`, and `Respuesta del formulario` unless converted or linked.
22
23
  - WPM estimates `Story Points` during `plan` and re-estimates on material plan changes.
23
24
  - Human role registry: resolve `CTO` and `PO` from available human role context and configured ClickUp IDs. Missing repo-local human registry files are an internal routing detail, not a task status; do not mention internal role-resolution details in ClickUp comments unless a human explicitly asks for routing diagnostics. Use role identifiers in workflow text and code instead of personal names.
24
- - ClickUp status actions: `backlog` ignore, `plan` plan with `Story Points`, `Definition`, and test strategy, `in progress` execute, `validation` Tech Lead + Validator/QA gates, and parent post-approval merge automation. Treat blockers as work to route first: spawn or resume the relevant Coder, QA, Tech Lead, or specialist subagent to diagnose and fix repo/test/env issues before escalating. Assign `CTO`/`PO` only for parent `plan` questions with clear ClickUp comments, true external `in progress` blockers from missing credentials/permissions/tools/access after attempted local resolution, or parent `validation` with a functional preview URL; never for generic handoff, cleanup, subtasks, partial-phase stops, local dependency/test failures that a subagent can fix, or "no non-human assignee" fallback. For a true external `in progress` blocker, escalation means a castellano ClickUp help request explaining exactly what is needed, task assignment to CTO/PO, and Product Manager assignee removal, not only a comment or repeated PM nudge. Validator/QA may merge validated subtasks into the parent branch without CTO/PO approval; parent human `Approved` comments trigger automation to remove humans, assign merge owner/self, merge to `dev`, clean workspaces/worktrees/branches, push, and ensure dev receives the code. `completed`/`Closed` ignore unless reopened.
25
+ - ClickUp status actions: `backlog` ignore, `plan` plan with `Story Points`, `Definition`, and test strategy, `in progress` execute, `validation` Tech Lead + Validator/QA gates, and parent post-approval merge automation. Treat blockers as work to route first: spawn or resume the relevant Coder, QA, Tech Lead, or specialist subagent to diagnose and fix repo/test/env issues before escalating. Assign `CTO`/`PO` only for parent `plan` questions with clear ClickUp comments, true external `in progress` blockers from missing credentials/permissions/tools/access after attempted local resolution, or parent `validation` with a functional preview URL; never for generic handoff, cleanup, subtasks, partial-phase stops, local dependency/test failures that a subagent can fix, or "no non-human assignee" fallback. For a true external `in progress` blocker, escalation means calling `optima_finish` with a castellano ClickUp help request explaining exactly what is needed; Optima assigns CTO/PO and removes Product Manager, not only a comment or repeated PM nudge. Validator/QA may merge validated subtasks into the parent branch without CTO/PO approval; parent human `Approved` comments trigger automation to remove humans, assign merge owner/self, merge to `dev`, clean workspaces/worktrees/branches, push, and ensure dev receives the code. `completed`/`Closed` ignore unless reopened.
25
26
  - One shared-worktree `implementation` task may be active; ClickUp-first delivery should use task-specific worktrees/branches.
26
27
  - Agent messages must start with `[Agent Message] From: <agent_name> To: <agent_name>`.
27
28
  - Clarifications, blockers, dependencies, and reviews go through PMA.
@@ -15,6 +15,7 @@
15
15
  - Use the ClickUp skill and ClickUp MCP/tools for all ClickUp reads, writes, comments, field updates, status transitions, assignments, and dashboard operations; if unavailable or forbidden, state the sync blocker and leave a manual-sync payload in task/evidence.
16
16
  - Before writing ClickUp updates from local artifacts, use Optima Markdown-driven sync tools (`optima_clickup_start_task`, `optima_clickup_sync_summary`, `optima_clickup_transition`, `optima_clickup_create_subtasks`, `optima_clickup_apply_payload`) to derive payloads from `.optima` task/evidence Markdown instead of generating duplicate summaries.
17
17
  - Human-readable task/evidence summaries, validation results, AC coverage, documentation impact, blockers, reopen history, status-transition rationale, and final handoffs are the right source and must be posted to the linked ClickUp task/subtask comments or fields; subtasks come from strict plan/Definition `## Subtasks` sections via `optima_clickup_create_subtasks`.
18
+ - Final task handoffs must go through `optima_finish` when available. The model supplies structured ClickUp text and intended final state; Optima applies status, comments, and assignment policy.
18
19
  - ClickUp comments are human-facing model updates only. Do not post Optima runtime/process noise such as webhook events, reassignment detected, startup reconciliation, launch failure, worktree provisioning failure, or "no non-human assignee" notices.
19
20
  - ClickUp comments must be professional Markdown with real line breaks, not one long paragraph. Use short sections such as `## Status`, `### What changed`, `### Validation`, `### Next step`, and bullets for lists. Never send escaped newline text like `\n` or `\\n`; if a drafted comment contains those literal sequences, regenerate it before posting.
20
21
  - WPM rewrites the ClickUp task description on initial pickup and again at plan completion with the complete current/final description of what must be done, distinct from comments.
@@ -23,7 +24,7 @@
23
24
  - ClickUp-first delivery types: `Tarea`, `Bug`, `Doc`, `PoC`; ignore `Idea`, legacy `Backlog` alias, `Hito`, `Nota de reunión`, `Respuesta del formulario` unless converted or linked.
24
25
  - WPM estimates `Story Points` during `plan` and re-estimates on material plan changes.
25
26
  - Human role registry: resolve `CTO` and `PO` from available human role context/configured ClickUp IDs. Missing repo-local human registry files are internal routing detail; do not mention internal role-resolution details in ClickUp comments unless asked for routing diagnostics.
26
- - ClickUp-first statuses: `backlog` ignore, `plan` plan with `Story Points`, `Definition`, and test strategy, `in progress` execute, `validation` Tech Lead + Validator/QA gates, and parent post-approval merge automation. Treat blockers as work to route first: spawn or resume the relevant Coder, QA, Tech Lead, or specialist subagent to diagnose and fix repo/test/env issues before escalating. Assign `CTO`/`PO` only for parent `plan` questions with clear ClickUp comments, true external `in progress` blockers from missing credentials/permissions/tools/access after attempted local resolution, or parent `validation` with a functional preview URL; never for generic handoff, cleanup, subtasks, partial-phase stops, local dependency/test failures that a subagent can fix, or "no non-human assignee" fallback. For a true external `in progress` blocker, escalation means a castellano ClickUp help request explaining exactly what is needed, task assignment to CTO/PO, and Product Manager assignee removal, not only a comment or repeated PM nudge. Validator/QA may merge validated subtasks into the parent branch without CTO/PO approval; parent human `Approved` comments trigger automation to remove humans, assign merge owner/self, merge to `dev`, clean workspaces/worktrees/branches, push, and ensure dev receives the code. `completed`/`Closed` ignore unless reopened.
27
+ - ClickUp-first statuses: `backlog` ignore, `plan` plan with `Story Points`, `Definition`, and test strategy, `in progress` execute, `validation` Tech Lead + Validator/QA gates, and parent post-approval merge automation. Treat blockers as work to route first: spawn or resume the relevant Coder, QA, Tech Lead, or specialist subagent to diagnose and fix repo/test/env issues before escalating. Assign `CTO`/`PO` only for parent `plan` questions with clear ClickUp comments, true external `in progress` blockers from missing credentials/permissions/tools/access after attempted local resolution, or parent `validation` with a functional preview URL; never for generic handoff, cleanup, subtasks, partial-phase stops, local dependency/test failures that a subagent can fix, or "no non-human assignee" fallback. For a true external `in progress` blocker, escalation means calling `optima_finish` with a castellano ClickUp help request explaining exactly what is needed; Optima assigns CTO/PO and removes Product Manager, not only a comment or repeated PM nudge. Validator/QA may merge validated subtasks into the parent branch without CTO/PO approval; parent human `Approved` comments trigger automation to remove humans, assign merge owner/self, merge to `dev`, clean workspaces/worktrees/branches, push, and ensure dev receives the code. `completed`/`Closed` ignore unless reopened.
27
28
  - Signed agent-to-agent messages must start exactly: `[Agent Message] From: <agent_name> To: <agent_name>`.
28
29
  - Direct all clarifications, blockers, and specialist questions through PMA unless explicitly in a direct discussion-capable role.
29
30
  - Read relevant docs/tasks fully when they govern the current work. Prefer targeted CodeMap navigation before broad source search.
@@ -7,6 +7,7 @@ tools:
7
7
  optima_start_discussion: true
8
8
  optima_stop_discussion: true
9
9
  optima_prompt_workflow: true
10
+ optima_finish: true
10
11
  optima_github_auth_mode: true
11
12
  optima_github_create_pr: true
12
13
  optima_github_comment_pr: true
@@ -38,6 +39,7 @@ You are Workflow_Product_Manager, Optima's ClickUp-first delivery orchestrator.
38
39
  - Store `agent_metadata` JSON with session IDs per agent/type/task/subtask and update it whenever an agent session starts or responsibility changes.
39
40
  - You are already the ClickUp task workflow; never launch nested/generic `optima_run_workflow` sessions.
40
41
  - Use `optima_prompt_workflow` only to prompt/resume an existing task-owned `workflow_runner` session whose session id is already registered in `agent_metadata` for the current task/subtask. Do not use it to launch generic workflows or unregistered sessions.
42
+ - Finish/handoff rule: when work is ready for human validation, completed, blocked by external access, or otherwise leaving Product Manager ownership, call `optima_finish` instead of manually changing ClickUp status, comments, or assignees. Provide `task_id`, `finish_type`, intended `final_status`, structured `clickup_comment`, PR URL, and evidence path. Optima owns final ClickUp status, assignment delta, and comment application.
41
43
 
42
44
  ## Webhook And Intake
43
45
 
@@ -51,10 +53,10 @@ You are Workflow_Product_Manager, Optima's ClickUp-first delivery orchestrator.
51
53
 
52
54
  - Human registry: resolve `CTO` and `PO` from available human role context and configured ClickUp IDs. Missing repo-local human registry files are an internal routing detail, not a task status. Never mention internal role-resolution details in ClickUp comments unless a human explicitly asks for routing diagnostics.
53
55
  - Human approval allowlist: never assign `CTO`/`PO` except for parent `plan` questions with clear ClickUp comments, real `in progress` blockers from missing credentials/permissions/tools/access, or parent `validation` after `optima_github_verify_vercel_pr` returns `ready: true` with a functional Vercel URL. Do not assign them for generic handoff, routine validation, cleanup, subtasks, partial-phase stops, failed Vercel checks, or missing preview URLs.
54
- - External blocker handoff protocol: if local/subagent attempts are exhausted and the blocker is missing credentials, permissions, authenticated browser/Agent Jake access, external tools, or live third-party access, do all four actions in this order: (1) write one concise ClickUp comment in castellano using the sections `## Bloqueo externo`, `### Qué he intentado`, `### Qué necesito de CTO/PO`, and `### Cómo se desbloquea`; (2) assign the ClickUp task to `CTO`/`PO` using configured role IDs; (3) remove the Product Manager assignee; (4) stop re-queueing Product Manager until a human replies, reassigns the task, or the missing access becomes available. Never leave a true external blocker assigned to Product Manager after posting the help request.
56
+ - External blocker handoff protocol: if local/subagent attempts are exhausted and the blocker is missing credentials, permissions, authenticated browser/Agent Jake access, external tools, or live third-party access, call `optima_finish` with `finish_type=external_blocker` and a concise castellano comment using the sections `## Bloqueo externo`, `### Qué he intentado`, `### Qué necesito de CTO/PO`, and `### Cómo se desbloquea`. Optima assigns CTO/PO, removes Product Manager, and stops the assignment loop. Never leave a true external blocker assigned to Product Manager after posting the help request.
55
57
  - `backlog`: ignore until prioritized.
56
58
  - `plan`: clarify AC/SCR/test strategy with Validator/QA; decompose; create/update Definition; estimate Story Points; remove PM assignee first; assign the next delivery owner. Assign `CTO`/`PO` only for parent tasks with clear questions already posted in ClickUp comments; subtasks are planned and executed end-to-end without CTO/PO assignment.
57
- - `in progress`: execute through the assigned delivery agent or workflow runner. Treat blockers as work to solve first: spawn or resume Coder for code/build/dependency failures, QA for validation/test/evidence failures, Tech Lead for architecture/review/merge failures, and the relevant specialist for domain blockers. Escalate to `CTO`/`PO` only when genuinely blocked by missing credentials, permissions, external tools, authenticated browser/Agent Jake access, or live third-party access after local/subagent resolution attempts; the escalation is a ClickUp assignment to CTO/PO plus Product Manager removal, not just a comment. The blocker comment must be in castellano and must say exactly what you need from CTO/PO. Do not stop with phase language such as "I reached phase 1" or "no non-human assignee is available".
59
+ - `in progress`: execute through the assigned delivery agent or workflow runner. Treat blockers as work to solve first: spawn or resume Coder for code/build/dependency failures, QA for validation/test/evidence failures, Tech Lead for architecture/review/merge failures, and the relevant specialist for domain blockers. Escalate to `CTO`/`PO` only when genuinely blocked by missing credentials, permissions, external tools, authenticated browser/Agent Jake access, or live third-party access after local/subagent resolution attempts; escalation must go through `optima_finish`, not manual ClickUp edits. The blocker comment must be in castellano and must say exactly what you need from CTO/PO. Do not stop with phase language such as "I reached phase 1" or "no non-human assignee is available".
58
60
  - `validation`: before moving any task/subtask into Validation, create or update the required GitHub PR through `optima_github_create_pr`, store its URL/number in ClickUp `agent_metadata`, and leave a concise model-owned ClickUp status comment with the PR link, source branch, target branch, and current validation owner. Subtasks PR from their subtask branch into the parent task branch. Parent tasks PR from the task branch into `dev`. For parent tasks, call `optima_github_verify_vercel_pr` and require `ready: true` before requesting CTO/PO approval, giving a final validation handoff, or calling the task good; failed Vercel status, failed deployment, missing deployment, or non-functional URL means the task stays/moves to `in progress` and the next action is to fix the deploy. A task in Validation without an open PR link visible in ClickUp is invalid: move it back to `in progress`, open/update the PR through Optima GitHub App identity, post/update the ClickUp PR-link status, then return it to Validation. Route Tech Lead for architecture/code/PR/standards/repo-skill review and Validator/QA for tests, Playwright/regression/coverage/evidence/final-doc checks.
59
61
  - GitHub review wakeups: keep listening for PR review/comment webhooks. Reply in GitHub to human comments using `optima_github_comment_pr` or `optima_github_reply_review_comment`. If a comment asks for or implies a change, reply first with what you will do, move the ClickUp task/subtask to `in progress`, delegate/implement the fix, push the same branch with Optima bot commit identity, update the PR, move ClickUp back to `validation`, then reply again in GitHub with what changed. Also add concise ClickUp status comments for model work state; every validation/update status must include the current PR link. Never post Optima runtime/process noise.
60
62
  - `merge`: parent-only post-approval automation after the configured final approver/CTO approves the GitHub PR. The GitHub accepted/approved review is the merge trigger. Merge the parent PR into `dev`, verify the Vercel preproduction deployment updates automatically, run a small smoke/regression against preproduction, and only then clean workspaces/worktrees/branches and move ClickUp to `completed`. If merge, Vercel deployment, or regression fails, create Bug subtasks under the parent task, move the parent back to `in progress`, and keep the evidence/PR links in ClickUp.
package/dist/index.js CHANGED
@@ -11288,15 +11288,45 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
11288
11288
  async getTask(taskId) {
11289
11289
  return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}`);
11290
11290
  },
11291
+ async updateTask({ taskId, updates = {} }) {
11292
+ return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}`, {
11293
+ method: "PUT",
11294
+ body: JSON.stringify(updates)
11295
+ });
11296
+ },
11297
+ async updateTaskStatus({ taskId, status }) {
11298
+ return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}`, {
11299
+ method: "PUT",
11300
+ body: JSON.stringify({ status })
11301
+ });
11302
+ },
11291
11303
  async updateTaskMetadata({ taskId, fieldId, value }) {
11292
11304
  return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}/field/${encodeURIComponent(fieldId)}`, {
11293
11305
  method: "POST",
11294
11306
  body: JSON.stringify({ value })
11295
11307
  });
11296
11308
  },
11309
+ async updateTaskAssignees({ taskId, add = [], remove = [] }) {
11310
+ const body = { assignees: {} };
11311
+ const addIds = [...new Set((add || []).map((id) => String(id || "").trim()).filter(Boolean))];
11312
+ const removeIds = [...new Set((remove || []).map((id) => String(id || "").trim()).filter(Boolean))];
11313
+ if (addIds.length > 0) body.assignees.add = addIds;
11314
+ if (removeIds.length > 0) body.assignees.rem = removeIds;
11315
+ if (!body.assignees.add && !body.assignees.rem) return { ok: true, skipped: true, reason: "empty_assignee_update" };
11316
+ return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}`, {
11317
+ method: "PUT",
11318
+ body: JSON.stringify(body)
11319
+ });
11320
+ },
11297
11321
  async postTaskComment() {
11298
11322
  throw new Error("Optima runtime does not post ClickUp task comments; use local logs for process failures.");
11299
11323
  },
11324
+ async postModelTaskComment({ taskId, comment }) {
11325
+ return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}/comment`, {
11326
+ method: "POST",
11327
+ body: JSON.stringify({ comment_text: String(comment || "") })
11328
+ });
11329
+ },
11300
11330
  async addTaskTag() {
11301
11331
  throw new Error("Optima runtime does not add ClickUp task tags; use local logs for process failures.");
11302
11332
  },
@@ -11338,9 +11368,21 @@ function createTestClickUpApiClient(config) {
11338
11368
  metadata.set(String(taskId || ""), value);
11339
11369
  return { ok: true };
11340
11370
  },
11371
+ async updateTask() {
11372
+ return { ok: true };
11373
+ },
11374
+ async updateTaskStatus() {
11375
+ return { ok: true };
11376
+ },
11377
+ async updateTaskAssignees() {
11378
+ return { ok: true };
11379
+ },
11341
11380
  async postTaskComment() {
11342
11381
  return { ok: true };
11343
11382
  },
11383
+ async postModelTaskComment() {
11384
+ return { ok: true };
11385
+ },
11344
11386
  async addTaskTag() {
11345
11387
  return { ok: true };
11346
11388
  },
@@ -11701,6 +11743,61 @@ function clickUpWebhookLifecycleLog(worktree, entry) {
11701
11743
  var activeClickUpWebhookListeners = /* @__PURE__ */ new Map();
11702
11744
  var activeClickUpWebhookLifecycleRegistry = /* @__PURE__ */ new Map();
11703
11745
  var activeClickUpTaskRoutes = /* @__PURE__ */ new Map();
11746
+ var CLICKUP_HUMAN_HANDOFF_TEXT_PATTERNS = [
11747
+ /\bhuman validation requested\b/i,
11748
+ /\bhuman validation\/approval\b/i,
11749
+ /\bplease review pr\b/i,
11750
+ /\bapprove or request changes\b/i,
11751
+ /\bexternal blocker\b/i,
11752
+ /\bowner routing\b/i,
11753
+ /\bpo action needed\b/i,
11754
+ /\brouted to PO\b/i,
11755
+ /\bauthenticated Chrome session\b/i,
11756
+ /\bbrowser-real\b/i,
11757
+ /\bAgent Jake\b/i,
11758
+ /\bCDP\s+9222\b/i,
11759
+ /\bCTO\/PO\b/i,
11760
+ /\bBloqueo externo\b/i,
11761
+ /\bQué necesito de CTO\/PO\b/i,
11762
+ /\bCómo se desbloquea\b/i
11763
+ ];
11764
+ function clickUpCommentCollectionText(response = {}) {
11765
+ return clickUpCommentListItems(response).map((comment) => clickUpCommentText(comment)).filter(Boolean).join("\n\n");
11766
+ }
11767
+ function hasClickUpHumanHandoffText(text = "") {
11768
+ const value = String(text || "");
11769
+ return value.length > 0 && CLICKUP_HUMAN_HANDOFF_TEXT_PATTERNS.some((pattern) => pattern.test(value));
11770
+ }
11771
+ function configuredHumanRoleAssigneeIds(config = {}) {
11772
+ const roleIds = config.routing?.humanRoleClickUpIds || {};
11773
+ return [...new Set(["CTO", "PO"].map((role) => String(roleIds[role] || "").trim()).filter(Boolean))];
11774
+ }
11775
+ async function selfHealClickUpHumanHandoffAssignment({ clickupClient, config, worktree = process.cwd(), taskId, task, commentsResponse = null } = {}) {
11776
+ const productManagerAssigneeId = String(config?.routing?.productManagerAssigneeId || "").trim();
11777
+ if (!taskId || !productManagerAssigneeId || !isClickUpTaskAssignedToProductManager(task, productManagerAssigneeId)) return { changed: false, reason: "not_pm_assigned" };
11778
+ if (typeof clickupClient?.updateTaskAssignees !== "function") return { changed: false, reason: "assignee_update_unavailable" };
11779
+ const humanAssigneeIds = configuredHumanRoleAssigneeIds(config);
11780
+ if (humanAssigneeIds.length === 0) return { changed: false, reason: "missing_human_role_ids" };
11781
+ let candidateText = "";
11782
+ if (commentsResponse) candidateText = clickUpCommentCollectionText(commentsResponse);
11783
+ if (!candidateText && typeof clickupClient?.getTaskComments === "function") {
11784
+ try {
11785
+ const response = await clickupClient.getTaskComments({ taskId, limit: CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT });
11786
+ candidateText = clickUpCommentCollectionText(response);
11787
+ } catch (error) {
11788
+ appendClickUpWebhookLocalLog(worktree, { type: "human_handoff_comment_scan_failed", taskId, message: error.message });
11789
+ }
11790
+ }
11791
+ if (!hasClickUpHumanHandoffText(candidateText)) return { changed: false, reason: "no_human_handoff_signal" };
11792
+ try {
11793
+ await clickupClient.updateTaskAssignees({ taskId, add: humanAssigneeIds, remove: [productManagerAssigneeId] });
11794
+ appendClickUpWebhookLocalLog(worktree, { type: "human_handoff_assignment_self_healed", taskId, add: humanAssigneeIds, remove: [productManagerAssigneeId] });
11795
+ return { changed: true, reason: "human_handoff_assignment_self_healed", add: humanAssigneeIds, remove: [productManagerAssigneeId] };
11796
+ } catch (error) {
11797
+ appendClickUpWebhookLocalLog(worktree, { type: "human_handoff_assignment_self_heal_failed", taskId, add: humanAssigneeIds, remove: [productManagerAssigneeId], message: error.message });
11798
+ return { changed: false, reason: "assignee_update_failed", message: error.message };
11799
+ }
11800
+ }
11704
11801
  async function ensureClickUpTaskWorktreeForWebhook({ opencodeBaseUrl = "", opencodeBaseUrlConfigured = false, openchamberBaseUrl = "", openchamberBaseUrlConfigured = false, clickupClient = null, webhookWorktree = process.cwd(), fetchImpl = globalThis.fetch, ...options } = {}) {
11705
11802
  void opencodeBaseUrlConfigured;
11706
11803
  if (!openchamberBaseUrlConfigured || !openchamberBaseUrl) return ensureClickUpTaskWorktree(options);
@@ -12525,6 +12622,12 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
12525
12622
  routed.ignored += 1;
12526
12623
  continue;
12527
12624
  }
12625
+ const humanHandoffSelfHeal = await selfHealClickUpHumanHandoffAssignment({ clickupClient, config, worktree, taskId, task });
12626
+ if (humanHandoffSelfHeal.changed) {
12627
+ routed.ignored += 1;
12628
+ routed.tasks.push({ taskId, action: "self_healed_human_handoff_assignment", ok: true, sessionId: null, branch: null, worktree: null, verification: humanHandoffSelfHeal.reason, delivered: false });
12629
+ continue;
12630
+ }
12528
12631
  if (watchdogMode) {
12529
12632
  const existingMetadata = clickUpTaskAgentMetadata(task, config.routing.metadataFieldId);
12530
12633
  const metadata = normalizeAgentMetadataJson(existingMetadata);
@@ -14181,6 +14284,55 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
14181
14284
  }
14182
14285
  return { ok: true, auth };
14183
14286
  };
14287
+ const finishClickUpTask = async ({ taskId, finishType = "", finalStatus = "", comment = "", prUrl = "", evidencePath = "" } = {}) => {
14288
+ if (!clickUpWebhookValidation.complete) return { ok: false, error: "clickup_not_configured" };
14289
+ if (!runtimeClickUpClient?.getTask) return { ok: false, error: "clickup_client_unavailable" };
14290
+ const normalizedTaskId = String(taskId || "").trim();
14291
+ if (!normalizedTaskId) return { ok: false, error: "task_id_required" };
14292
+ const normalizedFinishType = String(finishType || "").trim().toLowerCase().replace(/[\s-]+/g, "_");
14293
+ const inferredStatus = normalizedFinishType === "external_blocker" ? "in progress" : normalizedFinishType === "human_validation" ? "validation" : normalizedFinishType === "completed" ? "completed" : "";
14294
+ const status = String(finalStatus || inferredStatus || "").trim();
14295
+ const routing = clickUpWebhookValidation.config.routing || {};
14296
+ const productManagerAssigneeId = String(routing.productManagerAssigneeId || "").trim();
14297
+ const humanAssignees = ["CTO", "PO"].map((role) => String(routing.humanRoleClickUpIds?.[role] || "").trim()).filter(Boolean);
14298
+ const addAssignees = ["external_blocker", "human_validation"].includes(normalizedFinishType) ? humanAssignees : [];
14299
+ const removeAssignees = ["external_blocker", "human_validation", "completed"].includes(normalizedFinishType) && productManagerAssigneeId ? [productManagerAssigneeId] : [];
14300
+ const task = await runtimeClickUpClient.getTask(normalizedTaskId);
14301
+ const currentAssignees = clickUpAssigneeIds(task);
14302
+ const assignmentDelta = {
14303
+ add: [...new Set(addAssignees.filter((id) => !currentAssignees.includes(id)))],
14304
+ remove: [...new Set(removeAssignees.filter((id) => currentAssignees.includes(id)))]
14305
+ };
14306
+ const applied = [];
14307
+ if (status && runtimeClickUpClient.updateTaskStatus) {
14308
+ await runtimeClickUpClient.updateTaskStatus({ taskId: normalizedTaskId, status });
14309
+ applied.push("status");
14310
+ }
14311
+ if ((assignmentDelta.add.length > 0 || assignmentDelta.remove.length > 0) && runtimeClickUpClient.updateTaskAssignees) {
14312
+ await runtimeClickUpClient.updateTaskAssignees({ taskId: normalizedTaskId, add: assignmentDelta.add, remove: assignmentDelta.remove });
14313
+ applied.push("assignees");
14314
+ }
14315
+ const normalizedComment = String(comment || "").replace(/\\r\\n/g, "\n").replace(/\\n/g, "\n").trim();
14316
+ const commentWithContext = [
14317
+ normalizedComment,
14318
+ prUrl ? `
14319
+ PR: ${prUrl}` : "",
14320
+ evidencePath ? `
14321
+ Evidencia: ${evidencePath}` : ""
14322
+ ].join("").trim();
14323
+ if (commentWithContext && runtimeClickUpClient.postModelTaskComment) {
14324
+ await runtimeClickUpClient.postModelTaskComment({ taskId: normalizedTaskId, comment: commentWithContext });
14325
+ applied.push("comment");
14326
+ }
14327
+ return {
14328
+ ok: true,
14329
+ taskId: normalizedTaskId,
14330
+ finishType: normalizedFinishType || "status_update",
14331
+ status: status || null,
14332
+ assignmentDelta,
14333
+ applied
14334
+ };
14335
+ };
14184
14336
  const tools = {
14185
14337
  optima_init: tool({
14186
14338
  description: "Initialize the Optima workflow and CodeMap in the current repository",
@@ -14395,6 +14547,32 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
14395
14547
  return JSON.stringify(payload, null, 2);
14396
14548
  }
14397
14549
  }),
14550
+ optima_finish: tool({
14551
+ description: "Finish or hand off a ClickUp task through Optima. The model provides structured ClickUp text and intended final state; Optima applies status, comment, and assignment policy.",
14552
+ args: {
14553
+ task_id: tool.schema.string().describe("ClickUp task ID to finish or hand off"),
14554
+ finish_type: tool.schema.string().describe("One of: completed, human_validation, external_blocker, needs_work, status_update"),
14555
+ final_status: tool.schema.string().describe("Optional ClickUp status to set. If empty, Optima infers from finish_type where possible."),
14556
+ clickup_comment: tool.schema.string().describe("Human-facing structured ClickUp comment text with real line breaks. Use castellano for blocker/help comments."),
14557
+ pr_url: tool.schema.string().describe("Optional GitHub PR URL to include in the final ClickUp comment"),
14558
+ evidence_path: tool.schema.string().describe("Optional evidence path to include in the final ClickUp comment")
14559
+ },
14560
+ async execute(args) {
14561
+ try {
14562
+ const result = await finishClickUpTask({
14563
+ taskId: args.task_id,
14564
+ finishType: args.finish_type,
14565
+ finalStatus: args.final_status,
14566
+ comment: args.clickup_comment,
14567
+ prUrl: args.pr_url,
14568
+ evidencePath: args.evidence_path
14569
+ });
14570
+ return JSON.stringify(result, null, 2);
14571
+ } catch (error) {
14572
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14573
+ }
14574
+ }
14575
+ }),
14398
14576
  optima_clickup_create_subtasks: tool({
14399
14577
  description: "Generate dry-run ClickUp subtask creation payloads from a strict Markdown ## Subtasks section",
14400
14578
  args: {
@@ -14963,7 +15141,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
14963
15141
  }
14964
15142
  };
14965
15143
  }
14966
- OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpReviewAutomationPlan, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createGitHubApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpTaskWorktreeForWebhook, ensureClickUpTaskWorktreeOpenChamber, ensureClickUpWebhookSubscription, gitHubPullRequestContextFromPayload, gitHubWebhookExpectedPath, handleClickUpWebhookRequest, handleGitHubWebhookRequest, inspectOpenCodeSessionActivity, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, registerOpenChamberClickUpWorktree, routeGitHubWebhookEvent, scheduleClickUpAssignmentWatchdog, scheduleClickUpStartupReconciliation, syncOpenChamberWorktreeVisibility, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, verifyGitHubSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpRequiredPullRequest, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
15144
+ OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpReviewAutomationPlan, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createGitHubApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpTaskWorktreeForWebhook, ensureClickUpTaskWorktreeOpenChamber, ensureClickUpWebhookSubscription, gitHubPullRequestContextFromPayload, gitHubWebhookExpectedPath, handleClickUpWebhookRequest, handleGitHubWebhookRequest, inspectOpenCodeSessionActivity, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, registerOpenChamberClickUpWorktree, routeGitHubWebhookEvent, scheduleClickUpAssignmentWatchdog, selfHealClickUpHumanHandoffAssignment, scheduleClickUpStartupReconciliation, syncOpenChamberWorktreeVisibility, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, verifyGitHubSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpRequiredPullRequest, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
14967
15145
  export {
14968
15146
  OptimaPlugin as default
14969
15147
  };
@@ -11295,15 +11295,45 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
11295
11295
  async getTask(taskId) {
11296
11296
  return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}`);
11297
11297
  },
11298
+ async updateTask({ taskId, updates = {} }) {
11299
+ return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}`, {
11300
+ method: "PUT",
11301
+ body: JSON.stringify(updates)
11302
+ });
11303
+ },
11304
+ async updateTaskStatus({ taskId, status }) {
11305
+ return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}`, {
11306
+ method: "PUT",
11307
+ body: JSON.stringify({ status })
11308
+ });
11309
+ },
11298
11310
  async updateTaskMetadata({ taskId, fieldId, value }) {
11299
11311
  return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}/field/${encodeURIComponent(fieldId)}`, {
11300
11312
  method: "POST",
11301
11313
  body: JSON.stringify({ value })
11302
11314
  });
11303
11315
  },
11316
+ async updateTaskAssignees({ taskId, add = [], remove = [] }) {
11317
+ const body = { assignees: {} };
11318
+ const addIds = [...new Set((add || []).map((id) => String(id || "").trim()).filter(Boolean))];
11319
+ const removeIds = [...new Set((remove || []).map((id) => String(id || "").trim()).filter(Boolean))];
11320
+ if (addIds.length > 0) body.assignees.add = addIds;
11321
+ if (removeIds.length > 0) body.assignees.rem = removeIds;
11322
+ if (!body.assignees.add && !body.assignees.rem) return { ok: true, skipped: true, reason: "empty_assignee_update" };
11323
+ return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}`, {
11324
+ method: "PUT",
11325
+ body: JSON.stringify(body)
11326
+ });
11327
+ },
11304
11328
  async postTaskComment() {
11305
11329
  throw new Error("Optima runtime does not post ClickUp task comments; use local logs for process failures.");
11306
11330
  },
11331
+ async postModelTaskComment({ taskId, comment }) {
11332
+ return request(`https://api.clickup.com/api/v2/task/${encodeURIComponent(taskId)}/comment`, {
11333
+ method: "POST",
11334
+ body: JSON.stringify({ comment_text: String(comment || "") })
11335
+ });
11336
+ },
11307
11337
  async addTaskTag() {
11308
11338
  throw new Error("Optima runtime does not add ClickUp task tags; use local logs for process failures.");
11309
11339
  },
@@ -11345,9 +11375,21 @@ function createTestClickUpApiClient(config) {
11345
11375
  metadata.set(String(taskId || ""), value);
11346
11376
  return { ok: true };
11347
11377
  },
11378
+ async updateTask() {
11379
+ return { ok: true };
11380
+ },
11381
+ async updateTaskStatus() {
11382
+ return { ok: true };
11383
+ },
11384
+ async updateTaskAssignees() {
11385
+ return { ok: true };
11386
+ },
11348
11387
  async postTaskComment() {
11349
11388
  return { ok: true };
11350
11389
  },
11390
+ async postModelTaskComment() {
11391
+ return { ok: true };
11392
+ },
11351
11393
  async addTaskTag() {
11352
11394
  return { ok: true };
11353
11395
  },
@@ -11708,6 +11750,61 @@ function clickUpWebhookLifecycleLog(worktree, entry) {
11708
11750
  var activeClickUpWebhookListeners = /* @__PURE__ */ new Map();
11709
11751
  var activeClickUpWebhookLifecycleRegistry = /* @__PURE__ */ new Map();
11710
11752
  var activeClickUpTaskRoutes = /* @__PURE__ */ new Map();
11753
+ var CLICKUP_HUMAN_HANDOFF_TEXT_PATTERNS = [
11754
+ /\bhuman validation requested\b/i,
11755
+ /\bhuman validation\/approval\b/i,
11756
+ /\bplease review pr\b/i,
11757
+ /\bapprove or request changes\b/i,
11758
+ /\bexternal blocker\b/i,
11759
+ /\bowner routing\b/i,
11760
+ /\bpo action needed\b/i,
11761
+ /\brouted to PO\b/i,
11762
+ /\bauthenticated Chrome session\b/i,
11763
+ /\bbrowser-real\b/i,
11764
+ /\bAgent Jake\b/i,
11765
+ /\bCDP\s+9222\b/i,
11766
+ /\bCTO\/PO\b/i,
11767
+ /\bBloqueo externo\b/i,
11768
+ /\bQué necesito de CTO\/PO\b/i,
11769
+ /\bCómo se desbloquea\b/i
11770
+ ];
11771
+ function clickUpCommentCollectionText(response = {}) {
11772
+ return clickUpCommentListItems(response).map((comment) => clickUpCommentText(comment)).filter(Boolean).join("\n\n");
11773
+ }
11774
+ function hasClickUpHumanHandoffText(text = "") {
11775
+ const value = String(text || "");
11776
+ return value.length > 0 && CLICKUP_HUMAN_HANDOFF_TEXT_PATTERNS.some((pattern) => pattern.test(value));
11777
+ }
11778
+ function configuredHumanRoleAssigneeIds(config = {}) {
11779
+ const roleIds = config.routing?.humanRoleClickUpIds || {};
11780
+ return [...new Set(["CTO", "PO"].map((role) => String(roleIds[role] || "").trim()).filter(Boolean))];
11781
+ }
11782
+ async function selfHealClickUpHumanHandoffAssignment({ clickupClient, config, worktree = process.cwd(), taskId, task, commentsResponse = null } = {}) {
11783
+ const productManagerAssigneeId = String(config?.routing?.productManagerAssigneeId || "").trim();
11784
+ if (!taskId || !productManagerAssigneeId || !isClickUpTaskAssignedToProductManager(task, productManagerAssigneeId)) return { changed: false, reason: "not_pm_assigned" };
11785
+ if (typeof clickupClient?.updateTaskAssignees !== "function") return { changed: false, reason: "assignee_update_unavailable" };
11786
+ const humanAssigneeIds = configuredHumanRoleAssigneeIds(config);
11787
+ if (humanAssigneeIds.length === 0) return { changed: false, reason: "missing_human_role_ids" };
11788
+ let candidateText = "";
11789
+ if (commentsResponse) candidateText = clickUpCommentCollectionText(commentsResponse);
11790
+ if (!candidateText && typeof clickupClient?.getTaskComments === "function") {
11791
+ try {
11792
+ const response = await clickupClient.getTaskComments({ taskId, limit: CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT });
11793
+ candidateText = clickUpCommentCollectionText(response);
11794
+ } catch (error) {
11795
+ appendClickUpWebhookLocalLog(worktree, { type: "human_handoff_comment_scan_failed", taskId, message: error.message });
11796
+ }
11797
+ }
11798
+ if (!hasClickUpHumanHandoffText(candidateText)) return { changed: false, reason: "no_human_handoff_signal" };
11799
+ try {
11800
+ await clickupClient.updateTaskAssignees({ taskId, add: humanAssigneeIds, remove: [productManagerAssigneeId] });
11801
+ appendClickUpWebhookLocalLog(worktree, { type: "human_handoff_assignment_self_healed", taskId, add: humanAssigneeIds, remove: [productManagerAssigneeId] });
11802
+ return { changed: true, reason: "human_handoff_assignment_self_healed", add: humanAssigneeIds, remove: [productManagerAssigneeId] };
11803
+ } catch (error) {
11804
+ appendClickUpWebhookLocalLog(worktree, { type: "human_handoff_assignment_self_heal_failed", taskId, add: humanAssigneeIds, remove: [productManagerAssigneeId], message: error.message });
11805
+ return { changed: false, reason: "assignee_update_failed", message: error.message };
11806
+ }
11807
+ }
11711
11808
  async function ensureClickUpTaskWorktreeForWebhook({ opencodeBaseUrl = "", opencodeBaseUrlConfigured = false, openchamberBaseUrl = "", openchamberBaseUrlConfigured = false, clickupClient = null, webhookWorktree = process.cwd(), fetchImpl = globalThis.fetch, ...options } = {}) {
11712
11809
  void opencodeBaseUrlConfigured;
11713
11810
  if (!openchamberBaseUrlConfigured || !openchamberBaseUrl) return ensureClickUpTaskWorktree(options);
@@ -12532,6 +12629,12 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
12532
12629
  routed.ignored += 1;
12533
12630
  continue;
12534
12631
  }
12632
+ const humanHandoffSelfHeal = await selfHealClickUpHumanHandoffAssignment({ clickupClient, config, worktree, taskId, task });
12633
+ if (humanHandoffSelfHeal.changed) {
12634
+ routed.ignored += 1;
12635
+ routed.tasks.push({ taskId, action: "self_healed_human_handoff_assignment", ok: true, sessionId: null, branch: null, worktree: null, verification: humanHandoffSelfHeal.reason, delivered: false });
12636
+ continue;
12637
+ }
12535
12638
  if (watchdogMode) {
12536
12639
  const existingMetadata = clickUpTaskAgentMetadata(task, config.routing.metadataFieldId);
12537
12640
  const metadata = normalizeAgentMetadataJson(existingMetadata);
@@ -14188,6 +14291,55 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
14188
14291
  }
14189
14292
  return { ok: true, auth };
14190
14293
  };
14294
+ const finishClickUpTask = async ({ taskId, finishType = "", finalStatus = "", comment = "", prUrl = "", evidencePath = "" } = {}) => {
14295
+ if (!clickUpWebhookValidation.complete) return { ok: false, error: "clickup_not_configured" };
14296
+ if (!runtimeClickUpClient?.getTask) return { ok: false, error: "clickup_client_unavailable" };
14297
+ const normalizedTaskId = String(taskId || "").trim();
14298
+ if (!normalizedTaskId) return { ok: false, error: "task_id_required" };
14299
+ const normalizedFinishType = String(finishType || "").trim().toLowerCase().replace(/[\s-]+/g, "_");
14300
+ const inferredStatus = normalizedFinishType === "external_blocker" ? "in progress" : normalizedFinishType === "human_validation" ? "validation" : normalizedFinishType === "completed" ? "completed" : "";
14301
+ const status = String(finalStatus || inferredStatus || "").trim();
14302
+ const routing = clickUpWebhookValidation.config.routing || {};
14303
+ const productManagerAssigneeId = String(routing.productManagerAssigneeId || "").trim();
14304
+ const humanAssignees = ["CTO", "PO"].map((role) => String(routing.humanRoleClickUpIds?.[role] || "").trim()).filter(Boolean);
14305
+ const addAssignees = ["external_blocker", "human_validation"].includes(normalizedFinishType) ? humanAssignees : [];
14306
+ const removeAssignees = ["external_blocker", "human_validation", "completed"].includes(normalizedFinishType) && productManagerAssigneeId ? [productManagerAssigneeId] : [];
14307
+ const task = await runtimeClickUpClient.getTask(normalizedTaskId);
14308
+ const currentAssignees = clickUpAssigneeIds(task);
14309
+ const assignmentDelta = {
14310
+ add: [...new Set(addAssignees.filter((id) => !currentAssignees.includes(id)))],
14311
+ remove: [...new Set(removeAssignees.filter((id) => currentAssignees.includes(id)))]
14312
+ };
14313
+ const applied = [];
14314
+ if (status && runtimeClickUpClient.updateTaskStatus) {
14315
+ await runtimeClickUpClient.updateTaskStatus({ taskId: normalizedTaskId, status });
14316
+ applied.push("status");
14317
+ }
14318
+ if ((assignmentDelta.add.length > 0 || assignmentDelta.remove.length > 0) && runtimeClickUpClient.updateTaskAssignees) {
14319
+ await runtimeClickUpClient.updateTaskAssignees({ taskId: normalizedTaskId, add: assignmentDelta.add, remove: assignmentDelta.remove });
14320
+ applied.push("assignees");
14321
+ }
14322
+ const normalizedComment = String(comment || "").replace(/\\r\\n/g, "\n").replace(/\\n/g, "\n").trim();
14323
+ const commentWithContext = [
14324
+ normalizedComment,
14325
+ prUrl ? `
14326
+ PR: ${prUrl}` : "",
14327
+ evidencePath ? `
14328
+ Evidencia: ${evidencePath}` : ""
14329
+ ].join("").trim();
14330
+ if (commentWithContext && runtimeClickUpClient.postModelTaskComment) {
14331
+ await runtimeClickUpClient.postModelTaskComment({ taskId: normalizedTaskId, comment: commentWithContext });
14332
+ applied.push("comment");
14333
+ }
14334
+ return {
14335
+ ok: true,
14336
+ taskId: normalizedTaskId,
14337
+ finishType: normalizedFinishType || "status_update",
14338
+ status: status || null,
14339
+ assignmentDelta,
14340
+ applied
14341
+ };
14342
+ };
14191
14343
  const tools = {
14192
14344
  optima_init: tool({
14193
14345
  description: "Initialize the Optima workflow and CodeMap in the current repository",
@@ -14402,6 +14554,32 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
14402
14554
  return JSON.stringify(payload, null, 2);
14403
14555
  }
14404
14556
  }),
14557
+ optima_finish: tool({
14558
+ description: "Finish or hand off a ClickUp task through Optima. The model provides structured ClickUp text and intended final state; Optima applies status, comment, and assignment policy.",
14559
+ args: {
14560
+ task_id: tool.schema.string().describe("ClickUp task ID to finish or hand off"),
14561
+ finish_type: tool.schema.string().describe("One of: completed, human_validation, external_blocker, needs_work, status_update"),
14562
+ final_status: tool.schema.string().describe("Optional ClickUp status to set. If empty, Optima infers from finish_type where possible."),
14563
+ clickup_comment: tool.schema.string().describe("Human-facing structured ClickUp comment text with real line breaks. Use castellano for blocker/help comments."),
14564
+ pr_url: tool.schema.string().describe("Optional GitHub PR URL to include in the final ClickUp comment"),
14565
+ evidence_path: tool.schema.string().describe("Optional evidence path to include in the final ClickUp comment")
14566
+ },
14567
+ async execute(args) {
14568
+ try {
14569
+ const result = await finishClickUpTask({
14570
+ taskId: args.task_id,
14571
+ finishType: args.finish_type,
14572
+ finalStatus: args.final_status,
14573
+ comment: args.clickup_comment,
14574
+ prUrl: args.pr_url,
14575
+ evidencePath: args.evidence_path
14576
+ });
14577
+ return JSON.stringify(result, null, 2);
14578
+ } catch (error) {
14579
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14580
+ }
14581
+ }
14582
+ }),
14405
14583
  optima_clickup_create_subtasks: tool({
14406
14584
  description: "Generate dry-run ClickUp subtask creation payloads from a strict Markdown ## Subtasks section",
14407
14585
  args: {
@@ -14970,7 +15148,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
14970
15148
  }
14971
15149
  };
14972
15150
  }
14973
- OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpReviewAutomationPlan, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createGitHubApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpTaskWorktreeForWebhook, ensureClickUpTaskWorktreeOpenChamber, ensureClickUpWebhookSubscription, gitHubPullRequestContextFromPayload, gitHubWebhookExpectedPath, handleClickUpWebhookRequest, handleGitHubWebhookRequest, inspectOpenCodeSessionActivity, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, registerOpenChamberClickUpWorktree, routeGitHubWebhookEvent, scheduleClickUpAssignmentWatchdog, scheduleClickUpStartupReconciliation, syncOpenChamberWorktreeVisibility, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, verifyGitHubSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpRequiredPullRequest, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
15151
+ OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpReviewAutomationPlan, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createGitHubApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpTaskWorktreeForWebhook, ensureClickUpTaskWorktreeOpenChamber, ensureClickUpWebhookSubscription, gitHubPullRequestContextFromPayload, gitHubWebhookExpectedPath, handleClickUpWebhookRequest, handleGitHubWebhookRequest, inspectOpenCodeSessionActivity, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, registerOpenChamberClickUpWorktree, routeGitHubWebhookEvent, scheduleClickUpAssignmentWatchdog, selfHealClickUpHumanHandoffAssignment, scheduleClickUpStartupReconciliation, syncOpenChamberWorktreeVisibility, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, verifyGitHubSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpRequiredPullRequest, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
14974
15152
 
14975
15153
  // src/sanitize_cli.js
14976
15154
  var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
@@ -12,6 +12,7 @@
12
12
  - Ignored task types: `Idea`, legacy `Backlog` alias, `Hito`, `Nota de reunión`, `Respuesta del formulario` unless converted or linked to delivery work; `Idea` is non-delivery.
13
13
  - `product_manager` may pre-estimate "a qué huele" small/medium/large plus rough story points, but development requests must be converted to routed ClickUp work.
14
14
  - Human role registry: resolve `CTO` and `PO` from available human role context and configured ClickUp IDs. Missing repo-local human registry files are an internal routing detail, not a task status; do not mention internal role-resolution details in ClickUp comments unless a human explicitly asks for routing diagnostics. Use role identifiers in workflow text and automation config.
15
+ - Final task handoffs must use `optima_finish` when available: the model supplies structured ClickUp text and intended final state, while Optima applies status, comments, and assignment policy.
15
16
  - Status-to-action mapping: `backlog` -> `ignore`, `plan` -> `plan`, `in progress` -> `execute`, `validation` -> `validate`, `merge` -> `merge`, `completed`/`Closed` -> `ignore` unless reopened. `plan` does not imply generic human approval assignment; `merge` is parent-only post-approval automation, while subtasks merge after successful Validator/QA validation without waiting for a human approval status.
16
17
  - Branch-safe type slugs are lowercase ASCII: `tarea`, `bug`, `doc`, `poc`.
17
18
 
@@ -19,7 +20,7 @@
19
20
 
20
21
  - Agents must not assign ClickUp tasks to `CTO` or `PO` except for the explicit cases below; generic handoff, routine validation, duplicate-assignee cleanup, or incomplete phase handoff are prohibited.
21
22
  - **Parent planning questions:** only a parent task in `plan` may assign `CTO`/`PO`, and only after clear, concrete questions have been posted in ClickUp comments. Subtasks are planned and executed end-to-end without CTO/PO planning assignment.
22
- - **Real in-progress blocker:** a task in `in progress` may assign/escalate to `CTO`/`PO` only when blocked by missing credentials, permissions, authenticated browser/Agent Jake access, external tools, or live third-party access after local/subagent attempts are exhausted. Escalation means posting a concrete ClickUp help request in castellano explaining exactly what is needed, assigning CTO/PO, and removing the Product Manager assignee so watchdogs stop re-queueing PM until a human replies/reassigns. Agents must not stop at informal phase boundaries such as "I reached phase 1"; phases should have been subtasks, otherwise finish the accepted task.
23
+ - **Real in-progress blocker:** a task in `in progress` may assign/escalate to `CTO`/`PO` only when blocked by missing credentials, permissions, authenticated browser/Agent Jake access, external tools, or live third-party access after local/subagent attempts are exhausted. Escalation means calling `optima_finish` with a concrete ClickUp help request in castellano explaining exactly what is needed; Optima assigns CTO/PO and removes the Product Manager assignee so watchdogs stop re-queueing PM until a human replies/reassigns. Agents must not stop at informal phase boundaries such as "I reached phase 1"; phases should have been subtasks, otherwise finish the accepted task.
23
24
  - **Parent validation approval:** only a parent task in `validation` may assign `CTO`/`PO` for final validation, and only after `optima_github_verify_vercel_pr` returns `ready: true` for the current parent PR with a functional Vercel preproduction/preview URL. After a human comments `Approved`, automation reassigns to itself or the merge owner, removes human assignees, merges, cleans workspaces/worktrees/branches, pushes to `dev`, and ensures the dev/preproduction environment contains the code.
24
25
 
25
26
  ## Routing Rules
@@ -7,7 +7,8 @@
7
7
  - ClickUp-first delivery types: `Tarea`, `Bug`, `Doc`, `PoC`; ignored types: `Idea`, legacy `Backlog` alias, `Hito`, `Nota de reunión`, `Respuesta del formulario` unless converted/linked.
8
8
  - Product Manager without workflow never develops; it may pre-estimate "a qué huele" small/medium/large plus rough story points and route development into ClickUp tasks.
9
9
  - Human role registry: resolve `CTO` and `PO` from available human role context/configured ClickUp IDs. Missing repo-local human registry files are internal routing detail; do not mention internal role-resolution details in ClickUp comments unless asked for routing diagnostics.
10
- - ClickUp-first actions: `backlog` ignore, `plan` plan with `Story Points`, `Definition`, and test strategy; assign `CTO`/`PO` only for parent planning questions with clear ClickUp comments, real `in progress` blockers caused by missing credentials/tools/access/authenticated browser or Agent Jake/live third-party access after local/subagent attempts, or parent `validation` after `optima_github_verify_vercel_pr` returns `ready: true` with a functional Vercel URL. For true external `in progress` blockers, post a concrete castellano ClickUp help request explaining exactly what is needed, assign CTO/PO, and remove Product Manager so PM watchdogs stop looping until a human replies/reassigns. Subtasks execute end-to-end and merge to the parent branch after Validator/QA passes without CTO/PO approval; parent `Approved` comments trigger automation to remove humans, assign merge owner/self, merge to `dev`, clean workspaces/worktrees/branches, push, and ensure dev/preproduction receives the code; `completed`/`Closed` ignore unless reopened.
10
+ - Final task handoffs must use `optima_finish` when available; Optima applies final status, comment, and assignments.
11
+ - ClickUp-first actions: `backlog` ignore, `plan` plan with `Story Points`, `Definition`, and test strategy; assign `CTO`/`PO` only for parent planning questions with clear ClickUp comments, real `in progress` blockers caused by missing credentials/tools/access/authenticated browser or Agent Jake/live third-party access after local/subagent attempts, or parent `validation` after `optima_github_verify_vercel_pr` returns `ready: true` with a functional Vercel URL. For true external `in progress` blockers, call `optima_finish` with a concrete castellano ClickUp help request explaining exactly what is needed; Optima assigns CTO/PO and removes Product Manager so PM watchdogs stop looping until a human replies/reassigns. Subtasks execute end-to-end and merge to the parent branch after Validator/QA passes without CTO/PO approval; parent `Approved` comments trigger automation to remove humans, assign merge owner/self, merge to `dev`, clean workspaces/worktrees/branches, push, and ensure dev/preproduction receives the code; `completed`/`Closed` ignore unless reopened.
11
12
  - Routing: keep `tiny` to one slice and usually one specialist; keep `standard` bounded; decompose `complex` into slice-based subtasks.
12
13
  - `complex + implementation` normally uses `workflow_runner` in full mode.
13
14
  - WPM stores `agent_metadata`, re-estimates `Story Points` on material plan changes, and keeps `Definition` plan contract separate from final Documentation.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defend-tech/opencode-optima",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"