Commit graph

400 commits

Author SHA1 Message Date
SJCoetz
18afe7d2d9 feat(context): add order-item-context-service skeleton + types
Co-authored-by: Orca <help@stably.ai>
2026-08-07 02:23:45 +02:00
Claude
357decbb2b docs(plan): order-item context block — 12 TDD tasks
Implementation plan for the spec at
docs/superpowers/specs/2026-08-07-order-item-context-block-design.md.

Tasks: backend service skeleton + impl + tests, /api/v1/.../context
endpoint + tests, OrderItemContextPanel + tests,
OrderItemContextPopoverTrigger + tests, sprint modal wire-in, orders
page wire-in, full test suite + build smoke.

Each task follows a TDD cycle: write failing test, run to confirm,
implement minimal code, run to confirm pass, commit.

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-08-07 02:18:28 +02:00
Claude
3f82b0784d docs(spec): order-item context block (sprint modal + orders page)
Replaces the AI Insights section in order-sprint-process-modal with a
deterministic PocketBase-derived context block (last quote, last order,
last invoiced, stock). Same backend exposed as a popover trigger on
the orders detail page, replacing the existing AI-history ⓘ there.

Backend: new src/lib/server/order-item-context-service.ts +
GET /api/v1/order-items/[id]/context. Reuses pure helpers from
item-history-service (normaliseSkuForHistory, findBestDescriptionMatch,
aggregateSuppliers, computeCheapest).

UI: new <OrderItemContextPanel/> (inline expandable for the sprint
modal) and <OrderItemContextPopoverTrigger/> (ⓘ popover for orders
page). Apply = cost + supplier only; selling stays locked to the order.

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-08-07 02:16:31 +02:00
SJCoetz
f35779369a fix(stock-purchase-items): drop dead drv_collected_at write to unblock build
The markLineCollected service and the stock-orders page's
handleAssignDriver (delivery-sentinel auto-collect path) both
wrote `drv_collected_at` to `stock_purchase_items`, but that
field exists on `order_items`, not on `stock_purchase_items`
(verified against pb_schema-2.json). Per AGENTS.md "select
fields are not self-service", PB would 500 both writes at
runtime. The TS error from the service was the build break
flagged in the 2026-08-06 23:34 deployment.

The page.tsx write was a latent runtime bug — TypeScript
never caught it because PB's loose `update` patch typing
accepts any field name. Both writes are removed; the
collection moment is captured by PB's `updated` timestamp on
the line row, which is sufficient for the auto-collect audit
trail. Inline comments point to FEATURE-REQUESTS as the
follow-up if a dedicated collection-date field is wanted
later (would need a PB admin schema change).

Also drops the matching test assertions that read
`drv_collected_at` off the response and replaces them with
a comment explaining the absence.

