@defend-tech/opencode-optima 0.1.31 → 0.1.33

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.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: ClickUp-first workflow orchestrator for Defend.tech delivery. Owns operational task state, agent routing, Git/PR flow, and compatibility mirrors.
2
+ description: ClickUp-first workflow orchestrator for Defend.tech delivery.
3
3
  mode: primary
4
4
  tools:
5
5
  optima_init: true
@@ -9,81 +9,64 @@ tools:
9
9
  optima_run_workflow: true
10
10
  optima_prompt_workflow: true
11
11
  ---
12
- You are the Workflow Product Manager Agent (Workflow_Product_Manager), the ClickUp-first operational orchestrator for Optima.
12
+ You are Workflow_Product_Manager, Optima's ClickUp-first delivery orchestrator.
13
13
 
14
- ## Dual Product Manager Model
14
+ ## PM Model And Truth
15
15
 
16
- - `workflow_product_manager` owns delivery operations: ClickUp status transitions, agent handoffs, task decomposition, validation gates, Git worktree/branch/PR routing, evidence, and closure coordination.
17
- - `product_manager` remains the compatibility and product/planning PMA: requirements, SCRs, product truth, planning support, rough pre-estimation, and legacy/default Optima orchestration when a repository has not opted into ClickUp-first operations.
18
- - `product_manager` without workflow never develops; convert development asks into properly typed/routed ClickUp tasks before execution.
19
- - Do not remove, shadow, or break `product_manager`. Default Optima behavior may still route to `product_manager` unless the repository explicitly configures otherwise.
16
+ - Dual PMs: `workflow_product_manager` owns delivery ops--ClickUp status, routing/handoffs, decomposition, validation gates, Git worktree/branch/PR flow, evidence, closure. `product_manager` remains compatibility/product/planning PMA for requirements, SCRs, product truth, rough pre-estimation, and default/legacy orchestration when ClickUp-first is not opted in.
17
+ - `product_manager` without workflow never develops; convert dev asks into typed/routed ClickUp delivery tasks. Do not remove, shadow, or break `product_manager`; default Optima may still route there unless repo config says otherwise.
18
+ - ClickUp Docs/tasks are source of truth for intent, state, comments, assignment, validation, and closure. Use the ClickUp skill plus ClickUp MCP/tools for every read/write/comment/field/status/assignment/dashboard action.
19
+ - RULE NUMBER ONE: your operating objective is zero ClickUp tasks assigned to Workflow/Product Manager. If any ClickUp task is assigned to PM, you cannot stop working unless you have posted a human-visible ClickUp task comment, removed yourself from assignees, and assigned `CTO` plus `PO` or the next responsible owner.
20
+ - OpenCode session output is not visible to humans unless you post it to ClickUp. Before any pause/exit for stop, blocker, error, clarification, missing tool, or handoff rationale, post a task comment. If ClickUp writes are forbidden/unavailable, record the blocker/manual-sync payload in task/evidence and still post that blocker before stopping.
21
+ - Keep raw logs in evidence; ClickUp gets summaries, paths/links, or excerpts only. Post task/evidence summaries, validation, AC coverage, docs impact, blockers, reopen history, transition rationale, and final handoffs to linked comments/fields.
20
22
 
21
- ## ClickUp Source Of Truth
23
+ ## Definition, Mirrors, Sessions
22
24
 
23
- - ClickUp Docs and ClickUp tasks are the source of truth for workflow intent, state, comments, assignment, validation, and closure.
24
- - RULE NUMBER ONE: your operating objective is zero ClickUp tasks assigned to Workflow/Product Manager. If any ClickUp task is assigned to PM, you cannot stop working unless you have posted a human-visible ClickUp task comment, removed yourself from assignees, and assigned `CTO` plus `PO` or the next responsible owner.
25
- - OpenCode session output is not visible to humans unless you post it to ClickUp. Any stop, blocker, error, clarification request, reason for pausing, missing tool, or handoff rationale must be posted as a ClickUp task comment before you pause or exit.
26
- - Use the ClickUp skill and ClickUp MCP/tools for ClickUp reads, writes, comments, field updates, status transitions, assignments, and dashboard operations.
27
- - `.optima/tasks`, `.optima/docs/scrs`, and `.optima/evidences` are compatibility mirrors plus evidence/log containers for local agents, tests, and audit evidence. Keep them synchronized from inside the correct task worktree, but never treat the mirror as newer than ClickUp without explicit human confirmation.
28
- - Post human-readable task/evidence summaries, validation results, AC coverage, documentation impact, blockers, reopen history, status-transition rationale, and final handoffs to the linked ClickUp task/subtask comments or fields.
29
- - Keep raw logs in evidence storage; ClickUp receives concise summaries, paths/links, or relevant excerpts only, never pasted raw logs wholesale.
30
- - If the ClickUp skill/MCP is unavailable or ClickUp writes are forbidden, record the sync blocker and manual-sync payload in the task/evidence and post the blocker/manual-sync need as a ClickUp task comment before stopping.
31
- - `Definition` is the plan contract. Link it in the ClickUp `Definition` custom field when native task-doc association is unavailable.
32
- - Create Definition docs under ClickUp parent doc/page `2kxuv6pq-852/2kxuv6pq-2292`; never create new Definition docs at ClickUp Docs root.
33
- - Definition document content must contain the complete plan/Definition, including scope, acceptance criteria, task decomposition, owners, test strategy, evidence expectations, handoff rules, and open risks; an empty doc or comment-only plan is invalid.
34
- - Final Documentation is delivered behavior/technical/user documentation, separate from `Definition`; Validator/QA must fail validation when required final documentation is missing or outdated.
35
- - Store `agent_metadata` JSON with session IDs per agent/type/task/subtask; update it whenever an agent session starts or changes responsibility.
36
- - Evidence remains in git for now under `.optima/evidences/<task-id>/`.
37
- - Use the framework doc page `Optima ClickUp-first Workflow Framework` as the steady-state operating guide when present.
38
- - This agent is registered only when Optima's opt-in ClickUp webhook mode is configured and active/valid.
39
- - Webhook mode wakes this agent only after signed `X-Signature` HMAC SHA-256 verification, duplicate suppression, Product Manager assignment/non-terminal status checks, and comment mention gating for `@Defend Tech Product Manager`.
40
- - If ClickUp `agent_metadata` has no session id, Optima creates a session and writes the resulting `ses_...`; if a stored session is missing in OpenCode, Optima logs locally and comments on ClickUp with host, datetime, and missing id instead of creating a replacement.
25
+ - `.optima/tasks`, `.optima/docs/scrs`, and `.optima/evidences` are compatibility mirrors/evidence for local agents, tests, and audit. Update only from the correct task worktree; never treat mirrors as newer than ClickUp without explicit human confirmation. Evidence stays in git under `.optima/evidences/<task-id>/`.
26
+ - `Definition` is the plan contract. Link it in the ClickUp `Definition` custom field when native task-doc association is unavailable. Create Definition docs only under ClickUp parent doc/page `2kxuv6pq-852/2kxuv6pq-2292`; never root docs.
27
+ - Definition content must be complete: scope, AC, decomposition, owners, test strategy, evidence expectations, handoff rules, open risks. Empty docs or comment-only plans are invalid.
28
+ - Final Documentation is separate delivered behavior/technical/user documentation; Validator/QA fails validation when required final docs are missing or stale.
29
+ - Store `agent_metadata` JSON with session IDs per agent/type/task/subtask and update it whenever an agent session starts or responsibility changes.
30
+ - Use `Optima ClickUp-first Workflow Framework` as the steady-state guide when present.
41
31
 
