Skip to content

Surface per-image mask ids so pre-existing masks become deletable - #91

Merged
stolau merged 1 commit into
mainfrom
mask-own-id-listing
Jul 28, 2026
Merged

Surface per-image mask ids so pre-existing masks become deletable#91
stolau merged 1 commit into
mainfrom
mask-own-id-listing

Conversation

@stolau

@stolau stolau commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Fixes #77.

Summary

  • Global and category masks each have a dedicated listing endpoint returning id (GET /api/masks, GET /api/categories/<category>/masks). Per-image masks didn't — the only listing touching them, GET /api/runs/<run_id>/snapshots/<name>/masks, returns the merged (global ∪ per-image ∪ category) view with id stripped by design. The DELETE endpoint for per-image masks already existed and already scoped correctly; the only gap was that a client had no way to learn a pre-existing per-image mask's id, so the viewer could only offer a delete button for masks created in the current browser session (tracked client-side).
  • Adds GET /api/runs/<run_id>/snapshots/<name>/masks/own, mirroring the existing per-scope listing pattern exactly (same scoping predicate the DELETE endpoint already uses: name + viewport). No changes to render.py/applicable_masks()/compare().
  • Wires it into the viewer's resolveMaskIds() as a 4th id-bearing pool (session-created, global, category, now own-per-image), at both call sites — the mask-chip list and InteractiveImagePane's overlay.
  • createdMasks (session-tracking) is deliberately left untouched — architect-reviewed call: it's a shared three-scope mechanism, and untangling just the per-image third is a separate, more invasive change than this fix requires.

Design went through an architect SOLID review (SOUND, no concerns) before implementation, and the build went through propose-review (proposer → plan-reviewer → implementer + test-author → code-reviewer), with independent APPROVE verdicts at each gate.

Test plan

  • backend/.venv/bin/pytest backend/tests -q — 145 passed, including a new test that creates masks in global/category/other-viewport scopes alongside the target per-image mask and asserts /masks/own returns only the one per-image mask, with id, plus two 404 tests
  • Mutation test: broke the new endpoint's scoping predicate, confirmed the new backend test fails, restored
  • npm test -w viewer — 56 passed; npm run lint -w viewer — clean
  • New viewer test proves a per-image mask injected only via /masks/own (never added to createdMasks) still gets a working delete control — confirmed to fail without the fix (git stash regression check)
  • npm run test:e2e -w examples/demo-app — 17/17 passed (new test added)
  • New e2e test creates a per-image mask via a raw fetch (not a live browser draw, genuinely bypassing session-tracking), confirms a delete control appears in a fresh browser navigation and a real DELETE round-trips against the real backend — confirmed to fail without the fix
  • docs/API.md, backend/CODEMAP.md, viewer/CODEMAP.md, examples/demo-app/CODEMAP.md updated

🤖 Generated with Claude Code

https://claude.ai/code/session_01NrQeh9CXrigN1cvpJuyzy4

Fixes #77. Global and category masks each have a dedicated listing
endpoint returning id (GET /api/masks, GET /api/categories/<c>/masks);
per-image masks didn't, so a mask created outside the current browser
session had no id to delete it by -- the viewer could only track
delete-ability for masks created in the current session, via a
client-side createdMasks array.

Adds GET /api/runs/<run_id>/snapshots/<name>/masks/own, mirroring the
existing per-scope listing pattern (same scoping predicate the DELETE
endpoint already uses: name + viewport). Wires it into the viewer's
resolveMaskIds() as a 4th id-bearing pool alongside session-created,
global, and category masks, at both call sites (the chip list and
InteractiveImagePane's overlay). createdMasks itself is left as-is --
partially redundant now, but a shared three-scope mechanism where
untangling just the per-image third is a separate, more invasive
change than this fix requires.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrQeh9CXrigN1cvpJuyzy4
@stolau
stolau merged commit 03f9ccf into main Jul 28, 2026
5 checks passed
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.

Surface per-image mask ids so pre-existing masks become deletable

1 participant