Files
cnx_vinyl/docs/catalogue-intelligence.md
T
grabowski 15d28d2e0b Plant assistant: one agent management and operators can ask
New docs/plant-assistant.md (decision, Alexander, 26 Oct 2026): an
assistant for the whole plant, asked in Thai or English at kiosks, desks
and phones, answering from the knowledge base (SOPs as ERPNext Quality
Procedures, vendor manuals on the Assets, recipes and limits, this
repository, lessons learned) and from ERPNext and the machine-data store,
with sources on every answer. Roles and questions, what it does
(explain, trace, summarise, alert context, plan vs actual, drafts) and
never does (machines, submitting, safety improvisation, seeing more than
the asker, rating operators, needing the internet). Built on Typhoon 2.5
(open weights, Apache-2.0, CPU trial first), Typhoon ASR for optional
voice, pgvector retrieval, MCP tools in cnx-factory through the one
integration layer and the PostgreSQL replica; vinyl-ai GPU server only if
needed (~250k to 300k THB, RTX 5090 Thai retail Oct 2026). Test set per
role, phasing, cost, risks, open decisions.

Mirrors: digital-factory systems and machines tables, rough cost note;
catalogue advisor becomes a role of the assistant; worker-safety
training; roadmap item 18; README bullet and layout.
2026-10-27 09:33:25 +07:00

425 lines
25 KiB
Markdown

