diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index a02f39d..ae622b7 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -5,18 +5,20 @@ {"id":"openforge_catalog-5gb","title":"Frontend: Admin camera position controls UI","description":"Build admin interface to preview and set default camera angles for models. Shows live preview of each angle, allows selecting which is default. Integrates with admin set camera position API. Consider batch operations for setting defaults across similar models.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-21T09:58:45.023938263-07:00","updated_at":"2025-12-21T09:58:45.023938263-07:00","dependencies":[{"issue_id":"openforge_catalog-5gb","depends_on_id":"openforge_catalog-bll","type":"parent-child","created_at":"2025-12-21T09:59:19.862145404-07:00","created_by":"daemon"},{"issue_id":"openforge_catalog-5gb","depends_on_id":"openforge_catalog-hqe","type":"blocks","created_at":"2025-12-21T09:59:36.561390068-07:00","created_by":"daemon"}]} {"id":"openforge_catalog-5na","title":"Phased rollout: Test subset before bulk backfill","description":"Implement safe migration strategy: (1) Generate sprite sheets for small test set (~10-20 diverse models). (2) Deploy frontend with fallback to legacy single thumbnails. (3) Validate with users - test interactive controls, image quality, performance. (4) Iterate on camera positions/layout based on feedback. (5) Only after validation, run bulk backfill for all 8000+ STL files. Include ability to pause/resume bulk processing. Track which models have sprite sheets vs legacy thumbnails.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-21T10:13:57.517770238-07:00","updated_at":"2025-12-21T10:13:57.517770238-07:00","dependencies":[{"issue_id":"openforge_catalog-5na","depends_on_id":"openforge_catalog-bll","type":"parent-child","created_at":"2025-12-21T10:14:18.760726353-07:00","created_by":"daemon"},{"issue_id":"openforge_catalog-5na","depends_on_id":"openforge_catalog-4ji","type":"blocks","created_at":"2025-12-21T10:14:23.673079682-07:00","created_by":"daemon"},{"issue_id":"openforge_catalog-5na","depends_on_id":"openforge_catalog-3cx","type":"blocks","created_at":"2025-12-21T10:14:24.071569003-07:00","created_by":"daemon"},{"issue_id":"openforge_catalog-5na","depends_on_id":"openforge_catalog-dd4","type":"blocks","created_at":"2025-12-21T10:14:24.494018624-07:00","created_by":"daemon"}]} {"id":"openforge_catalog-5tg","title":"Backend API: Get thumbnail variants endpoint","description":"Create Flask endpoint to return sprite sheet info for a model. Returns: (1) Sprite sheet URL, (2) Grid layout (rows, cols, tile dimensions), (3) Angle metadata (name, position in grid, camera coords), (4) Default angle index. Frontend uses this to display correct region from sprite sheet. Handles models with legacy single thumbnail gracefully.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-21T09:58:32.287223899-07:00","updated_at":"2025-12-21T14:00:25.231595972-07:00","closed_at":"2025-12-21T14:00:25.231595972-07:00","close_reason":"Completed: Created GET /api/blueprints/\u003cblueprint_id\u003e/thumbnail-variants endpoint. Returns sprite sheet URL, grid layout (rows/cols/tile_size), angle metadata with camera positions, and default angle index. Gracefully handles legacy single thumbnails. All 319 tests passing.","dependencies":[{"issue_id":"openforge_catalog-5tg","depends_on_id":"openforge_catalog-bll","type":"parent-child","created_at":"2025-12-21T09:59:18.52350897-07:00","created_by":"daemon"},{"issue_id":"openforge_catalog-5tg","depends_on_id":"openforge_catalog-ubp","type":"blocks","created_at":"2025-12-21T09:59:35.26824895-07:00","created_by":"daemon"}]} -{"id":"openforge_catalog-750","title":"Postgres migration: thingiverse sync-state tables","description":"New schema version adding: thingiverse_thing (blueprint_id \u003c-\u003e thing_id, status, published_at, last_synced_at) and thingiverse_file (thing ref, catalog file ref + local hash, remote file id + remote hash, last_synced_at) — the diff ledger for the sync engine. Follow existing openforge/db/schema/version_NN.py migration pattern. If the HAR ticket proves the remote hash is SHA-256 (catalog stores MD5), include storage for the second hash algorithm.","notes":"SCOPE NOTES: (1) thingiverse_file must carry a file-type/role discriminator (model/image/zip/other) since things contain mixed file types. (2) This runs against the LOCAL db; prod gets a subset via the fixture path (see openforge_catalog-8yb) — keep the mapping (blueprint_id, thing_id, public_url) cleanly separable from local-only operational state (hash ledger, sync timestamps).","status":"open","priority":2,"issue_type":"task","owner":"devon.jones@gmail.com","created_at":"2026-07-06T11:42:25.104764326-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T11:55:49.425508213-06:00","dependencies":[{"issue_id":"openforge_catalog-750","depends_on_id":"openforge_catalog-4kx","type":"parent-child","created_at":"2026-07-06T11:42:52.476138903-06:00","created_by":"Devon Jones"}]} +{"id":"openforge_catalog-750","title":"Postgres migration: thingiverse sync-state tables","description":"New schema version adding: thingiverse_thing (blueprint_id \u003c-\u003e thing_id, status, published_at, last_synced_at) and thingiverse_file (thing ref, catalog file ref + local hash, remote file id + remote hash, last_synced_at) — the diff ledger for the sync engine. Follow existing openforge/db/schema/version_NN.py migration pattern. If the HAR ticket proves the remote hash is SHA-256 (catalog stores MD5), include storage for the second hash algorithm.","notes":"SCOPE NOTES: (1) thingiverse_file must carry a file-type/role discriminator (model/image/zip/other) since things contain mixed file types. (2) This runs against the LOCAL db; prod gets a subset via the fixture path (see openforge_catalog-8yb) — keep the mapping (blueprint_id, thing_id, public_url) cleanly separable from local-only operational state (hash ledger, sync timestamps).","status":"in_progress","priority":2,"issue_type":"task","owner":"devon.jones@gmail.com","created_at":"2026-07-06T11:42:25.104764326-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T14:40:56.467812707-06:00","dependencies":[{"issue_id":"openforge_catalog-750","depends_on_id":"openforge_catalog-4kx","type":"parent-child","created_at":"2026-07-06T11:42:52.476138903-06:00","created_by":"Devon Jones"}]} {"id":"openforge_catalog-7dg","title":"Auth/token manager for Thingiverse v2 (login + refresh)","description":"Python module: interactive 'login' does POST /v2/auth/login {usernameOrEmail, password} -\u003e AuthTokensResponse (handle 202 2FA via /v2/auth/2fa/login), persists refresh token securely (env file/keyring, never in repo); runtime path auto-refreshes access JWT via POST /v2/auth/refresh. Password is never stored. Buildable and testable today against the live API — verify a Devon-scoped JWT via GET /v2/users/me. Note: THINGIVERSE_APP_TOKEN in ~/.profile.d/099.api_keys.sh is v1-only and scoped to the wrong account (Joaquin Munguia, id 5577639) — do not use.","status":"closed","priority":1,"issue_type":"feature","owner":"devon.jones@gmail.com","created_at":"2026-07-06T11:42:24.708129858-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T14:39:30.409750275-06:00","closed_at":"2026-07-06T14:39:30.409750275-06:00","close_reason":"Merged in PR #224 to test. TokenManager (login/2FA/refresh/atomic 0600 persistence/server-side logout) + tv_auth CLI + 53 tests at 100% coverage. Live-verified: devonjones (id 47139) login, status, and refresh rotation against the real v2 API. Unblocks gn2 together with hnr (HAR capture).","dependencies":[{"issue_id":"openforge_catalog-7dg","depends_on_id":"openforge_catalog-4kx","type":"parent-child","created_at":"2026-07-06T11:42:52.071225891-06:00","created_by":"Devon Jones"}]} -{"id":"openforge_catalog-8yb","title":"Propagate thingiverse sync data to AWS prod (fixture + API upsert path)","description":"The tool runs against the LOCAL Postgres, but prod (AWS Lambda + serverless Postgres) is populated by replaying fixture JSON through the Flask API (bin/upload_fixture with OPENFORGE_BASE_URL, same pattern as blueprint fixtures). Thingiverse data (blueprint_id \u003c-\u003e thing_id mapping, public thing URLs) must ride that path so the app can render Thingiverse links in prod and support deeper integration later. Deliverables: fixture format for thingiverse mappings (exported from the local sync tables), an authenticated API upsert endpoint in the Flask app, and upload_fixture support. Note: prod likely only needs the mapping/links, not the per-file hash ledger — that is local operational state; decide the split when designing the fixture.","status":"open","priority":2,"issue_type":"feature","owner":"devon.jones@gmail.com","created_at":"2026-07-06T11:55:33.474857151-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T11:55:33.474857151-06:00","dependencies":[{"issue_id":"openforge_catalog-8yb","depends_on_id":"openforge_catalog-750","type":"blocks","created_at":"2026-07-06T11:55:47.512062985-06:00","created_by":"Devon Jones"},{"issue_id":"openforge_catalog-8yb","depends_on_id":"openforge_catalog-4kx","type":"parent-child","created_at":"2026-07-06T11:55:47.882392741-06:00","created_by":"Devon Jones"}]} +{"id":"openforge_catalog-8yb","title":"Propagate thingiverse sync data to AWS prod (fixture + API upsert path)","description":"The tool runs against the LOCAL Postgres, but prod (AWS Lambda + serverless Postgres) is populated by replaying fixture JSON through the Flask API (bin/upload_fixture with OPENFORGE_BASE_URL, same pattern as blueprint fixtures). Thingiverse data (blueprint_id \u003c-\u003e thing_id mapping, public thing URLs) must ride that path so the app can render Thingiverse links in prod and support deeper integration later. Deliverables: fixture format for thingiverse mappings (exported from the local sync tables), an authenticated API upsert endpoint in the Flask app, and upload_fixture support. Note: prod likely only needs the mapping/links, not the per-file hash ledger — that is local operational state; decide the split when designing the fixture.","notes":"DESIGN NOTE from PR #225 review: the schema allows one blueprint to appear in MULTIPLE things (partial unique index is per-thing only, and test_same_source_allowed_in_different_things pins that behavior). The prod fixture mapping is therefore 1:N — (blueprint_id, thing_id, public_url) derived per thingiverse_files row, not a unique-per-blueprint map. The app's Thingiverse links UI must handle a blueprint linking to several things.","status":"open","priority":2,"issue_type":"feature","owner":"devon.jones@gmail.com","created_at":"2026-07-06T11:55:33.474857151-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T14:51:24.318740515-06:00","dependencies":[{"issue_id":"openforge_catalog-8yb","depends_on_id":"openforge_catalog-750","type":"blocks","created_at":"2026-07-06T11:55:47.512062985-06:00","created_by":"Devon Jones"},{"issue_id":"openforge_catalog-8yb","depends_on_id":"openforge_catalog-4kx","type":"parent-child","created_at":"2026-07-06T11:55:47.882392741-06:00","created_by":"Devon Jones"}]} {"id":"openforge_catalog-ad5","title":"Sync engine: diff local files vs remote hashes, reconcile","description":"For each managed thing: diff manifest-directed local files against remote files using hash comparison (via thingiverse_file ledger) -\u003e upload new, replace changed, delete removed, patch drifted metadata. Idempotent; dry-run mode showing planned actions before mutating. Depends on hash-algorithm verification: if remote is SHA-256, compute+store SHA-256 alongside catalog MD5s.","notes":"PRIOR ART (old v1 tool): tv_update_description did bulk find/replace of boilerplate across all things matching a name filter, with --dry-run (desc_update.json shows real mass migrations happened). tv_tag_dl mirrored all things to local JSON with 3s sleeps (self-rate-limiting; v1 had practical rate limits). Lessons: (1) metadata drift detection should cover the shared-boilerplate case so a boilerplate change re-syncs every managed thing's description; (2) throttle API calls; (3) dry-run is a first-class requirement, already in scope.","status":"open","priority":1,"issue_type":"feature","owner":"devon.jones@gmail.com","created_at":"2026-07-06T11:42:26.401028262-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T12:00:34.191543122-06:00","dependencies":[{"issue_id":"openforge_catalog-ad5","depends_on_id":"openforge_catalog-gn2","type":"blocks","created_at":"2026-07-06T11:42:43.166238543-06:00","created_by":"Devon Jones"},{"issue_id":"openforge_catalog-ad5","depends_on_id":"openforge_catalog-750","type":"blocks","created_at":"2026-07-06T11:42:43.606420026-06:00","created_by":"Devon Jones"},{"issue_id":"openforge_catalog-ad5","depends_on_id":"openforge_catalog-d7a","type":"blocks","created_at":"2026-07-06T11:42:44.00974978-06:00","created_by":"Devon Jones"},{"issue_id":"openforge_catalog-ad5","depends_on_id":"openforge_catalog-4kx","type":"parent-child","created_at":"2026-07-06T11:42:53.860069002-06:00","created_by":"Devon Jones"}]} +{"id":"openforge_catalog-aq5","title":"Add general-reviewer to the agent pack (no Gemini on this repo)","description":"Gemini Code Assist/Cursor aren't installed on this repo, so the pack needs a formalized whole-PR generalist reviewer that ALWAYS spawns in every pr-review-loop round, covering what the specialists don't own (logic correctness, domain/design soundness, contract fidelity, docs accuracy). Previously run ad-hoc as the Claude fallback: the invalid-ruff-flag catch was on PR #223 (the pack's own creation PR); the JWT AttributeError catch was PR #224; the multi-accept AND-vs-ANY bug was PR #226.","status":"open","priority":2,"issue_type":"task","owner":"devon.jones@gmail.com","created_at":"2026-07-06T17:33:30.159979202-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T17:43:13.467181767-06:00"} {"id":"openforge_catalog-bll","title":"Multi-Angle Thumbnail Viewing System","description":"Enable users to view STL models from 10 isometric camera angles with interactive controls. Admins can set default viewing angles per model. System generates 10 isometric-perspective angles (6 primary directions + 4 diagonal corners) during file scan using stl-thumb --cam-pos, combined into single sprite sheet PNG. Frontend has drag-to-rotate and keyboard navigation. All angles maintain 3/4 view for depth, not flat orthographic.","status":"open","priority":1,"issue_type":"epic","created_at":"2025-12-21T09:57:50.607001989-07:00","updated_at":"2025-12-21T10:09:20.457266019-07:00"} -{"id":"openforge_catalog-d7a","title":"Manifest schema + metadata assembler","description":"Define per-thing manifest format (YAML): explicit file list for the thing plus field overrides. Assembler builds the thing payload with defaults pulled from catalog Postgres blueprint records (name, description, tag hierarchy -\u003e Thingiverse tags, category, license, images) and applies manifest overrides on top. Zero-manual-entry defaults, curated where it matters. Payload field names/shape finalized against the HAR contract.","notes":"PRIOR ART (old v1 tool): bin/new.*.json templates were per-product-line defaults (openforge2, openlock, construction_kit, miniature, tools): license, category, tags, boilerplate description. Key design lesson: descriptions = per-thing content + SHARED BOILERPLATE FOOTER (Patreon/tutorials/catalog links) that changes over time. Model boilerplate as a shared block composed at assembly time (stored once, referenced by manifests), not baked into each manifest — enables bulk boilerplate updates via re-sync. Old templates: ~/Dropbox/projects/Hardware/objects/OpenForgeDevelopment/openforge/bin/new.*.json","status":"open","priority":2,"issue_type":"feature","owner":"devon.jones@gmail.com","created_at":"2026-07-06T11:42:25.496132115-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T12:00:33.768875881-06:00","dependencies":[{"issue_id":"openforge_catalog-d7a","depends_on_id":"openforge_catalog-4kx","type":"parent-child","created_at":"2026-07-06T11:42:52.892986073-06:00","created_by":"Devon Jones"}]} +{"id":"openforge_catalog-d7a","title":"Manifest schema + metadata assembler","description":"Define per-thing manifest format (YAML): explicit file list for the thing plus field overrides. Assembler builds the thing payload with defaults pulled from catalog Postgres blueprint records (name, description, tag hierarchy -\u003e Thingiverse tags, category, license, images) and applies manifest overrides on top. Zero-manual-entry defaults, curated where it matters. Payload field names/shape finalized against the HAR contract.","notes":"PRIOR ART (old v1 tool): bin/new.*.json templates were per-product-line defaults (openforge2, openlock, construction_kit, miniature, tools): license, category, tags, boilerplate description. Key design lesson: descriptions = per-thing content + SHARED BOILERPLATE FOOTER (Patreon/tutorials/catalog links) that changes over time. Model boilerplate as a shared block composed at assembly time (stored once, referenced by manifests), not baked into each manifest — enables bulk boilerplate updates via re-sync. Old templates: ~/Dropbox/projects/Hardware/objects/OpenForgeDevelopment/openforge/bin/new.*.json","status":"in_progress","priority":2,"issue_type":"feature","owner":"devon.jones@gmail.com","created_at":"2026-07-06T11:42:25.496132115-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T15:00:55.159469636-06:00","dependencies":[{"issue_id":"openforge_catalog-d7a","depends_on_id":"openforge_catalog-4kx","type":"parent-child","created_at":"2026-07-06T11:42:52.892986073-06:00","created_by":"Devon Jones"}]} {"id":"openforge_catalog-dd4","title":"Integration: Wire scanner pipeline for multi-angle generation","description":"Wire multi-angle sprite sheet generation into file scanning pipeline, BUT ONLY AFTER phased rollout validation. Scanner should: (1) Support both legacy single thumbnails AND new sprite sheets during transition. (2) Have feature flag to enable/disable sprite generation. (3) Track which models have sprites vs legacy. (4) Handle failures gracefully - don't block scan if sprite generation fails. (5) Be pausable/resumable for bulk backfill. Only enable bulk processing after small test set is validated with users.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-21T09:58:49.223198843-07:00","updated_at":"2025-12-21T14:35:20.810908751-07:00","closed_at":"2025-12-21T14:35:20.810908751-07:00","close_reason":"Completed: Integrated sprite sheet generation into scanner pipeline with feature flag (ENABLE_SPRITE_THUMBNAILS). Created create_and_upload_thumbnail() helper that handles both sprite sheets and legacy thumbnails. Updated scanner.py and incremental.py to use new function. Gracefully handles failures without blocking scans. Supports transition period with both formats. All 319 tests passing.","dependencies":[{"issue_id":"openforge_catalog-dd4","depends_on_id":"openforge_catalog-bll","type":"parent-child","created_at":"2025-12-21T09:59:20.290625315-07:00","created_by":"daemon"},{"issue_id":"openforge_catalog-dd4","depends_on_id":"openforge_catalog-ubp","type":"blocks","created_at":"2025-12-21T09:59:37.011721674-07:00","created_by":"daemon"},{"issue_id":"openforge_catalog-dd4","depends_on_id":"openforge_catalog-5tg","type":"blocks","created_at":"2025-12-21T09:59:37.450897079-07:00","created_by":"daemon"},{"issue_id":"openforge_catalog-dd4","depends_on_id":"openforge_catalog-hqe","type":"blocks","created_at":"2025-12-21T09:59:37.840969584-07:00","created_by":"daemon"}]} {"id":"openforge_catalog-gn2","title":"Thingiverse v2 API client (write endpoints from HAR contract)","description":"Python client wrapping the v2 API using the auth/token manager: create thing, patch metadata, upload model file, replace file, delete file, list files w/ hashes, publish. Read endpoints (things/{id}/complete etc.) from the public spec (docs/thingiverse-api-v2.md); write endpoints implemented per the HAR-derived contract. Stub the write calls if started before the HAR ticket completes.","status":"open","priority":1,"issue_type":"feature","owner":"devon.jones@gmail.com","created_at":"2026-07-06T11:42:25.975705436-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T11:42:25.975705436-06:00","dependencies":[{"issue_id":"openforge_catalog-gn2","depends_on_id":"openforge_catalog-hnr","type":"blocks","created_at":"2026-07-06T11:42:42.234078586-06:00","created_by":"Devon Jones"},{"issue_id":"openforge_catalog-gn2","depends_on_id":"openforge_catalog-7dg","type":"blocks","created_at":"2026-07-06T11:42:42.679151017-06:00","created_by":"Devon Jones"},{"issue_id":"openforge_catalog-gn2","depends_on_id":"openforge_catalog-4kx","type":"parent-child","created_at":"2026-07-06T11:42:53.406992128-06:00","created_by":"Devon Jones"}]} {"id":"openforge_catalog-hnr","title":"Capture HAR of Thingiverse SPA create/upload/edit flow and extract API contract","description":"Devon captures a HAR (Network tab, preserve log, Fetch/XHR, 'Save all as HAR with content') of a complete flow: create draft thing, set name/description/tags/category/license, upload image, upload STL, replace the STL, delete a file, publish. HAR goes to scratchpad only — contains live JWT/refresh/cookies, never commit; rotate tokens after. From the HAR, extract and document: create-thing endpoint+payload, model-file upload mechanism (direct POST vs presigned URL — matters for Lambda), file object shape incl. hash field, metadata patch, and file delete. Also verify hash algorithm: hash the exact uploaded STL locally with MD5 and SHA-256 and match against the hash Thingiverse reports (Devon guesses SHA-256). Output: docs/thingiverse-api-v2-private.md contract doc.","notes":"SCOPE ADDITION: things will contain photos, zip files, and other non-STL files. During the HAR capture, upload at least one photo, one zip, and one STL, and capture whether they all go through the same upload endpoint/mechanism or different ones (the public spec only shows an image upload route). Verify whether the hash field is reported for ALL file types or only models.","status":"open","priority":1,"issue_type":"task","owner":"devon.jones@gmail.com","created_at":"2026-07-06T11:42:24.225732535-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T11:55:48.284424468-06:00","dependencies":[{"issue_id":"openforge_catalog-hnr","depends_on_id":"openforge_catalog-4kx","type":"parent-child","created_at":"2026-07-06T11:42:51.659042706-06:00","created_by":"Devon Jones"}]} {"id":"openforge_catalog-hqe","title":"Backend API: Admin set default camera position","description":"Create Flask endpoint for admins to set/update the default camera position for individual models. Accepts model_id and camera position (x,y,z). Requires admin authentication. Triggers thumbnail regeneration if needed.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-21T09:58:36.544944445-07:00","updated_at":"2025-12-21T14:01:55.202764217-07:00","closed_at":"2025-12-21T14:01:55.202764217-07:00","close_reason":"Completed: Created PATCH /api/blueprints/\u003cblueprint_id\u003e/thumbnail-variants/default-angle endpoint. Admin-only, requires authentication and CSRF protection. Validates angle index (0-9), updates sprite_metadata.default_angle in database. Returns error for legacy single thumbnails. All 319 tests passing.","dependencies":[{"issue_id":"openforge_catalog-hqe","depends_on_id":"openforge_catalog-bll","type":"parent-child","created_at":"2025-12-21T09:59:18.965408581-07:00","created_by":"daemon"},{"issue_id":"openforge_catalog-hqe","depends_on_id":"openforge_catalog-jnl","type":"blocks","created_at":"2025-12-21T09:59:35.746577657-07:00","created_by":"daemon"}]} {"id":"openforge_catalog-hr4","title":"Admin UI: Manually trigger sprite generation per model","description":"Add admin interface to manually trigger sprite sheet generation for individual models. Useful for: (1) Testing different camera positions on specific models. (2) Regenerating sprites after adjusting defaults. (3) Fixing failed generations. Shows progress, preview of result, ability to approve/reject before replacing existing thumbnail. Can mark models as 'sprite sheet validated' to track rollout progress.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-21T10:14:01.648534686-07:00","updated_at":"2025-12-21T10:14:01.648534686-07:00","dependencies":[{"issue_id":"openforge_catalog-hr4","depends_on_id":"openforge_catalog-bll","type":"parent-child","created_at":"2025-12-21T10:14:19.184588659-07:00","created_by":"daemon"},{"issue_id":"openforge_catalog-hr4","depends_on_id":"openforge_catalog-4ji","type":"blocks","created_at":"2025-12-21T10:14:24.909562746-07:00","created_by":"daemon"}]} {"id":"openforge_catalog-jnl","title":"Database schema for camera positions and thumbnail variants","description":"Add database schema to store: (1) Sprite sheet metadata - grid dimensions (rows, cols), tile size, angle positions in grid. (2) Camera position per angle (x,y,z coords). (3) Default angle index per model. (4) Sprite sheet file path/URL in R2. Consider if this extends existing thumbnail tables or needs new camera_angles table. Must support models with legacy single thumbnail during migration.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-21T09:58:19.570114017-07:00","updated_at":"2025-12-21T13:07:55.263321459-07:00","closed_at":"2025-12-21T13:07:55.263321459-07:00","close_reason":"Completed: Added sprite_metadata JSONB column to images table with GIN index. Migration version 16 created and tested. All tests passing.","dependencies":[{"issue_id":"openforge_catalog-jnl","depends_on_id":"openforge_catalog-bll","type":"parent-child","created_at":"2025-12-21T09:59:17.171165836-07:00","created_by":"daemon"}]} +{"id":"openforge_catalog-li2","title":"setup.py packaging: declare openforge subpackages and thingiverse template data","description":"From PR #226 review (importlib-resources-reviewer, P3): setup.py's packages=['openforge'] never lists subpackages (no find_packages()) and package_data only declares openforge/db/fixtures and openforge/openapi. openforge/thingiverse/templates/*.yaml (and the thingiverse subpackage generally) ship today only because Dockerfile.api.deploy raw-copies the source tree before setup.py install. If deploy ever moves to a built wheel/sdist, package data would 404 in production only. Fix: find_packages() + package_data entry for openforge/thingiverse/templates/*.yaml. Pre-existing gap, not introduced by #226. Original comment: gh api /repos/MasterworkTools/openforge-catalog/pulls/226/comments","status":"open","priority":3,"issue_type":"task","owner":"devon.jones@gmail.com","created_at":"2026-07-06T15:13:07.131836689-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T15:13:07.131836689-06:00"} {"id":"openforge_catalog-lpe","title":"CLI commands: thingiverse create/sync/status","description":"CLI surface in openforge/: 'thingiverse login' (auth manager), 'thingiverse create \u003cmanifest\u003e' (create + populate + upload files + record in sync tables), 'thingiverse sync [thing|--all]' (run sync engine), 'thingiverse status' (show managed things, pending diffs, last sync). Follow existing openforge CLI patterns.","status":"open","priority":2,"issue_type":"feature","owner":"devon.jones@gmail.com","created_at":"2026-07-06T11:42:26.871182106-06:00","created_by":"Devon Jones","updated_at":"2026-07-06T11:42:26.871182106-06:00","dependencies":[{"issue_id":"openforge_catalog-lpe","depends_on_id":"openforge_catalog-ad5","type":"blocks","created_at":"2026-07-06T11:42:44.422774979-06:00","created_by":"Devon Jones"},{"issue_id":"openforge_catalog-lpe","depends_on_id":"openforge_catalog-d7a","type":"blocks","created_at":"2026-07-06T11:42:44.898169896-06:00","created_by":"Devon Jones"},{"issue_id":"openforge_catalog-lpe","depends_on_id":"openforge_catalog-4kx","type":"parent-child","created_at":"2026-07-06T11:42:54.325118338-06:00","created_by":"Devon Jones"}]} {"id":"openforge_catalog-qjn","title":"Testing and documentation for multi-angle thumbnails","description":"Write tests: backend API tests, thumbnail generation tests, frontend component tests. Document: camera angle coordinate system, API endpoints, how to add new angles, admin workflow for setting defaults. Migration guide for existing thumbnails.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-21T09:58:53.460986877-07:00","updated_at":"2025-12-21T09:58:53.460986877-07:00","dependencies":[{"issue_id":"openforge_catalog-qjn","depends_on_id":"openforge_catalog-bll","type":"parent-child","created_at":"2025-12-21T09:59:20.738830826-07:00","created_by":"daemon"},{"issue_id":"openforge_catalog-qjn","depends_on_id":"openforge_catalog-dd4","type":"blocks","created_at":"2025-12-21T09:59:38.217939989-07:00","created_by":"daemon"}]} {"id":"openforge_catalog-szh","title":"Design R2 storage structure for multi-angle thumbnails","description":"Define R2 storage using SPRITE SHEET approach: Generate single PNG containing all 10 isometric-perspective angles in a grid layout (5x2 or 2x5 grid). Angles: 6 primary isometric views (front-iso, back-iso, left-iso, right-iso, top-iso, bottom-iso) + 4 diagonal corners (NE, NW, SE, SW). All maintain 3/4 perspective for depth, not flat orthographic. File naming: {model_id}_angles.png or {md5}_angles.png. Each angle occupies equal-sized tile. Store grid layout metadata in database. One file per model instead of 10 separate files.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-21T09:58:23.809557729-07:00","updated_at":"2025-12-21T13:09:23.10795275-07:00","closed_at":"2025-12-21T13:09:23.10795275-07:00","close_reason":"Completed: Created comprehensive design document (docs/sprite-sheet-storage-design.md) specifying R2 storage structure, sprite layout (2x5 grid, 512x512 tiles), 10 camera angles, path structure (sprites/{md5[:6]}/{md5}.png), and integration strategy.","dependencies":[{"issue_id":"openforge_catalog-szh","depends_on_id":"openforge_catalog-bll","type":"parent-child","created_at":"2025-12-21T09:59:17.627098048-07:00","created_by":"daemon"},{"issue_id":"openforge_catalog-szh","depends_on_id":"openforge_catalog-jnl","type":"blocks","created_at":"2025-12-21T09:59:34.44071374-07:00","created_by":"daemon"}]} diff --git a/.beads/last-touched b/.beads/last-touched index bc266d0..71f6977 100644 --- a/.beads/last-touched +++ b/.beads/last-touched @@ -1 +1 @@ -openforge_catalog-7dg +openforge_catalog-aq5 diff --git a/.reviewers/general-reviewer.md b/.reviewers/general-reviewer.md new file mode 100644 index 0000000..2fb7823 --- /dev/null +++ b/.reviewers/general-reviewer.md @@ -0,0 +1,46 @@ +# general-reviewer + +Whole-PR generalist review. This repo has no Gemini Code Assist or Cursor Bugbot installed, so this reviewer stands in for the broad-coverage bot pass: it reviews the change **as a whole** for problems the specialist reviewers don't own. It runs on **every PR, every round** — it is never skipped for scope reasons and is the last reviewer to retire under diminishing returns. + +**What to review (the generalist's beat):** + +1. **Logic correctness** — does the code do what it claims? Trace the actual behavior of non-trivial logic against its docstrings, comments, PR description, and any docs it cites (e.g. `docs/thingiverse-api-v2.md` for API-facing code). Claims that don't match code are findings; verify empirically (run the code, run the query, decode the example) when practical rather than reasoning from plausibility. +2. **Domain/design soundness** — is the data model / API shape / control flow right for what the surrounding tickets need next? Read the relevant beads tickets (`bd show `) for the work this PR feeds into; flag designs that force a rework one ticket later. +3. **Contract fidelity** — request/response shapes, field names and casing, status-code handling, schema/constraint semantics checked against the authoritative source (spec docs, the live DB, the dependency's actual source) rather than assumption. +4. **Cross-cutting integration** — interactions between the PR's parts, and between the PR and existing code, that no single-file review sees (e.g. a CHECK constraint interacting with an FK's ON DELETE action; a helper's semantics differing from the API-level shape it feeds). +5. **Docs and metadata accuracy** — PR body claims, docstrings, counts, commands. Run any command a doc tells the reader to run. +6. **House style at the design level** — openforge/CLAUDE.md and repo CLAUDE.md philosophy (fail-fast, functional-procedural, Unix tools, cost-conscious serverless), where the specialists' rules don't already cover it. + +**Explicitly NOT this reviewer's beat** (the specialists own these — do not duplicate): +complexity thresholds, exception-handling patterns, test coverage/quality, resource leaks, dead code, logging discipline, package-data loading, migration mechanics, serverless cost rules, frontend conventions, hooks/async patterns, credentials hygiene. If a finding belongs to a specialist's spec, leave it to them; post it only if that specialist is not spawned for this PR and the issue is P1/P2. + +**Method:** + +1. Read the PR body and every changed file IN FULL (not just hunks), plus enough surrounding code and referenced docs/tickets to judge intent. +2. Verify, don't vibe: run tests, run cited commands, query the live schema, trace call paths. The pack's history shows this reviewer's value is empirical catches (an invalid CLI flag documented as runnable; a JWT edge case reproduced by decoding a crafted token; a tag-engine semantic contradiction confirmed with a live query). +3. Post the review as a single PR comment in this structure: + +```markdown +## Code Review (general-reviewer) + +### Critical Issues +[bugs/logic errors with file:line and evidence] + +### Medium Issues +### Minor Issues +[be selective — no nitpicks] + +### Positive Observations + +### Recommendations Summary +**Must fix before merge:** ... +**Should fix:** ... +**Nice to have:** ... + +### Overall Assessment +[1-2 sentences; explicit LGTM when warranted] +``` + +4. On verification rounds: check the consolidated response comment, verify each claimed fix in the actual code (not the reply text), evaluate won't-fix rationales on their merits, scan the fix commits for new issues, and post per-item verdicts. + +**Severity mapping:** Critical → P1, Medium → P2, Minor → P3, consistent with the pack's convention. Findings here follow the same beads-deferral rules as everyone else's. diff --git a/AGENT-REVIEWERS.md b/AGENT-REVIEWERS.md index 9063aad..b0cafaf 100644 --- a/AGENT-REVIEWERS.md +++ b/AGENT-REVIEWERS.md @@ -2,6 +2,15 @@ Each H2 below names a reviewer. The one-line summary tells the main loop **what the reviewer checks and when to spawn it** — use it to decide whether the PR diff is in scope. The body points at `.reviewers/.md`, which the spawned Task reads as its complete specification. +## general-reviewer + +**What it checks:** the whole PR, generalist pass — logic correctness against claims/docs (verified empirically, not by plausibility), domain/design soundness against upcoming beads tickets, contract fidelity against authoritative sources, cross-cutting interactions between the PR's parts, PR-body/docstring accuracy, design-level house style. Explicitly does NOT duplicate the specialist reviewers' dimensions. +**When to spawn:** **every PR, every round** — this repo has no Gemini/Cursor, so this reviewer is the broad-coverage pass. Never skipped for scope; last to retire under diminishing returns. Posts a single structured PR comment (not line comments) and per-item verdicts on verification rounds. + +Read `.reviewers/general-reviewer.md` and follow it as your complete review specification. + +--- + ## complexity-reviewer **What it checks:** McCabe complexity > 10 (hard floor), the CLAUDE.md "And/Or" test and one-screen rule, nesting depth ≥ 4, parameter count > 5, class size > 20 public methods, nested ternaries, redundant single-call wrappers, generic identifiers in long functions. Covers Python and TypeScript/React. @@ -128,10 +137,13 @@ Read `.reviewers/credentials-hygiene-reviewer.md` and follow it as your complete Each reviewer runs independently and reports findings without coordination. A reviewer's silence on something is not an endorsement — it just means that reviewer didn't see anything in its scope. +**No external review bots on this repo.** Gemini Code Assist and Cursor Bugbot are not installed; do not burn `--wait` cycles polling for them. `general-reviewer` is the pack's broad-coverage substitute and spawns on every PR alongside the in-scope specialists. + **Per-reviewer file scope:** | Reviewer | Files in scope | |----------|----------------| +| `general-reviewer` | everything — spawns on every PR, every round | | `complexity-reviewer` | `*.py`, `*.ts`, `*.tsx` (skips tests) | | `error-handling-reviewer` | `*.py` (production code) | | `test-coverage-reviewer` | `*.py`, `src/**/*.ts(x)` | @@ -146,7 +158,7 @@ Each reviewer runs independently and reports findings without coordination. A re | `js-async-reviewer` | `src/**/*.ts(x)` with async/promise code | | `credentials-hygiene-reviewer` | everything (auth code, fixtures, artifacts, `.gitignore`) | -Skip reviewers whose file scope doesn't match the PR diff. +Skip reviewers whose file scope doesn't match the PR diff. (`general-reviewer`'s scope is "everything," so it always matches — the skip rule never applies to it by construction.) ## Branch targeting @@ -170,6 +182,8 @@ Every finding must be tagged with a beads-style priority: **Default severity per reviewer:** +- `general-reviewer`: maps its Critical/Medium/Minor sections to **P1/P2/P3**. + - `credentials-hygiene-reviewer`, `resource-leak-reviewer` (DB connections): **P1** by default. - `error-handling-reviewer`: **P2** by default; **P1** for silent swallows and `return` in `finally`. - `logging-reviewer`: **P2** by default; **P1** for sensitive data in log messages.