docs(phase-25): complete phase execution

This commit is contained in:
2026-04-10 11:13:18 +02:00
parent aeb3402576
commit 1789ee9093
3 changed files with 165 additions and 7 deletions

View File

@@ -69,7 +69,7 @@
**Milestone Goal:** Transform GearBox from a login-first tool into a public-first discovery platform with always-on catalog search and a browsable feed of community content.
- [x] **Phase 24: Public Access & Infrastructure** - Remove the login wall from read-only routes and add rate limiting to public endpoints (completed 2026-04-10)
- [ ] **Phase 25: Catalog Enrichment & Agent Tools** - Add attribution fields to global items, bulk import API, and MCP tools for agent-powered seeding
- [x] **Phase 25: Catalog Enrichment & Agent Tools** - Add attribution fields to global items, bulk import API, and MCP tools for agent-powered seeding (completed 2026-04-10)
- [ ] **Phase 26: Discovery Landing Page** - Replace the dashboard with a public-first landing page featuring catalog search and community feed
## Phase Details
@@ -149,7 +149,7 @@ Plans:
| 22. Add-from-Catalog & Thread Integration | v2.0 | 2/2 | Complete | 2026-04-06 |
| 23. Manual Entry Fallback | v2.0 | 1/1 | Complete | 2026-04-06 |
| 24. Public Access & Infrastructure | v2.1 | 2/2 | Complete | 2026-04-10 |
| 25. Catalog Enrichment & Agent Tools | v2.1 | 1/2 | In Progress| |
| 25. Catalog Enrichment & Agent Tools | v2.1 | 1/2 | Complete | 2026-04-10 |
| 26. Discovery Landing Page | v2.1 | 0/TBD | Not started | - |
## Backlog

View File

@@ -4,13 +4,13 @@ milestone: v2.1
milestone_name: Public Discovery
status: verifying
stopped_at: Completed 25-02-PLAN.md
last_updated: "2026-04-10T09:07:33.638Z"
last_updated: "2026-04-10T09:13:14.646Z"
last_activity: 2026-04-10
progress:
total_phases: 6
completed_phases: 1
completed_phases: 2
total_plans: 4
completed_plans: 3
completed_plans: 4
percent: 0
---
@@ -25,8 +25,8 @@ See: .planning/PROJECT.md (updated 2026-04-09)
## Current Position
Phase: 25 (catalog-enrichment-agent-tools) — EXECUTING
Plan: 2 of 2
Phase: 999.1
Plan: Not started
Status: Phase complete — ready for verification
Last activity: 2026-04-10

View File

