@runfusion/fusion 0.75.0-beta.1 → 0.75.0-beta.2

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.
Files changed (136) hide show
  1. package/dist/bin.js +389 -134
  2. package/dist/child-process-worker.js +224 -123
  3. package/dist/client/.vite/manifest.json +249 -249
  4. package/dist/client/assets/{AgentDetailView-CsCVX96A.js → AgentDetailView-C4Xpag_i.js} +1 -1
  5. package/dist/client/assets/{AgentPermissionPolicyEditor-Dh5wDe8g.js → AgentPermissionPolicyEditor-D-Fp7-sy.js} +1 -1
  6. package/dist/client/assets/{AgentsView-Dd_oZbFn.js → AgentsView-BmFsoY1Q.js} +3 -3
  7. package/dist/client/assets/{ChatView-gC7_BCmZ.js → ChatView-5KsB3BVO.js} +1 -1
  8. package/dist/client/assets/{CommandCenter-Bk6RQ-hm.js → CommandCenter-CacGI8AV.js} +1 -1
  9. package/dist/client/assets/{DevServerView-Da8h5OC-.js → DevServerView-DufjauVn.js} +1 -1
  10. package/dist/client/assets/{DirectoryPicker-DX9H1EdH.js → DirectoryPicker-DvwqfQDS.js} +1 -1
  11. package/dist/client/assets/{DocumentsView-BZ0g_Am7.js → DocumentsView-BbYJFIQ1.js} +1 -1
  12. package/dist/client/assets/{EvalsView-DsI_X6eQ.js → EvalsView-NBC8kOc-.js} +1 -1
  13. package/dist/client/assets/{ExperimentalAgentOnboardingModal-BfE5f-XK.js → ExperimentalAgentOnboardingModal-C809yXXY.js} +1 -1
  14. package/dist/client/assets/{GoalsView-43sqRFfC.js → GoalsView-CpNO9eI0.js} +1 -1
  15. package/dist/client/assets/{InsightsView-Dvgn_hrA.js → InsightsView-Cahy5FcR.js} +1 -1
  16. package/dist/client/assets/{MemoryView-DWbHznu3.js → MemoryView-BNJAQjqn.js} +1 -1
  17. package/dist/client/assets/{PiExtensionsManager-BKfmIRDt.js → PiExtensionsManager-DCHTyOmJ.js} +1 -1
  18. package/dist/client/assets/{PluginManager-xhkrAjSK.js → PluginManager-CBt-CqUz.js} +1 -1
  19. package/dist/client/assets/{PullRequestView-CyEwGgRD.js → PullRequestView-Dsb9RwmX.js} +1 -1
  20. package/dist/client/assets/{ReportModal--NSmch5M.js → ReportModal-8FjZOcbC.js} +1 -1
  21. package/dist/client/assets/{ResearchView-CgqZlNMD.js → ResearchView-DlSG7dOd.js} +1 -1
  22. package/dist/client/assets/{SecretsView-am_ZT9z8.js → SecretsView-BSajNE-f.js} +1 -1
  23. package/dist/client/assets/{SessionTerminal-DSc_7NQ-.js → SessionTerminal-M2fIpHrL.js} +1 -1
  24. package/dist/client/assets/{SettingsModal-Bp6sytUe.js → SettingsModal-DLcvlp44.js} +3 -3
  25. package/dist/client/assets/{SettingsModal-D2jjC8m8.js → SettingsModal-z6gOf7dG.js} +1 -1
  26. package/dist/client/assets/{SettingsTextareaRow-DjJJD-Af.js → SettingsTextareaRow-DeC19053.js} +1 -1
  27. package/dist/client/assets/{SetupWizardModal-DLIeefbh.js → SetupWizardModal-BOjMePFh.js} +2 -2
  28. package/dist/client/assets/{SkillsView-C67Gdb5y.js → SkillsView-BSBEC-Xm.js} +1 -1
  29. package/dist/client/assets/{WorkflowNodeEditor-BxhdNPdn.js → WorkflowNodeEditor-DZ9-RBqo.js} +1 -1
  30. package/dist/client/assets/{agent-import-generation-Bk9fEowy.js → agent-import-generation-COItiXMT.js} +1 -1
  31. package/dist/client/assets/{architectureDiagram-3BPJPVTR-s2YViNU1.js → architectureDiagram-3BPJPVTR-BwlCePoB.js} +1 -1
  32. package/dist/client/assets/{blockDiagram-GPEHLZMM-DMsf6VWl.js → blockDiagram-GPEHLZMM-KKrLi9v4.js} +1 -1
  33. package/dist/client/assets/{c4Diagram-AAUBKEIU-DXJExfmV.js → c4Diagram-AAUBKEIU-CkIDt_8M.js} +1 -1
  34. package/dist/client/assets/channel-CeYcnOtQ.js +1 -0
  35. package/dist/client/assets/{chunk-2J33WTMH-CH99GAl7.js → chunk-2J33WTMH-D9sbdaqY.js} +1 -1
  36. package/dist/client/assets/{chunk-4BX2VUAB-BTV9SXg5.js → chunk-4BX2VUAB-D3DtFoAY.js} +1 -1
  37. package/dist/client/assets/{chunk-55IACEB6-ntvLX3jz.js → chunk-55IACEB6-gxblxRIN.js} +1 -1
  38. package/dist/client/assets/{chunk-727SXJPM-DBNBtu-e.js → chunk-727SXJPM-D0AEDoHN.js} +1 -1
  39. package/dist/client/assets/{chunk-AQP2D5EJ-DvbtWh_x.js → chunk-AQP2D5EJ-BweoM7qY.js} +1 -1
  40. package/dist/client/assets/{chunk-FMBD7UC4-CUdRlAnf.js → chunk-FMBD7UC4-sjblhrjI.js} +1 -1
  41. package/dist/client/assets/{chunk-ND2GUHAM-B7J-met1.js → chunk-ND2GUHAM-CYuRENSs.js} +1 -1
  42. package/dist/client/assets/{chunk-QZHKN3VN-D7CahWp7.js → chunk-QZHKN3VN-CFKJr4kG.js} +1 -1
  43. package/dist/client/assets/classDiagram-4FO5ZUOK-CZwh2k1X.js +1 -0
  44. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CZwh2k1X.js +1 -0
  45. package/dist/client/assets/{cose-bilkent-S5V4N54A-6E0Aaw7K.js → cose-bilkent-S5V4N54A-6Jb6SgHM.js} +1 -1
  46. package/dist/client/assets/{dagre-BM42HDAG-Wi236AfH.js → dagre-BM42HDAG-FQJ87Ol3.js} +1 -1
  47. package/dist/client/assets/{dashboard-view-CrHqKsfL.js → dashboard-view-B1Rq2-73.js} +1 -1
  48. package/dist/client/assets/{dashboard-view-NpkFaG9s.js → dashboard-view-DH1Zo0m4.js} +1 -1
  49. package/dist/client/assets/{dashboard-view-hLsXVJJN.js → dashboard-view-DVMZNVoc.js} +1 -1
  50. package/dist/client/assets/{dashboard-view-BZDf9tzC.js → dashboard-view-DgLbkn5n.js} +1 -1
  51. package/dist/client/assets/{diagram-2AECGRRQ-B3TxtVcq.js → diagram-2AECGRRQ-CKYOu9to.js} +1 -1
  52. package/dist/client/assets/{diagram-5GNKFQAL-45Y_FHjp.js → diagram-5GNKFQAL-DPwjFnde.js} +1 -1
  53. package/dist/client/assets/{diagram-KO2AKTUF-D4w3m4cp.js → diagram-KO2AKTUF-DzZS9Nk3.js} +1 -1
  54. package/dist/client/assets/{diagram-LMA3HP47-Dv5F4ha2.js → diagram-LMA3HP47-DW9e5YSP.js} +1 -1
  55. package/dist/client/assets/{diagram-OG6HWLK6-CvOQ1IYN.js → diagram-OG6HWLK6-C7V3Mj63.js} +1 -1
  56. package/dist/client/assets/{erDiagram-TEJ5UH35-DYcp25dX.js → erDiagram-TEJ5UH35-DSQzBA0e.js} +1 -1
  57. package/dist/client/assets/{flowDiagram-I6XJVG4X-DnjuETCJ.js → flowDiagram-I6XJVG4X-BUuOq9tO.js} +1 -1
  58. package/dist/client/assets/{folder-open-ovJcpdOX.js → folder-open-DXNu4ED4.js} +1 -1
  59. package/dist/client/assets/{ganttDiagram-6RSMTGT7-SU7_CTa4.js → ganttDiagram-6RSMTGT7-Bc32G_Ef.js} +1 -1
  60. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DFvxZu8I.js → gitGraphDiagram-PVQCEYII-Cvfa0W6a.js} +1 -1
  61. package/dist/client/assets/{index-C_ZHzU4F.js → index-C5YdAqNQ.js} +14 -14
  62. package/dist/client/assets/index-DHk_s6rA.css +1 -0
  63. package/dist/client/assets/{infoDiagram-5YYISTIA-DgMEfdKk.js → infoDiagram-5YYISTIA-TqRUTfvy.js} +1 -1
  64. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CFjnOb7V.js → ishikawaDiagram-YF4QCWOH-D33iop1_.js} +1 -1
  65. package/dist/client/assets/{journeyDiagram-JHISSGLW-46dGsf_j.js → journeyDiagram-JHISSGLW-DH-TDR5Y.js} +1 -1
  66. package/dist/client/assets/{kanban-definition-UN3LZRKU-9-nz-BFT.js → kanban-definition-UN3LZRKU-CAcebzyW.js} +1 -1
  67. package/dist/client/assets/{mermaid.core-CYHDO5ss.js → mermaid.core-C3Nh1Sak.js} +4 -4
  68. package/dist/client/assets/{mindmap-definition-RKZ34NQL-BYQE0Nqo.js → mindmap-definition-RKZ34NQL-CSIccWV8.js} +1 -1
  69. package/dist/client/assets/{pieDiagram-4H26LBE5-BNSMLSjW.js → pieDiagram-4H26LBE5-hXJ3G3MR.js} +1 -1
  70. package/dist/client/assets/{puzzle-D2SwAtRX.js → puzzle-DI3elldh.js} +1 -1
  71. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-CNdSBA7P.js → quadrantDiagram-W4KKPZXB-B3wpvneC.js} +1 -1
  72. package/dist/client/assets/{requirementDiagram-4Y6WPE33-B6EJvc9H.js → requirementDiagram-4Y6WPE33-DPpYL2ca.js} +1 -1
  73. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-8DcYdUmL.js → sankeyDiagram-5OEKKPKP-BS9QXSEF.js} +1 -1
  74. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BjRXhtKa.js → sequenceDiagram-3UESZ5HK-C_fqU0MW.js} +1 -1
  75. package/dist/client/assets/{shield-alert-b4thCabv.js → shield-alert-CzZYSqmH.js} +1 -1
  76. package/dist/client/assets/{standing-instructions-template-B1X-jWip.js → standing-instructions-template-CsXA4WMZ.js} +1 -1
  77. package/dist/client/assets/{stateDiagram-AJRCARHV-Btrn_84P.js → stateDiagram-AJRCARHV-DbSZrprS.js} +1 -1
  78. package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-DiyZxQF1.js → stateDiagram-v2-BHNVJYJU-Cf_ckwmM.js} +1 -1
  79. package/dist/client/assets/{timeline-definition-PNZ67QCA-CjEYU1kO.js → timeline-definition-PNZ67QCA-B1ohvKjs.js} +1 -1
  80. package/dist/client/assets/{upload-DMts5fzZ.js → upload-D7nNe9wD.js} +1 -1
  81. package/dist/client/assets/{users-C9O98ACz.js → users-B79Dvre4.js} +1 -1
  82. package/dist/client/assets/{vennDiagram-CIIHVFJN-DtEb-r3I.js → vennDiagram-CIIHVFJN-CxZYCQ72.js} +1 -1
  83. package/dist/client/assets/{wardley-L42UT6IY-Ds-v1VZi.js → wardley-L42UT6IY-clJem8VU.js} +1 -1
  84. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-C15pYn-T.js → wardleyDiagram-YWT4CUSO-zE8lyaWe.js} +1 -1
  85. package/dist/client/assets/{xychartDiagram-2RQKCTM6-ChJ77iYU.js → xychartDiagram-2RQKCTM6-DsdjuSxQ.js} +1 -1
  86. package/dist/client/index.html +2 -2
  87. package/dist/client/version.json +1 -1
  88. package/dist/droid-cli/package.json +1 -1
  89. package/dist/extension.js +242 -125
  90. package/dist/migrations/0000_initial.sql +1 -0
  91. package/dist/migrations/0044_fn_8785_queued_episode_signature.sql +3 -0
  92. package/dist/pi-claude-cli/package.json +1 -1
  93. package/dist/plugin-sdk/index.d.ts +16 -0
  94. package/dist/plugin-sdk/index.js +6 -5
  95. package/dist/plugins/fusion-plugin-acp-runtime/bundled.js +6 -5
  96. package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
  97. package/dist/plugins/fusion-plugin-claude-runtime/bundled.js +6 -5
  98. package/dist/plugins/fusion-plugin-claude-runtime/package.json +1 -1
  99. package/dist/plugins/fusion-plugin-cli-printing-press/bundled.js +6 -5
  100. package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
  101. package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +6 -5
  102. package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
  103. package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +6 -5
  104. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  105. package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +6 -5
  106. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  107. package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +6 -5
  108. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  109. package/dist/plugins/fusion-plugin-grok-runtime/bundled.js +6 -5
  110. package/dist/plugins/fusion-plugin-grok-runtime/package.json +1 -1
  111. package/dist/plugins/fusion-plugin-hermes-runtime/.bundled.reload-3.js +19167 -0
  112. package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +6 -5
  113. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  114. package/dist/plugins/fusion-plugin-linear-import/bundled.js +6 -5
  115. package/dist/plugins/fusion-plugin-linear-import/package.json +1 -1
  116. package/dist/plugins/fusion-plugin-omp-runtime/bundled.js +6 -5
  117. package/dist/plugins/fusion-plugin-omp-runtime/package.json +1 -1
  118. package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +6 -5
  119. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  120. package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +6 -5
  121. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  122. package/dist/plugins/fusion-plugin-quality/bundled.js +6 -5
  123. package/dist/plugins/fusion-plugin-quality/package.json +1 -1
  124. package/dist/plugins/fusion-plugin-reports/bundled.js +6 -5
  125. package/dist/plugins/fusion-plugin-reports/package.json +1 -1
  126. package/dist/plugins/fusion-plugin-roadmap/bundled.js +6 -5
  127. package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
  128. package/dist/plugins/fusion-plugin-todos/bundled.js +6 -5
  129. package/dist/plugins/fusion-plugin-todos/package.json +1 -1
  130. package/dist/plugins/fusion-plugin-whatsapp-chat/bundled.js +6 -5
  131. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  132. package/package.json +1 -1
  133. package/dist/client/assets/channel-BRBjryTS.js +0 -1
  134. package/dist/client/assets/classDiagram-4FO5ZUOK-26ZYCgHv.js +0 -1
  135. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-26ZYCgHv.js +0 -1
  136. package/dist/client/assets/index-CJRmyxnw.css +0 -1
