@defend-tech/opencode-optima 0.1.70 → 0.1.72
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/assets/agents/workflow_product_manager.md +7 -3
- package/dist/index.js +125 -20
- package/dist/sanitize_cli.js +125 -20
- package/docs/guides/AGENTS.md +11 -0
- package/package.json +1 -1
- package/templates/optima.yaml.template +19 -0
|
@@ -45,8 +45,9 @@ You are Workflow_Product_Manager, Optima's ClickUp-first delivery orchestrator.
|
|
|
45
45
|
- `backlog`: ignore until prioritized.
|
|
46
46
|
- `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.
|
|
47
47
|
- `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, or access after local/subagent resolution attempts; do not stop with phase language such as "I reached phase 1" or "no non-human assignee is available".
|
|
48
|
-
- `validation`:
|
|
49
|
-
-
|
|
48
|
+
- `validation`: before moving any task/subtask into Validation, create or update the required GitHub PR and store its URL/number in ClickUp `agent_metadata`. Subtasks PR from their subtask branch into the parent task branch. Parent tasks PR from the task branch into `dev`. A task in Validation without an open PR is invalid: move it back to `in progress`, open/update the PR, 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.
|
|
49
|
+
- GitHub review wakeups: keep listening for PR review/comment webhooks. Reply in GitHub to human comments. 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, 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; never post Optima runtime/process noise.
|
|
50
|
+
- `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.
|
|
50
51
|
- `completed` / `Closed`: no execution unless explicitly reopened.
|
|
51
52
|
|
|
52
53
|
## Git, Worktree, PR
|
|
@@ -60,7 +61,10 @@ You are Workflow_Product_Manager, Optima's ClickUp-first delivery orchestrator.
|
|
|
60
61
|
- Unrelated active tasks in `.optima/tasks/current.md` must not block planning; move to/create the correct task worktree instead.
|
|
61
62
|
- Parent setup pulls remote once; after parent branch creation, subtasks can trust the parent local branch without continuous remote polling.
|
|
62
63
|
- Branches: parent `<clickup-task-type>/<parent-task-id>`; subtask `<clickup-task-type>/<parent-task-id>-subtask-<subtask-id>`; pending planned subtasks `<clickup-task-type>/<parent-task-id>-pending-<title-slug>`; PoC always `poc/<clickup-task-id>` and remains there unless productized later.
|
|
63
|
-
- PR targets/start points: subtask -> parent branch and starts from the parent branch; if parent branch/worktree is missing, bootstrap the parent from `dev`/`origin/dev` first
|
|
64
|
+
- PR targets/start points: subtask -> parent branch and starts from the parent branch; if parent branch/worktree is missing, bootstrap the parent from `dev`/`origin/dev` first. Parent task -> `dev` through a GitHub PR before entering Validation. Release -> `dev` to `main` only after explicit approval.
|
|
65
|
+
- Required PR gate: every transition into `validation` must have an open GitHub PR. Do not mark Validation with only local commits, evidence, or ClickUp comments.
|
|
66
|
+
- Review/merge cleanup: after the configured final approver/CTO approves and the PR is merged, keep the OpenCode session ids in ClickUp `agent_metadata`; delete only the merged branch/worktree after Vercel preproduction and smoke regression pass. If the task is reopened later, recreate/register the worktree from metadata/branch context and resume the preserved sessions.
|
|
67
|
+
- Final completion gate: parent task completion requires merged PR, Vercel preproduction deployment verified, smoke/regression evidence captured, no open review-change requests, final ClickUp comment with result, and status `completed`.
|
|
64
68
|
- Preserve user work and unrelated dirty files. Stop and ask if unexpected changes appear.
|
|
65
69
|
|
|
66
70
|
## Operating Style
|
package/dist/index.js
CHANGED
|
@@ -8819,6 +8819,70 @@ function deriveClickUpPrTarget({ isRelease = false, parentTaskId, parentBranch,
|
|
|
8819
8819
|
}
|
|
8820
8820
|
return devBranch;
|
|
8821
8821
|
}
|
|
8822
|
+
function deriveClickUpRequiredPullRequest({ taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", branch = "", parentBranch = "", devBranch = "dev", mainBranch = "main", isRelease = false, githubRemote = "origin", prUrl = "", prNumber = "" } = {}) {
|
|
8823
|
+
const effectiveParent = parentTaskId || taskId;
|
|
8824
|
+
const isSubtask = isClickUpSubtaskRoute({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
|
|
8825
|
+
const sourceBranch = branch || deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
|
|
8826
|
+
const targetBranch = deriveClickUpPrTarget({
|
|
8827
|
+
isRelease,
|
|
8828
|
+
parentBranch: isSubtask ? parentBranch || deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent }) : parentBranch,
|
|
8829
|
+
parentTaskId: isSubtask ? effectiveParent : "",
|
|
8830
|
+
parentTaskType: taskType,
|
|
8831
|
+
taskType,
|
|
8832
|
+
devBranch,
|
|
8833
|
+
mainBranch
|
|
8834
|
+
});
|
|
8835
|
+
return {
|
|
8836
|
+
required: true,
|
|
8837
|
+
sourceBranch,
|
|
8838
|
+
targetBranch,
|
|
8839
|
+
githubRemote,
|
|
8840
|
+
prUrl: String(prUrl || "").trim(),
|
|
8841
|
+
prNumber: String(prNumber || "").trim(),
|
|
8842
|
+
scope: isSubtask ? "subtask_to_parent" : isRelease ? "release_to_main" : "task_to_dev",
|
|
8843
|
+
policy: "validation_requires_open_pull_request"
|
|
8844
|
+
};
|
|
8845
|
+
}
|
|
8846
|
+
function buildClickUpReviewAutomationPlan({ isSubtask = false, humanApprover = "CTO", preproductionProvider = "Vercel", preproductionEnvironment = "preproduction" } = {}) {
|
|
8847
|
+
return {
|
|
8848
|
+
github_webhooks: {
|
|
8849
|
+
listen_for: ["pull_request_review", "pull_request_review_comment", "issue_comment", "pull_request"],
|
|
8850
|
+
comment_policy: "reply_before_change_and_after_change",
|
|
8851
|
+
change_request_status: "in progress",
|
|
8852
|
+
after_fix_status: "validation",
|
|
8853
|
+
state_storage: "ClickUp agent_metadata.github"
|
|
8854
|
+
},
|
|
8855
|
+
review_comments: [
|
|
8856
|
+
"Answer GitHub comments directly in GitHub.",
|
|
8857
|
+
"If a comment implies a code/doc/test change, first reply with the intended action, move the ClickUp task/subtask to in progress, implement, push the same branch, move it back to validation, update the PR, then reply again with what changed.",
|
|
8858
|
+
"Also update ClickUp with human-readable model work status; runtime/process noise stays in local logs."
|
|
8859
|
+
],
|
|
8860
|
+
accepted_review: {
|
|
8861
|
+
accepted_by: humanApprover,
|
|
8862
|
+
trigger: "approved_pull_request_review",
|
|
8863
|
+
action: isSubtask ? "merge_subtask_pr_into_parent_branch" : "merge_parent_pr_into_dev",
|
|
8864
|
+
delete_source_branch_after_success: true,
|
|
8865
|
+
delete_worktree_after_success: true,
|
|
8866
|
+
preserve_opencode_sessions_in_agent_metadata: true
|
|
8867
|
+
},
|
|
8868
|
+
preproduction: isSubtask ? {
|
|
8869
|
+
required: false,
|
|
8870
|
+
reason: "Subtask merge stops at parent branch; parent task owns dev/preproduction."
|
|
8871
|
+
} : {
|
|
8872
|
+
required: true,
|
|
8873
|
+
provider: preproductionProvider,
|
|
8874
|
+
environment: preproductionEnvironment,
|
|
8875
|
+
verify_deployment_after_merge: true,
|
|
8876
|
+
run_smoke_regression: true,
|
|
8877
|
+
on_failure: "create_bug_subtasks_and_return_parent_to_in_progress",
|
|
8878
|
+
completion_gate: "preproduction_deployed_and_regression_passed"
|
|
8879
|
+
},
|
|
8880
|
+
completion: {
|
|
8881
|
+
final_clickup_comment_required: true,
|
|
8882
|
+
final_status: isSubtask ? "completed_or_parent_validation" : "completed"
|
|
8883
|
+
}
|
|
8884
|
+
};
|
|
8885
|
+
}
|
|
8822
8886
|
function validateMainWorkspaceBranchSafety({ currentBranch, requiredBranch = "dev", forbiddenBranch = "main" } = {}) {
|
|
8823
8887
|
const normalized = String(currentBranch ?? "").trim();
|
|
8824
8888
|
if (!normalized) {
|
|
@@ -9481,6 +9545,7 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9481
9545
|
const worktree = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
|
|
9482
9546
|
const prTarget = isSubtask ? parentBranch : "dev";
|
|
9483
9547
|
const startFrom = isSubtask ? parentBranch : "dev";
|
|
9548
|
+
const requiredPullRequest = deriveClickUpRequiredPullRequest({ taskId, taskType, parentTaskId: effectiveParent, subtaskId, branch, parentBranch });
|
|
9484
9549
|
const estimate = preEstimateClickUpWork({ complexity, filesChanged, acceptanceCriteria, unknowns });
|
|
9485
9550
|
const metadata = mergeClickUpAgentMetadata(existingAgentMetadata, {
|
|
9486
9551
|
task: {
|
|
@@ -9494,6 +9559,10 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9494
9559
|
mirror_task_path: `.optima/tasks/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}.md`,
|
|
9495
9560
|
evidence_path: `.optima/evidences/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}/SUMMARY.md`,
|
|
9496
9561
|
delivery_evidence_path: deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent),
|
|
9562
|
+
github: {
|
|
9563
|
+
required_pull_request: requiredPullRequest,
|
|
9564
|
+
review_automation: buildClickUpReviewAutomationPlan({ isSubtask })
|
|
9565
|
+
},
|
|
9497
9566
|
...agentMetadata
|
|
9498
9567
|
}
|
|
9499
9568
|
});
|
|
@@ -9516,6 +9585,10 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9516
9585
|
parentBranch: parentBranch || void 0,
|
|
9517
9586
|
prTarget
|
|
9518
9587
|
},
|
|
9588
|
+
github: {
|
|
9589
|
+
requiredPullRequest,
|
|
9590
|
+
reviewAutomation: buildClickUpReviewAutomationPlan({ isSubtask })
|
|
9591
|
+
},
|
|
9519
9592
|
mirror: {
|
|
9520
9593
|
taskPath: `.optima/tasks/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}.md`,
|
|
9521
9594
|
evidenceSummaryPath: `.optima/evidences/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}/SUMMARY.md`,
|
|
@@ -9526,14 +9599,15 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9526
9599
|
wouldCreate: true
|
|
9527
9600
|
},
|
|
9528
9601
|
clickup: {
|
|
9529
|
-
comment: `Starting task from ${startFrom}. Branch: ${branch}. Worktree: ${worktree}. Delivery evidence: ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}.`,
|
|
9602
|
+
comment: `Starting task from ${startFrom}. Branch: ${branch}. Worktree: ${worktree}. Validation PR target: ${requiredPullRequest.targetBranch}. Delivery evidence: ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}.`,
|
|
9530
9603
|
description,
|
|
9531
9604
|
fields: {
|
|
9532
9605
|
Definition: definitionContent,
|
|
9533
9606
|
"Story Points": estimate.storyPoints,
|
|
9534
9607
|
agent_metadata: metadata,
|
|
9535
9608
|
branch,
|
|
9536
|
-
worktree
|
|
9609
|
+
worktree,
|
|
9610
|
+
pr_target: requiredPullRequest.targetBranch
|
|
9537
9611
|
},
|
|
9538
9612
|
definition_doc: {
|
|
9539
9613
|
parent: normalizeClickUpDefinitionDocParent(definitionDocParent),
|
|
@@ -9543,23 +9617,29 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9543
9617
|
}
|
|
9544
9618
|
};
|
|
9545
9619
|
}
|
|
9546
|
-
function buildClickUpTransitionPayload({ fromStatus, toStatus, validationPassed = false, validationFailed = false, isSubtask = false, reopen = false, assignees = [], removeAssignees = [], productManagerAssignee = "", requireProductManagerAssignee = true, planDescription = "", definition = "", definitionDocParent = CLICKUP_DEFINITION_DOC_PARENT, humansRegistry } = {}) {
|
|
9620
|
+
function buildClickUpTransitionPayload({ fromStatus, toStatus, validationPassed = false, validationFailed = false, isSubtask = false, reopen = false, assignees = [], removeAssignees = [], productManagerAssignee = "", requireProductManagerAssignee = true, planDescription = "", definition = "", definitionDocParent = CLICKUP_DEFINITION_DOC_PARENT, humansRegistry, taskId = "", taskType = "Tarea", parentTaskId = "", subtaskId = "", branch = "", parentBranch = "", prUrl = "", prNumber = "", githubRemote = "origin", devBranch = "dev", mainBranch = "main", preproductionProvider = "Vercel", preproductionEnvironment = "preproduction" } = {}) {
|
|
9547
9621
|
const from = normalizeClickUpStatus(fromStatus);
|
|
9622
|
+
const effectiveIsSubtask = Boolean(isSubtask || isClickUpSubtaskRoute({ taskType, parentTaskId: parentTaskId || taskId, subtaskId, taskId }));
|
|
9548
9623
|
let to = normalizeClickUpStatus(toStatus);
|
|
9549
9624
|
if (validationFailed) to = "in progress";
|
|
9550
|
-
if (!to && validationPassed) to =
|
|
9625
|
+
if (!to && validationPassed) to = effectiveIsSubtask ? "completed" : "merge";
|
|
9551
9626
|
if ((from === "completed" || from === "closed") && !reopen) {
|
|
9552
9627
|
return { ok: true, noop: true, dryRun: true, message: "Completed/closed task is a no-op unless reopen is explicit." };
|
|
9553
9628
|
}
|
|
9554
9629
|
const key = `${from}->${to}`;
|
|
9555
9630
|
const rule = CLICKUP_TRANSITIONS.get(key);
|
|
9556
9631
|
if (!rule) return { ok: false, dryRun: true, message: `Transition not allowed: ${key}` };
|
|
9557
|
-
const assignsFinalApprovers = rule.assignFinalApprovers === true && !(rule.parentOnlyFinalApproval &&
|
|
9632
|
+
const assignsFinalApprovers = rule.assignFinalApprovers === true && !(rule.parentOnlyFinalApproval && effectiveIsSubtask);
|
|
9558
9633
|
const requiresPlanCompletionContract = from === "plan" && to === "in progress";
|
|
9634
|
+
const requiresValidationPullRequest = from === "in progress" && to === "validation";
|
|
9559
9635
|
const definitionContent = compactMarkdownValue(definition);
|
|
9560
9636
|
const description = compactMarkdownValue(planDescription);
|
|
9637
|
+
const canDerivePullRequest = Boolean(taskId || branch);
|
|
9638
|
+
const shouldAttachPullRequest = requiresValidationPullRequest || (from === "validation" || to === "merge" || from === "merge") && canDerivePullRequest;
|
|
9639
|
+
const requiredPullRequest = shouldAttachPullRequest && canDerivePullRequest ? deriveClickUpRequiredPullRequest({ taskId, taskType, parentTaskId, subtaskId, branch, parentBranch, devBranch, mainBranch, githubRemote, prUrl, prNumber }) : null;
|
|
9640
|
+
const reviewAutomation = requiredPullRequest ? buildClickUpReviewAutomationPlan({ isSubtask: effectiveIsSubtask, preproductionProvider, preproductionEnvironment }) : null;
|
|
9561
9641
|
const validationErrors = [];
|
|
9562
|
-
if (rule.parentOnlyFinalApproval &&
|
|
9642
|
+
if (rule.parentOnlyFinalApproval && effectiveIsSubtask) {
|
|
9563
9643
|
validationErrors.push("Subtasks must not use the parent final-approval transition; merge validated subtasks directly into the parent branch/workspace.");
|
|
9564
9644
|
}
|
|
9565
9645
|
if (assignsFinalApprovers && !isRealClickUpAssigneeId(productManagerAssignee) && requireProductManagerAssignee !== false) {
|
|
@@ -9569,19 +9649,34 @@ function buildClickUpTransitionPayload({ fromStatus, toStatus, validationPassed
|
|
|
9569
9649
|
if (!description) validationErrors.push("planDescription is required for the plan-completion ClickUp task description rewrite.");
|
|
9570
9650
|
if (!definitionContent) validationErrors.push("definition is required and must contain the complete plan/Definition content at plan completion.");
|
|
9571
9651
|
}
|
|
9652
|
+
if (requiresValidationPullRequest) {
|
|
9653
|
+
if (!requiredPullRequest) validationErrors.push("taskId or branch is required before moving a ClickUp task to validation.");
|
|
9654
|
+
if (!requiredPullRequest?.sourceBranch) validationErrors.push("branch/sourceBranch is required before moving a ClickUp task to validation.");
|
|
9655
|
+
if (!requiredPullRequest?.targetBranch) validationErrors.push("PR target branch is required before moving a ClickUp task to validation.");
|
|
9656
|
+
if (!requiredPullRequest?.prUrl && !requiredPullRequest?.prNumber) validationErrors.push("An open GitHub pull request URL or number is required before moving a ClickUp task to validation.");
|
|
9657
|
+
}
|
|
9572
9658
|
if (validationErrors.length > 0) return clickUpPayloadValidationError(validationErrors);
|
|
9573
9659
|
const finalApprovers = assignsFinalApprovers ? finalApprovalAssignees(CLICKUP_FINAL_APPROVER_ROLES, humansRegistry) : [];
|
|
9574
9660
|
const explicitRemovals = (removeAssignees || []).filter(Boolean);
|
|
9575
9661
|
const normalizedProductManagerAssignee = isRealClickUpAssigneeId(productManagerAssignee) ? String(productManagerAssignee).trim() : "";
|
|
9576
9662
|
const removalTargets = assignsFinalApprovers ? [...new Set([normalizedProductManagerAssignee, ...explicitRemovals].filter(Boolean))] : explicitRemovals;
|
|
9577
9663
|
const assigned = [...new Set([...assignees || [], ...finalApprovers].filter(Boolean))].filter((assignee) => !removalTargets.includes(assignee));
|
|
9578
|
-
const authority = from === "validation" || to === "merge" ? determineClickUpMergeAuthority({ isSubtask, clickupStatus: to, validationPassed: validationPassed || to === "merge", humansRegistry }) : null;
|
|
9664
|
+
const authority = from === "validation" || to === "merge" ? determineClickUpMergeAuthority({ isSubtask: effectiveIsSubtask, clickupStatus: to, validationPassed: validationPassed || to === "merge", humansRegistry }) : null;
|
|
9579
9665
|
const fields = authority ? { merge_authority: JSON.stringify(authority) } : {};
|
|
9666
|
+
if (requiredPullRequest) {
|
|
9667
|
+
fields.github_pull_request = JSON.stringify(requiredPullRequest);
|
|
9668
|
+
fields.github_review_automation = JSON.stringify(reviewAutomation);
|
|
9669
|
+
}
|
|
9580
9670
|
if (definitionContent) fields.Definition = definitionContent;
|
|
9581
9671
|
return {
|
|
9582
9672
|
ok: true,
|
|
9583
9673
|
dryRun: true,
|
|
9584
|
-
transition: { fromStatus: from, toStatus: rule.status, validationPassed, validationFailed, isSubtask },
|
|
9674
|
+
transition: { fromStatus: from, toStatus: rule.status, validationPassed, validationFailed, isSubtask: effectiveIsSubtask },
|
|
9675
|
+
github: requiredPullRequest ? {
|
|
9676
|
+
requiredPullRequest,
|
|
9677
|
+
reviewAutomation
|
|
9678
|
+
} : void 0,
|
|
9679
|
+
vercel: reviewAutomation?.preproduction?.required ? reviewAutomation.preproduction : void 0,
|
|
9585
9680
|
clickup: {
|
|
9586
9681
|
status: rule.status,
|
|
9587
9682
|
assignees: assigned,
|
|
@@ -10654,20 +10749,25 @@ function appendDirectoryQuery(url, directory) {
|
|
|
10654
10749
|
const separator = url.includes("?") ? "&" : "?";
|
|
10655
10750
|
return `${url}${separator}directory=${encodeURIComponent(value)}`;
|
|
10656
10751
|
}
|
|
10657
|
-
|
|
10752
|
+
function normalizeOpenCodePromptDelivery(value, fallback = "queue") {
|
|
10753
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
10754
|
+
return normalized === "steer" || normalized === "queue" ? normalized : fallback;
|
|
10755
|
+
}
|
|
10756
|
+
async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent, directory, fetchImpl = globalThis.fetch, legacyOnly = false, delivery = "queue" } = {}) {
|
|
10658
10757
|
if (typeof fetchImpl !== "function") throw new Error("OpenCode direct prompt delivery requires fetch.");
|
|
10659
10758
|
const root = normalizeOpenCodeBaseUrl(baseUrl, "");
|
|
10660
10759
|
if (!root) throw new Error("OpenCode direct prompt delivery requires a base URL.");
|
|
10661
10760
|
const encodedSession = encodeURIComponent(sessionId);
|
|
10761
|
+
const promptDelivery = normalizeOpenCodePromptDelivery(delivery, "queue");
|
|
10662
10762
|
const attempts = [
|
|
10663
10763
|
legacyOnly ? null : {
|
|
10664
10764
|
name: "v2 prompt",
|
|
10665
10765
|
url: appendDirectoryQuery(`${root}/api/session/${encodedSession}/prompt`, directory),
|
|
10666
|
-
body: { prompt: { text }, delivery:
|
|
10766
|
+
body: { prompt: { text }, delivery: promptDelivery, resume: true },
|
|
10667
10767
|
accept: (response, data) => {
|
|
10668
10768
|
if (!response.ok) return null;
|
|
10669
10769
|
if (!openCodePromptAdmissionVerification(data, sessionId)) throw new Error("OpenCode v2 prompt response did not include a valid prompt admission.");
|
|
10670
|
-
return { ok: true, method: "http", endpoint: "/api/session/{sessionID}/prompt", status: response.status, data: data.data, response: data };
|
|
10770
|
+
return { ok: true, method: "http", endpoint: "/api/session/{sessionID}/prompt", status: response.status, delivery: promptDelivery, data: data.data, response: data };
|
|
10671
10771
|
}
|
|
10672
10772
|
},
|
|
10673
10773
|
{
|
|
@@ -10723,7 +10823,7 @@ async function callOpenCodePromptWithFallbacks(method, sessionId, flatPayload, s
|
|
|
10723
10823
|
}
|
|
10724
10824
|
throw firstError;
|
|
10725
10825
|
}
|
|
10726
|
-
async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false, legacyOnly = false, allowDirectFallback = true } = {}) {
|
|
10826
|
+
async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false, legacyOnly = false, allowDirectFallback = true, directDelivery = "queue" } = {}) {
|
|
10727
10827
|
const directBaseUrl = opencodeBaseUrl || baseUrl;
|
|
10728
10828
|
const parts = [{ type: "text", text }];
|
|
10729
10829
|
const flatPayload = { directory, agent, parts };
|
|
@@ -10747,7 +10847,7 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
|
|
|
10747
10847
|
firstError ??= error;
|
|
10748
10848
|
}
|
|
10749
10849
|
}
|
|
10750
|
-
if (allowDirectFallback && directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, directory, fetchImpl, legacyOnly });
|
|
10850
|
+
if (allowDirectFallback && directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, directory, fetchImpl, legacyOnly, delivery: directDelivery });
|
|
10751
10851
|
if (firstError) throw firstError;
|
|
10752
10852
|
throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
|
|
10753
10853
|
}
|
|
@@ -11120,11 +11220,11 @@ function openCodeBlockingPromptVerification(result, sessionId) {
|
|
|
11120
11220
|
if (parts.length > 0 || messageId) return { ok: true, method: parts.length > 0 ? "blocking_prompt_parts" : "blocking_prompt_message", messageId: messageId ? String(messageId) : null, sessionId: deliveredSessionId ? String(deliveredSessionId) : String(sessionId), parts: parts.length };
|
|
11121
11221
|
return null;
|
|
11122
11222
|
}
|
|
11123
|
-
async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, directPrompt = false, acceptPromptAdmission = false, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, applyBlockerOnFailure = true } = {}) {
|
|
11223
|
+
async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, directPrompt = false, directDelivery = "queue", acceptPromptAdmission = false, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, applyBlockerOnFailure = true } = {}) {
|
|
11124
11224
|
const beforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, directory, limit: 50 }).catch(() => null);
|
|
11125
11225
|
let sendResult;
|
|
11126
11226
|
try {
|
|
11127
|
-
sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: directPrompt, allowDirectFallback: directPrompt });
|
|
11227
|
+
sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: directPrompt, directDelivery, allowDirectFallback: directPrompt });
|
|
11128
11228
|
} catch (error) {
|
|
11129
11229
|
const reason2 = error.message || "message_delivery_failed";
|
|
11130
11230
|
appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason: reason2, fallbackAttempted: false });
|
|
@@ -11147,7 +11247,13 @@ async function deliverClickUpSessionEventWithVerification({ openCodeClient, send
|
|
|
11147
11247
|
}
|
|
11148
11248
|
if (blockingPromptVerification) return { ok: true, verification: blockingPromptVerification, admissionVerification: null, fallback: false };
|
|
11149
11249
|
if (admissionVerification && acceptPromptAdmission) {
|
|
11150
|
-
|
|
11250
|
+
appendClickUpWebhookLocalLog(worktree, {
|
|
11251
|
+
type: "message_delivery_admission_not_sufficient",
|
|
11252
|
+
taskId,
|
|
11253
|
+
sessionId,
|
|
11254
|
+
admission: admissionVerification,
|
|
11255
|
+
policy: "clickup_routing_requires_visible_delivery"
|
|
11256
|
+
});
|
|
11151
11257
|
}
|
|
11152
11258
|
let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, directory, beforeMessages, expectedText: text, markers: eventMarkers });
|
|
11153
11259
|
if (verification?.ok) return { ok: true, verification, admissionVerification, fallback: false };
|
|
@@ -11550,10 +11656,9 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
11550
11656
|
const sessionId = String(existingSessionId);
|
|
11551
11657
|
if (await sessionExists(openCodeClient, sessionId, { directory: taskRoute.worktree })) {
|
|
11552
11658
|
if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
|
|
11553
|
-
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, directPrompt: config.opencode?.promptDelivery === "http", acceptPromptAdmission: config.opencode?.acceptPromptAdmission === true, eventMarkers: [taskId, eventType], verifySessionEventDelivery, applyBlockerOnFailure: false });
|
|
11659
|
+
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, directPrompt: config.opencode?.promptDelivery === "http", directDelivery: "steer", acceptPromptAdmission: config.opencode?.acceptPromptAdmission === true, eventMarkers: [taskId, eventType], verifySessionEventDelivery, applyBlockerOnFailure: false });
|
|
11554
11660
|
if (!delivery.ok) {
|
|
11555
|
-
|
|
11556
|
-
return finish(recovery2);
|
|
11661
|
+
return finish({ ...delivery, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, replacementAttempted: false });
|
|
11557
11662
|
}
|
|
11558
11663
|
return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, deliveryVerification: delivery.verification, deliveryAdmission: delivery.admissionVerification, deliveryFallback: delivery.fallback, deliveryAttempts: delivery.fallback ? 2 : 1 });
|
|
11559
11664
|
}
|
|
@@ -13766,7 +13871,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
13766
13871
|
}
|
|
13767
13872
|
};
|
|
13768
13873
|
}
|
|
13769
|
-
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, compactPromptPath, createClickUpApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpTaskWorktreeForWebhook, ensureClickUpTaskWorktreeOpenChamber, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, inspectOpenCodeSessionActivity, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, registerOpenChamberClickUpWorktree, scheduleClickUpAssignmentWatchdog, scheduleClickUpStartupReconciliation, syncOpenChamberWorktreeVisibility, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, 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 };
|
|
13874
|
+
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, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpTaskWorktreeForWebhook, ensureClickUpTaskWorktreeOpenChamber, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, inspectOpenCodeSessionActivity, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, registerOpenChamberClickUpWorktree, scheduleClickUpAssignmentWatchdog, scheduleClickUpStartupReconciliation, syncOpenChamberWorktreeVisibility, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, 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 };
|
|
13770
13875
|
export {
|
|
13771
13876
|
OptimaPlugin as default
|
|
13772
13877
|
};
|
package/dist/sanitize_cli.js
CHANGED
|
@@ -8826,6 +8826,70 @@ function deriveClickUpPrTarget({ isRelease = false, parentTaskId, parentBranch,
|
|
|
8826
8826
|
}
|
|
8827
8827
|
return devBranch;
|
|
8828
8828
|
}
|
|
8829
|
+
function deriveClickUpRequiredPullRequest({ taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", branch = "", parentBranch = "", devBranch = "dev", mainBranch = "main", isRelease = false, githubRemote = "origin", prUrl = "", prNumber = "" } = {}) {
|
|
8830
|
+
const effectiveParent = parentTaskId || taskId;
|
|
8831
|
+
const isSubtask = isClickUpSubtaskRoute({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
|
|
8832
|
+
const sourceBranch = branch || deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
|
|
8833
|
+
const targetBranch = deriveClickUpPrTarget({
|
|
8834
|
+
isRelease,
|
|
8835
|
+
parentBranch: isSubtask ? parentBranch || deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent }) : parentBranch,
|
|
8836
|
+
parentTaskId: isSubtask ? effectiveParent : "",
|
|
8837
|
+
parentTaskType: taskType,
|
|
8838
|
+
taskType,
|
|
8839
|
+
devBranch,
|
|
8840
|
+
mainBranch
|
|
8841
|
+
});
|
|
8842
|
+
return {
|
|
8843
|
+
required: true,
|
|
8844
|
+
sourceBranch,
|
|
8845
|
+
targetBranch,
|
|
8846
|
+
githubRemote,
|
|
8847
|
+
prUrl: String(prUrl || "").trim(),
|
|
8848
|
+
prNumber: String(prNumber || "").trim(),
|
|
8849
|
+
scope: isSubtask ? "subtask_to_parent" : isRelease ? "release_to_main" : "task_to_dev",
|
|
8850
|
+
policy: "validation_requires_open_pull_request"
|
|
8851
|
+
};
|
|
8852
|
+
}
|
|
8853
|
+
function buildClickUpReviewAutomationPlan({ isSubtask = false, humanApprover = "CTO", preproductionProvider = "Vercel", preproductionEnvironment = "preproduction" } = {}) {
|
|
8854
|
+
return {
|
|
8855
|
+
github_webhooks: {
|
|
8856
|
+
listen_for: ["pull_request_review", "pull_request_review_comment", "issue_comment", "pull_request"],
|
|
8857
|
+
comment_policy: "reply_before_change_and_after_change",
|
|
8858
|
+
change_request_status: "in progress",
|
|
8859
|
+
after_fix_status: "validation",
|
|
8860
|
+
state_storage: "ClickUp agent_metadata.github"
|
|
8861
|
+
},
|
|
8862
|
+
review_comments: [
|
|
8863
|
+
"Answer GitHub comments directly in GitHub.",
|
|
8864
|
+
"If a comment implies a code/doc/test change, first reply with the intended action, move the ClickUp task/subtask to in progress, implement, push the same branch, move it back to validation, update the PR, then reply again with what changed.",
|
|
8865
|
+
"Also update ClickUp with human-readable model work status; runtime/process noise stays in local logs."
|
|
8866
|
+
],
|
|
8867
|
+
accepted_review: {
|
|
8868
|
+
accepted_by: humanApprover,
|
|
8869
|
+
trigger: "approved_pull_request_review",
|
|
8870
|
+
action: isSubtask ? "merge_subtask_pr_into_parent_branch" : "merge_parent_pr_into_dev",
|
|
8871
|
+
delete_source_branch_after_success: true,
|
|
8872
|
+
delete_worktree_after_success: true,
|
|
8873
|
+
preserve_opencode_sessions_in_agent_metadata: true
|
|
8874
|
+
},
|
|
8875
|
+
preproduction: isSubtask ? {
|
|
8876
|
+
required: false,
|
|
8877
|
+
reason: "Subtask merge stops at parent branch; parent task owns dev/preproduction."
|
|
8878
|
+
} : {
|
|
8879
|
+
required: true,
|
|
8880
|
+
provider: preproductionProvider,
|
|
8881
|
+
environment: preproductionEnvironment,
|
|
8882
|
+
verify_deployment_after_merge: true,
|
|
8883
|
+
run_smoke_regression: true,
|
|
8884
|
+
on_failure: "create_bug_subtasks_and_return_parent_to_in_progress",
|
|
8885
|
+
completion_gate: "preproduction_deployed_and_regression_passed"
|
|
8886
|
+
},
|
|
8887
|
+
completion: {
|
|
8888
|
+
final_clickup_comment_required: true,
|
|
8889
|
+
final_status: isSubtask ? "completed_or_parent_validation" : "completed"
|
|
8890
|
+
}
|
|
8891
|
+
};
|
|
8892
|
+
}
|
|
8829
8893
|
function validateMainWorkspaceBranchSafety({ currentBranch, requiredBranch = "dev", forbiddenBranch = "main" } = {}) {
|
|
8830
8894
|
const normalized = String(currentBranch ?? "").trim();
|
|
8831
8895
|
if (!normalized) {
|
|
@@ -9488,6 +9552,7 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9488
9552
|
const worktree = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
|
|
9489
9553
|
const prTarget = isSubtask ? parentBranch : "dev";
|
|
9490
9554
|
const startFrom = isSubtask ? parentBranch : "dev";
|
|
9555
|
+
const requiredPullRequest = deriveClickUpRequiredPullRequest({ taskId, taskType, parentTaskId: effectiveParent, subtaskId, branch, parentBranch });
|
|
9491
9556
|
const estimate = preEstimateClickUpWork({ complexity, filesChanged, acceptanceCriteria, unknowns });
|
|
9492
9557
|
const metadata = mergeClickUpAgentMetadata(existingAgentMetadata, {
|
|
9493
9558
|
task: {
|
|
@@ -9501,6 +9566,10 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9501
9566
|
mirror_task_path: `.optima/tasks/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}.md`,
|
|
9502
9567
|
evidence_path: `.optima/evidences/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}/SUMMARY.md`,
|
|
9503
9568
|
delivery_evidence_path: deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent),
|
|
9569
|
+
github: {
|
|
9570
|
+
required_pull_request: requiredPullRequest,
|
|
9571
|
+
review_automation: buildClickUpReviewAutomationPlan({ isSubtask })
|
|
9572
|
+
},
|
|
9504
9573
|
...agentMetadata
|
|
9505
9574
|
}
|
|
9506
9575
|
});
|
|
@@ -9523,6 +9592,10 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9523
9592
|
parentBranch: parentBranch || void 0,
|
|
9524
9593
|
prTarget
|
|
9525
9594
|
},
|
|
9595
|
+
github: {
|
|
9596
|
+
requiredPullRequest,
|
|
9597
|
+
reviewAutomation: buildClickUpReviewAutomationPlan({ isSubtask })
|
|
9598
|
+
},
|
|
9526
9599
|
mirror: {
|
|
9527
9600
|
taskPath: `.optima/tasks/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}.md`,
|
|
9528
9601
|
evidenceSummaryPath: `.optima/evidences/${branchSafeClickUpId(taskId || subtaskId || effectiveParent)}/SUMMARY.md`,
|
|
@@ -9533,14 +9606,15 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9533
9606
|
wouldCreate: true
|
|
9534
9607
|
},
|
|
9535
9608
|
clickup: {
|
|
9536
|
-
comment: `Starting task from ${startFrom}. Branch: ${branch}. Worktree: ${worktree}. Delivery evidence: ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}.`,
|
|
9609
|
+
comment: `Starting task from ${startFrom}. Branch: ${branch}. Worktree: ${worktree}. Validation PR target: ${requiredPullRequest.targetBranch}. Delivery evidence: ${deliveryEvidencePathForClickUpTask(taskId || subtaskId || effectiveParent)}.`,
|
|
9537
9610
|
description,
|
|
9538
9611
|
fields: {
|
|
9539
9612
|
Definition: definitionContent,
|
|
9540
9613
|
"Story Points": estimate.storyPoints,
|
|
9541
9614
|
agent_metadata: metadata,
|
|
9542
9615
|
branch,
|
|
9543
|
-
worktree
|
|
9616
|
+
worktree,
|
|
9617
|
+
pr_target: requiredPullRequest.targetBranch
|
|
9544
9618
|
},
|
|
9545
9619
|
definition_doc: {
|
|
9546
9620
|
parent: normalizeClickUpDefinitionDocParent(definitionDocParent),
|
|
@@ -9550,23 +9624,29 @@ function buildClickUpStartTaskPayload({ taskId, taskType = "Tarea", parentTaskId
|
|
|
9550
9624
|
}
|
|
9551
9625
|
};
|
|
9552
9626
|
}
|
|
9553
|
-
function buildClickUpTransitionPayload({ fromStatus, toStatus, validationPassed = false, validationFailed = false, isSubtask = false, reopen = false, assignees = [], removeAssignees = [], productManagerAssignee = "", requireProductManagerAssignee = true, planDescription = "", definition = "", definitionDocParent = CLICKUP_DEFINITION_DOC_PARENT, humansRegistry } = {}) {
|
|
9627
|
+
function buildClickUpTransitionPayload({ fromStatus, toStatus, validationPassed = false, validationFailed = false, isSubtask = false, reopen = false, assignees = [], removeAssignees = [], productManagerAssignee = "", requireProductManagerAssignee = true, planDescription = "", definition = "", definitionDocParent = CLICKUP_DEFINITION_DOC_PARENT, humansRegistry, taskId = "", taskType = "Tarea", parentTaskId = "", subtaskId = "", branch = "", parentBranch = "", prUrl = "", prNumber = "", githubRemote = "origin", devBranch = "dev", mainBranch = "main", preproductionProvider = "Vercel", preproductionEnvironment = "preproduction" } = {}) {
|
|
9554
9628
|
const from = normalizeClickUpStatus(fromStatus);
|
|
9629
|
+
const effectiveIsSubtask = Boolean(isSubtask || isClickUpSubtaskRoute({ taskType, parentTaskId: parentTaskId || taskId, subtaskId, taskId }));
|
|
9555
9630
|
let to = normalizeClickUpStatus(toStatus);
|
|
9556
9631
|
if (validationFailed) to = "in progress";
|
|
9557
|
-
if (!to && validationPassed) to =
|
|
9632
|
+
if (!to && validationPassed) to = effectiveIsSubtask ? "completed" : "merge";
|
|
9558
9633
|
if ((from === "completed" || from === "closed") && !reopen) {
|
|
9559
9634
|
return { ok: true, noop: true, dryRun: true, message: "Completed/closed task is a no-op unless reopen is explicit." };
|
|
9560
9635
|
}
|
|
9561
9636
|
const key = `${from}->${to}`;
|
|
9562
9637
|
const rule = CLICKUP_TRANSITIONS.get(key);
|
|
9563
9638
|
if (!rule) return { ok: false, dryRun: true, message: `Transition not allowed: ${key}` };
|
|
9564
|
-
const assignsFinalApprovers = rule.assignFinalApprovers === true && !(rule.parentOnlyFinalApproval &&
|
|
9639
|
+
const assignsFinalApprovers = rule.assignFinalApprovers === true && !(rule.parentOnlyFinalApproval && effectiveIsSubtask);
|
|
9565
9640
|
const requiresPlanCompletionContract = from === "plan" && to === "in progress";
|
|
9641
|
+
const requiresValidationPullRequest = from === "in progress" && to === "validation";
|
|
9566
9642
|
const definitionContent = compactMarkdownValue(definition);
|
|
9567
9643
|
const description = compactMarkdownValue(planDescription);
|
|
9644
|
+
const canDerivePullRequest = Boolean(taskId || branch);
|
|
9645
|
+
const shouldAttachPullRequest = requiresValidationPullRequest || (from === "validation" || to === "merge" || from === "merge") && canDerivePullRequest;
|
|
9646
|
+
const requiredPullRequest = shouldAttachPullRequest && canDerivePullRequest ? deriveClickUpRequiredPullRequest({ taskId, taskType, parentTaskId, subtaskId, branch, parentBranch, devBranch, mainBranch, githubRemote, prUrl, prNumber }) : null;
|
|
9647
|
+
const reviewAutomation = requiredPullRequest ? buildClickUpReviewAutomationPlan({ isSubtask: effectiveIsSubtask, preproductionProvider, preproductionEnvironment }) : null;
|
|
9568
9648
|
const validationErrors = [];
|
|
9569
|
-
if (rule.parentOnlyFinalApproval &&
|
|
9649
|
+
if (rule.parentOnlyFinalApproval && effectiveIsSubtask) {
|
|
9570
9650
|
validationErrors.push("Subtasks must not use the parent final-approval transition; merge validated subtasks directly into the parent branch/workspace.");
|
|
9571
9651
|
}
|
|
9572
9652
|
if (assignsFinalApprovers && !isRealClickUpAssigneeId(productManagerAssignee) && requireProductManagerAssignee !== false) {
|
|
@@ -9576,19 +9656,34 @@ function buildClickUpTransitionPayload({ fromStatus, toStatus, validationPassed
|
|
|
9576
9656
|
if (!description) validationErrors.push("planDescription is required for the plan-completion ClickUp task description rewrite.");
|
|
9577
9657
|
if (!definitionContent) validationErrors.push("definition is required and must contain the complete plan/Definition content at plan completion.");
|
|
9578
9658
|
}
|
|
9659
|
+
if (requiresValidationPullRequest) {
|
|
9660
|
+
if (!requiredPullRequest) validationErrors.push("taskId or branch is required before moving a ClickUp task to validation.");
|
|
9661
|
+
if (!requiredPullRequest?.sourceBranch) validationErrors.push("branch/sourceBranch is required before moving a ClickUp task to validation.");
|
|
9662
|
+
if (!requiredPullRequest?.targetBranch) validationErrors.push("PR target branch is required before moving a ClickUp task to validation.");
|
|
9663
|
+
if (!requiredPullRequest?.prUrl && !requiredPullRequest?.prNumber) validationErrors.push("An open GitHub pull request URL or number is required before moving a ClickUp task to validation.");
|
|
9664
|
+
}
|
|
9579
9665
|
if (validationErrors.length > 0) return clickUpPayloadValidationError(validationErrors);
|
|
9580
9666
|
const finalApprovers = assignsFinalApprovers ? finalApprovalAssignees(CLICKUP_FINAL_APPROVER_ROLES, humansRegistry) : [];
|
|
9581
9667
|
const explicitRemovals = (removeAssignees || []).filter(Boolean);
|
|
9582
9668
|
const normalizedProductManagerAssignee = isRealClickUpAssigneeId(productManagerAssignee) ? String(productManagerAssignee).trim() : "";
|
|
9583
9669
|
const removalTargets = assignsFinalApprovers ? [...new Set([normalizedProductManagerAssignee, ...explicitRemovals].filter(Boolean))] : explicitRemovals;
|
|
9584
9670
|
const assigned = [...new Set([...assignees || [], ...finalApprovers].filter(Boolean))].filter((assignee) => !removalTargets.includes(assignee));
|
|
9585
|
-
const authority = from === "validation" || to === "merge" ? determineClickUpMergeAuthority({ isSubtask, clickupStatus: to, validationPassed: validationPassed || to === "merge", humansRegistry }) : null;
|
|
9671
|
+
const authority = from === "validation" || to === "merge" ? determineClickUpMergeAuthority({ isSubtask: effectiveIsSubtask, clickupStatus: to, validationPassed: validationPassed || to === "merge", humansRegistry }) : null;
|
|
9586
9672
|
const fields = authority ? { merge_authority: JSON.stringify(authority) } : {};
|
|
9673
|
+
if (requiredPullRequest) {
|
|
9674
|
+
fields.github_pull_request = JSON.stringify(requiredPullRequest);
|
|
9675
|
+
fields.github_review_automation = JSON.stringify(reviewAutomation);
|
|
9676
|
+
}
|
|
9587
9677
|
if (definitionContent) fields.Definition = definitionContent;
|
|
9588
9678
|
return {
|
|
9589
9679
|
ok: true,
|
|
9590
9680
|
dryRun: true,
|
|
9591
|
-
transition: { fromStatus: from, toStatus: rule.status, validationPassed, validationFailed, isSubtask },
|
|
9681
|
+
transition: { fromStatus: from, toStatus: rule.status, validationPassed, validationFailed, isSubtask: effectiveIsSubtask },
|
|
9682
|
+
github: requiredPullRequest ? {
|
|
9683
|
+
requiredPullRequest,
|
|
9684
|
+
reviewAutomation
|
|
9685
|
+
} : void 0,
|
|
9686
|
+
vercel: reviewAutomation?.preproduction?.required ? reviewAutomation.preproduction : void 0,
|
|
9592
9687
|
clickup: {
|
|
9593
9688
|
status: rule.status,
|
|
9594
9689
|
assignees: assigned,
|
|
@@ -10661,20 +10756,25 @@ function appendDirectoryQuery(url, directory) {
|
|
|
10661
10756
|
const separator = url.includes("?") ? "&" : "?";
|
|
10662
10757
|
return `${url}${separator}directory=${encodeURIComponent(value)}`;
|
|
10663
10758
|
}
|
|
10664
|
-
|
|
10759
|
+
function normalizeOpenCodePromptDelivery(value, fallback = "queue") {
|
|
10760
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
10761
|
+
return normalized === "steer" || normalized === "queue" ? normalized : fallback;
|
|
10762
|
+
}
|
|
10763
|
+
async function sendOpenCodeSessionEventDirect({ baseUrl, sessionId, text, agent, directory, fetchImpl = globalThis.fetch, legacyOnly = false, delivery = "queue" } = {}) {
|
|
10665
10764
|
if (typeof fetchImpl !== "function") throw new Error("OpenCode direct prompt delivery requires fetch.");
|
|
10666
10765
|
const root = normalizeOpenCodeBaseUrl(baseUrl, "");
|
|
10667
10766
|
if (!root) throw new Error("OpenCode direct prompt delivery requires a base URL.");
|
|
10668
10767
|
const encodedSession = encodeURIComponent(sessionId);
|
|
10768
|
+
const promptDelivery = normalizeOpenCodePromptDelivery(delivery, "queue");
|
|
10669
10769
|
const attempts = [
|
|
10670
10770
|
legacyOnly ? null : {
|
|
10671
10771
|
name: "v2 prompt",
|
|
10672
10772
|
url: appendDirectoryQuery(`${root}/api/session/${encodedSession}/prompt`, directory),
|
|
10673
|
-
body: { prompt: { text }, delivery:
|
|
10773
|
+
body: { prompt: { text }, delivery: promptDelivery, resume: true },
|
|
10674
10774
|
accept: (response, data) => {
|
|
10675
10775
|
if (!response.ok) return null;
|
|
10676
10776
|
if (!openCodePromptAdmissionVerification(data, sessionId)) throw new Error("OpenCode v2 prompt response did not include a valid prompt admission.");
|
|
10677
|
-
return { ok: true, method: "http", endpoint: "/api/session/{sessionID}/prompt", status: response.status, data: data.data, response: data };
|
|
10777
|
+
return { ok: true, method: "http", endpoint: "/api/session/{sessionID}/prompt", status: response.status, delivery: promptDelivery, data: data.data, response: data };
|
|
10678
10778
|
}
|
|
10679
10779
|
},
|
|
10680
10780
|
{
|
|
@@ -10730,7 +10830,7 @@ async function callOpenCodePromptWithFallbacks(method, sessionId, flatPayload, s
|
|
|
10730
10830
|
}
|
|
10731
10831
|
throw firstError;
|
|
10732
10832
|
}
|
|
10733
|
-
async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false, legacyOnly = false, allowDirectFallback = true } = {}) {
|
|
10833
|
+
async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory, opencodeBaseUrl, baseUrl, fetchImpl, direct = false, legacyOnly = false, allowDirectFallback = true, directDelivery = "queue" } = {}) {
|
|
10734
10834
|
const directBaseUrl = opencodeBaseUrl || baseUrl;
|
|
10735
10835
|
const parts = [{ type: "text", text }];
|
|
10736
10836
|
const flatPayload = { directory, agent, parts };
|
|
@@ -10754,7 +10854,7 @@ async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, direct
|
|
|
10754
10854
|
firstError ??= error;
|
|
10755
10855
|
}
|
|
10756
10856
|
}
|
|
10757
|
-
if (allowDirectFallback && directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, directory, fetchImpl, legacyOnly });
|
|
10857
|
+
if (allowDirectFallback && directBaseUrl) return sendOpenCodeSessionEventDirect({ baseUrl: directBaseUrl, sessionId, text, agent, directory, fetchImpl, legacyOnly, delivery: directDelivery });
|
|
10758
10858
|
if (firstError) throw firstError;
|
|
10759
10859
|
throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
|
|
10760
10860
|
}
|
|
@@ -11127,11 +11227,11 @@ function openCodeBlockingPromptVerification(result, sessionId) {
|
|
|
11127
11227
|
if (parts.length > 0 || messageId) return { ok: true, method: parts.length > 0 ? "blocking_prompt_parts" : "blocking_prompt_message", messageId: messageId ? String(messageId) : null, sessionId: deliveredSessionId ? String(deliveredSessionId) : String(sessionId), parts: parts.length };
|
|
11128
11228
|
return null;
|
|
11129
11229
|
}
|
|
11130
|
-
async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, directPrompt = false, acceptPromptAdmission = false, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, applyBlockerOnFailure = true } = {}) {
|
|
11230
|
+
async function deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent, text, directory, opencodeBaseUrl, directPrompt = false, directDelivery = "queue", acceptPromptAdmission = false, eventMarkers = [], verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, applyBlockerOnFailure = true } = {}) {
|
|
11131
11231
|
const beforeMessages = await readOpenCodeSessionMessages(openCodeClient, { sessionId, directory, limit: 50 }).catch(() => null);
|
|
11132
11232
|
let sendResult;
|
|
11133
11233
|
try {
|
|
11134
|
-
sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: directPrompt, allowDirectFallback: directPrompt });
|
|
11234
|
+
sendResult = await sendSessionEvent(openCodeClient, { sessionId, agent, text, directory, opencodeBaseUrl, direct: directPrompt, directDelivery, allowDirectFallback: directPrompt });
|
|
11135
11235
|
} catch (error) {
|
|
11136
11236
|
const reason2 = error.message || "message_delivery_failed";
|
|
11137
11237
|
appendClickUpWebhookLocalLog(worktree, { type: "message_delivery_failed", taskId, sessionId, reason: reason2, fallbackAttempted: false });
|
|
@@ -11154,7 +11254,13 @@ async function deliverClickUpSessionEventWithVerification({ openCodeClient, send
|
|
|
11154
11254
|
}
|
|
11155
11255
|
if (blockingPromptVerification) return { ok: true, verification: blockingPromptVerification, admissionVerification: null, fallback: false };
|
|
11156
11256
|
if (admissionVerification && acceptPromptAdmission) {
|
|
11157
|
-
|
|
11257
|
+
appendClickUpWebhookLocalLog(worktree, {
|
|
11258
|
+
type: "message_delivery_admission_not_sufficient",
|
|
11259
|
+
taskId,
|
|
11260
|
+
sessionId,
|
|
11261
|
+
admission: admissionVerification,
|
|
11262
|
+
policy: "clickup_routing_requires_visible_delivery"
|
|
11263
|
+
});
|
|
11158
11264
|
}
|
|
11159
11265
|
let verification = await verifySessionEventDelivery(openCodeClient, { sessionId, directory, beforeMessages, expectedText: text, markers: eventMarkers });
|
|
11160
11266
|
if (verification?.ok) return { ok: true, verification, admissionVerification, fallback: false };
|
|
@@ -11557,10 +11663,9 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
11557
11663
|
const sessionId = String(existingSessionId);
|
|
11558
11664
|
if (await sessionExists(openCodeClient, sessionId, { directory: taskRoute.worktree })) {
|
|
11559
11665
|
if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
|
|
11560
|
-
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, directPrompt: config.opencode?.promptDelivery === "http", acceptPromptAdmission: config.opencode?.acceptPromptAdmission === true, eventMarkers: [taskId, eventType], verifySessionEventDelivery, applyBlockerOnFailure: false });
|
|
11666
|
+
const delivery = await deliverClickUpSessionEventWithVerification({ openCodeClient, sendSessionEvent, clickupClient, worktree, taskId, sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl, directPrompt: config.opencode?.promptDelivery === "http", directDelivery: "steer", acceptPromptAdmission: config.opencode?.acceptPromptAdmission === true, eventMarkers: [taskId, eventType], verifySessionEventDelivery, applyBlockerOnFailure: false });
|
|
11561
11667
|
if (!delivery.ok) {
|
|
11562
|
-
|
|
11563
|
-
return finish(recovery2);
|
|
11668
|
+
return finish({ ...delivery, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, replacementAttempted: false });
|
|
11564
11669
|
}
|
|
11565
11670
|
return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path, deliveryVerification: delivery.verification, deliveryAdmission: delivery.admissionVerification, deliveryFallback: delivery.fallback, deliveryAttempts: delivery.fallback ? 2 : 1 });
|
|
11566
11671
|
}
|
|
@@ -13773,7 +13878,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
13773
13878
|
}
|
|
13774
13879
|
};
|
|
13775
13880
|
}
|
|
13776
|
-
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, compactPromptPath, createClickUpApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpTaskWorktreeForWebhook, ensureClickUpTaskWorktreeOpenChamber, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, inspectOpenCodeSessionActivity, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, registerOpenChamberClickUpWorktree, scheduleClickUpAssignmentWatchdog, scheduleClickUpStartupReconciliation, syncOpenChamberWorktreeVisibility, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, 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 };
|
|
13881
|
+
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, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpTaskWorktreeForWebhook, ensureClickUpTaskWorktreeOpenChamber, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, inspectOpenCodeSessionActivity, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, registerOpenChamberClickUpWorktree, scheduleClickUpAssignmentWatchdog, scheduleClickUpStartupReconciliation, syncOpenChamberWorktreeVisibility, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, 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 };
|
|
13777
13882
|
|
|
13778
13883
|
// src/sanitize_cli.js
|
|
13779
13884
|
var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
|
package/docs/guides/AGENTS.md
CHANGED
|
@@ -61,3 +61,14 @@ Use shared policies and additive agent files by default. Do not create or popula
|
|
|
61
61
|
- PMA links the task to an approved SCR.
|
|
62
62
|
- Architect helps decompose the work into slice-based subtasks.
|
|
63
63
|
- `workflow_runner` executes the end-to-end delivery cycle through specialist delegation while PMA waits for completion notification.
|
|
64
|
+
|
|
65
|
+
## ClickUp-First PR And Preproduction Gate
|
|
66
|
+
|
|
67
|
+
For ClickUp-first delivery, Validation is a GitHub PR state, not a comment-only handoff.
|
|
68
|
+
|
|
69
|
+
- Subtasks open/update a PR from the subtask branch into the parent task branch before entering Validation.
|
|
70
|
+
- Parent tasks open/update a PR from the task branch into `dev` before entering Validation.
|
|
71
|
+
- GitHub review/comment webhooks wake the workflow owner. The agent replies in GitHub; if a comment requires a change, it moves ClickUp back to `in progress`, fixes/pushes the same branch, returns ClickUp to `validation`, updates the PR, and replies again with the result.
|
|
72
|
+
- The configured final approver/CTO approving the parent PR is the merge trigger. After merge to `dev`, Vercel preproduction must deploy automatically and pass a small smoke/regression check before cleanup and ClickUp `completed`.
|
|
73
|
+
- If merge, Vercel deployment, or regression fails, create Bug subtasks under the parent and return the parent to `in progress`.
|
|
74
|
+
- Worktrees/branches may be deleted only after the merge and preproduction gate pass. OpenCode session ids remain in ClickUp `agent_metadata` so a reopened task can recreate the worktree and resume context.
|
package/package.json
CHANGED
|
@@ -67,5 +67,24 @@ workflow:
|
|
|
67
67
|
subtask: parent_branch
|
|
68
68
|
parent: dev
|
|
69
69
|
release: main
|
|
70
|
+
validation_requires_pull_request: true # A task/subtask cannot enter Validation without an open GitHub PR
|
|
71
|
+
review_webhooks:
|
|
72
|
+
enabled: true
|
|
73
|
+
events:
|
|
74
|
+
- pull_request_review
|
|
75
|
+
- pull_request_review_comment
|
|
76
|
+
- issue_comment
|
|
77
|
+
- pull_request
|
|
78
|
+
comment_policy: reply_before_change_and_after_change
|
|
79
|
+
change_request_status: in progress
|
|
80
|
+
after_fix_status: validation
|
|
81
|
+
accepted_by: CTO
|
|
82
|
+
vercel:
|
|
83
|
+
preproduction:
|
|
84
|
+
required_for_parent_tasks: true
|
|
85
|
+
environment: preproduction
|
|
86
|
+
verify_deployment_after_merge: true
|
|
87
|
+
smoke_regression_required: true
|
|
88
|
+
on_failure: create_bug_subtasks_and_return_parent_to_in_progress
|
|
70
89
|
|
|
71
90
|
agents:
|