wogiflow 2.21.0 → 2.22.1

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 (261) hide show
  1. package/.claude/commands/wogi-finalize.md +83 -0
  2. package/.claude/commands/wogi-start.md +3 -3
  3. package/.claude/commands/wogi-story.md +27 -0
  4. package/.claude/docs/claude-code-compatibility.md +32 -1
  5. package/.claude/rules/_internal/self-maintenance.md +1 -1
  6. package/.claude/settings.json +1 -1
  7. package/lib/commands/login.js +1 -1
  8. package/lib/installer.js +5 -5
  9. package/lib/release-channel.js +1 -1
  10. package/lib/skill-registry.js +3 -3
  11. package/lib/workspace-dispatch-tracking.js +175 -0
  12. package/lib/workspace-events.js +1 -1
  13. package/lib/workspace-gates.js +2 -2
  14. package/lib/workspace-intelligence.js +1 -1
  15. package/lib/workspace-messages.js +1 -0
  16. package/lib/workspace-routing.js +18 -1
  17. package/lib/workspace.js +16 -17
  18. package/package.json +2 -2
  19. package/scripts/base-workflow-step.js +2 -2
  20. package/scripts/flow-adaptive-learning.js +6 -6
  21. package/scripts/flow-api-index.js +2 -2
  22. package/scripts/flow-architect-pass.js +1 -1
  23. package/scripts/flow-ask.js +1 -1
  24. package/scripts/flow-assumption-detector.js +1 -1
  25. package/scripts/flow-audit-gates.js +38 -12
  26. package/scripts/flow-audit.js +4 -4
  27. package/scripts/flow-auto-context.js +3 -3
  28. package/scripts/flow-background.js +1 -1
  29. package/scripts/flow-best-of-n.js +7 -7
  30. package/scripts/flow-bridge.js +3 -3
  31. package/scripts/flow-bug.js +2 -2
  32. package/scripts/flow-bulk-loop.js +7 -7
  33. package/scripts/flow-cascade-completion.js +2 -2
  34. package/scripts/flow-cascade.js +1 -1
  35. package/scripts/flow-checkpoint.js +2 -2
  36. package/scripts/flow-clarifying-questions.js +2 -2
  37. package/scripts/flow-cli.js +2 -2
  38. package/scripts/flow-code-intelligence.js +4 -4
  39. package/scripts/flow-community-sync.js +6 -6
  40. package/scripts/flow-community.js +1 -1
  41. package/scripts/flow-completion-truth-gate.js +161 -5
  42. package/scripts/flow-complexity.js +1 -1
  43. package/scripts/flow-config-defaults.js +18 -1
  44. package/scripts/flow-config-interactive.js +2 -2
  45. package/scripts/flow-config-loader.js +1 -1
  46. package/scripts/flow-config-migrate.js +5 -6
  47. package/scripts/flow-consistency-check.js +5 -5
  48. package/scripts/flow-context-compact/expander.js +1 -1
  49. package/scripts/flow-context-compact/index.js +2 -2
  50. package/scripts/flow-context-compact/section-extractor.js +3 -3
  51. package/scripts/flow-context-compact/summary-tree.js +1 -1
  52. package/scripts/flow-context-estimator.js +1 -1
  53. package/scripts/flow-context-gatherer.js +6 -6
  54. package/scripts/flow-context-generator.js +6 -6
  55. package/scripts/flow-context-init.js +2 -2
  56. package/scripts/flow-context-manager.js +1 -1
  57. package/scripts/flow-context-manifest.js +1 -1
  58. package/scripts/flow-context-monitor.js +5 -5
  59. package/scripts/flow-context-orchestrator.js +2 -2
  60. package/scripts/flow-context-scoring.js +4 -4
  61. package/scripts/flow-contract-scan.js +1 -1
  62. package/scripts/flow-correct.js +3 -3
  63. package/scripts/flow-damage-control.js +2 -2
  64. package/scripts/flow-deploy-gate.js +2 -2
  65. package/scripts/flow-deploy-history.js +1 -1
  66. package/scripts/flow-diff.js +3 -3
  67. package/scripts/flow-done-gates.js +1 -1
  68. package/scripts/flow-done.js +7 -7
  69. package/scripts/flow-durable-session.js +1 -1
  70. package/scripts/flow-entropy-monitor.js +3 -3
  71. package/scripts/flow-epics.js +5 -5
  72. package/scripts/flow-error-recovery.js +4 -4
  73. package/scripts/flow-eval-judge.js +5 -5
  74. package/scripts/flow-eval.js +7 -7
  75. package/scripts/flow-export-scanner.js +5 -5
  76. package/scripts/flow-extraction-review.js +1 -1
  77. package/scripts/flow-failure-learning.js +9 -9
  78. package/scripts/flow-feature.js +5 -5
  79. package/scripts/flow-figma-confirm.js +1 -1
  80. package/scripts/flow-figma-extract.js +2 -2
  81. package/scripts/flow-figma-index.js +2 -2
  82. package/scripts/flow-figma-match.js +1 -1
  83. package/scripts/flow-figma-mcp-server.js +3 -3
  84. package/scripts/flow-figma-orchestrator.js +1 -1
  85. package/scripts/flow-figma-registry.js +2 -2
  86. package/scripts/flow-function-index.js +2 -2
  87. package/scripts/flow-gate-confidence.js +2 -2
  88. package/scripts/flow-gate-telemetry.js +1 -1
  89. package/scripts/flow-gitignore.js +1 -1
  90. package/scripts/flow-guided-edit.js +3 -3
  91. package/scripts/flow-health.js +95 -8
  92. package/scripts/flow-hooks.js +3 -3
  93. package/scripts/flow-hybrid-detect.js +2 -2
  94. package/scripts/flow-hybrid-interactive.js +1 -1
  95. package/scripts/flow-hybrid-test.js +1 -1
  96. package/scripts/flow-hypothesis-generator.js +4 -4
  97. package/scripts/flow-instruction-richness.js +11 -11
  98. package/scripts/flow-intent-bootstrap.js +1 -1
  99. package/scripts/flow-intent-framing.js +1 -1
  100. package/scripts/flow-item-link.js +2 -2
  101. package/scripts/flow-knowledge-router.js +7 -7
  102. package/scripts/flow-knowledge-sync.js +3 -3
  103. package/scripts/flow-learning-orchestrator.js +1 -1
  104. package/scripts/flow-links.js +2 -2
  105. package/scripts/flow-log-manager.js +2 -2
  106. package/scripts/flow-logic-adversary.js +5 -4
  107. package/scripts/flow-long-input-chunking.js +1 -1
  108. package/scripts/flow-long-input-cli.js +3 -3
  109. package/scripts/flow-long-input.js +18 -18
  110. package/scripts/flow-loop-retry-learning.js +2 -2
  111. package/scripts/flow-lsp.js +4 -4
  112. package/scripts/flow-mcp-docs.js +1 -1
  113. package/scripts/flow-memory-blocks.js +5 -5
  114. package/scripts/flow-memory-compactor.js +3 -3
  115. package/scripts/flow-memory-db.js +4 -4
  116. package/scripts/flow-memory-sync.js +3 -3
  117. package/scripts/flow-metrics.js +2 -2
  118. package/scripts/flow-migrate-igr.js +2 -2
  119. package/scripts/flow-migrate.js +2 -2
  120. package/scripts/flow-model-adapter.js +4 -4
  121. package/scripts/flow-model-caller.js +8 -8
  122. package/scripts/flow-model-config.js +5 -5
  123. package/scripts/flow-model-profile.js +7 -7
  124. package/scripts/flow-model-router.js +5 -5
  125. package/scripts/flow-model-types.js +3 -3
  126. package/scripts/flow-models.js +8 -8
  127. package/scripts/flow-morning.js +1 -1
  128. package/scripts/flow-multi-approach.js +1 -1
  129. package/scripts/flow-orchestrate-context.js +2 -2
  130. package/scripts/flow-orchestrate-llm.js +4 -4
  131. package/scripts/flow-orchestrate-rollback.js +1 -1
  132. package/scripts/flow-orchestrate-state.js +6 -6
  133. package/scripts/flow-orchestrate-templates.js +1 -1
  134. package/scripts/flow-orchestrate-validation.js +2 -2
  135. package/scripts/flow-orchestrate-validator.js +1 -1
  136. package/scripts/flow-orchestrate.js +25 -25
  137. package/scripts/flow-parallel.js +1 -1
  138. package/scripts/flow-pattern-enforcer.js +7 -7
  139. package/scripts/flow-pattern-extractor.js +3 -3
  140. package/scripts/flow-peer-review.js +8 -8
  141. package/scripts/flow-pending.js +1 -1
  142. package/scripts/flow-permissions.js +1 -1
  143. package/scripts/flow-phased-task.js +1 -1
  144. package/scripts/flow-plan.js +1 -1
  145. package/scripts/flow-prd-manager.js +2 -2
  146. package/scripts/flow-product-scanner.js +2 -2
  147. package/scripts/flow-progress-tracker.js +2 -2
  148. package/scripts/flow-progress.js +1 -1
  149. package/scripts/flow-project-analyzer.js +3 -3
  150. package/scripts/flow-prompt-capture.js +2 -2
  151. package/scripts/flow-prompt-composer.js +3 -3
  152. package/scripts/flow-prompt-template.js +4 -4
  153. package/scripts/flow-providers.js +31 -23
  154. package/scripts/flow-queue.js +1 -1
  155. package/scripts/flow-registry-manager.js +4 -4
  156. package/scripts/flow-regression.js +1 -1
  157. package/scripts/flow-response-parser.js +1 -1
  158. package/scripts/flow-resume.js +1 -1
  159. package/scripts/flow-review-passes/index.js +2 -2
  160. package/scripts/flow-review-passes/integration.js +3 -3
  161. package/scripts/flow-review-passes/logic.js +3 -3
  162. package/scripts/flow-review-passes/security.js +2 -2
  163. package/scripts/flow-review-passes/structure.js +1 -1
  164. package/scripts/flow-review.js +11 -11
  165. package/scripts/flow-revision-tracker.js +2 -2
  166. package/scripts/flow-roadmap.js +2 -2
  167. package/scripts/flow-run-trace.js +1 -1
  168. package/scripts/flow-safety.js +3 -3
  169. package/scripts/flow-scanner-base.js +1 -1
  170. package/scripts/flow-scenario-engine.js +7 -7
  171. package/scripts/flow-schema-drift.js +4 -3
  172. package/scripts/flow-section-index.js +2 -2
  173. package/scripts/flow-section-resolver.js +4 -4
  174. package/scripts/flow-semantic-match.js +3 -3
  175. package/scripts/flow-session-end.js +56 -0
  176. package/scripts/flow-session-learning.js +2 -2
  177. package/scripts/flow-setup-hooks.js +1 -1
  178. package/scripts/flow-skill-create.js +3 -3
  179. package/scripts/flow-skill-freshness.js +2 -2
  180. package/scripts/flow-skill-generator.js +6 -6
  181. package/scripts/flow-skill-learn.js +7 -7
  182. package/scripts/flow-skill-matcher.js +2 -2
  183. package/scripts/flow-solution-optimizer.js +1 -1
  184. package/scripts/flow-spec-generator.js +5 -5
  185. package/scripts/flow-spec-verifier.js +2 -2
  186. package/scripts/flow-stack-wizard.js +6 -6
  187. package/scripts/flow-standards-checker.js +8 -8
  188. package/scripts/flow-standards-gate.js +4 -4
  189. package/scripts/flow-standards-learner.js +2 -2
  190. package/scripts/flow-start.js +9 -9
  191. package/scripts/flow-stats-collector.js +2 -2
  192. package/scripts/flow-status.js +1 -1
  193. package/scripts/flow-step-changelog.js +3 -3
  194. package/scripts/flow-step-complexity.js +1 -1
  195. package/scripts/flow-step-coverage.js +3 -3
  196. package/scripts/flow-step-knowledge.js +2 -2
  197. package/scripts/flow-step-pr-tests.js +2 -2
  198. package/scripts/flow-step-regression.js +3 -3
  199. package/scripts/flow-step-review.js +5 -5
  200. package/scripts/flow-story-gates.js +504 -0
  201. package/scripts/flow-story.js +207 -9
  202. package/scripts/flow-strict-adherence.js +2 -2
  203. package/scripts/flow-structure-sensor.js +283 -0
  204. package/scripts/flow-sync-anonymizer.js +3 -3
  205. package/scripts/flow-task-checkpoint.js +2 -2
  206. package/scripts/flow-task-classifier.js +2 -2
  207. package/scripts/flow-task-completion-summary.js +1 -1
  208. package/scripts/flow-task-enforcer.js +5 -5
  209. package/scripts/flow-tech-debt.js +3 -3
  210. package/scripts/flow-template-extractor.js +3 -3
  211. package/scripts/flow-templates.js +1 -1
  212. package/scripts/flow-test-api.js +12 -12
  213. package/scripts/flow-test-discovery.js +9 -9
  214. package/scripts/flow-test-generate.js +5 -5
  215. package/scripts/flow-test-integrity.js +3 -3
  216. package/scripts/flow-test-ui.js +8 -8
  217. package/scripts/flow-testing-deps.js +4 -4
  218. package/scripts/flow-tiered-learning.js +3 -3
  219. package/scripts/flow-todowrite-sync.js +1 -1
  220. package/scripts/flow-trap-zone.js +1 -1
  221. package/scripts/flow-verification-profile.js +9 -9
  222. package/scripts/flow-verify.js +2 -2
  223. package/scripts/flow-version-check.js +2 -2
  224. package/scripts/flow-webmcp-generator.js +3 -3
  225. package/scripts/flow-wiring-verifier.js +13 -13
  226. package/scripts/flow-workflow-steps.js +3 -3
  227. package/scripts/flow-workflow.js +1 -1
  228. package/scripts/flow-worktree.js +1 -1
  229. package/scripts/hooks/adapters/base-adapter.js +2 -2
  230. package/scripts/hooks/adapters/claude-code.js +18 -37
  231. package/scripts/hooks/core/commit-log-gate.js +2 -2
  232. package/scripts/hooks/core/component-check.js +3 -3
  233. package/scripts/hooks/core/config-change.js +1 -1
  234. package/scripts/hooks/core/deploy-gate.js +2 -1
  235. package/scripts/hooks/core/git-safety-gate.js +1 -1
  236. package/scripts/hooks/core/instructions-loaded.js +1 -1
  237. package/scripts/hooks/core/loop-check.js +1 -1
  238. package/scripts/hooks/core/manager-boundary-gate.js +3 -2
  239. package/scripts/hooks/core/observation-capture.js +6 -6
  240. package/scripts/hooks/core/overdue-dispatches.js +159 -0
  241. package/scripts/hooks/core/phase-gate.js +4 -4
  242. package/scripts/hooks/core/pre-compact.js +1 -1
  243. package/scripts/hooks/core/routing-gate.js +1 -1
  244. package/scripts/hooks/core/session-context.js +1 -1
  245. package/scripts/hooks/core/session-end.js +3 -3
  246. package/scripts/hooks/core/session-history.js +1 -1
  247. package/scripts/hooks/core/setup-handler.js +1 -1
  248. package/scripts/hooks/core/task-boundary-reset.js +2 -4
  249. package/scripts/hooks/core/task-completed.js +13 -7
  250. package/scripts/hooks/core/task-created.js +1 -1
  251. package/scripts/hooks/core/worktree-lifecycle.js +1 -1
  252. package/scripts/hooks/entry/claude-code/permission-denied.js +4 -2
  253. package/scripts/hooks/entry/claude-code/stop.js +92 -47
  254. package/scripts/hooks/entry/claude-code/user-prompt-submit.js +19 -1
  255. package/scripts/hooks/git/post-commit.js +1 -1
  256. package/scripts/postinstall.js +7 -7
  257. package/scripts/preuninstall.js +5 -5
  258. package/scripts/registries/component-registry.js +2 -2
  259. package/scripts/registries/contract-scanner.js +11 -11
  260. package/scripts/registries/schema-registry.js +5 -5
  261. package/scripts/registries/service-registry.js +9 -9