package/dist/extension.js CHANGED
@@ -11845,6 +11845,11 @@ APPROVE when the plan is executable and verifiable, not when it is cosmetically
11845
11845
  });
11846
11846
 
11847
11847
  // ../core/src/agents/agent-prompts.ts
11848
+ function buildPlanningDuplicatePolicyInstruction() {
11849
+ return `## Duplicate policy for this task
11850
+
11851
+ Only active tasks can be duplicate blockers. A matching task in a done or archived column is historical evidence, not a duplicate verdict: inspect it for context, then write a new plan for the reported work or regression. Do not emit \`DUPLICATE: ...\` for completed or archived work.`;
11852
+ }
11848
11853
  function buildTriageHeartbeatGuidance(options = {}) {
11849
11854
  const noTaskLine = options.plannerHeartbeatPatrolEnabled === false ? TRIAGE_HEARTBEAT_PATROL_DISABLED_INSTRUCTION : "If no task is assigned: execute your planning instructions. Patrol for vague requests, blocked tasks that need better specification, review follow-ups that should become new tasks, and dependency gaps that are slowing executors down.";
11850
11855
  const patrolSafetyLines = options.plannerHeartbeatPatrolEnabled === false ? "" : `
@@ -12136,7 +12141,7 @@ Write a lean, executable PROMPT.md quickly. Preserve safety gates, but skip heav
12136
12141
  - Preserve required safety sections for bugs, workflow routing, forensic tasks, and decision-only work.
12137
12142
 
12138
12143
  ## Duplicate check
12139
- Before writing a spec, call \`fn_task_list\` for active work, then call \`fn_task_search\` with 2-4 targeted keyword phrases from the title/description, such as file paths, symptoms, and symbols. For any likely match in \`done\` or \`archived\`, call \`fn_task_show\` and inspect it before deciding. If an existing task covers the same work, do not write a spec \u2014 but still write PROMPT.md, with its entire contents being the single line \`DUPLICATE: {existing-task-id}\` and nothing else. That file is how the duplicate is recorded; announcing it only in your reply leaves no plan behind and re-plans the task in a loop.
12144
+ Before writing a spec, call \`fn_task_list\` for active work, then call \`fn_task_search\` with \`includeDone: false\` and \`includeArchived: false\` for 2-4 targeted keyword phrases from the title/description, such as file paths, symptoms, and symbols. Do not search completed or archived work for duplicate candidates. When an active match is a duplicate, do not write a spec \u2014 but still write PROMPT.md, with its entire contents being the single line \`DUPLICATE: {existing-task-id}\` and nothing else. That file is how the duplicate is recorded; announcing it only in your reply leaves no plan behind and re-plans the task in a loop.
12140
12145
 
12141
12146
  ## Required PROMPT.md shape
12142
12147
  Write PROMPT.md with Original Description, Before \u2192 After Transformation, Mission, Dependencies, Context to Read First, File Scope, Steps, Documentation Requirements, Completion Criteria, Git Commit Convention, and Do NOT. Put \`## Original Description\` immediately after the title/\`Created\`/\`Size\` metadata with the operator's original task description copied **verbatim** (do not paraphrase). Put \`## Before \u2192 After Transformation\` next, before \`## Mission\`, with concise Before/After bullets: current state, target state, why it satisfies the user's request at a glance. In \`## Steps\`, every executable heading MUST use \`### Step N: <name>\` (e.g. \`### Step 1: Preflight\`). Do not write bare \`### Preflight\` / \`### Implementation\` headings, and do not add review-level, triage subtask, or proactive subtask headings.
@@ -12404,9 +12409,9 @@ Verified facts about this codebase's storage \u2014 cite these correctly so Plan
12404
12409
  - New Postgres migrations must be **registered explicitly** in \`packages/core/src/postgres/schema-applier.ts\` (version constant + bookkeeping check); a \`.sql\` file dropped in the migrations dir that is not wired there silently never runs.
12405
12410
 
12406
12411
  ## Duplicate check
12407
- Before writing a spec, first call \`fn_task_list\` to see active tasks, then call \`fn_task_search\` with 2-4 distinct keyword phrases from the task title and description (for example file paths, error symptoms, and symbol names).
12408
- For any likely match in \`done\` or \`archived\`, call \`fn_task_show\` to inspect details before deciding.
12409
- If a task already covers the same work (even if worded differently), do not write a spec.
12412
+ Before writing a spec, first call \`fn_task_list\` to see active tasks, then call \`fn_task_search\` with \`includeDone: false\` and \`includeArchived: false\` for 2-4 distinct keyword phrases from the task title and description (for example file paths, error symptoms, and symbol names).
12413
+ Do not search completed or archived work for duplicate candidates.
12414
+ If an actionable task already covers the same work (even if worded differently), do not write a spec.
12410
12415
  Instead you MUST still write PROMPT.md, with its ENTIRE contents being this one line and nothing else:
12411
12416
  \`DUPLICATE: {existing-task-id}\`
12412
12417
  Writing that file IS how you report the duplicate. Reporting it only in your reply is not recorded:
@@ -12437,7 +12442,7 @@ Workflow policy can disable proactive oversized-task splitting for operators who
12437
12442
  ## Triage tools
12438
12443
  You have these extra tools during triage:
12439
12444
  - \`fn_task_list\` \u2014 list existing active tasks
12440
- - \`fn_task_search\` \u2014 keyword search across tasks, including done and archived tasks
12445
+ - \`fn_task_search\` \u2014 keyword search across active tasks by default; history is opt-in for non-duplicate research
12441
12446
  - \`fn_task_show\` \u2014 inspect a task and its PROMPT.md
12442
12447
  - \`fn_task_create\` \u2014 create a child/follow-up task while triaging
12443
12448
  - \`fn_task_document_write\` \u2014 save a planning document (e.g., key="plan")
@@ -20177,6 +20182,7 @@ var init_project = __esm({
20177
20182
  worktree: text("worktree"),
20178
20183
  blockedBy: text("blocked_by"),
20179
20184
  overlapBlockedBy: text("overlap_blocked_by"),
20185
+ queuedLogEpisodeSignature: text("queued_log_episode_signature"),
20180
20186
  paused: integer("paused").default(0),
20181
20187
  userPaused: integer("user_paused").default(0),
20182
20188
  pausedReason: text("paused_reason"),
@@ -24327,6 +24333,22 @@ var init_near_duplicate_canonical = __esm({
24327
24333
  }
24328
24334
  });
24329
24335
 
24336
+ // ../core/src/duplicates/near-duplicate-canonical-flags.ts
24337
+ async function resolveNearDuplicateCanonicalFlags(store, canonical) {
24338
+ if (!canonical?.column) return void 0;
24339
+ const workflow = await resolveWorkflowIrForTask(store, canonical.id).catch(() => void 0);
24340
+ if (!workflow || workflow.version !== "v2") return void 0;
24341
+ const column = workflow.columns.find((candidate) => candidate.id === canonical.column);
24342
+ return column ? resolveColumnFlags(column) : void 0;
24343
+ }
24344
+ var init_near_duplicate_canonical_flags = __esm({
24345
+ "../core/src/duplicates/near-duplicate-canonical-flags.ts"() {
24346
+ "use strict";
24347
+ init_trait_registry();
24348
+ init_workflow_ir_resolver();
24349
+ }
24350
+ });
24351
+
24330
24352
  // ../core/src/git/gitlab-tracking.ts
24331
24353
  function formatGitLabTrackedItemRef(item) {
24332
24354
  const marker = item.kind === "merge_request" ? "!" : "#";
@@ -38088,6 +38110,7 @@ async function applySchemaBaseline(db, options = {}) {
38088
38110
  const taskLifecycleConsumersAlreadyApplied = applied.includes(TASK_LIFECYCLE_CONSUMERS_VERSION);
38089
38111
  const validatorInputFingerprintAlreadyApplied = applied.includes(VALIDATOR_INPUT_FINGERPRINT_VERSION);
38090
38112
  const unplannedExecutionBlockDedupeAlreadyApplied = applied.includes(UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION);
38113
+ const queuedEpisodeSignatureAlreadyApplied = applied.includes(QUEUED_EPISODE_SIGNATURE_VERSION);
38091
38114
  assertBinaryNotOlderThanDatabase(applied);
38092
38115
  let schemaChanged = false;
38093
38116
  if (!baselineAlreadyApplied) {
@@ -38481,17 +38504,23 @@ async function applySchemaBaseline(db, options = {}) {
38481
38504
  await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION}) ON CONFLICT (version) DO NOTHING`);
