Add Pool Profiles design + implementation plan

Named, portable profiles for the Image Pool: a Pool Profile companion node
(create/select/rename/delete/duplicate/export-import) outputs a POOL_PROFILE
id into the pool's new optional input; profile or pool_id wins. Registry
(name->id) in profiles.json; live edit-time grid switch via cross-node
propagation. TDD plan with a pure stdlib profiles layer incl. zip round-trip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 19:48:39 +02:00
parent d3358c8a75
commit 1b5ac98936
2 changed files with 710 additions and 0 deletions
@@ -0,0 +1,84 @@
# Pool Profiles (companion node) — Design
Date: 2026-06-21
Status: Approved (brainstorming complete, ready for implementation plan)
## 1. Purpose
Make Image Pool contents durable and reusable across workflows via named **profiles**.
A companion node `Pool Profile` creates/selects/manages named profiles and feeds the chosen
one into an Image Pool node, so the same set of images (with their masks/labels) can be
reloaded in any workflow by picking the profile. Profiles are also portable (zip
export/import) to move between machines.
## 2. Storage / registry
`input/grid_pool/profiles.json` maps a friendly **name → stable id**:
```json
{ "profiles": [ {"id": "<uuid>", "name": "characters_A", "created": 1718960000} ] }
```
Each profile's data stays in the existing layout `input/grid_pool/<id>/` (manifest.json +
images + masks). **Backward compatible:** existing random-UUID pools are simply unregistered
ids and keep working unchanged.
## 3. Nodes
### `Pool Profile` (companion, new)
- Widgets: `profile` (dropdown of names, JS-populated) + hidden `profile_id` (JS-owned, like
`pool_id`).
- Buttons: **Create, Rename, Delete, Duplicate/Save-as, Export, Import**.
- Output: `POOL_PROFILE` = the selected profile id.
- `run()` returns `profile_id or "default"`; `IS_CHANGED` returns `profile_id` (so a
selection change re-runs downstream).
### `Image Pool (Grid)` (existing, change — backward compatible)
- New **optional input `profile`** (`POOL_PROFILE`).
- `run()`/`_resolve()`/`IS_CHANGED` use `effective = profile or pool_id` (connected id wins).
- With nothing connected, behaves exactly as today (per-node UUID).
## 4. Live edit-time sync (key UX)
Modeled on `ComfyUI-JSON-Manager/web/project_key.js`: when the companion's selection changes
(or on connect), it walks its `POOL_PROFILE` output links, sets each connected pool node's
hidden `pool_id` widget to the profile id, and calls that pool's refresh. So selecting a
profile instantly shows its images in the grid, and adds/masks land in that profile. The
pool JS exposes a `node._datasetePoolRefresh()` hook for the companion to call.
## 5. Server routes (`/grid_pool/profiles/*`)
`list` (GET), `create` `{name}`, `rename` `{id,name}`, `delete` `{id}`, `duplicate`
`{id,name}`, `export` (GET `?id=` → streams a zip), `import` (multipart zip [+name] → new id).
The route layer generates UUIDs; the pure layer takes ids as params (testable).
## 6. Code shape
- `gates/profiles.py` — pure stdlib: registry read/write (atomic), `find_by_id/name`,
`create/rename/delete/duplicate`, and `export_profile`/`import_profile` (zipfile). Unit-
testable with tmp dirs; no comfy/torch.
- `gates/profiles_routes.py` — aiohttp glue (uuid gen, file streaming).
- `gates/profile_node.py` — the `PoolProfile` node.
- `web/pool_profile.js` — dropdown + action buttons + cross-node propagation.
- `gates/node.py` + `web/grid_image_pool.js` — small additive tweak: optional `profile`
input, `effective` id, and the `_datasetePoolRefresh` hook.
## 7. Edge cases
- Duplicate/import name collision → auto-suffix `name (2)`; create/rename reject duplicates.
- Delete removes the dir (`shutil.rmtree`) and the registry entry.
- Corrupt/missing `profiles.json` → treated as empty registry.
- Import zip carries a `profile_meta.json` (original name) under an internal `pool/` prefix;
imported under a fresh id so it never clobbers an existing profile.
- Profile connected then disconnected → pool keeps the last id (the profile); no data loss.
## 8. Phasing & testing
- **Phase 1**: `profiles.py` (registry + create/select) + `PoolProfile` node + routes +
frontend dropdown + live sync into the pool.
- **Phase 2**: rename / delete / duplicate.
- **Phase 3**: export / import (portable zip).
- **Phase 4 (optional)**: "adopt" an existing unnamed pool into a profile.
Testing: pytest for `profiles.py` (CRUD, duplicate copies images, export→import round-trips a
profile with its files); manual for dropdown, cross-node propagation, and the zip UI.