Carried out of #2926 so the residual keeps a tracker when that issue closes. Source-traced from the two follow-up comments on #2926 (originally from the #2951 review, comments 3983336933 and 3983545705). No runtime reproduction yet.
ProposalOperationContractValidator now projects a proposal's ordered column occupancy for the restore-lifecycle WIP contract (PR for #2926): card create, card move and both lifecycle actions fold into a per-column active-count delta, and a restore is measured against that. The warning surface was deliberately left out of that slice and still disagrees with the gate:
ProposalConflictDetector.GetProjectedColumnChangesAsync projects only create and move operations, not lifecycle effects. A WIP-1 column holding one archived card, with a proposal containing restore-lifecycle(A) plus a create/move of B, can still render capacity as 1/1 (Ok tone) even though the contract now refuses the proposal at approve. The warning is wrong in the safe direction here - the gate refuses first - but Review shows a positive capacity signal for a proposal that cannot apply.
- When an outgoing move's target card is archived, the active-only column baseline excludes it while
GetProjectedColumnChangesAsync still subtracts its source delta, so a move-out plus an incoming create/move can show false capacity alongside the stale-card warning. ProposalOperationContractValidator rejects archived non-lifecycle edits, so this is a preview-truth defect, not a demonstrated committed WIP bypass.
Acceptance
GetProjectedColumnChangesAsync applies ordered lifecycle effects (archive frees a slot, restore takes one) and active-state-aware move deltas, so the conflict/capacity rows agree with the contract gate.
- Regression: a mixed lifecycle + create/move proposal renders a
wip-limit warning rather than a positive capacity row.
- Regression: a move-out whose card is archived does not produce a false capacity signal.
- Control: a proposal that genuinely fits keeps its Ok capacity row.
Files backend/src/Taskdeck.Application/Services/ProposalConflictDetector.cs, backend/tests/Taskdeck.Application.Tests/Services/ProposalConflictDetectorTests.cs.
Proving check dotnet test backend/tests/Taskdeck.Application.Tests/Taskdeck.Application.Tests.csproj -c Release -m:1 --filter "FullyQualifiedName~ProposalConflictDetector"
Warning-surface truth only; no new WIP redesign is requested and the approve/execute gate is not in scope.
Carried out of #2926 so the residual keeps a tracker when that issue closes. Source-traced from the two follow-up comments on #2926 (originally from the #2951 review, comments 3983336933 and 3983545705). No runtime reproduction yet.
ProposalOperationContractValidatornow projects a proposal's ordered column occupancy for the restore-lifecycle WIP contract (PR for #2926): card create, card move and both lifecycle actions fold into a per-column active-count delta, and a restore is measured against that. The warning surface was deliberately left out of that slice and still disagrees with the gate:ProposalConflictDetector.GetProjectedColumnChangesAsyncprojects only create and move operations, not lifecycle effects. A WIP-1 column holding one archived card, with a proposal containingrestore-lifecycle(A)plus acreate/moveof B, can still render capacity as1/1(Ok tone) even though the contract now refuses the proposal at approve. The warning is wrong in the safe direction here - the gate refuses first - but Review shows a positive capacity signal for a proposal that cannot apply.GetProjectedColumnChangesAsyncstill subtracts its source delta, so a move-out plus an incoming create/move can show false capacity alongside the stale-card warning.ProposalOperationContractValidatorrejects archived non-lifecycle edits, so this is a preview-truth defect, not a demonstrated committed WIP bypass.Acceptance
GetProjectedColumnChangesAsyncapplies ordered lifecycle effects (archive frees a slot, restore takes one) and active-state-aware move deltas, so the conflict/capacity rows agree with the contract gate.wip-limitwarning rather than a positivecapacityrow.Files
backend/src/Taskdeck.Application/Services/ProposalConflictDetector.cs,backend/tests/Taskdeck.Application.Tests/Services/ProposalConflictDetectorTests.cs.Proving check
dotnet test backend/tests/Taskdeck.Application.Tests/Taskdeck.Application.Tests.csproj -c Release -m:1 --filter "FullyQualifiedName~ProposalConflictDetector"Warning-surface truth only; no new WIP redesign is requested and the approve/execute gate is not in scope.