Skip to content

Chat inventory intake: add equipment via the assistant (v5)#30

Open
philosophercode wants to merge 7 commits into
mainfrom
philosophercode/chat-inventory-intake
Open

Chat inventory intake: add equipment via the assistant (v5)#30
philosophercode wants to merge 7 commits into
mainfrom
philosophercode/chat-inventory-intake

Conversation

@philosophercode

Copy link
Copy Markdown
Owner

What

Adds a low-barrier way to contribute inventory through the existing chat (v5). A user describes equipment in free text, dictation, photos, or pasted product/manual URLs; an intake agent researches it, shows an identification card to confirm, and on confirmation writes a draft catalog listing to Notion (tool + unit + category + location + manual/video resources, all published=false).

Design spec: docs/superpowers/specs/2026-06-01-chat-inventory-intake-design.md.

Architecture

Introduces a capability registry — each capability (catalog, units, maintenance, intake) is defined once as data + a run(), then exposed through two adapters: the chat (AI SDK tools) and the MCP endpoint. This removes the prior chat-route/MCP-route duplication and means every capability is automatically available in both surfaces. report_issue (maintenance) is now MCP-callable too.

  • src/lib/capabilities/types, helpers, index, catalog, units, maintenance, intake, chat-adapter, mcp-adapter
  • Chat route + MCP route rewritten to compose from the registry
  • Chat now sends uploaded photos to the model (vision); ChatFab gains identification-card rendering + Web Speech dictation; "Add equipment" front door (reusing the PR v5: "Report a problem" nav button that opens the chat pre-seeded #25 launcher) + nav.add* in all 12 locales

Intake flow

research_tool (normalize + dedupe) → propose_listing (identification card, mandatory confirm gate) → create_tool (draft-by-default write, partial-failure reporting). Supports batches and an "add a unit to the existing tool" path on duplicates.

Safety / correctness

  • Draft-by-default: nothing reaches the live catalog until staff check Published in Notion.
  • Human confirmation is always required before any write.
  • Link verification: resource URLs are verified server-side (YouTube via oEmbed, others via HTTP) in both research_tool and create_tool; fabricated/dead links are pruned and reported, never written.
  • Honest errors: the chat surfaces real failure reasons (e.g. Anthropic 529 overload, Notion validation errors) instead of a generic message; the success card shows an error state on a failed/partial write.
  • MCP writes only exposed when MCP_TOKEN is configured.

Verification

  • npm run typecheck, npm run lint, npm run build all pass in v5/.
  • Tests: 228 passing. (5 GalleryShell failures are pre-existing on main from [codex] Improve mobile catalog UX #27's mobile-catalog change — unrelated to this PR.)
  • Manually tested end-to-end against a live Notion base: added a Creality Ender-3 V3 and a Glowforge Aura via the chat — draft tool + unit + category + location + verified manual/video resources created correctly; fabricated links caught and dropped; multi-select comma validation handled.

🤖 Generated with Claude Code

philosophercode and others added 6 commits June 2, 2026 04:28
Capability-registry architecture (chat + MCP adapters over one source of
truth), agentic intake flow with identification cards, draft-by-default
Notion writes, vision/audio/batch input, and phased build order. Builds on
the merged PR #25 chat launcher.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Capability-registry architecture: catalog/units/maintenance/intake
capabilities defined once, exposed through both the chat and MCP via
chat-adapter and mcp-adapter. Adds the agentic intake flow
(research_tool -> propose_listing -> create_tool) with draft-by-default
Notion writes, duplicate detection, batch handling, and identification
cards. Chat route now sends uploaded photos to the model (vision);
ChatFab gains card rendering and Web Speech dictation; "Add equipment"
front door + nav.add* in all 12 locales.

Built via contract-first parallel subagents. typecheck/lint/build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Wrap shared test render helper in ChatLauncherProvider so component
  tests (ChatFab/PrimaryNav/GlobalChrome) render — these were red on main
  from the PR #25 (launcher) / #29 (test-infra) merge collision.
- Add webSearch_20250305 to the chat route test's anthropic mock (the
  refactored route now wires native web_search alongside web_fetch).
- Add a `chatOnly` flag to CapabilityTool; mark intake research_tool /
  propose_listing chat-only (card/orchestration tools) so the MCP surface
  keeps its read-tool set and exposes only headless-meaningful tools.
- Update MCP route tests to the structured-JSON contract (design §3.3):
  not-found is a `found:false` result, not isError.

v5: typecheck/lint/build green, 232/232 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The agent could fabricate plausible-but-fake resource URLs (observed: an
invented YouTube video id that 404s on oEmbed). Add server-side link
verification:

- verifyUrl/verifyResourceLinks: YouTube checked via oEmbed (authoritative
  for existence — a watch page 200s even for dead videos), others via an
  HTTP GET treating only 404/410/DNS/malformed as invalid (so a bot-blocked
  but real manual isn't false-dropped).
- research_tool prunes unverified links from the candidate and returns them
  as dropped_links so the agent can tell the user.
- create_tool re-verifies as a hard gate before writing resources; dropped
  links are reported in warnings, never written.
- Prompt: forbid fabricating/guessing URLs (esp. video URLs), explain that
  links are verified server-side, and require tight, structured messages
  instead of multi-paragraph research narration.

Resources are already created as drafts (published=false) — confirmed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The chat showed a generic "Something went wrong. Try again." for every
failure, hiding what actually happened (e.g. a transient Anthropic 529
overload looked identical to a real bug).

- route: add onError (describeChatError) to createUIMessageStream and the
  merged toUIMessageStream so the SDK's masked default is replaced with a
  concise, user-facing reason — mapping 529/overloaded, 429/rate-limit,
  timeouts, and auth errors, and otherwise passing the real message through.
- ChatFab: render error.message verbatim, falling back to the generic
  translated string only when the error carries no message.
- tests updated: assert the real message is surfaced + the empty-message
  fallback.

Verified live against an ongoing Anthropic 529: the chat now reads
"The AI service is temporarily overloaded… please try again."

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Live testing surfaced a real write bug (now visible thanks to the error
surfacing): Notion rejects commas in multi-select option names, so a
material like "Wood (plywood, hardwood, veneer)" 400'd the whole tool
create, yet the success card still claimed "saved".

- notion.ts: multiSelectProp rewrites commas to " / " and de-dupes, so
  materials/ppe/tags always satisfy Notion's constraint.
- intake.ts: create_tool card uses a new "error" state when the write
  didn't actually land (success===false || no tool_id) instead of a
  misleading "Saved as a draft" badge; prompt now asks for short discrete
  tag values (no embedded commas).
- types/IdentificationCard/globals: add the "error" card state + banner.

Verified live: Glowforge Aura now writes cleanly (tool+unit+2 resources,
all published=false, both links live-verified 200).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 3, 2026 02:42
@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
makerlab-tools Ready Ready Preview, Comment Jun 3, 2026 3:47am
makerlab-tools-v5 Ready Ready Preview, Comment Jun 3, 2026 3:47am

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the “chat inventory intake” flow to v5 by introducing a capability registry (catalog/units/maintenance/intake) that can be exposed via both the chat route (AI SDK tools + streamed UI cards) and the MCP endpoint (tool registry), plus new UI affordances (Add equipment button, identification cards, dictation mic).

Changes:

  • Introduce src/lib/capabilities/ registry + chat/MCP adapters; refactor chat + MCP routes to consume the registry.
  • Add the intake flow (research_toolpropose_listingcreate_tool) with draft-by-default Notion writes and server-side link verification.
  • Extend the chat UI with identification-card rendering, a new “Add” nav front door, and Web Speech dictation.

Reviewed changes

Copilot reviewed 35 out of 36 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
v5/test/utils/render.tsx Wrap test renders with ChatLauncherProvider for nav/chat launch coverage.
v5/src/styles/globals.css Add styles for dictation mic button and identification card layout.
v5/src/lib/types.ts Add write-only image_uploads support for Notion file-upload-backed images.
v5/src/lib/notion.ts Add Notion write helpers + create/find-or-create functions for intake writes.
v5/src/lib/capabilities/units.ts Add units capability tools (get_unit_details, get_maintenance_history).
v5/src/lib/capabilities/types.ts Define shared capability/tool/card contracts and Zod schemas.
v5/src/lib/capabilities/mcp-adapter.ts Register capability tools on MCP server with write gating.
v5/src/lib/capabilities/maintenance.ts Port report_issue into a MCP-callable write capability.
v5/src/lib/capabilities/intake.ts Implement intake tools, duplicate detection, link verification, and draft creation.
v5/src/lib/capabilities/index.ts Export ordered capability registry + adapters/types re-exports.
v5/src/lib/capabilities/helpers.ts Shared unit/tool lookup + maintenance flattening helpers.
v5/src/lib/capabilities/chat-adapter.ts Wrap capability tools for AI SDK + compose shared system prompt.
v5/src/lib/capabilities/catalog.ts Add catalog discovery tools as a capability.
v5/src/components/PrimaryNav.tsx Add “Add equipment” nav entry point using ChatLauncherProvider.
v5/src/components/IdentificationCard.tsx New client component to render streamed identification cards.
v5/src/components/ChatLauncherContext.tsx Update docs/comments for additional launcher entry points.
v5/src/components/ChatFab.tsx Render data-card parts, add dictation mic, improve error display.
v5/src/components/ChatFab.test.tsx Extend tests for real error-message surfacing and fallback behavior.
v5/src/app/api/mcp/route.ts Rewrite MCP route to register tools from capability registry.
v5/src/app/api/mcp/route.test.ts Update MCP tests for structured JSON results via adapter.
v5/src/app/api/chat/route.ts Rewrite chat route to use capability registry; add attachment extraction + improved error mapping.
v5/src/app/api/chat/route.test.ts Mock new web_search tool integration.
v5/package-lock.json Lockfile update (peer flag adjustment).
v5/messages/ar.json Add nav.add* strings.
v5/messages/en.json Add nav.add* strings.
v5/messages/es.json Add nav.add* strings.
v5/messages/fr.json Add nav.add* strings.
v5/messages/he.json Add nav.add* strings.
v5/messages/hi.json Add nav.add* strings.
v5/messages/ja.json Add nav.add* strings.
v5/messages/ko.json Add nav.add* strings.
v5/messages/pt-BR.json Add nav.add* strings.
v5/messages/ru.json Add nav.add* strings.
v5/messages/tr.json Add nav.add* strings.
v5/messages/zh-CN.json Add nav.add* strings.
docs/superpowers/specs/2026-06-01-chat-inventory-intake-design.md Add the design spec referenced by the PR.
Files not reviewed (1)
  • v5/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +505 to +508
const imageUploads = candidate.image_upload_ids.map((id) => ({
id,
name: attachmentNames.get(id) || "photo",
}));
Comment on lines +77 to +81
// Convert the UI messages, attach any server-fetched manuals, and surface the
// uploaded photos for this turn both to the model (image bytes — design spec
// §6.1) and to the capability layer (file_upload ids the intake `create_tool`
// re-uses to attach the same photo to the new Notion page).
const baseMessages = await convertToModelMessages(messages);
))}
<button
type="button"
className="primary-nav-add"
Comment on lines +711 to +715
className={`chat-mic${isListening ? " chat-mic-active" : ""}`}
aria-label="Dictate"
aria-pressed={isListening}
title="Dictate"
onClick={toggleDictation}
Comment on lines +141 to +145
<p className="id-card-banner id-card-banner-success">
<span className="id-card-banner-icon">
<CheckIcon />
</span>
Saved as a draft — staff will publish it.
Comment on lines +226 to +247
function promptFragment(env: PromptEnv): string {
const { tools, focusedTool } = env;
const sections: string[] = [];

sections.push(
`## Browsing the catalog\n\nUse the catalog tools to answer questions about what's in the lab:\n\n- \`list_tools\` — list everything, optionally filtered by category or location (partial match). Use this for "what do you have", "show me the 3D printers", "what's in the wood shop".\n- \`search_tools\` — keyword search across names, descriptions, materials, and tags. Use this when the student describes a need ("something to cut acrylic", "a tool for sanding") rather than naming a tool.\n- \`get_tool_details\` — full details for one tool by id, slug, or name. Use this when the student asks about a specific tool's specs, training, PPE, restrictions, units, or resources, before answering with anything beyond the summary already in the catalog list below.\n\nGround every answer in the catalog. If a student asks about a tool that isn't in the catalog, say so honestly rather than inventing one.`
);

sections.push(
`## Linking tools\n\nWhenever you mention a tool that exists in the catalog below, **format its name as a markdown link** to its detail page using the slug provided in the catalog: \`[Tool Name](/tools/<slug>)\`. This lets the student jump straight to the tool's page. Examples:\n- "You could use the [Bambu Lab X1-Carbon Combo 3D Printer](/tools/<slug>) for that."\n- "For laser cutting acrylic, check the [Epilog Helix 24](/tools/<slug>)."\n\nDo **not** link the tool the student is already viewing (see Active tool context). Do not invent slugs — only use slugs from the catalog list.`
);

if (focusedTool) {
sections.push(
`## Active tool context\n\nThe student is currently viewing the **${focusedTool.name}** detail page in the MakerLab catalog. If they use pronouns like "this", "it", "that tool", or "the machine", or ask things like "how do I use it" / "what can I make with this" without naming a tool, assume they are asking about the ${focusedTool.name}. Do not wrap "${focusedTool.name}" itself in a tool link — the student is already on its page.`
);
}

sections.push(`## MakerLab catalog (${tools.length} tools)`);
sections.push(tools.map(describeCatalogEntry).join("\n"));

return sections.join("\n\n");
Resource links added via intake are created as drafts (published=false)
alongside the tool. groupResourcesByTool also filtered out published=false
resources, so even after staff published a tool, its manuals/SOP/wiki
links stayed invisible. Drop that per-resource filter: the catalog only
surfaces published tools, so tool-level publishing already gates
visibility — a tool's resources now show with it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants