Why
Product/manufacturer candidate extraction already exists (the submittals module derives product
candidates with paragraph anchors), but it only surfaces through the project-scoped submittal
register. Master libraries have their own review cadence: firms periodically audit whether the
manufacturers named in their masters still exist and remain relevant. A headless API should hand
any client the compiled manufacturer list for a library — with anchors back to every naming
paragraph — and accept review verdicts back, so the judgment work (done by a human or an
automated workflow outside the API) is persisted once and cited deterministically thereafter.
What
GET /libraries/{id}/products: distinct product/manufacturer candidates across the library's
specs — normalized name, occurrence count, citing specs, paragraph anchors, joined review
state. Reuses the existing extraction machinery at library scope.
- Review write-back (mirrors the standards-registry verdict pattern):
migration (reversible) for a small verdict table keyed by normalized candidate name —
status (active | defunct | unknown), verified_at (server-stamped), source_url, notes.
- Write path:
PUT /products/{normalizedName}/verification (or scoped under the library —
decide in review). Zod-validated.
- MCP:
list_library_products (read) + record_product_verification (write);
contract-map.ts entries.
Tasks
Acceptance criteria
- One call compiles the distinct manufacturers named across a library with anchors.
- A recorded verdict survives re-ingest of the underlying specs (keyed by normalized name,
not paragraph id) and appears in subsequent rollups.
Why
Product/manufacturer candidate extraction already exists (the submittals module derives product
candidates with paragraph anchors), but it only surfaces through the project-scoped submittal
register. Master libraries have their own review cadence: firms periodically audit whether the
manufacturers named in their masters still exist and remain relevant. A headless API should hand
any client the compiled manufacturer list for a library — with anchors back to every naming
paragraph — and accept review verdicts back, so the judgment work (done by a human or an
automated workflow outside the API) is persisted once and cited deterministically thereafter.
What
GET /libraries/{id}/products: distinct product/manufacturer candidates across the library'sspecs — normalized name, occurrence count, citing specs, paragraph anchors, joined review
state. Reuses the existing extraction machinery at library scope.
migration (reversible) for a small verdict table keyed by normalized candidate name —
status(active | defunct | unknown),verified_at(server-stamped),source_url,notes.PUT /products/{normalizedName}/verification(or scoped under the library —decide in review). Zod-validated.
list_library_products(read) +record_product_verification(write);contract-map.tsentries.Tasks
src/db/queries/: library-scope extraction/rollup + verdict upsertsrc/api/: routes +openapi.yamlsame PRsrc/mcp/: tools + tiers + contract mapnormalization edge cases (punctuation, "Inc."/"Corp." suffixes) — mark genuine
ambiguities as KNOWN AMBIGUITY per house rule
Acceptance criteria
not paragraph id) and appears in subsequent rollups.