Customs Decisions That Keep Their Evidence
The Situation
Trade Compliance Classification Engine is a classification workflow for import-heavy operators that need HS and HTS product decisions to be reviewable, repeatable, and backed by evidence. Compliance teams need it when spreadsheets, reviewer memory, and ad hoc rule notes make customs decisions hard to defend during internal review, supplier disputes, or audit preparation.
The product catalog is the pressure point. A distributor can know the SKU, product name, country of origin, materials, intended use, and jurisdiction, then still end up with a classification decision that lives in a spreadsheet cell with no trace of the rule pack, rejected candidates, confidence score, or reviewer correction behind it.
I built the system around a simple premise: a customs classifier should not pretend every product has a clean answer. It should record why it picked a code, why it refused to pick one, and what evidence the reviewer had when they overrode it.
The Cost of Doing Nothing
When customs classification stays in spreadsheets, the visible cost is reviewer time. The deeper cost is evidence loss. A wrong code can be corrected, but a decision with no rule version, no rejected candidates, and no review trail is hard to explain later.
Scenario estimate, not production savings: if a compliance analyst spends 12 minutes per SKU gathering product facts, checking a rule note, recording the chosen code, and preparing audit context, a 1,200 SKU catalog consumes 240 analyst hours. At a conservative €65/hour loaded rate, that is €15,600 of review labor before rework, escalation, or audit prep.
The repository does not prove live savings, customer adoption, or fines avoided. The business case is narrower and more defensible: preserve the evidence chain so classification work can be reviewed, corrected, exported, and rerun without rebuilding the story by hand.
What I Built
I built a Rust and Axum application with PostgreSQL as the record of truth, a reviewer workbench, queued classification jobs, immutable rule-pack activation, and audit export snapshots. Product import refuses rows that lack required customs facts. Classification requests are capped at 100 product IDs, which keeps reviewer-triggered runs bounded instead of turning one request into an unobserved bulk job.
The harder part was outcome honesty. Batch evidence showed a quiet failure mode: no-candidate, low-confidence, and tied-candidate cases could look successful if they were flattened into a generic classified state. I treated that as a product failure, not just a code bug. The engine now routes outcomes into classified, needs review, or blocked states, with reasons that survive into the audit record.
Rule packs go through activation gates before they can drive decisions. The gate checks syntax, rule validity, golden cases, runtime safety, and required output coverage. Once active, a rule pack is immutable for its tenant and jurisdiction. That choice matters because audit exports need the exact decision context, not the newest version of the rules.
System Flow
Data Model
Architecture Layers
The Decision Log
| Decision | Alternative Rejected | Why |
|---|---|---|
| Treat classification as an evidence record | Store only the selected HS or HTS code | Customs review needs candidates, rejected options, risk, confidence, rule version, and timestamps, not just a final label. |
| Block missing product facts at import | Let incomplete rows enter the classifier | Missing SKU, description, country, jurisdiction, product type, materials, or intended use turns the decision into guesswork. |
| Make active rule packs immutable per tenant and jurisdiction | Allow edits to active rules in place | Audit exports must explain the decision with the exact rule version that produced it. |
| Route ambiguity into review and blocked states | Force every run into classified or failed | No candidate, confidence below 0.82, or a tie within 0.05 are business states that need review, not hidden errors. |
| Use PostgreSQL leasing for background work | Add a separate queue service | The workload fits the database boundary, keeps deployment smaller, and still gives leases, retries, and bounded batches. |
| Keep optional integrations non-blocking | Make RAG, notification, or workflow services required | The classifier must run standalone for the core customs workflow, with ecosystem services attached only when configured. |
Ecosystem Integration
The engine can connect review, blocked, rule-pack failure, and audit-export failure paths to optional portfolio services when those services are configured. Reviewer alerts can move through the Event-Driven Notification Hub, while longer exception paths can move into the Workflow Automation Engine when a blocked decision needs approval steps, supplier follow-up, or escalation tracking. The optional RAG connection is treated as document support for reviewer context, not as the classification authority. The integrations are feature-flagged. The classifier runs standalone without ecosystem services.
Results
The shipped repository proves a bounded, review-first customs classification workflow rather than a production savings claim. The system can queue up to 100 products per request, lease 25 jobs per worker batch, separate classified, needs-review, and blocked outcomes, and freeze audit payloads with tenant, product, classification, rule-pack, candidate, override, and timestamp context.
Release safety is expressed as gates, not marketing. Rule-pack activation requires at least 10 golden cases. Backtest code checks 20 synthetic cases, an exact-code accuracy target of at least 0.85, a review-rate ceiling of 0.20, and zero false-low-risk denied-goods outcomes. Those are repository-backed thresholds, not production adoption metrics.
If this moved from portfolio spec to a live customs operation, the first scaling question would be evidence retention and reviewer throughput, not raw classification speed. The architecture already treats uncertainty as work to be routed. That is the difference between a tool that returns an answer and a system that can defend a decision.