@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/bin.js CHANGED
@@ -11925,6 +11925,11 @@ APPROVE when the plan is executable and verifiable, not when it is cosmetically
11925
11925
  });
11926
11926
 
11927
11927
  // ../core/src/agents/agent-prompts.ts
11928
+ function buildPlanningDuplicatePolicyInstruction() {
11929
+ return `## Duplicate policy for this task
11930
+
11931
+ 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.`;
11932
+ }
11928
11933
  function buildTriageHeartbeatGuidance(options = {}) {
11929
11934
  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.";
11930
11935
  const patrolSafetyLines = options.plannerHeartbeatPatrolEnabled === false ? "" : `
@@ -12216,7 +12221,7 @@ Write a lean, executable PROMPT.md quickly. Preserve safety gates, but skip heav
12216
12221
  - Preserve required safety sections for bugs, workflow routing, forensic tasks, and decision-only work.
12217
12222
 
12218
12223
  ## Duplicate check
12219
- 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.
12224
+ 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.
12220
12225
 
12221
12226
  ## Required PROMPT.md shape
12222
12227
  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.
@@ -12484,9 +12489,9 @@ Verified facts about this codebase's storage \u2014 cite these correctly so Plan
12484
12489
  - 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.
12485
12490
 
12486
12491
  ## Duplicate check
12487
- 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).
12488
- For any likely match in \`done\` or \`archived\`, call \`fn_task_show\` to inspect details before deciding.
12489
- If a task already covers the same work (even if worded differently), do not write a spec.
12492
+ 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).
12493
+ Do not search completed or archived work for duplicate candidates.
12494
+ If an actionable task already covers the same work (even if worded differently), do not write a spec.
12490
12495
  Instead you MUST still write PROMPT.md, with its ENTIRE contents being this one line and nothing else:
12491
12496
  \`DUPLICATE: {existing-task-id}\`
12492
12497
  Writing that file IS how you report the duplicate. Reporting it only in your reply is not recorded:
@@ -12517,7 +12522,7 @@ Workflow policy can disable proactive oversized-task splitting for operators who
12517
12522
  ## Triage tools
12518
12523
  You have these extra tools during triage:
12519
12524
  - \`fn_task_list\` \u2014 list existing active tasks
12520
- - \`fn_task_search\` \u2014 keyword search across tasks, including done and archived tasks
12525
+ - \`fn_task_search\` \u2014 keyword search across active tasks by default; history is opt-in for non-duplicate research
12521
12526
  - \`fn_task_show\` \u2014 inspect a task and its PROMPT.md
12522
12527
  - \`fn_task_create\` \u2014 create a child/follow-up task while triaging
12523
12528
  - \`fn_task_document_write\` \u2014 save a planning document (e.g., key="plan")
@@ -20257,6 +20262,7 @@ var init_project = __esm({
20257
20262
  worktree: text("worktree"),
20258
20263
  blockedBy: text("blocked_by"),
20259
20264
  overlapBlockedBy: text("overlap_blocked_by"),
20265
+ queuedLogEpisodeSignature: text("queued_log_episode_signature"),
20260
20266
  paused: integer("paused").default(0),
20261
20267
  userPaused: integer("user_paused").default(0),
20262
20268
  pausedReason: text("paused_reason"),
@@ -24407,6 +24413,22 @@ var init_near_duplicate_canonical = __esm({
24407
24413
  }
24408
24414
  });
24409
24415
 
24416
+ // ../core/src/duplicates/near-duplicate-canonical-flags.ts
24417
+ async function resolveNearDuplicateCanonicalFlags(store, canonical) {
24418
+ if (!canonical?.column) return void 0;
24419
+ const workflow = await resolveWorkflowIrForTask(store, canonical.id).catch(() => void 0);
24420
+ if (!workflow || workflow.version !== "v2") return void 0;
24421
+ const column = workflow.columns.find((candidate) => candidate.id === canonical.column);
24422
+ return column ? resolveColumnFlags(column) : void 0;
24423
+ }
24424
+ var init_near_duplicate_canonical_flags = __esm({
24425
+ "../core/src/duplicates/near-duplicate-canonical-flags.ts"() {
24426
+ "use strict";
24427
+ init_trait_registry();
24428
+ init_workflow_ir_resolver();
24429
+ }
24430
+ });
24431
+
24410
24432
  // ../core/src/git/gitlab-tracking.ts
24411
24433
  function formatGitLabTrackedItemRef(item) {
24412
24434
  const marker = item.kind === "merge_request" ? "!" : "#";
@@ -38168,6 +38190,7 @@ async function applySchemaBaseline(db, options = {}) {
38168
38190
  const taskLifecycleConsumersAlreadyApplied = applied.includes(TASK_LIFECYCLE_CONSUMERS_VERSION);
38169
38191
  const validatorInputFingerprintAlreadyApplied = applied.includes(VALIDATOR_INPUT_FINGERPRINT_VERSION);
38170
38192
  const unplannedExecutionBlockDedupeAlreadyApplied = applied.includes(UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION);
38193
+ const queuedEpisodeSignatureAlreadyApplied = applied.includes(QUEUED_EPISODE_SIGNATURE_VERSION);
38171
38194
  assertBinaryNotOlderThanDatabase(applied);
38172
38195
  let schemaChanged = false;
38173
38196
  if (!baselineAlreadyApplied) {
@@ -38561,17 +38584,23 @@ async function applySchemaBaseline(db, options = {}) {
38561
38584
  await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION}) ON CONFLICT (version) DO NOTHING`);
38562
38585
  schemaChanged = true;
38563
38586
  }