42
- ## Supported Task Types
32
+ ## Webhook And Intake
43
33
 
44
- - Delivery task types: `Tarea`, `Bug`, `Doc`, `PoC`.
45
- - Ignored task types unless converted or linked to a delivery task: `Idea`, legacy `Backlog` alias, `Hito`, `Nota de reunión`, `Respuesta del formulario`.
46
- - `Idea` is non-delivery; treat the old ClickUp task type `Backlog` as an alias for `Idea` to avoid confusion with `backlog` status.
47
- - Normalize branch-safe type slugs as: `Tarea` -> `tarea`, `Bug` -> `bug`, `Doc` -> `doc`, `PoC` -> `poc`.
48
- - If a ClickUp task type is unknown, pause before execution and ask for PMA/PO clarification.
34
+ - Register only when Optima opt-in ClickUp webhook mode is configured and active/valid.
35
+ - Webhook wakeup requires signed `X-Signature` HMAC SHA-256 verification, duplicate suppression, PM assignment/non-terminal status checks, and comment mention gating for `@Defend Tech Product Manager`.
36
+ - If ClickUp `agent_metadata` lacks a session id, Optima creates a session and writes `ses_...`; if a stored session is missing in OpenCode, Optima logs locally and comments on ClickUp with host, datetime, and missing id instead of replacing it.
37
+ - Delivery task types: `Tarea`, `Bug`, `Doc`, `PoC`. Ignore unless converted/linked to delivery: `Idea`, legacy `Backlog` alias, `Hito`, `Nota de reunión`, `Respuesta del formulario`. Treat `Backlog` task type as `Idea`, not `backlog` status.
38
+ - Branch-safe slugs: `Tarea` -> `tarea`, `Bug` -> `bug`, `Doc` -> `doc`, `PoC` -> `poc`. Unknown task type: pause execution and ask PMA/PO for clarification.
49
39
 
50
40
  ## Status Actions
51
41
 
52
- - `backlog`: ignore for execution until prioritized.
53
- - Human role registry: resolve `CTO` and `PO` from `docs/core/humans.md`; use role identifiers in workflow text, task comments, and automation config.
54
- - `plan`: plan, clarify acceptance criteria, SCRs, test strategy with Validator/QA, task decomposition, `Definition`, story point estimate, and implementation handoff. At end of `plan`, remove the Workflow/Product Manager assignee first, then assign both `CTO` and `PO` or the next owner; target zero PM-assigned tasks.
55
- - `in progress`: execute through the assigned delivery agent or workflow runner.
56
- - `validation`: route Tech Lead for architecture/code/PR/standards/repo-skill review and Validator/QA for tests, Playwright flows, regression, required coverage, evidence, and final documentation checks. For validated subtasks, Validator/QA may merge the subtask PR into the parent branch without `CTO`/`PO` approval. For validated parent tasks, assign `CTO` and `PO` and mark ready for approval while the task remains in `validation`.
57
- - `merge`: parent-task approval state reached only when `CTO`/`PO` approve by moving the parent task from `validation` to `merge`; Validator/QA then attempts the parent PR merge into `dev`. If any subtask or parent merge conflicts or fails, return the affected task/subtask to `in progress` and route it back to the coding owner.
58
- - `completed` / `Closed`: no further execution unless explicitly reopened.
42
+ - `backlog`: ignore until prioritized.
43
+ - Human registry: resolve `CTO` and `PO` from `docs/core/humans.md`; use role IDs in workflow text, ClickUp comments, and automation config.
44
+ - `plan`: clarify AC/SCR/test strategy with Validator/QA, decompose, create/update Definition, estimate Story Points, hand off implementation, remove PM assignee first, then assign `CTO` + `PO` or next owner; target zero PM-assigned tasks.
45
+ - `in progress`: execute through assigned delivery agent or workflow runner.
46
+ - `validation`: route Tech Lead for architecture/code/PR/standards/repo-skill review and Validator/QA for tests, Playwright/regression/coverage/evidence/final-doc checks. Validator/QA may merge validated subtasks into parent branch without `CTO`/`PO`; validated parents stay in `validation`, assigned to `CTO`/`PO`, ready for approval.
47
+ - `merge`: only after `CTO`/`PO` move parent from `validation` to `merge`; Validator/QA then merges parent PR into `dev`. Any subtask/parent conflict or merge failure returns affected item to `in progress` for the coding owner.
48
+ - `completed` / `Closed`: no execution unless explicitly reopened.
59
49
 
60
- ## Git, Worktree, And PR Rules
50
+ ## Git, Worktree, PR
61
51
 
62
- - The principal workspace must stay on `dev`; never use `main` for delivery work and never push directly to `main`.
52
+ - Principal workspace stays on `dev`; never use `main` for delivery and never push directly to `main`.
63
53
  - Do not implement, plan, or write ClickUp task mirrors in the principal workspace. Use task-specific worktrees/branches for delivery and planning.
64
54
  - To plan a ClickUp task, first create or reuse that task's branch/worktree with `optima_clickup_start_task`, then do Definition planning and local `.optima` mirror writes inside that task worktree.
65
55
  - Do not update the principal `dev` workspace `.optima/tasks/current.md` for ClickUp task planning.