@@ -79,6 +79,79 @@ Pre-finalization checks:
79
79
 
80
80
  If checks fail, display warnings and suggest fixes before proceeding.
81
81
 
82
+ ### Step 2.5: Merge-Plan Gate (when `mergePlan.threshold` exceeded)
83
+
84
+ **Activates when** the branch carries more commits than `config.finalization.mergePlan.threshold` (default **5**) OR the diff is flagged cross-repo by the workspace manifest. The gate writes — and requires the AI to fill in — `.workflow/scratch/merge-plan.md`. The gate exists because the "1-2h mostly mechanical" audit that predicted a 27-conflict merge (wogi-hub, 2026-04-16) counted commits-per-file without reading diff content; the fix is to force per-commit action assignment in a file.
85
+
86
+ **Mechanical invariants (gate blocks on violation):**
87
+
88
+ 1. For every commit in `git log <base>..<branch>`, the plan MUST contain one line starting with the short SHA and a tagged action. No commits in an "unaccounted" bucket.
89
+ 2. Allowed actions: `port | adapt | skip-style | superseded | skip-with-reason`.
90
+ 3. `git log <base>..<branch> | wc -l` MUST equal the count of SHA-prefixed lines in the plan. Mismatch → hard-stop until reconciled.
91
+ 4. `skip-with-reason` entries MUST include a one-line reason after a `—` (em dash).
92
+
93
+ **Structural-change detection (before plan write):**
94
+
95
+ Run the structure-change sensor (`scripts/flow-structure-sensor.js`) on the diff. If ≥ `config.finalization.mergePlan.restructureThreshold` (default **20%**) of changed files match one of these restructure patterns, display a STRUCTURAL CHANGE warning at the top of the plan and bias the default action for affected commits to `adapt`:
96
+
97
+ | Pattern | Example | Meaning |
98
+ |---------|---------|---------|
99
+ | `X.tsx` deleted + `X/X.tsx` added | `Card.tsx` → `Card/Card.tsx` | folder-per-component |
100
+ | `X.ts` deleted + `<dir>/X.ts` added at deeper depth | `utils.ts` → `utils/date.ts` | split into submodule |
101
+ | `X` deleted + `X.<ext>` added elsewhere | `types.ts` → `types/index.ts` | barrel introduction |
102
+
103
+ **Procedure:**
104
+
105
+ ```bash
106
+ # 1. Gather commit list
107
+ git log --pretty='%h %s' <base>..<branch> > .workflow/scratch/.merge-plan-commits.txt
108
+
109
+ # 2. Run structure sensor
110
+ node node_modules/wogiflow/scripts/flow-structure-sensor.js <base>..<branch> > .workflow/scratch/.merge-plan-sensor.json
111
+
112
+ # 3. Write .workflow/scratch/merge-plan.md using the template below,
113
+ # one SHA-prefixed line per commit. Read the FULL diff of each commit
114
+ # (not just the subject line) before assigning an action — that is
115
+ # the whole point of this gate.
116
+
117
+ # 4. Verify the mechanical invariant
118
+ test "$(git log --oneline <base>..<branch> | wc -l)" -eq \
119
+ "$(grep -cE '^[a-f0-9]{7,}\s' .workflow/scratch/merge-plan.md)"
120
+ ```
121
+
122
+ **Plan template** (write verbatim, then fill each row by reading the full diff):
123
+
124
+ ```markdown
125
+ # Merge plan: <branch> → <base>
126
+
127
+ Commits: N (from `git log <base>..<branch>`)
128
+ Structural-change sensor: <WARN|clean> — N/M files match restructure patterns
129
+ Cross-repo impact: <list workspace members affected, or "single-repo">
130
+
131
+ ## Per-commit actions
132
+
133
+ | SHA | Subject | Action | Notes |
134
+ |-----|---------|--------|-------|
135
+ | abc1234 | feat: add login form | port | — |
136
+ | def5678 | refactor: split Card.tsx into Card/ | adapt | folder-per-component restructure |
137
+ | ghi9012 | chore: lint fixes | skip-style | — |
138
+ | jkl3456 | revert: roll back header | skip-with-reason | superseded by mno7890 |
139
+
140
+ ## Structural risks
141
+
142
+ <leave empty if sensor is clean; otherwise list each pattern hit>
143
+
144
+ ## Content risks
145
+
146
+ <list overlaps in shared types, DTOs, API surface that need manual review>
147
+ ```
148
+
149
+ **When the plan is complete**, the gate verifies the commit-count invariant (step 4) and proceeds to Step 3 (options). If invariant fails, the command stops with the reconciliation command printed.
150
+
151
+ **Skip conditions:**
152
+ - `config.finalization.mergePlan.enabled: false` — opt-out for users who don't want the gate
153
+ - Branch commits ≤ threshold AND single-repo — small merges don't need a plan
154
+
82
155
  ### Step 3: Present Options