38587
+ if (!queuedEpisodeSignatureAlreadyApplied) {
38588
+ const migrationSql = await readFile4(QUEUED_EPISODE_SIGNATURE_MIGRATION_PATH, "utf8");
38589
+ await tx.execute(sql.raw(migrationSql));
38590
+ await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${QUEUED_EPISODE_SIGNATURE_VERSION}) ON CONFLICT (version) DO NOTHING`);
38591
+ schemaChanged = true;
38592
+ }
38564
38593
  return { applied: schemaChanged, pluginHooksRun: pluginHooks.length };
38565
38594
  });
38566
38595
  }
38567
- 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;
38596
+ 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;
38568
38597
  var init_schema_applier = __esm({
38569
38598
  "../core/src/postgres/schema-applier.ts"() {
38570
38599
  "use strict";
38571
38600
  init_drizzle_orm();
38572
38601
  init_plugin_schema_hook();
38573
38602
  init_advisory_locks();
38574
- SCHEMA_BASELINE_VERSION = "0043";
38603
+ SCHEMA_BASELINE_VERSION = "0044";
38575
38604
  TASK_DECLARED_SYMBOLS_VERSION = "0028";
38576
38605
  INITIAL_SCHEMA_VERSION = "0000";
38577
38606
  AUTOMATION_ISOLATION_SCHEMA_VERSION = "0001";
@@ -38615,6 +38644,7 @@ var init_schema_applier = __esm({
38615
38644
  TASK_LIFECYCLE_CONSUMERS_VERSION = "0041";
38616
38645
  VALIDATOR_INPUT_FINGERPRINT_VERSION = "0042";
38617
38646
  UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION = "0043";
38647
+ QUEUED_EPISODE_SIGNATURE_VERSION = "0044";
38618
38648
  LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION = "fusion_mark_legacy_adoption_drained";
38619
38649
  StaleBinarySchemaError = class extends Error {
38620
38650
  constructor(databaseVersion, binaryVersion) {
@@ -38738,6 +38768,7 @@ var init_schema_applier = __esm({
38738
38768
  TASK_LIFECYCLE_CONSUMERS_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0041_fn_8685_task_lifecycle_consumers.sql");
38739
38769
  VALIDATOR_INPUT_FINGERPRINT_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0042_fn_8694_validator_input_fingerprint.sql");
38740
38770
  UNPLANNED_EXECUTION_BLOCK_DEDUPE_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0043_fn8768_dispatch_dedupe.sql");
38771
+ QUEUED_EPISODE_SIGNATURE_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0044_fn_8785_queued_episode_signature.sql");
38741
38772
  }
38742
38773
  });
38743
38774
 
@@ -61677,9 +61708,14 @@ var init_central_core = __esm({
61677
61708
  incremented by real work and the durable counter it maintained was fiction.
61678
61709
 
61679
61710
  `getLiveRunningAgentCounts` SURVIVES and is now the only global readout. It is
61680
- TELEMETRY, not a limiter: it derives live per-project agent counts from the
61681
- registered side-effect-safe source so the dashboard can show "N running (all
61682
- projects)". Nothing gates on it.
61711
+ TELEMETRY, not a capacity limiter: it derives live per-project agent counts
61712
+ from the registered side-effect-safe source so the dashboard can show "N
61713
+ running (all projects)". Task admission does not gate on it.
61714
+
61715
+ FNXC:DevEngineWatch 2026-08-04-01:25:
61716
+ The opt-in source-development restart also reads this conservative telemetry
61717
+ to wait for active work to drain. That is shutdown safety, not capacity
61718
+ arbitration; it never reserves slots or changes task admission.
61683
61719
  */
61684
61720
  async getLiveRunningAgentCounts(options) {
61685
61721
  this.ensureInitialized();
@@ -62941,6 +62977,7 @@ var init_persistence = __esm({
62941
62977
  defineTaskColumn("worktree", (task) => task.worktree ?? null),
62942
62978
  defineTaskColumn("blockedBy", (task) => task.blockedBy ?? null),
62943
62979
  defineTaskColumn("overlapBlockedBy", (task) => task.overlapBlockedBy ?? null),
62980
+ defineTaskColumn("queuedLogEpisodeSignature", (task) => task.queuedLogEpisodeSignature ?? null),
62944
62981
  defineTaskColumn("paused", (task) => task.paused ? 1 : 0),
62945
62982
  defineTaskColumn("pausedReason", (task) => task.pausedReason ?? null),
62946
62983
  defineTaskColumn("wedgeNotification", (task) => toJsonNullable(task.wedgeNotification)),
@@ -63355,6 +63392,7 @@ function rowToTask(row) {
63355
63392
  worktree: row.worktree || void 0,
63356
63393
  blockedBy: row.blockedBy || void 0,
63357
63394
  overlapBlockedBy: row.overlapBlockedBy || void 0,
63395
+ queuedLogEpisodeSignature: row.queuedLogEpisodeSignature || void 0,
63358
63396
  paused: row.paused ? true : void 0,
63359
63397
  pausedReason: row.pausedReason || void 0,
63360
63398
  wedgeNotification: fromJson(row.wedgeNotification) ?? void 0,
@@ -69474,6 +69512,14 @@ function noop3() {
69474
69512
  function matchesParentScope(task, sourceParentTaskId) {
69475
69513
  return !sourceParentTaskId || task.sourceParentTaskId === sourceParentTaskId;
69476
69514
  }
69515
+ async function findActiveDuplicate(store, candidates, predicate) {
69516
+ for (const candidate of candidates) {
69517
+ if (!predicate(candidate)) continue;
69518
+ const flags = await resolveNearDuplicateCanonicalFlags(store, candidate);
69519
+ if (!isNearDuplicateCanonicalInactive(candidate, flags)) return candidate;
69520
+ }
69521
+ return void 0;
69522
+ }
69477
69523
  async function runDeterministicDuplicateGuard(store, input, opts) {
69478
69524
  const fingerprint = computeContentFingerprint(input);
69479
69525
  if (opts?.bypass === true || !fingerprint) {
@@ -69487,7 +69533,9 @@ async function runDeterministicDuplicateGuard(store, input, opts) {
69487
69533
  windowMs,
69488
69534
  includeArchived: false
69489
69535
  });
69490
- const deterministicConflict = deterministicMatches.find(
69536
+ const deterministicConflict = await findActiveDuplicate(
69537
+ store,
69538
+ deterministicMatches,
69491
69539
  (match) => matchesParentScope(match, opts?.sourceParentTaskId) && !acknowledged.has(match.id)
69492
69540
  );
69493
69541
  if (deterministicConflict) {
@@ -69534,7 +69582,9 @@ async function runDeterministicDuplicateGuard(store, input, opts) {
69534
69582
  windowMs,
69535
69583
  includeArchived: false
69536
69584
  });
69537
- const deterministicConflict = deterministicMatches.find(
69585
+ const deterministicConflict = await findActiveDuplicate(
69586
+ store,
69587
+ deterministicMatches,
69538
69588
  (match) => matchesParentScope(match, opts.sourceParentTaskId) && !acknowledged.has(match.id)
69539
69589
  );
69540
69590
  if (deterministicConflict) {
@@ -69558,7 +69608,9 @@ async function reconcileDeterministicDuplicate(store, args) {
69558
69608
  windowMs: clampWindowMs(args.windowMs),
69559
69609
  includeArchived: false
69560
69610
  });
69561
- const olderSibling = siblings.find(
69611
+ const olderSibling = await findActiveDuplicate(
69612
+ store,
69613
+ siblings,
69562
69614
  (sibling) => sibling.id !== args.createdTask.id && sibling.createdAt < args.createdTask.createdAt && matchesParentScope(sibling, args.sourceParentTaskId)
69563
69615
  );
69564
69616
  if (!olderSibling) {
@@ -69617,6 +69669,8 @@ var init_duplicate_guard = __esm({
69617
69669
  "../core/src/duplicates/duplicate-guard.ts"() {
69618
69670
  "use strict";
69619
69671
  init_duplicate_detection();
69672
+ init_near_duplicate_canonical();
69673
+ init_near_duplicate_canonical_flags();
69620
69674
  init_workflow_lifecycle_traits();
69621
69675
  FINGERPRINT_WINDOW_DEFAULT_MS = 6e5;
69622
69676
  FINGERPRINT_WINDOW_MAX_MS = 36e5;
@@ -93228,6 +93282,50 @@ async function runPluginColumnTransitionHooksImpl(store, taskId, workflowIr, fro
93228
93282
  await writeMarker2(remaining);
93229
93283
  }
93230
93284
  }
93285
+ async function transitionQueuedEpisodeImpl(store, id, transition) {
93286
+ const layer = store.asyncLayer;
93287
+ const projectId = layer.projectId?.trim() || "__legacy_unscoped__";
93288
+ const now = (/* @__PURE__ */ new Date()).toISOString();
93289
+ const result2 = await layer.transactionImmediate(async (tx) => {
93290
+ await acquireTaskAdvisoryXactLock(tx, projectId, id);
93291
+ const rows = await tx.select().from(project_exports.tasks).where(and(
93292
+ eq(project_exports.tasks.projectId, projectId),
93293
+ eq(project_exports.tasks.id, id),
93294
+ isNull(project_exports.tasks.deletedAt)
93295
+ ));
93296
+ const current = rows[0];
93297
+ if (!current) throw new Error(`Task ${id} not found or archived while queuing`);
93298
+ const appended = !(current.status === "queued" && (current.blockedBy ?? null) === transition.blockedBy && (current.overlapBlockedBy ?? null) === transition.overlapBlockedBy && (current.queuedLogEpisodeSignature ?? null) === transition.signature);
93299
+ const log43 = Array.isArray(current.log) ? [...current.log] : [];
93300
+ if (appended) {
93301
+ log43.push({
93302
+ timestamp: now,
93303
+ action: transition.action,
93304
+ outcome: truncateTaskLogOutcome(transition.outcome),
93305
+ ...transition.runContext ? { runContext: transition.runContext } : {}
93306
+ });
93307
+ const limit = getTaskActivityLogEntryLimit();
93308
+ if (log43.length > limit) log43.splice(0, log43.length - limit);
93309
+ }
93310
+ const updated = await tx.update(project_exports.tasks).set({
93311
+ status: "queued",
93312
+ blockedBy: transition.blockedBy,
93313
+ overlapBlockedBy: transition.overlapBlockedBy,
93314
+ queuedLogEpisodeSignature: transition.signature,
93315
+ ...appended ? { log: log43 } : {},
93316
+ updatedAt: now
93317
+ }).where(and(
93318
+ eq(project_exports.tasks.projectId, projectId),
93319
+ eq(project_exports.tasks.id, id)
93320
+ )).returning();
93321
+ return { appended, task: updated[0] };
93322
+ });
93323
+ const task = store.rowToTask(store.pgRowToTaskRow(result2.task));
93324
+ await store.writeTaskJsonFile(store.taskDir(id), task);
93325
+ if (store.isWatching) store.taskCache.set(id, { ...task });
93326
+ store.emitTaskLifecycleEventSafely("task:updated", [task]);
93327
+ return { appended: result2.appended, task };
93328
+ }
93231
93329
  async function checkAndRecordUnplannedExecutionBlockImpl(store, id, episode) {
93232
93330
  const layer = store.asyncLayer;
93233
93331
  const projectId = layer.projectId ?? "__legacy_unscoped__";
@@ -93339,6 +93437,7 @@ var init_audit_ops = __esm({
93339
93437
  init_comments();
93340
93438
  init_async_persistence();
93341
93439
  init_async_comments_attachments();
93440
+ init_task_advisory_lock();
93342
93441
  init_project_lane_vocabulary();
93343
93442
  init_schema2();
93344
93443
  }
@@ -99121,6 +99220,9 @@ var init_store = __esm({
99121
99220
  async checkAndRecordUnplannedExecutionBlock(id, episode) {
99122
99221
  return checkAndRecordUnplannedExecutionBlockImpl(this, id, episode);
99123
99222
  }
99223
+ async transitionQueuedEpisode(id, transition) {
99224
+ return transitionQueuedEpisodeImpl(this, id, transition);
99225
+ }
99124
99226
  async logEntry(id, action, outcome, runContext) {
99125
99227
  return logEntryImpl(this, id, action, outcome, runContext);
99126
99228
  }
@@ -127887,6 +127989,7 @@ __export(src_exports, {
127887
127989
  buildInsightExtractionPrompt: () => buildInsightExtractionPrompt,
127888
127990
  buildManualRetryResetPatch: () => buildManualRetryResetPatch,
127889
127991
  buildOriginalDescriptionSection: () => buildOriginalDescriptionSection,
127992
+ buildPlanningDuplicatePolicyInstruction: () => buildPlanningDuplicatePolicyInstruction,
127890
127993
  buildQmdCollectionAddArgs: () => buildQmdCollectionAddArgs,
127891
127994
  buildQmdRefreshCommands: () => buildQmdRefreshCommands,
127892
127995
  buildQmdSearchArgs: () => buildQmdSearchArgs,
@@ -128581,6 +128684,7 @@ __export(src_exports, {
128581
128684
  resolveMergerFallbackModel: () => resolveMergerFallbackModel,
128582
128685
  resolveMergerSettingsModel: () => resolveMergerSettingsModel,
128583
128686
  resolveMobileNavPrimaryItems: () => resolveMobileNavPrimaryItems,
128687
+ resolveNearDuplicateCanonicalFlags: () => resolveNearDuplicateCanonicalFlags,
128584
128688
  resolveNodeOverrideLanes: () => resolveNodeOverrideLanes,
128585
128689
  resolveOptionalReviewRevisionBudget: () => resolveOptionalReviewRevisionBudget,
128586
128690
  resolveOptionalStepRevisionBudget: () => resolveOptionalStepRevisionBudget,
@@ -128813,6 +128917,7 @@ var init_src2 = __esm({
128813
128917
  init_plugin_prompt_condition();
128814
128918
  init_plan_approval();
128815
128919
  init_near_duplicate_canonical();
128920
+ init_near_duplicate_canonical_flags();
128816
128921
  init_gitlab_tracking();
128817
128922
  init_planner_intervention2();
128818
128923
  init_planner_overseer_events();
@@ -169121,14 +169226,14 @@ async function findDefinedFeatureBootstrapDuplicate(store, input, sourceAgentId,
169121
169226
  if (!sourceAgentId && !sourceParentTaskId) return void 0;
169122
169227
  const candidates = await store.listTasks({ slim: true, includeArchived: true, includeDeleted: true });
169123
169228
  const byId = new Map(candidates.map((task) => [task.id, task]));
169124
- const isArchivedCandidate = await resolveArchivedColumnsForTasks(store, candidates);
169229
+ const isTerminalCandidate = await resolveTerminalColumnsForTasks(store, candidates);
169125
169230
  const matches = findSameAgentDuplicates({
169126
169231
  title: input.title,
169127
169232
  description: input.description,
169128
169233
  sourceParentTaskId
169129
169234
  }, candidates.flatMap((task) => {
169130
169235
  const createdAt = Date.parse(task.createdAt);
169131
- if (Number.isNaN(createdAt) || task.deletedAt || isArchivedCandidate(task)) return [];
169236
+ if (Number.isNaN(createdAt) || task.deletedAt || isTerminalCandidate(task)) return [];
169132
169237
  return [{
169133
169238
  id: task.id,
169134
169239
  title: task.title ?? "",
@@ -169179,21 +169284,6 @@ async function resolveTerminalColumnsForTasks(store, tasks2) {
169179
169284
  }
169180
169285
  return (task) => terminalByTaskId.get(task.id)?.has(task.column) === true;
169181
169286
  }
169182
- async function resolveArchivedColumnsForTasks(store, tasks2) {
169183
- const cache7 = /* @__PURE__ */ new Map();
169184
- const archivedByTaskId = /* @__PURE__ */ new Map();
169185
- for (const task of tasks2) {
169186
- if (archivedByTaskId.has(task.id)) continue;
169187
- const columns = /* @__PURE__ */ new Set(["archived"]);
169188
- try {
169189
- const ir2 = await resolveWorkflowIrForTask(store, task.id, cache7);
169190
- if (ir2) for (const id of columnsWithFlag(ir2, "archived")) columns.add(id);
169191
- } catch {
169192
- }
169193
- archivedByTaskId.set(task.id, columns);
169194
- }
169195
- return (task) => archivedByTaskId.get(task.id)?.has(task.column) === true;
169196
- }
169197
169287
  async function createAgentTask(store, input, options) {
169198
169288
  const validateDuplicateCanonical = input.validateDuplicateCanonical;
169199
169289
  const settings = typeof store.getSettings === "function" ? await store.getSettings() : {};
@@ -169258,11 +169348,12 @@ async function createAgentTask(store, input, options) {
169258
169348
  try {
169259
169349
  const acknowledged = new Set(options?.acknowledgedDuplicates ?? []);
169260
169350
  const candidates = await store.findRecentTasksBySourceParentTaskId(sourceParentTaskId);
169351
+ const isTerminalCandidate = await resolveTerminalColumnsForTasks(store, candidates);
169261
169352
  const matches = findSameAgentDuplicates({
169262
169353
  title: input.title,
169263
169354
  description: input.description,
169264
169355
  sourceParentTaskId
169265
- }, candidates.map((candidate) => ({
169356
+ }, candidates.filter((candidate) => !isTerminalCandidate(candidate)).map((candidate) => ({
169266
169357
  id: candidate.id,
169267
169358
  title: candidate.title ?? "",
169268
169359
  description: candidate.description,
@@ -169490,7 +169581,7 @@ function createTaskSearchTool(store) {
169490
169581
  return {
169491
169582
  name: "fn_task_search",
169492
169583
  label: "Search Tasks",
169493
- description: "Keyword search across tasks, including done and archived tasks by default. Use for duplicate detection and work discovery before filing new tasks.",
169584
+ description: "Keyword search across active tasks by default. Done and archived history is opt-in and must not be used for duplicate detection.",
169494
169585
  parameters: taskSearchParams,
169495
169586
  execute: async (_id, params) => {
169496
169587
  const query = params.query.trim();
@@ -169503,10 +169594,10 @@ function createTaskSearchTool(store) {
169503
169594
  const limit = Math.min(50, Math.max(1, Math.floor(params.limit ?? 20)));
169504
169595
  const results = await store.searchTasks(query, {
169505
169596
  slim: true,
169506
- includeArchived: params.includeArchived ?? true,
169597
+ includeArchived: params.includeArchived ?? false,
169507
169598
  limit
169508
169599
  });
169509
- const includeDone = params.includeDone ?? true;
169600
+ const includeDone = params.includeDone ?? false;
169510
169601
  const isTerminalResult = includeDone ? void 0 : await resolveTerminalColumnsForTasks(store, results);
169511
169602
  const filtered = includeDone ? results : results.filter((task) => !isTerminalResult(task));
169512
169603
  const lines = filtered.map(formatTaskSummaryLine);
@@ -186340,6 +186431,9 @@ var init_scheduler = __esm({
186340
186431
  }
186341
186432
  }
186342
186433
  }
186434
+ async transitionQueuedEpisode(task, input) {
186435
+ return (await this.store.transitionQueuedEpisode(task.id, input)).appended;
186436
+ }
186343
186437
  async logDispatchQueuedReason(taskId, reason, memoKey) {
186344
186438
  const key = `${taskId}:${memoKey ?? reason}`;
186345
186439
  if (this.wasDispatchQueuedReasonLogged.has(key)) {
@@ -186852,11 +186946,13 @@ var init_scheduler = __esm({
186852
186946
  }
186853
186947
  const unmetDeps = getUnmetSchedulingDependencies(task, tasks2, schedulingDependencyOptions);
186854
186948
  if (unmetDeps.length > 0) {
186855
- await this.store.updateTask(task.id, {
186856
- status: "queued",
186857
- blockedBy: unmetDeps[0]
186949
+ const normalizedUnmetDeps = [...new Set(unmetDeps)].sort();
186950
+ await this.transitionQueuedEpisode(task, {
186951
+ signature: `dependency:${normalizedUnmetDeps.join(",")}`,
186952
+ blockedBy: unmetDeps[0] ?? null,
186953
+ overlapBlockedBy: task.overlapBlockedBy ?? null,
186954
+ action: `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`
186858
186955
  });
186859
- await this.logDispatchQueuedReason(task.id, `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`);
186860
186956
  this.options.onBlocked?.(task, unmetDeps);
186861
186957
  return null;
186862
186958
  }
@@ -187217,16 +187313,13 @@ var init_scheduler = __esm({
187217
187313
  const overlappingTaskId = Array.from(activeScopes.entries()).sort(([aId], [bId]) => aId.localeCompare(bId)).find(([, activeScope]) => this.pathsOverlap(taskScope, activeScope))?.[0] ?? null;
187218
187314
  if (overlappingTaskId) {
187219
187315
  const activeLeaseColumn = activeScopeColumns.get(overlappingTaskId) ?? "in-progress";
187220
- await this.store.updateTask(task.id, {
187221
- status: "queued",
187316
+ await this.transitionQueuedEpisode(task, {
187317
+ signature: `file-scope:${overlappingTaskId}`,
187222
187318
  blockedBy: null,
187223
- overlapBlockedBy: overlappingTaskId
187319
+ overlapBlockedBy: overlappingTaskId,
187320
+ action: `queued \u2014 blocked by active file-scope lease ${overlappingTaskId} (column=${activeLeaseColumn})`
187224
187321
  });
187225
187322
  await this.rollbackRunningAgentsForQueuedTodoTask(task.id);
187226
- await this.logDispatchQueuedReason(
187227
- task.id,
187228
- `queued \u2014 blocked by active file-scope lease ${overlappingTaskId} (column=${activeLeaseColumn})`
187229
- );
187230
187323
  return null;
187231
187324
  }
187232
187325
  if (task.overlapBlockedBy) {
@@ -202705,13 +202798,6 @@ function hasTerminalInvalidDoneTransition(task) {
202705
202798
  const error3 = task.error ?? "";
202706
202799
  return error3.includes("Invalid transition:") && error3.includes("\u2192 'done'");
202707
202800
  }
202708
- async function resolveNearDuplicateCanonicalFlags(store, canonical) {
202709
- if (!canonical?.column) return void 0;
202710
- const ir2 = await resolveWorkflowIrForTask(store, canonical.id).catch(() => void 0);
202711
- if (!ir2 || ir2.version !== "v2") return void 0;
202712
- const column = ir2.columns.find((candidate) => candidate.id === canonical.column);
202713
- return column ? resolveColumnFlags(column) : void 0;
202714
- }
202715
202801
  function isTaskWorkComplete(task) {
202716
202802
  if (task.steps.length === 0) return false;
202717
202803
  return task.steps.every((step) => step.status === "done" || step.status === "skipped");
@@ -202871,7 +202957,6 @@ var init_self_healing = __esm({
202871
202957
  strandedHoldContinuationNoActionAudited = /* @__PURE__ */ new Set();
202872
202958
  /* FNXC:SymbolLock 2026-07-30-14:20: idle symbol-lock sweeps emit one no-action audit until a stale lock re-arms the diagnostic. */
202873
202959
  symbolLockNoActionAudited = false;
202874
- preservedQueuedOverlapLogged = /* @__PURE__ */ new Map();
202875
202960
  maintenanceTickCounter = 0;
202876
202961
  taskLifecycleRetentionLastPrunedAt = /* @__PURE__ */ new Map();
202877
202962
  processBootStartedAt = Date.now();
@@ -203531,7 +203616,6 @@ var init_self_healing = __esm({
203531
203616
  }
203532
203617
  this.finalizeUnprovenWarned.clear();
203533
203618
  this.strandedCompletedFailureProvenanceWarned.clear();
203534
- this.preservedQueuedOverlapLogged.clear();
203535
203619
  log33.debug("Stopped");
203536
203620
  }
203537
203621
  // ── Auto-unpause ───────────────────────────────────────────────────
@@ -206100,17 +206184,20 @@ var init_self_healing = __esm({
206100
206184
  if (todoTaskIds.has(dependent.id)) {
206101
206185
  if (unresolvedDeps.length > 0) {
206102
206186
  const nextBlocker = unresolvedDeps[0];
206103
- await this.store.updateTask(dependent.id, { blockedBy: nextBlocker, overlapBlockedBy, status: "queued" });
206104
- await this.store.logEntry(
206105
- dependent.id,
206106
- `Auto-recovered (FN-4523): cleared stale blockedBy \u2014 blocker ${taskId} is done; now blocked by ${nextBlocker}`
206107
- );
206187
+ const normalizedUnresolvedDeps = [...new Set(unresolvedDeps)].sort();
206188
+ await this.store.transitionQueuedEpisode(dependent.id, {
206189
+ signature: `dependency:${normalizedUnresolvedDeps.join(",")}`,
206190
+ blockedBy: nextBlocker,
206191
+ overlapBlockedBy,
206192
+ action: `Auto-recovered (FN-4523): cleared stale blockedBy \u2014 blocker ${taskId} is done; now blocked by ${nextBlocker}`
206193
+ });
206108
206194
  } else if (hasActiveOverlapBlocker) {
206109
- await this.store.updateTask(dependent.id, { blockedBy: null, overlapBlockedBy, status: "queued" });
206110
- await this.store.logEntry(
206111
- dependent.id,
206112
- `Auto-recovered (FN-4523): preserved queued status \u2014 still blocked by file scope overlap with ${overlapBlockedBy}`
206113
- );
206195
+ await this.store.transitionQueuedEpisode(dependent.id, {
206196
+ signature: `file-scope:${overlapBlockedBy}`,
206197
+ blockedBy: null,
206198
+ overlapBlockedBy,
206199
+ action: `Auto-recovered (FN-4523): preserved queued status \u2014 still blocked by file scope overlap with ${overlapBlockedBy}`
206200
+ });
206114
206201
  } else {
206115
206202
  await this.store.updateTask(dependent.id, { blockedBy: null, overlapBlockedBy: null, status: null });
206116
206203
  await this.store.logEntry(
@@ -206623,16 +206710,6 @@ var init_self_healing = __esm({
206623
206710
  countBlockedDepth(tasks2) {
206624
206711
  return tasks2.filter((task) => typeof task.blockedBy === "string" && task.blockedBy.trim().length > 0).length;
206625
206712
  }
206626
- shouldLogPreservedQueuedOverlap(taskId, overlapBlockedBy) {
206627
- if (!overlapBlockedBy) return false;
206628
- const previous = this.preservedQueuedOverlapLogged.get(taskId);
206629
- if (previous === overlapBlockedBy) return false;
206630
- this.preservedQueuedOverlapLogged.set(taskId, overlapBlockedBy);
206631
- return true;
206632
- }
206633
- clearPreservedQueuedOverlapMemo(taskId) {
206634
- this.preservedQueuedOverlapLogged.delete(taskId);
206635
- }
206636
206713
  /**
206637
206714
  * #1401: periodic transitionPending recovery sweep. Delegates to the store's
206638
206715
  * idempotent recovery method (a no-op when no stale markers exist), keeping
@@ -206865,7 +206942,6 @@ var init_self_healing = __esm({
206865
206942
  (task) => task.status === "queued" && (task.dependencies.length > 0 || Boolean(task.overlapBlockedBy))
206866
206943
  );
206867
206944
  if (blockedTasks.length === 0 && queuedDependencyTasks.length === 0) {
206868
- this.preservedQueuedOverlapLogged.clear();
206869
206945
  return 0;
206870
206946
  }
206871
206947
  const allTasks = await this.store.listTasks({ includeArchived: true });
@@ -206937,13 +207013,6 @@ var init_self_healing = __esm({
206937
207013
  hold: /* @__PURE__ */ new Set(["todo"]),
206938
207014
  review: /* @__PURE__ */ new Set(["in-review"])
206939
207015
  };
206940
- for (const [taskId, lastLoggedBlockerId] of this.preservedQueuedOverlapLogged) {
206941
- const memoTask = taskById.get(taskId);
206942
- const memoHasActiveOverlapBlocker = memoTask ? await hasActiveFileScopeOverlapBlocker(memoTask, memoTask.overlapBlockedBy) : false;
206943
- if (!candidates.has(taskId) || !memoTask || !lanesOf(taskId).hold.has(memoTask.column) || memoTask.status !== "queued" || memoTask.overlapBlockedBy !== lastLoggedBlockerId || !memoHasActiveOverlapBlocker) {
206944
- this.clearPreservedQueuedOverlapMemo(taskId);
206945
- }
206946
- }
206947
207016
  for (const task of candidates.values()) {
206948
207017
  const blockerId = task.blockedBy;
206949
207018
  const unresolvedDeps = task.dependencies.filter((depId) => {
@@ -207016,7 +207085,6 @@ var init_self_healing = __esm({
207016
207085
  let didRecover = false;
207017
207086
  if (todoTaskIds.has(task.id)) {
207018
207087
  if (unresolvedDeps.length > 0) {
207019
- this.clearPreservedQueuedOverlapMemo(task.id);
207020
207088
  const nextBlocker2 = unresolvedDeps[0];
207021
207089
  if (nextBlocker2 === blockerId) {
207022
207090
  continue;
@@ -207025,19 +207093,19 @@ var init_self_healing = __esm({
207025
207093
  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}`);