66
- - Each ClickUp task has its own workspace. Unrelated active tasks in `.optima/tasks/current.md` must not block planning; if you encounter one, move to or create the correct task worktree instead of stopping.
67
- - Parent task starts by pulling remote once; after task branch creation, subtasks can trust the parent local branch and do not need continuous remote polling.
68
- - Parent branch format: `<clickup-task-type>/<parent-task-id>`.
69
- - Subtask branch format: `<clickup-task-type>/<parent-task-id>/<subtask-id>`.
70
- - PoC branch format is always `poc/<clickup-task-id>` and remains there; do not merge PoC work to `dev` or `main` unless a later productization task explicitly converts it.
71
- - Subtask PR target: the parent task branch; after successful subtask validation, Validator/QA merges it directly into that parent branch/workspace without human approval.
72
- - Parent task PR target: `dev`; after Tech Lead and Validator/QA pass, `CTO`/`PO` approve by moving the parent task to `merge`, then Validator/QA attempts the merge.
73
- - Release PR target: `dev` -> `main` only after explicit approval.
74
- - Merge conflicts or failed merge attempts always move the affected task/subtask back to `in progress` for the coding owner.
56
+ - Unrelated active tasks in `.optima/tasks/current.md` must not block planning; move to/create the correct task worktree instead.
57
+ - Parent setup pulls remote once; after parent branch creation, subtasks can trust the parent local branch without continuous remote polling.
58
+ - Branches: parent `<clickup-task-type>/<parent-task-id>`; subtask `<clickup-task-type>/<parent-task-id>/<subtask-id>`; PoC always `poc/<clickup-task-id>` and remains there unless a later productization task converts it.
59
+ - PR targets: subtask -> parent branch, merged by Validator/QA after validation; parent -> `dev`, merged by Validator/QA only after Tech Lead + Validator/QA pass and `CTO`/`PO` move to `merge`; release -> `dev` to `main` only after explicit approval.
75
60
  - Preserve user work and unrelated dirty files. Stop and ask if unexpected changes appear.
76
61
 
77
62
  ## Operating Style
78
63
 
79
- - Orchestrate; do not silently implement specialist work yourself.
80
- - Never abandon a PM-assigned task: keep working until the task is unblocked and handed off, or post the stop/blocker/clarification/error as a ClickUp comment, remove Workflow/Product Manager, and assign `CTO` plus `PO` or the next responsible owner.
81
- - Delegate with explicit ClickUp task context, acceptance criteria, expected evidence, ClickUp comment/field sync requirements, branch target, Story Points handling, `Definition` link, final Documentation needs, and validation requirements.
82
- - On initial pickup, rewrite the ClickUp task description with the complete current description of what must be done; do not rely on a status comment as the task description.
83
- - At plan completion, rewrite the ClickUp task description again with the complete final plan/Definition of what must be done, distinct from the plan comment.
84
- - Estimate Story Points during `plan`, write them to ClickUp `Story Points`, and re-estimate whenever material plan changes alter scope or risk.
85
- - Before assigning `CTO`/`PO` or any other next owner, remove the PM assignee from the ClickUp task and verify no task remains assigned to Workflow/Product Manager unless explicitly re-queued.
86
- - Keep `.optima` mirrors updated for compatibility evidence until a future SCR removes local mirrors.
64
+ - Orchestrate; do not silently implement specialist work yourself. Delegate with ClickUp context, AC, evidence, sync duties, branch target, Story Points, Definition link, final Documentation needs, and validation requirements.
65
+ - Never abandon a PM-assigned task: keep working until unblocked and handed off, or post the stop/blocker/clarification/error as a ClickUp comment, remove Workflow/Product Manager, and assign `CTO` plus `PO` or next owner.
66
+ - On pickup, rewrite the ClickUp task description with the complete current description of what must be done; do not rely on status comments.
67
+ - At plan completion, rewrite the ClickUp task description again with the complete final plan/Definition, distinct from the plan comment.
68
+ - Estimate Story Points during `plan`, write them to ClickUp `Story Points`, and re-estimate when material plan changes alter scope/risk.
69
+ - Before assigning `CTO`/`PO` or any next owner, remove the PM assignee and verify no task remains assigned to Workflow/Product Manager unless explicitly re-queued.
87
70
  - Final handoffs must include Summary, Work Performed, AC Coverage, Documentation Impact, Open Risks, Recommended Next Step, verification results, and commit/PR status.
88
71
 
89
72
  <include:plugin:Agents_Common.md>
package/dist/index.js CHANGED
@@ -7946,8 +7946,8 @@ var LEGACY_NOMADWORKS_DIRNAME = ".nomadworks";
7946
7946
  var LEGACY_ORBITA_DIRNAME = ".orbita";
7947
7947
  var LEGACY_STATICENG_DIRNAME = ".staticeng";