Tests: vitest 59/59 across stock-purchase-items-service +
stock-orders/[id]; npm run build compiles clean
(Next.js 16.2.6 Turbopack + TypeScript).

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-08-07 01:39:03 +02:00
SJCoetz
fe8a3c6872 feat: rework invoicing stock booking section
Move StockBookingSection from above the tab strip into the To Invoice tab's card list for consistent visual styling. Add expandable, lazy-loaded purchase order line items with session-based caching to reduce redundant network requests. Update component UI, styling, and documentation, and fix test mocks to prevent conflation of stock_purchases and order_items collection calls. Add ZAR currency formatting helper for displaying stock line item costs.
2026-08-07 01:07:56 +02:00
SJCoetz
c4125ee40a feat: add split-invoice stock PO workflow and supporting tooling
Ship full split-invoice stock purchase order workflow:
- Add 5 new MCP tools for per-line stock purchase item management and catalogue updates
- Add idempotent database migration adding sku, unit, and supplier_sku fields to stock_purchase_items
- Implement service layers for all new operations, including auto-closing the parent PO when all lines are fully collected
- Add complete test coverage for new services and API routes
- Update all relevant documentation including MCP API specs, agent instructions, and feature request logs
2026-08-07 00:53:29 +02:00
SJCoetz
d167f81ad5 feat(stock-orders): inline cost_price editing on lines table
Co-authored-by: Orca <help@stably.ai>
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-06 23:36:27 +02:00
SJCoetz
d5ed390de4 feat(pb): add stock_purchases.additional_attachments file field (migration)
Co-authored-by: Orca <help@stably.ai>
2026-08-06 23:32:12 +02:00
SJCoetz
bbfe20ec2d docs: register stock_purchases.add_attachment in feature backlog + API reference
Co-authored-by: Orca <help@stably.ai>
2026-08-06 23:29:55 +02:00
SJCoetz
dd57d29de3 feat(stock-orders): attachments card on detail page
Co-authored-by: Orca <help@stably.ai>
2026-08-06 23:29:00 +02:00
SJCoetz
f89bbb3a35 feat(mcp): add stock_purchases.add_attachment tool
Co-authored-by: Orca <help@stably.ai>
2026-08-06 23:24:00 +02:00
SJCoetz
ccf04bc39f feat(api): add stock-purchase attachments delete route
Co-authored-by: Orca <help@stably.ai>
2026-08-06 23:21:25 +02:00
SJCoetz
a72c5ca0a7 feat(api): add stock-purchase attachments upload route
Co-authored-by: Orca <help@stably.ai>
2026-08-06 23:21:00 +02:00
SJCoetz
70c158b374 feat(stock-purchases): add additional_attachments service helpers
Co-authored-by: Orca <help@stably.ai>
2026-08-06 23:20:26 +02:00
SJCoetz
9e118a3b5f feat(stock-purchase): split warehouse confirm from accountant booking
Repositioned booking responsibility: warehouse confirms physical
collection only (records quantity_received + flips header to
"received"); the accountant books into inventory via the new
invoicing flow once the supplier invoice is processed. This way
the payment-cycle reasoning lives at the booking step, not at
receipt.

Changes
- Migration: add booked_at + booked_by to stock_purchases
- Service: bookStockFromInvoice (accountant-side, splits from
  bookStockRows)
- API route: POST /api/v1/invoicing/stock-book
- UI: stock rows surfaced on invoicing page (StockBookingSection)
- UI: stock-purchase detail page
  - Driver picker on header (sentinel "delivery" user auto-collects
    every line, real drivers set assigned_to_driver)
  - "Confirm collection" replaces "Mark received" (warehouse-side
    only — no inventory writes)
  - Cancel button with confirmation dialog
- collection-confirm: confirmStockRows stripped of inventory writes;
  the prior /api/v1/collection-confirm/book-stock route was renamed
  to /confirm-stock and is now strictly the warehouse-side confirm
- Tests for all new flows (5/5 passing on the stock-order detail
  page; 10/10 on StockBookingSection)

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-08-06 23:01:15 +02:00
SJCoetz
df87094eb9 chore(playwright): add 2026-08-06 test artifacts
Add captured Playwright page state snapshots and console logs from the 2026-08-06 test session, stored in the .playwright-mcp directory.
2026-08-06 19:38:39 +02:00
SJCoetz
bf54511db1 fix(buyer-tasks): make card layout mobile-friendly
At <=640px the buyer-tasks card content was crushed by the action
column sitting beside it: the PO number badge broke mid-string
('PO-2026-08-042' wrapping to 4 lines), supplier names wrapped to
6 lines, and the new 'Sent to ...' caption overlapped the PO
badge on the PO Sent tab.

Three responsive tweaks, all using Tailwind breakpoint utilities:

1. Outer card container switches from 'flex items-start' to
   'flex flex-col sm:flex-row sm:items-start'. The action column
   drops below the info column on mobile, giving the info all the
   available width and the buttons full-width tap targets.

2. PO number badge gains 'whitespace-nowrap'. The badge stays
   atomic; the parent's 'flex-wrap' carries the whole badge to a
   new line when it doesn't fit.

3. PO Sent right column: buttons stack vertically on mobile
   ('flex-col sm:flex-row' + 'flex-1 sm:flex-none' on each
   button) and the caption switches from 'text-right' to
   'text-left sm:text-right' so it sits under the buttons.