83
156
 
84
157
  ```
@@ -241,6 +314,12 @@ When `/wogi-start` completes a task that was executed in a worktree, the finaliz
241
314
  "includeTaskSpec": true,
242
315
  "includeCommitList": true,
243
316
  "includeFileSummary": true
317
+ },
318
+ "mergePlan": {
319
+ "enabled": true,
320
+ "threshold": 5,
321
+ "restructureThreshold": 0.20,
322
+ "alwaysForCrossRepo": true
244
323
  }
245
324
  }
246
325
  }
@@ -254,6 +333,10 @@ When `/wogi-start` completes a task that was executed in a worktree, the finaliz
254
333
  | `requirePRForTypes` | `[]` | Task types that must create a PR (useful for teams) |
255
334
  | `squashOnMerge` | `true` | Squash commits when merging |
256
335
  | `prTemplate` | `{...}` | What to include in auto-generated PR body |
336
+ | `mergePlan.enabled` | `true` | Require per-commit merge plan on large or cross-repo merges |
337
+ | `mergePlan.threshold` | `5` | Commit count above which the merge plan is required |
338
+ | `mergePlan.restructureThreshold` | `0.20` | % of changed files matching restructure patterns that triggers a structural-change warning |
339
+ | `mergePlan.alwaysForCrossRepo` | `true` | Require the plan on any cross-repo merge regardless of commit count |
257
340
 
258
341
  ## Examples
259
342
 
@@ -180,18 +180,18 @@ When epic creation adds 2+ stories to ready.json and `config.bulkOrchestrator.en
180
180
 
