Decompose: GET /v1/threads/catalog missing from backend #24

Closed
opened 2026-07-12 23:17:39 +00:00 by Mark · 2 comments
Owner

Context

The frontend easystitch-client.ts calls GET /v1/threads/catalog
(getThreadCatalog at line 446) to load the Brother PR10 thread catalog
for the thread-color picker. The backend easystitch-service does not
expose that endpoint — the route is missing from app.py and from the
auto-generated OpenAPI schema. The frontend has to fall back to the
hard-coded BROTHER_PR10_COLORS constant.

Because there's no path from catalog → frontend, paletteIndex: null
keeps coming out of the engine (see #27) and the catalog-aware thread
matching feature is dead in the water.

Goal

Add GET /v1/threads/catalog to the easystitch-service, returning the
Brother PR10 thread catalog with code/name/hex per entry, in the shape
the frontend's ThreadCatalogEntry interface already expects:

type ThreadCatalogEntry = { code: string; name: string; hex: string; }

Response shape:

{
  "catalog": [
    { "code": "001", "name": "White", "hex": "#ffffff" },
    { "code": "002", "name": "Black", "hex": "#000000" }
    ...
  ]
}

Data source: /data/brother_pr10.json (already referenced via
THREAD_PALETTE_PATH in .env.example).

Non-goals

  • Catalog authoring / curation (use the existing JSON file as-is).
  • Wiring the catalog into the digitize pipeline (out of scope — see
    issue #27 which handles the frontend's null paletteIndex tolerance
    for now).
  • POST /v1/threads/resolve improvements (separate concern; backend
    currently returns {"error": "not_implemented"} and that stub is
    tracked by #25).

Pipeline stages

P1: Wire catalog loader + route

Goal: Stand up the route that loads the catalog JSON file and returns
it under the catalog key.

Approach candidates:

  • A. Lazy-load via lru_cache or functools.lru_cache keyed on file
    mtime so the JSON is only re-read if it changes on disk.
  • B. Load once at startup, store on app.state (FastAPI app.state).
  • C. Load per-request with a simple json.load (no caching).

Recommend A — avoids stale-data risk of B, avoids the per-request
disk-hit cost of C.

Outputs: new route handler in easystitch-service/app.py; the
catalog JSON is loaded from the path resolved by
settings.thread_palette_path.

Acceptance:

  • GET http://localhost:8080/v1/threads/catalog returns HTTP 200
  • Response body: {"catalog": [{code, name, hex}, ...]} with at
    least 50 entries
    (full BROTHER PR10 palette has 100 threads)
  • Each entry has all three fields populated, no nulls
  • Unknown shape / file-missing returns 500 with a structured error
    envelope, not a Python traceback

Files touched:

  • /home/b/Projects/easystitch-service/app.py (add the route)
  • /home/b/Projects/easystitch-service/config.py (confirm
    thread_palette_path setting is exposed)

P2: Add tests

Goal: Lock down P1 with backend tests so a future change can't
silently break the catalog format.

Approach candidates:

  • A. Unit test against the FastAPI client fixture with an in-memory
    mock catalog file.
  • B. Integration test that runs against the live service with the real
    data/brother_pr10.json.

Recommend A. The catalog file in data/ is committed to the repo
and reading it from disk via the real path adds noise to the test
suite (and CI may not have the file at the expected path).

Outputs: new test file
/home/b/Projects/easystitch-service/tests/unit/test_threads_catalog.py
covering: happy path returns the catalog shape, missing file is
handled, malformed JSON is handled.

Acceptance:

  • python -m pytest tests/unit/test_threads_catalog.py -v exits 0
  • Each test specifies what assertion it makes
  • No production-code changes in this phase

Files touched:

  • /home/b/Projects/easystitch-service/tests/unit/test_threads_catalog.py (new)

Dependencies

  • Blocks: thread-color picker feature in ThreadCraft UI; any
    downstream feature that wants to map emitted thread hex codes to
    Brother PR10 catalog entries.
  • Blocked by: the catalog JSON file
    (/home/b/Projects/easystitch-service/data/brother_pr10.json) must
    exist with all 100 entries. Verify this exists before kicking
    off; if it's a stub with only a few entries, this phase is blocked
    on completing the catalog content first.

Research required

  • One spike to verify the existing
    data/brother_pr10.json shape and confirm it matches the
    ThreadCatalogEntry interface. If not, P1 needs a transform layer.
# Context The frontend `easystitch-client.ts` calls `GET /v1/threads/catalog` (`getThreadCatalog` at line 446) to load the Brother PR10 thread catalog for the thread-color picker. The backend `easystitch-service` does not expose that endpoint — the route is missing from `app.py` and from the auto-generated OpenAPI schema. The frontend has to fall back to the hard-coded `BROTHER_PR10_COLORS` constant. Because there's no path from catalog → frontend, `paletteIndex: null` keeps coming out of the engine (see #27) and the catalog-aware thread matching feature is dead in the water. ## Goal Add `GET /v1/threads/catalog` to the easystitch-service, returning the Brother PR10 thread catalog with code/name/hex per entry, in the shape the frontend's `ThreadCatalogEntry` interface already expects: ```ts type ThreadCatalogEntry = { code: string; name: string; hex: string; } ``` Response shape: ```json { "catalog": [ { "code": "001", "name": "White", "hex": "#ffffff" }, { "code": "002", "name": "Black", "hex": "#000000" } ... ] } ``` Data source: `/data/brother_pr10.json` (already referenced via `THREAD_PALETTE_PATH` in `.env.example`). ## Non-goals - Catalog authoring / curation (use the existing JSON file as-is). - Wiring the catalog into the digitize pipeline (out of scope — see issue #27 which handles the frontend's null `paletteIndex` tolerance for now). - `POST /v1/threads/resolve` improvements (separate concern; backend currently returns `{"error": "not_implemented"}` and that stub is tracked by #25). ## Pipeline stages ### P1: Wire catalog loader + route **Goal:** Stand up the route that loads the catalog JSON file and returns it under the `catalog` key. **Approach candidates:** - A. Lazy-load via `lru_cache` or `functools.lru_cache` keyed on file mtime so the JSON is only re-read if it changes on disk. - B. Load once at startup, store on `app.state` (FastAPI app.state). - C. Load per-request with a simple `json.load` (no caching). Recommend A — avoids stale-data risk of B, avoids the per-request disk-hit cost of C. **Outputs:** new route handler in `easystitch-service/app.py`; the catalog JSON is loaded from the path resolved by `settings.thread_palette_path`. **Acceptance:** - `GET http://localhost:8080/v1/threads/catalog` returns HTTP 200 - Response body: `{"catalog": [{code, name, hex}, ...]}` with **at least 50 entries** (full BROTHER PR10 palette has 100 threads) - Each entry has all three fields populated, no nulls - Unknown shape / file-missing returns 500 with a structured error envelope, not a Python traceback **Files touched:** - `/home/b/Projects/easystitch-service/app.py` (add the route) - `/home/b/Projects/easystitch-service/config.py` (confirm `thread_palette_path` setting is exposed) ### P2: Add tests **Goal:** Lock down P1 with backend tests so a future change can't silently break the catalog format. **Approach candidates:** - A. Unit test against the FastAPI client fixture with an in-memory mock catalog file. - B. Integration test that runs against the live service with the real `data/brother_pr10.json`. Recommend A. The catalog file in `data/` is committed to the repo and reading it from disk via the real path adds noise to the test suite (and CI may not have the file at the expected path). **Outputs:** new test file `/home/b/Projects/easystitch-service/tests/unit/test_threads_catalog.py` covering: happy path returns the catalog shape, missing file is handled, malformed JSON is handled. **Acceptance:** - `python -m pytest tests/unit/test_threads_catalog.py -v` exits 0 - Each test specifies what assertion it makes - No production-code changes in this phase **Files touched:** - `/home/b/Projects/easystitch-service/tests/unit/test_threads_catalog.py` (new) ## Dependencies - **Blocks:** thread-color picker feature in ThreadCraft UI; any downstream feature that wants to map emitted thread hex codes to Brother PR10 catalog entries. - **Blocked by:** the catalog JSON file (`/home/b/Projects/easystitch-service/data/brother_pr10.json`) must exist with all 100 entries. **Verify this exists before kicking off; if it's a stub with only a few entries, this phase is blocked on completing the catalog content first.** ## Research required - One spike to verify the existing `data/brother_pr10.json` shape and confirm it matches the `ThreadCatalogEntry` interface. If not, P1 needs a transform layer.
Author
Owner

Auto-triage: needs decompose

Marked triage-approved + needs-decompose. decompose-executor will pick this up and break it into implementable sub-issues.

## Auto-triage: needs decompose Marked `triage-approved` + `needs-decompose`. decompose-executor will pick this up and break it into implementable sub-issues.
Mark changed title from GET /v1/threads/catalog missing from backend — frontend ThreadPalette query is permanently 404 to Decompose: GET /v1/threads/catalog missing from backend 2026-07-13 01:53:50 +00:00
Author
Owner

This issue has been decomposed into the following sub-issues:

# Title Phase Scope
#29 Decompose: GET /v1/threads/catalog missing from backend — Ph Goal: Stand up the route that loads the catalog JSON file and returns it und
#30 Decompose: GET /v1/threads/catalog missing from backend — Ph Goal: Lock down P1 with backend tests so a future change can't silently brea

Each sub-issue has been labeled with triage-approved and is ready for queue routing.

This issue has been decomposed into the following sub-issues: | # | Title | Phase Scope | |---|-------|-------------| | #29 | Decompose: GET /v1/threads/catalog missing from backend — Ph | **Goal:** Stand up the route that loads the catalog JSON file and returns it und | | #30 | Decompose: GET /v1/threads/catalog missing from backend — Ph | **Goal:** Lock down P1 with backend tests so a future change can't silently brea | Each sub-issue has been labeled with `triage-approved` and is ready for queue routing.
Mark closed this issue 2026-07-13 01:54:40 +00:00
Sign in to join this conversation.
No description provided.