38482
38505
  schemaChanged = true;
38483
38506
  }
38507
+ if (!queuedEpisodeSignatureAlreadyApplied) {
38508
+ const migrationSql = await readFile4(QUEUED_EPISODE_SIGNATURE_MIGRATION_PATH, "utf8");
38509
+ await tx.execute(sql.raw(migrationSql));
38510
+ await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${QUEUED_EPISODE_SIGNATURE_VERSION}) ON CONFLICT (version) DO NOTHING`);
38511
+ schemaChanged = true;
38512
+ }
38484
38513
  return { applied: schemaChanged, pluginHooksRun: pluginHooks.length };
38485
38514
  });
38486
38515
  }
38487
- var SCHEMA_BASELINE_VERSION, TASK_DECLARED_SYMBOLS_VERSION, INITIAL_SCHEMA_VERSION, AUTOMATION_ISOLATION_SCHEMA_VERSION, ANALYTICS_ISOLATION_SCHEMA_VERSION, MONITOR_APPROVAL_ISOLATION_SCHEMA_VERSION, LEGACY_CUTOVER_PRESERVATION_SCHEMA_VERSION, MULTI_PROJECT_CUTOVER_SCHEMA_VERSION, PROJECT_OWNERSHIP_SCHEMA_VERSION, SQLITE_SCHEMA_PARITY_VERSION, SESSION_ADVISOR_ENABLED_SCHEMA_VERSION, MISSION_FIX_IDEMPOTENCY_VERSION, IMPORT_TRANSLATION_CACHE_VERSION, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_VERSION, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_VERSION, OWNER_PROJECT_ID_SPLIT_VERSION, CHAT_SESSION_PINS_VERSION, EXECUTOR_TOOL_FAILURE_RETRY_VERSION, EXECUTOR_ESCALATION_ATTEMPT_VERSION, GLOBAL_ROUTINES_SCHEMA_VERSION, TASK_MERGER_MODEL_LANE_VERSION, BULK_COMPLETION_REFUSAL_AT_VERSION, TASK_PROPOSAL_CLAIM_VERSION, CONFIGURATION_REVISIONS_VERSION, IDEATION_SCHEMA_VERSION, RESEARCH_FEATURE_PROVENANCE_VERSION, TASK_VERIFICATION_REQUEST_VERSION, SYMBOL_LOCKS_SCHEMA_VERSION, BIGINT_COUNTERS_VERSION, PLANNING_ACTIVE_TIMING_VERSION, SQLITE_MIGRATION_RUNTIME_READ_VERSION, WORKFLOW_TASK_CONTINUATIONS_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_VERSION, TASK_WEDGE_NOTIFICATION_VERSION, MILESTONE_ASSERTION_PROVENANCE_VERSION, MISSION_LINEAGE_STOP_VERSION, CHAT_SESSION_TAGS_VERSION, DROP_GLOBAL_CONCURRENCY_VERSION, MISSION_TASK_PREFIX_VERSION, CREDENTIAL_INSTANCE_SELECTION_VERSION, TASK_LIFECYCLE_OUTBOX_VERSION, TASK_LIFECYCLE_CONSUMERS_VERSION, VALIDATOR_INPUT_FINGERPRINT_VERSION, UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION, StaleBinarySchemaError, MIGRATION_BOOKKEEPING_TABLE, LEGACY_ADOPTION_DRAINED_MARKER, __dirname, MIGRATIONS_DIR, BASELINE_MIGRATION_PATH, AUTOMATION_ISOLATION_MIGRATION_PATH, ANALYTICS_ISOLATION_MIGRATION_PATH, MONITOR_APPROVAL_ISOLATION_MIGRATION_PATH, LEGACY_CUTOVER_PRESERVATION_MIGRATION_PATH, MULTI_PROJECT_CUTOVER_MIGRATION_PATH, PROJECT_OWNERSHIP_MIGRATION_PATH, SQLITE_SCHEMA_PARITY_MIGRATION_PATH, SESSION_ADVISOR_ENABLED_MIGRATION_PATH, MISSION_FIX_IDEMPOTENCY_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_MIGRATION_PATH, OWNER_PROJECT_ID_SPLIT_MIGRATION_PATH, CHAT_SESSION_PINS_MIGRATION_PATH, EXECUTOR_TOOL_FAILURE_RETRY_MIGRATION_PATH, EXECUTOR_ESCALATION_ATTEMPT_MIGRATION_PATH, GLOBAL_ROUTINES_MIGRATION_PATH, TASK_MERGER_MODEL_LANE_MIGRATION_PATH, BULK_COMPLETION_REFUSAL_AT_MIGRATION_PATH, TASK_PROPOSAL_CLAIM_MIGRATION_PATH, CONFIGURATION_REVISIONS_MIGRATION_PATH, IDEATION_MIGRATION_PATH, RESEARCH_FEATURE_PROVENANCE_MIGRATION_PATH, TASK_VERIFICATION_REQUEST_MIGRATION_PATH, SYMBOL_LOCKS_MIGRATION_PATH, BIGINT_COUNTERS_MIGRATION_PATH, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_VERSION, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_MIGRATION_PATH, PLANNING_ACTIVE_TIMING_MIGRATION_PATH, TASK_DECLARED_SYMBOLS_MIGRATION_PATH, SQLITE_MIGRATION_RUNTIME_READ_PATH, WORKFLOW_TASK_CONTINUATIONS_PATH, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_PATH, TASK_WEDGE_NOTIFICATION_MIGRATION_PATH, MILESTONE_ASSERTION_PROVENANCE_MIGRATION_PATH, MISSION_LINEAGE_STOP_MIGRATION_PATH, CHAT_SESSION_TAGS_MIGRATION_PATH, DROP_GLOBAL_CONCURRENCY_MIGRATION_PATH, MISSION_TASK_PREFIX_MIGRATION_PATH, CREDENTIAL_INSTANCE_SELECTION_MIGRATION_PATH, TASK_LIFECYCLE_OUTBOX_MIGRATION_PATH, TASK_LIFECYCLE_CONSUMERS_MIGRATION_PATH, VALIDATOR_INPUT_FINGERPRINT_MIGRATION_PATH, UNPLANNED_EXECUTION_BLOCK_DEDUPE_MIGRATION_PATH;
38516
+ var SCHEMA_BASELINE_VERSION, TASK_DECLARED_SYMBOLS_VERSION, INITIAL_SCHEMA_VERSION, AUTOMATION_ISOLATION_SCHEMA_VERSION, ANALYTICS_ISOLATION_SCHEMA_VERSION, MONITOR_APPROVAL_ISOLATION_SCHEMA_VERSION, LEGACY_CUTOVER_PRESERVATION_SCHEMA_VERSION, MULTI_PROJECT_CUTOVER_SCHEMA_VERSION, PROJECT_OWNERSHIP_SCHEMA_VERSION, SQLITE_SCHEMA_PARITY_VERSION, SESSION_ADVISOR_ENABLED_SCHEMA_VERSION, MISSION_FIX_IDEMPOTENCY_VERSION, IMPORT_TRANSLATION_CACHE_VERSION, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_VERSION, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_VERSION, OWNER_PROJECT_ID_SPLIT_VERSION, CHAT_SESSION_PINS_VERSION, EXECUTOR_TOOL_FAILURE_RETRY_VERSION, EXECUTOR_ESCALATION_ATTEMPT_VERSION, GLOBAL_ROUTINES_SCHEMA_VERSION, TASK_MERGER_MODEL_LANE_VERSION, BULK_COMPLETION_REFUSAL_AT_VERSION, TASK_PROPOSAL_CLAIM_VERSION, CONFIGURATION_REVISIONS_VERSION, IDEATION_SCHEMA_VERSION, RESEARCH_FEATURE_PROVENANCE_VERSION, TASK_VERIFICATION_REQUEST_VERSION, SYMBOL_LOCKS_SCHEMA_VERSION, BIGINT_COUNTERS_VERSION, PLANNING_ACTIVE_TIMING_VERSION, SQLITE_MIGRATION_RUNTIME_READ_VERSION, WORKFLOW_TASK_CONTINUATIONS_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_VERSION, TASK_WEDGE_NOTIFICATION_VERSION, MILESTONE_ASSERTION_PROVENANCE_VERSION, MISSION_LINEAGE_STOP_VERSION, CHAT_SESSION_TAGS_VERSION, DROP_GLOBAL_CONCURRENCY_VERSION, MISSION_TASK_PREFIX_VERSION, CREDENTIAL_INSTANCE_SELECTION_VERSION, TASK_LIFECYCLE_OUTBOX_VERSION, TASK_LIFECYCLE_CONSUMERS_VERSION, VALIDATOR_INPUT_FINGERPRINT_VERSION, UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION, QUEUED_EPISODE_SIGNATURE_VERSION, LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION, StaleBinarySchemaError, MIGRATION_BOOKKEEPING_TABLE, LEGACY_ADOPTION_DRAINED_MARKER, __dirname, MIGRATIONS_DIR, BASELINE_MIGRATION_PATH, AUTOMATION_ISOLATION_MIGRATION_PATH, ANALYTICS_ISOLATION_MIGRATION_PATH, MONITOR_APPROVAL_ISOLATION_MIGRATION_PATH, LEGACY_CUTOVER_PRESERVATION_MIGRATION_PATH, MULTI_PROJECT_CUTOVER_MIGRATION_PATH, PROJECT_OWNERSHIP_MIGRATION_PATH, SQLITE_SCHEMA_PARITY_MIGRATION_PATH, SESSION_ADVISOR_ENABLED_MIGRATION_PATH, MISSION_FIX_IDEMPOTENCY_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_SCOPE_FIX_MIGRATION_PATH, IMPORT_TRANSLATION_CACHE_LEGACY_PARTITION_BACKFILL_MIGRATION_PATH, OWNER_PROJECT_ID_SPLIT_MIGRATION_PATH, CHAT_SESSION_PINS_MIGRATION_PATH, EXECUTOR_TOOL_FAILURE_RETRY_MIGRATION_PATH, EXECUTOR_ESCALATION_ATTEMPT_MIGRATION_PATH, GLOBAL_ROUTINES_MIGRATION_PATH, TASK_MERGER_MODEL_LANE_MIGRATION_PATH, BULK_COMPLETION_REFUSAL_AT_MIGRATION_PATH, TASK_PROPOSAL_CLAIM_MIGRATION_PATH, CONFIGURATION_REVISIONS_MIGRATION_PATH, IDEATION_MIGRATION_PATH, RESEARCH_FEATURE_PROVENANCE_MIGRATION_PATH, TASK_VERIFICATION_REQUEST_MIGRATION_PATH, SYMBOL_LOCKS_MIGRATION_PATH, BIGINT_COUNTERS_MIGRATION_PATH, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_VERSION, WORKFLOW_IR_PIN_AND_LEGACY_ADOPTION_MIGRATION_PATH, PLANNING_ACTIVE_TIMING_MIGRATION_PATH, TASK_DECLARED_SYMBOLS_MIGRATION_PATH, SQLITE_MIGRATION_RUNTIME_READ_PATH, WORKFLOW_TASK_CONTINUATIONS_PATH, LEGACY_ADOPTION_DRAINED_MARKER_RUNTIME_GRANTS_PATH, TASK_WEDGE_NOTIFICATION_MIGRATION_PATH, MILESTONE_ASSERTION_PROVENANCE_MIGRATION_PATH, MISSION_LINEAGE_STOP_MIGRATION_PATH, CHAT_SESSION_TAGS_MIGRATION_PATH, DROP_GLOBAL_CONCURRENCY_MIGRATION_PATH, MISSION_TASK_PREFIX_MIGRATION_PATH, CREDENTIAL_INSTANCE_SELECTION_MIGRATION_PATH, TASK_LIFECYCLE_OUTBOX_MIGRATION_PATH, TASK_LIFECYCLE_CONSUMERS_MIGRATION_PATH, VALIDATOR_INPUT_FINGERPRINT_MIGRATION_PATH, UNPLANNED_EXECUTION_BLOCK_DEDUPE_MIGRATION_PATH, QUEUED_EPISODE_SIGNATURE_MIGRATION_PATH;
38488
38517
  var init_schema_applier = __esm({
38489
38518
  "../core/src/postgres/schema-applier.ts"() {
38490
38519
  "use strict";
38491
38520
  init_drizzle_orm();
38492
38521
  init_plugin_schema_hook();
38493
38522
  init_advisory_locks();
38494
- SCHEMA_BASELINE_VERSION = "0043";
38523
+ SCHEMA_BASELINE_VERSION = "0044";
38495
38524
  TASK_DECLARED_SYMBOLS_VERSION = "0028";
38496
38525
  INITIAL_SCHEMA_VERSION = "0000";
38497
38526
  AUTOMATION_ISOLATION_SCHEMA_VERSION = "0001";
@@ -38535,6 +38564,7 @@ var init_schema_applier = __esm({
38535
38564
  TASK_LIFECYCLE_CONSUMERS_VERSION = "0041";
38536
38565
  VALIDATOR_INPUT_FINGERPRINT_VERSION = "0042";
38537
38566
  UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION = "0043";
38567
+ QUEUED_EPISODE_SIGNATURE_VERSION = "0044";
38538
38568
  LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION = "fusion_mark_legacy_adoption_drained";
38539
38569
  StaleBinarySchemaError = class extends Error {
38540
38570
  constructor(databaseVersion, binaryVersion) {
@@ -38658,6 +38688,7 @@ var init_schema_applier = __esm({
38658
38688
  TASK_LIFECYCLE_CONSUMERS_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0041_fn_8685_task_lifecycle_consumers.sql");
38659
38689
  VALIDATOR_INPUT_FINGERPRINT_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0042_fn_8694_validator_input_fingerprint.sql");
38660
38690
  UNPLANNED_EXECUTION_BLOCK_DEDUPE_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0043_fn8768_dispatch_dedupe.sql");
38691
+ QUEUED_EPISODE_SIGNATURE_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0044_fn_8785_queued_episode_signature.sql");
38661
38692
  }
38662
38693
  });
38663
38694
 
@@ -61597,9 +61628,14 @@ var init_central_core = __esm({
61597
61628
  incremented by real work and the durable counter it maintained was fiction.
61598
61629
 
61599
61630
  `getLiveRunningAgentCounts` SURVIVES and is now the only global readout. It is
61600
- TELEMETRY, not a limiter: it derives live per-project agent counts from the
61601
- registered side-effect-safe source so the dashboard can show "N running (all
61602
- projects)". Nothing gates on it.
61631
+ TELEMETRY, not a capacity limiter: it derives live per-project agent counts
61632
+ from the registered side-effect-safe source so the dashboard can show "N
61633
+ running (all projects)". Task admission does not gate on it.
61634
+
61635
+ FNXC:DevEngineWatch 2026-08-04-01:25:
61636
+ The opt-in source-development restart also reads this conservative telemetry
61637
+ to wait for active work to drain. That is shutdown safety, not capacity
61638
+ arbitration; it never reserves slots or changes task admission.
61603
61639
  */
61604
61640
  async getLiveRunningAgentCounts(options) {
61605
61641
  this.ensureInitialized();
@@ -62861,6 +62897,7 @@ var init_persistence = __esm({
62861
62897
  defineTaskColumn("worktree", (task) => task.worktree ?? null),
62862
62898
  defineTaskColumn("blockedBy", (task) => task.blockedBy ?? null),
62863
62899
  defineTaskColumn("overlapBlockedBy", (task) => task.overlapBlockedBy ?? null),
62900
+ defineTaskColumn("queuedLogEpisodeSignature", (task) => task.queuedLogEpisodeSignature ?? null),
62864
62901
  defineTaskColumn("paused", (task) => task.paused ? 1 : 0),
62865
62902
  defineTaskColumn("pausedReason", (task) => task.pausedReason ?? null),
62866
62903
  defineTaskColumn("wedgeNotification", (task) => toJsonNullable(task.wedgeNotification)),
@@ -63275,6 +63312,7 @@ function rowToTask(row) {
63275
63312
  worktree: row.worktree || void 0,
63276
63313
  blockedBy: row.blockedBy || void 0,
63277
63314
  overlapBlockedBy: row.overlapBlockedBy || void 0,
63315
+ queuedLogEpisodeSignature: row.queuedLogEpisodeSignature || void 0,
63278
63316
  paused: row.paused ? true : void 0,
63279
63317
  pausedReason: row.pausedReason || void 0,
63280
63318
  wedgeNotification: fromJson(row.wedgeNotification) ?? void 0,
@@ -69394,6 +69432,14 @@ function noop3() {
69394
69432
  function matchesParentScope(task, sourceParentTaskId) {
69395
69433
  return !sourceParentTaskId || task.sourceParentTaskId === sourceParentTaskId;
69396
69434
  }
69435
+ async function findActiveDuplicate(store, candidates, predicate) {
69436
+ for (const candidate of candidates) {
69437
+ if (!predicate(candidate)) continue;
69438
+ const flags = await resolveNearDuplicateCanonicalFlags(store, candidate);
69439
+ if (!isNearDuplicateCanonicalInactive(candidate, flags)) return candidate;
69440
+ }
69441
+ return void 0;
69442
+ }
69397
69443
  async function runDeterministicDuplicateGuard(store, input, opts) {
69398
69444
  const fingerprint = computeContentFingerprint(input);
69399
69445
  if (opts?.bypass === true || !fingerprint) {
@@ -69407,7 +69453,9 @@ async function runDeterministicDuplicateGuard(store, input, opts) {
69407
69453
  windowMs,
69408
69454
  includeArchived: false
69409
69455
  });
69410
- const deterministicConflict = deterministicMatches.find(
69456
+ const deterministicConflict = await findActiveDuplicate(
69457
+ store,
69458
+ deterministicMatches,
69411
69459
  (match) => matchesParentScope(match, opts?.sourceParentTaskId) && !acknowledged.has(match.id)
69412
69460
  );
69413
69461
  if (deterministicConflict) {
@@ -69454,7 +69502,9 @@ async function runDeterministicDuplicateGuard(store, input, opts) {
69454
69502
  windowMs,
69455
69503
  includeArchived: false
69456
69504
  });
69457
- const deterministicConflict = deterministicMatches.find(
69505
+ const deterministicConflict = await findActiveDuplicate(
69506
+ store,
69507
+ deterministicMatches,
69458
69508
  (match) => matchesParentScope(match, opts.sourceParentTaskId) && !acknowledged.has(match.id)
69459
69509
  );
69460
69510
  if (deterministicConflict) {
@@ -69478,7 +69528,9 @@ async function reconcileDeterministicDuplicate(store, args) {
69478
69528
  windowMs: clampWindowMs(args.windowMs),
69479
69529
  includeArchived: false
69480
69530
  });
69481
- const olderSibling = siblings.find(
69531
+ const olderSibling = await findActiveDuplicate(
69532
+ store,
69533
+ siblings,
69482
69534
  (sibling) => sibling.id !== args.createdTask.id && sibling.createdAt < args.createdTask.createdAt && matchesParentScope(sibling, args.sourceParentTaskId)
69483
69535
  );
69484
69536
  if (!olderSibling) {
@@ -69537,6 +69589,8 @@ var init_duplicate_guard = __esm({
69537
69589
  "../core/src/duplicates/duplicate-guard.ts"() {
69538
69590
  "use strict";
69539
69591
  init_duplicate_detection();
69592
+ init_near_duplicate_canonical();
69593
+ init_near_duplicate_canonical_flags();
69540
69594
  init_workflow_lifecycle_traits();
69541
69595
  FINGERPRINT_WINDOW_DEFAULT_MS = 6e5;
69542
69596
  FINGERPRINT_WINDOW_MAX_MS = 36e5;
@@ -93148,6 +93202,50 @@ async function runPluginColumnTransitionHooksImpl(store, taskId, workflowIr, fro
93148
93202
  await writeMarker2(remaining);
93149
93203
  }
93150
93204
  }
93205
+ async function transitionQueuedEpisodeImpl(store, id, transition) {
93206
+ const layer = store.asyncLayer;
93207
+ const projectId = layer.projectId?.trim() || "__legacy_unscoped__";
93208
+ const now = (/* @__PURE__ */ new Date()).toISOString();
93209
+ const result2 = await layer.transactionImmediate(async (tx) => {
93210
+ await acquireTaskAdvisoryXactLock(tx, projectId, id);
93211
+ const rows = await tx.select().from(project_exports.tasks).where(and(
93212
+ eq(project_exports.tasks.projectId, projectId),
93213
+ eq(project_exports.tasks.id, id),
93214
+ isNull(project_exports.tasks.deletedAt)
93215
+ ));
93216
+ const current = rows[0];
93217
+ if (!current) throw new Error(`Task ${id} not found or archived while queuing`);
93218
+ const appended = !(current.status === "queued" && (current.blockedBy ?? null) === transition.blockedBy && (current.overlapBlockedBy ?? null) === transition.overlapBlockedBy && (current.queuedLogEpisodeSignature ?? null) === transition.signature);
93219
+ const log41 = Array.isArray(current.log) ? [...current.log] : [];
93220
+ if (appended) {
93221
+ log41.push({
93222
+ timestamp: now,
93223
+ action: transition.action,
93224
+ outcome: truncateTaskLogOutcome(transition.outcome),
93225
+ ...transition.runContext ? { runContext: transition.runContext } : {}
93226
+ });
93227
+ const limit = getTaskActivityLogEntryLimit();
93228
+ if (log41.length > limit) log41.splice(0, log41.length - limit);
93229
+ }
93230
+ const updated = await tx.update(project_exports.tasks).set({
93231
+ status: "queued",
93232
+ blockedBy: transition.blockedBy,
93233
+ overlapBlockedBy: transition.overlapBlockedBy,
93234
+ queuedLogEpisodeSignature: transition.signature,
93235
+ ...appended ? { log: log41 } : {},
93236
+ updatedAt: now
93237
+ }).where(and(
93238
+ eq(project_exports.tasks.projectId, projectId),
93239
+ eq(project_exports.tasks.id, id)
93240
+ )).returning();
93241
+ return { appended, task: updated[0] };
93242
+ });
93243
+ const task = store.rowToTask(store.pgRowToTaskRow(result2.task));
93244
+ await store.writeTaskJsonFile(store.taskDir(id), task);
93245
+ if (store.isWatching) store.taskCache.set(id, { ...task });
93246
+ store.emitTaskLifecycleEventSafely("task:updated", [task]);
93247
+ return { appended: result2.appended, task };
93248
+ }
93151
93249
  async function checkAndRecordUnplannedExecutionBlockImpl(store, id, episode) {
93152
93250
  const layer = store.asyncLayer;
93153
93251
  const projectId = layer.projectId ?? "__legacy_unscoped__";
@@ -93259,6 +93357,7 @@ var init_audit_ops = __esm({
93259
93357
  init_comments();
93260
93358
  init_async_persistence();
93261
93359
  init_async_comments_attachments();
93360
+ init_task_advisory_lock();
93262
93361
  init_project_lane_vocabulary();
93263
93362
  init_schema2();
93264
93363
  }
@@ -99041,6 +99140,9 @@ var init_store = __esm({
99041
99140
  async checkAndRecordUnplannedExecutionBlock(id, episode) {
99042
99141
  return checkAndRecordUnplannedExecutionBlockImpl(this, id, episode);
99043
99142
  }
99143
+ async transitionQueuedEpisode(id, transition) {
99144
+ return transitionQueuedEpisodeImpl(this, id, transition);
99145
+ }
99044
99146
  async logEntry(id, action, outcome, runContext) {
99045
99147
  return logEntryImpl(this, id, action, outcome, runContext);
99046
99148
  }
@@ -127807,6 +127909,7 @@ __export(src_exports, {
127807
127909
  buildInsightExtractionPrompt: () => buildInsightExtractionPrompt,
127808
127910
  buildManualRetryResetPatch: () => buildManualRetryResetPatch,
127809
127911
  buildOriginalDescriptionSection: () => buildOriginalDescriptionSection,
127912
+ buildPlanningDuplicatePolicyInstruction: () => buildPlanningDuplicatePolicyInstruction,
127810
127913
  buildQmdCollectionAddArgs: () => buildQmdCollectionAddArgs,
127811
127914
  buildQmdRefreshCommands: () => buildQmdRefreshCommands,
127812
127915
  buildQmdSearchArgs: () => buildQmdSearchArgs,
@@ -128501,6 +128604,7 @@ __export(src_exports, {
128501
128604
  resolveMergerFallbackModel: () => resolveMergerFallbackModel,
128502
128605
  resolveMergerSettingsModel: () => resolveMergerSettingsModel,
128503
128606
  resolveMobileNavPrimaryItems: () => resolveMobileNavPrimaryItems,
128607
+ resolveNearDuplicateCanonicalFlags: () => resolveNearDuplicateCanonicalFlags,
128504
128608
  resolveNodeOverrideLanes: () => resolveNodeOverrideLanes,
128505
128609
  resolveOptionalReviewRevisionBudget: () => resolveOptionalReviewRevisionBudget,
128506
128610
  resolveOptionalStepRevisionBudget: () => resolveOptionalStepRevisionBudget,
@@ -128733,6 +128837,7 @@ var init_src2 = __esm({
128733
128837
  init_plugin_prompt_condition();
128734
128838
  init_plan_approval();
128735
128839
  init_near_duplicate_canonical();
128840
+ init_near_duplicate_canonical_flags();
128736
128841
  init_gitlab_tracking();
128737
128842
  init_planner_intervention2();
128738
128843
  init_planner_overseer_events();
@@ -168299,14 +168404,14 @@ async function findDefinedFeatureBootstrapDuplicate(store, input, sourceAgentId,
168299
168404
  if (!sourceAgentId && !sourceParentTaskId) return void 0;
168300
168405
  const candidates = await store.listTasks({ slim: true, includeArchived: true, includeDeleted: true });
168301
168406
  const byId = new Map(candidates.map((task) => [task.id, task]));
168302
- const isArchivedCandidate = await resolveArchivedColumnsForTasks(store, candidates);
168407
+ const isTerminalCandidate = await resolveTerminalColumnsForTasks(store, candidates);
168303
168408
  const matches = findSameAgentDuplicates({
168304
168409
  title: input.title,
168305
168410
  description: input.description,
168306
168411
  sourceParentTaskId
168307
168412
  }, candidates.flatMap((task) => {
168308
168413
  const createdAt = Date.parse(task.createdAt);
168309
- if (Number.isNaN(createdAt) || task.deletedAt || isArchivedCandidate(task)) return [];
168414
+ if (Number.isNaN(createdAt) || task.deletedAt || isTerminalCandidate(task)) return [];
168310
168415
  return [{
168311
168416
  id: task.id,
168312
168417
  title: task.title ?? "",
@@ -168357,21 +168462,6 @@ async function resolveTerminalColumnsForTasks(store, tasks2) {
168357
168462
  }
168358
168463
  return (task) => terminalByTaskId.get(task.id)?.has(task.column) === true;
168359
168464
  }
168360
- async function resolveArchivedColumnsForTasks(store, tasks2) {
168361
- const cache = /* @__PURE__ */ new Map();
168362
- const archivedByTaskId = /* @__PURE__ */ new Map();
168363
- for (const task of tasks2) {
168364
- if (archivedByTaskId.has(task.id)) continue;
168365
- const columns = /* @__PURE__ */ new Set(["archived"]);
168366
- try {
168367
- const ir2 = await resolveWorkflowIrForTask(store, task.id, cache);
168368
- if (ir2) for (const id of columnsWithFlag(ir2, "archived")) columns.add(id);
168369
- } catch {
168370
- }
168371
- archivedByTaskId.set(task.id, columns);
168372
- }
168373
- return (task) => archivedByTaskId.get(task.id)?.has(task.column) === true;
168374
- }
168375
168465
  async function createAgentTask(store, input, options) {
168376
168466
  const validateDuplicateCanonical = input.validateDuplicateCanonical;
168377
168467
  const settings = typeof store.getSettings === "function" ? await store.getSettings() : {};
@@ -168436,11 +168526,12 @@ async function createAgentTask(store, input, options) {
168436
168526
  try {
168437
168527
  const acknowledged = new Set(options?.acknowledgedDuplicates ?? []);
168438
168528
  const candidates = await store.findRecentTasksBySourceParentTaskId(sourceParentTaskId);
168529
+ const isTerminalCandidate = await resolveTerminalColumnsForTasks(store, candidates);
168439
168530
  const matches = findSameAgentDuplicates({
168440
168531
  title: input.title,
168441
168532
  description: input.description,
168442
168533
  sourceParentTaskId
168443
- }, candidates.map((candidate) => ({
168534
+ }, candidates.filter((candidate) => !isTerminalCandidate(candidate)).map((candidate) => ({
168444
168535
  id: candidate.id,
168445
168536
  title: candidate.title ?? "",
168446
168537
  description: candidate.description,
@@ -168668,7 +168759,7 @@ function createTaskSearchTool(store) {
168668
168759
  return {
168669
168760
  name: "fn_task_search",
168670
168761
  label: "Search Tasks",
168671
- description: "Keyword search across tasks, including done and archived tasks by default. Use for duplicate detection and work discovery before filing new tasks.",
168762
+ description: "Keyword search across active tasks by default. Done and archived history is opt-in and must not be used for duplicate detection.",
168672
168763
  parameters: taskSearchParams,
168673
168764
  execute: async (_id, params) => {
168674
168765
  const query = params.query.trim();
@@ -168681,10 +168772,10 @@ function createTaskSearchTool(store) {
168681
168772
  const limit = Math.min(50, Math.max(1, Math.floor(params.limit ?? 20)));
168682
168773
  const results = await store.searchTasks(query, {
168683
168774
  slim: true,
168684
- includeArchived: params.includeArchived ?? true,
168775
+ includeArchived: params.includeArchived ?? false,
168685
168776
  limit
168686
168777
  });
168687
- const includeDone = params.includeDone ?? true;
168778
+ const includeDone = params.includeDone ?? false;
168688
168779
  const isTerminalResult = includeDone ? void 0 : await resolveTerminalColumnsForTasks(store, results);
168689
168780
  const filtered = includeDone ? results : results.filter((task) => !isTerminalResult(task));
168690
168781
  const lines = filtered.map(formatTaskSummaryLine);
@@ -185518,6 +185609,9 @@ var init_scheduler = __esm({
185518
185609
  }
185519
185610
  }
185520
185611
  }
185612
+ async transitionQueuedEpisode(task, input) {
185613
+ return (await this.store.transitionQueuedEpisode(task.id, input)).appended;
185614
+ }
185521
185615
  async logDispatchQueuedReason(taskId, reason, memoKey) {
185522
185616
  const key = `${taskId}:${memoKey ?? reason}`;
185523
185617
  if (this.wasDispatchQueuedReasonLogged.has(key)) {
@@ -186030,11 +186124,13 @@ var init_scheduler = __esm({
186030
186124
  }
186031
186125
  const unmetDeps = getUnmetSchedulingDependencies(task, tasks2, schedulingDependencyOptions);
186032
186126
  if (unmetDeps.length > 0) {
186033
- await this.store.updateTask(task.id, {
186034
- status: "queued",
186035
- blockedBy: unmetDeps[0]
186127
+ const normalizedUnmetDeps = [...new Set(unmetDeps)].sort();
186128
+ await this.transitionQueuedEpisode(task, {
186129
+ signature: `dependency:${normalizedUnmetDeps.join(",")}`,
186130
+ blockedBy: unmetDeps[0] ?? null,
186131
+ overlapBlockedBy: task.overlapBlockedBy ?? null,
186132
+ action: `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`
186036
186133
  });
186037
- await this.logDispatchQueuedReason(task.id, `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`);
186038
186134
  this.options.onBlocked?.(task, unmetDeps);
186039
186135
  return null;
186040
186136
  }
@@ -186395,16 +186491,13 @@ var init_scheduler = __esm({
186395
186491
  const overlappingTaskId = Array.from(activeScopes.entries()).sort(([aId], [bId]) => aId.localeCompare(bId)).find(([, activeScope]) => this.pathsOverlap(taskScope, activeScope))?.[0] ?? null;
186396
186492
  if (overlappingTaskId) {
186397
186493
  const activeLeaseColumn = activeScopeColumns.get(overlappingTaskId) ?? "in-progress";
186398
- await this.store.updateTask(task.id, {
186399
- status: "queued",
186494
+ await this.transitionQueuedEpisode(task, {
186495
+ signature: `file-scope:${overlappingTaskId}`,
186400
186496
  blockedBy: null,
186401
- overlapBlockedBy: overlappingTaskId
186497
+ overlapBlockedBy: overlappingTaskId,
186498
+ action: `queued \u2014 blocked by active file-scope lease ${overlappingTaskId} (column=${activeLeaseColumn})`
186402
186499
  });
186403
186500
  await this.rollbackRunningAgentsForQueuedTodoTask(task.id);
186404
- await this.logDispatchQueuedReason(
186405
- task.id,
186406
- `queued \u2014 blocked by active file-scope lease ${overlappingTaskId} (column=${activeLeaseColumn})`
186407
- );
186408
186501
  return null;
186409
186502
  }
186410
186503
  if (task.overlapBlockedBy) {
@@ -201883,13 +201976,6 @@ function hasTerminalInvalidDoneTransition(task) {
201883
201976
  const error3 = task.error ?? "";
201884
201977
  return error3.includes("Invalid transition:") && error3.includes("\u2192 'done'");
201885
201978
  }
201886
- async function resolveNearDuplicateCanonicalFlags(store, canonical) {
201887
- if (!canonical?.column) return void 0;
201888
- const ir2 = await resolveWorkflowIrForTask(store, canonical.id).catch(() => void 0);
201889
- if (!ir2 || ir2.version !== "v2") return void 0;
201890
- const column = ir2.columns.find((candidate) => candidate.id === canonical.column);
201891
- return column ? resolveColumnFlags(column) : void 0;
201892
- }
201893
201979
  function isTaskWorkComplete(task) {
201894
201980
  if (task.steps.length === 0) return false;
201895
201981
  return task.steps.every((step) => step.status === "done" || step.status === "skipped");
@@ -202049,7 +202135,6 @@ var init_self_healing = __esm({
202049
202135
  strandedHoldContinuationNoActionAudited = /* @__PURE__ */ new Set();
202050
202136
  /* FNXC:SymbolLock 2026-07-30-14:20: idle symbol-lock sweeps emit one no-action audit until a stale lock re-arms the diagnostic. */
202051
202137
  symbolLockNoActionAudited = false;
202052
- preservedQueuedOverlapLogged = /* @__PURE__ */ new Map();
202053
202138
  maintenanceTickCounter = 0;
202054
202139
  taskLifecycleRetentionLastPrunedAt = /* @__PURE__ */ new Map();
202055
202140
  processBootStartedAt = Date.now();
@@ -202709,7 +202794,6 @@ var init_self_healing = __esm({
202709
202794
  }
202710
202795
  this.finalizeUnprovenWarned.clear();
202711
202796
  this.strandedCompletedFailureProvenanceWarned.clear();
202712
- this.preservedQueuedOverlapLogged.clear();
202713
202797
  log33.debug("Stopped");
202714
202798
  }
202715
202799
  // ── Auto-unpause ───────────────────────────────────────────────────
@@ -205278,17 +205362,20 @@ var init_self_healing = __esm({
205278
205362
  if (todoTaskIds.has(dependent.id)) {
205279
205363
  if (unresolvedDeps.length > 0) {
205280
205364
  const nextBlocker = unresolvedDeps[0];
205281
- await this.store.updateTask(dependent.id, { blockedBy: nextBlocker, overlapBlockedBy, status: "queued" });
205282
- await this.store.logEntry(
205283
- dependent.id,
205284
- `Auto-recovered (FN-4523): cleared stale blockedBy \u2014 blocker ${taskId} is done; now blocked by ${nextBlocker}`
205285
- );
205365
+ const normalizedUnresolvedDeps = [...new Set(unresolvedDeps)].sort();
205366
+ await this.store.transitionQueuedEpisode(dependent.id, {
205367
+ signature: `dependency:${normalizedUnresolvedDeps.join(",")}`,
205368
+ blockedBy: nextBlocker,
205369
+ overlapBlockedBy,
205370
+ action: `Auto-recovered (FN-4523): cleared stale blockedBy \u2014 blocker ${taskId} is done; now blocked by ${nextBlocker}`
205371
+ });
205286
205372
  } else if (hasActiveOverlapBlocker) {
205287
- await this.store.updateTask(dependent.id, { blockedBy: null, overlapBlockedBy, status: "queued" });
205288
- await this.store.logEntry(
205289
- dependent.id,
205290
- `Auto-recovered (FN-4523): preserved queued status \u2014 still blocked by file scope overlap with ${overlapBlockedBy}`
205291
- );
205373
+ await this.store.transitionQueuedEpisode(dependent.id, {
205374
+ signature: `file-scope:${overlapBlockedBy}`,
205375
+ blockedBy: null,
205376
+ overlapBlockedBy,
205377
+ action: `Auto-recovered (FN-4523): preserved queued status \u2014 still blocked by file scope overlap with ${overlapBlockedBy}`
205378
+ });
205292
205379
  } else {
205293
205380
  await this.store.updateTask(dependent.id, { blockedBy: null, overlapBlockedBy: null, status: null });
205294
205381
  await this.store.logEntry(
@@ -205801,16 +205888,6 @@ var init_self_healing = __esm({
205801
205888
  countBlockedDepth(tasks2) {
205802
205889
  return tasks2.filter((task) => typeof task.blockedBy === "string" && task.blockedBy.trim().length > 0).length;
205803
205890
  }
205804
- shouldLogPreservedQueuedOverlap(taskId, overlapBlockedBy) {
205805
- if (!overlapBlockedBy) return false;
205806
- const previous = this.preservedQueuedOverlapLogged.get(taskId);
205807
- if (previous === overlapBlockedBy) return false;
205808
- this.preservedQueuedOverlapLogged.set(taskId, overlapBlockedBy);
205809
- return true;
205810
- }
205811
- clearPreservedQueuedOverlapMemo(taskId) {
205812
- this.preservedQueuedOverlapLogged.delete(taskId);
205813
- }
205814
205891
  /**
205815
205892
  * #1401: periodic transitionPending recovery sweep. Delegates to the store's
205816
205893
  * idempotent recovery method (a no-op when no stale markers exist), keeping
@@ -206043,7 +206120,6 @@ var init_self_healing = __esm({
206043
206120
  (task) => task.status === "queued" && (task.dependencies.length > 0 || Boolean(task.overlapBlockedBy))
206044
206121
  );
206045
206122
  if (blockedTasks.length === 0 && queuedDependencyTasks.length === 0) {
206046
- this.preservedQueuedOverlapLogged.clear();
206047
206123
  return 0;
206048
206124
  }
206049
206125
  const allTasks = await this.store.listTasks({ includeArchived: true });
@@ -206115,13 +206191,6 @@ var init_self_healing = __esm({
206115
206191
  hold: /* @__PURE__ */ new Set(["todo"]),
206116
206192
  review: /* @__PURE__ */ new Set(["in-review"])
206117
206193
  };
206118
- for (const [taskId, lastLoggedBlockerId] of this.preservedQueuedOverlapLogged) {
206119
- const memoTask = taskById.get(taskId);
206120
- const memoHasActiveOverlapBlocker = memoTask ? await hasActiveFileScopeOverlapBlocker(memoTask, memoTask.overlapBlockedBy) : false;
206121
- if (!candidates.has(taskId) || !memoTask || !lanesOf(taskId).hold.has(memoTask.column) || memoTask.status !== "queued" || memoTask.overlapBlockedBy !== lastLoggedBlockerId || !memoHasActiveOverlapBlocker) {
206122
- this.clearPreservedQueuedOverlapMemo(taskId);
206123
- }
206124
- }
206125
206194
  for (const task of candidates.values()) {
206126
206195
  const blockerId = task.blockedBy;
206127
206196
  const unresolvedDeps = task.dependencies.filter((depId) => {
@@ -206194,7 +206263,6 @@ var init_self_healing = __esm({
206194
206263
  let didRecover = false;
206195
206264
  if (todoTaskIds.has(task.id)) {
206196
206265
  if (unresolvedDeps.length > 0) {
206197
- this.clearPreservedQueuedOverlapMemo(task.id);
206198
206266
  const nextBlocker2 = unresolvedDeps[0];
206199
206267
  if (nextBlocker2 === blockerId) {
206200
206268
  continue;
@@ -206203,19 +206271,19 @@ var init_self_healing = __esm({
206203
206271
  await this.store.logEntry(task.id, `Auto-recovered (FN-5488): refreshed stale blockedBy \u2014 blocker=${blockerId} blockerStatus=${blocker?.status ?? "none"} reason=${reasonCode ?? "unspecified"}; ${reason}; now blocked by ${nextBlocker2}`);
206204
206272
  didRecover = true;
206205
206273
  } else if (hasActiveOverlapBlocker) {
206206
- await this.store.updateTask(task.id, { blockedBy: null, status: "queued" });
206207
- if (this.shouldLogPreservedQueuedOverlap(task.id, task.overlapBlockedBy)) {
206208
- await this.store.logEntry(task.id, `Auto-recovered (FN-5488): preserved queued status \u2014 blocker=${blockerId} blockerStatus=${blocker?.status ?? "none"} reason=${reasonCode ?? "unspecified"}; still blocked by file scope overlap with ${task.overlapBlockedBy}`);
206209
- didRecover = true;
206210
- }
206274
+ const transition = await this.store.transitionQueuedEpisode(task.id, {
206275
+ signature: `file-scope:${task.overlapBlockedBy}`,
206276
+ blockedBy: null,
206277
+ overlapBlockedBy: task.overlapBlockedBy ?? null,
206278
+ action: `Auto-recovered (FN-5488): preserved queued status \u2014 blocker=${blockerId} blockerStatus=${blocker?.status ?? "none"} reason=${reasonCode ?? "unspecified"}; still blocked by file scope overlap with ${task.overlapBlockedBy}`
206279
+ });
206280
+ didRecover = transition.appended;
206211
206281
  } else {
206212
- this.clearPreservedQueuedOverlapMemo(task.id);
206213
206282
  await this.store.updateTask(task.id, { blockedBy: null, overlapBlockedBy: null, status: null });
206214
206283
  await this.store.logEntry(task.id, `Auto-recovered (FN-5488): cleared stale blockedBy \u2014 blocker=${blockerId} blockerStatus=${blocker?.status ?? "none"} reason=${reasonCode ?? "unspecified"}; ${reason}`);
206215
206284
  didRecover = true;
206216
206285
  }
206217
206286
  } else {
206218
- this.clearPreservedQueuedOverlapMemo(task.id);
206219
206287
  await this.store.updateTask(task.id, { blockedBy: null });
206220
206288
  await this.store.logEntry(task.id, `Auto-recovered (FN-4091): cleared stale blockedBy \u2014 ${reason}`);
206221
206289
  didRecover = true;
@@ -206235,13 +206303,14 @@ var init_self_healing = __esm({
206235
206303
  if (queuedDependencyTaskIds.has(task.id)) {
206236
206304
  try {
206237
206305
  if (hasActiveOverlapBlocker) {
206238
- await this.store.updateTask(task.id, { blockedBy: null, status: "queued" });
206239
- if (this.shouldLogPreservedQueuedOverlap(task.id, task.overlapBlockedBy)) {
206240
- await this.store.logEntry(task.id, `Auto-recovered: preserved queued status \u2014 still blocked by file scope overlap with ${task.overlapBlockedBy}`);
206241
- recovered++;
206242
- }
206306
+ const transition = await this.store.transitionQueuedEpisode(task.id, {
206307
+ signature: `file-scope:${task.overlapBlockedBy}`,
206308
+ blockedBy: null,
206309
+ overlapBlockedBy: task.overlapBlockedBy ?? null,
206310
+ action: `Auto-recovered: preserved queued status \u2014 still blocked by file scope overlap with ${task.overlapBlockedBy}`
206311
+ });
206312
+ if (transition.appended) recovered++;
206243
206313
  } else {
206244
- this.clearPreservedQueuedOverlapMemo(task.id);
206245
206314
  await this.store.updateTask(task.id, { blockedBy: null, overlapBlockedBy: null, status: null });
206246
206315
  }
206247
206316
  } catch (err) {
@@ -206251,7 +206320,6 @@ var init_self_healing = __esm({
206251
206320
  }
206252
206321
  continue;
206253
206322
  }
206254
- this.clearPreservedQueuedOverlapMemo(task.id);
206255
206323
  const nextBlocker = unresolvedDeps[0] ?? null;
206256
206324
  if (nextBlocker && task.blockedBy !== nextBlocker) {
206257
206325
  try {
@@ -212462,14 +212530,12 @@ var init_self_healing = __esm({
212462
212530
  if (canClearInactiveMarker) {
212463
212531
  rmSync7(promptPath, { force: true });
212464
212532
  const priorClearCount = typeof task.sourceMetadata?.duplicateMarkerClearCount === "number" ? task.sourceMetadata.duplicateMarkerClearCount : 0;
212465
- const alreadyDismissed = isTriageDuplicateKeepAcknowledged(task.sourceMetadata, marker.canonicalId);
212466
- const exhausted = alreadyDismissed || priorClearCount >= 1;
212467
- const patch = exhausted ? buildMarkerExhaustedFailedTaskPatch(marker.canonicalId, priorClearCount) : buildMarkerClearedReplanTaskPatch(marker.canonicalId, priorClearCount);
212533
+ const patch = buildMarkerClearedReplanTaskPatch(marker.canonicalId, priorClearCount);
212468
212534
  await this.store.updateTask(task.id, patch);
212469
212535
  if (typeof this.store.logEntry === "function") {
212470
212536
  await Promise.resolve(this.store.logEntry(
212471
212537
  task.id,
212472
- exhausted ? buildDuplicateReplanExhaustedError(marker.canonicalId) : TRIAGE_MARKER_CLEARED_REPLAN_LOG_ACTION,
212538
+ TRIAGE_MARKER_CLEARED_REPLAN_LOG_ACTION,
212473
212539
  buildInactiveDuplicateClearFeedback(marker.canonicalId)
212474
212540
  )).catch(() => {
212475
212541
  });
@@ -213281,13 +213347,6 @@ function inlineTaskListFallback(lines, opts = {}) {
213281
213347
  function resolveTaskListFormatter(core) {
213282
213348
  return typeof core.formatTaskListText === "function" ? core.formatTaskListText : inlineTaskListFallback;
213283
213349
  }
213284
- async function resolveNearDuplicateCanonicalFlags2(store, canonical) {
213285
- if (!canonical?.column) return void 0;
213286
- const ir2 = await resolveWorkflowIrForTask(store, canonical.id).catch(() => void 0);
213287
- if (!ir2 || ir2.version !== "v2") return void 0;
213288
- const column = ir2.columns.find((candidate) => candidate.id === canonical.column);
213289
- return column ? resolveColumnFlags(column) : void 0;
213290
- }
213291
213350
  function isOrphanedLegacyTriageRow(column, declaresTriage) {
213292
213351
  return column === "triage" && !declaresTriage;
213293
213352
  }
@@ -215016,11 +215075,13 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
215016
215075
  const defaultTriagePrompt = resolveAgentPrompt("triage", void 0, { plannerHeartbeatPatrolEnabled });
215017
215076
  const resolvedBasePrompt = userTriagePrompt || (leanPlanning ? workflowFastPlanningPrompt || builtinSeamPrompt("planning-fast") || defaultTriagePrompt : workflowPlanningPrompt || defaultTriagePrompt);
215018
215077
  const renderedBasePrompt = renderTriagePolicyPlaceholders(resolvedBasePrompt, triagePolicySettings);
215078
+ const duplicatePolicyInstruction = buildPlanningDuplicatePolicyInstruction();
215019
215079
  const triageLayers = buildPromptLayers({
215020
215080
  basePrompt: renderedBasePrompt,
215021
215081
  goalContext: triageGoalResolution.goalContext,
215022
215082
  agentInstructions: [
215023
215083
  triageIdentitySection,
215084
+ duplicatePolicyInstruction,
215024
215085
  triageInstructions,
215025
215086
  isResearchToolSurfaceEnabled(settings) ? getResearchGuidanceForSurface("triage") : ""
215026
215087
  ].filter((section) => section.trim()).join("\n\n"),
@@ -215649,7 +215710,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
215649
215710
  const taskSearch = {
215650
215711
  name: "fn_task_search",
215651
215712
  label: "Search Tasks",
215652
- description: "Keyword search across tasks, including done and archived tasks by default. Use for duplicate detection before filing a new task.",
215713
+ description: "Keyword search across active tasks by default. Done and archived history is opt-in and must not be used for duplicate detection.",
215653
215714
  parameters: taskSearchParams2,
215654
215715
  execute: async (_callId, params) => {
215655
215716
  const query = params.query.trim();
@@ -215661,10 +215722,10 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
215661
215722
  }
215662
215723
  const results = await store.searchTasks(query, {
215663
215724
  slim: true,
215664
- includeArchived: params.includeArchived ?? true,
215725
+ includeArchived: params.includeArchived ?? false,
215665
215726
  limit: params.limit ?? 20
215666
215727
  });
215667
- const includeDone = params.includeDone ?? true;
215728
+ const includeDone = params.includeDone ?? false;
215668
215729
  const isTerminalResult = includeDone ? void 0 : await resolveTerminalColumnsForTasks(store, results);
215669
215730
  const filtered = includeDone ? results : results.filter((t) => !isTerminalResult(t));
215670
215731
  if (filtered.length === 0) {
@@ -215903,7 +215964,7 @@ Remove or replace these ids and call fn_task_create again.`
215903
215964
  if (canonicalTask?.id.toLowerCase() === task.id.toLowerCase()) {
215904
215965
  return false;
215905
215966
  }