181
181
  Non-blocking if transition fails.
182
182
 
183
- ### Effort Level Optimization (Claude Code 2.1.72+)
183
+ ### Effort Level Optimization (Claude Code 2.1.72+, xhigh added 2.1.111+)
184
184
 
185
185
  After task level classification (L0-L3), set the reasoning effort level to optimize token usage:
186
186
 
187
187
  | Task Level | Effort | Rationale |
188
188
  |------------|--------|-----------|
189
- | L0 (Epic) | high | Complex planning, multi-file architecture |
189
+ | L0 (Epic) | high (xhigh on Opus 4.7 for deep architectural reasoning) | Complex planning, multi-file architecture |
190
190
  | L1 (Story) | high | Multi-criteria implementation |
191
191
  | L2 (Task) | medium | Standard 1-5 file changes |
192
192
  | L3 (Subtask) | low | Single file, trivial change |
193
193
 
194
- This is advisory Claude Code 2.1.72 simplified effort to low/medium/high (removed "max"). The AI should adjust its reasoning depth accordingly during implementation phases.
194
+ This is advisory. Claude Code's effort levels: `low` / `medium` / `high` are universal. Claude Code 2.1.111+ added `xhigh` (between high and max) and `max` as Opus 4.7-only levels — other models fall back to `high`. Use `/effort` interactively (slider as of 2.1.111) to switch mid-session. The AI should adjust reasoning depth during implementation phases accordingly.
195
195
 
196
196
  ### Task Checkpoints (when `config.proactiveCompaction.enabled`)
197
197
 
@@ -8,11 +8,38 @@ Run `./scripts/flow story "<title>"` to create a story.
8
8
 
9
9
  Load `agents/story-writer.md` for the full story format.
10
10
 
11
+ ## Anti-Deferral Rule (MANDATORY)
12
+
13
+ **Every item the user provides MUST become a work item** (criterion or sub-task). Never silently filter items. If you believe an item should be deferred, **ASK the user** — do not decide autonomously.
14
+
15
+ For multi-item inputs, the command output MUST include: **"All {N} items captured as {criteria|sub-tasks}."** If any item cannot be mapped, the "Unmapped" warning must be surfaced, not suppressed.
16
+
17
+ This rule applies equally to deep-decomposition mode and flat stories.
18
+
19
+ ## Specification-Quality Gates (wf-63c0f4cc)
20
+
21
+ Five P0 gates run automatically at creation time (all fail-open):
22
+
23
+ | Gate | Fires When | Effect |
24
+ |------|-----------|--------|
25
+ | 1. Long Input | input ≥40 lines OR ≥5 discrete items | routes to `/wogi-extract-review` |
26
+ | 2. Item Reconciliation | input has ≥3 discrete items | writes manifest, verifies coverage |
27
+ | 3. Consumer Impact | input contains refactor/rename/migrate/etc. | greps consumers, flags phased migration at ≥5 breaking |
28
+ | 4. Scope-Confidence | input mentions "new X" / "existing Y" / "the Z service" | audits assumptions → "Pending Clarifications" block |
29
+ | 5. Intent Bootstrap | IGR artifacts missing + not already scheduled | schedules background bootstrap via session-state.json |
30
+
31
+ Gates enforce **specification quality at creation time**; runtime-quality gates (wiring, typecheck, tests) remain `/wogi-start`'s job.
32
+
33
+ Config: `storyFlow.consumerImpactAnalysis`, `storyFlow.scopeConfidenceAudit`, `storyFlow.itemReconciliation`. All default-true.
34
+
11
35
  ## Options
