@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
@@ -11923,6 +11923,11 @@ APPROVE when the plan is executable and verifiable, not when it is cosmetically
11923
11923
  });
11924
11924
 
11925
11925
  // ../core/src/agents/agent-prompts.ts
11926
+ function buildPlanningDuplicatePolicyInstruction() {
11927
+ return `## Duplicate policy for this task
11928
+
11929
+ 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.`;
11930
+ }
11926
11931
  function buildTriageHeartbeatGuidance(options = {}) {
11927
11932
  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.";
11928
11933
  const patrolSafetyLines = options.plannerHeartbeatPatrolEnabled === false ? "" : `
@@ -12214,7 +12219,7 @@ Write a lean, executable PROMPT.md quickly. Preserve safety gates, but skip heav
12214
12219
  - Preserve required safety sections for bugs, workflow routing, forensic tasks, and decision-only work.
12215
12220
 
12216
12221
  ## Duplicate check
12217
- 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.
12222
+ 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.
12218
12223
 
12219
12224
  ## Required PROMPT.md shape
12220
12225
  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.
@@ -12482,9 +12487,9 @@ Verified facts about this codebase's storage \u2014 cite these correctly so Plan
12482
12487
  - 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.
12483
12488
 
12484
12489
  ## Duplicate check
12485
- 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).
12486
- For any likely match in \`done\` or \`archived\`, call \`fn_task_show\` to inspect details before deciding.
12487
- If a task already covers the same work (even if worded differently), do not write a spec.
12490
+ 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).
12491
+ Do not search completed or archived work for duplicate candidates.
12492
+ If an actionable task already covers the same work (even if worded differently), do not write a spec.
12488
12493
  Instead you MUST still write PROMPT.md, with its ENTIRE contents being this one line and nothing else:
12489
12494
  \`DUPLICATE: {existing-task-id}\`
12490
12495
  Writing that file IS how you report the duplicate. Reporting it only in your reply is not recorded:
@@ -12515,7 +12520,7 @@ Workflow policy can disable proactive oversized-task splitting for operators who
12515
12520
  ## Triage tools
12516
12521
  You have these extra tools during triage:
12517
12522
  - \`fn_task_list\` \u2014 list existing active tasks
12518
- - \`fn_task_search\` \u2014 keyword search across tasks, including done and archived tasks
12523
+ - \`fn_task_search\` \u2014 keyword search across active tasks by default; history is opt-in for non-duplicate research
12519
12524
  - \`fn_task_show\` \u2014 inspect a task and its PROMPT.md
12520
12525
  - \`fn_task_create\` \u2014 create a child/follow-up task while triaging
12521
12526
  - \`fn_task_document_write\` \u2014 save a planning document (e.g., key="plan")
@@ -20255,6 +20260,7 @@ var init_project = __esm({
20255
20260
  worktree: text("worktree"),
20256
20261
  blockedBy: text("blocked_by"),
20257
20262
  overlapBlockedBy: text("overlap_blocked_by"),
20263
+ queuedLogEpisodeSignature: text("queued_log_episode_signature"),
20258
20264
  paused: integer("paused").default(0),
20259
20265
  userPaused: integer("user_paused").default(0),
20260
20266
  pausedReason: text("paused_reason"),
@@ -24405,6 +24411,22 @@ var init_near_duplicate_canonical = __esm({
24405
24411
  }
24406
24412
  });
24407
24413
 
24414
+ // ../core/src/duplicates/near-duplicate-canonical-flags.ts
24415
+ async function resolveNearDuplicateCanonicalFlags(store, canonical) {
24416
+ if (!canonical?.column) return void 0;
24417
+ const workflow = await resolveWorkflowIrForTask(store, canonical.id).catch(() => void 0);
24418
+ if (!workflow || workflow.version !== "v2") return void 0;
24419
+ const column = workflow.columns.find((candidate) => candidate.id === canonical.column);
24420
+ return column ? resolveColumnFlags(column) : void 0;
24421
+ }
24422
+ var init_near_duplicate_canonical_flags = __esm({
24423
+ "../core/src/duplicates/near-duplicate-canonical-flags.ts"() {
24424
+ "use strict";
24425
+ init_trait_registry();
24426
+ init_workflow_ir_resolver();
24427
+ }
24428
+ });
24429
+
24408
24430
  // ../core/src/git/gitlab-tracking.ts
24409
24431
  function formatGitLabTrackedItemRef(item) {
24410
24432
  const marker = item.kind === "merge_request" ? "!" : "#";
@@ -38166,6 +38188,7 @@ async function applySchemaBaseline(db, options = {}) {
38166
38188
  const taskLifecycleConsumersAlreadyApplied = applied.includes(TASK_LIFECYCLE_CONSUMERS_VERSION);
38167
38189
  const validatorInputFingerprintAlreadyApplied = applied.includes(VALIDATOR_INPUT_FINGERPRINT_VERSION);
38168
38190
  const unplannedExecutionBlockDedupeAlreadyApplied = applied.includes(UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION);
38191
+ const queuedEpisodeSignatureAlreadyApplied = applied.includes(QUEUED_EPISODE_SIGNATURE_VERSION);
38169
38192
  assertBinaryNotOlderThanDatabase(applied);
38170
38193
  let schemaChanged = false;
38171
38194
  if (!baselineAlreadyApplied) {
@@ -38559,17 +38582,23 @@ async function applySchemaBaseline(db, options = {}) {
38559
38582
  await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION}) ON CONFLICT (version) DO NOTHING`);
38560
38583
  schemaChanged = true;
38561
38584
  }
38585
+ if (!queuedEpisodeSignatureAlreadyApplied) {
38586
+ const migrationSql = await readFile4(QUEUED_EPISODE_SIGNATURE_MIGRATION_PATH, "utf8");
38587
+ await tx.execute(sql.raw(migrationSql));
38588
+ await tx.execute(sql`INSERT INTO public.${sql.identifier(MIGRATION_BOOKKEEPING_TABLE)} (version) VALUES (${QUEUED_EPISODE_SIGNATURE_VERSION}) ON CONFLICT (version) DO NOTHING`);
38589
+ schemaChanged = true;
38590
+ }
38562
38591
  return { applied: schemaChanged, pluginHooksRun: pluginHooks.length };
38563
38592
  });
38564
38593
  }