215906
- const canonicalFlags = await resolveNearDuplicateCanonicalFlags2(this.store, canonicalTask);
215967
+ const canonicalFlags = await resolveNearDuplicateCanonicalFlags(this.store, canonicalTask);
215907
215968
  if (isNearDuplicateCanonicalInactive(canonicalTask, canonicalFlags)) {
215908
215969
  planLog.log(`${task.id} explicit duplicate marker targets inactive ${canonicalId}; clearing marker for replanning`);
215909
215970
  } else {
@@ -216047,17 +216108,16 @@ Remove or replace these ids and call fn_task_create again.`
216047
216108
  const canonicalId = explicitDuplicateMarker.canonicalId;
216048
216109
  const canonicalTask = await this.store.getTask(canonicalId).catch(() => null);
216049
216110
  const canClearInactiveMarker = task.userPaused !== true && (task.paused !== true || task.pausedReason === "duplicate-decision-required") && (task.pausedReason == null || task.pausedReason === "duplicate-decision-required");
216050
- const canonicalFlags = await resolveNearDuplicateCanonicalFlags2(this.store, canonicalTask);
216111
+ const canonicalFlags = await resolveNearDuplicateCanonicalFlags(this.store, canonicalTask);
216051
216112
  const liveMeta = (await this.store.getTask(task.id).catch(() => null))?.sourceMetadata ?? task.sourceMetadata;
216052
216113
  const priorClearCount = typeof liveMeta?.duplicateMarkerClearCount === "number" ? liveMeta.duplicateMarkerClearCount : 0;
216053
216114
  if (isNearDuplicateCanonicalInactive(canonicalTask ?? void 0, canonicalFlags)) {
216054
216115
  if (canClearInactiveMarker) {
216055
- const alreadyDismissed = isTriageDuplicateKeepAcknowledged(liveMeta, canonicalId);
216056
216116
  await this.clearDuplicateMarkerForReplan(
216057
216117
  task,
216058
216118
  canonicalId,
216059
216119
  buildInactiveDuplicateClearFeedback(canonicalId),
216060
- { exhausted: alreadyDismissed || priorClearCount >= 1, priorClearCount }
216120
+ { exhausted: false, priorClearCount }
216061
216121
  );
216062
216122
  }
216063
216123
  return;
@@ -216297,7 +216357,7 @@ Remove or replace these ids and call fn_task_create again.`
216297
216357
  if (!canonicalTask) {
216298
216358
  return;
216299
216359
  }
216300
- const canonicalFlags = await resolveNearDuplicateCanonicalFlags2(this.store, canonicalTask);
216360
+ const canonicalFlags = await resolveNearDuplicateCanonicalFlags(this.store, canonicalTask);
216301
216361
  if (isNearDuplicateCanonicalInactive(canonicalTask, canonicalFlags)) {
216302
216362
  planLog.log(`${task.id}: near-duplicate candidate ${canonical.id} is inactive; skipping near-duplicate flag`);
216303
216363
  return;
@@ -232168,13 +232228,15 @@ Graph value: ${failureValue}` : ""}`, budget.key),
232168
232228
  recoveryRehome: true
232169
232229
  });