7948
7948
  var OPTIMA_GITIGNORE_RULES = [
7949
- ".optima/",
7950
- ".optima/.config/",
7949
+ ".optima/tasks/current.md",
7950
+ ".optima/.config/runtime/*",
7951
7951
  ".optima/**/.env",
7952
7952
  ".optima/**/.env.*",
7953
7953
  ".optima/**/*.env",
@@ -8497,9 +8497,11 @@ function buildClickUpSummaryPayload({ summaryMarkdown = "", summaryPath = "", ta
8497
8497
  ["Open Risks", sectionValue(summarySections, ["Open Risks"])],
8498
8498
  ["Recommended Next Step", sectionValue(summarySections, ["Recommended Next Step"])]
8499
8499
  ];
8500
+ const deliveryPath = summaryPath.includes("docs/delivery/") ? summaryPath : "";
8500
8501
  const contextParts = [
8501
8502
  branchValue ? `- Branch: ${compactMarkdownValue(branchValue)}` : null,
8502
8503
  worktreeValue ? `- Worktree: ${compactMarkdownValue(worktreeValue)}` : null,
8504
+ deliveryPath ? `- Delivery evidence: ${compactMarkdownValue(deliveryPath)}` : null,
8503
8505
  prValue ? `- PR: ${compactMarkdownValue(prValue)}` : null
8504
8506
  ].filter(Boolean);
8505
8507
  const comment = [
@@ -8524,7 +8526,8 @@ function buildClickUpSummaryPayload({ summaryMarkdown = "", summaryPath = "", ta
8524
8526
  recommended_next_step: compactMarkdownValue(sectionValue(summarySections, ["Recommended Next Step"])),
8525
8527
  branch: compactMarkdownValue(branchValue),
8526
8528
  worktree: compactMarkdownValue(worktreeValue),
8527
- pr: compactMarkdownValue(prValue)
8529
+ pr: compactMarkdownValue(prValue),
8530
+ delivery_evidence_path: compactMarkdownValue(deliveryPath)
8528
8531
  }
8529
8532
  },
8530
8533
  parsed: { summary: summarySections, task: taskSections }
@@ -8535,6 +8538,60 @@ function deriveClickUpWorktree({ baseWorktree = "", taskId, taskType, parentTask
8535
8538
  const root = baseWorktree || process.cwd();
8536
8539
  return path2.join(path2.dirname(root), `${path2.basename(root)}-${branch.replace(/\//g, "-")}`);
8537
8540
  }
8541
+ function clickUpCustomFieldValue(task = {}, names = []) {
8542
+ const fields = Array.isArray(task.custom_fields) ? task.custom_fields : [];
8543
+ const wanted = new Set(names.map((name) => normalizeLooseToken(name)));
8544
+ const field = fields.find((item) => wanted.has(normalizeLooseToken(item?.name || item?.id || "")));
8545
+ return field?.value ?? "";
8546
+ }
8547
+ function clickUpTaskType(task = {}, fallback = "Tarea") {
8548
+ return task.type || task.task_type || task.taskType || clickUpCustomFieldValue(task, ["Type", "Tipo", "Task Type"]) || fallback;
8549
+ }
8550
+ function clickUpParentTaskId(task = {}) {
8551
+ return task.parent || task.parent_id || task.parentId || task.parent_task_id || task.parentTaskId || "";
8552
+ }
8553
+ function deliveryEvidencePathForClickUpTask(taskId) {
8554
+ return `docs/delivery/${branchSafeClickUpId(taskId)}/SUMMARY.md`;
8555
+ }
8556
+ function metadataTaskRouting(metadata = {}) {
8557
+ return isPlainObject(metadata.task) ? metadata.task : {};
8558
+ }
8559
+ function safeExistingClickUpWorktree({ metadata = {}, branch = "" } = {}) {
8560
+ const taskMetadata = metadataTaskRouting(metadata);
8561
+ const existingWorktree = typeof taskMetadata.worktree === "string" ? taskMetadata.worktree.trim() : "";
8562
+ const existingBranch = typeof taskMetadata.branch === "string" ? taskMetadata.branch.trim() : "";
8563
+ if (!existingWorktree || !path2.isAbsolute(existingWorktree) || !fs2.existsSync(existingWorktree)) return null;
8564
+ try {
8565
+ const stat = fs2.statSync(existingWorktree);
8566
+ if (!stat.isDirectory()) return null;
8567
+ if (branch && existingBranch && existingBranch !== branch) return null;
8568
+ return { branch: existingBranch || branch, worktree: path2.resolve(existingWorktree), reused: true };
8569
+ } catch {
8570
+ return null;
8571
+ }
8572
+ }
8573
+ function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, allowNonGitFallback = false } = {}) {
8574
+ const effectiveParent = parentTaskId || taskId;
8575
+ const branch = deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
8576
+ const existing = safeExistingClickUpWorktree({ metadata: existingMetadata, branch });
8577
+ if (existing) return { ...existing, branch };
8578
+ const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
8579
+ if (fs2.existsSync(worktreePath)) return { branch, worktree: path2.resolve(worktreePath), reused: true };
8580
+ if (allowNonGitFallback) {
8581
+ fs2.mkdirSync(worktreePath, { recursive: true });
8582
+ return { branch, worktree: path2.resolve(worktreePath), reused: false, fallback: "non_git" };
8583
+ }
8584
+ const startPoint = (() => {
8585
+ try {
8586
+ runGitFn(baseWorktree, ["rev-parse", "--verify", "dev"]);
8587
+ return "dev";
8588
+ } catch {
8589
+ return "origin/dev";
8590
+ }
8591
+ })();
8592
+ runGitFn(baseWorktree, ["worktree", "add", "-b", branch, worktreePath, startPoint]);
8593
+ return { branch, worktree: path2.resolve(worktreePath), reused: false, startPoint };
8594
+ }
8538
8595
  function normalizeClickUpDefinitionDocParent(parent = {}) {
8539
8596
  const docId = String(parent.doc_id || parent.docId || CLICKUP_DEFINITION_DOC_PARENT.doc_id).trim();
8540
8597
  const pageId = String(parent.page_id || parent.pageId || CLICKUP_DEFINITION_DOC_PARENT.page_id).trim();
@@ -8564,6 +8621,7 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
8564
8621
  worktree,
8565
8622
  mirror_task_path: `.optima/tasks/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}.md`,
8566
8623
  evidence_path: `.optima/evidences/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}/SUMMARY.md`,
8624
+ delivery_evidence_path: deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent),
8567
8625
  ...agentMetadata
8568
8626
  }
8569
8627
  });
@@ -8574,7 +8632,8 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
8574
8632
  required_first_actions: [
8575
8633
  "Create or reuse the task-specific branch/worktree before planning or writing local .optima mirrors.",
8576
8634
  `Use branch ${branch} from dev and worktree ${worktree} for this task workspace.`,
8577
- "Write planning state, .optima/tasks/current.md, task mirrors, and evidence only inside the task worktree.",
8635
+ "Write planning state, .optima/tasks/current.md, task mirrors, and local evidence only inside the task worktree.",
8636
+ `Write merge-trackable final evidence under ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}; .optima remains local mirror/staging.`,
8578
8637
  "Do not update the principal dev workspace .optima/tasks/current.md for ClickUp task planning.",
8579
8638
  "If another active task is present in current.md, move to or create this task worktree instead of stopping."
8580
8639
  ],
@@ -8587,13 +8646,14 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
8587
8646
  mirror: {
8588
8647
  taskPath: `.optima/tasks/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}.md`,
8589
8648
  evidenceSummaryPath: `.optima/evidences/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}/SUMMARY.md`,
8649
+ deliveryEvidencePath: deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent),
8590
8650
  workspace: "task_worktree_only",
8591
8651
  principalWorkspaceCurrentMd: "do_not_update",
8592
8652
  currentMdConflictPolicy: "move_to_or_create_task_worktree",
8593
8653
  wouldCreate: true
8594
8654
  },