207026
207094
  didRecover = true;
207027
207095
  } else if (hasActiveOverlapBlocker) {
207028
- await this.store.updateTask(task.id, { blockedBy: null, status: "queued" });
207029
- if (this.shouldLogPreservedQueuedOverlap(task.id, task.overlapBlockedBy)) {
207030
- 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}`);
207031
- didRecover = true;
207032
- }
207096
+ const transition = await this.store.transitionQueuedEpisode(task.id, {
207097
+ signature: `file-scope:${task.overlapBlockedBy}`,
207098
+ blockedBy: null,
207099
+ overlapBlockedBy: task.overlapBlockedBy ?? null,
207100
+ 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}`
207101
+ });
207102
+ didRecover = transition.appended;
207033
207103
  } else {
207034
- this.clearPreservedQueuedOverlapMemo(task.id);
207035
207104
  await this.store.updateTask(task.id, { blockedBy: null, overlapBlockedBy: null, status: null });
207036
207105
  await this.store.logEntry(task.id, `Auto-recovered (FN-5488): cleared stale blockedBy \u2014 blocker=${blockerId} blockerStatus=${blocker?.status ?? "none"} reason=${reasonCode ?? "unspecified"}; ${reason}`);
207037
207106
  didRecover = true;
207038
207107
  }