232170
232230
  }
232171
- await this.store.updateTask(liveTask.id, { status: "queued", blockedBy: unmetDeps[0] }, this.getRunContextFor(liveTask.id));
232172
- await this.store.logEntry(
232173
- liveTask.id,
232174
- `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`,
232175
- "Executor pre-dispatch dependency gate blocked workflow/authoritative execution.",
232176
- this.getRunContextFor(liveTask.id)
232177
- );
232231
+ const normalizedUnmetDeps = [...new Set(unmetDeps)].sort();
232232
+ await this.store.transitionQueuedEpisode(liveTask.id, {
232233
+ signature: `dependency:${normalizedUnmetDeps.join(",")}`,
232234
+ blockedBy: unmetDeps[0] ?? null,
232235
+ overlapBlockedBy: liveTask.overlapBlockedBy ?? null,
232236
+ action: `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`,
232237
+ outcome: "Executor pre-dispatch dependency gate blocked workflow/authoritative execution.",
232238
+ runContext: this.getRunContextFor(liveTask.id)
232239
+ });
232178
232240
  executorLog.log(`${liveTask.id}: executor dispatch blocked by unmet dependencies: ${unmetDeps.join(", ")}`);
232179
232241
  return true;
232180
232242
  }
@@ -257802,6 +257864,40 @@ var init_in_process_runtime = __esm({
257802
257864
  throw err;
257803
257865
  }
257804
257866
  }