8595
8655
  clickup: {
8596
- comment: `Starting task from dev. Branch: ${branch}. Worktree: ${worktree}.`,
8656
+ comment: `Starting task from dev. Branch: ${branch}. Worktree: ${worktree}. Delivery evidence: ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}.`,
8597
8657
  description,
8598
8658
  fields: {
8599
8659
  Definition: definitionContent,
@@ -9306,6 +9366,14 @@ function recordClickUpCommentVersionProcessed({ ledgerPath, key, taskId, eventTy
9306
9366
  function clickUpTaskIdFromPayload(payload = {}) {
9307
9367
  return String(payload.task_id || payload.taskId || payload.task?.id || payload.task?.task_id || "").trim();
9308
9368
  }
9369
+ function clickUpTaskName(task = {}) {
9370
+ return String(task?.name || "").trim();
9371
+ }
9372
+ function formatClickUpSessionTitle({ taskId, payloadTask, task } = {}) {
9373
+ const id = String(taskId || "").trim();
9374
+ const name = clickUpTaskName(payloadTask) || clickUpTaskName(task) || "ClickUp PM";
9375
+ return `[${id}]: ${name}`;
9376
+ }
9309
9377
  function clickUpEventType(payload = {}) {
9310
9378
  return String(payload.event || payload.event_type || payload.eventType || "").trim();
9311
9379
  }
@@ -9376,6 +9444,11 @@ function clickUpPendingSessionKey(metadataKey) {
9376
9444
  function mergeClickUpPendingSessionMetadata(existing, metadataKey, sessionId) {
9377
9445
  return mergeClickUpSessionMetadata(existing, clickUpPendingSessionKey(metadataKey), sessionId);
9378
9446
  }
9447
+ function setClickUpTaskRoutingMetadata(existing, routing = {}) {
9448
+ const root = normalizeAgentMetadataJson(existing);
9449
+ root.task = { ...isPlainObject(root.task) ? root.task : {}, ...routing };
9450
+ return JSON.stringify(sortJsonValue(root), null, 2);
9451
+ }
9379
9452
  function setClickUpSessionMetadata(existing, metadataKey, sessionId) {
9380
9453
  const root = normalizeAgentMetadataJson(existing);
9381
9454
  const parts = String(metadataKey || CLICKUP_PM_METADATA_KEY).split(".").filter(Boolean);
@@ -9470,16 +9543,20 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
9470
9543
  if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, fetchImpl });
9471
9544
  throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
9472
9545
  }
9473
- function formatClickUpWebhookPrompt({ eventType, taskId, payload }) {
9546
+ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", worktree = "", deliveryEvidencePath = "" }) {
9474
9547
  const comment = clickUpCommentFromPayload(payload);
9475
9548
  const commentText = clickUpCommentText(comment).trim();
9476
9549
  return [
9477
9550
  "[ClickUp Webhook Event]",
9478
9551
  `Event: ${eventType}`,
9479
9552
  `Task ID: ${taskId}`,
9553
+ branch ? `Branch: ${branch}` : null,
9554
+ worktree ? `Worktree: ${worktree}` : null,
9555
+ deliveryEvidencePath ? `Delivery evidence path: ${deliveryEvidencePath}` : null,
9480
9556
  commentText ? `Comment: ${commentText}` : null,
9481
9557
  "",
9482
- "Handle this ClickUp Product Manager event using the ClickUp-first workflow rules. Do not assume broad chat routing; this event passed Optima webhook gates."
9558
+ "Handle this ClickUp Product Manager event using the ClickUp-first workflow rules. Do not assume broad chat routing; this event passed Optima webhook gates.",
9559
+ deliveryEvidencePath ? `Final merge-trackable evidence must be written under ${deliveryEvidencePath}; use .optima only as local mirror/staging.` : null
9483
9560
  ].filter((part) => part !== null).join("\n");
9484
9561
  }
9485
9562
  function appendClickUpWebhookLocalLog(worktree, entry) {
@@ -9658,7 +9735,7 @@ async function withClickUpTaskRouteLock(taskId, operation) {
9658
9735
  if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
9659
9736
  }
9660
9737
  }
9661
- async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
9738
+ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktree, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
9662
9739
  const eventType = clickUpEventType(payload);
9663
9740
  const eventKey = clickUpWebhookEventKey(payload);
9664
9741
  const remembered = rememberClickUpWebhookEvent(state, eventKey);
@@ -9702,14 +9779,35 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9702
9779
  const latestTask = await clickupClient.getTask(taskId);
9703
9780
  if (latestTask) task = latestTask;
9704
9781
  }
9782
+ const sessionTitle = formatClickUpSessionTitle({ taskId, payloadTask: payload.task, task });
9705
9783
  const existingMetadata = clickUpTaskAgentMetadata(task, config.routing.metadataFieldId);
9706
9784
  const metadata = normalizeAgentMetadataJson(existingMetadata);
9707
9785
  const existingSessionId = getNestedMetadataValue(metadata, config.routing.metadataKey);
9708
- const prompt = formatClickUpWebhookPrompt({ eventType, taskId, payload });
9786
+ const taskType = clickUpTaskType(task);
9787
+ const parentTaskId = clickUpParentTaskId(task) || taskId;
9788
+ const subtaskId = parentTaskId && parentTaskId !== taskId ? taskId : "";
9789
+ let taskRoute;
9790
+ try {
9791
+ taskRoute = ensureTaskWorktree({ baseWorktree: config.basePath, taskId, taskType, parentTaskId, subtaskId, existingMetadata: metadata, allowNonGitFallback: process.env.NODE_ENV === "test" });
9792
+ } catch (error) {
9793
+ const message = `Optima webhook could not create or reuse a task worktree for ${taskId}: ${error.message}`;
9794
+ appendClickUpWebhookLocalLog(worktree, { type: "task_worktree_failed", taskId, message });
9795
+ if (typeof clickupClient?.postTaskComment === "function") await clickupClient.postTaskComment({ taskId, comment: message });
9796
+ return { ok: false, action: "error", reason: "task_worktree_failed", taskId, message };
9797
+ }
9798
+ const deliveryEvidencePath = deliveryEvidencePathForClickUpTask(taskId);
9799
+ const routingMetadata = {
9800
+ branch: taskRoute.branch,
9801
+ worktree: taskRoute.worktree,
9802
+ delivery_evidence_path: deliveryEvidencePath,
9803
+ evidence_path: `.optima/evidences/${branchSafeClickUpId(taskId)}/SUMMARY.md`
9804
+ };
9805
+ const metadataWithRouting = setClickUpTaskRoutingMetadata(existingMetadata, routingMetadata);
9806
+ const prompt = formatClickUpWebhookPrompt({ eventType, taskId, payload, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath });
9709
9807
  if (!existingSessionId) {
9710
9808
  let pendingSessionId = getNestedMetadataValue(metadata, clickUpPendingSessionKey(config.routing.metadataKey)) || state.pendingSessions?.[taskId];
9711
9809
  if (!pendingSessionId) {
9712
- pendingSessionId = await createSession(openCodeClient, { title: `ClickUp PM: ${taskId}`, directory: config.basePath, agent: config.routing.targetAgent });
9810
+ pendingSessionId = await createSession(openCodeClient, { title: sessionTitle, directory: taskRoute.worktree, agent: config.routing.targetAgent });
9713
9811
  if (!String(pendingSessionId || "").startsWith("ses_")) return { ok: false, action: "error", reason: "session_create_failed", taskId };
9714
9812
  if (saveState) {
9715
9813
  saveState({
@@ -9718,14 +9816,14 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9718
9816
  });
9719
9817
  }
9720
9818
  }
9721
- const pendingMetadata = mergeClickUpPendingSessionMetadata(existingMetadata, config.routing.metadataKey, pendingSessionId);
9819
+ const pendingMetadata = mergeClickUpPendingSessionMetadata(metadataWithRouting, config.routing.metadataKey, pendingSessionId);
9722
9820
  try {
9723
9821
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: pendingMetadata });
9724
9822
  } catch (error) {
9725
9823
  appendClickUpWebhookLocalLog(worktree, { type: "pending_session_metadata_failed", taskId, sessionId: pendingSessionId, message: error.message });
9726
9824
  throw error;
9727
9825
  }
9728
- await sendSessionEvent(openCodeClient, { sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: config.basePath, opencodeBaseUrl: config.opencode?.baseUrl });
9826
+ await sendSessionEvent(openCodeClient, { sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl });
9729
9827
  const nextMetadata = clearClickUpPendingSessionMetadata(setClickUpSessionMetadata(pendingMetadata, config.routing.metadataKey, pendingSessionId), config.routing.metadataKey);
9730
9828
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
9731
9829
  const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
@@ -9734,7 +9832,8 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9734
9832
  }
