Skip to content

fix(ia): restore bulk-import Class-column links for legacy imports (#1624)#1625

Open
JasonWildMe wants to merge 2 commits into
mainfrom
fix/issue-1624-bulk-import-class-column
Open

fix(ia): restore bulk-import Class-column links for legacy imports (#1624)#1625
JasonWildMe wants to merge 2 commits into
mainfrom
fix/issue-1624-bulk-import-class-column

Conversation

@JasonWildMe

Copy link
Copy Markdown
Collaborator

Issue

Fixes #1624. Since the 10.2 update, the bulk-import task page (/react/bulk-import-task) shows an empty Class column — no Match Results links — for imports that existed before 10.2. Reported on Whiskerbook and GiraffeSpotter; re-sending the import to ID works around it.

Root cause

The Class column is fed by ImportTask.statsAnnotations(), which selects the task to link via Task.getPreferredMatchResultsTaskForAnnotation(ann, importTaskId, …), passing the import's id. The C15 selector restricts candidates to tasks whose parameters.importTaskId equals that id and returned null when none matched.

importTaskId is stamped onto tasks only by the modern bulk-import path (BulkImport.initiateIA()). Pre-10.2 imports' tasks never carry it, so the filter emptied the candidate list, the selector returned null, encounterTaskInfo got no entry, and the frontend rendered -. (Re-sending to ID creates freshly-stamped tasks, which is why that works around it.)

Fix

A single fallback in Task.getPreferredMatchResultsTaskForAnnotation: when no task carries this import's id, fall back to the annotation's un-stamped tasks (no importTaskId) instead of returning null. Candidates are bucketed three ways:

Bucket Meaning Used?
scoped importTaskId == this import first choice (preserves the 10.2 freeze)
legacy no importTaskId at all fallback when scoped empty
other import a different import's id silently dropped

Dropping the third bucket prevents a legacy import's page from linking to an unrelated, newer import's task.

Why it's safe — strict superset, no regression

  • scoped non-empty → tasks = scoped, byte-identical to the old tasks = filtered and every downstream decision. Any imperfect selection on this path is pre-existing C15 behavior, unchanged here.
  • scoped empty, legacy non-empty → returns a legacy task where the old code returned null. The only behavioral delta — the fix.
  • scoped empty, legacy empty → null, same as before.

The method never returns a different non-null task than before; it only converts specific null results into the legacy task. The encounter page is unaffected (it calls the 2-arg overload with importTaskId == null and never enters this branch).

Also normalizes a blank importTaskId to unscoped (defensive).

Review (Codex, 2 rounds)

Reviewed adversarially with a focus on unexpected fallback behavior. Findings addressed: blank-id edge fixed; doc-ordering wording corrected; the freeze-on-import limitation for the WBIA path (match tasks are created un-stamped in IBEISIA.processCallback) is pre-existing, not introduced here and documented.

Recommended follow-up (separate PR): propagate importTaskId into the taskParameters built in IBEISIA.processCallback so future WBIA match tasks are stamped and the freeze holds for them too. It does not repair already-persisted tasks, so this fallback is still required for existing data.

Full design notes and the no-regression analysis: docs/superpowers/specs/2026-06-17-issue-1624-bulk-import-class-column-fix.md.

Testing

No schema change, no data migration; repairs existing data on all installs at once.

  • Pre-10.2 import: Class-column links render again.
  • Modern (stamped) import: link still frozen on that import's task after an encounter-page re-ID.
  • Encounter page Match Results link: unchanged.

🤖 Generated with Claude Code

…1624)

Since 10.2 the bulk-import task page (/react/bulk-import-task) left the
Class column empty for imports created before the update. The match-task
selector Task.getPreferredMatchResultsTaskForAnnotation restricts
candidates to tasks whose parameters.importTaskId matches the import.
Pre-10.2 imports' tasks carry no such parameter (and WBIA match tasks
are un-stamped in general), so the selector returned null and no Match
Results link rendered.

Add a fallback: when no task carries this import's id, fall back to the
annotation's un-stamped tasks (no importTaskId) instead of returning
null. Tasks stamped with a DIFFERENT import's id are never used, so
cross-import isolation against stamped tasks is preserved. The change is
a strict superset of prior behavior -- the scoped-match path is byte
identical; only the previously-null case now yields the legacy task.

Also normalize a blank importTaskId to unscoped.

Design notes and the no-regression analysis:
docs/superpowers/specs/2026-06-17-issue-1624-bulk-import-class-column-fix.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov-commenter

codecov-commenter commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 51.61%. Comparing base (f6c25b8) to head (06eb730).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1625   +/-   ##
=======================================
  Coverage   51.61%   51.61%           
=======================================
  Files         308      308           
  Lines       12140    12140           
  Branches     3816     3816           
=======================================
  Hits         6266     6266           
  Misses       5591     5591           
  Partials      283      283           
Flag Coverage Δ
backend 51.61% <ø> (ø)
frontend 51.61% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

… v2 imports

For v2 ml-service bulk imports, getPreferredMatchResultsTaskForAnnotation
returned the detection/umbrella task (stamped with importTaskId but NOT a
renderable match task and owning no MatchResults) instead of the per-annotation
match task (renderable, owns the results, but unstamped). The importTaskId
scoping pre-filtered candidates before renderability was evaluated, so the
stamped-but-resultless detection task won and the selector fell back to its
root. Result: the /react/bulk-import-task "Class" column rendered
"<iaClass>: {}" and every Match Results link opened an empty page, even though
identification completed and candidates exist.

Fix: evaluate renderable match tasks first across all candidates; use
importTaskId only as a preference within that set (this import's stamped task,
else newest unstamped, never a foreign import's). Only when no usable renderable
task exists do we fall back to the prior scoped/legacy root logic, so PR #1625's
legacy-import (#1624) behavior and foreign-import isolation are preserved.

Extracts the pure selection logic into Task.selectPreferredMatchTask(List, String)
and adds TaskSelectPreferredMatchTaskTest (10 cases). Design + analysis in
docs/plans/2026-06-18-bulk-import-match-task-selection-fix.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@JasonWildMe JasonWildMe self-assigned this Jun 18, 2026
@JasonWildMe JasonWildMe requested a review from naknomum June 18, 2026 10:31

@naknomum naknomum left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Class column empty in bulk import tasks since 10.2 update

3 participants