257867
+ /**
257868
+ * Close every process-local admission source without aborting work that is
257869
+ * already running. Development source reload uses this boundary before it
257870
+ * waits for the live-agent count to reach zero.
257871
+ */
257872
+ beginDrain() {
257873
+ if (this.status !== "active") return;
257874
+ this.setStatus("paused");
257875
+ if (this.workflowContinuationTimer) {
257876
+ clearInterval(this.workflowContinuationTimer);
257877
+ this.workflowContinuationTimer = void 0;
257878
+ }
257879
+ const admissionStops = [
257880
+ ["self-healing manager", () => this.selfHealingManager?.stop()],
257881
+ ["routine scheduler", () => this.routineScheduler?.stop()],
257882
+ ["trigger scheduler", () => this.triggerScheduler?.stop()],
257883
+ ["stuck task detector", () => this.stuckTaskDetector?.stop()],
257884
+ ["heartbeat monitor", () => this.heartbeatMonitor?.stop()],
257885
+ ["triage processor", () => this.triageProcessor?.stop()],
257886
+ ["scheduler", () => this.scheduler?.stop()],
257887
+ ["mission autopilot", () => this.missionAutopilot?.stop()],
257888
+ ["mission execution loop", () => this.missionExecutionLoop?.stop()]
257889
+ ];
257890
+ for (const [label, stop] of admissionStops) {
257891
+ try {
257892
+ stop();
257893
+ } catch (error3) {
257894
+ runtimeLog.warn(
257895
+ `Failed to stop ${label} while draining ${this.config.projectId}: ${error3 instanceof Error ? error3.message : String(error3)}`
257896
+ );
257897
+ }
257898
+ }
257899
+ runtimeLog.log(`InProcessRuntime draining for ${this.config.projectId}`);
257900
+ }
257805
257901
  /**
257806
257902
  * Stop the runtime with graceful shutdown.
257807
257903
  *
@@ -261916,6 +262012,11 @@ ${detail}`);
261916
262012
  this.started = false;
261917
262013
  runtimeLog.log(`ProjectEngine stopped for ${this.config.projectId}`);
261918
262014
  }
262015
+ /** Stop new lifecycle admission while allowing active work to finish. */
262016
+ beginDrain() {
262017
+ this.shuttingDown = true;
262018
+ this.runtime.beginDrain();
262019
+ }
261919
262020
  // ── Public accessors ──
