**U9, PR2.** Docs-only, no changeset (AGENTS.md: internal docs). ## Why All seven slices in `docs/plans/workflow-owned-merge-stack/` were marked `draft-stack-handoff` — accurate when drafted 2026-06-09, wrong now. **A worker picking up this stack cold would have re-implemented S04, which has already landed.** I nearly did. ## Measured, against `main @ 46f35323c` | Slice | Was | Now | Evidence | |---|---|---|---| | S02 projection | draft | `landed-unwired` | `projectMergeRequestToWorkflowWorkItem` implemented, **0 production callers** | | S03 scheduler claim | draft | `landed-unwired` | `claimDueWorkflowWorkItem` implemented; its only caller is S05's processor, itself unwired | | S04 IR regions | draft | **`landed`** | `merge-gate`, `merge-retry`, `manual-merge-hold`, `merge-attempt`, `recovery-router` present in the coding IR | | S05 runtime driver | draft | `landed-unwired` | `runWorkItem` / `processDueWorkflowWorkItem` implemented, exported from `index.ts`, **0 production callers** | | S06/S07/S08 | draft | `not-started` | merge still runs through `merger.ts` + the live `ProjectEngine.mergeQueue` pump | ## The finding that changes U9's sequencing `WorkflowWorkItemKind` is `task | merge | retry | manual-hold | recovery`. The only live pump — `InProcessRuntime.drainWorkflowContinuations` — filters `kinds: ["task"]`. The generic processor that would claim the other four kinds has **no production caller**. So the entire merge-lane work-item vocabulary is dormant: **zero writers, zero readers.** **I checked whether this is a live bug and it is not.** Nothing in production writes a non-`task` kind — the only two writers (`plan-review-continuation.ts`, `workflow-column-boundary-hooks.ts`) both go through `replaceActiveTaskWorkflowContinuation`. Nothing is stranded today. I'd rather say that plainly than let a scary-sounding finding stand unqualified. But it produces a hard ordering constraint, now recorded in S07: > **S07 must not land before S03/S05 are actually driven.** S07 is the slice that starts writing `merge`-kind work items. If it lands first, those items are created and never claimed — a card that reaches the merge boundary and silently stops. This also reframes U9's job on S02/S03/S05: **wire them, don't build them.** ## Scope discipline Docs-only — `git diff --stat` is 9 files, all under `docs/`. No production code, no tests, no behavior. `pnpm lint` clean. I also fixed the three parent-plan lines asserting the slices are "all still `draft-stack-handoff`", and the four landed slices' Stack Role paragraphs that would otherwise contradict their own new Measured State block. Leaving those stale would recreate exactly the defect this PR fixes. Related: #2494 pins the S04 caveat — the IR regions are declared but their config is read by nothing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Updated workflow-owned merge planning documents with current implementation and wiring statuses. - Added measured wiring details showing which workflow capabilities are active, implemented but unused, or not started. - Clarified sequencing requirements to ensure merge processing is not enabled before prerequisite workflow paths are operational. - Corrected slice metadata and references to reflect the latest measured state. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
137 lines
12 KiB
Markdown
137 lines
12 KiB
Markdown
# Workflow Policy Ownership Map
|
||
|
||
## Purpose
|
||
|
||
This map is the U1 characterization artifact for moving merge, retry, scheduling,
|
||
and recovery policy into workflow IR/runtime. It classifies current production
|
||
branches before code is deleted or moved so later cutover work can prove that no
|
||
legacy engine control path was left unowned.
|
||
|
||
## Ownership Categories
|
||
|
||
- `substrate`: engine/core mechanics that remain below workflow policy.
|
||
- `workflow-policy`: decisions that must be represented by workflow nodes,
|
||
workflow node state, or workflow recovery events.
|
||
- `capability`: operations invoked by workflow nodes while still using shared
|
||
guard services.
|
||
- `compat-projection`: legacy task fields or records that may remain as
|
||
derived summaries during migration.
|
||
- `delete-after-cutover`: branches that should disappear once workflow parity is
|
||
authoritative.
|
||
|
||
## Catalog
|
||
|
||
| Surface | Current source | Current owner | Target owner | Disposition |
|
||
|---|---|---|---|---|
|
||
| Auto-merge queue enqueue and dequeue | `packages/engine/src/project-engine.ts` | `ProjectEngine` merge queue | workflow merge work items and merge-gate nodes | `workflow-policy`, `delete-after-cutover` |
|
||
| In-review handoff delay and startup sweep | `packages/engine/src/project-engine.ts` | `task:moved` listener plus in-review scan | workflow completion handoff node creates merge work | `workflow-policy` |
|
||
| Manual `onMerge` requests | `packages/engine/src/project-engine.ts` | engine public merge queue entry point | explicit human/manual workflow event that wakes merge node | `workflow-policy`, `capability` |
|
||
| Merge request shadow contract | `packages/core/src/store.ts`, `packages/engine/src/project-engine.ts`, `packages/engine/src/merger.ts` | store record plus shadow parity branches | workflow work-item state or compatibility projection | `compat-projection` |
|
||
| Merge checkout, integration, conflict resolution, squash, finalize | `packages/engine/src/merger.ts`, `packages/engine/src/merger-ai.ts`, `packages/engine/src/merger-integration-worktree.ts` | merger lifecycle procedures | workflow merge node capabilities calling guard services | `capability` |
|
||
| Branch-group member integration and group promotion | `packages/engine/src/group-merge-coordinator.ts`, `packages/engine/src/merge-trait.ts`, `packages/engine/src/merger-integration-worktree.ts` | group coordinator and merger helpers | branch-group workflow subgraph with separate member and promotion nodes | `workflow-policy`, `capability` |
|
||
| Merge target and auto-merge eligibility guards | `packages/core/src/task-merge.ts` | shared helper used by engine paths | shared guard service called by workflow nodes | `substrate` |
|
||
| Dependency satisfaction treats `in-review` as satisfied | `packages/engine/src/scheduler.ts`, `packages/core/src/task-merge.ts` | scheduler/task helper lifecycle interpretation | workflow completion handoff state and compatibility projection | `workflow-policy`, `compat-projection` |
|
||
| Active scope leases include unmerged `in-review` worktrees | `packages/engine/src/scheduler.ts` | scheduler overlap policy | workflow work leases plus repository guard services | `workflow-policy`, `substrate` |
|
||
| PR monitor starts/stops from `in-review` transitions | `packages/engine/src/scheduler.ts` | scheduler task-move listener | workflow PR/watch nodes or workflow events | `workflow-policy` |
|
||
| Generic agent capacity, routing, claim, and lease mechanics | `packages/engine/src/scheduler.ts` | scheduler | scheduler substrate claiming runnable workflow work | `substrate` |
|
||
| Executor retry storm cap | `packages/engine/src/__tests__/executor-retry-storm.test.ts`, `packages/engine/src/project-engine.ts` | engine retry counters and execution loop | workflow node retry policy plus runtime substrate guard | `workflow-policy`, `substrate` |
|
||
| Generic backoff helpers | `packages/engine/src/retry-with-backoff.ts`, `packages/engine/src/rate-limit-retry.ts` | helper functions | reusable substrate helper called by retry nodes | `substrate` |
|
||
| Transient merge error classification | `packages/engine/src/transient-merge-error-classifier.ts` | helper used by merger/self-healing | merge-node classification input, not route owner | `substrate` |
|
||
| Task-level retry summary fields | `packages/core/src/retry-summary.ts`, `packages/core/src/manual-retry-reset.ts` | task metadata and reset patch | compatibility projection from workflow node/run retry state | `compat-projection` |
|
||
| Manual retry reset | `packages/core/src/manual-retry-reset.ts`, dashboard/API callers | task metadata patch | workflow event clearing targeted failed node retry state | `workflow-policy`, `compat-projection` |
|
||
| Recover mergeable in-review tasks | `packages/engine/src/self-healing.ts` | self-healing directly re-enqueues merge | workflow recovery event wakes merge node | `workflow-policy`, `delete-after-cutover` |
|
||
| Completion handoff limbo recovery | `packages/engine/src/self-healing.ts` | self-healing re-emits auto-merge handoff | workflow recovery event or idempotent handoff node wake | `workflow-policy` |
|
||
| Transient merge failure recovery | `packages/engine/src/self-healing.ts` | self-healing resets merge retries and re-enqueues | merge-node retry policy and retry-after work item | `workflow-policy`, `delete-after-cutover` |
|
||
| Stale merge status recovery | `packages/engine/src/self-healing.ts` | self-healing clears status and may enqueue merge | workflow recovery event plus merge work reconciliation | `workflow-policy` |
|
||
| Already-landed and no-op finalization | `packages/engine/src/self-healing.ts`, `packages/engine/src/merger.ts` | self-healing/merger lifecycle paths | workflow recovery/finalize nodes with repository guard services | `workflow-policy`, `capability` |
|
||
| Backward in-review recovery paths | `packages/engine/src/self-healing.ts`, `docs/self-healing-backward-move-audit.md` | proof-gated self-healing mutations | workflow recovery nodes; engine only emits facts | `workflow-policy`, `delete-after-cutover` |
|
||
| Workflow runtime execution facade | `packages/engine/src/workflow-task-runtime.ts`, `packages/engine/src/workflow-graph-executor.ts` | runtime executes graph nodes | remains workflow runtime owner | `substrate`, `workflow-policy` |
|
||
| Built-in default workflow definitions | `packages/core/src/builtin-coding-workflow-ir.ts`, `packages/core/src/builtin-stepwise-coding-workflow-ir.ts`, `packages/core/src/builtin-pr-workflow-ir.ts` | partial lifecycle expression | authoritative source for default scheduling, retry, merge, and recovery regions | `workflow-policy` |
|
||
| Dashboard task-card merge/retry/stall badges | `packages/dashboard/app/components/TaskCard.tsx` | task fields and legacy classifications | workflow run/work-item projection first, legacy fields second | `compat-projection` |
|
||
| Reliability and diagnostics surfaces | `docs/diagnostics.md`, dashboard reliability views | self-healing and engine status strings | workflow-native recovery and held-work reasons | `compat-projection` |
|
||
|
||
## Non-Bypassable Guard Services
|
||
|
||
These remain centralized and are called by workflow node capabilities before
|
||
mutating git state:
|
||
|
||
- File-scope and squash overlap checks.
|
||
- Branch target and branch-group target validation.
|
||
- Worktree ownership and lease checks.
|
||
- Auto-merge processing gate, including `autoMerge:false` terminal-until-human
|
||
semantics and the shared-branch member integration exception.
|
||
- Run-audit correlation for git operations and recovery facts.
|
||
|
||
## Measured Wiring State (2026-07-28, U9 pre-flight)
|
||
|
||
The S02–S08 substrate is further along than the slice docs claim, but a large part
|
||
of it is **built and not driven**. Measured against `main` at `46f35323c`. Recorded
|
||
here because "the code exists and its tests pass" reads as landed, and for four of
|
||
these that is not the same as running.
|
||
|
||
| Capability | Implementation | Production callers | Reality |
|
||
|---|---|---|---|
|
||
| S1 work-item schema + store API | `0031_workflow_task_continuations.sql`, `store.ts` | live | **Wired.** Drives the plan-review continuation path. |
|
||
| S02 merge-request projection | `projectMergeRequestToWorkflowWorkItem` | **none** | Built, never invoked. |
|
||
| S03 generic scheduler claim | `claimDueWorkflowWorkItem` (`workflow-work-scheduler.ts`) | only the S05 processor, which is itself unwired | Built, never invoked. |
|
||
| S04 built-in IR merge regions | `merge-gate`, `merge-retry`, `manual-merge-hold`, `merge-attempt`, `recovery-router` in the coding IR | n/a — declarations | **Landed.** Node *config* is unread; see `u9-merge-region-node-config-authority.test.ts`. |
|
||
| S05 runtime work-item driver | `WorkflowTaskRuntime.runWorkItem`, `processDueWorkflowWorkItem` | **none** (exported from `index.ts` only) | Built, never invoked. |
|
||
| S06 git/merge capabilities | — | — | Not started. Merge runs through `merger.ts`. |
|
||
| S07 completion handoff creates merge work | — | — | Not started. |
|
||
| S08 workflow-owned merge processing | — | — | Not started. `ProjectEngine.mergeQueue` is the live pump. |
|
||
|
||
**The consequence that matters for U9.** `WorkflowWorkItemKind` is
|
||
`task | merge | retry | manual-hold | recovery`. The only live pump is
|
||
`InProcessRuntime.drainWorkflowContinuations`, and it filters `kinds: ["task"]`.
|
||
The generic processor that would claim the other four kinds
|
||
(`processDueWorkflowWorkItem`) has no production caller.
|
||
|
||
**Non-`task` work items are already being written, and nothing claims them.**
|
||
`createCompletionHandoffWorkflowWork` (`workflow-workitems-ops.ts:68`) writes
|
||
`kind: "merge"` when auto-merge is on and `kind: "manual-hold"` when it is off. It
|
||
is called from the LIVE handoff-to-review path (`moves.ts:438` and `:1150`), inside
|
||
the move transaction. The kind is computed into a variable
|
||
(`autoMerge ? "merge" : "manual-hold"`), which is why a literal grep for
|
||
`kind: "merge"` finds nothing — an earlier revision of this section wrongly
|
||
concluded there were zero writers on exactly that basis.
|
||
|
||
So the current state is:
|
||
|
||
| | State |
|
||
|---|---|
|
||
| Writers of `merge` / `manual-hold` | **Live** — every task that reaches review writes one |
|
||
| Claimers | **None** — the only pump filters `kinds: ["task"]` |
|
||
| Reconcilers | **None** — self-healing's continuation check also filters `kinds: ["task"]` (`self-healing.ts:6575`) |
|
||
| Only terminalization | The NEXT `createCompletionHandoffWorkflowWork` for the same task cancels prior ones as `superseded-by-completion-handoff` |
|
||
|
||
These rows therefore **accumulate in a non-terminal state** (`runnable` /
|
||
`manual-required`), one per task that reaches review, cleared only if that same task
|
||
hands off again.
|
||
|
||
**This does not stall any merge.** The actual merge is enqueued by
|
||
`enqueueMergeQueueInTransaction` in the same transaction, and the legacy
|
||
`ProjectEngine.mergeQueue` pump still drives it. The work items are parallel
|
||
bookkeeping that nothing consumes yet — accumulating dead rows, not stuck cards.
|
||
|
||
**What this means for S07.** S07 does not introduce the writer; the writer is
|
||
already here. What S07 changes is making those items *authoritative* for the merge
|
||
lane. Until the generic pump (S03/S05) is actually driven, promoting these rows from
|
||
bookkeeping to authority converts a benign row leak into cards that reach the merge
|
||
boundary and stop. **S07 must not land before S03/S05 are driven** — and the
|
||
pre-existing unclaimed rows need a reconcile/backfill decision as part of that work.
|
||
|
||
## Deletion Gates
|
||
|
||
- No production caller may start checkout, branch integration, squash, or finalize
|
||
except a workflow merge node or an explicit human/manual API that records an
|
||
equivalent workflow event.
|
||
- `Scheduler` may claim runnable workflow work, apply capacity/routing/leases,
|
||
and monitor PR/watch substrate events; it must not infer merge eligibility,
|
||
retry routing, or task lifecycle advancement from task columns.
|
||
- `SelfHealingManager` may publish typed recovery facts and reconcile metadata;
|
||
it must not directly requeue, pause, fail, unpause, or move merge/retry tasks
|
||
except through guarded workflow primitives.
|
||
- Task-level retry and merge fields are compatibility summaries. Workflow
|
||
run/node/work-item state is the policy authority.
|
||
|