12
36
 
13
37
  - `--deep` - Enable deep decomposition mode (auto-generate granular sub-tasks)
14
38
  - `--priority <P>` - Set priority P0-P4 (default: P2 from config)
15
39
  - `--json` - Output JSON for programmatic access
40
+ - `--skip-gates` - Skip all P0 gates (testing/debug only)
41
+ - `--bypass-long-input` - Skip Gate 1 (set by `/wogi-start` when it already routed long input)
42
+ - `--full-input <txt>` - Full user input for gates (when title is a summary)
16
43
 
17
44
  Examples:
18
45
  ```bash
@@ -408,6 +408,37 @@ await cancelTask('wf-123', 'superseded', false);
408
408
 
409
409
  - **Hardened "Open in editor" against command injection**: Security hardening for untrusted filenames. **Impact on WogiFlow**: Validates the same pattern in `.claude/rules/security/security-patterns.md` — external inputs going into shell commands must be validated. No WogiFlow code change needed.
410
410
 
411
+ ### Features in 2.1.111+
412
+
413
+ - **`xhigh` effort level for Opus 4.7**: New effort level sitting between `high` and `max`, available via `/effort`, `--effort`, and the model picker. Other models fall back to `high`. `/effort` now opens an interactive slider when called without arguments. **Impact on WogiFlow**: The effort-level mapping in `wogi-start.md` now acknowledges `xhigh`/`max` as Opus 4.7-only. For L0 epics running on Opus 4.7, users may prefer `xhigh` over `high` for deeper architectural reasoning — the mapping table documents this as an option. No code change needed; the mapping is advisory.
414
+
415
+ - **`/ultrareview` built-in command**: Claude Code now ships a native `/ultrareview` that runs parallel multi-agent analysis and critique in the cloud — invoke with no arguments to review the current branch, or `/ultrareview <PR#>` to fetch and review a specific GitHub PR. **Relationship to WogiFlow's review commands**: No collision (`wogi-*` prefix). How to choose:
416
+ - `/ultrareview` — cloud-side parallel multi-agent critique. Zero local setup. Best for standalone branch/PR reviews when you don't have peer models configured.
417
+ - `/wogi-peer-review` — uses the peer models you configured via `/wogi-models-setup` (local/BYO models). Best when you want specific perspectives (e.g., a different vendor's model) or offline/cost-controlled review.
418
+ - `/wogi-review` — single-reviewer code review wired into WogiFlow task state (findings logged to `last-review.json`, triaged via `/wogi-triage`). Best for in-flow review during task execution.
419
+ - `/wogi-review-fix` — auto-applies fixes from `/wogi-review` findings.
420
+ Users can combine them: run `/ultrareview` for a wide-angle cloud critique, then `/wogi-review` for task-linked findings.
421
+
422
+ - **`/less-permission-prompts` built-in skill**: Scans recent transcripts for common read-only Bash and MCP tool calls and proposes a prioritized allowlist for `.claude/settings.json`. **Relationship to WogiFlow**: Complementary to `computeLeanConfig()` in `lib/installer.js` — the installer produces a minimal allowlist at install time, while `/less-permission-prompts` tunes the allowlist based on actual session usage. Suggested workflow: after a few WogiFlow sessions, run `/less-permission-prompts` to prune redundant prompts. Future opportunity: surface this suggestion in `/wogi-health` output.
423
+
424
+ - **Auto-allow for read-only bash with globs and `cd <project-dir> &&` prefix**: Read-only commands like `ls *.ts` and commands starting with `cd <project-dir> &&` no longer trigger a permission prompt. **Impact on WogiFlow**: Reduces prompts during WogiFlow hook-driven validation (lint/typecheck) and user-driven exploration. Allowlist rules in `lib/installer.js` that duplicated these patterns are now redundant — minor cleanup opportunity (tracked, low priority). No action required; the installer's lean-config approach already avoids over-emitting.
425
+
426
+ - **Auto mode for Max subscribers on Opus 4.7**: Auto mode is now available for Max subscribers when using Opus 4.7, and no longer requires `--enable-auto-mode`. 2.1.112 fixed a "claude-opus-4-7 is temporarily unavailable" error in auto mode. **Impact on WogiFlow**: WogiFlow's model registry already lists Opus 4.7 (v2.22.0); auto-mode routing is orthogonal to WogiFlow's hybrid mode. Users on Max with Opus 4.7 benefit automatically.
427
+
428
+ - **`OTEL_LOG_RAW_API_BODIES` env var**: Emits full API request and response bodies as OpenTelemetry log events for debugging. **Impact on WogiFlow**: Useful when debugging hybrid mode (`/wogi-hybrid`) routing and peer-review (`/wogi-peer-review`) model calls — set this env var to see the exact payloads reaching each model. Complements WogiFlow's gate telemetry (`/wogi-gate-stats`) which tracks pass/catch/miss rates at a higher level. Set with: `export OTEL_LOG_RAW_API_BODIES=1`. Note: payloads may contain sensitive data — only enable in development.
429
+
430
+ - **Headless `--output-format stream-json` includes `plugin_errors` on init**: Plugin demotion errors (unsatisfied dependencies, conflicting versions) are now surfaced on the init event in headless mode. **WogiFlow opportunity**: `/wogi-health` could read this stream when running in CI/headless mode to flag plugin-registry issues before they cause silent failures. Tracked as an enhancement.
431
+
432
+ - **Opus 4.7 availability fix (2.1.112)**: Fixed a "claude-opus-4-7 is temporarily unavailable" error in auto mode. Aligned with WogiFlow v2.22.0 registry update. No WogiFlow code change needed.
433
+
434
+ - **Windows improvements**: `CLAUDE_ENV_FILE` and SessionStart hook environment files now apply on Windows (previously a no-op). Permission rules with drive-letter paths are now correctly root-anchored, and paths differing only by drive-letter case are recognized as the same path. **Impact on WogiFlow**: Windows users of WogiFlow's SessionStart hook can now configure environment variables via `CLAUDE_ENV_FILE`. Drive-letter-path permission rules generated by the installer now behave correctly. Automatic improvement after upgrade.
435
+
436
+ - **Miscellaneous UX**: Plan files named after the originating prompt (e.g. `fix-auth-race-snug-otter.md`), `/skills` menu supports sorting by estimated token count (press `t`), Ctrl+U clears the entire input buffer (Ctrl+Y restores), Ctrl+L forces a full redraw, and typo suggestions on near-miss subcommands. Documentation-only for WogiFlow.
437
+
438
+ - **Fixed "Unknown skill: commit" error**: Users without a custom `/commit` skill were seeing this error when Claude Code tried to invoke a non-existent built-in. **Impact on WogiFlow**: No WogiFlow-shipped `/commit` skill (commits go through `/wogi-finalize` and git commit instructions). Users benefit passively from the fix.
439
+
440
+ - **Reliability fixes (all automatic after upgrade)**: Terminal display tearing in iTerm2+tmux, `@`-file suggestions re-scanning entire project in non-git directories, LSP diagnostics from before an edit appearing after it, tab-completing `/resume` behavior, `/context` grid rendering, `/clear` dropping session name, spurious decompression/network/transient errors in the TUI. Reverted v2.1.110 cap on non-streaming fallback retries (now uncapped again). Fixed Bedrock/Vertex/Foundry 429 retries pointing users at the wrong status page, bare URLs unclickable when wrapped in tool output, feedback surveys appearing back-to-back. WogiFlow sessions benefit from all of these automatically.
441
+
411
442
  ### Simple Mode Naming Distinction
412
443
 
413
444
  Claude Code's `CLAUDE_CODE_SIMPLE` environment variable (which enables a simplified tool set) is **unrelated** to WogiFlow's `loops.simpleMode` (a lightweight task completion loop using string detection). They are separate features that happen to share the word "simple":
@@ -542,4 +573,4 @@ Run `/keybindings` in Claude Code to customize your shortcuts.
542
573
 
543
574
  ---
544
575
 
545
- *Last updated: 2026-04-16*
576
+ *Last updated: 2026-04-17*
@@ -75,7 +75,7 @@ Don't confuse them. `agents/security.md` (persona) is different from `.workflow/
75
75
 
76
76
  When modifying flow-*.js scripts:
77
77
  - Run `node --check scripts/<file>.js` after edits
78
- - WogiFlow has no test suite - syntax checking is the safety net
78
+ - Run `npm test` — WogiFlow has a native-Node test suite (50+ files under `tests/`, 1800+ assertions) covering hooks, flow-io, security, session state, workspace gates, and more
79
79
  - Check for circular dependencies when moving shared functions
80
80
 
81
81
  ## 8. Feature Refactoring Cleanup
@@ -170,6 +170,6 @@
170
170
  },