# Own Catalogue: Sales Map, Demand Forecast and Market Advisor
The idea (Alexander, 26 October 2026): if CNX owns a catalogue it can
press, its sales by shop, region and location can drive a model that says
what to press next. That needs three things: a **catalogue dashboard**
with a **map of where the records sell**, a **demand model** that turns
those sales into press decisions, and an **advisor agent** that points at
market opportunities. This document plans all three on the systems the
plant already intends to run. It is a planning position: every figure
without a source is an estimate, and nothing here is in the business
plan's revenue.
## Why a pressing plant should own a catalogue
- **It fills press time the plan does not sell.** At the planned 60 %
specials, specials run only on the Duplex system, and the Classic runs
at about two thirds of its ~128 records an hour
([revenue model](finance/revenue-model.md#operating-picture-of-the-launch-plant-october-2026)).
The spare third is 21 to 48 records an hour; at the plan's 60 to 70 %
effectiveness, 16 hours and 300 days that is **~60,000 to 160,000
records a year** (estimate), black and solid colours only. At the
calculator's planning rate the launch plant is balanced and that spare
time disappears, so the catalogue is a filler for idle hours, not a
reason to buy presses.
- **A repress is the plant's shortest and cheapest order.** The father
and mother of every side are archived, so a repress skips cutting and
the three artist gates and takes **~7 to 11 working days**; a mother
gives about ten stamper sets and a father 5 to 10 mothers
([production timeline](production-timeline.md#repress-the-shortest-path-in-the-plant)).
A label that has to order from a plant abroad presses big runs months
ahead and carries the stock. A label inside a plant with its own metal
archive can press **small runs often, against measured demand**. That
is what makes a demand model worth having: it is asked every few weeks,
for a few hundred records, not once a year for thousands.
- **There is a market for Thai catalogue abroad, and it runs through
Japan.** EM Records (Osaka) reissues luk thung and molam compiled by
Soi48, many of them first official reissues
([EM1185LP](https://www.discogs.com/release/14467921-Various-Classic-Productions-by-Surin-Phaksiri-Luk-Thung-Gems-from-the-1960s-80s),
[EM1198DLP](https://emrecords.ocnk.net/product/244)); Bangkok's
ZudRangMa Records co-released an Angkanang Kunchai compilation with EM
Records and Soi48 that launched in Japan with the singer present
([Bangkok Post](https://www.bangkokpost.com/life/arts-and-entertainment/419399/legendary-molam-star-unleashed-again)).
Japanese labels shipped 3.38 million records in 2025, and Japan is short
of pressing capacity ([revenue model](finance/revenue-model.md#resurrec-japan-october-2026)).
Contract pressing for Japanese labels now competes with ResurRec's new
Japan plant; selling Thai catalogue into Japan does not.
- **The reverse has been done.** P-Vine, a Japanese label, built its own
plant (VGAP, 2024) because domestic plants took six months to a year
and pressing abroad about six months, too slow for new releases. P-Vine
also said that, having made records themselves, it found the ~¥4,000 a
new record sells for "tight"
([Phile-web, 15 October 2024](https://www.phileweb.com/news/audio/202410/15/25744.html),
[BRUTUS, 2025](https://brutus.jp/en/post-430468/)). A catalogue earns the
retail or wholesale price instead of a pressing fee, and carries stock,
rights and marketing in exchange.
## What counts as our catalogue
| Kind | What it is | In scope |
|---|---|---|
| Poy Records releases | Titles whose masters the label owns; SST in Frankfurt cut lacquers for POY003, 005, 006 and 013 ([equipment suppliers](suppliers/equipment-suppliers.md)) | yes, the seed and the first data |
| Licensed reissues | Masters licensed for vinyl: territories, term, sell-off period, royalty per unit, advance | a decision (see Open decisions) |
| Customers' titles | Records the plant presses for artists and labels | **no**: their sales are their data |
Two rules follow from the third row:
- **Customers' data never trains our catalogue decisions.** Order sizes,
represses and shipping addresses of customer titles stay theirs. A
customer can opt in to share aggregate sales; nobody is opted in by
default.
- **Customer orders come first on the presses.** Catalogue runs fill
idle slots and never move a customer's date. A label that suspects its
plant favours its own releases goes elsewhere.
Which titles Poy Records holds, in which territories and until when, is
the first list to write; nothing below can be tested without it.
## The questions the system answers
1. **What to press next**: which title, before it runs out where it
sells.
2. **How many**: a run sized to the demand of the next few months, since
the next repress is two weeks away.
3. **Which variant**: black or a colour, within what the presses can
make (catalogue specials compete with customer specials on the Duplex;
black and solid colours go on the Classic's idle hours).
4. **Where the stock goes**: the plant, a distributor, or a shop on
consignment.
5. **Which titles to add**: licensing leads for a human to research.
## Data sources
| Source | What it gives | Location detail | Access and terms | Lands in |
|---|---|---|---|---|
| Own web shop, direct shop accounts, event sales (Poy Festival merch table) | Every sale with its ship-to or the event's place | postcode or city; the event venue | ours | ERPNext: Sales Invoice, POS Invoice, Delivery Note |
| Distributor statements | Units per account (shop) and territory per month, returns | shop or territory | the distribution agreement must require monthly sell-through by account, not only sell-in | ERPNext: Channel Sales Report (new, below) |
| Consignment shops | Monthly stock count, so sell-through per shop | the shop | the consignment agreement | ERPNext: Channel Sales Report, consignment warehouse per shop |
| Bandcamp | Raw sales report with the buyer's ship-to city, state, postcode and country for physical items; also as JSON through the Sales API ([help](https://get.bandcamp.help/en/articles/15263368-reading-your-raw-data-sales-report), [API](https://bandcamp.com/developer/sales)) | postcode or city | the label's own account | ERPNext: Sales Invoice per Bandcamp payout with lines, or Channel Sales Report |
| Record passport registrations | A buyer scans the sleeve code and, opting in, gives country and city: the end listener's place even when the record went through a shop or distributor | city | consent on the form; no IP address stored ([traceability](traceability.md)) | machine-data store, per-record rows keyed to the record key |
| Streaming of our own artists | Top countries and cities per artist and per song in Spotify for Artists ([Spotify](https://artists.spotify.com/en/blog/how-to-read-your-spotify-for-artists-data)); country totals in the distributor's streaming statements | country, top cities | only for artists we release | machine-data store, weekly series |
| Interest list and pre-orders | "Tell me when it is pressed" sign-ups and campaign pre-orders on the [artist platform](artist-platform.md) | country, city | consent on sign-up | ERPNext: Lead, Campaign, Payment Entry |
| Discogs monthly data dumps | Every release, version and pressing in the Discogs database: labels, countries, formats, identifiers ([data.discogs.com](https://data.discogs.com), CC0) | country of release | free to use (CC0) | a local index on the plant core, refreshed monthly |
| Discogs API: have/want counts, lowest price and copies for sale ([API](https://www.discogs.com/developers)) | The collectors' view of a title | none useful | **Restricted Data** (marketplace data by name; the have/want counts come from users' collections and wantlists, which the terms also restrict): no commercial use, no storage longer than needed, never shown more than six hours old, no marketing analytics without Discogs' written permission ([API Terms of Use](https://support.discogs.com/hc/en-us/articles/360009334593-API-Terms-of-Use), checked October 2026); 60 requests a minute | not stored; live look-ups by a person only, unless Discogs agrees in writing |
| Market totals | Country production and sales series, e.g. RIAJ for Japan ([revenue model](finance/revenue-model.md#resurrec-japan-october-2026)) | country | public | a context layer on the map |
| Events | Tours, festival slots, film or ad syncs, anniversaries, Record Store Day | city, date | entered by the team | ERPNext (a dated note on the Release) |
**Sell-in is not sell-through.** A distributor buying 300 copies says
nothing about fans until the shops sell them; returns can come months
later. The model uses sell-through wherever an agreement provides it and
treats sell-in as a weaker signal.
**Stockouts hide demand.** Weeks in which a title was out of stock at a
shop record zero sales, not zero demand. Those weeks are marked censored,
and the interest list and passport registrations carry the demand the
till could not.
## Where the data lives (ERPNext touchpoints)
The plant's standing rule holds: ERPNext is the only system of record,
our tools read and write it, and only machine-speed data stays outside
([digital factory](digital-factory.md#erpnext-the-plants-erp)).
| Object | In ERPNext | Outside (machine-data store) |
|---|---|---|
| Release, side, item per format and variant | existing custom Release and Side documents; Item per release, format and variant | none |
| Rights | **new** Licence document in `cnx_erp`: licensor, territories, term, sell-off end, royalty basis and rate, advance and recoupment; Release links to it; "owned" or "licensed" on the Release | none |
| Royalties | royalty accrual per unit sold, posted as journal entries; a royalty statement print format per licensor | none |
| Places | Address gains latitude and longitude of its **postcode or city centroid** (custom fields, filled by the geocoder); shops are Customers in a "Shop" group with their exact location; regions are ERPNext Territories | none |
| Sales | Sales Invoice, POS Invoice, Delivery Note | none |
| Third-party sales | **new** Channel Sales Report: source (distributor, Bandcamp, consignment count), period, lines of item, place (shop, territory or city), units sold, returned, on hand | none |
| Stock | Warehouses at the plant, at each distributor and one per consignment shop | none |
| Metal | Serial No per father, mother and stamper with sets used per mother (already planned) | metrology raw files |
| Recommendations | **new** Catalogue Recommendation: title and variant, quantity, the forecast quantiles it rests on, the reason, status (proposed, accepted, rejected), link to the Work Order it became | the full forecast rows per title, place and month |
| Market opportunities | CRM Opportunity and Lead (new shop, distributor, territory, licensing lead) | none |
| External signals | none | weekly streams per country and city, passport registrations, Discogs dump index, map aggregates |
The forecast rows and map aggregates are derived and can be rebuilt from
ERPNext and the signal series at any time; nobody types into them.
Decisions and their outcomes are ERPNext documents, which is what lets
the model and the advisor be scored later.
```mermaid
flowchart LR
subgraph S[Sources]
SHOP[Web shop, shop accounts,<br/>event POS]
DIST[Distributor and<br/>consignment statements]
BC[Bandcamp sales report]
INT[Interest list,<br/>pre-orders]
PP[Record passport<br/>registrations, opt-in]
STR[Streaming by country<br/>and city, own artists]
DG[Discogs CC0 dump]
end
ERP[(ERPNext<br/>releases, licences, sales,<br/>stock, channel reports)]
MD[(Machine-data store<br/>signal series, forecast rows)]
FC[Forecast job]
DASH[Catalogue dashboard<br/>and sales map]
AG[Market advisor]
TEAM[Team decides]
SHOP --> ERP
DIST --> ERP
BC --> ERP
INT --> ERP
PP --> MD
STR --> MD
DG --> MD
ERP --> FC
MD --> FC
FC --> MD
FC -->|recommendations| ERP
ERP --> DASH
MD --> DASH
DASH --> AG
AG -->|drafts: recommendations,<br/>opportunities| ERP
ERP --> TEAM
TEAM -->|repress work order| ERP
```
## Personal data
Buyers' names and addresses are personal data under Thailand's PDPA
(B.E. 2562, fully in force since 1 June 2022;
[overview](https://pacmap.dev/regulation/th-pdpa-2019)), and so is a sales
location fine enough to point at one buyer.
The map is built so that it never needs a person:
- addresses are geocoded to the **postcode or city centroid**, never to
the house;
- a map cell shows a count only when it holds at least five buyers;
smaller cells merge into the surrounding region;
- only shops and venues, which are businesses, appear as points;
- passport registrations ask for country and city, with consent, and
store no name, e-mail or IP address unless the fan asks to be told
about represses;
- the advisor sees aggregates only; no personal data goes into a prompt.
## The catalogue dashboard
One internal tool for the label team, with five views.
1. **Catalogue.** Every title and variant: owned or licensed, licence
territories and expiry, stock by place (plant, distributors, each
consignment shop), sell-through over 4, 13 and 52 weeks, weeks of
cover at the current rate, repress-ready (father and mother archived,
stamper sets left on the mother from ERPNext's counts), last pressing,
royalties accrued, and a flag when a recommendation is open.
2. **Sales map.** Sales density by city or hexagon over time, with a
time slider and filters by title, genre, variant and channel. Layers:
own sales; shops with our stock (pins sized by stock, coloured by
weeks of cover); distributor territories; streaming listeners of our
artists; passport registrations; interest-list sign-ups; country
totals (RIAJ and others) as context. A **white-space layer** marks
places where listeners, sign-ups or registrations are high and sales
or stock are low: the map's version of a market opportunity.
3. **Title page.** The sales curve with out-of-stock weeks shaded, the
forecast fan for the next months, the title's own map, its metal
genealogy and its licence clock.
4. **Repress planner.** Proposed runs against free press slots from the
factory scheduler (customer work orders already placed, catalogue in
the gaps), with the Classic's idle hours as the default home for black
and solid colours.
5. **Advisor inbox.** The advisor's notes, each with its numbers linked
to the rows they came from, and accept or reject buttons that create
or close the ERPNext document.
**How it is built.** Same stack and rules as every other tool
([digital factory](digital-factory.md#the-factory-application)): Rust
(axum, minijinja, HTMX) in `cnx-factory`, its own ERPNext integration
user, deployed by the clan, internal only (office and the team over the
mesh, not public). The map is open source and self-hosted, in line with
the plant's open-source decision
([digital factory](digital-factory.md#open-source-throughout-and-working-with-other-plants)):
- **MapLibre GL JS** (BSD-3-Clause) in the browser;
- **vector tiles from OpenStreetMap data** (ODbL, attribution on the map)
as one Protomaps PMTiles file (BSD-3-Clause tooling) served from the
fleet, so no Google Maps or Mapbox account;
- **H3** hexagons (Apache-2.0) for binning;
- **geocoding offline**: postcode and city centroids from GeoNames
(CC BY 4.0), with a self-hosted Nominatim only if centroids prove too
coarse.
## The demand model
The model grows with the data. Each stage has to beat the simpler one
before it is allowed to decide anything.
**Stage 0: no sales history (a new title or a reissue candidate).**
Demand is borrowed from comparable titles (genre, era, the artist's
streaming, how many pressings already exist per the Discogs dump) and
tested cheaply: a **pre-order campaign on the
[artist platform](artist-platform.md#fan-funded-campaigns)** for our own
title is the experiment. If the pre-orders reach the minimum run, press;
if not, refund and learn. The same mechanism the platform offers artists
becomes the label's market test.
**Stage 1: the first months of sales.** A reorder rule, not a model:
sell-through per title and place per week, with out-of-stock weeks left
out of the rate. Repress when the stock that will be left at the end of
the repress lead time (~7 to 11 working days, plus transport to where it
sells) falls below a safety stock. Dashboard view 1 shows this from the
first sale.
**Stage 2: a year of data and a few dozen titles (thresholds are
estimates).** A hierarchical count model of weekly sales per title and
place (negative binomial, with partial pooling across titles of the same
genre and across nearby places), so a title with three months of history
borrows strength from its neighbours. Inputs: past sales with censoring
for stockouts, the artist's streaming in that place with a lag, price,
variant, season (year-end, Record Store Day), events, age since release,
passport registrations and sign-ups. Output: a forecast **distribution**
(quantiles), never a single number. Runs as a nightly batch job on the
plant core (Python with statsmodels or PyMC from nixpkgs, as a systemd
timer).
**How many to press.** With a two-week repress the run only has to cover
the next few months, so it is sized by the classic stocking rule: press
the quantile of next-period demand at which the margin lost on a missed
sale balances the cost of a record left unsold.
- Missed-sale cost: net selling price after the channel's margin, minus
direct cost (1.35 to 2.70 USD a record in the
[revenue model](finance/revenue-model.md#operating-picture-of-the-launch-plant-october-2026)),
minus royalty.
- Unsold-record cost: direct cost plus storage and the money tied up,
minus what the PVC is worth as regrind after the label punch
([pressing line](equipment/pressing-line.md#newbilt-range-making-vinyl-europe-2026-flyers)).
- Floor: a run long enough to pay the press setup (half a day per run in
the [production timeline](production-timeline.md#repress-the-shortest-path-in-the-plant))
and within one stamper's life (1,000 to 2,000 pressings).
Our wholesale and retail prices for catalogue titles are not in any
document yet; they are inputs to fill before the rule produces a number.
**How it is checked.** Rolling back-tests on our own history: do the
80 % intervals contain about 80 % of outcomes, and does following the
model give fewer out-of-stock weeks and less stock unsold after a year
than the Stage 1 rule did? Accepted and rejected recommendations, with
what then sold, are ERPNext records, so this is a report, not a project.
## The market advisor
An agent that reads the dashboard's data and writes a **weekly note**
plus answers to questions ("where does molam sell outside Thailand?",
"which shops stock out fastest?"). It **proposes and never acts**: it
places no work order, sends no e-mail to a shop or licensor and changes
no price. An accepted proposal becomes an ERPNext document created by a
person.
What it looks for:
- **Repress now**: weeks of cover below the repress lead time plus
safety, with the run size from the model.
- **Move stock**: a shop out of stock while another sits on months of
cover.
- **White space**: places with listeners, sign-ups or registrations and
no shop or distributor; proposed as a CRM Lead for a shop or
distributor to approach.
- **Variants**: colours that sell faster in a region than black, within
what the presses can make that month.
- **Events**: an artist's tour or festival date in a city where stock is
thin.
- **Licensing leads**: titles in genres that sell for us that, per the
Discogs dump, were never reissued or not for decades, with the original
label named. A lead for a person to research; the advisor never says
that rights are available.
How it is kept honest:
- the numbers come from the model and the database, the words from the
agent; every figure in a note links to its row and date;
- it separates our own data from outside signals and says how sure it
is;
- it has read-only tools: the dashboard API, the forecast rows, ERPNext
reports through a read-only integration user, the local Discogs dump
index, this repository's market notes, and web search through a
self-hosted SearXNG for news (tours, syncs, competing reissues);
- every suggestion and its outcome is logged, and after six months it is
scored against the plain reorder rule; if it does not beat it, it
becomes a report instead of an agent.
**Which model runs it.** The advisor is one role of the
[plant assistant](plant-assistant.md): the same open-weights model on the
plant's own machines, the same tool rules, with the catalogue tools and
the catalogue roles' permissions added. The forecasting is ordinary
statistics, so the language model only reads, searches and writes. A
hosted model is a fallback to decide on only if the open one proves
inadequate, and then only aggregates leave the fleet.
## How it fits the plant
- **Scheduling.** Catalogue work orders enter the same ERPNext schedule
as customer orders, flagged as fillers; the factory scheduler places
them in idle Classic hours first and never moves a customer date.
- **Metal archive.** Catalogue fathers and mothers sit in the archive
room with everyone else's
([facility security](facility-security.md)); "repress-ready" on the
dashboard is ERPNext's serial-number data.
- **Traceability.** Catalogue sleeves carry the record passport code like
every other record, which is what makes the registration layer of the
map possible ([traceability](traceability.md)).
- **Artist platform.** Catalogue pre-orders use the campaign machinery
and its ring-fenced payments; the catalogue's own web shop is a
separate storefront on the same public side
([artist platform](artist-platform.md#how-it-is-built)).
- **Money.** Not in the [revenue model](finance/revenue-model.md) or the
[total investment](finance/total-investment.md). A catalogue needs
working capital for stock and licence advances, which neither plan
carries.
## Phasing
| Roadmap phase | Step |
|---|---|
| 0, validate (now) | List Poy Records' titles and rights; collect its existing sales (Bandcamp report, shops, festival) into one sheet and draw the first map by hand; decide whether to ask Discogs for written permission |
| 1 to 2, quotes and commit | Decide whether the catalogue is in scope, under which label, owned only or licensed too, and how the working capital is funded |
| 3, build | ERPNext: Licence, Channel Sales Report, Catalogue Recommendation, geocode fields, consignment warehouses; dashboard views 1 and 2 in the factory application; geocoder and tiles on the fleet |
| 4, launch | First catalogue represses in idle slots on the Stage 1 rule; passport registrations live; advisor version 0 (rule-based notes written up by the language model) |
| 5, expand | Stage 2 model once there is a year of data; licensing leads; offer label customers the same dashboard on their own data (opt-in), like the open plant stack, not in the revenue |
## Rough cost (estimates)
| Item | Effort or cost |
|---|---|
| ERPNext additions in `cnx_erp` (Licence, Channel Sales Report, Catalogue Recommendation, geocode fields, royalty statement) | 0.5 to 1 person-month |
| Importers (Bandcamp, distributor and consignment statements, streaming exports) | 0.5 to 1 person-month |
| Dashboard and sales map in the factory application | 1.5 to 3 person-months |
| Forecast job: Stage 1 rule, then the Stage 2 model | 0.5, then 1 to 2 person-months |
| Advisor: tools, prompts, logging, scoring | 1 to 1.5 person-months |
| Language model and its machine | shared with the [plant assistant](plant-assistant.md#rough-cost-estimates) |
| Map tiles, geocoder, hosting | on the existing fleet |
## Risks
- **Stock is the risk a pressing plant normally avoids.** Small, frequent
represses keep it small; the model's job is to keep it smaller.
- **Small data.** A few dozen titles is little to learn from; the stages
above exist so that no complex model decides before it has beaten a
simple rule.
- **Data access.** Distributors and shops report sell-through only if the
contract says so; that clause goes into every agreement from the
first.
- **Platform terms.** Discogs' marketplace and wantlist data are
restricted; the plan uses its CC0 dumps and live look-ups by a person
unless Discogs agrees in writing.
- **Conflict with customers.** The scheduling rule and the data rule
above have to be written into the customer terms, not only into this
document.
- **Distraction.** A label is a second business. The catalogue stays a
filler for idle press time until the plant's own order book is
validated ([roadmap](roadmap.md) item 1).
## Open decisions
1. **Catalogue in scope**, and under which name: Poy Records (the seed),
CNX Vinyl, or a new imprint.
2. **Owned titles only, or licensed reissues too.** Licensing adds
advances, royalty accounting and territory limits.
3. **Working capital** for stock and advances: how much and from where;
neither plan carries it.
4. **Discogs**: ask for written permission to use have/want and
marketplace data commercially, or work from the CC0 dumps alone.
5. **Language model**: open-weights on the fleet (default) or hosted.
6. **Label customers**: whether they are offered the dashboard on their
own data, and on what terms.