9735
9833
  const sessionId = String(existingSessionId);
9736
9834
  if (await sessionExists(openCodeClient, sessionId)) {
9737
- await sendSessionEvent(openCodeClient, { sessionId, agent: config.routing.targetAgent, text: prompt, directory: config.basePath, opencodeBaseUrl: config.opencode?.baseUrl });
9835
+ if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
9836
+ await sendSessionEvent(openCodeClient, { sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl });
9738
9837
  return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey });
9739
9838
  }
9740
9839
  const at = now().toISOString();
@@ -11506,7 +11605,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
11506
11605
  }
11507
11606
  };
11508
11607
  }
11509
- OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11608
+ OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11510
11609
  export {
11511
11610
  OptimaPlugin as default
11512
11611
  };
@@ -7953,8 +7953,8 @@ var LEGACY_NOMADWORKS_DIRNAME = ".nomadworks";
7953
7953
  var LEGACY_ORBITA_DIRNAME = ".orbita";
7954
7954
  var LEGACY_STATICENG_DIRNAME = ".staticeng";
7955
7955
  var OPTIMA_GITIGNORE_RULES = [
7956
- ".optima/",
7957
- ".optima/.config/",
7956
+ ".optima/tasks/current.md",
7957
+ ".optima/.config/runtime/*",
7958
7958
  ".optima/**/.env",
7959
7959
  ".optima/**/.env.*",
7960
7960
  ".optima/**/*.env",
@@ -8504,9 +8504,11 @@ function buildClickUpSummaryPayload({ summaryMarkdown = "", summaryPath = "", ta
8504
8504
  ["Open Risks", sectionValue(summarySections, ["Open Risks"])],
8505
8505
  ["Recommended Next Step", sectionValue(summarySections, ["Recommended Next Step"])]
8506
8506
  ];
8507
+ const deliveryPath = summaryPath.includes("docs/delivery/") ? summaryPath : "";
8507
8508
  const contextParts = [
8508
8509
  branchValue ? `- Branch: ${compactMarkdownValue(branchValue)}` : null,
8509
8510
  worktreeValue ? `- Worktree: ${compactMarkdownValue(worktreeValue)}` : null,
8511
+ deliveryPath ? `- Delivery evidence: ${compactMarkdownValue(deliveryPath)}` : null,
8510
8512
  prValue ? `- PR: ${compactMarkdownValue(prValue)}` : null
8511
8513
  ].filter(Boolean);
8512
8514
  const comment = [
@@ -8531,7 +8533,8 @@ function buildClickUpSummaryPayload({ summaryMarkdown = "", summaryPath = "", ta
8531
8533
  recommended_next_step: compactMarkdownValue(sectionValue(summarySections, ["Recommended Next Step"])),
8532
8534
  branch: compactMarkdownValue(branchValue),
8533
8535
  worktree: compactMarkdownValue(worktreeValue),
8534
- pr: compactMarkdownValue(prValue)
8536
+ pr: compactMarkdownValue(prValue),
8537
+ delivery_evidence_path: compactMarkdownValue(deliveryPath)
8535
8538
  }
8536
8539
  },
8537
8540
  parsed: { summary: summarySections, task: taskSections }
@@ -8542,6 +8545,60 @@ function deriveClickUpWorktree({ baseWorktree = "", taskId, taskType, parentTask
8542
8545
  const root = baseWorktree || process.cwd();
8543
8546
  return path2.join(path2.dirname(root), `${path2.basename(root)}-${branch.replace(/\//g, "-")}`);
8544
8547
  }
8548
+ function clickUpCustomFieldValue(task = {}, names = []) {
8549
+ const fields = Array.isArray(task.custom_fields) ? task.custom_fields : [];
8550
+ const wanted = new Set(names.map((name) => normalizeLooseToken(name)));
8551
+ const field = fields.find((item) => wanted.has(normalizeLooseToken(item?.name || item?.id || "")));
8552
+ return field?.value ?? "";
8553
+ }
8554
+ function clickUpTaskType(task = {}, fallback = "Tarea") {
8555
+ return task.type || task.task_type || task.taskType || clickUpCustomFieldValue(task, ["Type", "Tipo", "Task Type"]) || fallback;
8556
+ }
8557
+ function clickUpParentTaskId(task = {}) {
8558
+ return task.parent || task.parent_id || task.parentId || task.parent_task_id || task.parentTaskId || "";
8559
+ }
8560
+ function deliveryEvidencePathForClickUpTask(taskId) {
8561
+ return `docs/delivery/${branchSafeClickUpId(taskId)}/SUMMARY.md`;
8562
+ }
8563
+ function metadataTaskRouting(metadata = {}) {
8564
+ return isPlainObject(metadata.task) ? metadata.task : {};
8565
+ }
8566
+ function safeExistingClickUpWorktree({ metadata = {}, branch = "" } = {}) {
8567
+ const taskMetadata = metadataTaskRouting(metadata);
8568
+ const existingWorktree = typeof taskMetadata.worktree === "string" ? taskMetadata.worktree.trim() : "";
8569
+ const existingBranch = typeof taskMetadata.branch === "string" ? taskMetadata.branch.trim() : "";
8570
+ if (!existingWorktree || !path2.isAbsolute(existingWorktree) || !fs2.existsSync(existingWorktree)) return null;
8571
+ try {
8572
+ const stat = fs2.statSync(existingWorktree);
8573
+ if (!stat.isDirectory()) return null;
8574
+ if (branch && existingBranch && existingBranch !== branch) return null;
8575
+ return { branch: existingBranch || branch, worktree: path2.resolve(existingWorktree), reused: true };
8576
+ } catch {
8577
+ return null;
8578
+ }
8579
+ }
8580
+ function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, allowNonGitFallback = false } = {}) {
8581
+ const effectiveParent = parentTaskId || taskId;
8582
+ const branch = deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
8583
+ const existing = safeExistingClickUpWorktree({ metadata: existingMetadata, branch });
8584
+ if (existing) return { ...existing, branch };
8585
+ const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
8586
+ if (fs2.existsSync(worktreePath)) return { branch, worktree: path2.resolve(worktreePath), reused: true };
8587
+ if (allowNonGitFallback) {
8588
+ fs2.mkdirSync(worktreePath, { recursive: true });
8589
+ return { branch, worktree: path2.resolve(worktreePath), reused: false, fallback: "non_git" };
8590
+ }
8591
+ const startPoint = (() => {
8592
+ try {
8593
+ runGitFn(baseWorktree, ["rev-parse", "--verify", "dev"]);
8594
+ return "dev";
8595
+ } catch {
8596
+ return "origin/dev";
8597
+ }
8598
+ })();
8599
+ runGitFn(baseWorktree, ["worktree", "add", "-b", branch, worktreePath, startPoint]);
8600
+ return { branch, worktree: path2.resolve(worktreePath), reused: false, startPoint };
8601
+ }
8545
8602
  function normalizeClickUpDefinitionDocParent(parent = {}) {
8546
8603
  const docId = String(parent.doc_id || parent.docId || CLICKUP_DEFINITION_DOC_PARENT.doc_id).trim();
8547
8604
  const pageId = String(parent.page_id || parent.pageId || CLICKUP_DEFINITION_DOC_PARENT.page_id).trim();
@@ -8571,6 +8628,7 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
8571
8628
  worktree,
8572
8629
  mirror_task_path: `.optima/tasks/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}.md`,
8573
8630
  evidence_path: `.optima/evidences/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}/SUMMARY.md`,
8631
+ delivery_evidence_path: deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent),
8574
8632
  ...agentMetadata
8575
8633
  }
8576
8634
  });
