src/app/api/v1/sprint/attachment-counts/route.ts
- Chunk itemIds into batches of 80 before building the PocketBase filter;
the live cap was hit at ~115 IDs × 15 chars and surfaced as a
generic Next 500 ("Something went wrong while processing your
request.") that hid the real cause. 7 chunks for the sprint
page's max of 500 items.
src/app/layout.tsx
- Add metadataBase derived from NEXT_PUBLIC_SITE_URL /
NEXT_PUBLIC_POCKETBASE_URL. Silences the per-page
"metadataBase not set" warning during dev renders and gives
OG / Twitter cards a stable canonical base.
src/app/api/v1/{quotes,order-items,purchase-orders,stock,
stock-purchases,users,sprint,driver,ai-insights}/route.ts (NEW)
- Add the missing top-level collection routes the agent API
needs. seven are straight passthroughs to their PB collection;
/sprint proxies to quote_items with optional claimStatus and
category query params; /driver lists PB users (no role field
in PB so callers narrow with ?filter=); /ai-insights returns
a roll-up count + most-recent regenerate timestamp across the
four collections that carry ai_* JSON fields.
src/app/api/v1/collection-pool/{route.ts,route.test.ts},
src/app/(app)/collection-pool/{page.tsx,page.test.tsx,
view-model.ts,view-model.test.ts,components.tsx} (DELETED)
- Drop the collection_pool feature entirely: PB has no
collection_pool collection on the live instance
(pb:migrate:orders-v2 was never run), the codebase comment
in purchase-orders-service.ts says "now-removed", and the
sidebar entrypoint has been migrated to /sprint + /deliveries.
The replacement operational status is the `item_status =
"collection_pool"` enum value on order_items, which stays.
src/lib/server/orders-service.ts (+ test), src/lib/pb-helpers.ts,
src/components/sidebar.tsx, src/components/ui/assign-driver-dialog.tsx,
src/app/(app)/collection-map/sidebar.tsx,
src/app/api/mcp/route.ts (+ test) (MODIFIED)
- Remove listCollectionPool + getCollectionPool +
updateCollectionPoolStatus, the /collection-pool sidebar entry,
the deep-link in assign-driver-dialog and collection-map
sidebar, and the collection_pool.list MCP tool (the tool,
handler, and test mock).
scripts/migrations/2026-07-26-fix-quote-counter-and-reassign-numbers.ts (NEW)
- One-shot migration script that:
1. Reads every quote, sorts by created ASC.
2. Reassigns clean Q202607001 … Q202607{N} over the whole
table (including the e+22 garbage and the legacy
Qnnnnnn rows).
3. Sets quote_counters.(year=2026, month=7).last_seq = N
so the next /api/v1/quotes/next-number returns a clean,
atomic, unique number.
Default mode = dry-run; pass --apply to write.
Already executed against the live cloud PB during this session
(verified: 200/200 rows updated, counter at 200, next-number
returns Q202607201, Q202607202, Q202607203 on consecutive calls).
AGENTS.md (MODIFIED)
- Add data-layer-discipline block matching the style of the
three existing blocks. Documents which layer is legacy (pb-helpers)
vs current (lib/server/*-service.ts via /api/v1/*), gives the
migration order (customers → orders → quotes → suppliers →
companies → rest), and includes the grep one-liner future AI
agents should run before extending pb-helpers.
Not in this commit (your WIP, untouched):
src/app/(app)/{dashboard,delivery-notes,layout,orders/[id],
quotes/[id],quotes/approvals,quotes,sprint}/page.tsx,
src/app/(auth)/login/page.tsx, src/components/charts/*,
src/components/{company-form,customer-form,quote-email-card,
sprint-item-pricing-modal,supplier-form,theme-bootstrap,
theme-picker}*.{tsx,test.tsx}, src/app/api/invoicing/delivery-note/*
src/app/api/v1/quotes/[id]/pdf/route.ts, src/app/globals.css,
src/lib/{item-status-ui,pdf/quote-document,quote-status*,themes,
types}.ts, src/lib/server/{order-item-status*,quotes-service}.ts,
src/styles/themes.css, APP_CAPABILITIES.md, README.md,
docs/SHIPPED.md, scripts/smoke/screenshot-pages.mjs,
and the new untracked files under docs/, scripts/migrations/
2026-07-19…25, src/app/{(app)/deliveries,(app)/delivery-notes,
api/v1/ai-insights/rerun,api/v1/deliveries,api/v1/delivery-notes,
api/v1/quotes/[id]/partial-approve,api/v1/quotes/[id]/split},
src/components/{ai-insights-panel,ai-insights-renderer,
ai-item-history-trigger,brand-stripe,delivery-notes,ui/stock-item-search,
ui/duplicate-warning,ui/duplicate-warning.test}.tsx, supplier-form.test.tsx,
src/lib/{pb-helpers-duplicate.test,server/deliveries-service*,
server/delivery-notes-service*}, public/wfdash-logo*.png,
opencode.jsonc.
3.7 KiB
This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ before writing any code. Heed deprecation notices.
PocketBase select fields are not self-service
The order_items.item_status field is a select with a fixed list of
values. If you want to set a new value (e.g. delivery_note for the
new delivery-note flow), you must add it to the schema in the PB admin
first — the API will 500 with "Failed to update record." otherwise.
The local pb-exports/pb_schema-2.json is a snapshot for reference
only; the source of truth is the running PB instance.
If a new code path sets a select value that doesn't exist yet, ask the user to add it via the PB admin before debugging the API. They do this manually (it's a UI-only change in the PB admin).
Reuse existing multi-file fields for new attachment types
The order_items collection has a multi-file field order_item_attachements
(maxSelect: 10, accepts any MIME). When adding a new document type
to attach to an order item (e.g. delivery note PDF), upload the file
to this field rather than creating a new attachment collection. The
operator queries by some key field on the item (e.g.
delivery_note_number) to find the items, then reads the attachments
off each item.
Pattern in code:
- POST endpoint: generate the PDF, upload via multipart/form-data
using the
order_item_attachementsfield on each item in the group - GET endpoint: query
order_itemsby the key field, find a file whose name matches the expected pattern (e.g.DN26071401.pdf), stream the bytes back via PB's/api/files/...URL - Stable filename per document type so the retrieval side can disambiguate from other attachments on the same item
Data access layer discipline
The app has two coexisting data layers:
- Legacy:
src/lib/pb-helpers.ts— used directly by Server Components undersrc/app/(app)/**/page.tsxand form components. - Current:
src/lib/server/*-service.ts— exposed via/api/v1/*route handlers and/api/mcp, used by the agent API and external integrations.
Both layers read the same PocketBase collections. Pick the right one depending on what you're doing.
Rules
For new endpoints / features: put business logic in
src/lib/server/<feature>-service.ts and expose it via a thin
src/app/api/v1/<feature>/<op>/route.ts. Do NOT extend pb-helpers.ts.
For new pages / form components: call /api/v1/* (or have the
Server Component call the corresponding lib/server/*-service.ts
function directly). Do NOT import from pb-helpers.ts.
When touching a page that already uses pb-helpers.ts: migrate its
data calls to the service layer in the same commit. One resource at a
time, no big-bang rewrites. Lowest-risk order: customers → orders →
quotes → suppliers → companies → everything else.
How to spot you're reaching for the legacy layer
pb-helpers.ts is the file in question. Grep for it:
grep -rn 'from "@/lib/pb-helpers"' src/ --include="*.ts" --include="*.tsx"
Each hit is one caller to migrate when you're in the area.
Migration cost indicator
A single page rewrite is typically: replace getX(...) import + call
with await fetch('/api/v1/x?...') (or a direct service-layer call),
then update the page to await. Half-day budget for 5 pages if no
weird cross-resource joins are involved.