fix(import): reject conflicting assignee labels before preview or apply - #3014
Conversation
Refs #2980. Share ordinal label validation across preview and both apply routes; add 18 regression cases and qualification notes.
|
Exact-head qualification update for |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Review round 1 - triage (Claude lane, alpha-product-trust)Independent review (fresh context, read-only, given the full patch and issue #2980): no CRITICAL and no HIGH. It went after the one hypothesis that could have blocked - a valid Taskdeck export now being falsely rejected - and refuted it on the source: the only producer of The remaining static gap it named honestly: no Api-level round trip exists today for a board with two cards assigned to two different users ( Fixed here (
|
# Conflicts: # docs/STATUS.md
|
Base moved twice while this PR was in the gate: The Codex connector returned a usage-limit notice here as well ( |
Summary
Closes #2980 once qualified and merged.
The shared import validator now records one ordinal display label per ordinal source key. A conflicting occurrence fails before board/card/column/label/audit construction, whether reached through Preview, typed Apply, or the raw
{source, assigneeMappings}Apply wrapper. Choosing Me or Unassigned does not hide the ambiguity.No automatic name matching, trimming, case folding, identity remapping, endpoint/auth policy, schema, or UI change. Existing bounded-field, missing-mapping, unknown-key, and importer-only target rules remain.
Regression coverage added
18 native Application.Tests cases cover changed/case-changed/whitespace-changed labels, a conflict within one card, both Apply routes with both mapping choices, identical repeats and distinct-card counts, case-distinct keys, archived cards and audit counts, missing maps, and forbidden third-party targets. Every rejected import asserts zero repository Adds, zero SaveChanges/commit, and one rollback.
These tests invoke the real service with mocked repositories; they are not a real-SQLite proof.
Verification / draft gate
The source baseline was checked byte-for-byte against blob
5d8a669ae588471a1cbd42417ca49218bbb3d4b3. The committed production blob90f0cf3c55efb72f1a2142d806aaf5b40936912amatches the locally prepared patch; only the intended validation block changed.NOT RUN locally: .NET compilation/tests, full backend solution, repo docs/link checks. This editing environment has no .NET SDK or direct package/repository network. No synthetic C# execution or passing native test result is claimed. Keep draft pending exact-head hosted CI and independent review.
Commands, behavioral contract and handoff:
docs/analysis/2026-09-11-import-assignee-label-consistency.md.Coexistence
Independent of #3010; based on main
4c479a7ff311912ffb96da027f6e525521bb70df. Does not touch assignment eligibility/deactivation/webhooks, CI control paths, or canonical shared docs. The evidence note carries the proposed contract and the landing follow-through for CARD_ASSIGNMENTS/STATUS. No merge, deployment or human-action check-off.Claude lane qualification (2026-09-12, alpha-product-trust)
The Codex lane released this draft on 2026-09-11; this lane took it over for qualification and landing. The draft gate above is now satisfied, so the PR is marked ready for review.
Ran locally in an isolated worktree:
dotnet test backend/tests/Taskdeck.Application.Tests/Taskdeck.Application.Tests.csproj -c Release -m:1 --filter "FullyQualifiedName~BoardImportAssigneeConsistencyTests"- 18/18 passed.BoardJsonExportImportService.csreverted to theorigin/mainsource and only the new test file kept, the class runs 8 failed / 10 passed - all threePreview_RejectsConflictingLabelsBeforeConstructingABoardrows, the same-card conflict, and all fourApply_BothRoutesRejectConflicts_EvenWithExplicitMappingsrows go red. That also refutes the "the raw wrapper case passes for the wrong reason" hypothesis: a deserialization failure would return beforeBeginTransactionAsync, soAssertNoWrites'RollbackTransactionAsync, Times.Oncecould not hold.... --filter "...BoardImportAssigneeConsistencyTests|...ExportImport|...BoardImport|...BoardJson"- 103/103 passed, re-run after mergingorigin/main(so at the post-fix(review): name restores in card side-effect disclosures #3018 base, not only the drafted one).node scripts/check-docs-governance.mjsandnode scripts/check-doc-links.mjs- both pass.Assertions read, not assumed.
AssertNoWritesverifies noAddAsyncon Boards, Columns, Cards, Labels or AuditLogs, noSaveChangesAsync, noCommitTransactionAsync, andRollbackTransactionAsyncexactly once. All three entry points -PreviewBoardAsync, the typedImportBoardAsync, and the raw{source, assigneeMappings}ImportBoardFromJsonAsync- funnel throughImportBoardCoreAsync, which is where the new check lives. These are mocked-repository application-boundary assertions, as the PR states; they are not a real-SQLite proof.Added on landing: the two shared-doc follow-throughs the draft deliberately deferred - the one-label-per-key paragraph in
docs/product/CARD_ASSIGNMENTS.mdand one boundeddocs/STATUS.mdentry - plusorigin/mainmerged in (one conflict, the STATUS paragraph both sides add). No source or test change from the draft head.Not run locally: the full backend solution, any real-SQLite or HTTP-level import test, and the frontend suite. Hosted CI at this head is the solution-level evidence.