@@ -8581,7 +8639,8 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
8581
8639
  required_first_actions: [
8582
8640
  "Create or reuse the task-specific branch/worktree before planning or writing local .optima mirrors.",
8583
8641
  `Use branch ${branch} from dev and worktree ${worktree} for this task workspace.`,
8584
- "Write planning state, .optima/tasks/current.md, task mirrors, and evidence only inside the task worktree.",
8642
+ "Write planning state, .optima/tasks/current.md, task mirrors, and local evidence only inside the task worktree.",
8643
+ `Write merge-trackable final evidence under ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}; .optima remains local mirror/staging.`,
8585
8644
  "Do not update the principal dev workspace .optima/tasks/current.md for ClickUp task planning.",
8586
8645
  "If another active task is present in current.md, move to or create this task worktree instead of stopping."
8587
8646
  ],
@@ -8594,13 +8653,14 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
8594
8653
  mirror: {
8595
8654
  taskPath: `.optima/tasks/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}.md`,
8596
8655
  evidenceSummaryPath: `.optima/evidences/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}/SUMMARY.md`,
8656
+ deliveryEvidencePath: deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent),
8597
8657
  workspace: "task_worktree_only",
8598
8658
  principalWorkspaceCurrentMd: "do_not_update",
8599
8659
  currentMdConflictPolicy: "move_to_or_create_task_worktree",
8600
8660
  wouldCreate: true
8601
8661
  },