171
171
  "_comment_dynamicHooks": "TaskCreated (2.1.84+) and PermissionDenied (2.1.88+) are added by postinstall.js when the CC version supports them. They must NOT be committed statically — CC rejects the entire settings file if it encounters an unknown hook event name.",
172
172
  "_wogiFlowManaged": true,
173
- "_wogiFlowVersion": "2.17.5",
173
+ "_wogiFlowVersion": "2.22.0",
174
174
  "_comment": "Shared WogiFlow hook configuration. Committed to repo for team use. User-specific overrides go in settings.local.json."
175
175
  }
@@ -414,7 +414,7 @@ async function login(args) {
414
414
  console.error(`\n\nServer error (${tokenResponse.statusCode}). Retrying...`);
415
415
  process.stdout.write('x');
416
416
  }
417
- } catch (err) {
417
+ } catch (_err) {
418
418
  process.stdout.write('x');
419
419
  }
420
420
  }
package/lib/installer.js CHANGED
@@ -194,7 +194,7 @@ function detectProjectScripts(projectRoot) {
194
194
  let pkgData;
195
195
  try {
196
196
  pkgData = safeReadJson(packageJsonPath);
197
- } catch (err) {
197
+ } catch (_err) {
198
198
  return detected;
199
199
  }
200
200
 
@@ -989,7 +989,7 @@ async function init(args) {
989
989
  try {
990
990
  const analyzer = require('../scripts/flow-project-analyzer');
991
991
  detection = analyzer.detectProjectType(projectRoot);
992
- } catch (err) {
992
+ } catch (_err) {
993
993
  // Fallback: use basic projectType from installer's own detection
994
994
  detection = {
995
995
  projectType: config.projectType || 'unknown',
@@ -1030,7 +1030,7 @@ async function init(args) {
1030
1030
  // Non-fatal — profile can be generated later via /wogi-test
1031
1031
  }
1032
1032
  }
1033
- } catch (err) {
1033
+ } catch (_err) {
1034
1034
  // Non-fatal — flow-verification-profile may not exist yet during initial install
1035
1035
  }
1036
1036
 
@@ -1083,7 +1083,7 @@ function updateManifestAfterInit(cliDir) {
1083
1083
  if (!manifest || !Array.isArray(manifest.files)) {
1084
1084
  manifest = { version: 1, files: [], directories: ['.workflow'] };
1085
1085
  }
1086
- } catch (err) {
1086
+ } catch (_err) {
1087
1087
  // No manifest yet — create a fresh one
1088
1088
  manifest = { version: 1, files: [], directories: ['.workflow'] };
1089
1089
  }
@@ -1129,7 +1129,7 @@ function walkDirForManifest(dir, baseDir, fileSet) {
1129
1129
  fileSet.add(path.relative(baseDir, fullPath));
1130
1130
  }
1131
1131
  }
1132
- } catch (err) {
1132
+ } catch (_err) {
1133
1133
  // Non-critical — skip unreadable dirs
1134
1134
  }
1135
1135
  }
@@ -128,7 +128,7 @@ async function getLatestVersion(channel) {
128
128
  // Get version for the channel's npm tag
129
129
  const distTags = pkg['dist-tags'] || {};
130
130
  return distTags[channelConfig.npmTag] || distTags.latest || pkg.version;
131
- } catch (err) {
131
+ } catch (_err) {
132
132
  return null;
133
133
  }
134
134
  }
@@ -154,7 +154,7 @@ async function fetchSkillIndex(projectRoot) {
154
154
  throw new Error('Invalid index data');
155
155
  }
156
156
  return parsed;
157
- } catch (err) {
157
+ } catch (_err) {
158
158
  // Return mock index for development/offline
159
159
  return {
160
160
  version: '1.0',
@@ -201,7 +201,7 @@ async function fetchSkillManifest(skillName, projectRoot) {
201
201
  throw new Error('Invalid manifest data');
202
202
  }
203
203
  return parsed;
204
- } catch (err) {
204
+ } catch (_err) {
205
205
  throw new Error(`Skill '${skillName}' not found in registry`);
206
206
  }
207
207
  }