38565
- 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;
38594
+ 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;
38566
38595
  var init_schema_applier = __esm({
38567
38596
  "../core/src/postgres/schema-applier.ts"() {
38568
38597
  "use strict";
38569
38598
  init_drizzle_orm();
38570
38599
  init_plugin_schema_hook();
38571
38600
  init_advisory_locks();
38572
- SCHEMA_BASELINE_VERSION = "0043";
38601
+ SCHEMA_BASELINE_VERSION = "0044";
38573
38602
  TASK_DECLARED_SYMBOLS_VERSION = "0028";
38574
38603
  INITIAL_SCHEMA_VERSION = "0000";
38575
38604
  AUTOMATION_ISOLATION_SCHEMA_VERSION = "0001";
@@ -38613,6 +38642,7 @@ var init_schema_applier = __esm({
38613
38642
  TASK_LIFECYCLE_CONSUMERS_VERSION = "0041";
38614
38643
  VALIDATOR_INPUT_FINGERPRINT_VERSION = "0042";
38615
38644
  UNPLANNED_EXECUTION_BLOCK_DEDUPE_VERSION = "0043";
38645
+ QUEUED_EPISODE_SIGNATURE_VERSION = "0044";
38616
38646
  LEGACY_ADOPTION_DRAINED_MARKER_FUNCTION = "fusion_mark_legacy_adoption_drained";
38617
38647
  StaleBinarySchemaError = class extends Error {
38618
38648
  constructor(databaseVersion, binaryVersion) {
@@ -38736,6 +38766,7 @@ var init_schema_applier = __esm({
38736
38766
  TASK_LIFECYCLE_CONSUMERS_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0041_fn_8685_task_lifecycle_consumers.sql");
38737
38767
  VALIDATOR_INPUT_FINGERPRINT_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0042_fn_8694_validator_input_fingerprint.sql");
38738
38768
  UNPLANNED_EXECUTION_BLOCK_DEDUPE_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0043_fn8768_dispatch_dedupe.sql");
38769
+ QUEUED_EPISODE_SIGNATURE_MIGRATION_PATH = join8(MIGRATIONS_DIR, "0044_fn_8785_queued_episode_signature.sql");
38739
38770
  }
38740
38771
  });
38741
38772
 
@@ -61675,9 +61706,14 @@ var init_central_core = __esm({
61675
61706
  incremented by real work and the durable counter it maintained was fiction.
61676
61707
 
61677
61708
  `getLiveRunningAgentCounts` SURVIVES and is now the only global readout. It is
61678
- TELEMETRY, not a limiter: it derives live per-project agent counts from the
61679
- registered side-effect-safe source so the dashboard can show "N running (all
61680
- projects)". Nothing gates on it.
61709
+ TELEMETRY, not a capacity limiter: it derives live per-project agent counts
61710
+ from the registered side-effect-safe source so the dashboard can show "N
61711
+ running (all projects)". Task admission does not gate on it.
61712
+
61713
+ FNXC:DevEngineWatch 2026-08-04-01:25:
61714
+ The opt-in source-development restart also reads this conservative telemetry
61715
+ to wait for active work to drain. That is shutdown safety, not capacity
61716
+ arbitration; it never reserves slots or changes task admission.
61681
61717
  */
61682
61718
  async getLiveRunningAgentCounts(options) {
61683
61719
  this.ensureInitialized();
@@ -62939,6 +62975,7 @@ var init_persistence = __esm({
62939
62975
  defineTaskColumn("worktree", (task) => task.worktree ?? null),
62940
62976
  defineTaskColumn("blockedBy", (task) => task.blockedBy ?? null),
62941
62977
  defineTaskColumn("overlapBlockedBy", (task) => task.overlapBlockedBy ?? null),
62978
+ defineTaskColumn("queuedLogEpisodeSignature", (task) => task.queuedLogEpisodeSignature ?? null),
62942
62979
  defineTaskColumn("paused", (task) => task.paused ? 1 : 0),
62943
62980
  defineTaskColumn("pausedReason", (task) => task.pausedReason ?? null),
62944
62981
  defineTaskColumn("wedgeNotification", (task) => toJsonNullable(task.wedgeNotification)),
@@ -63353,6 +63390,7 @@ function rowToTask(row) {
63353
63390
  worktree: row.worktree || void 0,
63354
63391
  blockedBy: row.blockedBy || void 0,
63355
63392
  overlapBlockedBy: row.overlapBlockedBy || void 0,
63393
+ queuedLogEpisodeSignature: row.queuedLogEpisodeSignature || void 0,
63356
63394
  paused: row.paused ? true : void 0,
63357
63395
  pausedReason: row.pausedReason || void 0,
63358
63396
  wedgeNotification: fromJson(row.wedgeNotification) ?? void 0,
@@ -69472,6 +69510,14 @@ function noop3() {
69472
69510
  function matchesParentScope(task, sourceParentTaskId) {
69473
69511
  return !sourceParentTaskId || task.sourceParentTaskId === sourceParentTaskId;
69474
69512
  }
69513
+ async function findActiveDuplicate(store, candidates, predicate) {
69514
+ for (const candidate of candidates) {
69515
+ if (!predicate(candidate)) continue;
69516
+ const flags = await resolveNearDuplicateCanonicalFlags(store, candidate);
69517
+ if (!isNearDuplicateCanonicalInactive(candidate, flags)) return candidate;
69518
+ }
69519
+ return void 0;
69520
+ }
69475
69521
  async function runDeterministicDuplicateGuard(store, input, opts) {
69476
69522
  const fingerprint = computeContentFingerprint(input);
69477
69523
  if (opts?.bypass === true || !fingerprint) {
@@ -69485,7 +69531,9 @@ async function runDeterministicDuplicateGuard(store, input, opts) {
69485
69531
  windowMs,
69486
69532
  includeArchived: false
69487
69533
  });
69488
- const deterministicConflict = deterministicMatches.find(
69534
+ const deterministicConflict = await findActiveDuplicate(
69535
+ store,
69536
+ deterministicMatches,
69489
69537
  (match) => matchesParentScope(match, opts?.sourceParentTaskId) && !acknowledged.has(match.id)
69490
69538
  );
69491
69539
  if (deterministicConflict) {
@@ -69532,7 +69580,9 @@ async function runDeterministicDuplicateGuard(store, input, opts) {
69532
69580
  windowMs,
69533
69581
  includeArchived: false
69534
69582
  });
69535
- const deterministicConflict = deterministicMatches.find(
69583
+ const deterministicConflict = await findActiveDuplicate(
69584
+ store,
69585
+ deterministicMatches,
69536
69586
  (match) => matchesParentScope(match, opts.sourceParentTaskId) && !acknowledged.has(match.id)
69537
69587
  );
69538
69588
  if (deterministicConflict) {
@@ -69556,7 +69606,9 @@ async function reconcileDeterministicDuplicate(store, args) {
69556
69606
  windowMs: clampWindowMs(args.windowMs),
69557
69607
  includeArchived: false
69558
69608
  });
69559
- const olderSibling = siblings.find(
69609
+ const olderSibling = await findActiveDuplicate(
69610
+ store,
69611
+ siblings,
69560
69612
  (sibling) => sibling.id !== args.createdTask.id && sibling.createdAt < args.createdTask.createdAt && matchesParentScope(sibling, args.sourceParentTaskId)
69561
69613
  );
69562
69614
  if (!olderSibling) {
@@ -69615,6 +69667,8 @@ var init_duplicate_guard = __esm({
69615
69667
  "../core/src/duplicates/duplicate-guard.ts"() {
69616
69668
  "use strict";
69617
69669
  init_duplicate_detection();
69670
+ init_near_duplicate_canonical();
69671
+ init_near_duplicate_canonical_flags();
69618
69672
  init_workflow_lifecycle_traits();
69619
69673
  FINGERPRINT_WINDOW_DEFAULT_MS = 6e5;
69620
69674
  FINGERPRINT_WINDOW_MAX_MS = 36e5;
@@ -93226,6 +93280,50 @@ async function runPluginColumnTransitionHooksImpl(store, taskId, workflowIr, fro
93226
93280
  await writeMarker2(remaining);
93227
93281
  }
93228
93282
  }
93283
+ async function transitionQueuedEpisodeImpl(store, id, transition) {
93284
+ const layer = store.asyncLayer;
93285
+ const projectId = layer.projectId?.trim() || "__legacy_unscoped__";
93286
+ const now = (/* @__PURE__ */ new Date()).toISOString();
93287
+ const result = await layer.transactionImmediate(async (tx) => {
93288
+ await acquireTaskAdvisoryXactLock(tx, projectId, id);
93289
+ const rows = await tx.select().from(project_exports.tasks).where(and(
93290
+ eq(project_exports.tasks.projectId, projectId),
93291
+ eq(project_exports.tasks.id, id),
93292
+ isNull(project_exports.tasks.deletedAt)
93293
+ ));
93294
+ const current = rows[0];
93295
+ if (!current) throw new Error(`Task ${id} not found or archived while queuing`);
93296
+ const appended = !(current.status === "queued" && (current.blockedBy ?? null) === transition.blockedBy && (current.overlapBlockedBy ?? null) === transition.overlapBlockedBy && (current.queuedLogEpisodeSignature ?? null) === transition.signature);
93297
+ const log41 = Array.isArray(current.log) ? [...current.log] : [];
93298
+ if (appended) {
93299
+ log41.push({
93300
+ timestamp: now,
93301
+ action: transition.action,
93302
+ outcome: truncateTaskLogOutcome(transition.outcome),
93303
+ ...transition.runContext ? { runContext: transition.runContext } : {}
93304
+ });
93305
+ const limit = getTaskActivityLogEntryLimit();
93306
+ if (log41.length > limit) log41.splice(0, log41.length - limit);
93307
+ }
93308
+ const updated = await tx.update(project_exports.tasks).set({
93309
+ status: "queued",
93310
+ blockedBy: transition.blockedBy,
93311
+ overlapBlockedBy: transition.overlapBlockedBy,
93312
+ queuedLogEpisodeSignature: transition.signature,
93313
+ ...appended ? { log: log41 } : {},
93314
+ updatedAt: now
93315
+ }).where(and(
93316
+ eq(project_exports.tasks.projectId, projectId),
93317
+ eq(project_exports.tasks.id, id)
93318
+ )).returning();
93319
+ return { appended, task: updated[0] };
93320
+ });
93321
+ const task = store.rowToTask(store.pgRowToTaskRow(result.task));
93322
+ await store.writeTaskJsonFile(store.taskDir(id), task);
93323
+ if (store.isWatching) store.taskCache.set(id, { ...task });
93324
+ store.emitTaskLifecycleEventSafely("task:updated", [task]);
93325
+ return { appended: result.appended, task };
93326
+ }
93229
93327
  async function checkAndRecordUnplannedExecutionBlockImpl(store, id, episode) {
93230
93328
  const layer = store.asyncLayer;
93231
93329
  const projectId = layer.projectId ?? "__legacy_unscoped__";
@@ -93337,6 +93435,7 @@ var init_audit_ops = __esm({
93337
93435
  init_comments();
93338
93436
  init_async_persistence();
93339
93437
  init_async_comments_attachments();
93438
+ init_task_advisory_lock();
93340
93439
  init_project_lane_vocabulary();
93341
93440
  init_schema2();
93342
93441
  }
@@ -99119,6 +99218,9 @@ var init_store = __esm({
99119
99218
  async checkAndRecordUnplannedExecutionBlock(id, episode) {
99120
99219
  return checkAndRecordUnplannedExecutionBlockImpl(this, id, episode);
99121
99220
  }
99221
+ async transitionQueuedEpisode(id, transition) {
99222
+ return transitionQueuedEpisodeImpl(this, id, transition);
99223
+ }
99122
99224
  async logEntry(id, action, outcome, runContext) {
99123
99225
  return logEntryImpl(this, id, action, outcome, runContext);
99124
99226
  }
@@ -127885,6 +127987,7 @@ __export(src_exports, {
127885
127987
  buildInsightExtractionPrompt: () => buildInsightExtractionPrompt,
127886
127988
  buildManualRetryResetPatch: () => buildManualRetryResetPatch,
127887
127989
  buildOriginalDescriptionSection: () => buildOriginalDescriptionSection,
127990
+ buildPlanningDuplicatePolicyInstruction: () => buildPlanningDuplicatePolicyInstruction,
127888
127991
  buildQmdCollectionAddArgs: () => buildQmdCollectionAddArgs,
127889
127992
  buildQmdRefreshCommands: () => buildQmdRefreshCommands,
127890
127993
  buildQmdSearchArgs: () => buildQmdSearchArgs,
@@ -128579,6 +128682,7 @@ __export(src_exports, {
128579
128682
  resolveMergerFallbackModel: () => resolveMergerFallbackModel,
128580
128683
  resolveMergerSettingsModel: () => resolveMergerSettingsModel,
128581
128684
  resolveMobileNavPrimaryItems: () => resolveMobileNavPrimaryItems,
128685
+ resolveNearDuplicateCanonicalFlags: () => resolveNearDuplicateCanonicalFlags,
128582
128686
  resolveNodeOverrideLanes: () => resolveNodeOverrideLanes,
128583
128687
  resolveOptionalReviewRevisionBudget: () => resolveOptionalReviewRevisionBudget,
128584
128688
  resolveOptionalStepRevisionBudget: () => resolveOptionalStepRevisionBudget,
@@ -128811,6 +128915,7 @@ var init_src2 = __esm({
128811
128915
  init_plugin_prompt_condition();
128812
128916
  init_plan_approval();
128813
128917
  init_near_duplicate_canonical();
128918
+ init_near_duplicate_canonical_flags();
128814
128919
  init_gitlab_tracking();
128815
128920
  init_planner_intervention2();
128816
128921
  init_planner_overseer_events();
@@ -169919,14 +170024,14 @@ async function findDefinedFeatureBootstrapDuplicate(store, input, sourceAgentId,
169919
170024
  if (!sourceAgentId && !sourceParentTaskId) return void 0;
169920
170025
  const candidates = await store.listTasks({ slim: true, includeArchived: true, includeDeleted: true });
169921
170026
  const byId = new Map(candidates.map((task) => [task.id, task]));
169922
- const isArchivedCandidate = await resolveArchivedColumnsForTasks(store, candidates);
170027
+ const isTerminalCandidate = await resolveTerminalColumnsForTasks(store, candidates);
169923
170028
  const matches = findSameAgentDuplicates({
169924
170029
  title: input.title,
169925
170030
  description: input.description,
169926
170031
  sourceParentTaskId
169927
170032
  }, candidates.flatMap((task) => {
169928
170033
  const createdAt = Date.parse(task.createdAt);
169929
- if (Number.isNaN(createdAt) || task.deletedAt || isArchivedCandidate(task)) return [];
170034
+ if (Number.isNaN(createdAt) || task.deletedAt || isTerminalCandidate(task)) return [];
169930
170035
  return [{
169931
170036
  id: task.id,
169932
170037
  title: task.title ?? "",
@@ -169977,21 +170082,6 @@ async function resolveTerminalColumnsForTasks(store, tasks2) {
169977
170082
  }
169978
170083
  return (task) => terminalByTaskId.get(task.id)?.has(task.column) === true;
169979
170084
  }
169980
- async function resolveArchivedColumnsForTasks(store, tasks2) {
169981
- const cache = /* @__PURE__ */ new Map();
169982
- const archivedByTaskId = /* @__PURE__ */ new Map();
169983
- for (const task of tasks2) {
169984
- if (archivedByTaskId.has(task.id)) continue;
169985
- const columns = /* @__PURE__ */ new Set(["archived"]);
169986
- try {
169987
- const ir2 = await resolveWorkflowIrForTask(store, task.id, cache);
169988
- if (ir2) for (const id of columnsWithFlag(ir2, "archived")) columns.add(id);
169989
- } catch {
169990
- }
169991
- archivedByTaskId.set(task.id, columns);
169992
- }
169993
- return (task) => archivedByTaskId.get(task.id)?.has(task.column) === true;
169994
- }
169995
170085
  async function createAgentTask(store, input, options) {
169996
170086
  const validateDuplicateCanonical = input.validateDuplicateCanonical;
169997
170087
  const settings = typeof store.getSettings === "function" ? await store.getSettings() : {};
@@ -170056,11 +170146,12 @@ async function createAgentTask(store, input, options) {
170056
170146
  try {
170057
170147
  const acknowledged = new Set(options?.acknowledgedDuplicates ?? []);
170058
170148
  const candidates = await store.findRecentTasksBySourceParentTaskId(sourceParentTaskId);
170149
+ const isTerminalCandidate = await resolveTerminalColumnsForTasks(store, candidates);
170059
170150
  const matches = findSameAgentDuplicates({
170060
170151
  title: input.title,
170061
170152
  description: input.description,
170062
170153
  sourceParentTaskId
170063
- }, candidates.map((candidate) => ({
170154
+ }, candidates.filter((candidate) => !isTerminalCandidate(candidate)).map((candidate) => ({
170064
170155
  id: candidate.id,
170065
170156
  title: candidate.title ?? "",
170066
170157
  description: candidate.description,
@@ -170288,7 +170379,7 @@ function createTaskSearchTool(store) {
170288
170379
  return {
170289
170380
  name: "fn_task_search",
170290
170381
  label: "Search Tasks",
170291
- description: "Keyword search across tasks, including done and archived tasks by default. Use for duplicate detection and work discovery before filing new tasks.",
170382
+ description: "Keyword search across active tasks by default. Done and archived history is opt-in and must not be used for duplicate detection.",
170292
170383
  parameters: taskSearchParams,
170293
170384
  execute: async (_id, params) => {
170294
170385
  const query = params.query.trim();
@@ -170301,10 +170392,10 @@ function createTaskSearchTool(store) {
170301
170392
  const limit = Math.min(50, Math.max(1, Math.floor(params.limit ?? 20)));
170302
170393
  const results = await store.searchTasks(query, {
170303
170394
  slim: true,
170304
- includeArchived: params.includeArchived ?? true,
170395
+ includeArchived: params.includeArchived ?? false,
170305
170396
  limit
170306
170397
  });
170307
- const includeDone = params.includeDone ?? true;
170398
+ const includeDone = params.includeDone ?? false;
170308
170399
  const isTerminalResult = includeDone ? void 0 : await resolveTerminalColumnsForTasks(store, results);
170309
170400
  const filtered = includeDone ? results : results.filter((task) => !isTerminalResult(task));
170310
170401
  const lines = filtered.map(formatTaskSummaryLine);
@@ -191422,6 +191513,9 @@ var Scheduler = class {
191422
191513
  }
191423
191514
  }
191424
191515
  }
191516
+ async transitionQueuedEpisode(task, input) {
191517
+ return (await this.store.transitionQueuedEpisode(task.id, input)).appended;
191518
+ }
191425
191519
  async logDispatchQueuedReason(taskId, reason, memoKey) {
191426
191520
  const key = `${taskId}:${memoKey ?? reason}`;
191427
191521
  if (this.wasDispatchQueuedReasonLogged.has(key)) {
@@ -191934,11 +192028,13 @@ var Scheduler = class {
191934
192028
  }
191935
192029
  const unmetDeps = getUnmetSchedulingDependencies(task, tasks2, schedulingDependencyOptions);
191936
192030
  if (unmetDeps.length > 0) {
191937
- await this.store.updateTask(task.id, {
191938
- status: "queued",
191939
- blockedBy: unmetDeps[0]
192031
+ const normalizedUnmetDeps = [...new Set(unmetDeps)].sort();
192032
+ await this.transitionQueuedEpisode(task, {
192033
+ signature: `dependency:${normalizedUnmetDeps.join(",")}`,
192034
+ blockedBy: unmetDeps[0] ?? null,
192035
+ overlapBlockedBy: task.overlapBlockedBy ?? null,
192036
+ action: `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`
191940
192037
  });
191941
- await this.logDispatchQueuedReason(task.id, `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`);
191942
192038
  this.options.onBlocked?.(task, unmetDeps);
191943
192039
  return null;
191944
192040
  }
@@ -192299,16 +192395,13 @@ var Scheduler = class {
192299
192395
  const overlappingTaskId = Array.from(activeScopes.entries()).sort(([aId], [bId]) => aId.localeCompare(bId)).find(([, activeScope]) => this.pathsOverlap(taskScope, activeScope))?.[0] ?? null;
192300
192396
  if (overlappingTaskId) {
192301
192397
  const activeLeaseColumn = activeScopeColumns.get(overlappingTaskId) ?? "in-progress";
192302
- await this.store.updateTask(task.id, {
192303
- status: "queued",
192398
+ await this.transitionQueuedEpisode(task, {
192399
+ signature: `file-scope:${overlappingTaskId}`,
192304
192400
  blockedBy: null,
192305
- overlapBlockedBy: overlappingTaskId
192401
+ overlapBlockedBy: overlappingTaskId,
192402
+ action: `queued \u2014 blocked by active file-scope lease ${overlappingTaskId} (column=${activeLeaseColumn})`
192306
192403
  });
192307
192404
  await this.rollbackRunningAgentsForQueuedTodoTask(task.id);
192308
- await this.logDispatchQueuedReason(
192309
- task.id,
192310
- `queued \u2014 blocked by active file-scope lease ${overlappingTaskId} (column=${activeLeaseColumn})`
192311
- );
192312
192405
  return null;
192313
192406
  }
192314
192407
  if (task.overlapBlockedBy) {
@@ -207098,13 +207191,6 @@ function hasTerminalInvalidDoneTransition(task) {
207098
207191
  return error2.includes("Invalid transition:") && error2.includes("\u2192 'done'");
207099
207192
  }
207100
207193
  var UNREADABLE_WORKFLOW_SELECTION = "\0unreadable-workflow-selection";
207101
- async function resolveNearDuplicateCanonicalFlags(store, canonical) {
207102
- if (!canonical?.column) return void 0;
207103
- const ir2 = await resolveWorkflowIrForTask(store, canonical.id).catch(() => void 0);
207104
- if (!ir2 || ir2.version !== "v2") return void 0;
207105
- const column = ir2.columns.find((candidate) => candidate.id === canonical.column);
207106
- return column ? resolveColumnFlags(column) : void 0;
207107
- }
207108
207194
  var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidence {
207109
207195
  constructor(store, options) {
207110
207196
  super();
@@ -207156,7 +207242,6 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
207156
207242
  strandedHoldContinuationNoActionAudited = /* @__PURE__ */ new Set();
207157
207243
  /* FNXC:SymbolLock 2026-07-30-14:20: idle symbol-lock sweeps emit one no-action audit until a stale lock re-arms the diagnostic. */
207158
207244
  symbolLockNoActionAudited = false;
207159
- preservedQueuedOverlapLogged = /* @__PURE__ */ new Map();
207160
207245
  maintenanceTickCounter = 0;
207161
207246
  taskLifecycleRetentionLastPrunedAt = /* @__PURE__ */ new Map();
207162
207247
  processBootStartedAt = Date.now();
@@ -207816,7 +207901,6 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
207816
207901
  }
207817
207902
  this.finalizeUnprovenWarned.clear();
207818
207903
  this.strandedCompletedFailureProvenanceWarned.clear();
207819
- this.preservedQueuedOverlapLogged.clear();
207820
207904
  log34.debug("Stopped");
207821
207905
  }
207822
207906
  // ── Auto-unpause ───────────────────────────────────────────────────
@@ -210385,17 +210469,20 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
210385
210469
  if (todoTaskIds.has(dependent.id)) {
210386
210470
  if (unresolvedDeps.length > 0) {
210387
210471
  const nextBlocker = unresolvedDeps[0];
210388
- await this.store.updateTask(dependent.id, { blockedBy: nextBlocker, overlapBlockedBy, status: "queued" });
210389
- await this.store.logEntry(
210390
- dependent.id,
210391
- `Auto-recovered (FN-4523): cleared stale blockedBy \u2014 blocker ${taskId} is done; now blocked by ${nextBlocker}`
210392
- );
210472
+ const normalizedUnresolvedDeps = [...new Set(unresolvedDeps)].sort();
210473
+ await this.store.transitionQueuedEpisode(dependent.id, {
210474
+ signature: `dependency:${normalizedUnresolvedDeps.join(",")}`,
210475
+ blockedBy: nextBlocker,
210476
+ overlapBlockedBy,
210477
+ action: `Auto-recovered (FN-4523): cleared stale blockedBy \u2014 blocker ${taskId} is done; now blocked by ${nextBlocker}`
210478
+ });
210393
210479
  } else if (hasActiveOverlapBlocker) {
210394
- await this.store.updateTask(dependent.id, { blockedBy: null, overlapBlockedBy, status: "queued" });
210395
- await this.store.logEntry(
210396
- dependent.id,
210397
- `Auto-recovered (FN-4523): preserved queued status \u2014 still blocked by file scope overlap with ${overlapBlockedBy}`
210398
- );
210480
+ await this.store.transitionQueuedEpisode(dependent.id, {
210481
+ signature: `file-scope:${overlapBlockedBy}`,
210482
+ blockedBy: null,
210483
+ overlapBlockedBy,
210484
+ action: `Auto-recovered (FN-4523): preserved queued status \u2014 still blocked by file scope overlap with ${overlapBlockedBy}`
210485
+ });
210399
210486
  } else {
210400
210487
  await this.store.updateTask(dependent.id, { blockedBy: null, overlapBlockedBy: null, status: null });
210401
210488
  await this.store.logEntry(
@@ -210908,16 +210995,6 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
210908
210995
  countBlockedDepth(tasks2) {
210909
210996
  return tasks2.filter((task) => typeof task.blockedBy === "string" && task.blockedBy.trim().length > 0).length;
210910
210997
  }
210911
- shouldLogPreservedQueuedOverlap(taskId, overlapBlockedBy) {
210912
- if (!overlapBlockedBy) return false;
210913
- const previous = this.preservedQueuedOverlapLogged.get(taskId);
210914
- if (previous === overlapBlockedBy) return false;
210915
- this.preservedQueuedOverlapLogged.set(taskId, overlapBlockedBy);
210916
- return true;
210917
- }
210918
- clearPreservedQueuedOverlapMemo(taskId) {
210919
- this.preservedQueuedOverlapLogged.delete(taskId);
210920
- }
210921
210998
  /**
210922
210999
  * #1401: periodic transitionPending recovery sweep. Delegates to the store's
210923
211000
  * idempotent recovery method (a no-op when no stale markers exist), keeping
@@ -211150,7 +211227,6 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
211150
211227
  (task) => task.status === "queued" && (task.dependencies.length > 0 || Boolean(task.overlapBlockedBy))
211151
211228
  );
211152
211229
  if (blockedTasks.length === 0 && queuedDependencyTasks.length === 0) {
211153
- this.preservedQueuedOverlapLogged.clear();
211154
211230
  return 0;
211155
211231
  }
211156
211232
  const allTasks = await this.store.listTasks({ includeArchived: true });
@@ -211222,13 +211298,6 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
211222
211298
  hold: /* @__PURE__ */ new Set(["todo"]),
211223
211299
  review: /* @__PURE__ */ new Set(["in-review"])
211224
211300
  };
211225
- for (const [taskId, lastLoggedBlockerId] of this.preservedQueuedOverlapLogged) {
211226
- const memoTask = taskById.get(taskId);
211227
- const memoHasActiveOverlapBlocker = memoTask ? await hasActiveFileScopeOverlapBlocker(memoTask, memoTask.overlapBlockedBy) : false;
211228
- if (!candidates.has(taskId) || !memoTask || !lanesOf(taskId).hold.has(memoTask.column) || memoTask.status !== "queued" || memoTask.overlapBlockedBy !== lastLoggedBlockerId || !memoHasActiveOverlapBlocker) {
211229
- this.clearPreservedQueuedOverlapMemo(taskId);
211230
- }
211231
- }
211232
211301
  for (const task of candidates.values()) {
211233
211302
  const blockerId = task.blockedBy;
211234
211303
  const unresolvedDeps = task.dependencies.filter((depId) => {
@@ -211301,7 +211370,6 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
211301
211370
  let didRecover = false;
211302
211371
  if (todoTaskIds.has(task.id)) {
211303
211372
  if (unresolvedDeps.length > 0) {
211304
- this.clearPreservedQueuedOverlapMemo(task.id);
211305
211373
  const nextBlocker2 = unresolvedDeps[0];
211306
211374
  if (nextBlocker2 === blockerId) {
211307
211375
  continue;
@@ -211310,19 +211378,19 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
211310
211378
  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}`);
211311
211379
  didRecover = true;
211312
211380
  } else if (hasActiveOverlapBlocker) {
211313
- await this.store.updateTask(task.id, { blockedBy: null, status: "queued" });
211314
- if (this.shouldLogPreservedQueuedOverlap(task.id, task.overlapBlockedBy)) {
211315
- 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}`);
211316
- didRecover = true;
211317
- }
211381
+ const transition = await this.store.transitionQueuedEpisode(task.id, {
211382
+ signature: `file-scope:${task.overlapBlockedBy}`,
211383
+ blockedBy: null,
211384
+ overlapBlockedBy: task.overlapBlockedBy ?? null,
211385
+ 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}`
211386
+ });
211387
+ didRecover = transition.appended;
211318
211388
  } else {
211319
- this.clearPreservedQueuedOverlapMemo(task.id);
211320
211389
  await this.store.updateTask(task.id, { blockedBy: null, overlapBlockedBy: null, status: null });
211321
211390
  await this.store.logEntry(task.id, `Auto-recovered (FN-5488): cleared stale blockedBy \u2014 blocker=${blockerId} blockerStatus=${blocker?.status ?? "none"} reason=${reasonCode ?? "unspecified"}; ${reason}`);
211322
211391
  didRecover = true;
211323
211392
  }
211324
211393
  } else {
211325
- this.clearPreservedQueuedOverlapMemo(task.id);
211326
211394
  await this.store.updateTask(task.id, { blockedBy: null });
211327
211395
  await this.store.logEntry(task.id, `Auto-recovered (FN-4091): cleared stale blockedBy \u2014 ${reason}`);
211328
211396
  didRecover = true;
@@ -211342,13 +211410,14 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
211342
211410
  if (queuedDependencyTaskIds.has(task.id)) {
211343
211411
  try {
211344
211412
  if (hasActiveOverlapBlocker) {
211345
- await this.store.updateTask(task.id, { blockedBy: null, status: "queued" });
211346
- if (this.shouldLogPreservedQueuedOverlap(task.id, task.overlapBlockedBy)) {
211347
- await this.store.logEntry(task.id, `Auto-recovered: preserved queued status \u2014 still blocked by file scope overlap with ${task.overlapBlockedBy}`);
211348
- recovered++;
211349
- }
211413
+ const transition = await this.store.transitionQueuedEpisode(task.id, {
211414
+ signature: `file-scope:${task.overlapBlockedBy}`,
211415
+ blockedBy: null,
211416
+ overlapBlockedBy: task.overlapBlockedBy ?? null,
211417
+ action: `Auto-recovered: preserved queued status \u2014 still blocked by file scope overlap with ${task.overlapBlockedBy}`
211418
+ });
211419
+ if (transition.appended) recovered++;
211350
211420
  } else {
211351
- this.clearPreservedQueuedOverlapMemo(task.id);
211352
211421
  await this.store.updateTask(task.id, { blockedBy: null, overlapBlockedBy: null, status: null });
211353
211422
  }
211354
211423
  } catch (err) {
@@ -211358,7 +211427,6 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
211358
211427
  }
211359
211428
  continue;
211360
211429
  }
211361
- this.clearPreservedQueuedOverlapMemo(task.id);
211362
211430
  const nextBlocker = unresolvedDeps[0] ?? null;
211363
211431
  if (nextBlocker && task.blockedBy !== nextBlocker) {
211364
211432
  try {
@@ -217569,14 +217637,12 @@ var SelfHealingManager = class _SelfHealingManager extends SelfHealingGitEvidenc
217569
217637
  if (canClearInactiveMarker) {
217570
217638
  rmSync7(promptPath, { force: true });
217571
217639
  const priorClearCount = typeof task.sourceMetadata?.duplicateMarkerClearCount === "number" ? task.sourceMetadata.duplicateMarkerClearCount : 0;
217572
- const alreadyDismissed = isTriageDuplicateKeepAcknowledged(task.sourceMetadata, marker.canonicalId);
217573
- const exhausted = alreadyDismissed || priorClearCount >= 1;
217574
- const patch = exhausted ? buildMarkerExhaustedFailedTaskPatch(marker.canonicalId, priorClearCount) : buildMarkerClearedReplanTaskPatch(marker.canonicalId, priorClearCount);
217640
+ const patch = buildMarkerClearedReplanTaskPatch(marker.canonicalId, priorClearCount);
217575
217641
  await this.store.updateTask(task.id, patch);
217576
217642
  if (typeof this.store.logEntry === "function") {
217577
217643
  await Promise.resolve(this.store.logEntry(
217578
217644
  task.id,
217579
- exhausted ? buildDuplicateReplanExhaustedError(marker.canonicalId) : TRIAGE_MARKER_CLEARED_REPLAN_LOG_ACTION,
217645
+ TRIAGE_MARKER_CLEARED_REPLAN_LOG_ACTION,
217580
217646
  buildInactiveDuplicateClearFeedback(marker.canonicalId)
217581
217647
  )).catch(() => {
217582
217648
  });
@@ -227611,13 +227677,15 @@ Graph value: ${failureValue}` : ""}`, budget.key),
227611
227677
  recoveryRehome: true
227612
227678
  });
227613
227679
  }
227614
- await this.store.updateTask(liveTask.id, { status: "queued", blockedBy: unmetDeps[0] }, this.getRunContextFor(liveTask.id));
227615
- await this.store.logEntry(
227616
- liveTask.id,
227617
- `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`,
227618
- "Executor pre-dispatch dependency gate blocked workflow/authoritative execution.",
227619
- this.getRunContextFor(liveTask.id)
227620
- );
227680
+ const normalizedUnmetDeps = [...new Set(unmetDeps)].sort();
227681
+ await this.store.transitionQueuedEpisode(liveTask.id, {
227682
+ signature: `dependency:${normalizedUnmetDeps.join(",")}`,
227683
+ blockedBy: unmetDeps[0] ?? null,
227684
+ overlapBlockedBy: liveTask.overlapBlockedBy ?? null,
227685
+ action: `queued \u2014 unmet dependencies: ${unmetDeps.join(", ")}`,
227686
+ outcome: "Executor pre-dispatch dependency gate blocked workflow/authoritative execution.",
227687
+ runContext: this.getRunContextFor(liveTask.id)
227688
+ });
227621
227689
  executorLog.log(`${liveTask.id}: executor dispatch blocked by unmet dependencies: ${unmetDeps.join(", ")}`);
227622
227690
  return true;
227623
227691
  }
@@ -243563,13 +243631,6 @@ function resolveTaskListFormatter(core) {
243563
243631
  }
243564
243632
  var STALE_PLANNING_STATUS_GRACE_MS = 20 * 6e4;
243565
243633
  var LEGACY_PLANNER_COLUMN_IDS = /* @__PURE__ */ new Set(["triage", "todo"]);
243566
- async function resolveNearDuplicateCanonicalFlags2(store, canonical) {
243567
- if (!canonical?.column) return void 0;
243568
- const ir2 = await resolveWorkflowIrForTask(store, canonical.id).catch(() => void 0);
243569
- if (!ir2 || ir2.version !== "v2") return void 0;
243570
- const column = ir2.columns.find((candidate) => candidate.id === canonical.column);
243571
- return column ? resolveColumnFlags(column) : void 0;
243572
- }
243573
243634
  function isOrphanedLegacyTriageRow(column, declaresTriage) {
243574
243635
  return column === "triage" && !declaresTriage;
243575
243636
  }
@@ -244961,11 +245022,13 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
244961
245022
  const defaultTriagePrompt = resolveAgentPrompt("triage", void 0, { plannerHeartbeatPatrolEnabled });
244962
245023
  const resolvedBasePrompt = userTriagePrompt || (leanPlanning ? workflowFastPlanningPrompt || builtinSeamPrompt("planning-fast") || defaultTriagePrompt : workflowPlanningPrompt || defaultTriagePrompt);
244963
245024
  const renderedBasePrompt = renderTriagePolicyPlaceholders(resolvedBasePrompt, triagePolicySettings);
245025
+ const duplicatePolicyInstruction = buildPlanningDuplicatePolicyInstruction();
244964
245026
  const triageLayers = buildPromptLayers({
244965
245027
  basePrompt: renderedBasePrompt,
244966
245028
  goalContext: triageGoalResolution.goalContext,
244967
245029
  agentInstructions: [
244968
245030
  triageIdentitySection,
245031
+ duplicatePolicyInstruction,
244969
245032
  triageInstructions,
244970
245033
  isResearchToolSurfaceEnabled(settings) ? getResearchGuidanceForSurface("triage") : ""
244971
245034
  ].filter((section) => section.trim()).join("\n\n"),
@@ -245594,7 +245657,7 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
245594
245657
  const taskSearch = {
245595
245658
  name: "fn_task_search",
245596
245659
  label: "Search Tasks",
245597
- description: "Keyword search across tasks, including done and archived tasks by default. Use for duplicate detection before filing a new task.",
245660
+ description: "Keyword search across active tasks by default. Done and archived history is opt-in and must not be used for duplicate detection.",
245598
245661
  parameters: taskSearchParams2,
245599
245662
  execute: async (_callId, params) => {
245600
245663
  const query = params.query.trim();
@@ -245606,10 +245669,10 @@ You are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.
245606
245669
  }
245607
245670
  const results = await store.searchTasks(query, {
245608
245671
  slim: true,
245609
- includeArchived: params.includeArchived ?? true,
245672
+ includeArchived: params.includeArchived ?? false,
245610
245673
  limit: params.limit ?? 20
245611
245674
  });
245612
- const includeDone = params.includeDone ?? true;
245675
+ const includeDone = params.includeDone ?? false;
245613
245676
  const isTerminalResult = includeDone ? void 0 : await resolveTerminalColumnsForTasks(store, results);
245614
245677
  const filtered = includeDone ? results : results.filter((t) => !isTerminalResult(t));
245615
245678
  if (filtered.length === 0) {
@@ -245848,7 +245911,7 @@ Remove or replace these ids and call fn_task_create again.`
245848
245911
  if (canonicalTask?.id.toLowerCase() === task.id.toLowerCase()) {
245849
245912
  return false;
245850
245913
  }
245851
- const canonicalFlags = await resolveNearDuplicateCanonicalFlags2(this.store, canonicalTask);
245914
+ const canonicalFlags = await resolveNearDuplicateCanonicalFlags(this.store, canonicalTask);
245852
245915
  if (isNearDuplicateCanonicalInactive(canonicalTask, canonicalFlags)) {
245853
245916
  planLog.log(`${task.id} explicit duplicate marker targets inactive ${canonicalId}; clearing marker for replanning`);
245854
245917
  } else {
@@ -245992,17 +246055,16 @@ Remove or replace these ids and call fn_task_create again.`
245992
246055
  const canonicalId = explicitDuplicateMarker.canonicalId;
245993
246056
  const canonicalTask = await this.store.getTask(canonicalId).catch(() => null);
245994
246057
  const canClearInactiveMarker = task.userPaused !== true && (task.paused !== true || task.pausedReason === "duplicate-decision-required") && (task.pausedReason == null || task.pausedReason === "duplicate-decision-required");
245995
- const canonicalFlags = await resolveNearDuplicateCanonicalFlags2(this.store, canonicalTask);
246058
+ const canonicalFlags = await resolveNearDuplicateCanonicalFlags(this.store, canonicalTask);
245996
246059
  const liveMeta = (await this.store.getTask(task.id).catch(() => null))?.sourceMetadata ?? task.sourceMetadata;
245997
246060
  const priorClearCount = typeof liveMeta?.duplicateMarkerClearCount === "number" ? liveMeta.duplicateMarkerClearCount : 0;
245998
246061
  if (isNearDuplicateCanonicalInactive(canonicalTask ?? void 0, canonicalFlags)) {
245999
246062
  if (canClearInactiveMarker) {
246000
- const alreadyDismissed = isTriageDuplicateKeepAcknowledged(liveMeta, canonicalId);
246001
246063
  await this.clearDuplicateMarkerForReplan(
246002
246064
  task,
246003
246065
  canonicalId,
246004
246066
  buildInactiveDuplicateClearFeedback(canonicalId),
246005
- { exhausted: alreadyDismissed || priorClearCount >= 1, priorClearCount }
246067
+ { exhausted: false, priorClearCount }
246006
246068
  );
246007
246069
  }
246008
246070
  return;
@@ -246242,7 +246304,7 @@ Remove or replace these ids and call fn_task_create again.`
246242
246304
  if (!canonicalTask) {
246243
246305
  return;
246244
246306
  }
246245
- const canonicalFlags = await resolveNearDuplicateCanonicalFlags2(this.store, canonicalTask);
246307
+ const canonicalFlags = await resolveNearDuplicateCanonicalFlags(this.store, canonicalTask);
246246
246308
  if (isNearDuplicateCanonicalInactive(canonicalTask, canonicalFlags)) {
246247
246309
  planLog.log(`${task.id}: near-duplicate candidate ${canonical.id} is inactive; skipping near-duplicate flag`);
246248
246310
  return;
@@ -248158,6 +248220,40 @@ var InProcessRuntime = class extends EventEmitter27 {
248158
248220
  throw err;
248159
248221
  }
248160
248222
  }
248223
+ /**
248224
+ * Close every process-local admission source without aborting work that is
248225
+ * already running. Development source reload uses this boundary before it
248226
+ * waits for the live-agent count to reach zero.
248227
+ */
248228
+ beginDrain() {
248229
+ if (this.status !== "active") return;
248230
+ this.setStatus("paused");
248231
+ if (this.workflowContinuationTimer) {
248232
+ clearInterval(this.workflowContinuationTimer);
248233
+ this.workflowContinuationTimer = void 0;
248234
+ }
248235
+ const admissionStops = [
248236
+ ["self-healing manager", () => this.selfHealingManager?.stop()],
248237
+ ["routine scheduler", () => this.routineScheduler?.stop()],
248238
+ ["trigger scheduler", () => this.triggerScheduler?.stop()],
248239
+ ["stuck task detector", () => this.stuckTaskDetector?.stop()],
248240
+ ["heartbeat monitor", () => this.heartbeatMonitor?.stop()],
248241
+ ["triage processor", () => this.triageProcessor?.stop()],
248242
+ ["scheduler", () => this.scheduler?.stop()],
248243
+ ["mission autopilot", () => this.missionAutopilot?.stop()],
248244
+ ["mission execution loop", () => this.missionExecutionLoop?.stop()]
248245
+ ];
248246
+ for (const [label, stop] of admissionStops) {
248247
+ try {
248248
+ stop();
248249
+ } catch (error2) {
248250
+ runtimeLog.warn(
248251
+ `Failed to stop ${label} while draining ${this.config.projectId}: ${error2 instanceof Error ? error2.message : String(error2)}`
248252
+ );
248253
+ }
248254
+ }
248255
+ runtimeLog.log(`InProcessRuntime draining for ${this.config.projectId}`);
248256
+ }
248161
248257
  /**
248162
248258
  * Stop the runtime with graceful shutdown.
248163
248259
  *
@@ -255450,6 +255546,11 @@ ${detail}`);
255450
255546
  this.started = false;
255451
255547
  runtimeLog.log(`ProjectEngine stopped for ${this.config.projectId}`);
255452
255548
  }
255549
+ /** Stop new lifecycle admission while allowing active work to finish. */
255550
+ beginDrain() {
255551
+ this.shuttingDown = true;
255552
+ this.runtime.beginDrain();
255553
+ }
255453
255554
  // ── Public accessors ──
255454
255555
  /** Get the underlying InProcessRuntime. */
255455
255556
  getRuntime() {