Desktop layout is unchanged — verified at 1280px that the
side-by-side buttons, right-aligned caption, and inline single
button all still render exactly as before.

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-08-06 18:21:38 +02:00
SJCoetz
d2ffbea6bc feat(buyer-tasks): show recipient + send date on PO Sent card
Adds a small muted caption under the Re-Send / Mark as ready buttons
on the PO Sent tab showing the email address the PO was sent to and
the send timestamp (en-GB short format like '5 Aug 2026, 14:23').

Data sources come from the existing BuyerTaskGroup.emailPayload
exposed in commit f61c173 — no new PB fields, no new types. The
caption only renders when sentAt is present, so legacy rows (or a
fresh draft flipped to po_sent without a successful send) stay clean.

Visible on mail_failed too: the operator can see who we tried to
reach and when, alongside the existing 'Mail failed' banner.

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-08-06 18:16:22 +02:00
SJCoetz
f3052bfd40 Merge branch 'feat/buyer-tasks-resend-task1'
Adds a Re-Send button next to Mark as ready on the PO Sent tab of the buyer-tasks page. The button opens the existing SendPoModal in resend mode, pre-filled from the PO's previous email_payload (to/cc/bcc/subject/body). Also migrates the hardcoded purple Mark-as-ready color to the themed primary token, and fixes the mail-failed 'Resend' label that previously opened the wrong dialog.
2026-08-06 18:11:59 +02:00
SJCoetz
add7ab3302 fix(send-po-modal): seed default body when resend has no prior email_payload
Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-08-06 18:10:08 +02:00
SJCoetz
abdc3a4d9f fix(send-po-modal): preserve previous message body on resend (don't overwrite with default template)
Co-authored-by: Orca <help@stably.ai>
2026-08-06 18:06:22 +02:00
SJCoetz
f61c173a1a fix(buyer-tasks): surface PO email_payload + attachments on group for resend modal prefill
Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-08-06 18:01:58 +02:00
SJCoetz
6008f17c9a feat(buyer-tasks): add Re-Send button on PO Sent tab + theme-token cleanup
Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-08-06 17:43:17 +02:00
SJCoetz
ff94086ec0 docs(plan): add implementation plan for Re-Send button on PO Sent tab
Co-authored-by: Orca <help@stably.ai>
2026-08-06 17:34:28 +02:00
SJCoetz
3a954982d0 docs(spec): add Re-Send button on PO Sent tab of buyer-tasks
Co-authored-by: Orca <help@stably.ai>
2026-08-06 17:33:25 +02:00
SJCoetz
7f477ca949 fix: resolve PO bug, add sku, improve webhooks
Add `sku` to the order_item.patch MCP tool whitelist and update its documentation
Fix stuck stock POs in buyer tasks po_sent tab by filtering out downstream statuses
Rewrite margin approval webhook to use fire-and-forget dispatch to avoid UI blocking, add server-side error logging
Add MCP feature requests and local setup documentation
2026-08-06 13:13:57 +02:00
SJCoetz
4a69b57ec2 docs(pdf): remove stray duplicate 'from quoteSubtotal() in two ways:' line
Co-authored-by: Orca <help@stably.ai>
2026-08-06 12:22:22 +02:00
SJCoetz
68dcdd565d chore(pdf): silence unused-vars on intentional totalAmount destructure
Co-authored-by: Orca <help@stably.ai>
2026-08-06 12:21:00 +02:00
SJCoetz
180288e6e3 test(pdf): add render smoke tests for PO PDF (covers VAT totals)
Co-authored-by: Orca <help@stably.ai>
2026-08-06 12:13:52 +02:00
SJCoetz
07900075a4 docs(pdf): move VAT bullet to Layout block + tidy effective-qty comment
Co-authored-by: Orca <help@stably.ai>
2026-08-06 12:08:14 +02:00
SJCoetz
7a6e32cadb feat(pdf): add 15% VAT line + VAT-inclusive Total to PO PDF
Co-authored-by: Orca <help@stably.ai>
2026-08-06 12:02:55 +02:00
SJCoetz
a1714833a8 docs(pdf): fix poSubtotal JSDoc summary (effective-qty prose) + tidy blank lines
Co-authored-by: Orca <help@stably.ai>
2026-08-06 12:00:48 +02:00
SJCoetz
965f3e3bbe docs(pdf): restore poSubtotal JSDoc + clarify fallback uses || not ??
Co-authored-by: Orca <help@stably.ai>
2026-08-06 11:56:51 +02:00
SJCoetz
b3d9eeb5ce feat(pdf): add poSubtotal helper for PO PDF totals
Co-authored-by: Orca <help@stably.ai>
2026-08-06 11:54:23 +02:00
SJCoetz
40d069b5aa docs(plan): add 15% VAT to supplier PO PDF implementation plan
Co-authored-by: Orca <help@stably.ai>
2026-08-06 11:48:21 +02:00
SJCoetz
a663de2d36 docs(spec): clarify placement of vat computation
Co-authored-by: Orca <help@stably.ai>
2026-08-06 11:45:11 +02:00
SJCoetz
3beedc7ad6 docs(spec): add 15% VAT to supplier-facing PO PDF
Co-authored-by: Orca <help@stably.ai>
2026-08-06 11:44:54 +02:00
SJCoetz
5053ec96ea test: add quote detail page layout regression tests
Add a vitest suite that enforces critical layout invariants for the quote detail page, and include accompanying Playwright trace snapshots and console logs from the test execution.
2026-08-06 02:04:28 +02:00
SJCoetz
e2bcafe4d8 chore(playwright-mcp): add test snapshot and console logs 2026-08-06 01:56:47 +02:00
SJCoetz
2bd8fcf247 fix: resolve PocketBase expand projection bug
This commit fixes a critical PocketBase projection bug where omitting "expand" from fields lists silently drops related data, leading to missing supplier/customer/order info across the app. It also includes usability improvements and test coverage:

1.  Add "expand" to PB fields projections in invoicing page, quotes lookup API, sprint page, and quote search helper
2.  Update PO Sent tab filter to exclude "collected" and "ready_invoice" statuses
3.  Reorder buyer tasks list to sort by supplier first, then order number
4.  Add comprehensive regression tests for all fixed issues

Included Playwright test capture artifacts from local testing.
2026-08-06 01:33:16 +02:00
SJCoetz
d8dea63d16 feat(invoicing): include sibling order items in invoicing rows
- extract reusable constants and helper functions including escapeFilterValue, dedupeRawRows, listAllInvoicingRows, and loadInvoicingRows
- add safe filter value escaping to prevent query injection risks
- rewrite data fetching to pull all related order items for orders with ready-to-invoice rows, then deduplicate results
- replace raw getFullList calls with paginated PocketBase API usage
- update test suite to mock getList instead of getFullList and add coverage for new behavior
- clean up redundant eslint disable comments in modal components
2026-08-05 23:17:06 +02:00
SJCoetz
9f601002af refactor(collection-confirm): make PO cards start collapsed by default
Remove the initial open state for the first PO card, and update all tests to expect the default collapsed state. Users can expand cards manually to view contents, improving scannability for long PO queues.
2026-08-05 23:08:44 +02:00
SJCoetz
12fa65a481 feat: add new order, invoice, and dashboard management tools
- Add server-side `/api/v1/dashboard` endpoint to reduce client-side payload and round trips
- Implement invoice upload and order-item SKU update APIs with complete test suites
- Refactor order and order-items list endpoints (including MCP tools) to support proper PocketBase filter and sort params
- Fix pagination validation for order-items route to clamp page ≥1 and perPage 1-500
- Add redirects in next.config.ts to map underscore collection paths to hyphenated API routes
- Improve buyer tasks page to avoid redundant tab count refetches when switching tabs
- Refactor collection confirm components with editable SKUs and updated accessibility labels
- Add cached admin PocketBase auth to minimize repeated authentication calls
- Add performance testing scripts for dashboard endpoint latency
- Update quotes list route to default to excluding archived counts
- Fix photo strip component text and labels for non-invoice documents
- Refactor sprint page to lazy load supplier/customer data for pricing modal
- Clean up unused imports in test files
- Add full test coverage for all new APIs, services, and components
2026-08-05 23:00:54 +02:00
SJCoetz
d79c433704 fix(quotes): import QuoteItem + return real array from dedup branch
Production deploy failed the TypeScript type-check (next build exited 1)
because commit 2acfd2d changed CreateQuoteFromEmailResult.items from
'number' to 'QuoteItem[]' without updating:

  1. The type import — QuoteItem was never added to the import from
     @/lib/types (line 6 only brought in Quote, Customer, Company).

  2. The dedup branch (line 1060-1076) — still passed itemsCount: number
     where the new shape requires QuoteItem[]. Now fetches the real rows
     via getFullList and sets items_count = items.length for back-compat.

The build log shows the error was visible right at 'Running TypeScript ...'
but the deployment platform's log scraper cut off the actual stderr,
making it look like an opaque DeploymentException. Reproduced locally
with 'npm run build' to confirm both errors.

Build now passes (no next build / tsc failures). All 1392 tests pass.

Co-authored-by: Orca <help@stably.ai>
2026-08-05 16:18:31 +02:00
SJCoetz
ba1f916102 feat(orders): show From + Subject in review modal Source of truth header
The Source of truth panel previously only showed the email subject and
body inside a collapsed-by-default toggle. Operators couldn't see who
sent the mail or what the subject was without expanding the body.

This change adds a persistent header above the collapsible body with
two always-visible lines:

  From:     jane@acme.test    (AtSign icon)
  Subject:  RFQ for 10x bolts  (Mail icon)

Sender resolution chain (in getOrderReviewData):

  1. order.email_sender  — n8n ingest snapshot of the actual From:
                           on the inbound RFQ (best signal)
  2. quote.email_sender  — covers pre-snapshot legacy orders
  3. customer.email      — last-resort fallback
  4. ''                  — renders as '(unknown sender)' placeholder

Subject already lived on the same record; the header just promotes it
out of the collapsed body so it's visible without expanding. The
existing '(no subject)' / '(no body)' placeholder pattern is matched
with a new '(unknown sender)' placeholder.

Tests: 7 new (3 modal header, 4 service sender fallback chain). 101
passing total.

Co-authored-by: Orca <help@stably.ai>
2026-08-05 16:09:53 +02:00
SJCoetz
7f1603d9b0 test(mcp): cover order_items.update + quotes.update regenerate_pdf
- order_items.update happy path: patches cost_price + qty_invoiced
  + supplier_invoice_number, verifies the whitelist strips nothing
  and the row is written
- order_items.update forbidden: system-controlled fields (order,
  purchase_order, quote_item, id) are rejected
- quotes.update regenerate_pdf:true: patch is saved first, then
  the PDF endpoint is called with the freshly-persisted letterhead,
  and the response includes both the updated quote and the new PDF
  attachment metadata
- tools/list test count: 77 (was 76)

All 1385 tests pass.

Co-authored-by: Orca <help@stably.ai>
2026-08-05 15:52:30 +02:00
SJCoetz
106af81934 feat(mcp): order_items.update for invoice reconciliation + quotes.update regenerate_pdf
Two new capabilities, both requested by the agent for the
collected-vs-invoice reconciliation workflow:

1. order_items.update — patch fields on an order_item for invoice
   reconciliation. Lets the agent compare what the supplier invoiced
   (qty_invoiced + supplier_invoice_number + cost_price) against what
   was ordered (quantity) and what was collected (qty_collected),
   then write the differences. Whitelist mirrors the quote_items.update
   pattern:

     cost_price, selling_price, quantity, markup, markup_percent,
     notes, qty_processed/assigned/collected/verified/stocked/picked/
     loaded/delivered/invoiced/cancelled, supplier_invoice_number,
     customer_invoice_number, collection_date, collected_date,
     verified_date, delivered_date, invoiced_date

   System-controlled fields (id, order, purchase_order, quote_item,
   product, created, updated) are rejected with forbidden_field so
   the agent can never accidentally reassign an item to a different
   order or PO.

2. quotes.update with regenerate_pdf:true — the agent can now
   switch letterhead (or any other header field) AND re-render the
   PDF in a single call. The patch is saved first, then the existing
   POST /api/v1/quotes/{id}/pdf route is called using the freshly-
   persisted letterhead, so the response always matches the record.
   Useful for: 'switch this quote to ISHERWOOD and send me the new
   PDF'. Without the flag, the existing 2-step workflow still works
   (quotes.update, then quotes.get_pdf).

All 1385 tests pass (was 1382; +3: regenerate_pdf happy path,
order_items.update happy path, order_items.update forbidden field).

Co-authored-by: Orca <help@stably.ai>
2026-08-05 15:52:30 +02:00
SJCoetz
f76b235e9a test(mcp): cover quotes.get_pdf, list_attachments, and the two new order attachment tools
- quotes.list_attachments: proxies to GET /api/v1/quotes/{id}/attachments
- quotes.get_pdf: proxies to /api/v1/quotes/{id}/pdf/base64 (with optional
  letterhead + regenerate query params)
- orders.add_attachment: posts multipart to /api/v1/orders/{id}/attachments/upload
- orders.list_attachments: proxies to /api/v1/orders/{id}/matched-attachments

All four tests use a stubbed global fetch (restored in finally) so
the MCP route's internal HTTP calls are deterministic and don't hit
the real server. Updated the tools/list test to include the four new
tools (76 total, was 72).

All 1382 tests pass.

Co-authored-by: Orca <help@stably.ai>
2026-08-05 11:51:23 +02:00
SJCoetz
b3344f9f47 feat(mcp): quote PDF retrieval + order/quote attachment tools
Closes the four gaps the agent flagged for the email→quote→PDF→send
workflow:

NEW REST ROUTES
- GET  /api/v1/quotes/[id]/pdf/base64  — auto-renders the quote PDF
  (idempotent against an existing pdf_generated_at) and returns it as
  { pdf: <base64>, attachment_id, filename, size, content_type,
  letterhead }. 422 on placeholder customer / unpriced items. Used by
  the new quotes.get_pdf MCP tool to give MCP clients a way to
  download the rendered PDF — the streaming /api/v1/quotes/[id]/pdf
  endpoint is still available for the WebUI's iframe.

- POST /api/v1/orders/[id]/attachments  — uploads a file to the
  order's MATCHED QUOTE. PB has no order_attachments collection;
  orders share the quote_attachments table via their matched quote.
  201 returns the new attachment + matched_quote_id so the agent can
  confirm where the file landed.

NEW MCP TOOLS (catalog now 76, was 72)
- quotes.list_attachments   — list all attachments on a quote
  (generated PDF + original RFQ email attachments + operator uploads)
- quotes.get_pdf             — auto-render + return the PDF binary as
  base64 so the agent can save/forward/email it
- orders.add_attachment      — upload to an order (proxy to the new
  order upload route; multipart form)
- orders.list_attachments    — list attachments on an order (proxies
  the existing /api/v1/orders/[id]/matched-attachments route, which
  returns the matched quote's attachments)

The 'upload to a PDF' workflow is now: attachments.upload_for_quote
(or orders.add_attachment) → quotes.list_attachments to see what's
there (including the generated PDF) → quotes.get_pdf to download
the PDF → quotes.send_email with selectedAttachmentIds to ship it all.

WHY the existing 'attachments.upload_for_quote' was not enough:
the agent previously had no way to retrieve the rendered PDF binary
or to attach files to orders. Now it has both. The order upload goes
via the matched quote because that's the single attachment table in PB.

All 1382 tests pass (was 1378; +4 for the new tools).

Co-authored-by: Orca <help@stably.ai>
2026-08-05 11:51:23 +02:00
SJCoetz
0ac05b847b test(mcp): cover quotes.update + create_from_email unit field
- quotes.update: happy path with whitelisted fields, forbidden_field
  rejection for system-controlled fields (status/customer/company/
  quote_number/id), empty-patch rejection, missing-id rejection
- create_from_email: unit field now preserved end-to-end, result.items
  returns the actual QuoteItem array (not just a count)

Also updates the existing tools/list test (now 72 tools, was 71) and
the create_from_email mock in route.test.ts to use the new
items/items_count shape so it matches the type.

All 1379 tests pass.

Co-authored-by: Orca <help@stably.ai>
2026-08-05 11:39:16 +02:00