207039
207108
  } else {
207040
- this.clearPreservedQueuedOverlapMemo(task.id);
207041
207109
  await this.store.updateTask(task.id, { blockedBy: null });
207042
207110
  await this.store.logEntry(task.id, `Auto-recovered (FN-4091): cleared stale blockedBy \u2014 ${reason}`);
207043
207111
  didRecover = true;
@@ -207057,13 +207125,14 @@ var init_self_healing = __esm({
207057
207125
  if (queuedDependencyTaskIds.has(task.id)) {
207058
207126
  try {
207059
207127
  if (hasActiveOverlapBlocker) {
207060
- await this.store.updateTask(task.id, { blockedBy: null, status: "queued" });
207061
- if (this.shouldLogPreservedQueuedOverlap(task.id, task.overlapBlockedBy)) {
207062
- await this.store.logEntry(task.id, `Auto-recovered: preserved queued status \u2014 still blocked by file scope overlap with ${task.overlapBlockedBy}`);
207063
- recovered++;
207064
- }
207128
+ const transition = await this.store.transitionQueuedEpisode(task.id, {
207129
+ signature: `file-scope:${task.overlapBlockedBy}`,
207130
+ blockedBy: null,
207131
+ overlapBlockedBy: task.overlapBlockedBy ?? null,
207132
+ action: `Auto-recovered: preserved queued status \u2014 still blocked by file scope overlap with ${task.overlapBlockedBy}`
207133
+ });
207134
+ if (transition.appended) recovered++;
207065
207135
  } else {
207066
- this.clearPreservedQueuedOverlapMemo(task.id);
207067
207136
  await this.store.updateTask(task.id, { blockedBy: null, overlapBlockedBy: null, status: null });
207068
207137
  }
207069
207138
  } catch (err) {
@@ -207073,7 +207142,6 @@ var init_self_healing = __esm({
207073
207142
  }
207074
207143
  continue;
207075
207144
  }
207076
- this.clearPreservedQueuedOverlapMemo(task.id);
207077
207145
  const nextBlocker = unresolvedDeps[0] ?? null;
207078
207146
  if (nextBlocker && task.blockedBy !== nextBlocker) {
207079
207147
  try {
@@ -213284,14 +213352,12 @@ var init_self_healing = __esm({
213284
213352
  if (canClearInactiveMarker) {
213285
213353
  rmSync7(promptPath, { force: true });
213286
213354
  const priorClearCount = typeof task.sourceMetadata?.duplicateMarkerClearCount === "number" ? task.sourceMetadata.duplicateMarkerClearCount : 0;
213287
- const alreadyDismissed = isTriageDuplicateKeepAcknowledged(task.sourceMetadata, marker.canonicalId);
213288
- const exhausted = alreadyDismissed || priorClearCount >= 1;
213289
- const patch = exhausted ? buildMarkerExhaustedFailedTaskPatch(marker.canonicalId, priorClearCount) : buildMarkerClearedReplanTaskPatch(marker.canonicalId, priorClearCount);
213355
+ const patch = buildMarkerClearedReplanTaskPatch(marker.canonicalId, priorClearCount);
213290
213356
  await this.store.updateTask(task.id, patch);
213291
213357
  if (typeof this.store.logEntry === "function") {
213292
213358
  await Promise.resolve(this.store.logEntry(
213293
213359
  task.id,
213294
- exhausted ? buildDuplicateReplanExhaustedError(marker.canonicalId) : TRIAGE_MARKER_CLEARED_REPLAN_LOG_ACTION,
213360
+ TRIAGE_MARKER_CLEARED_REPLAN_LOG_ACTION,
213295
213361
  buildInactiveDuplicateClearFeedback(marker.canonicalId)
213296
213362
  )).catch(() => {
213297
213363
  });
@@ -214103,13 +214169,6 @@ function inlineTaskListFallback(lines, opts = {}) {
214103
214169
  function resolveTaskListFormatter(core) {
214104
214170
  return typeof core.formatTaskListText === "function" ? core.formatTaskListText : inlineTaskListFallback;
214105
214171
  }
214106
- async function resolveNearDuplicateCanonicalFlags2(store, canonical) {
214107
- if (!canonical?.column) return void 0;
214108
- const ir2 = await resolveWorkflowIrForTask(store, canonical.id).catch(() => void 0);
214109
- if (!ir2 || ir2.version !== "v2") return void 0;
214110
- const column = ir2.columns.find((candidate) => candidate.id === canonical.column);
214111
- return column ? resolveColumnFlags(column) : void 0;
214112
- }
214113
214172
  function isOrphanedLegacyTriageRow(column, declaresTriage) {
214114
214173
  return column === "triage" && !declaresTriage;
214115
214174
  }
@@ -215838,11 +215897,13 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
215838
215897
  const defaultTriagePrompt = resolveAgentPrompt("triage", void 0, { plannerHeartbeatPatrolEnabled });
215839
215898
  const resolvedBasePrompt = userTriagePrompt || (leanPlanning ? workflowFastPlanningPrompt || builtinSeamPrompt("planning-fast") || defaultTriagePrompt : workflowPlanningPrompt || defaultTriagePrompt);
215840
215899
  const renderedBasePrompt = renderTriagePolicyPlaceholders(resolvedBasePrompt, triagePolicySettings);
215900
+ const duplicatePolicyInstruction = buildPlanningDuplicatePolicyInstruction();
215841
215901
  const triageLayers = buildPromptLayers({
215842
215902
  basePrompt: renderedBasePrompt,
215843
215903
  goalContext: triageGoalResolution.goalContext,
215844
215904
  agentInstructions: [
215845
215905
  triageIdentitySection,
215906
+ duplicatePolicyInstruction,
215846
215907
  triageInstructions,
215847
215908
  isResearchToolSurfaceEnabled(settings) ? getResearchGuidanceForSurface("triage") : ""
215848
215909
  ].filter((section) => section.trim()).join("\n\n"),
@@ -216471,7 +216532,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
216471
216532
  const taskSearch = {
216472
216533
  name: "fn_task_search",
216473
216534
  label: "Search Tasks",
216474
- description: "Keyword search across tasks, including done and archived tasks by default. Use for duplicate detection before filing a new task.",
216535
+ description: "Keyword search across active tasks by default. Done and archived history is opt-in and must not be used for duplicate detection.",
216475
216536
  parameters: taskSearchParams2,
216476
216537
  execute: async (_callId, params) => {
216477
216538
  const query = params.query.trim();
@@ -216483,10 +216544,10 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
216483
216544
  }
216484
216545
  const results = await store.searchTasks(query, {
216485
216546
  slim: true,
216486
- includeArchived: params.includeArchived ?? true,
216547
+ includeArchived: params.includeArchived ?? false,
216487
216548
  limit: params.limit ?? 20
216488
216549
  });
216489
- const includeDone = params.includeDone ?? true;
216550
+ const includeDone = params.includeDone ?? false;
216490
216551
  const isTerminalResult = includeDone ? void 0 : await resolveTerminalColumnsForTasks(store, results);
216491
216552
  const filtered = includeDone ? results : results.filter((t) => !isTerminalResult(t));
216492
216553
  if (filtered.length === 0) {
@@ -216725,7 +216786,7 @@ Remove or replace these ids and call fn_task_create again.`
216725
216786
  if (canonicalTask?.id.toLowerCase() === task.id.toLowerCase()) {
216726
216787
  return false;
216727
216788
  }
216728
- const canonicalFlags = await resolveNearDuplicateCanonicalFlags2(this.store, canonicalTask);
216789
+ const canonicalFlags = await resolveNearDuplicateCanonicalFlags(this.store, canonicalTask);
216729
216790
  if (isNearDuplicateCanonicalInactive(canonicalTask, canonicalFlags)) {
216730
216791
  planLog.log(`${task.id} explicit duplicate marker targets inactive ${canonicalId}; clearing marker for replanning`);
216731
216792
  } else {
@@ -216869,17 +216930,16 @@ Remove or replace these ids and call fn_task_create again.`
216869
216930
  const canonicalId = explicitDuplicateMarker.canonicalId;
216870
216931
  const canonicalTask = await this.store.getTask(canonicalId).catch(() => null);
216871
216932
  const canClearInactiveMarker = task.userPaused !== true && (task.paused !== true || task.pausedReason === "duplicate-decision-required") && (task.pausedReason == null || task.pausedReason === "duplicate-decision-required");
216872
- const canonicalFlags = await resolveNearDuplicateCanonicalFlags2(this.store, canonicalTask);
216933
+ const canonicalFlags = await resolveNearDuplicateCanonicalFlags(this.store, canonicalTask);
216873
216934
  const liveMeta = (await this.store.getTask(task.id).catch(() => null))?.sourceMetadata ?? task.sourceMetadata;
216874
216935
  const priorClearCount = typeof liveMeta?.duplicateMarkerClearCount === "number" ? liveMeta.duplicateMarkerClearCount : 0;
216875
216936
  if (isNearDuplicateCanonicalInactive(canonicalTask ?? void 0, canonicalFlags)) {
216876
216937
  if (canClearInactiveMarker) {
216877
- const alreadyDismissed = isTriageDuplicateKeepAcknowledged(liveMeta, canonicalId);
216878
216938
  await this.clearDuplicateMarkerForReplan(
216879
216939
  task,
216880
216940
  canonicalId,
216881
216941
  buildInactiveDuplicateClearFeedback(canonicalId),
216882
- { exhausted: alreadyDismissed || priorClearCount >= 1, priorClearCount }
216942
+ { exhausted: false, priorClearCount }
216883
216943
  );
216884
216944
  }
216885
216945
  return;
@@ -217119,7 +217179,7 @@ Remove or replace these ids and call fn_task_create again.`
217119
217179
  if (!canonicalTask) {
217120
217180
  return;
217121
217181
  }
217122
- const canonicalFlags = await resolveNearDuplicateCanonicalFlags2(this.store, canonicalTask);
217182
+ const canonicalFlags = await resolveNearDuplicateCanonicalFlags(this.store, canonicalTask);
217123
217183
  if (isNearDuplicateCanonicalInactive(canonicalTask, canonicalFlags)) {
217124
217184
  planLog.log(`${task.id}: near-duplicate candidate ${canonical.id} is inactive; skipping near-duplicate flag`);
217125
217185
  return;
@@ -232990,13 +233050,15 @@ Graph value: ${failureValue}` : ""}`, budget.key),
232990
233050
  recoveryRehome: true
232991
233051
  });
232992
233052
  }
232993
- await this.store.updateTask(liveTask.id, { status: "queued", blockedBy: unmetDeps[0] }, this.getRunContextFor(liveTask.id));
232994
- await this.store.logEntry(
232995
- liveTask.id,
232996
- `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`,
232997
- "Executor pre-dispatch dependency gate blocked workflow/authoritative execution.",
232998
- this.getRunContextFor(liveTask.id)
232999
- );
233053
+ const normalizedUnmetDeps = [...new Set(unmetDeps)].sort();
233054
+ await this.store.transitionQueuedEpisode(liveTask.id, {
233055
+ signature: `dependency:${normalizedUnmetDeps.join(",")}`,
233056
+ blockedBy: unmetDeps[0] ?? null,
233057
+ overlapBlockedBy: liveTask.overlapBlockedBy ?? null,
233058
+ action: `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`,
233059
+ outcome: "Executor pre-dispatch dependency gate blocked workflow/authoritative execution.",
233060
+ runContext: this.getRunContextFor(liveTask.id)
233061
+ });
233000
233062
  executorLog.log(`${liveTask.id}: executor dispatch blocked by unmet dependencies: ${unmetDeps.join(", ")}`);
233001
233063
  return true;
233002
233064
  }
@@ -258624,6 +258686,40 @@ var init_in_process_runtime = __esm({
258624
258686
  throw err;
258625
258687
  }
258626
258688
  }
258689
+ /**
258690
+ * Close every process-local admission source without aborting work that is
258691
+ * already running. Development source reload uses this boundary before it
258692
+ * waits for the live-agent count to reach zero.
258693
+ */
258694
+ beginDrain() {
258695
+ if (this.status !== "active") return;
258696
+ this.setStatus("paused");
258697
+ if (this.workflowContinuationTimer) {
258698
+ clearInterval(this.workflowContinuationTimer);
258699
+ this.workflowContinuationTimer = void 0;
258700
+ }
258701
+ const admissionStops = [
258702
+ ["self-healing manager", () => this.selfHealingManager?.stop()],
258703
+ ["routine scheduler", () => this.routineScheduler?.stop()],
258704
+ ["trigger scheduler", () => this.triggerScheduler?.stop()],
258705
+ ["stuck task detector", () => this.stuckTaskDetector?.stop()],
258706
+ ["heartbeat monitor", () => this.heartbeatMonitor?.stop()],
258707
+ ["triage processor", () => this.triageProcessor?.stop()],
258708
+ ["scheduler", () => this.scheduler?.stop()],
258709
+ ["mission autopilot", () => this.missionAutopilot?.stop()],
258710
+ ["mission execution loop", () => this.missionExecutionLoop?.stop()]
258711
+ ];
258712
+ for (const [label, stop] of admissionStops) {
258713
+ try {
258714
+ stop();
258715
+ } catch (error3) {
258716
+ runtimeLog.warn(
258717
+ `Failed to stop ${label} while draining ${this.config.projectId}: ${error3 instanceof Error ? error3.message : String(error3)}`
258718
+ );
258719
+ }
258720
+ }
258721
+ runtimeLog.log(`InProcessRuntime draining for ${this.config.projectId}`);
258722
+ }
258627
258723
  /**
258628
258724
  * Stop the runtime with graceful shutdown.
258629
258725
  *
@@ -262738,6 +262834,11 @@ ${detail}`);
262738
262834
  this.started = false;
262739
262835
  runtimeLog.log(`ProjectEngine stopped for ${this.config.projectId}`);
262740
262836
  }
262837
+ /** Stop new lifecycle admission while allowing active work to finish. */
262838
+ beginDrain() {
262839
+ this.shuttingDown = true;
262840
+ this.runtime.beginDrain();
262841
+ }
262741
262842
  // ── Public accessors ──
262742
262843
  /** Get the underlying InProcessRuntime. */
262743
262844
  getRuntime() {
@@ -266316,14 +266417,30 @@ var init_project_engine_manager = __esm({
266316
266417
  }
266317
266418
  runtimeLog.log(`Engine startup complete: ${started} started, ${failed} failed`);
266318
266419
  }
266319
- /** Gracefully stop all engines and reconciliation. */
266320
- async stopAll() {
266420
+ /** Close admission on every engine and stop reconciliation without tearing engines down. */
266421
+ beginDrain() {
266321
266422
  this.stopped = true;
266322
266423
  this.reconciliationStopped = true;
266323
266424
  if (this.reconciliationInterval !== null) {
266324
266425
  clearInterval(this.reconciliationInterval);
266325
266426
  this.reconciliationInterval = null;
266326
266427
  }
266428
+ for (const engine of this.engines.values()) {
266429
+ try {
266430
+ engine.beginDrain?.();
266431
+ } catch (error3) {
266432
+ runtimeLog.warn(
266433
+ `Engine drain error: ${error3 instanceof Error ? error3.message : String(error3)}`
266434
+ );
266435
+ }
266436
+ }
266437
+ for (const starting of this.starting.values()) {
266438
+ void starting.then((engine) => engine.beginDrain?.()).catch(() => void 0);
266439
+ }
266440
+ }
266441
+ /** Gracefully stop all engines and reconciliation. */
266442
+ async stopAll() {
266443
+ this.beginDrain();
266327
266444
  try {
266328
266445
  this.centralCore.stopDiscovery();
266329
266446
  await this.centralCore.markLocalNodeOffline();
@@ -292512,7 +292629,7 @@ function buildDuplicateQuery(title2, description) {
292512
292629
  const selected = deduped.sort((left, right) => right.length - left.length).slice(0, 5);
292513
292630
  return selected.join(" ");
292514
292631
  }
292515
- async function computeDuplicateMatches(scopedStore, input) {
292632
+ async function computeDuplicateMatches(scopedStore, input, classifyBlocker) {
292516
292633
  const query = buildDuplicateQuery(input.title, input.description);
292517
292634
  if (query.length === 0) {
292518
292635
  return [];
@@ -292522,7 +292639,12 @@ async function computeDuplicateMatches(scopedStore, input) {
292522
292639
  includeArchived: false,
292523
292640
  limit: 20
292524
292641
  });
292525
- const candidates = results.map((task) => ({
292642
+ const eligibility = await Promise.all(results.map(async (task) => ({
292643
+ task,
292644
+ blocker: await classifyBlocker(task)
292645
+ })));
292646
+ const eligibleResults = eligibility.filter(({ blocker }) => blocker).map(({ task }) => task);
292647
+ const candidates = eligibleResults.map((task) => ({
292526
292648
  id: task.id,
292527
292649
  title: task.title ?? "",
292528
292650
  description: task.description ?? "",
@@ -292540,6 +292662,10 @@ async function computeDuplicateMatches(scopedStore, input) {
292540
292662
  }
292541
292663
  );
292542
292664
  }
292665
+ async function isDuplicateBlocker(store, canonical) {
292666
+ const flags = await resolveNearDuplicateCanonicalFlags(store, canonical);
292667
+ return !isNearDuplicateCanonicalInactive(canonical, flags);
292668
+ }
292543
292669
  function buildReviewerAgentItemId(input) {
292544
292670
  const stepPart = input.step ? `step-${input.step}` : "step-na";
292545
292671
  const verdictPart = (input.verdict ?? "unknown").toLowerCase();
@@ -292951,7 +293077,7 @@ function registerTaskWorkflowRoutes(ctx, deps) {
292951
293077
  description: description.trim(),
292952
293078
  limit,
292953
293079
  threshold
292954
- });
293080
+ }, (canonical) => isDuplicateBlocker(scopedStore, canonical));
292955
293081
  res.json({ matches });
292956
293082
  return;
292957
293083
  } catch (err) {
@@ -293071,6 +293197,15 @@ function registerTaskWorkflowRoutes(ctx, deps) {
293071
293197
  }
293072
293198
  } : void 0;
293073
293199
  const normalizedSource = source && typeof source === "object" && "sourceType" in source && typeof source.sourceType === "string" ? source : { sourceType: "api" };
293200
+ const duplicateBlockerVerdicts = /* @__PURE__ */ new Map();
293201
+ const classifyDuplicateBlocker = (canonical) => {
293202
+ const key = `${canonical.id}:${canonical.column}:${canonical.deletedAt ?? ""}`;
293203
+ const existing = duplicateBlockerVerdicts.get(key);
293204
+ if (existing) return existing;
293205
+ const verdict = isDuplicateBlocker(scopedStore, canonical);
293206
+ duplicateBlockerVerdicts.set(key, verdict);
293207
+ return verdict;
293208
+ };
293074
293209
  const requestedBranchMode = getBranchSelectionMode(branchSelection);
293075
293210
  const { branch: normalizedBranch, baseBranch: normalizedBaseBranch, sharedFeatureBranch } = resolveBranchSelection(branchSelection, branch, baseBranch);
293076
293211
  let validatedGithubTracking;
@@ -293156,7 +293291,7 @@ function registerTaskWorkflowRoutes(ctx, deps) {
293156
293291
  contentFingerprint = deterministicGuard.fingerprint;
293157
293292
  let matchesAfterAckFilter = [];
293158
293293
  try {
293159
- if (deterministicGuard.action === "duplicate" && deterministicGuard.existing) {
293294
+ if (deterministicGuard.action === "duplicate" && deterministicGuard.existing && await classifyDuplicateBlocker(deterministicGuard.existing)) {
293160
293295
  throw conflict("duplicate_candidates", {
293161
293296
  matches: [{
293162
293297
  id: deterministicGuard.existing.id,
@@ -293171,7 +293306,7 @@ function registerTaskWorkflowRoutes(ctx, deps) {
293171
293306
  const duplicateMatches = bypassDuplicateCheck === true ? [] : await computeDuplicateMatches(scopedStore, {
293172
293307
  title: normalizedTitle,
293173
293308
  description: normalizedDescription
293174
- });
293309
+ }, classifyDuplicateBlocker);
293175
293310
  matchesAfterAckFilter = duplicateMatches.filter((match) => !acknowledgedDuplicateIds.includes(match.id));
293176
293311
  if (matchesAfterAckFilter.length > 0) {
293177
293312
  throw conflict("duplicate_candidates", { matches: matchesAfterAckFilter });
@@ -293197,11 +293332,17 @@ function registerTaskWorkflowRoutes(ctx, deps) {
293197
293332
  const fullRows = await scopedStore.listTasks({ slim: false, includeArchived: false });
293198
293333
  const byId = new Map(fullRows.map((row) => [row.id, row]));
293199
293334
  const candidateMap = /* @__PURE__ */ new Map();
293200
- for (const row of candidateRows) {
293335
+ const classifiedRows = await Promise.all(candidateRows.map(async (row) => {
293336
+ const full = byId.get(row.id);
293337
+ return { row, full, blocker: full ? await classifyDuplicateBlocker(full) : true };
293338
+ }));
293339
+ for (const { row, full, blocker } of classifiedRows) {
293201
293340
  if (acknowledgedDuplicateIds.includes(row.id)) {
293202
293341
  continue;
293203
293342
  }
293204
- const full = byId.get(row.id);
293343
+ if (!blocker) {
293344
+ continue;
293345
+ }
293205
293346
  candidateMap.set(row.id, {
293206
293347
  id: row.id,
293207
293348
  title: row.title ?? "",
@@ -293249,7 +293390,7 @@ ${normalizedDescription}`;
293249
293390
  const explicitMarkerBypassed = bypassDuplicateCheck === true || (explicitDuplicateMarker ? acknowledgedDuplicateIds.includes(explicitDuplicateMarker.canonicalId) : false);
293250
293391
  if (explicitDuplicateMarker && !explicitMarkerBypassed) {
293251
293392
  const canonical = await scopedStore.getTask(explicitDuplicateMarker.canonicalId).catch(() => null);
293252
- if (canonical && !canonical.deletedAt) {
293393
+ if (canonical && !canonical.deletedAt && await classifyDuplicateBlocker(canonical)) {
293253
293394
  try {
293254
293395
  await scopedStore.recordActivity({
293255
293396
  type: "task:auto-archived-duplicate",
@@ -293356,7 +293497,8 @@ ${normalizedDescription}`;
293356
293497
  createdTask: taskWithBranchContext,
293357
293498
  fingerprint: bypassDuplicateCheck === true ? null : contentFingerprint,
293358
293499
  windowMs: 6e4,
293359
- logger: runtimeLogger
293500
+ logger: runtimeLogger,
293501
+ onDuplicate: async (canonical) => await classifyDuplicateBlocker(canonical) ? "archive-created" : "keep-created"
293360
293502
  });
293361
293503
  if (deterministicReconcile.outcome === "archived") {
293362
293504
  res.status(200).json(deterministicReconcile.canonical);
@@ -396597,6 +396739,96 @@ var init_startup_phase = __esm({
396597
396739
  }
396598
396740
  });
396599
396741
 
396742
+ // src/commands/dev-source-restart.ts
396743
+ function isDevSourceChangeMessage(message) {
396744
+ return Boolean(message) && typeof message === "object" && message.type === DEV_SOURCE_CHANGE_MESSAGE;
396745
+ }
396746
+ function registerDevSourceRestart({
396747
+ enabled,
396748
+ processEvents = process,
396749
+ notifyArmed,
396750
+ beginDrain,
396751
+ getLiveRunningAgentCounts,
396752
+ requestRestart,
396753
+ logger: logger3 = console,
396754
+ recheckIntervalMs = 15e3
396755
+ }) {
396756
+ if (!enabled) return () => void 0;
396757
+ let disposed = false;
396758
+ let pending = false;
396759
+ let checking = false;
396760
+ let restartAccepted = false;
396761
+ let deferredForActiveWork = false;
396762
+ let drainStarted = false;
396763
+ let timer2;
396764
+ const schedule = (delayMs) => {
396765
+ if (disposed || timer2) return;
396766
+ timer2 = setTimeout(() => {
396767
+ timer2 = void 0;
396768
+ void checkRestartSafety();
396769
+ }, delayMs);
396770
+ timer2.unref?.();
396771
+ };
396772
+ const checkRestartSafety = async () => {
396773
+ if (disposed || restartAccepted || !pending || checking) return;
396774
+ checking = true;
396775
+ try {
396776
+ const { currentlyActive } = await getLiveRunningAgentCounts();
396777
+ if (currentlyActive > 0) {
396778
+ if (!deferredForActiveWork) {
396779
+ const noun = currentlyActive === 1 ? "agent" : "agents";
396780
+ logger3.log(`[fusion:dev] source restart deferred until ${currentlyActive} active ${noun} reach a safe boundary`);
396781
+ deferredForActiveWork = true;
396782
+ }
396783
+ schedule(recheckIntervalMs);
396784
+ return;
396785
+ }
396786
+ if (deferredForActiveWork) {
396787
+ logger3.log("[fusion:dev] active work drained \u2014 applying pending source restart");
396788
+ deferredForActiveWork = false;
396789
+ }
396790
+ restartAccepted = requestRestart("dev-source-change");
396791
+ if (restartAccepted) {
396792
+ pending = false;
396793
+ } else {
396794
+ logger3.warn("[fusion:dev] source restart request was declined by the host lifecycle");
396795
+ schedule(recheckIntervalMs);
396796
+ }
396797
+ } catch (error3) {
396798
+ logger3.warn(`[fusion:dev] could not verify restart safety: ${error3 instanceof Error ? error3.message : String(error3)}`);
396799
+ schedule(recheckIntervalMs);
396800
+ } finally {
396801
+ checking = false;
396802
+ }
396803
+ };
396804
+ const onMessage = (message) => {
396805
+ if (restartAccepted || !isDevSourceChangeMessage(message)) return;
396806
+ pending = true;
396807
+ if (drainStarted) return;
396808
+ drainStarted = true;
396809
+ void Promise.resolve().then(beginDrain).then(() => schedule(0)).catch((error3) => {
396810
+ logger3.warn(`[fusion:dev] could not begin source restart drain: ${error3 instanceof Error ? error3.message : String(error3)}`);
396811
+ });
396812
+ };
396813
+ processEvents.on("message", onMessage);
396814
+ notifyArmed?.();
396815
+ return () => {
396816
+ disposed = true;
396817
+ pending = false;
396818
+ if (timer2) clearTimeout(timer2);
396819
+ timer2 = void 0;
396820
+ processEvents.off("message", onMessage);
396821
+ };
396822
+ }
396823
+ var DEV_SOURCE_CHANGE_MESSAGE, DEV_SOURCE_RESTART_ARMED_MESSAGE;
396824
+ var init_dev_source_restart = __esm({
396825
+ "src/commands/dev-source-restart.ts"() {
396826
+ "use strict";
396827
+ DEV_SOURCE_CHANGE_MESSAGE = "fusion:dev-source-changed";
396828
+ DEV_SOURCE_RESTART_ARMED_MESSAGE = "fusion:dev-source-restart-armed";
396829
+ }
396830
+ });
396831
+
396600
396832
  // src/commands/dashboard.ts
396601
396833
  var dashboard_exports = {};
396602
396834
  __export(dashboard_exports, {
@@ -397342,6 +397574,21 @@ async function runDashboard(port, opts = {}) {
397342
397574
  getRecent: (limit) => logSink.getRecentEntries(limit),
397343
397575
  subscribe: (listener) => logSink.subscribeEntries(listener)
397344
397576
  };
397577
+ const bindDevSourceRestart = (centralCore, beginDrain) => {
397578
+ disposeCallbacks.push(registerDevSourceRestart({
397579
+ enabled: process.env.FUSION_DEV_WATCH === "1" && systemControlForServer.supervised && Boolean(systemControlForServer.sourceWorkspaceRoot),
397580
+ beginDrain,
397581
+ notifyArmed: () => {
397582
+ process.send?.({ type: DEV_SOURCE_RESTART_ARMED_MESSAGE });
397583
+ },
397584
+ getLiveRunningAgentCounts: () => centralCore.getLiveRunningAgentCounts(),
397585
+ requestRestart: (reason) => requestSelfRestart?.(reason) ?? false,
397586
+ logger: {
397587
+ log: (message) => logSink.log(message, "dashboard"),
397588
+ warn: (message) => logSink.warn(message, "dashboard")
397589
+ }
397590
+ }));
397591
+ };
397345
397592
  const SHUTDOWN_HARD_EXIT_GRACE_MS = 3e3;
397346
397593
  const SHUTDOWN_STEP_SLOW_MS = 1e3;
397347
397594
  let currentShutdownStep = null;
@@ -398079,6 +398326,7 @@ async function runDashboard(port, opts = {}) {
398079
398326
  }, 300);
398080
398327
  return true;
398081
398328
  };
398329
+ bindDevSourceRestart(centralCoreForEngine, () => engineManager.beginDrain());
398082
398330
  registerHandler(process, "SIGINT", () => void shutdown("SIGINT"));
398083
398331
  registerHandler(process, "SIGTERM", () => void shutdown("SIGTERM"));
398084
398332
  registerHandler(process, "SIGHUP", () => {
@@ -398339,6 +398587,12 @@ async function runDashboard(port, opts = {}) {
398339
398587
  }, 300);
398340
398588
  return true;
398341
398589
  };
398590
+ if (centralCoreForMesh) {
398591
+ bindDevSourceRestart(centralCoreForMesh, () => {
398592
+ triggerScheduler?.stop();
398593
+ heartbeatMonitorImpl?.stop();
398594
+ });
398595
+ }
398342
398596
  registerHandler(process, "SIGINT", () => void devShutdown("SIGINT"));
398343
398597
  registerHandler(process, "SIGTERM", () => void devShutdown("SIGTERM"));
398344
398598
  registerHandler(process, "SIGHUP", () => {
@@ -399113,6 +399367,7 @@ var init_dashboard = __esm({
399113
399367
  init_dashboard_tui();
399114
399368
  init_dashboard_startup_chain();
399115
399369
  init_startup_phase();
399370
+ init_dev_source_restart();
399116
399371
  processDiagnosticsRegistered = false;
399117
399372
  diagnosticIntervalHandle = null;
399118
399373
  DIAGNOSTIC_INTERVAL_MS = 30 * 60 * 1e3;