@@ -213,7 +213,7 @@ async function fetchSkillManifest(skillName, projectRoot) {
213
213
  * @param {string} projectRoot - Project root directory
214
214
  * @returns {Promise<Object>} Downloaded files
215
215
  */
216
- async function downloadSkillFiles(skillName, manifest, projectRoot) {
216
+ async function downloadSkillFiles(skillName, manifest, _projectRoot) {
217
217
  const files = {};
218
218
  const baseUrl = `${REGISTRY_CONFIG.baseUrl}/${REGISTRY_CONFIG.branch}/skills/${skillName}`;
219
219
 
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Workspace — Dispatch Tracking (wf-d3e67abe)
5
+ *
6
+ * Silent-worker-halt detection via file-based dispatch records.
7
+ *
8
+ * Manager records every dispatch; any pending dispatch past its
9
+ * expectedDeadline without a matching completion/stop message =
10
+ * silent death. Surfaced on the next manager turn via the
11
+ * UserPromptSubmit hook (no background processes).
12
+ *
13
+ * State file: .workspace/state/dispatched-tasks.json
14
+ * Ring buffer of last MAX_ACTIVE records; older overflow to
15
+ * .workspace/state/dispatched-tasks.archive.jsonl (append-only).
16
+ */
17
+
18
+ const fs = require('node:fs');
19
+ const path = require('node:path');
20
+ const { safeReadJson } = require('./utils');
21
+
22
+ const DEFAULT_DURATION_MS = 30 * 60 * 1000; // 30 min — matches waitForCompletion default
23
+ const MAX_ACTIVE = 100;
24
+ const SCHEMA_VERSION = 1;
25
+
26
+ const VALID_STATUSES = new Set(['pending', 'completed', 'graceful-stop', 'silent-halt']);
27
+
28
+ function stateFilePath(workspaceRoot) {
29
+ return path.join(workspaceRoot, '.workspace', 'state', 'dispatched-tasks.json');
30
+ }
31
+
32
+ function archiveFilePath(workspaceRoot) {
33
+ return path.join(workspaceRoot, '.workspace', 'state', 'dispatched-tasks.archive.jsonl');
34
+ }
35
+
36
+ function loadState(workspaceRoot) {
37
+ const data = safeReadJson(stateFilePath(workspaceRoot), null);
38
+ if (data && typeof data === 'object' && Array.isArray(data.dispatches)) {
39
+ return { version: data.version || SCHEMA_VERSION, dispatches: data.dispatches };
40
+ }
41
+ return { version: SCHEMA_VERSION, dispatches: [] };
42
+ }
43
+
44
+ function saveState(workspaceRoot, state) {
45
+ const filePath = stateFilePath(workspaceRoot);
46
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
47
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2));
48
+ }
49
+
50
+ function archiveRecord(workspaceRoot, record) {
51
+ const filePath = archiveFilePath(workspaceRoot);
52
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
53
+ fs.appendFileSync(filePath, JSON.stringify(record) + '\n');
54
+ }
55
+
56
+ /**
57
+ * Record a dispatch. Appends to state and trims ring buffer.
58
+ *
59
+ * @param {string} workspaceRoot
60
+ * @param {Object} params
61
+ * @param {string} params.taskId - wf-XXXXXXXX
62
+ * @param {string} params.repoName
63
+ * @param {number} [params.expectedDurationMs=DEFAULT_DURATION_MS]
64
+ * @param {string} [params.dispatchedBy='manager']
65
+ * @returns {Object} the created record
66
+ */
67
+ function recordDispatch(workspaceRoot, { taskId, repoName, expectedDurationMs, dispatchedBy }) {
68
+ if (!workspaceRoot || typeof workspaceRoot !== 'string') {
69
+ throw new Error('workspaceRoot required');
70
+ }
71
+ if (!/^wf-[0-9a-f]{8}$/i.test(taskId || '')) {
72
+ throw new Error(`Invalid taskId: ${taskId}`);
73
+ }
74
+ if (!repoName || typeof repoName !== 'string') {
75
+ throw new Error('repoName required');
76
+ }
77
+
78
+ const durationMs = Number.isFinite(expectedDurationMs) && expectedDurationMs > 0
79
+ ? expectedDurationMs
80
+ : DEFAULT_DURATION_MS;
81
+ const now = Date.now();
82
+ const dispatchedAt = new Date(now).toISOString();
83
+ const expectedDeadline = new Date(now + durationMs).toISOString();
84
+
85
+ const record = {
86
+ taskId,
87
+ repoName,
88
+ dispatchedAt,
89
+ expectedDeadline,
90
+ expectedDurationMs: durationMs,
91
+ status: 'pending',
92
+ dispatchedBy: dispatchedBy || 'manager',
93
+ reconciledAt: null,
94
+ reconciledReason: null
95
+ };
96
+
97
+ const state = loadState(workspaceRoot);
98
+ state.dispatches.push(record);
99
+
100
+ // Ring buffer: overflow oldest records to archive
101
+ while (state.dispatches.length > MAX_ACTIVE) {
102
+ const overflow = state.dispatches.shift();
103
+ try { archiveRecord(workspaceRoot, overflow); }
104
+ catch (_err) { /* non-fatal — archive is best-effort */ }
105
+ }
106
+
107
+ saveState(workspaceRoot, state);
108
+ return record;
109
+ }
110
+
111
+ /**
112
+ * Reconcile the most recent pending record for a task.
113
+ *
114
+ * @param {string} workspaceRoot
115
+ * @param {string} taskId
116
+ * @param {string} status - 'completed' | 'graceful-stop' | 'silent-halt'
117
+ * @param {string} [reason]
118
+ * @returns {Object|null} updated record, or null if not found
119
+ */
120
+ function reconcileDispatch(workspaceRoot, taskId, status, reason) {
121
+ if (!VALID_STATUSES.has(status) || status === 'pending') {
122
+ throw new Error(`Invalid reconcile status: ${status}`);
123
+ }
124
+ const state = loadState(workspaceRoot);
125
+ // Find most recent pending record for this taskId (last wins — most recent dispatch)
126
+ for (let i = state.dispatches.length - 1; i >= 0; i--) {
127
+ const r = state.dispatches[i];
128
+ if (r && r.taskId === taskId && r.status === 'pending') {
129
+ r.status = status;
130
+ r.reconciledAt = new Date().toISOString();
131
+ r.reconciledReason = reason || null;
132
+ saveState(workspaceRoot, state);
133
+ return r;
134
+ }
135
+ }
136
+ return null;
137
+ }
138
+
139
+ /**
140
+ * Read all currently-active dispatch records (not archived).
141
+ *
142
+ * @param {string} workspaceRoot
143
+ * @returns {Array<Object>}
144
+ */
145
+ function readDispatches(workspaceRoot) {
146
+ return loadState(workspaceRoot).dispatches;
147
+ }
148
+
149
+ /**
150
+ * Get dispatches whose expectedDeadline has passed and are still pending.
151
+ *
152
+ * @param {string} workspaceRoot
153
+ * @param {number} [now=Date.now()]
154
+ * @returns {Array<Object>} overdue records
155
+ */
156
+ function getOverdueDispatches(workspaceRoot, now) {
157
+ const ts = Number.isFinite(now) ? now : Date.now();
158
+ const dispatches = readDispatches(workspaceRoot);
159
+ return dispatches.filter(r => {
160
+ if (!r || r.status !== 'pending') return false;
161
+ const deadline = Date.parse(r.expectedDeadline || '');
162
+ return Number.isFinite(deadline) && deadline < ts;
163
+ });
164
+ }
165
+
166
+ module.exports = {
167
+ DEFAULT_DURATION_MS,
168
+ MAX_ACTIVE,
169
+ recordDispatch,
170
+ reconcileDispatch,
171
+ readDispatches,
172
+ getOverdueDispatches,
173
+ stateFilePath,
174
+ archiveFilePath
175
+ };
@@ -46,7 +46,7 @@ const EVENT_TYPES = [
46
46
 
47
47
  const EVENTS_FILE = 'events.json';
48
48
  const MAX_EVENTS = 500; // Keep last 500 events
49
- const EVENT_ID_PATTERN = /^evt-[a-f0-9]{8}$/;
49
+ const _EVENT_ID_PATTERN = /^evt-[a-f0-9]{8}$/;
50
50
 
51
51
  // ============================================================
52
52
  // Event Creation
@@ -470,7 +470,7 @@ function broadcastPostChange(workspaceRoot, fromRepo, context, options = {}) {
470
470
  * @param {Object} taskMeta
471
471
  * @returns {{ passed: boolean, message: string, severity: string }}
472
472
  */
473
- function gateDeploymentReadiness(workspaceRoot, context, taskMeta) {
473
+ function gateDeploymentReadiness(workspaceRoot, _context, taskMeta) {
474
474
  const { execFileSync } = require('node:child_process');
475
475
 
476
476
  try {
@@ -668,7 +668,7 @@ function gatePeerNotification(workspaceRoot, context, taskMeta) {
668
668
  * Gate: cascadeVerification
669
669
  * For library repos, verify all consumers were notified.
670
670
  */
671
- function gateCascadeVerification(workspaceRoot, context, taskMeta) {
671
+ function gateCascadeVerification(workspaceRoot, context, _taskMeta) {
672
672
  const gate = WORKSPACE_GATES.find(g => g.name === 'cascadeVerification');
673
673
 
674
674
  if (!context.currentMember || context.currentMember.role !== 'library') {
@@ -694,7 +694,7 @@ function analyzeReviewForCrossRepoImpact(workspaceRoot, manifest, changedFiles,
694
694
  result.endpointChanges = changedApiFiles;
695
695
 
696
696
  // Check if contract was also updated
697
- const contractsDir = path.join(workspaceRoot, '.workspace', 'contracts');
697
+ const _contractsDir = path.join(workspaceRoot, '.workspace', 'contracts');
698
698
  const contractFiles = changedFiles.filter(f => f.includes('.workspace/contracts'));
699
699
 
700
700
  if (contractFiles.length === 0 && changedApiFiles.length > 0) {
@@ -22,6 +22,7 @@ const MESSAGE_TYPES = [
22
22
  'question', // "Does your side handle X?"
23
23
  'bug-report', // "Your endpoint returns 500 when I send Y"
24
24
  'task-complete', // "I finished my side of feature Z"
25
+ 'worker-stopped', // Graceful Stop hook — worker session ending, not necessarily at task completion
25
26
  'needs-help', // "I'm stuck, can you check X on your side?"
26
27
  'heads-up', // "I'm about to change Y, just FYI"
27
28
  'impact-query', // Pre-dev: "I'm about to change X, will this break you?"
@@ -344,7 +344,7 @@ function generateParallelInvestigation(workspaceRoot, bugDescription, manifest)
344
344
  const investigators = [];
345
345
 
346
346
  for (const [name, member] of Object.entries(manifest.members)) {
347
- const repoPath = path.resolve(workspaceRoot, member.path);
347
+ const _repoPath = path.resolve(workspaceRoot, member.path);
348
348
 
349
349
  investigators.push({
350
350
  repoName: name,
@@ -700,6 +700,8 @@ function checkWorkerHealth(port) {
700
700
  * @param {string} taskId — task ID to start
701
701
  * @param {Object} [opts] — dispatch options
702
702
  * @param {string} [opts.effortLevel] — reasoning effort to propagate ('low'|'medium'|'high')
703
+ * @param {number} [opts.expectedDurationMs] — override deadline for silent-halt detection
704
+ * (wf-d3e67abe). Defaults to DEFAULT_DURATION_MS from workspace-dispatch-tracking.
703
705
  * @returns {Promise<{ ok: boolean, message: string }>}
704
706
  */
705
707
  async function dispatchToChannel(workspaceRoot, repoName, taskId, opts = {}) {
@@ -739,6 +741,21 @@ async function dispatchToChannel(workspaceRoot, repoName, taskId, opts = {}) {
739
741
  const dispatchBody = `${effortPrefix}/wogi-start ${taskId}`;
740
742
  const result = await httpPost('127.0.0.1', port, dispatchBody);
741
743
  if (result.ok) {
744
+ // wf-d3e67abe — record dispatch for silent-halt detection.
745
+ // Fail-open: if tracking write fails, dispatch itself still succeeds.
746
+ try {
747
+ const { recordDispatch } = require('./workspace-dispatch-tracking');
748
+ recordDispatch(workspaceRoot, {
749
+ taskId,
750
+ repoName,
751
+ expectedDurationMs: opts.expectedDurationMs,
752
+ dispatchedBy: process.env.WOGI_REPO_NAME || 'manager'
753
+ });
754
+ } catch (err) {
755
+ if (process.env.DEBUG) {
756
+ console.error(`[dispatchToChannel] Dispatch tracking failed (non-fatal): ${err.message}`);
757
+ }
758
+ }
742
759
  return { ok: true, message: `Dispatched /wogi-start ${taskId} to ${repoName} (port ${port})${effortLevel ? ` [effort: ${effortLevel}]` : ''}` };
743
760
  }
744
761