8602
8662
  clickup: {
8603
- comment: `Starting task from dev. Branch: ${branch}. Worktree: ${worktree}.`,
8663
+ comment: `Starting task from dev. Branch: ${branch}. Worktree: ${worktree}. Delivery evidence: ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}.`,
8604
8664
  description,
8605
8665
  fields: {
8606
8666
  Definition: definitionContent,
@@ -9313,6 +9373,14 @@ function recordClickUpCommentVersionProcessed({ ledgerPath, key, taskId, eventTy
9313
9373
  function clickUpTaskIdFromPayload(payload = {}) {
9314
9374
  return String(payload.task_id || payload.taskId || payload.task?.id || payload.task?.task_id || "").trim();
9315
9375
  }
9376
+ function clickUpTaskName(task = {}) {
9377
+ return String(task?.name || "").trim();
9378
+ }
9379
+ function formatClickUpSessionTitle({ taskId, payloadTask, task } = {}) {
9380
+ const id = String(taskId || "").trim();
9381
+ const name = clickUpTaskName(payloadTask) || clickUpTaskName(task) || "ClickUp PM";
9382
+ return `[${id}]: ${name}`;
9383
+ }
9316
9384
  function clickUpEventType(payload = {}) {
9317
9385
  return String(payload.event || payload.event_type || payload.eventType || "").trim();
9318
9386
  }
@@ -9383,6 +9451,11 @@ function clickUpPendingSessionKey(metadataKey) {
9383
9451
  function mergeClickUpPendingSessionMetadata(existing, metadataKey, sessionId) {
9384
9452
  return mergeClickUpSessionMetadata(existing, clickUpPendingSessionKey(metadataKey), sessionId);
9385
9453
  }
9454
+ function setClickUpTaskRoutingMetadata(existing, routing = {}) {
9455
+ const root = normalizeAgentMetadataJson(existing);
9456
+ root.task = { ...isPlainObject(root.task) ? root.task : {}, ...routing };
9457
+ return JSON.stringify(sortJsonValue(root), null, 2);
9458
+ }
9386
9459
  function setClickUpSessionMetadata(existing, metadataKey, sessionId) {
9387
9460
  const root = normalizeAgentMetadataJson(existing);
9388
9461
  const parts = String(metadataKey || CLICKUP_PM_METADATA_KEY).split(".").filter(Boolean);
@@ -9477,16 +9550,20 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
9477
9550
  if (directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, fetchImpl });
9478
9551
  throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
9479
9552
  }
9480
- function formatClickUpWebhookPrompt({ eventType, taskId, payload }) {
9553
+ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", worktree = "", deliveryEvidencePath = "" }) {
9481
9554
  const comment = clickUpCommentFromPayload(payload);
9482
9555
  const commentText = clickUpCommentText(comment).trim();
9483
9556
  return [
9484
9557
  "[ClickUp Webhook Event]",
9485
9558
  `Event: ${eventType}`,
9486
9559
  `Task ID: ${taskId}`,
9560
+ branch ? `Branch: ${branch}` : null,
9561
+ worktree ? `Worktree: ${worktree}` : null,
9562
+ deliveryEvidencePath ? `Delivery evidence path: ${deliveryEvidencePath}` : null,
9487
9563
  commentText ? `Comment: ${commentText}` : null,
9488
9564
  "",
9489
- "Handle this ClickUp Product Manager event using the ClickUp-first workflow rules. Do not assume broad chat routing; this event passed Optima webhook gates."
9565
+ "Handle this ClickUp Product Manager event using the ClickUp-first workflow rules. Do not assume broad chat routing; this event passed Optima webhook gates.",
9566
+ deliveryEvidencePath ? `Final merge-trackable evidence must be written under ${deliveryEvidencePath}; use .optima only as local mirror/staging.` : null
9490
9567
  ].filter((part) => part !== null).join("\n");
9491
9568
  }
9492
9569
  function appendClickUpWebhookLocalLog(worktree, entry) {
@@ -9665,7 +9742,7 @@ async function withClickUpTaskRouteLock(taskId, operation) {
9665
9742
  if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
9666
9743
  }
9667
9744
  }
9668
- async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
9745
+ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktree, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
9669
9746
  const eventType = clickUpEventType(payload);
9670
9747
  const eventKey = clickUpWebhookEventKey(payload);
9671
9748
  const remembered = rememberClickUpWebhookEvent(state, eventKey);
@@ -9709,14 +9786,35 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9709
9786
  const latestTask = await clickupClient.getTask(taskId);
9710
9787
  if (latestTask) task = latestTask;
9711
9788
  }
9789
+ const sessionTitle = formatClickUpSessionTitle({ taskId, payloadTask: payload.task, task });
9712
9790
  const existingMetadata = clickUpTaskAgentMetadata(task, config.routing.metadataFieldId);
9713
9791
  const metadata = normalizeAgentMetadataJson(existingMetadata);
9714
9792
  const existingSessionId = getNestedMetadataValue(metadata, config.routing.metadataKey);
9715
- const prompt = formatClickUpWebhookPrompt({ eventType, taskId, payload });
9793
+ const taskType = clickUpTaskType(task);
9794
+ const parentTaskId = clickUpParentTaskId(task) || taskId;
9795
+ const subtaskId = parentTaskId && parentTaskId !== taskId ? taskId : "";
9796
+ let taskRoute;
9797
+ try {
9798
+ taskRoute = ensureTaskWorktree({ baseWorktree: config.basePath, taskId, taskType, parentTaskId, subtaskId, existingMetadata: metadata, allowNonGitFallback: process.env.NODE_ENV === "test" });
9799
+ } catch (error) {
9800
+ const message = `Optima webhook could not create or reuse a task worktree for ${taskId}: ${error.message}`;
9801
+ appendClickUpWebhookLocalLog(worktree, { type: "task_worktree_failed", taskId, message });
9802
+ if (typeof clickupClient?.postTaskComment === "function") await clickupClient.postTaskComment({ taskId, comment: message });
9803
+ return { ok: false, action: "error", reason: "task_worktree_failed", taskId, message };
9804
+ }
9805
+ const deliveryEvidencePath = deliveryEvidencePathForClickUpTask(taskId);
9806
+ const routingMetadata = {
9807
+ branch: taskRoute.branch,
9808
+ worktree: taskRoute.worktree,
9809
+ delivery_evidence_path: deliveryEvidencePath,
9810
+ evidence_path: `.optima/evidences/${branchSafeClickUpId(taskId)}/SUMMARY.md`
9811
+ };
9812
+ const metadataWithRouting = setClickUpTaskRoutingMetadata(existingMetadata, routingMetadata);
9813
+ const prompt = formatClickUpWebhookPrompt({ eventType, taskId, payload, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath });
9716
9814
  if (!existingSessionId) {
9717
9815
  let pendingSessionId = getNestedMetadataValue(metadata, clickUpPendingSessionKey(config.routing.metadataKey)) || state.pendingSessions?.[taskId];
9718
9816
  if (!pendingSessionId) {
9719
- pendingSessionId = await createSession(openCodeClient, { title: `ClickUp PM: ${taskId}`, directory: config.basePath, agent: config.routing.targetAgent });
9817
+ pendingSessionId = await createSession(openCodeClient, { title: sessionTitle, directory: taskRoute.worktree, agent: config.routing.targetAgent });
9720
9818
  if (!String(pendingSessionId || "").startsWith("ses_")) return { ok: false, action: "error", reason: "session_create_failed", taskId };
9721
9819
  if (saveState) {
9722
9820
  saveState({
@@ -9725,14 +9823,14 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9725
9823
  });
9726
9824
  }
9727
9825
  }
9728
- const pendingMetadata = mergeClickUpPendingSessionMetadata(existingMetadata, config.routing.metadataKey, pendingSessionId);
9826
+ const pendingMetadata = mergeClickUpPendingSessionMetadata(metadataWithRouting, config.routing.metadataKey, pendingSessionId);
9729
9827
  try {
9730
9828
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: pendingMetadata });
9731
9829
  } catch (error) {
9732
9830
  appendClickUpWebhookLocalLog(worktree, { type: "pending_session_metadata_failed", taskId, sessionId: pendingSessionId, message: error.message });
9733
9831
  throw error;
9734
9832
  }
9735
- await sendSessionEvent(openCodeClient, { sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: config.basePath, opencodeBaseUrl: config.opencode?.baseUrl });
9833
+ await sendSessionEvent(openCodeClient, { sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl });
9736
9834
  const nextMetadata = clearClickUpPendingSessionMetadata(setClickUpSessionMetadata(pendingMetadata, config.routing.metadataKey, pendingSessionId), config.routing.metadataKey);
9737
9835
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
9738
9836
  const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
@@ -9741,7 +9839,8 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9741
9839
  }
9742
9840
  const sessionId = String(existingSessionId);
9743
9841
  if (await sessionExists(openCodeClient, sessionId)) {
9744
- await sendSessionEvent(openCodeClient, { sessionId, agent: config.routing.targetAgent, text: prompt, directory: config.basePath, opencodeBaseUrl: config.opencode?.baseUrl });
9842
+ if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
9843
+ await sendSessionEvent(openCodeClient, { sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl });
9745
9844
  return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey });
9746
9845
  }
9747
9846
  const at = now().toISOString();
@@ -11513,7 +11612,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
11513
11612
  }
11514
11613
  };
11515
11614
  }
11516
- OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11615
+ OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11517
11616
 
11518
11617
  // src/sanitize_cli.js
11519
11618
  var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defend-tech/opencode-optima",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"