Integration pathways
August 2, 2026 · View on GitHub
Written against a Homebox-derived inventory system, but nothing below depends on Homebox specifically — the requirements are a table of items, somewhere to put a foreign key, and a way to run a classification job.
Design rule: one column, one join
The taxonomy is reference data. It should be loaded, not copied.
The whole integration is one nullable column on the item table holding a 6-digit
class code, plus a table holding the taxonomy itself. Do not denormalise the
attributes onto items. Copying fragile and bulk_density_kg_per_l onto every
row means the day the taxonomy is corrected, every historical row keeps the old
answer, and you have quietly created a second source of truth that drifts.
The one exception is anything a user has explicitly overridden — see Overrides, below.
Pathway A — reference table (recommended)
Load dist/taxonomy.csv into a table and add a foreign key.
CREATE TABLE hg_class (
id char(6) PRIMARY KEY,
name text NOT NULL,
parent char(4) NOT NULL,
division_id char(2) NOT NULL,
description text NOT NULL,
typical_mass_kg numeric(8,3) NOT NULL,
bulk_density_kg_per_l numeric(6,3) NOT NULL,
fragile boolean NOT NULL DEFAULT false,
crushable boolean NOT NULL DEFAULT false,
child_hazard boolean NOT NULL DEFAULT false,
child_hazard_reasons text[] NOT NULL DEFAULT '{}',
hazmat text NOT NULL DEFAULT 'none',
battery text NOT NULL DEFAULT 'none',
mover_restricted boolean NOT NULL DEFAULT false,
storage_zone text NOT NULL,
access_frequency text NOT NULL,
value_density text NOT NULL DEFAULT 'low',
co_store_avoid text[] NOT NULL DEFAULT '{}',
status text NOT NULL DEFAULT 'active'
-- remaining columns per docs/schema.md
);
ALTER TABLE items
ADD COLUMN hg_class_id char(6) REFERENCES hg_class(id),
ADD COLUMN hg_confidence real, -- 0..1 from the classifier
ADD COLUMN hg_classified_at timestamptz,
ADD COLUMN hg_source text; -- 'ai' | 'human' | 'rule' | 'import'
CREATE INDEX ON items (hg_class_id);
CREATE INDEX ON items (left(hg_class_id, 2)); -- division roll-up
hg_source and hg_confidence are not optional extras. Without them you cannot
distinguish a code a person chose from one a model guessed at 0.4, and you will
not be able to re-run the classifier later without destroying human corrections.
Refresh is a re-import: the file is small, codes are stable and never reused, so
an upsert on id is safe and no item row needs touching.
Pathway B — read the JSON at runtime
For an app that would rather not own a table: ship dist/taxonomy.json, load it
into memory at boot, keep the code on the item and resolve attributes in the
application layer.
Simpler, and it forfeits the ability to filter or aggregate by attribute in SQL — which is most of the point. Reasonable for a mobile or offline client, wrong for the primary system.
Pathway C — MCP tool surface
An inventory system already exposing MCP gains most from three tools, and they should be separate calls rather than one overloaded one:
| Tool | Takes | Returns |
|---|---|---|
classify_item | name, description, photo | ranked candidate codes with confidence |
get_class | code | the full attribute record |
check_container | container id | roll-up: mass, fragile %, hazards, conflicts |
classify_item should return candidates, not a decision. A tool that returns
one code invites the caller to write it to the database; a tool that returns
three with confidences invites the caller to apply a threshold, which is the
behaviour you want.
The classification pipeline
Assignment is an LLM job. The taxonomy is designed for it: class description
fields state boundaries explicitly ("Table knives only — kitchen knives are
100602") and aliases carry the vocabulary people actually use.
A workable prompt shape:
-
Retrieve ~20 candidate classes by embedding or trigram match on
name + aliases + description. Do not put all 84 — eventually several hundred — classes in the prompt; recall does not need it and precision suffers. -
Ask for the best code plus confidence plus a one-line reason, from the candidate list only.
-
Apply thresholds:
Confidence Action ≥ 0.85 write the code, hg_source = 'ai'0.5 – 0.85 write it, flag for review < 0.5 write 9999, queue for a human
9999 is a feature. A classifier that must always choose will put the
drain cleaner somewhere, and a wrong safety flag is worse than an absent one. A
populated 9999 bucket is a work queue; a confidently wrong code is a lie the
system will act on.
Where a photo is available, use it. Material and construction — the things that drive density and fragility — are visible and are frequently absent from the text description.
Cost and batching
Classification is a bulk one-off followed by a trickle. For a first pass over an existing inventory, batch 25–50 items per call against a shared candidate list drawn from the whole batch; per-item calls cost roughly an order of magnitude more for no accuracy gain. New items thereafter classify one at a time on creation, where latency matters more than throughput.
Overrides
Class attributes are defaults. An individual item may legitimately deviate — an unusually heavy pan, a book that is a first edition.
Store overrides as a sparse JSON column on the item (hg_overrides), not as a
full copy of the attribute set, and resolve at read time as
coalesce(item.override, class.value). Sparse means a taxonomy correction still
reaches every item that has not explicitly disagreed with it.
An override is also a signal. Several items in the same class overriding the same field the same way means the class value is wrong and should be fixed upstream.
Versioning
Pin the taxonomy version you classified against. Codes never change meaning and never get reused, so a pinned version and a current version are always reconcilable — but a class that was deprecated and superseded needs a migration, and you cannot write one without knowing where you started.
Record the version in the item row or in a job log. Deprecations are the only
breaking change the scheme permits, and superseded_by gives the migration path
mechanically.
Order of work
- Load the reference table. Nothing else can start.
- Add the columns to
items. Leave them null; nothing breaks. - Classify one division's worth of items by hand — 50 or so — and keep it as a gold set.
- Run the classifier over the same items and measure. Under about 80% top-1 agreement, the problem is almost always the class descriptions, not the model.
- Bulk classify. Work the
9999queue. - Only then build anything that consumes the flags. Roll-ups computed over a half-classified inventory produce confident, wrong numbers, and people stop trusting the feature before it works.