@@ -0,0 +1,158 @@
---
phase: 25-catalog-enrichment-agent-tools
verified: 2026-04-10T09:30:00Z
status: passed
score: 11/11 must-haves verified
re_verification: false
human_verification:
- test: "Open a catalog item with imageCredit and imageSourceUrl set in the database"
expected: "'Photo: <credit>' text appears below the product image, with a 'Source' link that opens the original image URL"
why_human: "Visual rendering and link behavior cannot be verified without a running browser"
- test: "Open a catalog item with sourceUrl set"
expected: "'View product page →' link appears below the description and opens the product page"
why_human: "Visual layout and link behavior cannot be verified without a running browser"
---
# Phase 25: Catalog Enrichment Agent Tools — Verification Report
**Phase Goal:** Global items carry attribution metadata and can be bulk-populated by an MCP agent swarm
**Verified:** 2026-04-10T09:30:00Z
**Status:** PASSED
**Re-verification:** No — initial verification
## Goal Achievement
### Observable Truths (Plan 01)
| # | Truth | Status | Evidence |
|---|-------|--------|----------|
| 1 | upsertGlobalItem called with sourceUrl, imageCredit, imageSourceUrl returns them in the result | VERIFIED | `tests/services/global-item.service.test.ts` line 306: passes all 3 attribution fields and asserts each is returned; test passes |
| 2 | Two upserts with the same (brand, model) return the same item id and created: false on the second call | VERIFIED | `tests/services/global-item.service.test.ts` line 285: verifies `created: false` on second upsert; test passes |
| 3 | Inserting a duplicate (brand, model) updates the existing row instead of failing | VERIFIED | `onConflictDoUpdate` with `target: [globalItems.brand, globalItems.model]` in service; migration adds unique constraint |
| 4 | bulkUpsertGlobalItems returns accurate created vs updated counts matching input mix | VERIFIED | `tests/services/global-item.service.test.ts` tests bulk with mix of new and existing; 21 tests pass |
| 5 | Tags are synced (create-if-not-exists) when provided, left untouched when omitted | VERIFIED | Three-way tag logic (undefined/[]/[names]) confirmed in service and tested at lines 324, 344, 368 |
### Observable Truths (Plan 02)
| # | Truth | Status | Evidence |
|---|-------|--------|----------|
| 6 | POST /api/global-items upserts a single catalog item and returns the item with id | VERIFIED | Route implemented with `zValidator`; `tests/routes/global-items.test.ts` 16 tests pass |
| 7 | POST /api/global-items/bulk upserts up to 100 items in a single transaction and returns created/updated counts | VERIFIED | Route implemented; test covers count accuracy and max-100 enforcement |
| 8 | POST /api/global-items/bulk rejects the entire batch if any item fails validation | VERIFIED | `zValidator` middleware rejects before DB; test confirms 400 with invalid item in array |
| 9 | MCP tool upsert_catalog_item writes a global item with attribution fields | VERIFIED | `catalog.ts` implements handler calling `upsertGlobalItem`; SEED-03 test at line 296 passes all 3 attribution fields and asserts result; 24 MCP tests pass |
| 10 | MCP tool bulk_upsert_catalog batch-writes global items via the bulk service | VERIFIED | `catalog.ts` implements handler calling `bulkUpsertGlobalItems`; tests at lines 318 and 336 pass |
| 11 | Catalog detail page shows image credit and source link below the image when present | VERIFIED (code) | `$globalItemId.tsx` lines 87103: conditional attribution block with `Photo:` credit and `Source` link; client type extended with all 3 fields. Human visual check needed |
**Score:** 11/11 truths verified (1 with additional human visual check recommended)
---
## Required Artifacts
| Artifact | Expected | Status | Details |
|----------|----------|--------|---------|
| `src/db/schema.ts` | globalItems table with attribution columns and unique constraint | VERIFIED | Lines 147152: sourceUrl, imageCredit, imageSourceUrl columns + `unique().on(table.brand, table.model)` |
| `drizzle-pg/0003_loving_serpent_society.sql` | Migration adding 3 columns + unique constraint | VERIFIED | 4-line migration: 3 ALTER TABLE ADD COLUMN + unique constraint |
| `src/shared/schemas.ts` | Zod schemas for upsert and bulk upsert | VERIFIED | `upsertGlobalItemSchema` at line 106, `bulkUpsertGlobalItemsSchema` at line 120 with `.max(100)` |
| `src/shared/types.ts` | UpsertGlobalItemInput and BulkUpsertGlobalItemsInput types | VERIFIED | Lines 5556 export both types inferred from Zod schemas |
| `src/server/services/global-item.service.ts` | upsertGlobalItem and bulkUpsertGlobalItems functions | VERIFIED | Both exported at lines 105 and 176; full implementation, no stubs |
| `tests/services/global-item.service.test.ts` | Tests for upsert, duplicate handling, bulk, tags | VERIFIED | 8 new tests in `describe("upsert operations")`; 21 total pass |
| `src/server/routes/global-items.ts` | POST / and POST /bulk route handlers | VERIFIED | Lines 4360: both routes with zValidator |
| `src/server/mcp/tools/catalog.ts` | catalogToolDefinitions and registerCatalogTools | VERIFIED | File exists; both exports present; attribution fields in inputSchema |
| `src/server/mcp/index.ts` | Catalog tool registration in createMcpServer | VERIFIED | Lines 1012: imports; lines 6267: registration loop |
| `src/client/hooks/useGlobalItems.ts` | GlobalItem interface with attribution fields | VERIFIED | Lines 1315: sourceUrl, imageCredit, imageSourceUrl as `string \| null` |
| `src/client/routes/global-items/$globalItemId.tsx` | Attribution display below image | VERIFIED | Lines 87104: attribution block + fallback spacer div |
| `tests/routes/global-items.test.ts` | Tests for POST single and bulk endpoints | VERIFIED | 9 new tests for POST; 16 total pass |
| `tests/mcp/tools.test.ts` | Tests for catalog MCP tools | VERIFIED | 6 new catalog tool tests; 24 total pass |
---
## Key Link Verification
| From | To | Via | Status | Details |
|------|----|-----|--------|---------|
| `src/server/routes/global-items.ts` | `src/server/services/global-item.service.ts` | import + call upsertGlobalItem / bulkUpsertGlobalItems | WIRED | Lines 813: both service functions imported; lines 46, 57: called in handlers |
| `src/server/mcp/tools/catalog.ts` | `src/server/services/global-item.service.ts` | import + call upsertGlobalItem / bulkUpsertGlobalItems | WIRED | Lines 36: both imported; lines 96, 122: called in tool handlers |
| `src/server/mcp/index.ts` | `src/server/mcp/tools/catalog.ts` | import catalogToolDefinitions + registerCatalogTools | WIRED | Lines 1012: both imported; lines 6367: registerCatalogTools(db) called, loop registers all tools |
| `src/client/routes/global-items/$globalItemId.tsx` | `src/client/hooks/useGlobalItems.ts` | useGlobalItem hook, GlobalItem interface | WIRED | Line 4: useGlobalItem imported; lines 88, 90, 91, 193: item.imageCredit, item.imageSourceUrl, item.sourceUrl all referenced in JSX |
---
## Data-Flow Trace (Level 4)
| Artifact | Data Variable | Source | Produces Real Data | Status |
|----------|---------------|--------|--------------------|--------|
| `$globalItemId.tsx` | `item.imageCredit`, `item.imageSourceUrl`, `item.sourceUrl` | `useGlobalItem``GET /api/global-items/:id``getGlobalItemWithOwnerCount``db.select().from(globalItems)` | Yes — `select()` without column restriction returns all columns including attribution fields | FLOWING |
| `tests/services/global-item.service.test.ts` | `result.item.sourceUrl`, `result.item.imageCredit`, `result.item.imageSourceUrl` | `upsertGlobalItem``tx.insert(...).returning()` | Yes — `returning()` returns full inserted/updated row | FLOWING |
---
## Behavioral Spot-Checks
| Behavior | Command | Result | Status |
|----------|---------|--------|--------|
| Service tests pass | `bun test tests/services/global-item.service.test.ts` | 21 pass, 0 fail | PASS |
| Route tests pass | `bun test tests/routes/global-items.test.ts` | 16 pass, 0 fail | PASS |
| MCP tool tests pass | `bun test tests/mcp/tools.test.ts` | 24 pass, 0 fail | PASS |
| Source lint clean | `bun run lint` (src/ and tests/ only) | No errors in src/ or tests/ | PASS |
| Build succeeds | Not run (no build output to check) | N/A | SKIP — build output not verified |
Note: Full `bun test` suite shows 15 failures and 7 errors, but all failures are in `tests/services/storage.service.test.ts` — a pre-existing mock/dynamic-import issue from phase 17 (commit `be1197f`, April 7) that predates phase 25. No phase 25 file is responsible. The `.obsidian/` lint errors are from Obsidian vault JSON files outside the source tree.
---
## Requirements Coverage
| Requirement | Source Plan | Description | Status | Evidence |
|-------------|------------|-------------|--------|----------|
| CATL-01 | 25-01 | Global items have attribution fields (sourceUrl, manufacturer, imageCredit, imageSourceUrl) | SATISFIED | sourceUrl, imageCredit, imageSourceUrl columns added to schema; `brand` column serves as manufacturer per plan D-02 |
| CATL-02 | 25-01 | Global items have a unique constraint on (brand, model) preventing duplicates | SATISFIED | `unique().on(table.brand, table.model)` in schema; migration 0003 applied |
| CATL-03 | 25-02 | Catalog detail pages display image attribution with credit and source link | SATISFIED (visual check needed) | Attribution block in `$globalItemId.tsx` lines 87103; human visual test required |
| CATL-04 | 25-02 | Bulk import API endpoint accepts multiple catalog items in one request | SATISFIED | `POST /api/global-items/bulk` implemented and tested |
| CATL-05 | 25-01 | Bulk import uses upsert semantics (ON CONFLICT update, not fail) | SATISFIED | `onConflictDoUpdate` in both `upsertGlobalItem` and `bulkUpsertGlobalItems` |
| SEED-01 | 25-02 | MCP server has a dedicated `upsert_catalog_item` tool that writes to globalItems (not user-scoped) | SATISFIED | `upsert_catalog_item` in `catalogToolDefinitions`; registered without userId |
| SEED-02 | 25-02 | MCP server has a `bulk_upsert_catalog` tool for batch catalog population | SATISFIED | `bulk_upsert_catalog` in `catalogToolDefinitions`; registered and tested |
| SEED-03 | 25-02 | Catalog MCP tools include attribution fields (sourceUrl, manufacturer, imageCredit) as parameters | SATISFIED | `sourceUrl`, `imageCredit`, `imageSourceUrl` in `catalogItemInputSchema`; test at line 296 explicitly passes and asserts all 3 |
All 8 required requirement IDs are satisfied. No orphaned requirements found for phase 25.
---
## Anti-Patterns Found
| File | Line | Pattern | Severity | Impact |
|------|------|---------|----------|--------|
| None | — | — | — | — |
No TODO, FIXME, placeholder, empty return, or hardcoded-empty-data patterns found in phase 25 files.
---
## Human Verification Required
### 1. Image Attribution Display
**Test:** Create a global item via `POST /api/global-items` with `imageCredit: "Test Photographer"` and `imageSourceUrl: "https://example.com/image"`. Open the catalog detail page for that item.
**Expected:** Below the product image, "Photo: Test Photographer · Source" appears in small gray text, with "Source" as a clickable link opening `https://example.com/image`.
**Why human:** Visual layout, conditional rendering, and link behavior require a running browser.
### 2. Product Page Link Display
**Test:** Create a global item with `sourceUrl: "https://example.com/product"`. Open its detail page.
**Expected:** "View product page →" link appears below the description section and opens the correct URL.
**Why human:** Visual layout and link behavior require a running browser.
---
## Summary
Phase 25 achieves its goal. Global items now carry attribution metadata (`sourceUrl`, `imageCredit`, `imageSourceUrl`) stored in the database with a unique constraint on `(brand, model)`. An MCP agent swarm can populate the catalog in bulk via `upsert_catalog_item` and `bulk_upsert_catalog` tools, both wired to the service layer through direct imports. The HTTP surface is also available via `POST /api/global-items` and `POST /api/global-items/bulk` with Zod validation. The client detail page renders attribution inline below the product image.
All 8 requirement IDs (CATL-01 through CATL-05, SEED-01 through SEED-03) are satisfied with direct code evidence. All phase-specific tests (61 across 3 test files) pass. Pre-existing storage.service test failures and .obsidian lint issues are not introduced by this phase.
Two items are flagged for human visual verification: attribution rendering and product page link on the catalog detail page.
---
_Verified: 2026-04-10T09:30:00Z_
_Verifier: Claude (gsd-verifier)_