261920
262021
  /** Get the underlying InProcessRuntime. */
261921
262022
  getRuntime() {
@@ -265494,14 +265595,30 @@ var init_project_engine_manager = __esm({
265494
265595
  }
265495
265596
  runtimeLog.log(`Engine startup complete: ${started} started, ${failed} failed`);
265496
265597
  }
265497
- /** Gracefully stop all engines and reconciliation. */
265498
- async stopAll() {
265598
+ /** Close admission on every engine and stop reconciliation without tearing engines down. */
265599
+ beginDrain() {
265499
265600
  this.stopped = true;
265500
265601
  this.reconciliationStopped = true;
265501
265602
  if (this.reconciliationInterval !== null) {
265502
265603
  clearInterval(this.reconciliationInterval);
265503
265604
  this.reconciliationInterval = null;
265504
265605
  }
265606
+ for (const engine of this.engines.values()) {
265607
+ try {
265608
+ engine.beginDrain?.();
265609
+ } catch (error3) {
265610
+ runtimeLog.warn(
265611
+ `Engine drain error: ${error3 instanceof Error ? error3.message : String(error3)}`
265612
+ );
265613
+ }
265614
+ }
265615
+ for (const starting of this.starting.values()) {
265616
+ void starting.then((engine) => engine.beginDrain?.()).catch(() => void 0);
265617
+ }
265618
+ }
265619
+ /** Gracefully stop all engines and reconciliation. */
265620
+ async stopAll() {
265621
+ this.beginDrain();
265505
265622
  try {
265506
265623
  this.centralCore.stopDiscovery();
265507
265624
  await this.centralCore.markLocalNodeOffline();